mirror of
https://gitlab.uni-freiburg.de/opensourcevdi/spice
synced 2025-12-27 07:29:32 +00:00
client: Remove client code
The client has been superseded by virt-viewer ( http://virt-manager.org/download/sources/virt-viewer/ ) and is no longer being maintained.
This commit is contained in:
parent
b532ef0866
commit
1876971442
@ -3,10 +3,6 @@ ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
SUBDIRS = spice-common server docs
|
||||
|
||||
if SUPPORT_CLIENT
|
||||
SUBDIRS += client
|
||||
endif
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = spice-server.pc
|
||||
|
||||
|
||||
13
client/.gitignore
vendored
13
client/.gitignore
vendored
@ -1,13 +0,0 @@
|
||||
*.la
|
||||
*.lo
|
||||
*.loT
|
||||
*.o
|
||||
.deps
|
||||
.libs
|
||||
Makefile
|
||||
Makefile.in
|
||||
generated_demarshallers.cpp
|
||||
generated_demarshallers1.cpp
|
||||
generated_marshallers.cpp
|
||||
generated_marshallers1.cpp
|
||||
spicec
|
||||
@ -1,249 +0,0 @@
|
||||
NULL =
|
||||
|
||||
bin_PROGRAMS = spicec
|
||||
|
||||
spicec_SOURCES = \
|
||||
application.cpp \
|
||||
application.h \
|
||||
audio_channels.h \
|
||||
audio_devices.h \
|
||||
cache.hpp \
|
||||
canvas.cpp \
|
||||
canvas.h \
|
||||
client_net_socket.cpp \
|
||||
client_net_socket.h \
|
||||
cmd_line_parser.cpp \
|
||||
cmd_line_parser.h \
|
||||
common.h \
|
||||
controller.cpp \
|
||||
controller.h \
|
||||
cursor.cpp \
|
||||
cursor.h \
|
||||
cursor_channel.cpp \
|
||||
cursor_channel.h \
|
||||
debug.h \
|
||||
display_channel.cpp \
|
||||
display_channel.h \
|
||||
event_sources.h \
|
||||
foreign_menu.cpp \
|
||||
foreign_menu.h \
|
||||
glz_decoded_image.h \
|
||||
glz_decoder.cpp \
|
||||
glz_decoder.h \
|
||||
glz_decoder_config.h \
|
||||
glz_decoder_window.cpp \
|
||||
glz_decoder_window.h \
|
||||
hot_keys.cpp \
|
||||
hot_keys.h \
|
||||
icon.h \
|
||||
inputs_channel.cpp \
|
||||
inputs_channel.h \
|
||||
inputs_handler.h \
|
||||
jpeg_decoder.cpp \
|
||||
jpeg_decoder.h \
|
||||
menu.cpp \
|
||||
menu.h \
|
||||
mjpeg_decoder.cpp \
|
||||
mjpeg_decoder.h \
|
||||
monitor.cpp \
|
||||
monitor.h \
|
||||
pixels_source.h \
|
||||
platform.h \
|
||||
playback_channel.cpp \
|
||||
process_loop.cpp \
|
||||
process_loop.h \
|
||||
read_write_mutex.h \
|
||||
record_channel.cpp \
|
||||
red_canvas_base.h \
|
||||
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.h \
|
||||
red_pixmap_sw.h \
|
||||
red_sw_canvas.cpp \
|
||||
red_sw_canvas.h \
|
||||
red_types.h \
|
||||
red_window.h \
|
||||
screen.cpp \
|
||||
screen.h \
|
||||
screen_layer.cpp \
|
||||
screen_layer.h \
|
||||
shared_cache.hpp \
|
||||
threads.cpp \
|
||||
threads.h \
|
||||
utils.cpp \
|
||||
utils.h \
|
||||
zlib_decoder.cpp \
|
||||
zlib_decoder.h \
|
||||
$(BUILT_SOURCES) \
|
||||
$(NULL)
|
||||
|
||||
if OS_WIN32
|
||||
spicec_SOURCES += \
|
||||
red_gdi_canvas.cpp \
|
||||
red_gdi_canvas.h \
|
||||
red_pixmap_gdi.h \
|
||||
windows/atomic_count.h \
|
||||
windows/event_sources_p.cpp \
|
||||
windows/main.cpp \
|
||||
windows/my_getopt.cpp \
|
||||
windows/named_pipe.cpp \
|
||||
windows/named_pipe.h \
|
||||
windows/pixels_source.cpp \
|
||||
windows/pixels_source_p.h \
|
||||
windows/platform.cpp \
|
||||
windows/platform_utils.cpp \
|
||||
windows/platform_utils.h \
|
||||
windows/playback.cpp \
|
||||
windows/playback.h \
|
||||
windows/record.cpp \
|
||||
windows/record.h \
|
||||
windows/red_drawable.cpp \
|
||||
windows/red_pixmap.cpp \
|
||||
windows/red_pixmap_gdi.cpp \
|
||||
windows/red_pixmap_sw.cpp \
|
||||
windows/red_window.cpp \
|
||||
windows/red_window_p.h \
|
||||
windows/resource.h \
|
||||
windows/stdint.h \
|
||||
windows/win_platform.h \
|
||||
$(NULL)
|
||||
|
||||
spicec_resource_LDADD = windows/redc.o
|
||||
|
||||
windows/redc.o: windows/redc.rc
|
||||
$(WINDRES) $< -o $@
|
||||
else
|
||||
spicec_SOURCES += \
|
||||
x11/atomic_count.h \
|
||||
x11/event_sources_p.cpp \
|
||||
x11/event_sources_p.h \
|
||||
x11/main.cpp \
|
||||
x11/named_pipe.cpp \
|
||||
x11/named_pipe.h \
|
||||
x11/pixels_source.cpp \
|
||||
x11/pixels_source_p.h \
|
||||
x11/platform.cpp \
|
||||
x11/platform_utils.cpp \
|
||||
x11/platform_utils.h \
|
||||
x11/playback.cpp \
|
||||
x11/playback.h \
|
||||
x11/record.cpp \
|
||||
x11/record.h \
|
||||
x11/red_drawable.cpp \
|
||||
x11/red_pixmap.cpp \
|
||||
x11/red_pixmap_sw.cpp \
|
||||
x11/red_window.cpp \
|
||||
x11/red_window_p.h \
|
||||
x11/res.cpp \
|
||||
x11/res.h \
|
||||
x11/resource.h \
|
||||
x11/x_icon.cpp \
|
||||
x11/x_icon.h \
|
||||
x11/x_platform.h \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
if OS_WIN32
|
||||
PLATFORM_INCLUDES=-I$(top_srcdir)/client/windows
|
||||
else
|
||||
PLATFORM_INCLUDES=-I$(top_srcdir)/client/x11
|
||||
endif
|
||||
|
||||
if SUPPORT_GUI
|
||||
spicec_SOURCES += \
|
||||
gui/gui.cpp \
|
||||
gui/gui.h \
|
||||
gui/resource_provider.cpp \
|
||||
gui/resource_provider.h \
|
||||
gui/softrenderer.cpp \
|
||||
gui/softrenderer.h \
|
||||
gui/softtexture.cpp \
|
||||
gui/softtexture.h \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
if SUPPORT_GL
|
||||
spicec_SOURCES += \
|
||||
red_gl_canvas.cpp \
|
||||
red_gl_canvas.h \
|
||||
red_pixmap_gl.h \
|
||||
$(NULL)
|
||||
|
||||
if !OS_WIN32
|
||||
spicec_SOURCES += x11/red_pixmap_gl.cpp
|
||||
endif
|
||||
|
||||
endif
|
||||
|
||||
if SUPPORT_SMARTCARD
|
||||
spicec_SOURCES += \
|
||||
smartcard_channel.cpp \
|
||||
smartcard_channel.h \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
|
||||
AM_CPPFLAGS = \
|
||||
-D__STDC_LIMIT_MACROS \
|
||||
$(PLATFORM_INCLUDES) \
|
||||
-I$(top_srcdir)/spice-common \
|
||||
$(ALSA_CFLAGS) \
|
||||
$(CEGUI_CFLAGS) \
|
||||
$(CEGUI06_CFLAGS) \
|
||||
$(CELT051_CFLAGS) \
|
||||
$(GL_CFLAGS) \
|
||||
$(MISC_X_CFLAGS) \
|
||||
$(PIXMAN_CFLAGS) \
|
||||
$(COMMON_CFLAGS) \
|
||||
$(SPICE_NONPKGCONFIG_CFLAGS) \
|
||||
$(SMARTCARD_CFLAGS) \
|
||||
$(SSL_CFLAGS) \
|
||||
$(XRANDR_CFLAGS) \
|
||||
$(XFIXES_CFLAGS) \
|
||||
$(WARN_CXXFLAGS) \
|
||||
$(XINERAMA_CFLAGS) \
|
||||
$(CXIMAGE_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
spicec_LDFLAGS = $(SPICEC_STATIC_LINKAGE_BSTATIC)
|
||||
|
||||
spicec_LDADD = \
|
||||
$(top_builddir)/spice-common/common/libspice-common.la \
|
||||
$(top_builddir)/spice-common/common/libspice-common-client.la \
|
||||
$(ALSA_LIBS) \
|
||||
$(CEGUI_LIBS) \
|
||||
$(CEGUI06_LIBS) \
|
||||
$(CELT051_LIBS) \
|
||||
$(GL_LIBS) \
|
||||
$(JPEG_LIBS) \
|
||||
$(MISC_X_LIBS) \
|
||||
$(PIXMAN_LIBS) \
|
||||
$(SMARTCARD_LIBS) \
|
||||
$(SPICE_NONPKGCONFIG_LIBS) \
|
||||
$(SSL_LIBS) \
|
||||
$(XFIXES_LIBS) \
|
||||
$(XRANDR_LIBS) \
|
||||
$(Z_LIBS) \
|
||||
$(XINERAMA_LIBS) \
|
||||
$(spicec_resource_LDADD) \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_DIST = \
|
||||
glz_decode_tmpl.c \
|
||||
x11/images/red_icon.c \
|
||||
x11/images/alt_image.c \
|
||||
gui/commonv2c.ttf.c \
|
||||
gui/commonwealth-10.font.c \
|
||||
gui/dejavu_sans-10.font.c \
|
||||
gui/dejavu_sans.ttf.c \
|
||||
gui/taharez_look.imageset.c \
|
||||
gui/taharez_look.looknfeel.c \
|
||||
gui/taharez_look.scheme.c \
|
||||
gui/taharez_look.tga.c \
|
||||
$(NULL)
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,413 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_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"
|
||||
#include "process_loop.h"
|
||||
#include "foreign_menu.h"
|
||||
#include "controller.h"
|
||||
|
||||
#ifdef USE_SMARTCARD
|
||||
struct SmartcardOptions;
|
||||
#endif
|
||||
class RedScreen;
|
||||
class Application;
|
||||
class ScreenLayer;
|
||||
class InfoLayer;
|
||||
class InputsHandler;
|
||||
class Monitor;
|
||||
class CmdLineParser;
|
||||
class Menu;
|
||||
|
||||
#ifdef USE_GUI
|
||||
class GUI;
|
||||
class GUITimer;
|
||||
class GUIBarrier;
|
||||
|
||||
#ifdef GUI_DEMO
|
||||
class TestTimer;
|
||||
#endif
|
||||
#endif // USE_GUI
|
||||
|
||||
|
||||
class ConnectedEvent: public Event {
|
||||
public:
|
||||
virtual void response(AbstractProcessLoop& events_loop);
|
||||
};
|
||||
|
||||
class DisconnectedEvent: public Event {
|
||||
public:
|
||||
DisconnectedEvent() : _error_code (SPICEC_ERROR_CODE_SUCCESS) {}
|
||||
DisconnectedEvent(int error_code) : _error_code (error_code) {}
|
||||
virtual void response(AbstractProcessLoop& events_loop);
|
||||
|
||||
private:
|
||||
int _error_code;
|
||||
};
|
||||
|
||||
class VisibilityEvent: public Event {
|
||||
public:
|
||||
VisibilityEvent(int screen_id) : _screen_id (screen_id) {}
|
||||
|
||||
virtual void response(AbstractProcessLoop& events_loop);
|
||||
|
||||
private:
|
||||
int _screen_id;
|
||||
};
|
||||
|
||||
struct MonitorInfo {
|
||||
int depth;
|
||||
SpicePoint size;
|
||||
SpicePoint position;
|
||||
};
|
||||
|
||||
class MonitorsQuery: public SyncEvent {
|
||||
public:
|
||||
MonitorsQuery() {}
|
||||
|
||||
virtual void do_response(AbstractProcessLoop& events_loop);
|
||||
std::vector<MonitorInfo>& get_monitors() {return _monitors;}
|
||||
|
||||
private:
|
||||
std::vector<MonitorInfo> _monitors;
|
||||
};
|
||||
|
||||
class SwitchHostEvent: public Event {
|
||||
public:
|
||||
SwitchHostEvent(const char* host, int port, int sport, const char* cert_subject);
|
||||
virtual void response(AbstractProcessLoop& events_loop);
|
||||
|
||||
private:
|
||||
std::string _host;
|
||||
int _port;
|
||||
int _sport;
|
||||
std::string _cert_subject;
|
||||
};
|
||||
|
||||
enum CanvasOption {
|
||||
CANVAS_OPTION_INVALID,
|
||||
CANVAS_OPTION_SW,
|
||||
#ifdef WIN32
|
||||
CANVAS_OPTION_GDI,
|
||||
#endif
|
||||
#ifdef USE_OPENGL
|
||||
CANVAS_OPTION_OGL_FBO,
|
||||
CANVAS_OPTION_OGL_PBUFF,
|
||||
#endif
|
||||
};
|
||||
|
||||
class StickyKeyTimer: public Timer {
|
||||
public:
|
||||
virtual void response(AbstractProcessLoop& events_loop);
|
||||
};
|
||||
|
||||
typedef struct StickyInfo {
|
||||
bool trace_is_on;
|
||||
bool sticky_mode;
|
||||
bool key_first_down; // True when (1) a potential sticky key is pressed,
|
||||
// and none of the other keys are pressed and (2) in the moment
|
||||
// of pressing, _sticky_mode is false. When the key is up
|
||||
// for the first time, it is set to false.
|
||||
bool key_down; // The physical state of the sticky key. Valid only till
|
||||
// stickiness is removed.
|
||||
RedKey key; // the key that is currently being traced, or,
|
||||
// if _sticky mode is on, the sticky key
|
||||
AutoRef<StickyKeyTimer> timer;
|
||||
} StickyInfo;
|
||||
|
||||
|
||||
typedef std::list<KeyHandler*> KeyHandlersStack;
|
||||
#ifdef USE_GUI
|
||||
typedef std::list<GUIBarrier*> GUIBarriers;
|
||||
#endif // USE_GUI
|
||||
|
||||
enum AppMenuItemType {
|
||||
APP_MENU_ITEM_TYPE_INVALID,
|
||||
APP_MENU_ITEM_TYPE_FOREIGN,
|
||||
APP_MENU_ITEM_TYPE_CONTROLLER,
|
||||
};
|
||||
|
||||
typedef struct AppMenuItem {
|
||||
AppMenuItemType type;
|
||||
int32_t conn_ref;
|
||||
uint32_t ext_id;
|
||||
} AppMenuItem;
|
||||
|
||||
typedef std::map<int, AppMenuItem> AppMenuItemMap;
|
||||
|
||||
class Application : public ProcessLoop,
|
||||
public Platform::EventListener,
|
||||
public Platform::DisplayModeListener,
|
||||
public ForeignMenuInterface,
|
||||
public ControllerInterface {
|
||||
public:
|
||||
|
||||
enum State {
|
||||
DISCONNECTED,
|
||||
CONNECTING,
|
||||
CONNECTED,
|
||||
VISIBILITY,
|
||||
DISCONECTING,
|
||||
};
|
||||
|
||||
#ifdef USE_GUI
|
||||
enum GuiMode {
|
||||
GUI_MODE_FULL,
|
||||
GUI_MODE_ACTIVE_SESSION,
|
||||
GUI_MODE_MINIMAL,
|
||||
};
|
||||
#endif // USE_GUI
|
||||
|
||||
Application();
|
||||
virtual ~Application();
|
||||
|
||||
int run();
|
||||
|
||||
void set_key_handler(KeyHandler& handler);
|
||||
void remove_key_handler(KeyHandler& handler);
|
||||
void set_mouse_handler(MouseHandler& handler);
|
||||
void remove_mouse_handler(MouseHandler& handler);
|
||||
void capture_mouse();
|
||||
void release_mouse_capture();
|
||||
RedScreen* find_screen(int id);
|
||||
RedScreen* get_screen(int id);
|
||||
|
||||
void on_screen_unlocked(RedScreen& screen);
|
||||
void on_screen_destroyed(int id, bool was_captured);
|
||||
void on_mouse_motion(int dx, int dy, int buttons_state);
|
||||
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_char(uint32_t ch);
|
||||
void on_deactivate_screen(RedScreen* screen);
|
||||
void on_activate_screen(RedScreen* screen);
|
||||
void on_start_screen_key_interception(RedScreen* screen);
|
||||
void on_stop_screen_key_interception(RedScreen* screen);
|
||||
virtual void on_start_running();
|
||||
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_disconnected(int spice_error_code);
|
||||
void on_disconnecting();
|
||||
void on_visibility_start(int screen_id);
|
||||
|
||||
void enter_full_screen();
|
||||
void exit_full_screen();
|
||||
bool toggle_full_screen();
|
||||
void resize_screen(RedScreen *screen, int width, int height);
|
||||
void minimize();
|
||||
void set_title(const std::string& title);
|
||||
void hide();
|
||||
void show();
|
||||
void external_show();
|
||||
void connect();
|
||||
void switch_host(const std::string& host, int port, int sport, const std::string& cert_subject);
|
||||
#ifdef USE_SMARTCARD
|
||||
void enable_smartcard(bool enable);
|
||||
#endif
|
||||
|
||||
const PeerConnectionOptMap& get_con_opt_map() {return _peer_con_opt;}
|
||||
const RedPeer::HostAuthOptions& get_host_auth_opt() { return _host_auth_opt;}
|
||||
const std::string& get_connection_ciphers() { return _con_ciphers;}
|
||||
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);
|
||||
|
||||
int get_foreign_menu_item_id(int32_t opaque_conn_ref, uint32_t msg_id);
|
||||
void clear_menu_items(int32_t opaque_conn_ref);
|
||||
void remove_menu_item(int item_id);
|
||||
void update_menu();
|
||||
|
||||
//controller interface begin
|
||||
void set_auto_display_res(bool auto_display_res);
|
||||
bool connect(const std::string& host, int port, int sport, const std::string& password);
|
||||
void disconnect();
|
||||
void quit();
|
||||
void show_me(bool full_screen);
|
||||
void hide_me();
|
||||
void set_hotkeys(const std::string& hotkeys);
|
||||
void set_default_hotkeys(void);
|
||||
int get_controller_menu_item_id(int32_t opaque_conn_ref, uint32_t msg_id);
|
||||
void set_menu(Menu* menu);
|
||||
void delete_menu();
|
||||
void beep();
|
||||
|
||||
#ifdef USE_GUI
|
||||
bool is_disconnect_allowed();
|
||||
void hide_gui();
|
||||
#endif
|
||||
|
||||
const std::string& get_host();
|
||||
int get_port();
|
||||
int get_sport();
|
||||
const std::string& get_password();
|
||||
//controller interface end
|
||||
|
||||
#ifdef GUI_DEMO
|
||||
void message_box_test();
|
||||
#endif
|
||||
|
||||
#ifdef USE_SMARTCARD
|
||||
const SmartcardOptions* get_smartcard_options() const {
|
||||
return _smartcard_options;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int main(int argc, char** argv, const char* version_str);
|
||||
|
||||
private:
|
||||
bool set_channels_security(CmdLineParser& parser, bool on, char *val, const char* arg0);
|
||||
bool set_channels_security(int port, int sport);
|
||||
bool set_connection_ciphers(const char* ciphers, const char* arg0);
|
||||
bool set_ca_file(const char* ca_file, const char* arg0);
|
||||
bool set_host_cert_subject(const char* subject, const char* arg0);
|
||||
bool set_enable_channels(CmdLineParser& parser, bool enable, char *val, const char* arg0);
|
||||
bool set_canvas_option(CmdLineParser& parser, char *val, const char* arg0);
|
||||
bool set_disabled_display_effects(CmdLineParser& parser, char *val, const char* arg0,
|
||||
DisplaySetting& disp_setting);
|
||||
void on_cmd_line_invalid_arg(const char* arg0, const char* what, const char* val);
|
||||
bool process_cmd_line(int argc, char** argv, bool& full_screen);
|
||||
void register_channels();
|
||||
void abort();
|
||||
void init_menu();
|
||||
void unpress_all();
|
||||
bool release_capture();
|
||||
bool do_connect();
|
||||
bool do_disconnect();
|
||||
void set_state(State);
|
||||
|
||||
void restore_screens_size();
|
||||
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(std::vector<SpicePoint> *sizes);
|
||||
void rearrange_monitors(bool force_capture,
|
||||
bool enter_full_screen,
|
||||
RedScreen* screen = NULL,
|
||||
std::vector<SpicePoint> *sizes = NULL);
|
||||
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_menu_item_id(AppMenuItemType type, int32_t conn_ref, uint32_t ext_id);
|
||||
int get_hotkeys_command();
|
||||
bool is_key_set_pressed(const HotkeySet& key_set);
|
||||
void do_on_key_up(RedKey key);
|
||||
void __remove_key_handler(KeyHandler& handler);
|
||||
|
||||
void show_info_layer();
|
||||
void hide_info_layer();
|
||||
#ifdef USE_GUI
|
||||
void attach_gui_barriers();
|
||||
void detach_gui_barriers();
|
||||
void show_gui();
|
||||
void create_gui_barrier(RedScreen& screen, int id);
|
||||
void destroyed_gui_barrier(int id);
|
||||
void destroyed_gui_barriers();
|
||||
#endif // USE_GUI
|
||||
|
||||
// returns the press value before operation (i.e., if it was already pressed)
|
||||
bool press_key(RedKey key);
|
||||
bool unpress_key(RedKey key);
|
||||
void reset_sticky();
|
||||
static bool is_sticky_trace_key(RedKey key);
|
||||
void sync_keyboard_modifiers();
|
||||
|
||||
static void init_logger();
|
||||
static void init_globals();
|
||||
static void init_platform_globals();
|
||||
static void cleanup_platform_globals();
|
||||
static void cleanup_globals();
|
||||
void init_remainder();
|
||||
|
||||
friend class DisconnectedEvent;
|
||||
friend class ConnectionErrorEvent;
|
||||
friend class MonitorsQuery;
|
||||
friend class AutoAbort;
|
||||
friend class StickyKeyTimer;
|
||||
|
||||
private:
|
||||
RedClient _client;
|
||||
PeerConnectionOptMap _peer_con_opt;
|
||||
RedPeer::HostAuthOptions _host_auth_opt;
|
||||
std::string _con_ciphers;
|
||||
std::vector<bool> _enabled_channels;
|
||||
std::vector<RedScreen*> _screens;
|
||||
RedScreen* _main_screen;
|
||||
bool _active;
|
||||
bool _full_screen;
|
||||
bool _changing_screens;
|
||||
bool _out_of_sync;
|
||||
int _exit_code;
|
||||
RedScreen* _active_screen;
|
||||
bool _keyboard_state[REDKEY_NUM_KEYS];
|
||||
int _num_keys_pressed;
|
||||
HotKeys _hot_keys;
|
||||
CommandsMap _commands_map;
|
||||
std::auto_ptr<InfoLayer> _info_layer;
|
||||
KeyHandler* _key_handler;
|
||||
KeyHandlersStack _key_handlers;
|
||||
MouseHandler* _mouse_handler;
|
||||
const MonitorsList* _monitors;
|
||||
std::string _title;
|
||||
bool _sys_key_intercept_mode;
|
||||
StickyInfo _sticky_info;
|
||||
std::vector<int> _canvas_types;
|
||||
AutoRef<Menu> _app_menu;
|
||||
AutoRef<ForeignMenu> _foreign_menu;
|
||||
bool _enable_controller;
|
||||
AutoRef<Controller> _controller;
|
||||
AppMenuItemMap _app_menu_items;
|
||||
#ifdef USE_GUI
|
||||
std::auto_ptr<GUI> _gui;
|
||||
AutoRef<GUITimer> _gui_timer;
|
||||
GUIBarriers _gui_barriers;
|
||||
GuiMode _gui_mode;
|
||||
#ifdef GUI_DEMO
|
||||
AutoRef<TestTimer> _gui_test_timer;
|
||||
#endif
|
||||
#endif // USE_GUI
|
||||
bool _during_host_switch;
|
||||
|
||||
State _state;
|
||||
#ifdef USE_SMARTCARD
|
||||
SmartcardOptions* _smartcard_options;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,107 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_AUDIO_CHANNELS
|
||||
#define _H_AUDIO_CHANNELS
|
||||
|
||||
#include "common/snd_codec.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();
|
||||
|
||||
protected:
|
||||
virtual void on_disconnect();
|
||||
|
||||
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_compressed_data(RedPeer::InMessage* message);
|
||||
void null_handler(RedPeer::InMessage* message);
|
||||
void disable();
|
||||
|
||||
void set_data_handler();
|
||||
|
||||
void clear();
|
||||
|
||||
private:
|
||||
WavePlaybackAbstract* _wave_player;
|
||||
uint32_t _mode;
|
||||
uint32_t _frame_bytes;
|
||||
SndCodec _codec;
|
||||
bool _playing;
|
||||
uint32_t _frame_count;
|
||||
};
|
||||
|
||||
class RecordChannel: public RedChannel, private Platform::RecordClient {
|
||||
public:
|
||||
RecordChannel(RedClient& client, uint32_t id);
|
||||
~RecordChannel(void);
|
||||
|
||||
bool abort(void);
|
||||
|
||||
static ChannelFactory& Factory();
|
||||
|
||||
protected:
|
||||
virtual void on_disconnect();
|
||||
|
||||
private:
|
||||
void handle_start(RedPeer::InMessage* message);
|
||||
void handle_stop(RedPeer::InMessage* message);
|
||||
|
||||
virtual void add_event_source(EventSources::File& event_source);
|
||||
virtual void remove_event_source(EventSources::File& event_source);
|
||||
virtual void add_event_source(EventSources::Trigger& event_source);
|
||||
virtual void remove_event_source(EventSources::Trigger& event_source);
|
||||
virtual void push_frame(uint8_t *frame);
|
||||
|
||||
void set_desired_mode(int frequency);
|
||||
void send_record_mode();
|
||||
void send_start_mark();
|
||||
void release_message(RecordSamplesMessage *message);
|
||||
RecordSamplesMessage * get_message();
|
||||
void clear();
|
||||
|
||||
private:
|
||||
WaveRecordAbstract* _wave_recorder;
|
||||
Mutex _messages_lock;
|
||||
std::list<RecordSamplesMessage *> _messages;
|
||||
int _mode;
|
||||
SndCodec _codec;
|
||||
uint32_t _frame_bytes;
|
||||
|
||||
uint8_t compressed_buf[SND_CODEC_MAX_COMPRESSED_BYTES];
|
||||
|
||||
friend class RecordSamplesMessage;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,43 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_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;
|
||||
};
|
||||
|
||||
class WaveRecordAbstract {
|
||||
public:
|
||||
WaveRecordAbstract() {}
|
||||
virtual ~WaveRecordAbstract() {}
|
||||
|
||||
|
||||
virtual void start() = 0;
|
||||
virtual void stop() = 0;
|
||||
virtual bool abort() = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
121
client/cache.hpp
121
client/cache.hpp
@ -1,121 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_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 Base = EmptyBase>
|
||||
class Cache : public Base {
|
||||
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) {
|
||||
spice_printerr("%s id %" PRIu64 ", double insert", Treat::name(), id);
|
||||
return;
|
||||
}
|
||||
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 %" PRIu64 ", 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 %" PRIu64 ", 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
|
||||
@ -1,186 +0,0 @@
|
||||
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
#include "canvas.h"
|
||||
#include "utils.h"
|
||||
#include "debug.h"
|
||||
|
||||
static SpiceCanvas* surfaces_cache_op_get(SpiceImageSurfaces *surfaces, uint32_t surface_id)
|
||||
{
|
||||
SurfacesCache* surfaces_cache = static_cast<SurfacesCache*>(surfaces);
|
||||
if (!surfaces_cache->exist(surface_id)) {
|
||||
return NULL;
|
||||
}
|
||||
return (*surfaces_cache)[surface_id]->get_internal_canvas();
|
||||
}
|
||||
|
||||
SurfacesCache::SurfacesCache()
|
||||
{
|
||||
static SpiceImageSurfacesOps surfaces_ops = {
|
||||
surfaces_cache_op_get,
|
||||
};
|
||||
ops = &surfaces_ops;
|
||||
}
|
||||
|
||||
bool SurfacesCache::exist(uint32_t surface_id)
|
||||
{
|
||||
return (this->count(surface_id) != 0);
|
||||
}
|
||||
|
||||
Canvas::Canvas(PixmapCache& pixmap_cache, PaletteCache& palette_cache,
|
||||
GlzDecoderWindow &glz_decoder_window, SurfacesCache &csurfaces)
|
||||
: _canvas (NULL)
|
||||
, _pixmap_cache (pixmap_cache)
|
||||
, _palette_cache (palette_cache)
|
||||
, _glz_decoder(glz_decoder_window, _glz_handler, _glz_debug)
|
||||
, _surfaces_cache(csurfaces)
|
||||
{
|
||||
}
|
||||
|
||||
Canvas::~Canvas()
|
||||
{
|
||||
/* _canvas is both set and destroyed by derived class */
|
||||
}
|
||||
|
||||
void Canvas::clear()
|
||||
{
|
||||
if (_canvas) {
|
||||
_canvas->ops->clear(_canvas);
|
||||
}
|
||||
}
|
||||
|
||||
void Canvas::begin_draw(SpiceMsgDisplayBase& base, int size, size_t min_size)
|
||||
{
|
||||
}
|
||||
|
||||
void Canvas::draw_fill(SpiceMsgDisplayDrawFill& fill, int size)
|
||||
{
|
||||
begin_draw(fill.base, size, sizeof(SpiceMsgDisplayDrawFill));
|
||||
_canvas->ops->draw_fill(_canvas, &fill.base.box, &fill.base.clip, &fill.data);
|
||||
touched_bbox(&fill.base.box);
|
||||
}
|
||||
|
||||
void Canvas::draw_text(SpiceMsgDisplayDrawText& text, int size)
|
||||
{
|
||||
begin_draw(text.base, size, sizeof(SpiceMsgDisplayDrawText));
|
||||
_canvas->ops->draw_text(_canvas, &text.base.box, &text.base.clip, &text.data);
|
||||
touched_bbox(&text.base.box);
|
||||
}
|
||||
|
||||
void Canvas::draw_opaque(SpiceMsgDisplayDrawOpaque& opaque, int size)
|
||||
{
|
||||
begin_draw(opaque.base, size, sizeof(SpiceMsgDisplayDrawOpaque));
|
||||
_canvas->ops->draw_opaque(_canvas, &opaque.base.box, &opaque.base.clip, &opaque.data);
|
||||
touched_bbox(&opaque.base.box);
|
||||
}
|
||||
|
||||
void Canvas::draw_copy(SpiceMsgDisplayDrawCopy& copy, int size)
|
||||
{
|
||||
begin_draw(copy.base, size, sizeof(SpiceMsgDisplayDrawCopy));
|
||||
_canvas->ops->draw_copy(_canvas, ©.base.box, ©.base.clip, ©.data);
|
||||
touched_bbox(©.base.box);
|
||||
}
|
||||
|
||||
void Canvas::draw_transparent(SpiceMsgDisplayDrawTransparent& transparent, int size)
|
||||
{
|
||||
begin_draw(transparent.base, size, sizeof(SpiceMsgDisplayDrawTransparent));
|
||||
_canvas->ops->draw_transparent(_canvas, &transparent.base.box, &transparent.base.clip, &transparent.data);
|
||||
touched_bbox(&transparent.base.box);
|
||||
}
|
||||
|
||||
void Canvas::draw_alpha_blend(SpiceMsgDisplayDrawAlphaBlend& alpha_blend, int size)
|
||||
{
|
||||
begin_draw(alpha_blend.base, size, sizeof(SpiceMsgDisplayDrawAlphaBlend));
|
||||
_canvas->ops->draw_alpha_blend(_canvas, &alpha_blend.base.box, &alpha_blend.base.clip, &alpha_blend.data);
|
||||
touched_bbox(&alpha_blend.base.box);
|
||||
}
|
||||
|
||||
void Canvas::draw_composite(SpiceMsgDisplayDrawComposite& composite, int size)
|
||||
{
|
||||
begin_draw(composite.base, size, sizeof(SpiceMsgDisplayDrawComposite));
|
||||
_canvas->ops->draw_composite(_canvas, &composite.base.box, &composite.base.clip, &composite.data);
|
||||
touched_bbox(&composite.base.box);
|
||||
}
|
||||
|
||||
void Canvas::copy_bits(SpiceMsgDisplayCopyBits& copy, int size)
|
||||
{
|
||||
begin_draw(copy.base, size, sizeof(SpiceMsgDisplayCopyBits));
|
||||
_canvas->ops->copy_bits(_canvas, ©.base.box, ©.base.clip, ©.src_pos);
|
||||
touched_bbox(©.base.box);
|
||||
}
|
||||
|
||||
void Canvas::draw_blend(SpiceMsgDisplayDrawBlend& blend, int size)
|
||||
{
|
||||
begin_draw(blend.base, size, sizeof(SpiceMsgDisplayDrawBlend));
|
||||
_canvas->ops->draw_blend(_canvas, &blend.base.box, &blend.base.clip, &blend.data);
|
||||
touched_bbox(&blend.base.box);
|
||||
}
|
||||
|
||||
void Canvas::draw_blackness(SpiceMsgDisplayDrawBlackness& blackness, int size)
|
||||
{
|
||||
begin_draw(blackness.base, size, sizeof(SpiceMsgDisplayDrawBlackness));
|
||||
_canvas->ops->draw_blackness(_canvas, &blackness.base.box, &blackness.base.clip, &blackness.data);
|
||||
touched_bbox(&blackness.base.box);
|
||||
}
|
||||
|
||||
void Canvas::draw_whiteness(SpiceMsgDisplayDrawWhiteness& whiteness, int size)
|
||||
{
|
||||
begin_draw(whiteness.base, size, sizeof(SpiceMsgDisplayDrawWhiteness));
|
||||
_canvas->ops->draw_whiteness(_canvas, &whiteness.base.box, &whiteness.base.clip, &whiteness.data);
|
||||
touched_bbox(&whiteness.base.box);
|
||||
}
|
||||
|
||||
void Canvas::draw_invers(SpiceMsgDisplayDrawInvers& invers, int size)
|
||||
{
|
||||
begin_draw(invers.base, size, sizeof(SpiceMsgDisplayDrawInvers));
|
||||
_canvas->ops->draw_invers(_canvas, &invers.base.box, &invers.base.clip, &invers.data);
|
||||
touched_bbox(&invers.base.box);
|
||||
}
|
||||
|
||||
void Canvas::draw_rop3(SpiceMsgDisplayDrawRop3& rop3, int size)
|
||||
{
|
||||
begin_draw(rop3.base, size, sizeof(SpiceMsgDisplayDrawRop3));
|
||||
_canvas->ops->draw_rop3(_canvas, &rop3.base.box, &rop3.base.clip, &rop3.data);
|
||||
touched_bbox(&rop3.base.box);
|
||||
}
|
||||
|
||||
void Canvas::draw_stroke(SpiceMsgDisplayDrawStroke& stroke, int size)
|
||||
{
|
||||
begin_draw(stroke.base, size, sizeof(SpiceMsgDisplayDrawStroke));
|
||||
_canvas->ops->draw_stroke(_canvas, &stroke.base.box, &stroke.base.clip, &stroke.data);
|
||||
touched_bbox(&stroke.base.box);
|
||||
}
|
||||
|
||||
void Canvas::put_image(
|
||||
#ifdef WIN32
|
||||
HDC dc,
|
||||
#endif
|
||||
const PixmapHeader& image, const SpiceRect& dest, const QRegion* clip)
|
||||
{
|
||||
_canvas->ops->put_image(_canvas,
|
||||
#ifdef WIN32
|
||||
dc,
|
||||
#endif
|
||||
&dest, image.data, image.width, image.height, image.stride,
|
||||
clip);
|
||||
touched_bbox(&dest);
|
||||
}
|
||||
352
client/canvas.h
352
client/canvas.h
@ -1,352 +0,0 @@
|
||||
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_CANVAS
|
||||
#define _H_CANVAS
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "common/region.h"
|
||||
#include "common/messages.h"
|
||||
#include "common/canvas_utils.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "debug.h"
|
||||
#include "cache.hpp"
|
||||
#include "shared_cache.hpp"
|
||||
#include "glz_decoded_image.h"
|
||||
#include "glz_decoder.h"
|
||||
#include "jpeg_decoder.h"
|
||||
#include "zlib_decoder.h"
|
||||
|
||||
enum CanvasType {
|
||||
CANVAS_TYPE_INVALID,
|
||||
CANVAS_TYPE_SW,
|
||||
CANVAS_TYPE_GL,
|
||||
CANVAS_TYPE_GDI,
|
||||
};
|
||||
|
||||
class PixmapCacheTreat {
|
||||
public:
|
||||
static inline pixman_image_t *get(pixman_image_t *surf)
|
||||
{
|
||||
return pixman_image_ref(surf);
|
||||
}
|
||||
|
||||
static inline void release(pixman_image_t *surf)
|
||||
{
|
||||
pixman_image_unref(surf);
|
||||
}
|
||||
|
||||
static const char* name() { return "pixmap";}
|
||||
};
|
||||
|
||||
class SpiceImageCacheBase;
|
||||
|
||||
typedef SharedCache<pixman_image_t, PixmapCacheTreat, 1024, SpiceImageCacheBase> PixmapCache;
|
||||
|
||||
class SpiceImageCacheBase {
|
||||
public:
|
||||
SpiceImageCache base;
|
||||
|
||||
static void op_put(SpiceImageCache *c, uint64_t id, pixman_image_t *surface)
|
||||
{
|
||||
PixmapCache* cache = reinterpret_cast<PixmapCache*>(c);
|
||||
cache->add(id, surface);
|
||||
}
|
||||
|
||||
static void op_put_lossy(SpiceImageCache *c, uint64_t id, pixman_image_t *surface)
|
||||
{
|
||||
PixmapCache* cache = reinterpret_cast<PixmapCache*>(c);
|
||||
cache->add(id, surface, TRUE);
|
||||
}
|
||||
|
||||
static void op_replace_lossy(SpiceImageCache *c, uint64_t id, pixman_image_t *surface)
|
||||
{
|
||||
PixmapCache* cache = reinterpret_cast<PixmapCache*>(c);
|
||||
cache->replace(id, surface);
|
||||
}
|
||||
|
||||
static pixman_image_t* op_get(SpiceImageCache *c, uint64_t id)
|
||||
{
|
||||
PixmapCache* cache = reinterpret_cast<PixmapCache*>(c);
|
||||
return cache->get(id);
|
||||
}
|
||||
|
||||
static pixman_image_t* op_get_lossless(SpiceImageCache *c, uint64_t id)
|
||||
{
|
||||
PixmapCache* cache = reinterpret_cast<PixmapCache*>(c);
|
||||
return cache->get_lossless(id);
|
||||
}
|
||||
|
||||
SpiceImageCacheBase()
|
||||
{
|
||||
static SpiceImageCacheOps cache_ops = {
|
||||
op_put,
|
||||
op_get,
|
||||
op_put_lossy,
|
||||
op_replace_lossy,
|
||||
op_get_lossless
|
||||
};
|
||||
base.ops = &cache_ops;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class CachedPalette {
|
||||
public:
|
||||
CachedPalette(SpicePalette* palette)
|
||||
: _refs(1)
|
||||
{
|
||||
int size = sizeof(SpicePalette) + palette->num_ents * sizeof(uint32_t);
|
||||
CachedPalette **ptr = (CachedPalette **)new uint8_t[size + sizeof(CachedPalette *)];
|
||||
*ptr = this;
|
||||
_palette = (SpicePalette*)(ptr + 1);
|
||||
memcpy(_palette, palette, size);
|
||||
}
|
||||
|
||||
CachedPalette* ref()
|
||||
{
|
||||
_refs++;
|
||||
return this;
|
||||
}
|
||||
|
||||
void unref()
|
||||
{
|
||||
if (--_refs == 0) {
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
static void unref(SpicePalette *pal)
|
||||
{
|
||||
CachedPalette **ptr = (CachedPalette **)pal;
|
||||
(*(ptr - 1))->unref();
|
||||
}
|
||||
|
||||
SpicePalette* palette() { return _palette;}
|
||||
|
||||
private:
|
||||
~CachedPalette()
|
||||
{
|
||||
delete[] (uint8_t *)((CachedPalette **)_palette - 1);
|
||||
}
|
||||
|
||||
private:
|
||||
int _refs;
|
||||
SpicePalette* _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";}
|
||||
};
|
||||
|
||||
class SpicePaletteCacheBase;
|
||||
typedef Cache<CachedPalette, PaletteCacheTreat, 1024, SpicePaletteCacheBase> PaletteCache;
|
||||
|
||||
class SpicePaletteCacheBase {
|
||||
public:
|
||||
SpicePaletteCache base;
|
||||
|
||||
static void op_put(SpicePaletteCache *c, SpicePalette *palette)
|
||||
{
|
||||
PaletteCache* cache = reinterpret_cast<PaletteCache*>(c);
|
||||
AutoRef<CachedPalette> cached_palette(new CachedPalette(palette));
|
||||
cache->add(palette->unique, *cached_palette);
|
||||
}
|
||||
|
||||
static SpicePalette* op_get(SpicePaletteCache *c, uint64_t id)
|
||||
{
|
||||
PaletteCache* cache = reinterpret_cast<PaletteCache*>(c);
|
||||
return cache->get(id)->palette();
|
||||
}
|
||||
|
||||
static void op_release (SpicePaletteCache *c,
|
||||
SpicePalette *palette)
|
||||
{
|
||||
CachedPalette::unref(palette);
|
||||
}
|
||||
|
||||
SpicePaletteCacheBase()
|
||||
{
|
||||
static SpicePaletteCacheOps cache_ops = {
|
||||
op_put,
|
||||
op_get,
|
||||
op_release
|
||||
};
|
||||
base.ops = &cache_ops;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* 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, pixman_image_t *surface)
|
||||
: GlzDecodedImage(id, win_head_id, data, size, bytes_per_pixel)
|
||||
, _surface (surface)
|
||||
{
|
||||
pixman_image_ref(_surface);
|
||||
}
|
||||
|
||||
virtual ~GlzDecodedSurface()
|
||||
{
|
||||
pixman_image_unref(_surface);
|
||||
}
|
||||
|
||||
private:
|
||||
pixman_image_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)
|
||||
{
|
||||
ASSERT(type == LZ_IMAGE_TYPE_RGB32 || type == LZ_IMAGE_TYPE_RGBA);
|
||||
|
||||
pixman_image_t *surface =
|
||||
alloc_lz_image_surface((LzDecodeUsrData *)opaque_usr_info,
|
||||
type == LZ_IMAGE_TYPE_RGBA ? PIXMAN_a8r8g8b8 : PIXMAN_x8r8g8b8,
|
||||
width, height, gross_pixels, top_down);
|
||||
uint8_t *data = (uint8_t *)pixman_image_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 SPICE_GNUC_NORETURN 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;
|
||||
|
||||
typedef std::map<uint32_t, Canvas*> SurfacesCanvasesMap;
|
||||
|
||||
class SurfacesCache: public SpiceImageSurfaces, public SurfacesCanvasesMap {
|
||||
public:
|
||||
SurfacesCache();
|
||||
bool exist(uint32_t surface_id);
|
||||
};
|
||||
|
||||
class Canvas {
|
||||
public:
|
||||
Canvas(PixmapCache& bits_cache, PaletteCache& palette_cache,
|
||||
GlzDecoderWindow &glz_decoder_window, SurfacesCache& csurfaces);
|
||||
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;
|
||||
|
||||
void clear();
|
||||
|
||||
void draw_fill(SpiceMsgDisplayDrawFill& fill, int size);
|
||||
void draw_text(SpiceMsgDisplayDrawText& text, int size);
|
||||
void draw_opaque(SpiceMsgDisplayDrawOpaque& opaque, int size);
|
||||
void draw_copy(SpiceMsgDisplayDrawCopy& copy, int size);
|
||||
void draw_transparent(SpiceMsgDisplayDrawTransparent& transparent, int size);
|
||||
void draw_alpha_blend(SpiceMsgDisplayDrawAlphaBlend& alpha_blend, int size);
|
||||
void copy_bits(SpiceMsgDisplayCopyBits& copy_bits, int size);
|
||||
void draw_blend(SpiceMsgDisplayDrawBlend& blend, int size);
|
||||
void draw_blackness(SpiceMsgDisplayDrawBlackness& blackness, int size);
|
||||
void draw_whiteness(SpiceMsgDisplayDrawWhiteness& whiteness, int size);
|
||||
void draw_invers(SpiceMsgDisplayDrawInvers& invers, int size);
|
||||
void draw_rop3(SpiceMsgDisplayDrawRop3& rop3, int size);
|
||||
void draw_stroke(SpiceMsgDisplayDrawStroke& stroke, int size);
|
||||
void draw_composite(SpiceMsgDisplayDrawComposite& composite, int size);
|
||||
|
||||
void put_image(
|
||||
#ifdef WIN32
|
||||
HDC dc,
|
||||
#endif
|
||||
const PixmapHeader& image,
|
||||
const SpiceRect& dest, const QRegion* clip);
|
||||
|
||||
virtual CanvasType get_pixmap_type() { return CANVAS_TYPE_INVALID; }
|
||||
|
||||
virtual SpiceCanvas *get_internal_canvas() { return _canvas; }
|
||||
|
||||
protected:
|
||||
virtual void touched_bbox(const SpiceRect *bbox) {};
|
||||
|
||||
PixmapCache& pixmap_cache() { return _pixmap_cache;}
|
||||
PaletteCache& palette_cache() { return _palette_cache;}
|
||||
SurfacesCache& surfaces_cache() { return _surfaces_cache;}
|
||||
|
||||
GlzDecoder& glz_decoder() {return _glz_decoder;}
|
||||
JpegDecoder& jpeg_decoder() { return _jpeg_decoder;}
|
||||
ZlibDecoder& zlib_decoder() { return _zlib_decoder;}
|
||||
|
||||
private:
|
||||
void begin_draw(SpiceMsgDisplayBase& base, int size, size_t min_size);
|
||||
|
||||
protected:
|
||||
SpiceCanvas* _canvas;
|
||||
|
||||
private:
|
||||
PixmapCache& _pixmap_cache;
|
||||
PaletteCache& _palette_cache;
|
||||
|
||||
GlzDecodeSurfaceHandler _glz_handler;
|
||||
GlzDecoderCanvasDebug _glz_debug;
|
||||
GlzDecoder _glz_decoder;
|
||||
|
||||
JpegDecoder _jpeg_decoder;
|
||||
ZlibDecoder _zlib_decoder;
|
||||
|
||||
SurfacesCache& _surfaces_cache;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@ -1,388 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
Author:
|
||||
yhalperi@redhat.com
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
#include "client_net_socket.h"
|
||||
#include "debug.h"
|
||||
#include <spice/error_codes.h>
|
||||
#include "utils.h"
|
||||
|
||||
ClientNetSocket::ClientNetSocket(uint16_t id, const struct in_addr& dst_addr, uint16_t dst_port,
|
||||
ProcessLoop& process_loop, EventHandler& event_handler)
|
||||
: _id (id)
|
||||
, _local_addr (dst_addr)
|
||||
, _local_port (dst_port)
|
||||
, _peer (INVALID_SOCKET)
|
||||
, _process_loop (process_loop)
|
||||
, _event_handler (event_handler)
|
||||
, _num_recv_tokens (0)
|
||||
, _send_message (NULL)
|
||||
, _send_pos (0)
|
||||
, _status (SOCKET_STATUS_CLOSED)
|
||||
, _fin_pending (false)
|
||||
, _close_pending (false)
|
||||
{
|
||||
}
|
||||
|
||||
ClientNetSocket::~ClientNetSocket()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
bool ClientNetSocket::connect(uint32_t recv_tokens)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
int no_delay;
|
||||
|
||||
|
||||
ASSERT(_peer == INVALID_SOCKET && _status == SOCKET_STATUS_CLOSED);
|
||||
|
||||
addr.sin_port = _local_port;
|
||||
addr.sin_addr.s_addr = _local_addr.s_addr;
|
||||
addr.sin_family = AF_INET;
|
||||
|
||||
if ((_peer = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {
|
||||
int err = sock_error();
|
||||
THROW("%s: failed to create socket: %s", __FUNCTION__, sock_err_message(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("connect to ip=%s port=%d (connection_id=%d)",
|
||||
inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), _id);
|
||||
|
||||
if (::connect(_peer, (struct sockaddr*)&addr, sizeof(sockaddr_in)) == SOCKET_ERROR) {
|
||||
int err = sock_error();
|
||||
closesocket(_peer);
|
||||
_peer = INVALID_SOCKET;
|
||||
LOG_INFO("connect to ip=%s port=%d failed %s (connection_id=%d)",
|
||||
inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), sock_err_message(err), _id);
|
||||
return false;
|
||||
}
|
||||
|
||||
_process_loop.add_socket(*this);
|
||||
_status = SOCKET_STATUS_OPEN;
|
||||
_num_recv_tokens = recv_tokens;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClientNetSocket::push_disconnect()
|
||||
{
|
||||
if ((_status == SOCKET_STATUS_CLOSED) || _close_pending) {
|
||||
THROW("%s: disconnect attempt for disconnected socket %s %d", __FUNCTION__,
|
||||
inet_ntoa(_local_addr), ntohs(_local_port));
|
||||
}
|
||||
|
||||
_close_pending = true;
|
||||
|
||||
if (!during_send()) {
|
||||
close_and_tell();
|
||||
}
|
||||
}
|
||||
|
||||
void ClientNetSocket::push_fin()
|
||||
{
|
||||
if ((_status == SOCKET_STATUS_OPEN) || (_status == SOCKET_STATUS_RECEIVED_FIN)) {
|
||||
_fin_pending = true;
|
||||
if (!during_send()) {
|
||||
try {
|
||||
apply_guest_fin();
|
||||
} catch (ClientNetSocket::ShutdownExcpetion&) {
|
||||
close_and_tell();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
THROW("%s: unexpected fin connection_id=%d (status=%d)", __FUNCTION__,
|
||||
_id, _status);
|
||||
}
|
||||
}
|
||||
|
||||
void ClientNetSocket::add_recv_tokens(uint32_t num_tokens)
|
||||
{
|
||||
if ((_status == SOCKET_STATUS_CLOSED) || _close_pending) {
|
||||
THROW("%s: ack attempt for disconnected socket connection_id=%d", __FUNCTION__,
|
||||
_id);
|
||||
}
|
||||
|
||||
_num_recv_tokens += num_tokens;
|
||||
|
||||
// recv might have not been called because tokens weren't available
|
||||
if (_num_recv_tokens && (_num_recv_tokens == num_tokens)) {
|
||||
if (can_receive()) {
|
||||
receive();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClientNetSocket::push_send(SendBuffer& buf)
|
||||
{
|
||||
if (!can_send()) {
|
||||
THROW("%s: unexpected send attempt for connection_id=%d (status = %d)",
|
||||
__FUNCTION__, _id, _status);
|
||||
}
|
||||
|
||||
if (_fin_pending || _close_pending) {
|
||||
THROW("%s: unexpected send attempt for connection_id=%d - shutdown send pending",
|
||||
__FUNCTION__, _id);
|
||||
}
|
||||
|
||||
_send_messages.push_back(buf.ref());
|
||||
send();
|
||||
}
|
||||
|
||||
void ClientNetSocket::on_event()
|
||||
{
|
||||
if (can_send()) {
|
||||
send();
|
||||
}
|
||||
|
||||
if (!during_send()) {
|
||||
if (_close_pending) {
|
||||
close_and_tell();
|
||||
} else if (_fin_pending) {
|
||||
apply_guest_fin();
|
||||
}
|
||||
}
|
||||
|
||||
if (can_receive()) {
|
||||
receive();
|
||||
}
|
||||
}
|
||||
|
||||
void ClientNetSocket::apply_guest_fin()
|
||||
{
|
||||
if (_status == SOCKET_STATUS_OPEN) {
|
||||
if (shutdown(_peer, SHUT_WR) == SOCKET_ERROR) {
|
||||
int err = sock_error();
|
||||
LOG_INFO("shutdown in connection_id=%d failed %s", _id, sock_err_message(err));
|
||||
throw ClientNetSocket::ShutdownExcpetion();
|
||||
}
|
||||
|
||||
_fin_pending = false;
|
||||
_status = SOCKET_STATUS_SENT_FIN;
|
||||
} else if (_status == SOCKET_STATUS_RECEIVED_FIN) {
|
||||
close_and_tell();
|
||||
}
|
||||
}
|
||||
|
||||
void ClientNetSocket::handle_client_fin()
|
||||
{
|
||||
if (_status == SOCKET_STATUS_OPEN) {
|
||||
_status = SOCKET_STATUS_RECEIVED_FIN;
|
||||
_event_handler.on_socket_fin_recv(*this);
|
||||
} else if (_status == SOCKET_STATUS_SENT_FIN) {
|
||||
close_and_tell();
|
||||
}
|
||||
}
|
||||
|
||||
inline bool ClientNetSocket::during_send()
|
||||
{
|
||||
return ((!_send_messages.empty()) || _send_message);
|
||||
}
|
||||
|
||||
inline bool ClientNetSocket::can_send()
|
||||
{
|
||||
return ((_status == SOCKET_STATUS_OPEN) || (_status == SOCKET_STATUS_RECEIVED_FIN));
|
||||
}
|
||||
|
||||
inline bool ClientNetSocket::can_receive()
|
||||
{
|
||||
return ((_status == SOCKET_STATUS_OPEN) || (_status == SOCKET_STATUS_SENT_FIN));
|
||||
}
|
||||
|
||||
void ClientNetSocket::send_message_done()
|
||||
{
|
||||
_send_message->unref();
|
||||
_send_message = NULL;
|
||||
_send_pos = 0;
|
||||
_event_handler.on_socket_message_send_done(*this);
|
||||
}
|
||||
|
||||
void ClientNetSocket::send()
|
||||
{
|
||||
ASSERT(_peer != INVALID_SOCKET);
|
||||
try {
|
||||
if (_send_message) {
|
||||
_send_pos += send_buf(_send_message->data() + _send_pos,
|
||||
_send_message->size() - _send_pos);
|
||||
|
||||
if (_send_pos != _send_message->size()) {
|
||||
return;
|
||||
} else {
|
||||
send_message_done();
|
||||
}
|
||||
}
|
||||
|
||||
while (!_send_messages.empty()) {
|
||||
_send_message = _send_messages.front();
|
||||
_send_messages.pop_front();
|
||||
_send_pos = send_buf(_send_message->data(), _send_message->size());
|
||||
if (_send_pos != _send_message->size()) {
|
||||
return;
|
||||
} else {
|
||||
send_message_done();
|
||||
}
|
||||
}
|
||||
} catch (ClientNetSocket::SendException&) {
|
||||
close_and_tell();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t ClientNetSocket::send_buf(const uint8_t* buf, uint32_t size)
|
||||
{
|
||||
const uint8_t* pos = buf;
|
||||
ASSERT(_peer != INVALID_SOCKET);
|
||||
while (size) {
|
||||
int now;
|
||||
if ((now = ::send(_peer, (char*)pos, size, MSG_NOSIGNAL)) == SOCKET_ERROR) {
|
||||
int err = sock_error();
|
||||
if (err == WOULDBLOCK_ERR) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (err == INTERRUPTED_ERR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG_INFO("send in connection_id=%d failed %s", _id, sock_err_message(err));
|
||||
throw ClientNetSocket::SendException();
|
||||
}
|
||||
size -= now;
|
||||
pos += now;
|
||||
}
|
||||
return pos - buf;
|
||||
}
|
||||
|
||||
void ClientNetSocket::receive()
|
||||
{
|
||||
ASSERT(_peer != INVALID_SOCKET);
|
||||
bool shutdown;
|
||||
while (_num_recv_tokens) {
|
||||
ReceiveBuffer& rcv_buf = alloc_receive_buffer();
|
||||
uint32_t size;
|
||||
|
||||
try {
|
||||
size = receive_buf(rcv_buf.buf(), rcv_buf.buf_max_size(), shutdown);
|
||||
} catch (ClientNetSocket::ReceiveException&) {
|
||||
rcv_buf.release_buf();
|
||||
close_and_tell();
|
||||
return;
|
||||
}
|
||||
|
||||
if (size) {
|
||||
rcv_buf.set_buf_size(size);
|
||||
_num_recv_tokens--;
|
||||
_event_handler.on_socket_message_recv_done(*this, rcv_buf);
|
||||
} else {
|
||||
rcv_buf.release_buf();
|
||||
}
|
||||
|
||||
if (shutdown) {
|
||||
handle_client_fin();
|
||||
return;
|
||||
}
|
||||
|
||||
if (size < rcv_buf.buf_max_size()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t ClientNetSocket::receive_buf(uint8_t* buf, uint32_t max_size, bool& shutdown)
|
||||
{
|
||||
uint8_t* pos = buf;
|
||||
ASSERT(_peer != INVALID_SOCKET);
|
||||
|
||||
shutdown = false;
|
||||
|
||||
while (max_size) {
|
||||
int now;
|
||||
if ((now = ::recv(_peer, (char*)pos, max_size, 0)) <= 0) {
|
||||
if (now == 0) {
|
||||
shutdown = true;
|
||||
break; // a case where fin is received, but before that, there is a msg
|
||||
}
|
||||
|
||||
int err = sock_error();
|
||||
|
||||
if (err == WOULDBLOCK_ERR) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (err == INTERRUPTED_ERR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG_INFO("receive in connection_id=%d failed errno=%s", _id, sock_err_message(err));
|
||||
throw ClientNetSocket::ReceiveException();
|
||||
}
|
||||
max_size -= now;
|
||||
pos += now;
|
||||
}
|
||||
|
||||
return (pos - buf);
|
||||
}
|
||||
|
||||
void ClientNetSocket::release_wait_send_messages()
|
||||
{
|
||||
if (_send_message) {
|
||||
_send_message->unref();
|
||||
_send_message = NULL;
|
||||
_send_pos = 0;
|
||||
}
|
||||
|
||||
while (!_send_messages.empty()) {
|
||||
_send_messages.front()->unref();
|
||||
_send_messages.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
void ClientNetSocket::close()
|
||||
{
|
||||
release_wait_send_messages();
|
||||
apply_disconnect();
|
||||
}
|
||||
|
||||
void ClientNetSocket::close_and_tell()
|
||||
{
|
||||
close();
|
||||
_event_handler.on_socket_disconnect(*this);
|
||||
}
|
||||
|
||||
void ClientNetSocket::apply_disconnect()
|
||||
{
|
||||
if (_peer != INVALID_SOCKET) {
|
||||
_process_loop.remove_socket(*this);
|
||||
closesocket(_peer);
|
||||
_peer = INVALID_SOCKET;
|
||||
LOG_INFO("closing connection_id=%d", _id);
|
||||
}
|
||||
_status = SOCKET_STATUS_CLOSED;
|
||||
_close_pending = false;
|
||||
_fin_pending = false;
|
||||
}
|
||||
@ -1,154 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Author:
|
||||
yhalperi@redhat.com
|
||||
*/
|
||||
|
||||
#ifndef _H_CLIENT_NET_SOCKET
|
||||
#define _H_CLIENT_NET_SOCKET
|
||||
|
||||
#include "platform_utils.h"
|
||||
#include "common.h"
|
||||
#include "process_loop.h"
|
||||
|
||||
/* interface for connections inside client LAN */
|
||||
|
||||
typedef enum {
|
||||
SOCKET_STATUS_OPEN,
|
||||
SOCKET_STATUS_SENT_FIN,
|
||||
SOCKET_STATUS_RECEIVED_FIN,
|
||||
SOCKET_STATUS_CLOSED,
|
||||
} SocketStatus;
|
||||
|
||||
class ClientNetSocket: public EventSources::Socket {
|
||||
public:
|
||||
class ReceiveBuffer;
|
||||
class SendBuffer;
|
||||
|
||||
class EventHandler;
|
||||
|
||||
class SendException {};
|
||||
class ReceiveException {};
|
||||
class ShutdownExcpetion {};
|
||||
|
||||
ClientNetSocket(uint16_t id, const struct in_addr& dst_addr, uint16_t dst_port,
|
||||
ProcessLoop& process_loop, ClientNetSocket::EventHandler& event_handler);
|
||||
virtual ~ClientNetSocket();
|
||||
|
||||
bool connect(uint32_t recv_tokens);
|
||||
void push_disconnect();
|
||||
void push_fin();
|
||||
void push_send(SendBuffer& buf);
|
||||
void add_recv_tokens(uint32_t num_tokens);
|
||||
|
||||
bool is_connected() {return _status != SOCKET_STATUS_CLOSED;}
|
||||
|
||||
inline uint16_t id() {return _id;}
|
||||
inline const struct in_addr& local_addr() {return _local_addr;}
|
||||
inline uint16_t local_port() {return _local_port;}
|
||||
|
||||
/* EventSources::Socket interface */
|
||||
void on_event();
|
||||
int get_socket() {return _peer;}
|
||||
|
||||
protected:
|
||||
virtual ReceiveBuffer& alloc_receive_buffer() = 0;
|
||||
|
||||
private:
|
||||
void send();
|
||||
void receive();
|
||||
|
||||
uint32_t send_buf(const uint8_t* buf, uint32_t size);
|
||||
uint32_t receive_buf(uint8_t* buf, uint32_t max_size, bool& shutdown);
|
||||
|
||||
bool can_receive();
|
||||
bool can_send();
|
||||
|
||||
void apply_disconnect();
|
||||
void apply_guest_fin();
|
||||
|
||||
void close();
|
||||
void close_and_tell();
|
||||
|
||||
void handle_client_fin();
|
||||
|
||||
bool during_send();
|
||||
void release_wait_send_messages();
|
||||
void clear();
|
||||
void send_message_done();
|
||||
|
||||
private:
|
||||
uint16_t _id;
|
||||
struct in_addr _local_addr;
|
||||
uint16_t _local_port;
|
||||
|
||||
SOCKET _peer;
|
||||
|
||||
ProcessLoop& _process_loop;
|
||||
|
||||
EventHandler& _event_handler;
|
||||
|
||||
uint32_t _num_recv_tokens;
|
||||
|
||||
std::list<SendBuffer*> _send_messages;
|
||||
SendBuffer* _send_message;
|
||||
uint32_t _send_pos;
|
||||
|
||||
SocketStatus _status;
|
||||
bool _fin_pending;
|
||||
bool _close_pending;
|
||||
};
|
||||
|
||||
class ClientNetSocket::ReceiveBuffer {
|
||||
public:
|
||||
ReceiveBuffer() {}
|
||||
|
||||
virtual uint8_t* buf() = 0;
|
||||
virtual uint32_t buf_max_size() = 0;
|
||||
virtual void set_buf_size(uint32_t size) = 0;
|
||||
virtual void release_buf() = 0;
|
||||
|
||||
protected:
|
||||
virtual ~ReceiveBuffer() {}
|
||||
};
|
||||
|
||||
class ClientNetSocket::SendBuffer {
|
||||
public:
|
||||
SendBuffer() {};
|
||||
|
||||
virtual const uint8_t* data() = 0;
|
||||
virtual uint32_t size() = 0;
|
||||
virtual ClientNetSocket::SendBuffer* ref() = 0;
|
||||
virtual void unref() = 0;
|
||||
|
||||
protected:
|
||||
virtual ~SendBuffer() {}
|
||||
};
|
||||
|
||||
class ClientNetSocket::EventHandler {
|
||||
public:
|
||||
EventHandler() {}
|
||||
virtual ~EventHandler() {}
|
||||
virtual void on_socket_message_recv_done(ClientNetSocket& sckt, ReceiveBuffer& buf) = 0;
|
||||
virtual void on_socket_message_send_done(ClientNetSocket& sckt) = 0;
|
||||
virtual void on_socket_disconnect(ClientNetSocket& sckt) = 0;
|
||||
virtual void on_socket_fin_recv(ClientNetSocket& sckt) = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@ -1,518 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "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)
|
||||
, separator (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_separator (0)
|
||||
, _positional_args (allow_positional_args)
|
||||
, _done (false)
|
||||
{
|
||||
//Enables multiple instantiations. One at a time, not thread-safe.
|
||||
optind = 1;
|
||||
opterr = 1;
|
||||
optopt = 0;
|
||||
optarg = 0;
|
||||
}
|
||||
|
||||
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_AVAILABLE) {
|
||||
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 required_arg, char short_name)
|
||||
{
|
||||
if (id < OPTION_FIRST_AVAILABLE) {
|
||||
THROW("invalid id");
|
||||
}
|
||||
|
||||
if (arg_name.size() == 0) {
|
||||
THROW("invalid arg name");
|
||||
}
|
||||
|
||||
add_private(id, name, short_name, required_arg ? REQUIRED_ARGUMENT : OPTIONAL_ARGUMENT, help,
|
||||
arg_name);
|
||||
}
|
||||
|
||||
void CmdLineParser::set_multi(int id, char separator)
|
||||
{
|
||||
if (_argv) {
|
||||
THROW("unexpected");
|
||||
}
|
||||
|
||||
if (!ispunct(separator)) {
|
||||
THROW("invalid separator");
|
||||
}
|
||||
|
||||
Option* opt = find(id);
|
||||
|
||||
if (!opt) {
|
||||
THROW("not found");
|
||||
}
|
||||
|
||||
if (opt->type == NO_ARGUMENT) {
|
||||
THROW("can't set multi for option without argument");
|
||||
}
|
||||
|
||||
opt->separator = separator;
|
||||
}
|
||||
|
||||
void CmdLineParser::set_required(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 separator)
|
||||
{
|
||||
if (!optarg) {
|
||||
return NULL;
|
||||
}
|
||||
_multi_args = new char[strlen(optarg) + 1];
|
||||
_multi_separator = separator;
|
||||
strcpy(_multi_args, optarg);
|
||||
if ((_multi_next = strchr(_multi_args, _multi_separator))) {
|
||||
*(_multi_next++) = 0;
|
||||
}
|
||||
return _multi_args;
|
||||
}
|
||||
|
||||
char* CmdLineParser::next_multi()
|
||||
{
|
||||
if (!_multi_next) {
|
||||
_multi_separator = 0;
|
||||
delete[] _multi_args;
|
||||
_multi_args = NULL;
|
||||
return NULL;
|
||||
}
|
||||
char* ret = _multi_next;
|
||||
if ((_multi_next = strchr(_multi_next, _multi_separator))) {
|
||||
*(_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 && optarg[-1] != '=')
|
||||
? optind - 2
|
||||
: optind - 1;
|
||||
std::string cmd_name(_argv[name_pos] + 2);
|
||||
if (cmd_name.find(opt_obj->name) != 0) {
|
||||
Platform::term_printf("%s: invalid abbreviated option '--%s'\n", _argv[0], cmd_name.c_str());
|
||||
return OPTION_ERROR;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (opt_obj->separator) {
|
||||
*val = start_multi(optarg, opt_obj->separator);
|
||||
} else {
|
||||
*val = optarg;
|
||||
}
|
||||
opt_obj->is_set = true;
|
||||
return opt_obj->id;
|
||||
}
|
||||
case -1: {
|
||||
*val = NULL;
|
||||
if (!_positional_args && optind != _argc) {
|
||||
Platform::term_printf("%s: unexpected positional arguments\n", _argv[0]);
|
||||
return OPTION_ERROR;
|
||||
}
|
||||
if ((opt_obj = find_missing_opt())) {
|
||||
Platform::term_printf("%s: option --%s is required\n", _argv[0], opt_obj->name.c_str());
|
||||
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) {
|
||||
Platform::term_printf("%s: invalid option '--%s'\n", _argv[0], cmd_name.c_str());
|
||||
return OPTION_ERROR;
|
||||
}
|
||||
#endif
|
||||
Platform::term_printf("%s: option --%s requires an argument\n",
|
||||
_argv[0], opt_obj->name.c_str());
|
||||
} else if (optopt == 0) {
|
||||
Platform::term_printf("%s: invalid option '%s'\n", _argv[0], _argv[optind - 1]);
|
||||
} else if ((opt_obj = find((char)optopt))) {
|
||||
Platform::term_printf("%s: option '-%c' requires an argument\n",
|
||||
_argv[0], opt_obj->short_name);
|
||||
} else {
|
||||
Platform::term_printf("%s: invalid option '-%c'\n", _argv[0], char(optopt));
|
||||
}
|
||||
return OPTION_ERROR;
|
||||
default:
|
||||
if (opt > 255 || !(opt_obj = find((char)opt))) {
|
||||
*val = NULL;
|
||||
return OPTION_ERROR;
|
||||
}
|
||||
if (opt_obj->separator) {
|
||||
*val = start_multi(optarg, opt_obj->separator);
|
||||
} 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++];
|
||||
}
|
||||
|
||||
void CmdLineParser::show_help()
|
||||
{
|
||||
static const int HELP_START_POS = 30;
|
||||
static const unsigned HELP_WIDTH = 80 - HELP_START_POS;
|
||||
std::ostringstream os;
|
||||
|
||||
os << _argv[0] << " - " << _description.c_str() << "\n\noptions:\n\n";
|
||||
|
||||
Options::iterator iter = _options.begin();
|
||||
for (; iter != _options.end(); ++iter) {
|
||||
CmdLineParser::Option* opt = *iter;
|
||||
|
||||
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->separator) {
|
||||
os << opt->arg_name << opt->separator << 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) {
|
||||
size_t last_space, now = HELP_WIDTH;
|
||||
std::string sub;
|
||||
sub.append(line, 0, now);
|
||||
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));
|
||||
}
|
||||
|
||||
os << "\n";
|
||||
Platform::term_printf("%s", os.str().c_str());
|
||||
}
|
||||
@ -1,104 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_CMD_LINE_PARSER
|
||||
#define _H_CMD_LINE_PARSER
|
||||
|
||||
class CmdLineParser {
|
||||
public:
|
||||
|
||||
enum {
|
||||
OPTION_ERROR = -1,
|
||||
OPTION_DONE = 0,
|
||||
OPTION_HELP = 256,
|
||||
OPTION_FIRST_AVAILABLE,
|
||||
};
|
||||
|
||||
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 required_arg, char short_name = 0);
|
||||
void set_multi(int id, char separator);
|
||||
void set_required(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 separator);
|
||||
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 separator;
|
||||
};
|
||||
|
||||
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_separator;
|
||||
bool _positional_args;
|
||||
bool _done;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,92 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_COMMON
|
||||
#define _H_COMMON
|
||||
|
||||
#ifndef WIN32
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32_WCE
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <inttypes.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <exception>
|
||||
#include <list>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#ifdef __GNUC__
|
||||
#define UNICODE 1
|
||||
#define _UNICODE 1
|
||||
#if !defined __MINGW32__
|
||||
#define WINVER 0x0501
|
||||
#define swprintf_s(_str, _len, _fmt, ...) \
|
||||
swprintf(_str, _fmt, ## __VA_ARGS__)
|
||||
#define vsnprintf_s(_str, _len1, _len2, _fmt, _valist) \
|
||||
vsnprintf(_str, _len2, _fmt, _valist)
|
||||
#define _ftime_s(_t) _ftime(_t)
|
||||
#endif
|
||||
#endif
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
|
||||
#ifndef __GNUC__
|
||||
#pragma warning(disable:4355)
|
||||
#pragma warning(disable:4996)
|
||||
#pragma warning(disable:4200)
|
||||
|
||||
extern const char* PACKAGE_VERSION;
|
||||
#endif
|
||||
|
||||
#define strcasecmp stricmp
|
||||
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <X11/X.h>
|
||||
#ifdef USE_OPENGL
|
||||
#include <GL/glx.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
#if __SIZEOF_POINTER__ == 8
|
||||
#define RED64
|
||||
#endif
|
||||
#elif defined(_WIN64)
|
||||
#define RED64
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && !defined(PRIu64)
|
||||
#define PRIu64 "I64u"
|
||||
#endif
|
||||
|
||||
#include <spice/types.h>
|
||||
#include "red_types.h"
|
||||
|
||||
#endif
|
||||
@ -1,448 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
#include "controller.h"
|
||||
#include <spice/controller_prot.h>
|
||||
#include "cmd_line_parser.h"
|
||||
#include "menu.h"
|
||||
#include "utils.h"
|
||||
#include "debug.h"
|
||||
#include "platform.h"
|
||||
|
||||
|
||||
#ifdef WIN32
|
||||
#define PIPE_NAME_MAX_LEN 50
|
||||
#define PIPE_NAME "SpiceController-%lu"
|
||||
#endif
|
||||
|
||||
Controller::Controller(ControllerInterface *handler)
|
||||
: _handler (handler)
|
||||
, _exclusive (false)
|
||||
, _refs (1)
|
||||
{
|
||||
ASSERT(_handler);
|
||||
#ifdef WIN32
|
||||
char pipe_name[PIPE_NAME_MAX_LEN];
|
||||
snprintf(pipe_name, PIPE_NAME_MAX_LEN, PIPE_NAME, Platform::get_process_id());
|
||||
#else
|
||||
const char *pipe_name = getenv("SPICE_XPI_SOCKET");
|
||||
if (!pipe_name) {
|
||||
LOG_ERROR("Failed to get a controller connection (SPICE_XPI_SOCKET)");
|
||||
throw Exception("Failed to get a controller connection (SPICE_XPI_SOCKET)");
|
||||
}
|
||||
#endif
|
||||
LOG_INFO("Creating a controller connection %s", pipe_name);
|
||||
_pipe = NamedPipe::create(pipe_name, *this);
|
||||
if (!_pipe) {
|
||||
LOG_ERROR("Failed to create a controller connection");
|
||||
}
|
||||
}
|
||||
|
||||
Controller::~Controller()
|
||||
{
|
||||
std::map<NamedPipe::ConnectionRef, ControllerConnection*>::const_iterator conn;
|
||||
for (conn = _connections.begin(); conn != _connections.end(); ++conn) {
|
||||
conn->second->reset_handler();
|
||||
delete conn->second;
|
||||
}
|
||||
if (_pipe) {
|
||||
NamedPipe::destroy(_pipe);
|
||||
}
|
||||
}
|
||||
|
||||
NamedPipe::ConnectionInterface& Controller::create()
|
||||
{
|
||||
ControllerConnection *conn = new ControllerConnection(_handler, *this);
|
||||
|
||||
if (conn == NULL) {
|
||||
throw Exception("Error allocating a new controller connection");
|
||||
}
|
||||
return *conn;
|
||||
}
|
||||
|
||||
bool Controller::set_exclusive(bool exclusive)
|
||||
{
|
||||
if (_exclusive) {
|
||||
LOG_ERROR("Cannot init controller, an exclusive controller already connected");
|
||||
return false;
|
||||
}
|
||||
if (exclusive && _connections.size() > 1) {
|
||||
LOG_ERROR("Cannot init exclusive controller, other controllers already connected");
|
||||
return false;
|
||||
}
|
||||
_exclusive = exclusive;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Controller::add_connection(NamedPipe::ConnectionRef conn_ref, ControllerConnection *conn)
|
||||
{
|
||||
_connections[conn_ref] = conn;
|
||||
conn->on_data();
|
||||
}
|
||||
|
||||
void Controller::remove_connection(NamedPipe::ConnectionRef conn_ref)
|
||||
{
|
||||
ControllerConnection *conn = _connections[conn_ref];
|
||||
_connections.erase(conn_ref);
|
||||
_exclusive = false;
|
||||
delete conn;
|
||||
}
|
||||
|
||||
void Controller::on_command(NamedPipe::ConnectionRef conn_ref, int32_t id)
|
||||
{
|
||||
ControllerConnection *conn = _connections[conn_ref];
|
||||
ControllerValue msg;
|
||||
|
||||
ASSERT(conn);
|
||||
msg.base.id = CONTROLLER_MENU_ITEM_CLICK;
|
||||
msg.base.size = sizeof(msg);
|
||||
msg.value = id;
|
||||
conn->write_msg(&msg.base, msg.base.size);
|
||||
}
|
||||
|
||||
ControllerConnection::ControllerConnection(ControllerInterface *handler, Controller& parent)
|
||||
: _handler (handler)
|
||||
, _parent (parent)
|
||||
, _initialized (false)
|
||||
, _write_pending (0)
|
||||
, _write_pos (_write_buf)
|
||||
, _read_pos (_read_buf)
|
||||
, _port (-1)
|
||||
, _sport (-1)
|
||||
, _full_screen (false)
|
||||
, _auto_display_res (false)
|
||||
{
|
||||
}
|
||||
|
||||
ControllerConnection::~ControllerConnection()
|
||||
{
|
||||
if (_opaque != NamedPipe::INVALID_CONNECTION) {
|
||||
NamedPipe::destroy_connection(_opaque);
|
||||
}
|
||||
if (_handler) {
|
||||
_handler->clear_menu_items(_opaque);
|
||||
_handler->delete_menu();
|
||||
}
|
||||
}
|
||||
|
||||
void ControllerConnection::bind(NamedPipe::ConnectionRef conn_ref)
|
||||
{
|
||||
_opaque = conn_ref;
|
||||
_parent.add_connection(conn_ref, this);
|
||||
}
|
||||
|
||||
void ControllerConnection::on_data()
|
||||
{
|
||||
if (_write_pending) {
|
||||
LOG_INFO("Resume pending write %d", _write_pending);
|
||||
if (!write_msg(_write_pos, _write_pending)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
while (read_msgs());
|
||||
}
|
||||
|
||||
bool ControllerConnection::read_msgs()
|
||||
{
|
||||
uint8_t *pos = _read_buf;
|
||||
size_t nread = _read_pos - _read_buf;
|
||||
int32_t size;
|
||||
|
||||
ASSERT(_handler);
|
||||
ASSERT(_opaque != NamedPipe::INVALID_CONNECTION);
|
||||
size = NamedPipe::read(_opaque, (uint8_t*)_read_pos, sizeof(_read_buf) - nread);
|
||||
if (size == 0) {
|
||||
return false;
|
||||
} else if (size < 0) {
|
||||
LOG_ERROR("Error reading from named pipe %d", size);
|
||||
_parent.remove_connection(_opaque);
|
||||
return false;
|
||||
}
|
||||
nread += size;
|
||||
while (nread > 0) {
|
||||
if (!_initialized && nread >= sizeof(ControllerInitHeader)) {
|
||||
ControllerInitHeader *init = (ControllerInitHeader *)pos;
|
||||
if (init->magic != CONTROLLER_MAGIC || init->version != CONTROLLER_VERSION) {
|
||||
LOG_ERROR("Bad controller init, magic=0x%x version=%u", init->magic,
|
||||
init->version);
|
||||
_parent.remove_connection(_opaque);
|
||||
return false;
|
||||
}
|
||||
if (nread < init->size) {
|
||||
break;
|
||||
}
|
||||
if (!handle_init((ControllerInit*)init)) {
|
||||
_parent.remove_connection(_opaque);
|
||||
return false;
|
||||
}
|
||||
nread -= init->size;
|
||||
pos += init->size;
|
||||
_initialized = true;
|
||||
}
|
||||
if (!_initialized || nread < sizeof(ControllerMsg)) {
|
||||
break;
|
||||
}
|
||||
ControllerMsg *hdr = (ControllerMsg *)pos;
|
||||
if (hdr->size < sizeof(ControllerMsg)) {
|
||||
LOG_ERROR("Bad controller message, size=%u", hdr->size);
|
||||
_parent.remove_connection(_opaque);
|
||||
return false;
|
||||
}
|
||||
if (nread < hdr->size) {
|
||||
break;
|
||||
}
|
||||
handle_message(hdr);
|
||||
nread -= hdr->size;
|
||||
pos += hdr->size;
|
||||
}
|
||||
if (nread > 0 && pos != _read_buf) {
|
||||
memmove(_read_buf, pos, nread);
|
||||
}
|
||||
_read_pos = _read_buf + nread;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ControllerConnection::write_msg(const void *buf, int len)
|
||||
{
|
||||
RecurciveLock lock(_write_lock);
|
||||
uint8_t *pos;
|
||||
int32_t written = 0;
|
||||
|
||||
ASSERT(_opaque != NamedPipe::INVALID_CONNECTION);
|
||||
if (_write_pending && buf != _write_pos) {
|
||||
if ((_write_pos + _write_pending + len > _write_buf + sizeof(_write_buf)) &&
|
||||
!write_msg(_write_pos, _write_pending)) {
|
||||
return false;
|
||||
}
|
||||
if (_write_pending) {
|
||||
if (_write_pos + _write_pending + len > _write_buf + sizeof(_write_buf)) {
|
||||
DBG(0, "Dropping message, due to insufficient space in write buffer");
|
||||
return true;
|
||||
}
|
||||
memcpy(_write_pos + _write_pending, buf, len);
|
||||
_write_pending += len;
|
||||
}
|
||||
}
|
||||
pos = (uint8_t*)buf;
|
||||
while (len && (written = NamedPipe::write(_opaque, pos, len)) > 0) {
|
||||
pos += written;
|
||||
len -= written;
|
||||
}
|
||||
if (len && written == 0) {
|
||||
if (_write_pending) {
|
||||
_write_pos = pos;
|
||||
} else {
|
||||
_write_pos = _write_buf;
|
||||
memcpy(_write_buf, pos, len);
|
||||
}
|
||||
_write_pending = len;
|
||||
} else if (written < 0) {
|
||||
LOG_ERROR("Error writing to named pipe %d", written);
|
||||
_parent.remove_connection(_opaque);
|
||||
return false;
|
||||
} else {
|
||||
_write_pending = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ControllerConnection::handle_init(ControllerInit *init)
|
||||
{
|
||||
ASSERT(_handler);
|
||||
if (init->credentials != 0) {
|
||||
LOG_ERROR("Controller menu has wrong credentials 0x%x", init->credentials);
|
||||
return false;
|
||||
}
|
||||
if (!_parent.set_exclusive(init->flags & CONTROLLER_FLAG_EXCLUSIVE)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ControllerConnection::handle_message(ControllerMsg *hdr)
|
||||
{
|
||||
uint32_t value = ((ControllerValue*)hdr)->value;
|
||||
char *data = (char*)((ControllerData*)hdr)->data;
|
||||
|
||||
ASSERT(_handler);
|
||||
switch (hdr->id) {
|
||||
case CONTROLLER_HOST:
|
||||
_host.assign(data);
|
||||
break;
|
||||
case CONTROLLER_PORT:
|
||||
_port = value;
|
||||
break;
|
||||
case CONTROLLER_SPORT:
|
||||
_sport = value;
|
||||
break;
|
||||
case CONTROLLER_PASSWORD:
|
||||
_password.assign(data);
|
||||
break;
|
||||
case CONTROLLER_SECURE_CHANNELS:
|
||||
case CONTROLLER_DISABLE_CHANNELS:
|
||||
return set_multi_val(hdr->id, data);
|
||||
case CONTROLLER_TLS_CIPHERS:
|
||||
return _handler->set_connection_ciphers(data, "Controller");
|
||||
case CONTROLLER_CA_FILE:
|
||||
return _handler->set_ca_file(data, "Controller");
|
||||
case CONTROLLER_HOST_SUBJECT:
|
||||
return _handler->set_host_cert_subject(data, "Controller");
|
||||
case CONTROLLER_FULL_SCREEN:
|
||||
_full_screen = !!(value & CONTROLLER_SET_FULL_SCREEN);
|
||||
_handler->set_auto_display_res(!!(value & CONTROLLER_AUTO_DISPLAY_RES));
|
||||
break;
|
||||
case CONTROLLER_SET_TITLE: {
|
||||
std::string str;
|
||||
string_printf(str, "%s", data);
|
||||
_handler->set_title(str);
|
||||
break;
|
||||
}
|
||||
case CONTROLLER_HOTKEYS:
|
||||
_handler->set_hotkeys(data);
|
||||
break;
|
||||
case CONTROLLER_CONNECT:
|
||||
_handler->connect(_host, _port, _sport, _password);
|
||||
break;
|
||||
case CONTROLLER_SHOW:
|
||||
_handler->show_me(_full_screen);
|
||||
break;
|
||||
case CONTROLLER_HIDE:
|
||||
_handler->hide_me();
|
||||
break;
|
||||
case CONTROLLER_CREATE_MENU:
|
||||
return create_menu((char*)data);
|
||||
case CONTROLLER_DELETE_MENU:
|
||||
_handler->delete_menu();
|
||||
break;
|
||||
#if USE_SMARTCARD
|
||||
case CONTROLLER_ENABLE_SMARTCARD:
|
||||
_handler->enable_smartcard(value);
|
||||
break;
|
||||
#endif
|
||||
case CONTROLLER_SEND_CAD:
|
||||
default:
|
||||
LOG_ERROR("Ignoring an unknown/SEND_CAD controller message %u", hdr->id);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
#define next_tok(str, delim, state) strtok(str, delim)
|
||||
#else
|
||||
#define next_tok(str, delim, state) strtok_r(str, delim, state)
|
||||
#endif
|
||||
|
||||
bool ControllerConnection::create_menu(char* resource)
|
||||
{
|
||||
bool ret = true;
|
||||
#ifndef WIN32
|
||||
char* item_state = 0;
|
||||
#endif
|
||||
char* next_item;
|
||||
const char* param;
|
||||
const char* text;
|
||||
int parent_id;
|
||||
int flags;
|
||||
int state;
|
||||
int id;
|
||||
|
||||
ASSERT(_handler);
|
||||
AutoRef<Menu> app_menu(_handler->get_app_menu());
|
||||
AutoRef<Menu> menu(new Menu((*app_menu)->get_target(), ""));
|
||||
char* item = next_tok(resource, CONTROLLER_MENU_ITEM_DELIMITER, &item_state);
|
||||
while (item != NULL) {
|
||||
next_item = item + strlen(item) + 1;
|
||||
ret = ret && (param = next_tok(item, CONTROLLER_MENU_PARAM_DELIMITER, &item_state)) &&
|
||||
sscanf(param, "%d", &parent_id);
|
||||
ret = ret && (param = next_tok(NULL, CONTROLLER_MENU_PARAM_DELIMITER, &item_state)) &&
|
||||
sscanf(param, "%d", &id);
|
||||
ret = ret && (text = next_tok(NULL, CONTROLLER_MENU_PARAM_DELIMITER, &item_state));
|
||||
ret = ret && (param = next_tok(NULL, CONTROLLER_MENU_PARAM_DELIMITER, &item_state)) &&
|
||||
sscanf(param, "%d", &flags);
|
||||
|
||||
if (!ret) {
|
||||
DBG(0, "item parsing failed %s", item);
|
||||
break;
|
||||
}
|
||||
DBG(0, "parent_id=%d, id=%d, text=%s, flags=%d", parent_id, id, text, flags);
|
||||
|
||||
AutoRef<Menu> sub_menu((*menu)->find_sub(parent_id));
|
||||
if (!(ret = !!*sub_menu)) {
|
||||
DBG(0, "submenu not found %s", item);
|
||||
break;
|
||||
}
|
||||
|
||||
if (flags & CONTROLLER_MENU_FLAGS_SEPARATOR) {
|
||||
(*sub_menu)->add_separator();
|
||||
} else if (flags & CONTROLLER_MENU_FLAGS_POPUP) {
|
||||
AutoRef<Menu> sub(new Menu((*app_menu)->get_target(), text, id));
|
||||
(*sub_menu)->add_sub(sub.release());
|
||||
} else {
|
||||
state = 0;
|
||||
if (flags & (CONTROLLER_MENU_FLAGS_DISABLED | CONTROLLER_MENU_FLAGS_GRAYED)) {
|
||||
state |= Menu::MENU_ITEM_STATE_DIM;
|
||||
}
|
||||
if (flags & CONTROLLER_MENU_FLAGS_CHECKED) {
|
||||
state |= Menu::MENU_ITEM_STATE_CHECKED;
|
||||
}
|
||||
if (id >= SPICE_MENU_INTERNAL_ID_BASE) {
|
||||
id = ((id - SPICE_MENU_INTERNAL_ID_BASE) >> SPICE_MENU_INTERNAL_ID_SHIFT) + 1;
|
||||
} else {
|
||||
id = _handler->get_controller_menu_item_id(_opaque, id);
|
||||
}
|
||||
(*sub_menu)->add_command(text, id, state);
|
||||
}
|
||||
item = next_tok(next_item, CONTROLLER_MENU_ITEM_DELIMITER, &item_state);
|
||||
}
|
||||
if (ret) {
|
||||
_handler->set_menu(*menu);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ControllerConnection::set_multi_val(uint32_t op, char* multi_val)
|
||||
{
|
||||
CmdLineParser parser("", false);
|
||||
int id = CmdLineParser::OPTION_FIRST_AVAILABLE;
|
||||
char* argv[] = {NULL, (char*)"--set", multi_val};
|
||||
char* val;
|
||||
|
||||
ASSERT(_handler);
|
||||
parser.add(id, "set", "none", "none", true);
|
||||
parser.set_multi(id, ',');
|
||||
parser.begin(3, argv);
|
||||
if (parser.get_option(&val) != id) {
|
||||
return false;
|
||||
}
|
||||
switch (op) {
|
||||
case CONTROLLER_SECURE_CHANNELS:
|
||||
_handler->set_channels_security(parser, true, val, "Controller");
|
||||
break;
|
||||
case CONTROLLER_DISABLE_CHANNELS:
|
||||
_handler->set_enable_channels(parser, false, val, "Controller");
|
||||
break;
|
||||
default:
|
||||
DBG(0, "unsupported op %u", op);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -1,119 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_CONTROLLER_MENU
|
||||
#define _H_CONTROLLER_MENU
|
||||
|
||||
#include "named_pipe.h"
|
||||
#include "threads.h"
|
||||
|
||||
class ControllerConnection;
|
||||
struct ControllerInit;
|
||||
struct ControllerMsg;
|
||||
class CmdLineParser;
|
||||
class Menu;
|
||||
|
||||
class ControllerInterface {
|
||||
public:
|
||||
virtual ~ControllerInterface() {}
|
||||
|
||||
virtual bool connect(const std::string& host, int port, int sport,
|
||||
const std::string& password) = 0;
|
||||
virtual void set_title(const std::string& title) = 0;
|
||||
virtual void set_auto_display_res(bool auto_display_res) = 0;
|
||||
virtual void show_me(bool full_screen) = 0;
|
||||
virtual void hide_me() = 0;
|
||||
virtual bool set_channels_security(CmdLineParser& parser, bool on, char *val,
|
||||
const char* arg0) = 0;
|
||||
virtual bool set_enable_channels(CmdLineParser& parser, bool enable, char *val,
|
||||
const char* arg0) = 0;
|
||||
virtual bool set_connection_ciphers(const char* ciphers, const char* arg0) = 0;
|
||||
virtual bool set_ca_file(const char* ca_file, const char* arg0) = 0;
|
||||
virtual bool set_host_cert_subject(const char* subject, const char* arg0) = 0;
|
||||
virtual void set_hotkeys(const std::string& hotkeys) = 0;
|
||||
virtual int get_controller_menu_item_id(int32_t opaque_conn_ref, uint32_t id) = 0;
|
||||
virtual void clear_menu_items(int32_t opaque_conn_ref) = 0;
|
||||
virtual Menu* get_app_menu() = 0;
|
||||
virtual void set_menu(Menu* menu) = 0;
|
||||
virtual void delete_menu() = 0;
|
||||
#ifdef USE_SMARTCARD
|
||||
virtual void enable_smartcard(bool enable) = 0;
|
||||
#endif
|
||||
};
|
||||
|
||||
class Controller : public NamedPipe::ListenerInterface {
|
||||
public:
|
||||
Controller(ControllerInterface *handler);
|
||||
virtual ~Controller();
|
||||
|
||||
Controller* ref() { _refs++; return this;}
|
||||
void unref() { if (!--_refs) delete this;}
|
||||
|
||||
virtual NamedPipe::ConnectionInterface &create();
|
||||
bool set_exclusive(bool exclusive);
|
||||
void add_connection(NamedPipe::ConnectionRef conn_ref, ControllerConnection *conn);
|
||||
void remove_connection(NamedPipe::ConnectionRef conn_ref);
|
||||
void on_command(NamedPipe::ConnectionRef conn_ref, int32_t id);
|
||||
|
||||
private:
|
||||
ControllerInterface *_handler;
|
||||
std::map<NamedPipe::ConnectionRef, ControllerConnection*> _connections;
|
||||
NamedPipe::ListenerRef _pipe;
|
||||
bool _exclusive;
|
||||
int _refs;
|
||||
};
|
||||
|
||||
#define CONTROLLER_BUF_SIZE 4096
|
||||
|
||||
class ControllerConnection : public NamedPipe::ConnectionInterface {
|
||||
public:
|
||||
ControllerConnection(ControllerInterface *handler, Controller& parent);
|
||||
virtual ~ControllerConnection();
|
||||
|
||||
virtual void bind(NamedPipe::ConnectionRef conn_ref);
|
||||
virtual void on_data();
|
||||
bool write_msg(const void *buf, int len);
|
||||
void reset_handler() { _handler = NULL;}
|
||||
|
||||
private:
|
||||
bool read_msgs();
|
||||
bool handle_init(ControllerInit *init);
|
||||
bool handle_message(ControllerMsg *hdr);
|
||||
bool create_menu(char* resource);
|
||||
bool set_multi_val(uint32_t op, char* multi_val);
|
||||
|
||||
private:
|
||||
ControllerInterface *_handler;
|
||||
Controller& _parent;
|
||||
bool _initialized;
|
||||
|
||||
int _write_pending;
|
||||
uint8_t *_write_pos;
|
||||
uint8_t *_read_pos;
|
||||
uint8_t _write_buf[CONTROLLER_BUF_SIZE];
|
||||
uint8_t _read_buf[CONTROLLER_BUF_SIZE];
|
||||
RecurciveMutex _write_lock;
|
||||
|
||||
std::string _host;
|
||||
std::string _password;
|
||||
int _port;
|
||||
int _sport;
|
||||
bool _full_screen;
|
||||
bool _auto_display_res;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,115 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
#include "cursor.h"
|
||||
#include "utils.h"
|
||||
#include "debug.h"
|
||||
|
||||
CursorData::CursorData(SpiceCursor& 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 SPICE_CURSOR_TYPE_ALPHA:
|
||||
expected_size = (_header.width << 2) * _header.height;
|
||||
break;
|
||||
case SPICE_CURSOR_TYPE_MONO:
|
||||
expected_size = (SPICE_ALIGN(_header.width, 8) >> 2) * _header.height;
|
||||
break;
|
||||
case SPICE_CURSOR_TYPE_COLOR4:
|
||||
expected_size = (SPICE_ALIGN(_header.width, 2) >> 1) * _header.height;
|
||||
expected_size += (SPICE_ALIGN(_header.width, 8) >> 3) * _header.height;
|
||||
expected_size += 16 * sizeof(uint32_t);
|
||||
break;
|
||||
case SPICE_CURSOR_TYPE_COLOR8:
|
||||
expected_size = _header.width * _header.height;
|
||||
expected_size += (SPICE_ALIGN(_header.width, 8) >> 3) * _header.height;
|
||||
expected_size += 256 * sizeof(uint32_t);
|
||||
break;
|
||||
case SPICE_CURSOR_TYPE_COLOR16:
|
||||
expected_size = (_header.width << 1) * _header.height;
|
||||
expected_size += (SPICE_ALIGN(_header.width, 8) >> 3) * _header.height;
|
||||
break;
|
||||
case SPICE_CURSOR_TYPE_COLOR24:
|
||||
expected_size = (_header.width * 3) * _header.height;
|
||||
expected_size += (SPICE_ALIGN(_header.width, 8) >> 3) * _header.height;
|
||||
break;
|
||||
case SPICE_CURSOR_TYPE_COLOR32:
|
||||
expected_size = (_header.width << 2) * _header.height;
|
||||
expected_size += (SPICE_ALIGN(_header.width, 8) >> 3) * _header.height;
|
||||
break;
|
||||
}
|
||||
|
||||
if (data_size < expected_size) {
|
||||
THROW("access violation 0x%" PRIuPTR " %u", (uintptr_t)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 SpiceCursorHeader& header, int& size)
|
||||
{
|
||||
switch (header.type) {
|
||||
case SPICE_CURSOR_TYPE_ALPHA:
|
||||
case SPICE_CURSOR_TYPE_COLOR32:
|
||||
size = (header.width << 2) * header.height;
|
||||
return 32;
|
||||
case SPICE_CURSOR_TYPE_MONO:
|
||||
size = (SPICE_ALIGN(header.width, 8) >> 3) * header.height;
|
||||
return 1;
|
||||
case SPICE_CURSOR_TYPE_COLOR4:
|
||||
size = (SPICE_ALIGN(header.width, 2) >> 1) * header.height;
|
||||
return 4;
|
||||
case SPICE_CURSOR_TYPE_COLOR8:
|
||||
size = header.width * header.height;
|
||||
return 8;
|
||||
case SPICE_CURSOR_TYPE_COLOR16:
|
||||
size = (header.width << 1) * header.height;
|
||||
return 16;
|
||||
case SPICE_CURSOR_TYPE_COLOR24:
|
||||
size = (header.width * 3) * header.height;
|
||||
return 24;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -1,72 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_CURSOR_
|
||||
#define _H_CURSOR_
|
||||
|
||||
#include "common/messages.h"
|
||||
#include "threads.h"
|
||||
#include "red_window_p.h"
|
||||
|
||||
class CursorOpaque {
|
||||
public:
|
||||
CursorOpaque() {}
|
||||
virtual ~CursorOpaque() {}
|
||||
};
|
||||
|
||||
class LocalCursor;
|
||||
|
||||
class CursorData {
|
||||
public:
|
||||
CursorData(SpiceCursor& 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 SpiceCursorHeader& header() const { return _header;}
|
||||
const uint8_t* data() const { return _data;}
|
||||
|
||||
private:
|
||||
~CursorData();
|
||||
|
||||
private:
|
||||
AtomicCount _atomic;
|
||||
SpiceCursorHeader _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 SpiceCursorHeader& header, int &size);
|
||||
|
||||
private:
|
||||
AtomicCount _atomic;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,670 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "common/rect.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "cursor_channel.h"
|
||||
#include "display_channel.h"
|
||||
#include "cursor.h"
|
||||
#include "red_client.h"
|
||||
#include "application.h"
|
||||
#include "debug.h"
|
||||
#include "utils.h"
|
||||
#include "screen.h"
|
||||
#include "red_pixmap_sw.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 SpiceRect& area) = 0;
|
||||
};
|
||||
|
||||
class AlphaCursor: public NaitivCursor {
|
||||
public:
|
||||
AlphaCursor(const SpiceCursorHeader& header, const uint8_t* data);
|
||||
|
||||
virtual void draw(RedDrawable& dest, int x, int y, const SpiceRect& area);
|
||||
|
||||
private:
|
||||
std::auto_ptr<RedPixmap> _pixmap;
|
||||
};
|
||||
|
||||
class MonoCursor: public NaitivCursor {
|
||||
public:
|
||||
MonoCursor(const SpiceCursorHeader& header, const uint8_t* data);
|
||||
|
||||
virtual void draw(RedDrawable& dest, int x, int y, const SpiceRect& area);
|
||||
|
||||
private:
|
||||
std::auto_ptr<RedPixmap> _pixmap;
|
||||
int _height;
|
||||
};
|
||||
|
||||
class UnsupportedCursor: public NaitivCursor {
|
||||
public:
|
||||
UnsupportedCursor(const SpiceCursorHeader& header);
|
||||
virtual void draw(RedDrawable& dest, int x, int y, const SpiceRect& area);
|
||||
|
||||
private:
|
||||
int _hot_x;
|
||||
int _hot_y;
|
||||
};
|
||||
|
||||
UnsupportedCursor::UnsupportedCursor(const SpiceCursorHeader& 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 SpiceRect& area)
|
||||
{
|
||||
SpiceRect dest_area;
|
||||
SpiceRect 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 SpiceCursorHeader& header, const uint8_t* data)
|
||||
: _pixmap (new RedPixmapSw(header.width, header.height,
|
||||
RedDrawable::ARGB32, true, 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 SpiceRect& area)
|
||||
{
|
||||
dest.blend_pixels(*_pixmap, area.left - x, area.top - y, area);
|
||||
}
|
||||
|
||||
MonoCursor::MonoCursor(const SpiceCursorHeader& header, const uint8_t* data)
|
||||
: _pixmap (NULL)
|
||||
, _height (header.height)
|
||||
{
|
||||
_pixmap.reset(new RedPixmapSw(header.width, _height * 2, RedDrawable::A1,
|
||||
true, NULL));
|
||||
|
||||
int dest_stride = _pixmap->get_stride();
|
||||
uint8_t *dest_line = _pixmap->get_data();
|
||||
int src_stride = SPICE_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 SpiceRect& 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 SpiceCursorHeader& header);
|
||||
|
||||
virtual void draw(RedDrawable& dest, int x, int y, const SpiceRect& area);
|
||||
|
||||
protected:
|
||||
void init_pixels(const SpiceCursorHeader& 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 SpiceCursorHeader& header)
|
||||
: _pixmap (new RedPixmapSw(header.width, header.height,
|
||||
RedDrawable::ARGB32, true, NULL))
|
||||
, _invers (NULL)
|
||||
{
|
||||
_invers.reset(new RedPixmapSw(header.width, header.height, RedDrawable::A1,
|
||||
true, NULL));
|
||||
}
|
||||
|
||||
void ColorCursor::init_pixels(const SpiceCursorHeader& header, const uint8_t* pixels,
|
||||
const uint8_t *and_mask)
|
||||
{
|
||||
int mask_stride = SPICE_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 SpiceRect& 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 SpiceCursorHeader& 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 SpiceCursorHeader& 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 SpiceCursorHeader& header, const uint8_t* data)
|
||||
: ColorCursor(header)
|
||||
, _src_stride (SPICE_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 AttachDispayEvent: public Event {
|
||||
public:
|
||||
AttachDispayEvent(CursorChannel& channel)
|
||||
: _channel (channel)
|
||||
{
|
||||
}
|
||||
|
||||
class UpdateDisplayChannel: public ForEachChannelFunc {
|
||||
public:
|
||||
UpdateDisplayChannel(CursorChannel& channel)
|
||||
: _channel (channel)
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool operator() (RedChannel& channel)
|
||||
{
|
||||
if (channel.get_type() != SPICE_CHANNEL_DISPLAY ||
|
||||
channel.get_id() != _channel.get_id()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
_channel.attach_display(&static_cast<DisplayChannel&>(channel));
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
CursorChannel& _channel;
|
||||
};
|
||||
|
||||
virtual void response(AbstractProcessLoop& events_loop)
|
||||
{
|
||||
UpdateDisplayChannel func(_channel);
|
||||
_channel.get_client().for_each_channel(func);
|
||||
}
|
||||
|
||||
private:
|
||||
CursorChannel& _channel;
|
||||
};
|
||||
|
||||
class CursorUpdateEvent: public Event {
|
||||
public:
|
||||
CursorUpdateEvent(CursorChannel& channel)
|
||||
: _channel (channel)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void response(AbstractProcessLoop& events_loop)
|
||||
{
|
||||
DisplayChannel* display_channel = _channel._display_channel;
|
||||
if (!display_channel) {
|
||||
return;
|
||||
}
|
||||
|
||||
Lock lock(_channel._update_lock);
|
||||
if (_channel._cursor_visible) {
|
||||
display_channel->set_cursor(_channel._cursor);
|
||||
return;
|
||||
}
|
||||
|
||||
display_channel->hide_cursor();
|
||||
}
|
||||
|
||||
private:
|
||||
CursorChannel& _channel;
|
||||
};
|
||||
|
||||
class CursorHandler: public MessageHandlerImp<CursorChannel, SPICE_CHANNEL_CURSOR> {
|
||||
public:
|
||||
CursorHandler(CursorChannel& channel)
|
||||
: MessageHandlerImp<CursorChannel, SPICE_CHANNEL_CURSOR>(channel) {}
|
||||
};
|
||||
|
||||
CursorChannel::CursorChannel(RedClient& client, uint32_t id)
|
||||
: RedChannel(client, SPICE_CHANNEL_CURSOR, id, new CursorHandler(*this))
|
||||
, ScreenLayer(SCREEN_LAYER_CURSOR, false)
|
||||
, _cursor (NULL)
|
||||
, _cursor_visible (false)
|
||||
, _display_channel (NULL)
|
||||
{
|
||||
CursorHandler* handler = static_cast<CursorHandler*>(get_message_handler());
|
||||
|
||||
handler->set_handler(SPICE_MSG_MIGRATE, &CursorChannel::handle_migrate);
|
||||
handler->set_handler(SPICE_MSG_SET_ACK, &CursorChannel::handle_set_ack);
|
||||
handler->set_handler(SPICE_MSG_PING, &CursorChannel::handle_ping);
|
||||
handler->set_handler(SPICE_MSG_WAIT_FOR_CHANNELS, &CursorChannel::handle_wait_for_channels);
|
||||
handler->set_handler(SPICE_MSG_DISCONNECTING, &CursorChannel::handle_disconnect);
|
||||
handler->set_handler(SPICE_MSG_NOTIFY, &CursorChannel::handle_notify);
|
||||
|
||||
handler->set_handler(SPICE_MSG_CURSOR_INIT, &CursorChannel::handle_init);
|
||||
handler->set_handler(SPICE_MSG_CURSOR_RESET, &CursorChannel::handle_reset);
|
||||
handler->set_handler(SPICE_MSG_CURSOR_SET, &CursorChannel::handle_cursor_set);
|
||||
handler->set_handler(SPICE_MSG_CURSOR_MOVE, &CursorChannel::handle_cursor_move);
|
||||
handler->set_handler(SPICE_MSG_CURSOR_HIDE, &CursorChannel::handle_cursor_hide);
|
||||
handler->set_handler(SPICE_MSG_CURSOR_TRAIL, &CursorChannel::handle_cursor_trail);
|
||||
handler->set_handler(SPICE_MSG_CURSOR_INVAL_ONE, &CursorChannel::handle_inval_one);
|
||||
handler->set_handler(SPICE_MSG_CURSOR_INVAL_ALL, &CursorChannel::handle_inval_all);
|
||||
}
|
||||
|
||||
CursorChannel::~CursorChannel()
|
||||
{
|
||||
ASSERT(!_cursor);
|
||||
}
|
||||
|
||||
void CursorChannel::on_connect()
|
||||
{
|
||||
AutoRef<AttachDispayEvent> attach_event(new AttachDispayEvent(*this));
|
||||
get_client().push_event(*attach_event);
|
||||
}
|
||||
|
||||
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::update_display_cursor()
|
||||
{
|
||||
if (!_display_channel) {
|
||||
return;
|
||||
}
|
||||
|
||||
AutoRef<CursorUpdateEvent> update_event(new CursorUpdateEvent(*this));
|
||||
get_client().push_event(*update_event);
|
||||
}
|
||||
|
||||
void CursorChannel::remove_cursor()
|
||||
{
|
||||
Lock lock(_update_lock);
|
||||
_cursor_visible = false;
|
||||
if (_cursor) {
|
||||
_cursor->unref();
|
||||
_cursor = NULL;
|
||||
}
|
||||
lock.unlock();
|
||||
clear_area();
|
||||
update_display_cursor();
|
||||
}
|
||||
|
||||
void CursorChannel::copy_pixels(const QRegion& dest_region, RedDrawable& dest_dc)
|
||||
{
|
||||
pixman_box32_t *rects;
|
||||
int num_rects;
|
||||
|
||||
Lock lock(_update_lock);
|
||||
|
||||
if (!_cursor_visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
rects = pixman_region32_rectangles((pixman_region32_t *)&dest_region, &num_rects);
|
||||
for (int i = 0; i < num_rects; i++) {
|
||||
SpiceRect r;
|
||||
|
||||
r.left = rects[i].x1;
|
||||
r.top = rects[i].y1;
|
||||
r.right = rects[i].x2;
|
||||
r.bottom = rects[i].y2;
|
||||
ASSERT(_cursor && _cursor->get_opaque());
|
||||
((NaitivCursor*)_cursor->get_opaque())->draw(dest_dc, _cursor_rect.left, _cursor_rect.top,
|
||||
r);
|
||||
}
|
||||
}
|
||||
|
||||
void CursorChannel::create_native_cursor(CursorData* cursor)
|
||||
{
|
||||
CursorOpaque* native_cursor = cursor->get_opaque();
|
||||
|
||||
if (native_cursor) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (cursor->header().type) {
|
||||
case SPICE_CURSOR_TYPE_ALPHA:
|
||||
native_cursor = new AlphaCursor(cursor->header(), cursor->data());
|
||||
break;
|
||||
case SPICE_CURSOR_TYPE_COLOR32:
|
||||
native_cursor = new ColorCursor32(cursor->header(), cursor->data());
|
||||
break;
|
||||
case SPICE_CURSOR_TYPE_MONO:
|
||||
native_cursor = new MonoCursor(cursor->header(), cursor->data());
|
||||
break;
|
||||
case SPICE_CURSOR_TYPE_COLOR4:
|
||||
native_cursor = new ColorCursor4(cursor->header(), cursor->data());
|
||||
break;
|
||||
case SPICE_CURSOR_TYPE_COLOR8:
|
||||
native_cursor = new UnsupportedCursor(cursor->header());
|
||||
break;
|
||||
case SPICE_CURSOR_TYPE_COLOR16:
|
||||
native_cursor = new ColorCursor16(cursor->header(), cursor->data());
|
||||
break;
|
||||
case SPICE_CURSOR_TYPE_COLOR24:
|
||||
native_cursor = new UnsupportedCursor(cursor->header());
|
||||
break;
|
||||
default:
|
||||
THROW("invalid cursor type");
|
||||
}
|
||||
cursor->set_opaque(native_cursor);
|
||||
}
|
||||
|
||||
void CursorChannel::set_cursor(SpiceCursor& red_cursor, int x, int y, bool visible)
|
||||
{
|
||||
CursorData *cursor;
|
||||
|
||||
if (red_cursor.flags & SPICE_CURSOR_FLAGS_NONE) {
|
||||
remove_cursor();
|
||||
return;
|
||||
}
|
||||
|
||||
if (red_cursor.flags & SPICE_CURSOR_FLAGS_FROM_CACHE) {
|
||||
cursor = _cursor_cache.get(red_cursor.header.unique);
|
||||
} else {
|
||||
cursor = new CursorData(red_cursor, red_cursor.data_size);
|
||||
if (red_cursor.flags & SPICE_CURSOR_FLAGS_CACHE_ME) {
|
||||
ASSERT(red_cursor.header.unique);
|
||||
_cursor_cache.add(red_cursor.header.unique, cursor);
|
||||
}
|
||||
}
|
||||
|
||||
AutoRef<CursorData> cursor_ref(cursor);
|
||||
create_native_cursor(cursor);
|
||||
|
||||
Lock lock(_update_lock);
|
||||
_hot_pos.x = x;
|
||||
_hot_pos.y = y;
|
||||
_cursor_visible = visible;
|
||||
_cursor_rect.left = x - cursor->header().hot_spot_x;
|
||||
_cursor_rect.right = _cursor_rect.left + cursor->header().width;
|
||||
_cursor_rect.top = y - cursor->header().hot_spot_y;
|
||||
_cursor_rect.bottom = _cursor_rect.top + cursor->header().height;
|
||||
|
||||
if (_cursor) {
|
||||
_cursor->unref();
|
||||
}
|
||||
_cursor = cursor->ref();
|
||||
lock.unlock();
|
||||
|
||||
update_display_cursor();
|
||||
|
||||
if (get_client().get_mouse_mode() == SPICE_MOUSE_MODE_SERVER) {
|
||||
if (_cursor_visible) {
|
||||
set_rect_area(_cursor_rect);
|
||||
} else {
|
||||
clear_area();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CursorChannel::attach_display(DisplayChannel* channel)
|
||||
{
|
||||
if (_display_channel) {
|
||||
return;
|
||||
}
|
||||
|
||||
_display_channel = channel;
|
||||
|
||||
Lock lock(_update_lock);
|
||||
if (!_cursor_visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
_display_channel->set_cursor(_cursor);
|
||||
}
|
||||
|
||||
void CursorChannel::detach_display()
|
||||
{
|
||||
_display_channel = NULL;
|
||||
}
|
||||
|
||||
void CursorChannel::handle_init(RedPeer::InMessage *message)
|
||||
{
|
||||
SpiceMsgCursorInit *init = (SpiceMsgCursorInit*)message->data();
|
||||
attach_to_screen(get_client().get_application(), get_id());
|
||||
remove_cursor();
|
||||
_cursor_cache.clear();
|
||||
set_cursor(init->cursor, 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)
|
||||
{
|
||||
SpiceMsgCursorSet* set = (SpiceMsgCursorSet*)message->data();
|
||||
set_cursor(set->cursor, set->position.x,
|
||||
set->position.y, set->visible != 0);
|
||||
}
|
||||
|
||||
void CursorChannel::handle_cursor_move(RedPeer::InMessage* message)
|
||||
{
|
||||
SpiceMsgCursorMove* move = (SpiceMsgCursorMove*)message->data();
|
||||
|
||||
if (!_cursor) {
|
||||
return;
|
||||
}
|
||||
|
||||
Lock lock(_update_lock);
|
||||
_cursor_visible = true;
|
||||
int dx = move->position.x - _hot_pos.x;
|
||||
int dy = move->position.y - _hot_pos.y;
|
||||
_hot_pos.x += dx;
|
||||
_hot_pos.y += dy;
|
||||
_cursor_rect.left += dx;
|
||||
_cursor_rect.right += dx;
|
||||
_cursor_rect.top += dy;
|
||||
_cursor_rect.bottom += dy;
|
||||
lock.unlock();
|
||||
|
||||
if (get_client().get_mouse_mode() == SPICE_MOUSE_MODE_SERVER) {
|
||||
set_rect_area(_cursor_rect);
|
||||
return;
|
||||
}
|
||||
|
||||
update_display_cursor();
|
||||
}
|
||||
|
||||
void CursorChannel::handle_cursor_hide(RedPeer::InMessage* message)
|
||||
{
|
||||
Lock lock(_update_lock);
|
||||
|
||||
_cursor_visible = false;
|
||||
update_display_cursor();
|
||||
|
||||
if (get_client().get_mouse_mode() == SPICE_MOUSE_MODE_SERVER) {
|
||||
clear_area();
|
||||
}
|
||||
}
|
||||
|
||||
void CursorChannel::handle_cursor_trail(RedPeer::InMessage* message)
|
||||
{
|
||||
SpiceMsgCursorTrail* trail = (SpiceMsgCursorTrail*)message->data();
|
||||
DBG(0, "length %u frequency %u", trail->length, trail->frequency)
|
||||
}
|
||||
|
||||
void CursorChannel::handle_inval_one(RedPeer::InMessage* message)
|
||||
{
|
||||
SpiceMsgDisplayInvalOne* inval = (SpiceMsgDisplayInvalOne*)message->data();
|
||||
_cursor_cache.remove(inval->id);
|
||||
}
|
||||
|
||||
void CursorChannel::handle_inval_all(RedPeer::InMessage* message)
|
||||
{
|
||||
_cursor_cache.clear();
|
||||
}
|
||||
|
||||
void CursorChannel::on_mouse_mode_change()
|
||||
{
|
||||
Lock lock(_update_lock);
|
||||
|
||||
if (get_client().get_mouse_mode() == SPICE_MOUSE_MODE_CLIENT) {
|
||||
clear_area();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_cursor_visible) {
|
||||
set_rect_area(_cursor_rect);
|
||||
}
|
||||
}
|
||||
|
||||
class CursorFactory: public ChannelFactory {
|
||||
public:
|
||||
CursorFactory() : ChannelFactory(SPICE_CHANNEL_CURSOR) {}
|
||||
virtual RedChannel* construct(RedClient& client, uint32_t id)
|
||||
{
|
||||
return new CursorChannel(client, id);
|
||||
}
|
||||
};
|
||||
|
||||
static CursorFactory factory;
|
||||
|
||||
ChannelFactory& CursorChannel::Factory()
|
||||
{
|
||||
return factory;
|
||||
}
|
||||
@ -1,93 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_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 DisplayChannel;
|
||||
|
||||
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 CursorChannel: public RedChannel, public ScreenLayer {
|
||||
public:
|
||||
CursorChannel(RedClient& client, uint32_t id);
|
||||
virtual ~CursorChannel();
|
||||
|
||||
static ChannelFactory& Factory();
|
||||
void on_mouse_mode_change();
|
||||
|
||||
void attach_display(DisplayChannel* channel);
|
||||
void detach_display();
|
||||
|
||||
protected:
|
||||
virtual void on_connect();
|
||||
virtual void on_disconnect();
|
||||
|
||||
private:
|
||||
static void create_native_cursor(CursorData* cursor);
|
||||
|
||||
void update_display_cursor();
|
||||
void set_cursor(SpiceCursor& red_cursor, 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 AttachDispayEvent;
|
||||
friend class CursorUpdateEvent;
|
||||
|
||||
private:
|
||||
CursorCache _cursor_cache;
|
||||
CursorData* _cursor;
|
||||
SpicePoint _hot_pos;
|
||||
SpiceRect _cursor_rect;
|
||||
Mutex _update_lock;
|
||||
bool _cursor_visible;
|
||||
DisplayChannel* _display_channel;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,92 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_DEBUG
|
||||
#define _H_DEBUG
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sstream>
|
||||
|
||||
#include "platform.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
|
||||
|
||||
enum {
|
||||
LOG_DEBUG,
|
||||
LOG_INFO,
|
||||
LOG_WARN,
|
||||
LOG_ERROR,
|
||||
LOG_FATAL
|
||||
};
|
||||
|
||||
void spice_log(unsigned int type, const char *function, const char *format, ...);
|
||||
void spice_log_cleanup(void);
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define SPICE_FUNC_NAME __PRETTY_FUNCTION__
|
||||
#else
|
||||
#define SPICE_FUNC_NAME __FUNCTION__
|
||||
#endif
|
||||
|
||||
#define LOG(type, format, ...) spice_log(type, SPICE_FUNC_NAME, format, ## __VA_ARGS__)
|
||||
|
||||
#define LOG_INFO(format, ...) LOG(LOG_INFO, format, ## __VA_ARGS__)
|
||||
#define LOG_WARN(format, ...) LOG(LOG_WARN, format, ## __VA_ARGS__)
|
||||
#define LOG_ERROR(format, ...) LOG(LOG_ERROR, format, ## __VA_ARGS__)
|
||||
|
||||
#define PANIC(format, ...) { \
|
||||
LOG(LOG_FATAL, format, ## __VA_ARGS__); \
|
||||
ON_PANIC(); \
|
||||
}
|
||||
|
||||
#define PANIC_ON(x) if ((x)) { \
|
||||
LOG(LOG_FATAL, "%s panic %s\n", __FUNCTION__, #x); \
|
||||
ON_PANIC(); \
|
||||
}
|
||||
|
||||
#define DBGLEVEL 1000
|
||||
|
||||
#define DBG(level, format, ...) { \
|
||||
if (level <= DBGLEVEL) { \
|
||||
LOG(LOG_DEBUG, format, ## __VA_ARGS__); \
|
||||
} \
|
||||
}
|
||||
|
||||
#endif // _H_DEBUG
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,247 +0,0 @@
|
||||
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_DISPLAY_CHANNEL
|
||||
#define _H_DISPLAY_CHANNEL
|
||||
|
||||
#include "common/region.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "canvas.h"
|
||||
#include "red_channel.h"
|
||||
#include "cache.hpp"
|
||||
#include "screen_layer.h"
|
||||
#include "process_loop.h"
|
||||
#ifdef USE_OPENGL
|
||||
#include "red_pixmap_gl.h"
|
||||
#endif
|
||||
#include "glz_decoder_window.h"
|
||||
|
||||
class RedScreen;
|
||||
class ChannelFactory;
|
||||
class VideoStream;
|
||||
class DisplayChannel;
|
||||
class CursorData;
|
||||
class InputsChannel;
|
||||
|
||||
class StreamsTrigger: public EventSources::Trigger {
|
||||
public:
|
||||
StreamsTrigger(DisplayChannel& channel);
|
||||
|
||||
virtual void on_event();
|
||||
|
||||
private:
|
||||
DisplayChannel& _channel;
|
||||
};
|
||||
|
||||
#ifdef USE_OPENGL
|
||||
class GLInterruptRecreate: public EventSources::Trigger {
|
||||
public:
|
||||
GLInterruptRecreate(DisplayChannel& channel);
|
||||
virtual void trigger();
|
||||
virtual void on_event();
|
||||
|
||||
private:
|
||||
DisplayChannel& _channel;
|
||||
Mutex _lock;
|
||||
Condition _cond;
|
||||
};
|
||||
#endif
|
||||
|
||||
class InterruptUpdate: public EventSources::Trigger {
|
||||
public:
|
||||
InterruptUpdate(DisplayChannel& channel);
|
||||
|
||||
virtual void on_event();
|
||||
|
||||
private:
|
||||
DisplayChannel& _channel;
|
||||
};
|
||||
|
||||
class StreamsTimer: public Timer {
|
||||
public:
|
||||
StreamsTimer(DisplayChannel& channel);
|
||||
virtual void response(AbstractProcessLoop& events_loop);
|
||||
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_OPENGL
|
||||
virtual void recreate_ogl_context();
|
||||
virtual void recreate_ogl_context_interrupt();
|
||||
virtual void pre_migrate();
|
||||
virtual void post_migrate();
|
||||
#endif
|
||||
virtual void update_interrupt();
|
||||
void set_cursor(CursorData* cursor);
|
||||
void hide_cursor();
|
||||
void set_capture_mode(bool on);
|
||||
|
||||
virtual bool pointer_test(int x, int y);
|
||||
virtual void on_pointer_enter(int x, int y, unsigned int buttons_state);
|
||||
virtual void on_pointer_motion(int x, int y, unsigned int buttons_state);
|
||||
virtual void on_pointer_leave();
|
||||
virtual void on_mouse_button_press(int button, int buttons_state);
|
||||
virtual void on_mouse_button_release(int button, int buttons_state);
|
||||
|
||||
void attach_inputs(InputsChannel* inputs_channel);
|
||||
void detach_inputs();
|
||||
|
||||
static ChannelFactory& Factory();
|
||||
|
||||
protected:
|
||||
virtual void on_connect();
|
||||
virtual void on_disconnect();
|
||||
virtual void on_disconnect_mig_src();
|
||||
|
||||
private:
|
||||
void set_draw_handlers();
|
||||
void clear_draw_handlers();
|
||||
bool create_sw_canvas(int surface_id, int width, int height, uint32_t format);
|
||||
#ifdef USE_OPENGL
|
||||
bool create_ogl_canvas(int surface_id, int width, int height, uint32_t format, bool recreate,
|
||||
RenderType rendertype);
|
||||
#endif
|
||||
#ifdef WIN32
|
||||
bool create_gdi_canvas(int surface_id, int width, int height, uint32_t format);
|
||||
#endif
|
||||
void destroy_canvas(int surface_id);
|
||||
void create_canvas(int surface_id, const std::vector<int>& canvas_type, int width, int height,
|
||||
uint32_t format);
|
||||
void destroy_streams();
|
||||
void update_cursor();
|
||||
|
||||
void create_primary_surface(int width, int height, uint32_t format);
|
||||
void create_surface(int surface_id, int width, int height, uint32_t format);
|
||||
void destroy_primary_surface();
|
||||
void destroy_surface(int surface_id);
|
||||
void destroy_all_surfaces();
|
||||
void do_destroy_all_surfaces();
|
||||
void destroy_off_screen_surfaces();
|
||||
void do_destroy_off_screen_surfaces();
|
||||
|
||||
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_surface_create(RedPeer::InMessage* message);
|
||||
void handle_surface_destroy(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 handle_draw_composite(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);
|
||||
void reset_screen();
|
||||
void clear(bool destroy_primary = true);
|
||||
|
||||
static void set_clip_rects(const SpiceClip& clip, uint32_t& num_clip_rects, SpiceRect*& clip_rects);
|
||||
|
||||
private:
|
||||
SurfacesCache _surfaces_cache;
|
||||
SurfacesCache _swsurfaces_cache;
|
||||
PixmapCache& _pixmap_cache;
|
||||
PaletteCache _palette_cache;
|
||||
GlzDecoderWindow& _glz_window;
|
||||
bool _mark;
|
||||
int _x_res;
|
||||
int _y_res;
|
||||
uint32_t _format;
|
||||
#ifdef USE_OPENGL
|
||||
RenderType _rendertype;
|
||||
#endif
|
||||
|
||||
#ifndef RED64
|
||||
Mutex _mark_lock;
|
||||
#endif
|
||||
uint64_t _update_mark;
|
||||
Mutex _streams_lock;
|
||||
|
||||
Mutex _timer_lock;
|
||||
AutoRef<StreamsTimer> _streams_timer;
|
||||
uint32_t _next_timer_time;
|
||||
|
||||
AutoRef<CursorData> _cursor;
|
||||
bool _cursor_visibal;
|
||||
bool _active_pointer;
|
||||
bool _capture_mouse_mode;
|
||||
InputsChannel* _inputs_channel;
|
||||
|
||||
SpicePoint _pointer_pos;
|
||||
int _buttons_state;
|
||||
|
||||
std::vector<VideoStream*> _streams;
|
||||
VideoStream* _active_streams;
|
||||
StreamsTrigger _streams_trigger;
|
||||
#ifdef USE_OPENGL
|
||||
GLInterruptRecreate _gl_interrupt_recreate;
|
||||
#endif
|
||||
InterruptUpdate _interrupt_update;
|
||||
|
||||
bool _mig_wait_primary;
|
||||
bool _check_diff;
|
||||
friend class SetModeEvent;
|
||||
friend class CreatePrimarySurfaceEvent;
|
||||
friend class DestroyPrimarySurfaceEvent;
|
||||
friend class CreateSurfaceEvent;
|
||||
friend class DestroySurfaceEvent;
|
||||
friend class DestroyAllSurfacesEvent;
|
||||
friend class ActivateTimerEvent;
|
||||
friend class VideoStream;
|
||||
friend class StreamsTrigger;
|
||||
friend class GLInterupt;
|
||||
friend class StreamsTimer;
|
||||
friend class AttachChannelsEvent;
|
||||
friend class DetachChannelsEvent;
|
||||
friend class MigPrimarySurfaceTimer;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,94 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_EVENT_SOURCES
|
||||
#define _H_EVENT_SOURCES
|
||||
|
||||
#include "common.h"
|
||||
#include "event_sources_p.h"
|
||||
|
||||
class EventSource;
|
||||
|
||||
// TODO: the class is not thread safe
|
||||
class EventSources: public EventSources_p {
|
||||
public:
|
||||
class Trigger;
|
||||
class Socket;
|
||||
class File;
|
||||
class Handle;
|
||||
|
||||
EventSources();
|
||||
virtual ~EventSources();
|
||||
|
||||
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 add_handle(Handle& handle);
|
||||
void remove_handle(Handle& handle);
|
||||
|
||||
/* return true if the events loop should quit */
|
||||
bool wait_events(int timeout_ms = INFINITE);
|
||||
};
|
||||
|
||||
class EventSource {
|
||||
public:
|
||||
virtual ~EventSource() {}
|
||||
virtual void on_event() = 0;
|
||||
|
||||
private:
|
||||
virtual void action() {on_event();}
|
||||
|
||||
friend class EventSources;
|
||||
};
|
||||
|
||||
class EventSources::Trigger: public EventSource, private Trigger_p {
|
||||
public:
|
||||
Trigger();
|
||||
virtual ~Trigger();
|
||||
virtual void trigger();
|
||||
virtual void reset();
|
||||
|
||||
private:
|
||||
virtual void action();
|
||||
|
||||
friend class EventSources;
|
||||
};
|
||||
|
||||
class EventSources::Socket: public EventSource {
|
||||
protected:
|
||||
virtual int get_socket() = 0;
|
||||
|
||||
friend class EventSources;
|
||||
};
|
||||
|
||||
|
||||
class EventSources::File: public EventSource {
|
||||
protected:
|
||||
virtual int get_fd() = 0;
|
||||
|
||||
friend class EventSources;
|
||||
};
|
||||
|
||||
class EventSources::Handle: public EventSource, public Handle_p {
|
||||
|
||||
friend class EventSources;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,376 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
#include "foreign_menu.h"
|
||||
#include <spice/foreign_menu_prot.h>
|
||||
#include "menu.h"
|
||||
#include "utils.h"
|
||||
#include "debug.h"
|
||||
#include "platform.h"
|
||||
|
||||
#define PIPE_NAME_MAX_LEN 50
|
||||
|
||||
#ifdef WIN32
|
||||
#define PIPE_NAME "SpiceForeignMenu-%lu"
|
||||
#elif defined(__i386__) || __SIZEOF_LONG__ == 4
|
||||
#define PIPE_NAME "/tmp/SpiceForeignMenu-%llu.uds"
|
||||
#else
|
||||
#define PIPE_NAME "/tmp/SpiceForeignMenu-%lu.uds"
|
||||
#endif
|
||||
|
||||
ForeignMenu::ForeignMenu(ForeignMenuInterface *handler, bool active)
|
||||
: _handler (handler)
|
||||
, _active (active)
|
||||
, _refs (1)
|
||||
{
|
||||
char pipe_name[PIPE_NAME_MAX_LEN];
|
||||
|
||||
ASSERT(_handler != NULL);
|
||||
#ifndef WIN32
|
||||
const char *p_socket = getenv("SPICE_FOREIGN_MENU_SOCKET");
|
||||
if (p_socket) {
|
||||
LOG_INFO("Creating a foreign menu connection %s", p_socket);
|
||||
_foreign_menu = NamedPipe::create(p_socket, *this);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
snprintf(pipe_name, PIPE_NAME_MAX_LEN, PIPE_NAME, Platform::get_process_id());
|
||||
LOG_INFO("Creating a foreign menu connection %s", pipe_name);
|
||||
_foreign_menu = NamedPipe::create(pipe_name, *this);
|
||||
}
|
||||
if (!_foreign_menu) {
|
||||
LOG_ERROR("Failed to create a foreign menu connection");
|
||||
}
|
||||
}
|
||||
|
||||
ForeignMenu::~ForeignMenu()
|
||||
{
|
||||
std::map<NamedPipe::ConnectionRef, ForeignMenuConnection*>::const_iterator conn;
|
||||
for (conn = _connections.begin(); conn != _connections.end(); ++conn) {
|
||||
conn->second->reset_handler();
|
||||
delete conn->second;
|
||||
}
|
||||
if (_foreign_menu) {
|
||||
NamedPipe::destroy(_foreign_menu);
|
||||
}
|
||||
}
|
||||
|
||||
NamedPipe::ConnectionInterface& ForeignMenu::create()
|
||||
{
|
||||
ForeignMenuConnection *conn = new ForeignMenuConnection(_handler, *this);
|
||||
|
||||
if (conn == NULL) {
|
||||
throw Exception("Error allocating a new foreign menu connection");
|
||||
}
|
||||
return *conn;
|
||||
}
|
||||
|
||||
void ForeignMenu::add_connection(NamedPipe::ConnectionRef conn_ref, ForeignMenuConnection *conn)
|
||||
{
|
||||
_connections[conn_ref] = conn;
|
||||
if (_active) {
|
||||
send_active_state(conn, FOREIGN_MENU_APP_ACTIVATED);
|
||||
}
|
||||
conn->on_data();
|
||||
}
|
||||
|
||||
void ForeignMenu::remove_connection(NamedPipe::ConnectionRef conn_ref)
|
||||
{
|
||||
ForeignMenuConnection *conn = _connections[conn_ref];
|
||||
_connections.erase(conn_ref);
|
||||
delete conn;
|
||||
}
|
||||
|
||||
void ForeignMenu::add_sub_menus()
|
||||
{
|
||||
std::map<NamedPipe::ConnectionRef, ForeignMenuConnection*>::const_iterator conn;
|
||||
for (conn = _connections.begin(); conn != _connections.end(); ++conn) {
|
||||
conn->second->add_sub_menu();
|
||||
}
|
||||
}
|
||||
|
||||
void ForeignMenu::on_command(NamedPipe::ConnectionRef conn_ref, int32_t id)
|
||||
{
|
||||
ForeignMenuConnection *conn = _connections[conn_ref];
|
||||
FrgMenuEvent msg;
|
||||
|
||||
ASSERT(conn);
|
||||
msg.base.id = FOREIGN_MENU_ITEM_EVENT;
|
||||
msg.base.size = sizeof(FrgMenuEvent);
|
||||
msg.id = id;
|
||||
msg.action = FOREIGN_MENU_EVENT_CLICK;
|
||||
conn->write_msg(&msg.base, msg.base.size);
|
||||
}
|
||||
|
||||
void ForeignMenu::on_activate()
|
||||
{
|
||||
std::map<NamedPipe::ConnectionRef, ForeignMenuConnection*>::const_iterator conn;
|
||||
_active = true;
|
||||
for (conn = _connections.begin(); conn != _connections.end(); ++conn) {
|
||||
send_active_state(conn->second, FOREIGN_MENU_APP_ACTIVATED);
|
||||
}
|
||||
}
|
||||
|
||||
void ForeignMenu::on_deactivate()
|
||||
{
|
||||
std::map<NamedPipe::ConnectionRef, ForeignMenuConnection*>::const_iterator conn;
|
||||
_active = false;
|
||||
for (conn = _connections.begin(); conn != _connections.end(); ++conn) {
|
||||
send_active_state(conn->second, FOREIGN_MENU_APP_DEACTIVATED);
|
||||
}
|
||||
}
|
||||
|
||||
void ForeignMenu::send_active_state(ForeignMenuConnection *conn, int32_t cmd)
|
||||
{
|
||||
FrgMenuMsg msg;
|
||||
|
||||
ASSERT(conn != NULL);
|
||||
msg.id = cmd;
|
||||
msg.size = sizeof(FrgMenuMsg);
|
||||
conn->write_msg(&msg, msg.size);
|
||||
}
|
||||
|
||||
ForeignMenuConnection::ForeignMenuConnection(ForeignMenuInterface *handler, ForeignMenu& parent)
|
||||
: _handler (handler)
|
||||
, _parent (parent)
|
||||
, _sub_menu (NULL)
|
||||
, _initialized (false)
|
||||
, _write_pending (0)
|
||||
, _write_pos (_write_buf)
|
||||
, _read_pos (_read_buf)
|
||||
{
|
||||
}
|
||||
|
||||
ForeignMenuConnection::~ForeignMenuConnection()
|
||||
{
|
||||
if (_opaque != NamedPipe::INVALID_CONNECTION) {
|
||||
NamedPipe::destroy_connection(_opaque);
|
||||
}
|
||||
if (_handler) {
|
||||
AutoRef<Menu> app_menu(_handler->get_app_menu());
|
||||
(*app_menu)->remove_sub(_sub_menu);
|
||||
_handler->update_menu();
|
||||
_handler->clear_menu_items(_opaque);
|
||||
}
|
||||
if (_sub_menu) {
|
||||
_sub_menu->unref();
|
||||
}
|
||||
}
|
||||
|
||||
void ForeignMenuConnection::bind(NamedPipe::ConnectionRef conn_ref)
|
||||
{
|
||||
_opaque = conn_ref;
|
||||
_parent.add_connection(conn_ref, this);
|
||||
}
|
||||
|
||||
void ForeignMenuConnection::on_data()
|
||||
{
|
||||
if (_write_pending) {
|
||||
LOG_INFO("Resume pending write %d", _write_pending);
|
||||
if (!write_msg(_write_pos, _write_pending)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
while (read_msgs());
|
||||
}
|
||||
|
||||
bool ForeignMenuConnection::read_msgs()
|
||||
{
|
||||
uint8_t *pos = _read_buf;
|
||||
size_t nread = _read_pos - _read_buf;
|
||||
int32_t size;
|
||||
|
||||
ASSERT(_handler);
|
||||
ASSERT(_opaque != NamedPipe::INVALID_CONNECTION);
|
||||
size = NamedPipe::read(_opaque, (uint8_t*)_read_pos, sizeof(_read_buf) - nread);
|
||||
if (size == 0) {
|
||||
return false;
|
||||
} else if (size < 0) {
|
||||
LOG_ERROR("Error reading from named pipe %d", size);
|
||||
_parent.remove_connection(_opaque);
|
||||
return false;
|
||||
}
|
||||
nread += size;
|
||||
while (nread > 0) {
|
||||
if (!_initialized && nread >= sizeof(FrgMenuInitHeader)) {
|
||||
FrgMenuInitHeader *init = (FrgMenuInitHeader *)pos;
|
||||
if (init->magic != FOREIGN_MENU_MAGIC || init->version != FOREIGN_MENU_VERSION) {
|
||||
LOG_ERROR("Bad foreign menu init, magic=0x%x version=%u", init->magic,
|
||||
init->version);
|
||||
_parent.remove_connection(_opaque);
|
||||
return false;
|
||||
}
|
||||
if (nread < init->size) {
|
||||
break;
|
||||
}
|
||||
if (!handle_init((FrgMenuInit*)init)) {
|
||||
_parent.remove_connection(_opaque);
|
||||
return false;
|
||||
}
|
||||
nread -= init->size;
|
||||
pos += init->size;
|
||||
_initialized = true;
|
||||
}
|
||||
if (!_initialized || nread < sizeof(FrgMenuMsg)) {
|
||||
break;
|
||||
}
|
||||
FrgMenuMsg *hdr = (FrgMenuMsg *)pos;
|
||||
if (hdr->size < sizeof(FrgMenuMsg)) {
|
||||
LOG_ERROR("Bad foreign menu message, size=%u", hdr->size);
|
||||
_parent.remove_connection(_opaque);
|
||||
return false;
|
||||
}
|
||||
if (nread < hdr->size) {
|
||||
break;
|
||||
}
|
||||
handle_message(hdr);
|
||||
nread -= hdr->size;
|
||||
pos += hdr->size;
|
||||
}
|
||||
if (nread > 0 && pos != _read_buf) {
|
||||
memmove(_read_buf, pos, nread);
|
||||
}
|
||||
_read_pos = _read_buf + nread;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ForeignMenuConnection::write_msg(const void *buf, int len)
|
||||
{
|
||||
RecurciveLock lock(_write_lock);
|
||||
uint8_t *pos;
|
||||
int32_t written = 0;
|
||||
|
||||
ASSERT(_opaque != NamedPipe::INVALID_CONNECTION);
|
||||
if (_write_pending && buf != _write_pos) {
|
||||
if ((_write_pos + _write_pending + len > _write_buf + sizeof(_write_buf)) &&
|
||||
!write_msg(_write_pos, _write_pending)) {
|
||||
return false;
|
||||
}
|
||||
if (_write_pending) {
|
||||
if (_write_pos + _write_pending + len > _write_buf + sizeof(_write_buf)) {
|
||||
DBG(0, "Dropping message, due to insufficient space in write buffer");
|
||||
return true;
|
||||
}
|
||||
memcpy(_write_pos + _write_pending, buf, len);
|
||||
_write_pending += len;
|
||||
}
|
||||
}
|
||||
pos = (uint8_t*)buf;
|
||||
while (len && (written = NamedPipe::write(_opaque, pos, len)) > 0) {
|
||||
pos += written;
|
||||
len -= written;
|
||||
}
|
||||
if (len && written == 0) {
|
||||
if (_write_pending) {
|
||||
_write_pos = pos;
|
||||
} else {
|
||||
_write_pos = _write_buf;
|
||||
memcpy(_write_buf, pos, len);
|
||||
}
|
||||
_write_pending = len;
|
||||
} else if (written < 0) {
|
||||
LOG_ERROR("Error writing to named pipe %d", written);
|
||||
_parent.remove_connection(_opaque);
|
||||
return false;
|
||||
} else {
|
||||
_write_pending = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ForeignMenuConnection::handle_init(FrgMenuInit *init)
|
||||
{
|
||||
std::string title = "Untitled";
|
||||
|
||||
ASSERT(_handler);
|
||||
if (_sub_menu) {
|
||||
LOG_ERROR("Foreign menu already initialized");
|
||||
return false;
|
||||
}
|
||||
if (init->credentials != 0) {
|
||||
LOG_ERROR("Foreign menu has wrong credentials 0x%x", init->credentials);
|
||||
return false;
|
||||
}
|
||||
if (init->base.size > offsetof(FrgMenuInit, title)) {
|
||||
((char*)init)[init->base.size - 1] = '\0';
|
||||
title = (char*)init->title;
|
||||
}
|
||||
_sub_menu = new Menu((CommandTarget&)*_handler, title);
|
||||
add_sub_menu();
|
||||
_handler->update_menu();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ForeignMenuConnection::add_sub_menu()
|
||||
{
|
||||
if (_sub_menu) {
|
||||
AutoRef<Menu> app_menu(_handler->get_app_menu());
|
||||
(*app_menu)->add_sub(_sub_menu);
|
||||
}
|
||||
}
|
||||
|
||||
bool ForeignMenuConnection::handle_message(FrgMenuMsg *hdr)
|
||||
{
|
||||
ASSERT(_sub_menu);
|
||||
ASSERT(_handler);
|
||||
switch (hdr->id) {
|
||||
case FOREIGN_MENU_SET_TITLE:
|
||||
((char*)hdr)[hdr->size - 1] = '\0';
|
||||
_sub_menu->set_name((char*)((FrgMenuSetTitle*)hdr)->string);
|
||||
break;
|
||||
case FOREIGN_MENU_ADD_ITEM: {
|
||||
FrgMenuAddItem *msg = (FrgMenuAddItem*)hdr;
|
||||
((char*)hdr)[hdr->size - 1] = '\0';
|
||||
int id = _handler->get_foreign_menu_item_id(_opaque, msg->id);
|
||||
_sub_menu->add_command((char*)msg->string, id, get_item_state(msg->type));
|
||||
break;
|
||||
}
|
||||
case FOREIGN_MENU_REMOVE_ITEM: {
|
||||
int id = _handler->get_foreign_menu_item_id(_opaque, ((FrgMenuRmItem*)hdr)->id);
|
||||
_sub_menu->remove_command(id);
|
||||
_handler->remove_menu_item(id);
|
||||
break;
|
||||
}
|
||||
case FOREIGN_MENU_CLEAR:
|
||||
_sub_menu->clear();
|
||||
_handler->clear_menu_items(_opaque);
|
||||
break;
|
||||
case FOREIGN_MENU_MODIFY_ITEM:
|
||||
default:
|
||||
LOG_ERROR("Ignoring an unknown foreign menu identifier %u", hdr->id);
|
||||
return false;
|
||||
}
|
||||
_handler->update_menu();
|
||||
return true;
|
||||
}
|
||||
|
||||
int ForeignMenuConnection::get_item_state(int item_type)
|
||||
{
|
||||
int state = 0;
|
||||
|
||||
if (item_type & FOREIGN_MENU_ITEM_TYPE_CHECKED) {
|
||||
state |= Menu::MENU_ITEM_STATE_CHECKED;
|
||||
}
|
||||
if (item_type & FOREIGN_MENU_ITEM_TYPE_DIM) {
|
||||
state |= Menu::MENU_ITEM_STATE_DIM;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
@ -1,98 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_FOREIGN_MENU
|
||||
#define _H_FOREIGN_MENU
|
||||
|
||||
#include "named_pipe.h"
|
||||
#include "menu.h"
|
||||
|
||||
class ForeignMenuConnection;
|
||||
struct FrgMenuInit;
|
||||
struct FrgMenuMsg;
|
||||
|
||||
class ForeignMenuInterface : public CommandTarget {
|
||||
public:
|
||||
virtual ~ForeignMenuInterface() {}
|
||||
|
||||
virtual int get_foreign_menu_item_id(int32_t opaque_conn_ref, uint32_t msg_id) = 0;
|
||||
virtual void clear_menu_items(int32_t opaque_conn_ref) = 0;
|
||||
virtual void remove_menu_item(int item_id) = 0;
|
||||
virtual Menu* get_app_menu() = 0;
|
||||
virtual void update_menu() = 0;
|
||||
};
|
||||
|
||||
class ForeignMenu : public NamedPipe::ListenerInterface {
|
||||
public:
|
||||
ForeignMenu(ForeignMenuInterface *handler, bool active = false);
|
||||
virtual ~ForeignMenu();
|
||||
|
||||
ForeignMenu* ref() { _refs++; return this;}
|
||||
void unref() { if (!--_refs) delete this;}
|
||||
|
||||
virtual NamedPipe::ConnectionInterface &create();
|
||||
void add_connection(NamedPipe::ConnectionRef conn_ref, ForeignMenuConnection *conn);
|
||||
void remove_connection(NamedPipe::ConnectionRef conn_ref);
|
||||
void add_sub_menus();
|
||||
void on_command(NamedPipe::ConnectionRef conn_ref, int32_t id);
|
||||
void on_activate();
|
||||
void on_deactivate();
|
||||
|
||||
private:
|
||||
void send_active_state(ForeignMenuConnection *conn, int32_t cmd);
|
||||
|
||||
private:
|
||||
ForeignMenuInterface *_handler;
|
||||
std::map<NamedPipe::ConnectionRef, ForeignMenuConnection*> _connections;
|
||||
NamedPipe::ListenerRef _foreign_menu;
|
||||
bool _active;
|
||||
int _refs;
|
||||
};
|
||||
|
||||
#define FOREIGN_MENU_BUF_SIZE 4096
|
||||
|
||||
class ForeignMenuConnection : public NamedPipe::ConnectionInterface {
|
||||
public:
|
||||
ForeignMenuConnection(ForeignMenuInterface *handler, ForeignMenu& parent);
|
||||
virtual ~ForeignMenuConnection();
|
||||
|
||||
virtual void bind(NamedPipe::ConnectionRef conn_ref);
|
||||
virtual void on_data();
|
||||
bool write_msg(const void *buf, int len);
|
||||
void reset_handler() { _handler = NULL;}
|
||||
void add_sub_menu();
|
||||
|
||||
private:
|
||||
bool read_msgs();
|
||||
bool handle_init(FrgMenuInit *init);
|
||||
bool handle_message(FrgMenuMsg *hdr);
|
||||
int get_item_state(int item_type);
|
||||
|
||||
private:
|
||||
ForeignMenuInterface *_handler;
|
||||
ForeignMenu& _parent;
|
||||
Menu* _sub_menu;
|
||||
bool _initialized;
|
||||
int _write_pending;
|
||||
uint8_t *_write_pos;
|
||||
uint8_t *_read_pos;
|
||||
uint8_t _write_buf[FOREIGN_MENU_BUF_SIZE];
|
||||
uint8_t _read_buf[FOREIGN_MENU_BUF_SIZE];
|
||||
RecurciveMutex _write_lock;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,337 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
// 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) - assigns the pixel to the place pointed by out and
|
||||
increases out. Used in RLE.
|
||||
Need special handling because in alpha we copy only
|
||||
the pad byte.
|
||||
COPY_REF_PIXEL(ref, out) - copies the pixel pointed by ref to the pixel pointed by out.
|
||||
Increases ref and out.
|
||||
COPY_COMP_PIXEL(encoder, out) - copies pixel from the compressed buffer to the decompressed
|
||||
buffer. Increases out.
|
||||
*/
|
||||
|
||||
#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: separate into routines that decode to dist,len. and to a routine that
|
||||
// actually 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, SpicePalette *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
|
||||
@ -1,64 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_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); // palette 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
|
||||
@ -1,301 +0,0 @@
|
||||
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
#include "glz_decoder_config.h"
|
||||
#include "glz_decoder.h"
|
||||
|
||||
static void op_decode (SpiceGlzDecoder *decoder,
|
||||
uint8_t *data,
|
||||
SpicePalette *plt,
|
||||
void *usr_data)
|
||||
{
|
||||
GlzDecoder* _decoder = static_cast<GlzDecoder*>(decoder);
|
||||
_decoder->decode(data, plt, usr_data);
|
||||
}
|
||||
|
||||
GlzDecoder::GlzDecoder(GlzDecoderWindow &images_window,
|
||||
GlzDecodeHandler &usr_handler, GlzDecoderDebug &debug_calls)
|
||||
: _images_window (images_window)
|
||||
, _usr_handler (usr_handler)
|
||||
, _debug_calls (debug_calls)
|
||||
{
|
||||
static SpiceGlzDecoderOps decoder_ops = {
|
||||
op_decode,
|
||||
};
|
||||
ops = &decoder_ops;
|
||||
}
|
||||
|
||||
GlzDecoder::~GlzDecoder()
|
||||
{
|
||||
}
|
||||
|
||||
void GlzDecoder::decode_header()
|
||||
{
|
||||
uint32_t 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, SpicePalette *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, SpicePalette *palette, void *opaque_usr_info)
|
||||
{
|
||||
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
|
||||
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]));
|
||||
}
|
||||
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);
|
||||
}
|
||||
@ -1,84 +0,0 @@
|
||||
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_GLZ_DECODER
|
||||
#define _H_GLZ_DECODER
|
||||
|
||||
#include "common/lz_common.h"
|
||||
#include "glz_decoder_config.h"
|
||||
#include "glz_decoder_window.h"
|
||||
#include "red_canvas_base.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 SpiceGlzDecoder
|
||||
{
|
||||
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, SpicePalette *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
|
||||
@ -1,64 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_GLZ_DECODER_CONFIG
|
||||
#define _H_GLZ_DECODER_CONFIG
|
||||
|
||||
#include <exception>
|
||||
#include <sstream>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <spice/types.h>
|
||||
#include <spice/macros.h>
|
||||
#include "common/lz_common.h"
|
||||
|
||||
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
|
||||
@ -1,329 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "glz_decoder_window.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define INIT_IMAGES_CAPACITY 100
|
||||
#define WIN_REALLOC_FACTOR 1.5
|
||||
|
||||
GlzDecoderWindow::GlzDecoderWindow(GlzDecoderDebug &debug_calls)
|
||||
: _aborting (false)
|
||||
, _debug_calls (debug_calls)
|
||||
{
|
||||
_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 occur, 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 changes to the window are
|
||||
// not allowed. In addition 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::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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
inline bool GlzDecoderWindow::will_overflow(uint64_t image_id, uint64_t relative_head_id)
|
||||
{
|
||||
if (image_id <= _tail_image_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
_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
|
||||
|
||||
/* maintaining 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;
|
||||
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);
|
||||
}
|
||||
@ -1,122 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_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(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();
|
||||
|
||||
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:
|
||||
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
|
||||
|
||||
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
|
||||
@ -1,20 +0,0 @@
|
||||
NULL =
|
||||
|
||||
EXTRA_DIST = \
|
||||
commonv2c.ttf.c \
|
||||
commonwealth-10.font.c \
|
||||
dejavu_sans-10.font.c \
|
||||
dejavu_sans.ttf.c \
|
||||
gui.cpp \
|
||||
gui.h \
|
||||
resource_provider.cpp \
|
||||
resource_provider.h \
|
||||
softrenderer.cpp \
|
||||
softrenderer.h \
|
||||
softtexture.cpp \
|
||||
softtexture.h \
|
||||
taharez_look.imageset.c \
|
||||
taharez_look.looknfeel.c \
|
||||
taharez_look.scheme.c \
|
||||
taharez_look.tga.c \
|
||||
$(NULL)
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,12 +0,0 @@
|
||||
const unsigned char commonwealth_10_font[] = {
|
||||
0x3c,0x3f,0x78,0x6d,0x6c,0x20,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x3d,0x22,0x31,
|
||||
0x2e,0x30,0x22,0x20,0x3f,0x3e,0x0a,0x3c,0x46,0x6f,0x6e,0x74,0x20,0x4e,0x61,0x6d,
|
||||
0x65,0x3d,0x22,0x43,0x6f,0x6d,0x6d,0x6f,0x6e,0x77,0x65,0x61,0x6c,0x74,0x68,0x2d,
|
||||
0x31,0x30,0x22,0x20,0x46,0x69,0x6c,0x65,0x6e,0x61,0x6d,0x65,0x3d,0x22,0x43,0x6f,
|
||||
0x6d,0x6d,0x6f,0x6e,0x76,0x32,0x63,0x2e,0x74,0x74,0x66,0x22,0x20,0x54,0x79,0x70,
|
||||
0x65,0x3d,0x22,0x46,0x72,0x65,0x65,0x54,0x79,0x70,0x65,0x22,0x20,0x53,0x69,0x7a,
|
||||
0x65,0x3d,0x22,0x31,0x30,0x22,0x20,0x4e,0x61,0x74,0x69,0x76,0x65,0x48,0x6f,0x72,
|
||||
0x7a,0x52,0x65,0x73,0x3d,0x22,0x38,0x30,0x30,0x22,0x20,0x4e,0x61,0x74,0x69,0x76,
|
||||
0x65,0x56,0x65,0x72,0x74,0x52,0x65,0x73,0x3d,0x22,0x36,0x30,0x30,0x22,0x20,0x41,
|
||||
0x75,0x74,0x6f,0x53,0x63,0x61,0x6c,0x65,0x64,0x3d,0x22,0x74,0x72,0x75,0x65,0x22,
|
||||
0x2f,0x3e,0x0a};
|
||||
@ -1,12 +0,0 @@
|
||||
const unsigned char dejavu_sans_10_font[] = {
|
||||
0x3c,0x3f,0x78,0x6d,0x6c,0x20,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x3d,0x22,0x31,
|
||||
0x2e,0x30,0x22,0x20,0x3f,0x3e,0x0a,0x3c,0x46,0x6f,0x6e,0x74,0x20,0x4e,0x61,0x6d,
|
||||
0x65,0x3d,0x22,0x44,0x65,0x6a,0x61,0x56,0x75,0x53,0x61,0x6e,0x73,0x2d,0x31,0x30,
|
||||
0x22,0x20,0x46,0x69,0x6c,0x65,0x6e,0x61,0x6d,0x65,0x3d,0x22,0x44,0x65,0x6a,0x61,
|
||||
0x56,0x75,0x53,0x61,0x6e,0x73,0x2e,0x74,0x74,0x66,0x22,0x20,0x54,0x79,0x70,0x65,
|
||||
0x3d,0x22,0x46,0x72,0x65,0x65,0x54,0x79,0x70,0x65,0x22,0x20,0x53,0x69,0x7a,0x65,
|
||||
0x3d,0x22,0x31,0x30,0x22,0x20,0x4e,0x61,0x74,0x69,0x76,0x65,0x48,0x6f,0x72,0x7a,
|
||||
0x52,0x65,0x73,0x3d,0x22,0x38,0x30,0x30,0x22,0x20,0x4e,0x61,0x74,0x69,0x76,0x65,
|
||||
0x56,0x65,0x72,0x74,0x52,0x65,0x73,0x3d,0x22,0x36,0x30,0x30,0x22,0x20,0x41,0x75,
|
||||
0x74,0x6f,0x53,0x63,0x61,0x6c,0x65,0x64,0x3d,0x22,0x74,0x72,0x75,0x65,0x22,0x2f,
|
||||
0x3e,0x0a};
|
||||
File diff suppressed because it is too large
Load Diff
1418
client/gui/gui.cpp
1418
client/gui/gui.cpp
File diff suppressed because it is too large
Load Diff
143
client/gui/gui.h
143
client/gui/gui.h
@ -1,143 +0,0 @@
|
||||
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
Copyright (C) 2010 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef _H_GUI
|
||||
#define _H_GUI
|
||||
|
||||
#include "softrenderer.h"
|
||||
#include "screen_layer.h"
|
||||
#include "inputs_handler.h"
|
||||
#include "application.h"
|
||||
|
||||
class RedPixmapSw;
|
||||
|
||||
class GUI : public ScreenLayer, public KeyHandler {
|
||||
public:
|
||||
class Dialog;
|
||||
class Tab;
|
||||
class TabFactory;
|
||||
|
||||
typedef std::list<TabFactory*> TabFactorys;
|
||||
|
||||
GUI(Application& app, Application::State state);
|
||||
virtual ~GUI();
|
||||
|
||||
void set_screen(RedScreen* screen); //show and hide
|
||||
|
||||
Application& get_application() { return _app;}
|
||||
#ifdef USE_GUI
|
||||
CEGUI::System& gui_system() { return *_gui_system;}
|
||||
#endif // USE_GUI
|
||||
|
||||
void set_state(Application::State state);
|
||||
bool is_visible() { return !!_dialog;}
|
||||
bool prepare_dialog();
|
||||
bool is_disconnect_allowed() { return _app.is_disconnect_allowed();}
|
||||
|
||||
virtual bool pointer_test(int x, int y) { return contains_point(x, y);}
|
||||
virtual void on_pointer_enter(int x, int y, unsigned int buttons_state);
|
||||
virtual void on_pointer_leave();
|
||||
virtual void on_pointer_motion(int x, int y, unsigned int buttons_state);
|
||||
virtual void on_mouse_button_press(int button, int buttons_state);
|
||||
virtual void on_mouse_button_release(int button, int buttons_state);
|
||||
|
||||
virtual void on_key_down(RedKey key);
|
||||
virtual void on_key_up(RedKey key);
|
||||
virtual void on_char(uint32_t ch);
|
||||
virtual bool permit_focus_loss() { return false;}
|
||||
|
||||
void idle();
|
||||
|
||||
virtual void copy_pixels(const QRegion& dest_region, RedDrawable& dest_dc);
|
||||
virtual void on_size_changed();
|
||||
|
||||
void register_tab_factory(TabFactory& factory);
|
||||
void unregister_tab_factory(TabFactory& factory);
|
||||
|
||||
class BoxResponse {
|
||||
public:
|
||||
virtual ~BoxResponse() {}
|
||||
virtual void response(int response) = 0;
|
||||
virtual void aborted() = 0;
|
||||
};
|
||||
|
||||
enum MessageType {
|
||||
QUESTION,
|
||||
INFO,
|
||||
WARNING,
|
||||
ERROR_MSG
|
||||
};
|
||||
|
||||
struct ButtonInfo {
|
||||
int id;
|
||||
const char *text;
|
||||
};
|
||||
|
||||
typedef std::vector<ButtonInfo> ButtonsList;
|
||||
bool message_box(MessageType type, const char *text, const ButtonsList& buttons,
|
||||
BoxResponse* _response_handler);
|
||||
|
||||
private:
|
||||
TabFactorys& get_factoris() { return _tab_factorys;}
|
||||
|
||||
void create_dialog();
|
||||
void detach();
|
||||
void update_layer_area();
|
||||
void init_cegui();
|
||||
void conditional_update();
|
||||
void set_dialog(Dialog* dialog);
|
||||
void dettach_dialog(Dialog* dialog);
|
||||
|
||||
private:
|
||||
Application& _app;
|
||||
Application::State _state;
|
||||
RedPixmapSw* _pixmap;
|
||||
#ifdef USE_GUI
|
||||
CEGUI::SoftRenderer* _renderer;
|
||||
CEGUI::System* _gui_system;
|
||||
#endif // USE_GUI
|
||||
Dialog* _dialog;
|
||||
uint64_t _prev_time;
|
||||
TabFactorys _tab_factorys;
|
||||
|
||||
friend class Dialog;
|
||||
};
|
||||
|
||||
class GUI::Tab {
|
||||
public:
|
||||
virtual ~Tab() {}
|
||||
|
||||
#ifdef USE_GUI
|
||||
virtual CEGUI::Window& get_root_window() = 0;
|
||||
#endif // USE_GUI
|
||||
virtual const std::string& get_name() = 0;
|
||||
};
|
||||
|
||||
class GUI::TabFactory {
|
||||
public:
|
||||
TabFactory() : _order (-1) {}
|
||||
TabFactory(int order) : _order (order) {}
|
||||
|
||||
virtual ~TabFactory() {}
|
||||
virtual Tab* create_tab(bool connected, int width, int hight) = 0;
|
||||
int get_order() { return _order;}
|
||||
|
||||
private:
|
||||
int _order;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,148 +0,0 @@
|
||||
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
Copyright (C) 2010 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "resource_provider.h"
|
||||
#include "debug.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "CEGUIExceptions.h"
|
||||
|
||||
#include "taharez_look.scheme.c"
|
||||
#include "taharez_look.imageset.c"
|
||||
#include "taharez_look.tga.c"
|
||||
#include "commonwealth-10.font.c"
|
||||
#include "commonv2c.ttf.c"
|
||||
#include "taharez_look.looknfeel.c"
|
||||
#include "dejavu_sans-10.font.c"
|
||||
#include "dejavu_sans.ttf.c"
|
||||
|
||||
//(echo "const unsigned char <struct name>[] =
|
||||
//{"; od -txC -v <in file name> | sed -e "s/^[0-9]*//" -e s"/ \([0-9a-f][0-9a-f]\)/0x\1,/g" -e"\$d"
|
||||
//| sed -e"\$s/,$/};/") > <out file name>.c
|
||||
|
||||
void CEGUIResourceProvider::loadRawDataContainer(const CEGUI::String &filename,
|
||||
CEGUI::RawDataContainer &output,
|
||||
const CEGUI::String &resourceGroup)
|
||||
{
|
||||
DBG(0, "%s", filename.c_str());
|
||||
if (strcmp(filename.c_str(), "TaharezLook.scheme") == 0) {
|
||||
DBG(0, "size %d", sizeof(taharez_look_schem));
|
||||
output.setData((CEGUI::uint8*)taharez_look_schem);
|
||||
output.setSize(sizeof(taharez_look_schem));
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(filename.c_str(), "TaharezLook.imageset") == 0) {
|
||||
DBG(0, "size %d", sizeof(taharez_look_imageset));
|
||||
output.setData((CEGUI::uint8*)taharez_look_imageset);
|
||||
output.setSize(sizeof(taharez_look_imageset));
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(filename.c_str(), "TaharezLook.tga") == 0) {
|
||||
DBG(0, "size %d", sizeof(taharez_look_tga));
|
||||
output.setData((CEGUI::uint8*)taharez_look_tga);
|
||||
output.setSize(sizeof(taharez_look_tga));
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(filename.c_str(), "Commonwealth-10.font") == 0) {
|
||||
DBG(0, "size %d", sizeof(commonwealth_10_font));
|
||||
output.setData((CEGUI::uint8*)commonwealth_10_font);
|
||||
output.setSize(sizeof(commonwealth_10_font));
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(filename.c_str(), "Commonv2c.ttf") == 0) {
|
||||
DBG(0, "size %d", sizeof(commonv2c_ttf));
|
||||
output.setData((CEGUI::uint8*)commonv2c_ttf);
|
||||
output.setSize(sizeof(commonv2c_ttf));
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(filename.c_str(), "TaharezLook.looknfeel") == 0) {
|
||||
DBG(0, "size %d", sizeof(taharez_look_looknfeel));
|
||||
output.setData((CEGUI::uint8*)taharez_look_looknfeel);
|
||||
output.setSize(sizeof(taharez_look_looknfeel));
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(filename.c_str(), "DejaVuSans-10.font") == 0) {
|
||||
DBG(0, "size %d", sizeof(dejavu_sans_10_font));
|
||||
output.setData((CEGUI::uint8*)dejavu_sans_10_font);
|
||||
output.setSize(sizeof(dejavu_sans_10_font));
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(filename.c_str(), "DejaVuSans.ttf") == 0) {
|
||||
DBG(0, "size %d", sizeof(dejavu_sans_ttf));
|
||||
output.setData((CEGUI::uint8*)dejavu_sans_ttf);
|
||||
output.setSize(sizeof(dejavu_sans_ttf));
|
||||
return;
|
||||
}
|
||||
|
||||
throw CEGUI::GenericException("failed");
|
||||
}
|
||||
|
||||
void CEGUIResourceProvider::unloadRawDataContainer(CEGUI::RawDataContainer& data)
|
||||
{
|
||||
data.setData(NULL);
|
||||
data.setSize(0);
|
||||
}
|
||||
|
||||
struct ResString{
|
||||
int id;
|
||||
const char* str;
|
||||
} res_strings[] = {
|
||||
{STR_MESG_MISSING_HOST_NAME, "Missing host name"},
|
||||
{STR_MESG_INVALID_PORT, "Invalid port"},
|
||||
{STR_MESG_INVALID_SPORT, "Invalid sport"},
|
||||
{STR_MESG_MISSING_PORT, "Missing port"},
|
||||
{STR_MESG_CONNECTING, "Connecting to"},
|
||||
{STR_BUTTON_OK, "OK"},
|
||||
{STR_BUTTON_CANCEL, "Cancel"},
|
||||
{STR_BUTTON_CONNECT, "Connect"},
|
||||
{STR_BUTTON_QUIT, "Quit"},
|
||||
{STR_BUTTON_CLOSE, "Close"},
|
||||
{STR_BUTTON_DISCONNECT, "Disconnect"},
|
||||
{STR_BUTTON_OPTIONS, "Options"},
|
||||
{STR_BUTTON_BACK, "Back"},
|
||||
{STR_LABEL_HOST, "Host"},
|
||||
{STR_LABEL_PORT, "Port"},
|
||||
{STR_LABEL_SPORT, "Secure port"},
|
||||
{STR_LABEL_PASSWORD, "Password"},
|
||||
{0, NULL},
|
||||
};
|
||||
|
||||
const char* res_get_string(int id)
|
||||
{
|
||||
ResString *string;
|
||||
|
||||
for (string = res_strings; string->str; string++) {
|
||||
if (string->id == id) {
|
||||
return string->str;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -1,59 +0,0 @@
|
||||
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
Copyright (C) 2010 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef _H_RESOURCE_PROVIDER
|
||||
#define _H_RESOURCE_PROVIDER
|
||||
|
||||
/* CEGUI 0.6 bug, CEGUITexture.h doesn't include this, we need to */
|
||||
#include <cstddef>
|
||||
|
||||
#include "CEGUIDefaultResourceProvider.h"
|
||||
|
||||
class CEGUIResourceProvider: public CEGUI::ResourceProvider {
|
||||
public:
|
||||
virtual void loadRawDataContainer(const CEGUI::String &filename,
|
||||
CEGUI::RawDataContainer &output,
|
||||
const CEGUI::String &resourceGroup);
|
||||
|
||||
virtual void unloadRawDataContainer(CEGUI::RawDataContainer& data);
|
||||
};
|
||||
|
||||
enum {
|
||||
STR_INVALID,
|
||||
STR_MESG_MISSING_HOST_NAME,
|
||||
STR_MESG_INVALID_PORT,
|
||||
STR_MESG_INVALID_SPORT,
|
||||
STR_MESG_MISSING_PORT,
|
||||
STR_MESG_CONNECTING,
|
||||
STR_BUTTON_OK,
|
||||
STR_BUTTON_CANCEL,
|
||||
STR_BUTTON_CONNECT,
|
||||
STR_BUTTON_QUIT,
|
||||
STR_BUTTON_CLOSE,
|
||||
STR_BUTTON_DISCONNECT,
|
||||
STR_BUTTON_OPTIONS,
|
||||
STR_BUTTON_BACK,
|
||||
STR_LABEL_HOST,
|
||||
STR_LABEL_PORT,
|
||||
STR_LABEL_SPORT,
|
||||
STR_LABEL_PASSWORD,
|
||||
};
|
||||
|
||||
//todo: move to x11/res.cpp and make x11/res.cpp cross-platform
|
||||
const char* res_get_string(int id);
|
||||
|
||||
#endif
|
||||
@ -1,390 +0,0 @@
|
||||
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
Copyright (C) 2010 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
#include "utils.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include "softrenderer.h"
|
||||
#include "softtexture.h"
|
||||
|
||||
#include "CEGUIExceptions.h"
|
||||
#include "CEGUIImageCodec.h"
|
||||
#include "CEGUIDynamicModule.h"
|
||||
#include "CEGUIEventArgs.h"
|
||||
|
||||
#define S_(X) #X
|
||||
#define STRINGIZE(X) S_(X)
|
||||
|
||||
namespace CEGUI {
|
||||
|
||||
SoftRenderer::SoftRenderer(uint8_t* surface, uint width, uint height, uint stride,
|
||||
ImageCodec* codec)
|
||||
: _surface (surface)
|
||||
, _width (width)
|
||||
, _height (height)
|
||||
, _image_codec (codec)
|
||||
, _image_codec_module (NULL)
|
||||
, _queueing(true)
|
||||
{
|
||||
assert(stride == width * 4); //for now
|
||||
if (!_image_codec) {
|
||||
setupImageCodec();
|
||||
}
|
||||
}
|
||||
|
||||
SoftRenderer::~SoftRenderer()
|
||||
{
|
||||
destroyAllTextures();
|
||||
cleanupImageCodec();
|
||||
}
|
||||
|
||||
|
||||
void SoftRenderer::reset_surface(uint8_t* surface, uint width, uint height, uint stride)
|
||||
{
|
||||
assert(stride == width * 4); //for now
|
||||
_surface = surface;
|
||||
_width = width;
|
||||
_height = height;
|
||||
|
||||
EventArgs args;
|
||||
fireEvent(EventDisplaySizeChanged, args, EventNamespace);
|
||||
}
|
||||
|
||||
#if defined(CEGUI_STATIC)
|
||||
extern "C" CEGUI::ImageCodec* createImageCodec(void);
|
||||
extern "C" void destroyImageCodec(CEGUI::ImageCodec*);
|
||||
#endif
|
||||
|
||||
void SoftRenderer::setupImageCodec()
|
||||
{
|
||||
#if defined(CEGUI_STATIC)
|
||||
_destroy_image_codec = destroyImageCodec;
|
||||
_image_codec = createImageCodec();
|
||||
#else
|
||||
String _default_codec_name(STRINGIZE(TGAImageCodec/*CEGUI_DEFAULT_IMAGE_CODEC*/));
|
||||
DynamicModule* module = NULL;
|
||||
|
||||
try {
|
||||
DynamicModule* module = new DynamicModule(String("CEGUI") + _default_codec_name);
|
||||
|
||||
_destroy_image_codec = (void(*)(ImageCodec*))module->getSymbolAddress("destroyImageCodec");
|
||||
|
||||
if (!_destroy_image_codec) {
|
||||
throw GenericException("Missing destroyImageCodec symbol");
|
||||
}
|
||||
|
||||
ImageCodec* (*create_f)(void);
|
||||
create_f = (ImageCodec* (*)(void))module->getSymbolAddress("createImageCodec");
|
||||
|
||||
if (!create_f) {
|
||||
throw GenericException("Missing createImageCodec symbol");
|
||||
}
|
||||
|
||||
_image_codec = create_f();
|
||||
} catch (...) {
|
||||
delete module;
|
||||
throw;
|
||||
}
|
||||
_image_codec_module = module;
|
||||
#endif
|
||||
}
|
||||
|
||||
void SoftRenderer::cleanupImageCodec()
|
||||
{
|
||||
_destroy_image_codec(_image_codec);
|
||||
delete _image_codec_module;
|
||||
}
|
||||
|
||||
static inline uint8_t calac_pixel(uint64_t c1, uint64_t c2, uint64_t c3, uint64_t a_mul)
|
||||
{
|
||||
//(c' * c" * a' * a" + c"' * 255 ^ 3 - c"' * a' * a" * 255) / 255^4
|
||||
|
||||
return uint8_t((c1 * c2 * a_mul + c3 * 255 * 255 * 255 - c3 * a_mul * 255) / (255 * 255 * 255));
|
||||
}
|
||||
|
||||
inline void SoftRenderer::componnentAtPoint(int x_pos, int y_pos,
|
||||
int top_left, int top_right,
|
||||
int bottom_left, int bottom_right,
|
||||
uint64_t& comp)
|
||||
{
|
||||
int a = top_left + (((x_pos * (top_right - top_left)) + (1 << 15)) >> 16);
|
||||
int b = bottom_left + (((x_pos * (bottom_right - bottom_left)) + (1 << 15)) >> 16);
|
||||
comp = a + (((b - a) * y_pos + (1 << 15)) >> 16);
|
||||
}
|
||||
|
||||
void SoftRenderer::colourAtPoint(int x, int x_max, int y, int y_max,
|
||||
const ColourIRect& colours,
|
||||
uint64_t& r, uint64_t& g,
|
||||
uint64_t& b, uint64_t& a)
|
||||
{
|
||||
int x_pos = (x << 16) / x_max;
|
||||
int y_pos = (y << 16) / y_max;
|
||||
|
||||
|
||||
componnentAtPoint(x_pos, y_pos, colours.top_left.r, colours.top_right.r,
|
||||
colours.bottom_left.r, colours.bottom_right.r, r);
|
||||
componnentAtPoint(x_pos, y_pos, colours.top_left.g, colours.top_right.g,
|
||||
colours.bottom_left.g, colours.bottom_right.g, g);
|
||||
componnentAtPoint(x_pos, y_pos, colours.top_left.b, colours.top_right.b,
|
||||
colours.bottom_left.b, colours.bottom_right.b, b);
|
||||
componnentAtPoint(x_pos, y_pos, colours.top_left.a, colours.top_right.a,
|
||||
colours.bottom_left.a, colours.bottom_right.a, a);
|
||||
}
|
||||
|
||||
void SoftRenderer::renderQuadWithColourRect(const QuadInfo& quad)
|
||||
{
|
||||
uint32_t* src = quad.tex->_surf + quad.tex_src.top * (int)quad.tex->getWidth();
|
||||
src += quad.tex_src.left;
|
||||
|
||||
|
||||
int src_width = quad.tex_src.right - quad.tex_src.left;
|
||||
int src_height = quad.tex_src.bottom - quad.tex_src.top;
|
||||
|
||||
int dest_width = quad.dest.right - quad.dest.left;
|
||||
int dest_height = quad.dest.bottom - quad.dest.top;
|
||||
|
||||
|
||||
uint32_t x_scale = (src_width << 16) / dest_width;
|
||||
uint32_t y_scale = (src_height << 16) / dest_height;
|
||||
|
||||
uint32_t* line = (uint32_t*)_surface + quad.dest.top * _width;
|
||||
line += quad.dest.left;
|
||||
|
||||
for (int i = 0; i < dest_height; line += _width, i++) {
|
||||
uint32_t* pix = line;
|
||||
uint32_t* src_line = src + (((i * y_scale) + (1 << 15)) >> 16) * (int)quad.tex->getWidth();
|
||||
|
||||
for (int j = 0; j < dest_width; pix++, j++) {
|
||||
uint64_t r;
|
||||
uint64_t g;
|
||||
uint64_t b;
|
||||
uint64_t a;
|
||||
|
||||
colourAtPoint(j, dest_width, i, dest_height, quad.colors, r, g, b, a);
|
||||
|
||||
uint8_t* tex_pix = (uint8_t*)&src_line[(((j * x_scale)+ (1 << 15)) >> 16)];
|
||||
uint64_t a_mul = a * tex_pix[3];
|
||||
|
||||
((uint8_t *)pix)[0] = calac_pixel(tex_pix[0], b, ((uint8_t *)pix)[0], a_mul);
|
||||
((uint8_t *)pix)[1] = calac_pixel(tex_pix[1], g, ((uint8_t *)pix)[1], a_mul);
|
||||
((uint8_t *)pix)[2] = calac_pixel(tex_pix[2], r, ((uint8_t *)pix)[2], a_mul);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SoftRenderer::renderQuad(const QuadInfo& quad)
|
||||
{
|
||||
if (!quad.colors.top_left.isSameColour(quad.colors.top_right) ||
|
||||
!quad.colors.top_left.isSameColour(quad.colors.bottom_left) ||
|
||||
!quad.colors.top_left.isSameColour(quad.colors.bottom_right)) {
|
||||
renderQuadWithColourRect(quad);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
uint32_t* src = quad.tex->_surf + quad.tex_src.top * (int)quad.tex->getWidth();
|
||||
src += quad.tex_src.left;
|
||||
|
||||
|
||||
int src_width = quad.tex_src.right - quad.tex_src.left;
|
||||
int src_height = quad.tex_src.bottom - quad.tex_src.top;
|
||||
|
||||
int dest_width = quad.dest.right - quad.dest.left;
|
||||
int dest_height = quad.dest.bottom - quad.dest.top;
|
||||
|
||||
|
||||
uint32_t x_scale = (src_width << 16) / dest_width;
|
||||
uint32_t y_scale = (src_height << 16) / dest_height;
|
||||
|
||||
uint32_t* line = (uint32_t*)_surface + quad.dest.top * _width;
|
||||
line += quad.dest.left;
|
||||
|
||||
uint64_t r = quad.colors.top_left.r;
|
||||
uint64_t g = quad.colors.top_left.g;
|
||||
uint64_t b = quad.colors.top_left.b;
|
||||
uint64_t a = quad.colors.top_left.a;
|
||||
|
||||
for (int i = 0; i < dest_height; line += _width, i++) {
|
||||
uint32_t* pix = line;
|
||||
uint32_t* src_line = src + (((i * y_scale) + (1 << 15)) >> 16) * (int)quad.tex->getWidth();
|
||||
|
||||
for (int j = 0; j < dest_width; pix++, j++) {
|
||||
uint8_t* tex_pix = (uint8_t*)&src_line[(((j * x_scale)+ (1 << 15)) >> 16)];
|
||||
uint64_t a_mul = a * tex_pix[3];
|
||||
|
||||
((uint8_t *)pix)[0] = calac_pixel(tex_pix[0], b, ((uint8_t *)pix)[0], a_mul);
|
||||
((uint8_t *)pix)[1] = calac_pixel(tex_pix[1], g, ((uint8_t *)pix)[1], a_mul);
|
||||
((uint8_t *)pix)[2] = calac_pixel(tex_pix[2], r, ((uint8_t *)pix)[2], a_mul);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void SoftRenderer::setRGB(ColourI& dest, const colour& src)
|
||||
{
|
||||
dest.r = uint8_t(src.getRed()* 255);
|
||||
dest.g = uint8_t(src.getGreen() * 255);
|
||||
dest.b = uint8_t(src.getBlue() * 255);
|
||||
dest.a = uint8_t(src.getAlpha() * 255);
|
||||
}
|
||||
|
||||
void SoftRenderer::addQuad(const Rect& dest_rect, float z, const Texture* texture,
|
||||
const Rect& texture_rect, const ColourRect& colours,
|
||||
QuadSplitMode quad_split_mode)
|
||||
{
|
||||
if (dest_rect.d_right <= dest_rect.d_left || dest_rect.d_bottom <= dest_rect.d_top) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (texture_rect.d_right <= texture_rect.d_left ||
|
||||
texture_rect.d_bottom <= texture_rect.d_top) {
|
||||
return;
|
||||
}
|
||||
|
||||
QuadInfo quad;
|
||||
quad.dest.top = (int)dest_rect.d_top;
|
||||
quad.dest.left = (int)dest_rect.d_left;
|
||||
quad.dest.bottom = (int)dest_rect.d_bottom;
|
||||
quad.dest.right = (int)dest_rect.d_right;
|
||||
|
||||
quad.tex = (const SoftTexture*)texture;
|
||||
|
||||
quad.tex_src.top = int(texture_rect.d_top * texture->getHeight());
|
||||
quad.tex_src.bottom = int(texture_rect.d_bottom * texture->getHeight());
|
||||
quad.tex_src.left = int(texture_rect.d_left * texture->getWidth());
|
||||
quad.tex_src.right = int(texture_rect.d_right * texture->getWidth());
|
||||
|
||||
setRGB(quad.colors.top_left, colours.d_top_left);
|
||||
setRGB(quad.colors.top_right, colours.d_top_right);
|
||||
setRGB(quad.colors.bottom_left, colours.d_bottom_left);
|
||||
setRGB(quad.colors.bottom_right, colours.d_bottom_right);
|
||||
|
||||
quad.z = z;
|
||||
|
||||
if (!_queueing) {
|
||||
renderQuad(quad);
|
||||
return;
|
||||
}
|
||||
|
||||
_queue.insert(quad);
|
||||
}
|
||||
|
||||
void SoftRenderer::doRender()
|
||||
{
|
||||
QuadQueue::iterator iter = _queue.begin();
|
||||
|
||||
for (; iter != _queue.end(); ++iter) {
|
||||
renderQuad(*iter);
|
||||
}
|
||||
}
|
||||
|
||||
void SoftRenderer::clearRenderList()
|
||||
{
|
||||
_queue.clear();
|
||||
}
|
||||
|
||||
void SoftRenderer::setQueueingEnabled(bool val)
|
||||
{
|
||||
_queueing = val;
|
||||
}
|
||||
|
||||
bool SoftRenderer::isQueueingEnabled() const
|
||||
{
|
||||
return _queueing;
|
||||
}
|
||||
|
||||
Texture* SoftRenderer::createTexture()
|
||||
{
|
||||
SoftTexture* texture = new SoftTexture(this);
|
||||
_textures.push_back(texture);
|
||||
return texture;
|
||||
}
|
||||
|
||||
Texture* SoftRenderer::createTexture(const String& filename,
|
||||
const String& resourceGroup)
|
||||
{
|
||||
SoftTexture* texture = new SoftTexture(this, filename, resourceGroup);
|
||||
_textures.push_back(texture);
|
||||
return texture;
|
||||
}
|
||||
|
||||
Texture* SoftRenderer::createTexture(float size)
|
||||
{
|
||||
SoftTexture* texture = new SoftTexture(this, (uint)size);
|
||||
_textures.push_back(texture);
|
||||
return texture;
|
||||
}
|
||||
|
||||
void SoftRenderer::destroyTexture(Texture* texture)
|
||||
{
|
||||
if (!texture) {
|
||||
return;
|
||||
}
|
||||
SoftTexture* soft_texture = (SoftTexture*)texture;
|
||||
_textures.remove(soft_texture);
|
||||
delete soft_texture;
|
||||
}
|
||||
|
||||
void SoftRenderer::destroyAllTextures()
|
||||
{
|
||||
while (!_textures.empty()) {
|
||||
SoftTexture* texture = *_textures.begin();
|
||||
_textures.pop_front();
|
||||
delete texture;
|
||||
}
|
||||
}
|
||||
|
||||
uint SoftRenderer::getMaxTextureSize() const
|
||||
{
|
||||
return 1 << 16;
|
||||
}
|
||||
|
||||
float SoftRenderer::getWidth() const
|
||||
{
|
||||
return (float)_width;
|
||||
}
|
||||
|
||||
float SoftRenderer::getHeight() const
|
||||
{
|
||||
return (float)_height;
|
||||
}
|
||||
|
||||
Size SoftRenderer::getSize() const
|
||||
{
|
||||
return Size((float)_width, (float)_height);
|
||||
}
|
||||
|
||||
Rect SoftRenderer::getRect() const
|
||||
{
|
||||
return Rect(0, 0, (float)_width, (float)_height);
|
||||
}
|
||||
|
||||
uint SoftRenderer::getHorzScreenDPI() const
|
||||
{
|
||||
return 96;
|
||||
}
|
||||
|
||||
uint SoftRenderer::getVertScreenDPI() const
|
||||
{
|
||||
return 96;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,148 +0,0 @@
|
||||
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
Copyright (C) 2010 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef _directfbrenderer_h_
|
||||
#define _directfbrenderer_h_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <list>
|
||||
#include <set>
|
||||
/* CEGUI 0.6 bug, CEGUITexture.h doesn't include this, we need to */
|
||||
#include <cstddef>
|
||||
|
||||
#include "CEGUIRenderer.h"
|
||||
#include "CEGUIColourRect.h"
|
||||
#include "CEGUIRect.h"
|
||||
|
||||
|
||||
namespace CEGUI
|
||||
{
|
||||
class SoftTexture;
|
||||
class ImageCodec;
|
||||
|
||||
class SoftRenderer : public Renderer
|
||||
{
|
||||
public:
|
||||
SoftRenderer(uint8_t* surface, uint width, uint height, uint stride,
|
||||
ImageCodec* codec = NULL);
|
||||
virtual ~SoftRenderer();
|
||||
|
||||
void reset_surface(uint8_t* surface, uint width, uint height, uint stride);
|
||||
|
||||
virtual void addQuad(const Rect& dest_rect, float z, const Texture* tex,
|
||||
const Rect& texture_rect, const ColourRect& colours,
|
||||
QuadSplitMode quad_split_mode);
|
||||
virtual void doRender();
|
||||
virtual void clearRenderList();
|
||||
virtual void setQueueingEnabled(bool setting);
|
||||
virtual bool isQueueingEnabled() const;
|
||||
|
||||
virtual Texture* createTexture();
|
||||
virtual Texture* createTexture(const String& filename,
|
||||
const String& resourceGroup);
|
||||
virtual Texture* createTexture(float size);
|
||||
virtual void destroyTexture(Texture* texture);
|
||||
virtual void destroyAllTextures();
|
||||
virtual uint getMaxTextureSize() const;
|
||||
|
||||
virtual float getWidth() const;
|
||||
virtual float getHeight() const;
|
||||
virtual Size getSize() const;
|
||||
virtual Rect getRect() const;
|
||||
|
||||
virtual uint getHorzScreenDPI() const;
|
||||
virtual uint getVertScreenDPI() const;
|
||||
|
||||
ImageCodec* getImageCodec() { return _image_codec;}
|
||||
|
||||
private:
|
||||
void setupImageCodec();
|
||||
void cleanupImageCodec();
|
||||
struct QuadInfo;
|
||||
void renderQuad(const QuadInfo& quad);
|
||||
void renderQuadWithColourRect(const QuadInfo& quad);
|
||||
|
||||
class ColourI {
|
||||
public:
|
||||
|
||||
bool isSameColour(const ColourI& other) const
|
||||
{
|
||||
return other.r == r && other.g == g && other.b == b && other.a == a;
|
||||
}
|
||||
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
uint8_t a;
|
||||
};
|
||||
|
||||
static inline void setRGB(ColourI& dest, const colour& src);
|
||||
static inline void componnentAtPoint(int x_pos, int y_pos,
|
||||
int top_left, int top_right,
|
||||
int bottom_left, int bottom_right,
|
||||
uint64_t& comp);
|
||||
|
||||
struct ColourIRect {
|
||||
ColourI top_left;
|
||||
ColourI top_right;
|
||||
ColourI bottom_left;
|
||||
ColourI bottom_right;
|
||||
};
|
||||
|
||||
static void colourAtPoint(int x, int x_max, int y, int y_max,
|
||||
const ColourIRect& colours,
|
||||
uint64_t& r, uint64_t& g,
|
||||
uint64_t& b, uint64_t& a);
|
||||
|
||||
private:
|
||||
uint8_t* _surface;
|
||||
int _width;
|
||||
int _height;
|
||||
ImageCodec* _image_codec;
|
||||
DynamicModule* _image_codec_module;
|
||||
void (*_destroy_image_codec)(ImageCodec*);
|
||||
bool _queueing;
|
||||
|
||||
struct RectI {
|
||||
int left;
|
||||
int top;
|
||||
int right;
|
||||
int bottom;
|
||||
};
|
||||
|
||||
struct QuadInfo {
|
||||
RectI dest;
|
||||
const SoftTexture* tex;
|
||||
RectI tex_src;
|
||||
ColourIRect colors;
|
||||
float z;
|
||||
|
||||
bool operator < (const QuadInfo& other) const
|
||||
{
|
||||
return z > other.z;
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::multiset<QuadInfo> QuadQueue;
|
||||
QuadQueue _queue;
|
||||
|
||||
typedef std::list<SoftTexture *> TexturesList;
|
||||
TexturesList _textures;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -1,139 +0,0 @@
|
||||
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
Copyright (C) 2010 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "softtexture.h"
|
||||
#include "softrenderer.h"
|
||||
|
||||
#include "CEGUIImageCodec.h"
|
||||
#include "CEGUISystem.h"
|
||||
#include "CEGUIExceptions.h"
|
||||
|
||||
namespace CEGUI
|
||||
{
|
||||
|
||||
|
||||
SoftTexture::SoftTexture(Renderer* owner)
|
||||
: Texture (owner)
|
||||
, _surf (NULL)
|
||||
, _width (0)
|
||||
, _height (0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
SoftTexture::SoftTexture(Renderer* owner, uint size)
|
||||
: Texture (owner)
|
||||
, _surf (new uint32[size * size])
|
||||
, _width (size)
|
||||
, _height (size)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
SoftTexture::SoftTexture(Renderer* owner, const String& filename,
|
||||
const String& resourceGroup)
|
||||
: Texture (owner)
|
||||
, _surf (NULL)
|
||||
, _width (0)
|
||||
, _height (0)
|
||||
{
|
||||
loadFromFile(filename, resourceGroup);
|
||||
}
|
||||
|
||||
|
||||
SoftTexture::~SoftTexture()
|
||||
{
|
||||
freeSurf();
|
||||
}
|
||||
|
||||
void SoftTexture::freeSurf()
|
||||
{
|
||||
_width = _height = 0;
|
||||
delete[] _surf;
|
||||
_surf = NULL;
|
||||
}
|
||||
|
||||
void SoftTexture::loadFromFile(const String& filename, const String& resourceGroup)
|
||||
{
|
||||
freeSurf();
|
||||
SoftRenderer* renderer = static_cast<SoftRenderer*>(getRenderer());
|
||||
RawDataContainer texture_file;
|
||||
ResourceProvider* resource_provider = System::getSingleton().getResourceProvider();
|
||||
resource_provider->loadRawDataContainer(filename, texture_file, resourceGroup);
|
||||
ImageCodec *codec = renderer->getImageCodec();
|
||||
Texture* res = codec->load(texture_file, this);
|
||||
resource_provider->unloadRawDataContainer(texture_file);
|
||||
if (!res) {
|
||||
throw RendererException("load from file failed");
|
||||
}
|
||||
}
|
||||
|
||||
void SoftTexture::loadFromMemory(const void* buffPtr, uint buffWidth,
|
||||
uint buffHeight,
|
||||
PixelFormat pixelFormat)
|
||||
{
|
||||
freeSurf();
|
||||
_surf = new uint32[buffWidth * buffHeight];
|
||||
_width = buffWidth;
|
||||
_height = buffHeight;
|
||||
|
||||
switch (pixelFormat) {
|
||||
case PF_RGBA: {
|
||||
const uint32_t *src = static_cast<const uint32_t *>(buffPtr);
|
||||
uint32* line = _surf;
|
||||
uint32* end_line = _surf + _width * _height;
|
||||
for (int i = 0; line != end_line; line += _width, i++) {
|
||||
uint32* pixel = line;
|
||||
uint32* end_pixel = pixel + _width;
|
||||
for (; pixel != end_pixel; pixel++, src++) {
|
||||
((uint8_t*)pixel)[0] = ((uint8_t*)src)[2];
|
||||
((uint8_t*)pixel)[1] = ((uint8_t*)src)[1];
|
||||
((uint8_t*)pixel)[2] = ((uint8_t*)src)[0];
|
||||
((uint8_t*)pixel)[3] = ((uint8_t*)src)[3];
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PF_RGB: {
|
||||
const uint8_t *src = static_cast<const uint8_t *>(buffPtr);
|
||||
uint32* line = _surf;
|
||||
uint32* end_line = _surf + _width * _height;
|
||||
for (int i = 0; line != end_line; line += _width, i++) {
|
||||
uint8* pixel = (uint8*)line;
|
||||
uint8* end_pixel = (uint8*)(line + _width);
|
||||
for (; pixel != end_pixel; pixel += 4, src += 3) {
|
||||
pixel[2] = src[0];
|
||||
pixel[1] = src[1];
|
||||
pixel[0] = src[2];
|
||||
pixel[3] = 0xff;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw RendererException("invalid pixel format");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -1,58 +0,0 @@
|
||||
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
Copyright (C) 2010 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef _softtexture_h_
|
||||
#define _softtexture_h_
|
||||
|
||||
#include <stdint.h>
|
||||
/* CEGUI 0.6 bug, CEGUITexture.h doesn't include this, we need to */
|
||||
#include <cstddef>
|
||||
|
||||
#include "CEGUIBase.h"
|
||||
#include "CEGUITexture.h"
|
||||
|
||||
namespace CEGUI
|
||||
{
|
||||
class SoftTexture : public Texture
|
||||
{
|
||||
public:
|
||||
SoftTexture(Renderer* owner);
|
||||
SoftTexture(Renderer* owner, uint size);
|
||||
SoftTexture(Renderer* owner, const String& filename,
|
||||
const String& resourceGroup);
|
||||
virtual ~SoftTexture();
|
||||
|
||||
virtual ushort getWidth(void) const { return _width;}
|
||||
virtual ushort getHeight(void) const { return _height;}
|
||||
|
||||
virtual void loadFromFile(const String& filename, const String& resourceGroup);
|
||||
virtual void loadFromMemory(const void* buffPtr, uint buffWidth, uint buffHeight,
|
||||
PixelFormat pixelFormat);
|
||||
|
||||
private:
|
||||
void freeSurf();
|
||||
|
||||
private:
|
||||
uint32_t* _surf;
|
||||
ushort _width;
|
||||
ushort _height;
|
||||
|
||||
friend class SoftRenderer;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,449 +0,0 @@
|
||||
const unsigned char taharez_look_schem[] = {
|
||||
0x3c,0x3f,0x78,0x6d,0x6c,0x20,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x3d,0x22,0x31,
|
||||
0x2e,0x30,0x22,0x20,0x3f,0x3e,0x0a,0x3c,0x47,0x55,0x49,0x53,0x63,0x68,0x65,0x6d,
|
||||
0x65,0x20,0x4e,0x61,0x6d,0x65,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,
|
||||
0x6f,0x6f,0x6b,0x22,0x3e,0x0a,0x09,0x3c,0x49,0x6d,0x61,0x67,0x65,0x73,0x65,0x74,
|
||||
0x20,0x4e,0x61,0x6d,0x65,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,
|
||||
0x6f,0x6b,0x22,0x20,0x46,0x69,0x6c,0x65,0x6e,0x61,0x6d,0x65,0x3d,0x22,0x54,0x61,
|
||||
0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2e,0x69,0x6d,0x61,0x67,0x65,0x73,
|
||||
0x65,0x74,0x22,0x20,0x2f,0x3e,0x0a,0x09,0x3c,0x46,0x6f,0x6e,0x74,0x20,0x4e,0x61,
|
||||
0x6d,0x65,0x3d,0x22,0x43,0x6f,0x6d,0x6d,0x6f,0x6e,0x77,0x65,0x61,0x6c,0x74,0x68,
|
||||
0x2d,0x31,0x30,0x22,0x20,0x46,0x69,0x6c,0x65,0x6e,0x61,0x6d,0x65,0x3d,0x22,0x43,
|
||||
0x6f,0x6d,0x6d,0x6f,0x6e,0x77,0x65,0x61,0x6c,0x74,0x68,0x2d,0x31,0x30,0x2e,0x66,
|
||||
0x6f,0x6e,0x74,0x22,0x20,0x2f,0x3e,0x0a,0x09,0x3c,0x4c,0x6f,0x6f,0x6b,0x4e,0x46,
|
||||
0x65,0x65,0x6c,0x20,0x46,0x69,0x6c,0x65,0x6e,0x61,0x6d,0x65,0x3d,0x22,0x54,0x61,
|
||||
0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2e,0x6c,0x6f,0x6f,0x6b,0x6e,0x66,
|
||||
0x65,0x65,0x6c,0x22,0x20,0x2f,0x3e,0x0a,0x09,0x3c,0x57,0x69,0x6e,0x64,0x6f,0x77,
|
||||
0x52,0x65,0x6e,0x64,0x65,0x72,0x65,0x72,0x53,0x65,0x74,0x20,0x46,0x69,0x6c,0x65,
|
||||
0x6e,0x61,0x6d,0x65,0x3d,0x22,0x43,0x45,0x47,0x55,0x49,0x46,0x61,0x6c,0x61,0x67,
|
||||
0x61,0x72,0x64,0x57,0x52,0x42,0x61,0x73,0x65,0x22,0x20,0x2f,0x3e,0x0a,0x09,0x3c,
|
||||
0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,0x20,
|
||||
0x57,0x69,0x6e,0x64,0x6f,0x77,0x54,0x79,0x70,0x65,0x3d,0x22,0x54,0x61,0x68,0x61,
|
||||
0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x42,0x75,0x74,0x74,0x6f,0x6e,0x22,0x20,
|
||||
0x20,0x20,0x20,0x20,0x20,0x54,0x61,0x72,0x67,0x65,0x74,0x54,0x79,0x70,0x65,0x3d,
|
||||
0x22,0x43,0x45,0x47,0x55,0x49,0x2f,0x50,0x75,0x73,0x68,0x42,0x75,0x74,0x74,0x6f,
|
||||
0x6e,0x22,0x20,0x20,0x52,0x65,0x6e,0x64,0x65,0x72,0x65,0x72,0x3d,0x22,0x46,0x61,
|
||||
0x6c,0x61,0x67,0x61,0x72,0x64,0x2f,0x42,0x75,0x74,0x74,0x6f,0x6e,0x22,0x20,0x20,
|
||||
0x20,0x20,0x20,0x20,0x20,0x4c,0x6f,0x6f,0x6b,0x4e,0x46,0x65,0x65,0x6c,0x3d,0x22,
|
||||
0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x42,0x75,0x74,0x74,
|
||||
0x6f,0x6e,0x22,0x20,0x2f,0x3e,0x0a,0x09,0x3c,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,
|
||||
0x64,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,0x20,0x57,0x69,0x6e,0x64,0x6f,0x77,0x54,
|
||||
0x79,0x70,0x65,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,
|
||||
0x2f,0x43,0x68,0x65,0x63,0x6b,0x62,0x6f,0x78,0x22,0x20,0x20,0x20,0x20,0x54,0x61,
|
||||
0x72,0x67,0x65,0x74,0x54,0x79,0x70,0x65,0x3d,0x22,0x43,0x45,0x47,0x55,0x49,0x2f,
|
||||
0x43,0x68,0x65,0x63,0x6b,0x62,0x6f,0x78,0x22,0x20,0x20,0x20,0x20,0x52,0x65,0x6e,
|
||||
0x64,0x65,0x72,0x65,0x72,0x3d,0x22,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x2f,
|
||||
0x54,0x6f,0x67,0x67,0x6c,0x65,0x42,0x75,0x74,0x74,0x6f,0x6e,0x22,0x20,0x4c,0x6f,
|
||||
0x6f,0x6b,0x4e,0x46,0x65,0x65,0x6c,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,
|
||||
0x4c,0x6f,0x6f,0x6b,0x2f,0x43,0x68,0x65,0x63,0x6b,0x62,0x6f,0x78,0x22,0x20,0x2f,
|
||||
0x3e,0x0a,0x09,0x3c,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x4d,0x61,0x70,0x70,
|
||||
0x69,0x6e,0x67,0x20,0x57,0x69,0x6e,0x64,0x6f,0x77,0x54,0x79,0x70,0x65,0x3d,0x22,
|
||||
0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x49,0x6d,0x61,0x67,
|
||||
0x65,0x42,0x75,0x74,0x74,0x6f,0x6e,0x22,0x20,0x54,0x61,0x72,0x67,0x65,0x74,0x54,
|
||||
0x79,0x70,0x65,0x3d,0x22,0x43,0x45,0x47,0x55,0x49,0x2f,0x50,0x75,0x73,0x68,0x42,
|
||||
0x75,0x74,0x74,0x6f,0x6e,0x22,0x20,0x20,0x52,0x65,0x6e,0x64,0x65,0x72,0x65,0x72,
|
||||
0x3d,0x22,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x2f,0x42,0x75,0x74,0x74,0x6f,
|
||||
0x6e,0x22,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x4c,0x6f,0x6f,0x6b,0x4e,0x46,0x65,
|
||||
0x65,0x6c,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,
|
||||
0x49,0x6d,0x61,0x67,0x65,0x42,0x75,0x74,0x74,0x6f,0x6e,0x22,0x20,0x2f,0x3e,0x0a,
|
||||
0x09,0x3c,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x4d,0x61,0x70,0x70,0x69,0x6e,
|
||||
0x67,0x20,0x57,0x69,0x6e,0x64,0x6f,0x77,0x54,0x79,0x70,0x65,0x3d,0x22,0x54,0x61,
|
||||
0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x52,0x61,0x64,0x69,0x6f,0x42,
|
||||
0x75,0x74,0x74,0x6f,0x6e,0x22,0x20,0x54,0x61,0x72,0x67,0x65,0x74,0x54,0x79,0x70,
|
||||
0x65,0x3d,0x22,0x43,0x45,0x47,0x55,0x49,0x2f,0x52,0x61,0x64,0x69,0x6f,0x42,0x75,
|
||||
0x74,0x74,0x6f,0x6e,0x22,0x20,0x52,0x65,0x6e,0x64,0x65,0x72,0x65,0x72,0x3d,0x22,
|
||||
0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x2f,0x54,0x6f,0x67,0x67,0x6c,0x65,0x42,
|
||||
0x75,0x74,0x74,0x6f,0x6e,0x22,0x20,0x4c,0x6f,0x6f,0x6b,0x4e,0x46,0x65,0x65,0x6c,
|
||||
0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x52,0x61,
|
||||
0x64,0x69,0x6f,0x42,0x75,0x74,0x74,0x6f,0x6e,0x22,0x20,0x2f,0x3e,0x0a,0x09,0x3c,
|
||||
0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,0x20,
|
||||
0x57,0x69,0x6e,0x64,0x6f,0x77,0x54,0x79,0x70,0x65,0x3d,0x22,0x54,0x61,0x68,0x61,
|
||||
0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x46,0x72,0x61,0x6d,0x65,0x57,0x69,0x6e,
|
||||
0x64,0x6f,0x77,0x22,0x20,0x20,0x54,0x61,0x72,0x67,0x65,0x74,0x54,0x79,0x70,0x65,
|
||||
0x3d,0x22,0x43,0x45,0x47,0x55,0x49,0x2f,0x46,0x72,0x61,0x6d,0x65,0x57,0x69,0x6e,
|
||||
0x64,0x6f,0x77,0x22,0x20,0x52,0x65,0x6e,0x64,0x65,0x72,0x65,0x72,0x3d,0x22,0x46,
|
||||
0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x2f,0x46,0x72,0x61,0x6d,0x65,0x57,0x69,0x6e,
|
||||
0x64,0x6f,0x77,0x22,0x20,0x20,0x4c,0x6f,0x6f,0x6b,0x4e,0x46,0x65,0x65,0x6c,0x3d,
|
||||
0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x46,0x72,0x61,
|
||||
0x6d,0x65,0x57,0x69,0x6e,0x64,0x6f,0x77,0x22,0x20,0x2f,0x3e,0x0a,0x09,0x3c,0x46,
|
||||
0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,0x20,0x57,
|
||||
0x69,0x6e,0x64,0x6f,0x77,0x54,0x79,0x70,0x65,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,
|
||||
0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x54,0x69,0x74,0x6c,0x65,0x62,0x61,0x72,0x22,
|
||||
0x20,0x20,0x20,0x20,0x20,0x54,0x61,0x72,0x67,0x65,0x74,0x54,0x79,0x70,0x65,0x3d,
|
||||
0x22,0x43,0x45,0x47,0x55,0x49,0x2f,0x54,0x69,0x74,0x6c,0x65,0x62,0x61,0x72,0x22,
|
||||
0x20,0x20,0x20,0x20,0x52,0x65,0x6e,0x64,0x65,0x72,0x65,0x72,0x3d,0x22,0x46,0x61,
|
||||
0x6c,0x61,0x67,0x61,0x72,0x64,0x2f,0x54,0x69,0x74,0x6c,0x65,0x62,0x61,0x72,0x22,
|
||||
0x20,0x20,0x20,0x20,0x20,0x4c,0x6f,0x6f,0x6b,0x4e,0x46,0x65,0x65,0x6c,0x3d,0x22,
|
||||
0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x54,0x69,0x74,0x6c,
|
||||
0x65,0x62,0x61,0x72,0x22,0x20,0x2f,0x3e,0x0a,0x09,0x3c,0x46,0x61,0x6c,0x61,0x67,
|
||||
0x61,0x72,0x64,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,0x20,0x57,0x69,0x6e,0x64,0x6f,
|
||||
0x77,0x54,0x79,0x70,0x65,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,
|
||||
0x6f,0x6b,0x2f,0x53,0x79,0x73,0x74,0x65,0x6d,0x42,0x75,0x74,0x74,0x6f,0x6e,0x22,
|
||||
0x20,0x54,0x61,0x72,0x67,0x65,0x74,0x54,0x79,0x70,0x65,0x3d,0x22,0x43,0x45,0x47,
|
||||
0x55,0x49,0x2f,0x50,0x75,0x73,0x68,0x42,0x75,0x74,0x74,0x6f,0x6e,0x22,0x20,0x20,
|
||||
0x52,0x65,0x6e,0x64,0x65,0x72,0x65,0x72,0x3d,0x22,0x46,0x61,0x6c,0x61,0x67,0x61,
|
||||
0x72,0x64,0x2f,0x53,0x79,0x73,0x74,0x65,0x6d,0x42,0x75,0x74,0x74,0x6f,0x6e,0x22,
|
||||
0x20,0x4c,0x6f,0x6f,0x6b,0x4e,0x46,0x65,0x65,0x6c,0x3d,0x22,0x54,0x61,0x68,0x61,
|
||||
0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x42,0x75,0x74,0x74,0x6f,0x6e,0x22,0x20,
|
||||
0x2f,0x3e,0x0a,0x09,0x3c,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x4d,0x61,0x70,
|
||||
0x70,0x69,0x6e,0x67,0x20,0x57,0x69,0x6e,0x64,0x6f,0x77,0x54,0x79,0x70,0x65,0x3d,
|
||||
0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x45,0x64,0x69,
|
||||
0x74,0x62,0x6f,0x78,0x22,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x54,
|
||||
0x61,0x72,0x67,0x65,0x74,0x54,0x79,0x70,0x65,0x3d,0x22,0x43,0x45,0x47,0x55,0x49,
|
||||
0x2f,0x45,0x64,0x69,0x74,0x62,0x6f,0x78,0x22,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
|
||||
0x20,0x20,0x20,0x52,0x65,0x6e,0x64,0x65,0x72,0x65,0x72,0x3d,0x22,0x46,0x61,0x6c,
|
||||
0x61,0x67,0x61,0x72,0x64,0x2f,0x45,0x64,0x69,0x74,0x62,0x6f,0x78,0x22,0x20,0x20,
|
||||
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x4c,0x6f,0x6f,0x6b,0x4e,0x46,0x65,0x65,
|
||||
0x6c,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x45,
|
||||
0x64,0x69,0x74,0x62,0x6f,0x78,0x22,0x20,0x2f,0x3e,0x0a,0x09,0x3c,0x46,0x61,0x6c,
|
||||
0x61,0x67,0x61,0x72,0x64,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,0x20,0x57,0x69,0x6e,
|
||||
0x64,0x6f,0x77,0x54,0x79,0x70,0x65,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,
|
||||
0x4c,0x6f,0x6f,0x6b,0x2f,0x4d,0x75,0x6c,0x74,0x69,0x4c,0x69,0x6e,0x65,0x45,0x64,
|
||||
0x69,0x74,0x62,0x6f,0x78,0x22,0x20,0x54,0x61,0x72,0x67,0x65,0x74,0x54,0x79,0x70,
|
||||
0x65,0x3d,0x22,0x43,0x45,0x47,0x55,0x49,0x2f,0x4d,0x75,0x6c,0x74,0x69,0x4c,0x69,
|
||||
0x6e,0x65,0x45,0x64,0x69,0x74,0x62,0x6f,0x78,0x22,0x20,0x52,0x65,0x6e,0x64,0x65,
|
||||
0x72,0x65,0x72,0x3d,0x22,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x2f,0x4d,0x75,
|
||||
0x6c,0x74,0x69,0x4c,0x69,0x6e,0x65,0x45,0x64,0x69,0x74,0x62,0x6f,0x78,0x22,0x20,
|
||||
0x4c,0x6f,0x6f,0x6b,0x4e,0x46,0x65,0x65,0x6c,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,
|
||||
0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x4d,0x75,0x6c,0x74,0x69,0x4c,0x69,0x6e,0x65,
|
||||
0x45,0x64,0x69,0x74,0x62,0x6f,0x78,0x22,0x20,0x2f,0x3e,0x0a,0x09,0x3c,0x46,0x61,
|
||||
0x6c,0x61,0x67,0x61,0x72,0x64,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,0x20,0x57,0x69,
|
||||
0x6e,0x64,0x6f,0x77,0x54,0x79,0x70,0x65,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,
|
||||
0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x4d,0x65,0x6e,0x75,0x62,0x61,0x72,0x22,0x20,0x20,
|
||||
0x20,0x54,0x61,0x72,0x67,0x65,0x74,0x54,0x79,0x70,0x65,0x3d,0x22,0x43,0x45,0x47,
|
||||
0x55,0x49,0x2f,0x4d,0x65,0x6e,0x75,0x62,0x61,0x72,0x22,0x20,0x20,0x20,0x52,0x65,
|
||||
0x6e,0x64,0x65,0x72,0x65,0x72,0x3d,0x22,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,
|
||||
0x2f,0x4d,0x65,0x6e,0x75,0x62,0x61,0x72,0x22,0x20,0x20,0x20,0x4c,0x6f,0x6f,0x6b,
|
||||
0x4e,0x46,0x65,0x65,0x6c,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,
|
||||
0x6f,0x6b,0x2f,0x4d,0x65,0x6e,0x75,0x62,0x61,0x72,0x22,0x20,0x2f,0x3e,0x0a,0x09,
|
||||
0x3c,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,
|
||||
0x20,0x57,0x69,0x6e,0x64,0x6f,0x77,0x54,0x79,0x70,0x65,0x3d,0x22,0x54,0x61,0x68,
|
||||
0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x50,0x6f,0x70,0x75,0x70,0x4d,0x65,
|
||||
0x6e,0x75,0x22,0x20,0x54,0x61,0x72,0x67,0x65,0x74,0x54,0x79,0x70,0x65,0x3d,0x22,
|
||||
0x43,0x45,0x47,0x55,0x49,0x2f,0x50,0x6f,0x70,0x75,0x70,0x4d,0x65,0x6e,0x75,0x22,
|
||||
0x20,0x52,0x65,0x6e,0x64,0x65,0x72,0x65,0x72,0x3d,0x22,0x46,0x61,0x6c,0x61,0x67,
|
||||
0x61,0x72,0x64,0x2f,0x50,0x6f,0x70,0x75,0x70,0x4d,0x65,0x6e,0x75,0x22,0x20,0x4c,
|
||||
0x6f,0x6f,0x6b,0x4e,0x46,0x65,0x65,0x6c,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,
|
||||
0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x50,0x6f,0x70,0x75,0x70,0x4d,0x65,0x6e,0x75,0x22,
|
||||
0x20,0x2f,0x3e,0x0a,0x09,0x3c,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x4d,0x61,
|
||||
0x70,0x70,0x69,0x6e,0x67,0x20,0x57,0x69,0x6e,0x64,0x6f,0x77,0x54,0x79,0x70,0x65,
|
||||
0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x4d,0x65,
|
||||
0x6e,0x75,0x49,0x74,0x65,0x6d,0x22,0x20,0x20,0x54,0x61,0x72,0x67,0x65,0x74,0x54,
|
||||
0x79,0x70,0x65,0x3d,0x22,0x43,0x45,0x47,0x55,0x49,0x2f,0x4d,0x65,0x6e,0x75,0x49,
|
||||
0x74,0x65,0x6d,0x22,0x20,0x20,0x52,0x65,0x6e,0x64,0x65,0x72,0x65,0x72,0x3d,0x22,
|
||||
0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x2f,0x4d,0x65,0x6e,0x75,0x49,0x74,0x65,
|
||||
0x6d,0x22,0x20,0x20,0x4c,0x6f,0x6f,0x6b,0x4e,0x46,0x65,0x65,0x6c,0x3d,0x22,0x54,
|
||||
0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x4d,0x65,0x6e,0x75,0x49,
|
||||
0x74,0x65,0x6d,0x22,0x20,0x2f,0x3e,0x0a,0x09,0x3c,0x46,0x61,0x6c,0x61,0x67,0x61,
|
||||
0x72,0x64,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,0x20,0x57,0x69,0x6e,0x64,0x6f,0x77,
|
||||
0x54,0x79,0x70,0x65,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,
|
||||
0x6b,0x2f,0x41,0x6c,0x74,0x65,0x72,0x6e,0x61,0x74,0x65,0x50,0x72,0x6f,0x67,0x72,
|
||||
0x65,0x73,0x73,0x42,0x61,0x72,0x22,0x20,0x54,0x61,0x72,0x67,0x65,0x74,0x54,0x79,
|
||||
0x70,0x65,0x3d,0x22,0x43,0x45,0x47,0x55,0x49,0x2f,0x50,0x72,0x6f,0x67,0x72,0x65,
|
||||
0x73,0x73,0x42,0x61,0x72,0x22,0x20,0x52,0x65,0x6e,0x64,0x65,0x72,0x65,0x72,0x3d,
|
||||
0x22,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x2f,0x50,0x72,0x6f,0x67,0x72,0x65,
|
||||
0x73,0x73,0x42,0x61,0x72,0x22,0x20,0x4c,0x6f,0x6f,0x6b,0x4e,0x46,0x65,0x65,0x6c,
|
||||
0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x41,0x6c,
|
||||
0x74,0x50,0x72,0x6f,0x67,0x72,0x65,0x73,0x73,0x42,0x61,0x72,0x22,0x20,0x2f,0x3e,
|
||||
0x0a,0x09,0x3c,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x4d,0x61,0x70,0x70,0x69,
|
||||
0x6e,0x67,0x20,0x57,0x69,0x6e,0x64,0x6f,0x77,0x54,0x79,0x70,0x65,0x3d,0x22,0x54,
|
||||
0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x50,0x72,0x6f,0x67,0x72,
|
||||
0x65,0x73,0x73,0x42,0x61,0x72,0x22,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
|
||||
0x20,0x54,0x61,0x72,0x67,0x65,0x74,0x54,0x79,0x70,0x65,0x3d,0x22,0x43,0x45,0x47,
|
||||
0x55,0x49,0x2f,0x50,0x72,0x6f,0x67,0x72,0x65,0x73,0x73,0x42,0x61,0x72,0x22,0x20,
|
||||
0x52,0x65,0x6e,0x64,0x65,0x72,0x65,0x72,0x3d,0x22,0x46,0x61,0x6c,0x61,0x67,0x61,
|
||||
0x72,0x64,0x2f,0x50,0x72,0x6f,0x67,0x72,0x65,0x73,0x73,0x42,0x61,0x72,0x22,0x20,
|
||||
0x4c,0x6f,0x6f,0x6b,0x4e,0x46,0x65,0x65,0x6c,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,
|
||||
0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x50,0x72,0x6f,0x67,0x72,0x65,0x73,0x73,0x42,
|
||||
0x61,0x72,0x22,0x20,0x2f,0x3e,0x0a,0x09,0x3c,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,
|
||||
0x64,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,0x20,0x57,0x69,0x6e,0x64,0x6f,0x77,0x54,
|
||||
0x79,0x70,0x65,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,
|
||||
0x2f,0x56,0x55,0x4d,0x65,0x74,0x65,0x72,0x22,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
|
||||
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x54,0x61,0x72,0x67,0x65,0x74,0x54,0x79,0x70,
|
||||
0x65,0x3d,0x22,0x43,0x45,0x47,0x55,0x49,0x2f,0x50,0x72,0x6f,0x67,0x72,0x65,0x73,
|
||||
0x73,0x42,0x61,0x72,0x22,0x20,0x52,0x65,0x6e,0x64,0x65,0x72,0x65,0x72,0x3d,0x22,
|
||||
0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x2f,0x50,0x72,0x6f,0x67,0x72,0x65,0x73,
|
||||
0x73,0x42,0x61,0x72,0x22,0x20,0x4c,0x6f,0x6f,0x6b,0x4e,0x46,0x65,0x65,0x6c,0x3d,
|
||||
0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x56,0x55,0x4d,
|
||||
0x65,0x74,0x65,0x72,0x22,0x20,0x2f,0x3e,0x0a,0x09,0x3c,0x46,0x61,0x6c,0x61,0x67,
|
||||
0x61,0x72,0x64,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,0x20,0x57,0x69,0x6e,0x64,0x6f,
|
||||
0x77,0x54,0x79,0x70,0x65,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,
|
||||
0x6f,0x6b,0x2f,0x56,0x65,0x72,0x74,0x69,0x63,0x61,0x6c,0x53,0x63,0x72,0x6f,0x6c,
|
||||
0x6c,0x62,0x61,0x72,0x22,0x20,0x20,0x20,0x54,0x61,0x72,0x67,0x65,0x74,0x54,0x79,
|
||||
0x70,0x65,0x3d,0x22,0x43,0x45,0x47,0x55,0x49,0x2f,0x53,0x63,0x72,0x6f,0x6c,0x6c,
|
||||
0x62,0x61,0x72,0x22,0x20,0x52,0x65,0x6e,0x64,0x65,0x72,0x65,0x72,0x3d,0x22,0x46,
|
||||
0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x2f,0x53,0x63,0x72,0x6f,0x6c,0x6c,0x62,0x61,
|
||||
0x72,0x22,0x20,0x4c,0x6f,0x6f,0x6b,0x4e,0x46,0x65,0x65,0x6c,0x3d,0x22,0x54,0x61,
|
||||
0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x56,0x65,0x72,0x74,0x69,0x63,
|
||||
0x61,0x6c,0x53,0x63,0x72,0x6f,0x6c,0x6c,0x62,0x61,0x72,0x22,0x20,0x2f,0x3e,0x0a,
|
||||
0x09,0x3c,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x4d,0x61,0x70,0x70,0x69,0x6e,
|
||||
0x67,0x20,0x57,0x69,0x6e,0x64,0x6f,0x77,0x54,0x79,0x70,0x65,0x3d,0x22,0x54,0x61,
|
||||
0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x48,0x6f,0x72,0x69,0x7a,0x6f,
|
||||
0x6e,0x74,0x61,0x6c,0x53,0x63,0x72,0x6f,0x6c,0x6c,0x62,0x61,0x72,0x22,0x20,0x54,
|
||||
0x61,0x72,0x67,0x65,0x74,0x54,0x79,0x70,0x65,0x3d,0x22,0x43,0x45,0x47,0x55,0x49,
|
||||
0x2f,0x53,0x63,0x72,0x6f,0x6c,0x6c,0x62,0x61,0x72,0x22,0x20,0x52,0x65,0x6e,0x64,
|
||||
0x65,0x72,0x65,0x72,0x3d,0x22,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x2f,0x53,
|
||||
0x63,0x72,0x6f,0x6c,0x6c,0x62,0x61,0x72,0x22,0x20,0x4c,0x6f,0x6f,0x6b,0x4e,0x46,
|
||||
0x65,0x65,0x6c,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,
|
||||
0x2f,0x48,0x6f,0x72,0x69,0x7a,0x6f,0x6e,0x74,0x61,0x6c,0x53,0x63,0x72,0x6f,0x6c,
|
||||
0x6c,0x62,0x61,0x72,0x22,0x20,0x2f,0x3e,0x0a,0x09,0x3c,0x46,0x61,0x6c,0x61,0x67,
|
||||
0x61,0x72,0x64,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,0x20,0x57,0x69,0x6e,0x64,0x6f,
|
||||
0x77,0x54,0x79,0x70,0x65,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,
|
||||
0x6f,0x6b,0x2f,0x56,0x65,0x72,0x74,0x69,0x63,0x61,0x6c,0x53,0x63,0x72,0x6f,0x6c,
|
||||
0x6c,0x62,0x61,0x72,0x54,0x68,0x75,0x6d,0x62,0x22,0x20,0x20,0x20,0x54,0x61,0x72,
|
||||
0x67,0x65,0x74,0x54,0x79,0x70,0x65,0x3d,0x22,0x43,0x45,0x47,0x55,0x49,0x2f,0x54,
|
||||
0x68,0x75,0x6d,0x62,0x22,0x20,0x52,0x65,0x6e,0x64,0x65,0x72,0x65,0x72,0x3d,0x22,
|
||||
0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x2f,0x42,0x75,0x74,0x74,0x6f,0x6e,0x22,
|
||||
0x20,0x4c,0x6f,0x6f,0x6b,0x4e,0x46,0x65,0x65,0x6c,0x3d,0x22,0x54,0x61,0x68,0x61,
|
||||
0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x56,0x65,0x72,0x74,0x69,0x63,0x61,0x6c,
|
||||
0x53,0x63,0x72,0x6f,0x6c,0x6c,0x62,0x61,0x72,0x54,0x68,0x75,0x6d,0x62,0x22,0x20,
|
||||
0x2f,0x3e,0x0a,0x09,0x3c,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x4d,0x61,0x70,
|
||||
0x70,0x69,0x6e,0x67,0x20,0x57,0x69,0x6e,0x64,0x6f,0x77,0x54,0x79,0x70,0x65,0x3d,
|
||||
0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x48,0x6f,0x72,
|
||||
0x69,0x7a,0x6f,0x6e,0x74,0x61,0x6c,0x53,0x63,0x72,0x6f,0x6c,0x6c,0x62,0x61,0x72,
|
||||
0x54,0x68,0x75,0x6d,0x62,0x22,0x20,0x54,0x61,0x72,0x67,0x65,0x74,0x54,0x79,0x70,
|
||||
0x65,0x3d,0x22,0x43,0x45,0x47,0x55,0x49,0x2f,0x54,0x68,0x75,0x6d,0x62,0x22,0x20,
|
||||
0x52,0x65,0x6e,0x64,0x65,0x72,0x65,0x72,0x3d,0x22,0x46,0x61,0x6c,0x61,0x67,0x61,
|
||||
0x72,0x64,0x2f,0x42,0x75,0x74,0x74,0x6f,0x6e,0x22,0x20,0x4c,0x6f,0x6f,0x6b,0x4e,
|
||||
0x46,0x65,0x65,0x6c,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,
|
||||
0x6b,0x2f,0x48,0x6f,0x72,0x69,0x7a,0x6f,0x6e,0x74,0x61,0x6c,0x53,0x63,0x72,0x6f,
|
||||
0x6c,0x6c,0x62,0x61,0x72,0x54,0x68,0x75,0x6d,0x62,0x22,0x20,0x2f,0x3e,0x0a,0x09,
|
||||
0x3c,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,
|
||||
0x20,0x57,0x69,0x6e,0x64,0x6f,0x77,0x54,0x79,0x70,0x65,0x3d,0x22,0x54,0x61,0x68,
|
||||
0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x4c,0x61,0x72,0x67,0x65,0x56,0x65,
|
||||
0x72,0x74,0x69,0x63,0x61,0x6c,0x53,0x63,0x72,0x6f,0x6c,0x6c,0x62,0x61,0x72,0x22,
|
||||
0x20,0x20,0x20,0x20,0x20,0x20,0x54,0x61,0x72,0x67,0x65,0x74,0x54,0x79,0x70,0x65,
|
||||
0x3d,0x22,0x43,0x45,0x47,0x55,0x49,0x2f,0x53,0x63,0x72,0x6f,0x6c,0x6c,0x62,0x61,
|
||||
0x72,0x22,0x20,0x52,0x65,0x6e,0x64,0x65,0x72,0x65,0x72,0x3d,0x22,0x46,0x61,0x6c,
|
||||
0x61,0x67,0x61,0x72,0x64,0x2f,0x53,0x63,0x72,0x6f,0x6c,0x6c,0x62,0x61,0x72,0x22,
|
||||
0x20,0x4c,0x6f,0x6f,0x6b,0x4e,0x46,0x65,0x65,0x6c,0x3d,0x22,0x54,0x61,0x68,0x61,
|
||||
0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x4c,0x61,0x72,0x67,0x65,0x56,0x65,0x72,
|
||||
0x74,0x69,0x63,0x61,0x6c,0x53,0x63,0x72,0x6f,0x6c,0x6c,0x62,0x61,0x72,0x22,0x20,
|
||||
0x2f,0x3e,0x0a,0x09,0x3c,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x4d,0x61,0x70,
|
||||
0x70,0x69,0x6e,0x67,0x20,0x57,0x69,0x6e,0x64,0x6f,0x77,0x54,0x79,0x70,0x65,0x3d,
|
||||
0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x4c,0x61,0x72,
|
||||
0x67,0x65,0x56,0x65,0x72,0x74,0x69,0x63,0x61,0x6c,0x53,0x63,0x72,0x6f,0x6c,0x6c,
|
||||
0x62,0x61,0x72,0x54,0x68,0x75,0x6d,0x62,0x22,0x20,0x54,0x61,0x72,0x67,0x65,0x74,
|
||||
0x54,0x79,0x70,0x65,0x3d,0x22,0x43,0x45,0x47,0x55,0x49,0x2f,0x54,0x68,0x75,0x6d,
|
||||
0x62,0x22,0x20,0x20,0x20,0x20,0x20,0x52,0x65,0x6e,0x64,0x65,0x72,0x65,0x72,0x3d,
|
||||
0x22,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x2f,0x42,0x75,0x74,0x74,0x6f,0x6e,
|
||||
0x22,0x20,0x20,0x20,0x20,0x4c,0x6f,0x6f,0x6b,0x4e,0x46,0x65,0x65,0x6c,0x3d,0x22,
|
||||
0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x4c,0x61,0x72,0x67,
|
||||
0x65,0x56,0x65,0x72,0x74,0x69,0x63,0x61,0x6c,0x53,0x63,0x72,0x6f,0x6c,0x6c,0x62,
|
||||
0x61,0x72,0x54,0x68,0x75,0x6d,0x62,0x22,0x20,0x2f,0x3e,0x0a,0x09,0x3c,0x46,0x61,
|
||||
0x6c,0x61,0x67,0x61,0x72,0x64,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,0x20,0x57,0x69,
|
||||
0x6e,0x64,0x6f,0x77,0x54,0x79,0x70,0x65,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,
|
||||
0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x54,0x61,0x62,0x42,0x75,0x74,0x74,0x6f,0x6e,0x22,
|
||||
0x20,0x20,0x54,0x61,0x72,0x67,0x65,0x74,0x54,0x79,0x70,0x65,0x3d,0x22,0x43,0x45,
|
||||
0x47,0x55,0x49,0x2f,0x54,0x61,0x62,0x42,0x75,0x74,0x74,0x6f,0x6e,0x22,0x20,0x20,
|
||||
0x52,0x65,0x6e,0x64,0x65,0x72,0x65,0x72,0x3d,0x22,0x46,0x61,0x6c,0x61,0x67,0x61,
|
||||
0x72,0x64,0x2f,0x54,0x61,0x62,0x42,0x75,0x74,0x74,0x6f,0x6e,0x22,0x20,0x20,0x4c,
|
||||
0x6f,0x6f,0x6b,0x4e,0x46,0x65,0x65,0x6c,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,
|
||||
0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x54,0x61,0x62,0x42,0x75,0x74,0x74,0x6f,0x6e,0x22,
|
||||
0x20,0x2f,0x3e,0x0a,0x09,0x3c,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x4d,0x61,
|
||||
0x70,0x70,0x69,0x6e,0x67,0x20,0x57,0x69,0x6e,0x64,0x6f,0x77,0x54,0x79,0x70,0x65,
|
||||
0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x54,0x61,
|
||||
0x62,0x43,0x6f,0x6e,0x74,0x72,0x6f,0x6c,0x22,0x20,0x54,0x61,0x72,0x67,0x65,0x74,
|
||||
0x54,0x79,0x70,0x65,0x3d,0x22,0x43,0x45,0x47,0x55,0x49,0x2f,0x54,0x61,0x62,0x43,
|
||||
0x6f,0x6e,0x74,0x72,0x6f,0x6c,0x22,0x20,0x52,0x65,0x6e,0x64,0x65,0x72,0x65,0x72,
|
||||
0x3d,0x22,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x2f,0x54,0x61,0x62,0x43,0x6f,
|
||||
0x6e,0x74,0x72,0x6f,0x6c,0x22,0x20,0x4c,0x6f,0x6f,0x6b,0x4e,0x46,0x65,0x65,0x6c,
|
||||
0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x54,0x61,
|
||||
0x62,0x43,0x6f,0x6e,0x74,0x72,0x6f,0x6c,0x22,0x20,0x2f,0x3e,0x0a,0x09,0x3c,0x46,
|
||||
0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,0x20,0x57,
|
||||
0x69,0x6e,0x64,0x6f,0x77,0x54,0x79,0x70,0x65,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,
|
||||
0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x54,0x61,0x62,0x43,0x6f,0x6e,0x74,0x65,0x6e,
|
||||
0x74,0x50,0x61,0x6e,0x65,0x22,0x20,0x20,0x20,0x20,0x54,0x61,0x72,0x67,0x65,0x74,
|
||||
0x54,0x79,0x70,0x65,0x3d,0x22,0x44,0x65,0x66,0x61,0x75,0x6c,0x74,0x57,0x69,0x6e,
|
||||
0x64,0x6f,0x77,0x22,0x20,0x20,0x20,0x20,0x52,0x65,0x6e,0x64,0x65,0x72,0x65,0x72,
|
||||
0x3d,0x22,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x2f,0x44,0x65,0x66,0x61,0x75,
|
||||
0x6c,0x74,0x22,0x20,0x20,0x20,0x20,0x4c,0x6f,0x6f,0x6b,0x4e,0x46,0x65,0x65,0x6c,
|
||||
0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x54,0x61,
|
||||
0x62,0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x50,0x61,0x6e,0x65,0x22,0x20,0x2f,0x3e,
|
||||
0x0a,0x09,0x3c,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x4d,0x61,0x70,0x70,0x69,
|
||||
0x6e,0x67,0x20,0x57,0x69,0x6e,0x64,0x6f,0x77,0x54,0x79,0x70,0x65,0x3d,0x22,0x54,
|
||||
0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x54,0x61,0x62,0x42,0x75,
|
||||
0x74,0x74,0x6f,0x6e,0x50,0x61,0x6e,0x65,0x22,0x20,0x20,0x20,0x20,0x54,0x61,0x72,
|
||||
0x67,0x65,0x74,0x54,0x79,0x70,0x65,0x3d,0x22,0x44,0x65,0x66,0x61,0x75,0x6c,0x74,
|
||||
0x57,0x69,0x6e,0x64,0x6f,0x77,0x22,0x20,0x20,0x20,0x20,0x52,0x65,0x6e,0x64,0x65,
|
||||
0x72,0x65,0x72,0x3d,0x22,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x2f,0x44,0x65,
|
||||
0x66,0x61,0x75,0x6c,0x74,0x22,0x20,0x20,0x20,0x20,0x4c,0x6f,0x6f,0x6b,0x4e,0x46,
|
||||
0x65,0x65,0x6c,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,
|
||||
0x2f,0x54,0x61,0x62,0x42,0x75,0x74,0x74,0x6f,0x6e,0x50,0x61,0x6e,0x65,0x22,0x20,
|
||||
0x2f,0x3e,0x0a,0x09,0x3c,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x4d,0x61,0x70,
|
||||
0x70,0x69,0x6e,0x67,0x20,0x57,0x69,0x6e,0x64,0x6f,0x77,0x54,0x79,0x70,0x65,0x3d,
|
||||
0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x43,0x6f,0x6d,
|
||||
0x62,0x6f,0x44,0x72,0x6f,0x70,0x4c,0x69,0x73,0x74,0x22,0x20,0x54,0x61,0x72,0x67,
|
||||
0x65,0x74,0x54,0x79,0x70,0x65,0x3d,0x22,0x43,0x45,0x47,0x55,0x49,0x2f,0x43,0x6f,
|
||||
0x6d,0x62,0x6f,0x44,0x72,0x6f,0x70,0x4c,0x69,0x73,0x74,0x22,0x20,0x52,0x65,0x6e,
|
||||
0x64,0x65,0x72,0x65,0x72,0x3d,0x22,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x2f,
|
||||
0x4c,0x69,0x73,0x74,0x62,0x6f,0x78,0x22,0x20,0x4c,0x6f,0x6f,0x6b,0x4e,0x46,0x65,
|
||||
0x65,0x6c,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,
|
||||
0x43,0x6f,0x6d,0x62,0x6f,0x44,0x72,0x6f,0x70,0x4c,0x69,0x73,0x74,0x22,0x20,0x2f,
|
||||
0x3e,0x0a,0x09,0x3c,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x4d,0x61,0x70,0x70,
|
||||
0x69,0x6e,0x67,0x20,0x57,0x69,0x6e,0x64,0x6f,0x77,0x54,0x79,0x70,0x65,0x3d,0x22,
|
||||
0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x43,0x6f,0x6d,0x62,
|
||||
0x6f,0x45,0x64,0x69,0x74,0x62,0x6f,0x78,0x22,0x20,0x20,0x54,0x61,0x72,0x67,0x65,
|
||||
0x74,0x54,0x79,0x70,0x65,0x3d,0x22,0x43,0x45,0x47,0x55,0x49,0x2f,0x45,0x64,0x69,
|
||||
0x74,0x62,0x6f,0x78,0x22,0x09,0x20,0x52,0x65,0x6e,0x64,0x65,0x72,0x65,0x72,0x3d,
|
||||
0x22,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x2f,0x45,0x64,0x69,0x74,0x62,0x6f,
|
||||
0x78,0x22,0x20,0x4c,0x6f,0x6f,0x6b,0x4e,0x46,0x65,0x65,0x6c,0x3d,0x22,0x54,0x61,
|
||||
0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x43,0x6f,0x6d,0x62,0x6f,0x45,
|
||||
0x64,0x69,0x74,0x62,0x6f,0x78,0x22,0x20,0x2f,0x3e,0x0a,0x09,0x3c,0x46,0x61,0x6c,
|
||||
0x61,0x67,0x61,0x72,0x64,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,0x20,0x57,0x69,0x6e,
|
||||
0x64,0x6f,0x77,0x54,0x79,0x70,0x65,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,
|
||||
0x4c,0x6f,0x6f,0x6b,0x2f,0x43,0x6f,0x6d,0x62,0x6f,0x62,0x6f,0x78,0x22,0x09,0x54,
|
||||
0x61,0x72,0x67,0x65,0x74,0x54,0x79,0x70,0x65,0x3d,0x22,0x43,0x45,0x47,0x55,0x49,
|
||||
0x2f,0x43,0x6f,0x6d,0x62,0x6f,0x62,0x6f,0x78,0x22,0x09,0x20,0x52,0x65,0x6e,0x64,
|
||||
0x65,0x72,0x65,0x72,0x3d,0x22,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x2f,0x44,
|
||||
0x65,0x66,0x61,0x75,0x6c,0x74,0x22,0x20,0x4c,0x6f,0x6f,0x6b,0x4e,0x46,0x65,0x65,
|
||||
0x6c,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x43,
|
||||
0x6f,0x6d,0x62,0x6f,0x62,0x6f,0x78,0x22,0x20,0x2f,0x3e,0x0a,0x09,0x3c,0x46,0x61,
|
||||
0x6c,0x61,0x67,0x61,0x72,0x64,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,0x20,0x57,0x69,
|
||||
0x6e,0x64,0x6f,0x77,0x54,0x79,0x70,0x65,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,
|
||||
0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x4c,0x69,0x73,0x74,0x62,0x6f,0x78,0x22,0x20,0x20,
|
||||
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x54,0x61,0x72,0x67,0x65,0x74,0x54,
|
||||
0x79,0x70,0x65,0x3d,0x22,0x43,0x45,0x47,0x55,0x49,0x2f,0x4c,0x69,0x73,0x74,0x62,
|
||||
0x6f,0x78,0x22,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x52,0x65,
|
||||
0x6e,0x64,0x65,0x72,0x65,0x72,0x3d,0x22,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,
|
||||
0x2f,0x4c,0x69,0x73,0x74,0x62,0x6f,0x78,0x22,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
|
||||
0x20,0x20,0x20,0x20,0x4c,0x6f,0x6f,0x6b,0x4e,0x46,0x65,0x65,0x6c,0x3d,0x22,0x54,
|
||||
0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x4c,0x69,0x73,0x74,0x62,
|
||||
0x6f,0x78,0x22,0x20,0x2f,0x3e,0x0a,0x09,0x3c,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,
|
||||
0x64,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,0x20,0x57,0x69,0x6e,0x64,0x6f,0x77,0x54,
|
||||
0x79,0x70,0x65,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,
|
||||
0x2f,0x4c,0x69,0x73,0x74,0x48,0x65,0x61,0x64,0x65,0x72,0x22,0x20,0x20,0x20,0x20,
|
||||
0x20,0x20,0x20,0x20,0x54,0x61,0x72,0x67,0x65,0x74,0x54,0x79,0x70,0x65,0x3d,0x22,
|
||||
0x43,0x45,0x47,0x55,0x49,0x2f,0x4c,0x69,0x73,0x74,0x48,0x65,0x61,0x64,0x65,0x72,
|
||||
0x22,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x52,0x65,0x6e,0x64,0x65,0x72,0x65,
|
||||
0x72,0x3d,0x22,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x2f,0x4c,0x69,0x73,0x74,
|
||||
0x48,0x65,0x61,0x64,0x65,0x72,0x22,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x4c,
|
||||
0x6f,0x6f,0x6b,0x4e,0x46,0x65,0x65,0x6c,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,
|
||||
0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x4c,0x69,0x73,0x74,0x48,0x65,0x61,0x64,0x65,0x72,
|
||||
0x22,0x20,0x2f,0x3e,0x0a,0x09,0x3c,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x4d,
|
||||
0x61,0x70,0x70,0x69,0x6e,0x67,0x20,0x57,0x69,0x6e,0x64,0x6f,0x77,0x54,0x79,0x70,
|
||||
0x65,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x4c,
|
||||
0x69,0x73,0x74,0x48,0x65,0x61,0x64,0x65,0x72,0x53,0x65,0x67,0x6d,0x65,0x6e,0x74,
|
||||
0x22,0x20,0x54,0x61,0x72,0x67,0x65,0x74,0x54,0x79,0x70,0x65,0x3d,0x22,0x43,0x45,
|
||||
0x47,0x55,0x49,0x2f,0x4c,0x69,0x73,0x74,0x48,0x65,0x61,0x64,0x65,0x72,0x53,0x65,
|
||||
0x67,0x6d,0x65,0x6e,0x74,0x22,0x20,0x52,0x65,0x6e,0x64,0x65,0x72,0x65,0x72,0x3d,
|
||||
0x22,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x2f,0x4c,0x69,0x73,0x74,0x48,0x65,
|
||||
0x61,0x64,0x65,0x72,0x53,0x65,0x67,0x6d,0x65,0x6e,0x74,0x22,0x20,0x4c,0x6f,0x6f,
|
||||
0x6b,0x4e,0x46,0x65,0x65,0x6c,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,
|
||||
0x6f,0x6f,0x6b,0x2f,0x4c,0x69,0x73,0x74,0x48,0x65,0x61,0x64,0x65,0x72,0x53,0x65,
|
||||
0x67,0x6d,0x65,0x6e,0x74,0x22,0x20,0x2f,0x3e,0x0a,0x09,0x3c,0x46,0x61,0x6c,0x61,
|
||||
0x67,0x61,0x72,0x64,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,0x20,0x57,0x69,0x6e,0x64,
|
||||
0x6f,0x77,0x54,0x79,0x70,0x65,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,
|
||||
0x6f,0x6f,0x6b,0x2f,0x4d,0x75,0x6c,0x74,0x69,0x43,0x6f,0x6c,0x75,0x6d,0x6e,0x4c,
|
||||
0x69,0x73,0x74,0x22,0x20,0x20,0x20,0x54,0x61,0x72,0x67,0x65,0x74,0x54,0x79,0x70,
|
||||
0x65,0x3d,0x22,0x43,0x45,0x47,0x55,0x49,0x2f,0x4d,0x75,0x6c,0x74,0x69,0x43,0x6f,
|
||||
0x6c,0x75,0x6d,0x6e,0x4c,0x69,0x73,0x74,0x22,0x20,0x20,0x20,0x52,0x65,0x6e,0x64,
|
||||
0x65,0x72,0x65,0x72,0x3d,0x22,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x2f,0x4d,
|
||||
0x75,0x6c,0x74,0x69,0x43,0x6f,0x6c,0x75,0x6d,0x6e,0x4c,0x69,0x73,0x74,0x22,0x20,
|
||||
0x20,0x20,0x4c,0x6f,0x6f,0x6b,0x4e,0x46,0x65,0x65,0x6c,0x3d,0x22,0x54,0x61,0x68,
|
||||
0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x4d,0x75,0x6c,0x74,0x69,0x43,0x6f,
|
||||
0x6c,0x75,0x6d,0x6e,0x4c,0x69,0x73,0x74,0x22,0x20,0x2f,0x3e,0x0a,0x09,0x3c,0x46,
|
||||
0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,0x20,0x57,
|
||||
0x69,0x6e,0x64,0x6f,0x77,0x54,0x79,0x70,0x65,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,
|
||||
0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x53,0x6c,0x69,0x64,0x65,0x72,0x22,0x20,0x20,
|
||||
0x20,0x20,0x20,0x20,0x54,0x61,0x72,0x67,0x65,0x74,0x54,0x79,0x70,0x65,0x3d,0x22,
|
||||
0x43,0x45,0x47,0x55,0x49,0x2f,0x53,0x6c,0x69,0x64,0x65,0x72,0x22,0x20,0x20,0x20,
|
||||
0x20,0x20,0x20,0x52,0x65,0x6e,0x64,0x65,0x72,0x65,0x72,0x3d,0x22,0x46,0x61,0x6c,
|
||||
0x61,0x67,0x61,0x72,0x64,0x2f,0x53,0x6c,0x69,0x64,0x65,0x72,0x22,0x20,0x20,0x4c,
|
||||
0x6f,0x6f,0x6b,0x4e,0x46,0x65,0x65,0x6c,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,
|
||||
0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x53,0x6c,0x69,0x64,0x65,0x72,0x22,0x20,0x2f,0x3e,
|
||||
0x0a,0x09,0x3c,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x4d,0x61,0x70,0x70,0x69,
|
||||
0x6e,0x67,0x20,0x57,0x69,0x6e,0x64,0x6f,0x77,0x54,0x79,0x70,0x65,0x3d,0x22,0x54,
|
||||
0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x53,0x6c,0x69,0x64,0x65,
|
||||
0x72,0x54,0x68,0x75,0x6d,0x62,0x22,0x20,0x54,0x61,0x72,0x67,0x65,0x74,0x54,0x79,
|
||||
0x70,0x65,0x3d,0x22,0x43,0x45,0x47,0x55,0x49,0x2f,0x54,0x68,0x75,0x6d,0x62,0x22,
|
||||
0x20,0x52,0x65,0x6e,0x64,0x65,0x72,0x65,0x72,0x3d,0x22,0x46,0x61,0x6c,0x61,0x67,
|
||||
0x61,0x72,0x64,0x2f,0x42,0x75,0x74,0x74,0x6f,0x6e,0x22,0x20,0x20,0x4c,0x6f,0x6f,
|
||||
0x6b,0x4e,0x46,0x65,0x65,0x6c,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,
|
||||
0x6f,0x6f,0x6b,0x2f,0x53,0x6c,0x69,0x64,0x65,0x72,0x54,0x68,0x75,0x6d,0x62,0x22,
|
||||
0x20,0x2f,0x3e,0x0a,0x09,0x3c,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x4d,0x61,
|
||||
0x70,0x70,0x69,0x6e,0x67,0x20,0x57,0x69,0x6e,0x64,0x6f,0x77,0x54,0x79,0x70,0x65,
|
||||
0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x53,0x63,
|
||||
0x72,0x6f,0x6c,0x6c,0x61,0x62,0x6c,0x65,0x50,0x61,0x6e,0x65,0x22,0x20,0x54,0x61,
|
||||
0x72,0x67,0x65,0x74,0x54,0x79,0x70,0x65,0x3d,0x22,0x43,0x45,0x47,0x55,0x49,0x2f,
|
||||
0x53,0x63,0x72,0x6f,0x6c,0x6c,0x61,0x62,0x6c,0x65,0x50,0x61,0x6e,0x65,0x22,0x20,
|
||||
0x52,0x65,0x6e,0x64,0x65,0x72,0x65,0x72,0x3d,0x22,0x46,0x61,0x6c,0x61,0x67,0x61,
|
||||
0x72,0x64,0x2f,0x53,0x63,0x72,0x6f,0x6c,0x6c,0x61,0x62,0x6c,0x65,0x50,0x61,0x6e,
|
||||
0x65,0x22,0x20,0x4c,0x6f,0x6f,0x6b,0x4e,0x46,0x65,0x65,0x6c,0x3d,0x22,0x54,0x61,
|
||||
0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x53,0x63,0x72,0x6f,0x6c,0x6c,
|
||||
0x61,0x62,0x6c,0x65,0x50,0x61,0x6e,0x65,0x22,0x20,0x2f,0x3e,0x0a,0x09,0x3c,0x46,
|
||||
0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,0x20,0x57,
|
||||
0x69,0x6e,0x64,0x6f,0x77,0x54,0x79,0x70,0x65,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,
|
||||
0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x53,0x70,0x69,0x6e,0x6e,0x65,0x72,0x22,0x20,
|
||||
0x54,0x61,0x72,0x67,0x65,0x74,0x54,0x79,0x70,0x65,0x3d,0x22,0x43,0x45,0x47,0x55,
|
||||
0x49,0x2f,0x53,0x70,0x69,0x6e,0x6e,0x65,0x72,0x22,0x20,0x52,0x65,0x6e,0x64,0x65,
|
||||
0x72,0x65,0x72,0x3d,0x22,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x2f,0x44,0x65,
|
||||
0x66,0x61,0x75,0x6c,0x74,0x22,0x20,0x4c,0x6f,0x6f,0x6b,0x4e,0x46,0x65,0x65,0x6c,
|
||||
0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x53,0x70,
|
||||
0x69,0x6e,0x6e,0x65,0x72,0x22,0x20,0x2f,0x3e,0x0a,0x09,0x3c,0x46,0x61,0x6c,0x61,
|
||||
0x67,0x61,0x72,0x64,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,0x20,0x57,0x69,0x6e,0x64,
|
||||
0x6f,0x77,0x54,0x79,0x70,0x65,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,
|
||||
0x6f,0x6f,0x6b,0x2f,0x54,0x6f,0x6f,0x6c,0x74,0x69,0x70,0x22,0x20,0x54,0x61,0x72,
|
||||
0x67,0x65,0x74,0x54,0x79,0x70,0x65,0x3d,0x22,0x43,0x45,0x47,0x55,0x49,0x2f,0x54,
|
||||
0x6f,0x6f,0x6c,0x74,0x69,0x70,0x22,0x20,0x52,0x65,0x6e,0x64,0x65,0x72,0x65,0x72,
|
||||
0x3d,0x22,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x2f,0x54,0x6f,0x6f,0x6c,0x74,
|
||||
0x69,0x70,0x22,0x20,0x4c,0x6f,0x6f,0x6b,0x4e,0x46,0x65,0x65,0x6c,0x3d,0x22,0x54,
|
||||
0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x54,0x6f,0x6f,0x6c,0x74,
|
||||
0x69,0x70,0x22,0x20,0x2f,0x3e,0x0a,0x09,0x3c,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,
|
||||
0x64,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,0x20,0x57,0x69,0x6e,0x64,0x6f,0x77,0x54,
|
||||
0x79,0x70,0x65,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,
|
||||
0x2f,0x53,0x74,0x61,0x74,0x69,0x63,0x49,0x6d,0x61,0x67,0x65,0x22,0x20,0x54,0x61,
|
||||
0x72,0x67,0x65,0x74,0x54,0x79,0x70,0x65,0x3d,0x22,0x44,0x65,0x66,0x61,0x75,0x6c,
|
||||
0x74,0x57,0x69,0x6e,0x64,0x6f,0x77,0x22,0x20,0x52,0x65,0x6e,0x64,0x65,0x72,0x65,
|
||||
0x72,0x3d,0x22,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x2f,0x53,0x74,0x61,0x74,
|
||||
0x69,0x63,0x49,0x6d,0x61,0x67,0x65,0x22,0x20,0x4c,0x6f,0x6f,0x6b,0x4e,0x46,0x65,
|
||||
0x65,0x6c,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,
|
||||
0x53,0x74,0x61,0x74,0x69,0x63,0x49,0x6d,0x61,0x67,0x65,0x22,0x20,0x2f,0x3e,0x0a,
|
||||
0x09,0x3c,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x4d,0x61,0x70,0x70,0x69,0x6e,
|
||||
0x67,0x20,0x57,0x69,0x6e,0x64,0x6f,0x77,0x54,0x79,0x70,0x65,0x3d,0x22,0x54,0x61,
|
||||
0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x53,0x74,0x61,0x74,0x69,0x63,
|
||||
0x54,0x65,0x78,0x74,0x22,0x20,0x20,0x54,0x61,0x72,0x67,0x65,0x74,0x54,0x79,0x70,
|
||||
0x65,0x3d,0x22,0x44,0x65,0x66,0x61,0x75,0x6c,0x74,0x57,0x69,0x6e,0x64,0x6f,0x77,
|
||||
0x22,0x20,0x52,0x65,0x6e,0x64,0x65,0x72,0x65,0x72,0x3d,0x22,0x46,0x61,0x6c,0x61,
|
||||
0x67,0x61,0x72,0x64,0x2f,0x53,0x74,0x61,0x74,0x69,0x63,0x54,0x65,0x78,0x74,0x22,
|
||||
0x20,0x20,0x4c,0x6f,0x6f,0x6b,0x4e,0x46,0x65,0x65,0x6c,0x3d,0x22,0x54,0x61,0x68,
|
||||
0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x53,0x74,0x61,0x74,0x69,0x63,0x54,
|
||||
0x65,0x78,0x74,0x22,0x20,0x2f,0x3e,0x0a,0x09,0x3c,0x46,0x61,0x6c,0x61,0x67,0x61,
|
||||
0x72,0x64,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,0x20,0x57,0x69,0x6e,0x64,0x6f,0x77,
|
||||
0x54,0x79,0x70,0x65,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,
|
||||
0x6b,0x2f,0x49,0x74,0x65,0x6d,0x4c,0x69,0x73,0x74,0x62,0x6f,0x78,0x22,0x20,0x54,
|
||||
0x61,0x72,0x67,0x65,0x74,0x54,0x79,0x70,0x65,0x3d,0x22,0x43,0x45,0x47,0x55,0x49,
|
||||
0x2f,0x49,0x74,0x65,0x6d,0x4c,0x69,0x73,0x74,0x62,0x6f,0x78,0x22,0x20,0x52,0x65,
|
||||
0x6e,0x64,0x65,0x72,0x65,0x72,0x3d,0x22,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,
|
||||
0x2f,0x49,0x74,0x65,0x6d,0x4c,0x69,0x73,0x74,0x62,0x6f,0x78,0x22,0x20,0x4c,0x6f,
|
||||
0x6f,0x6b,0x4e,0x46,0x65,0x65,0x6c,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,
|
||||
0x4c,0x6f,0x6f,0x6b,0x2f,0x49,0x74,0x65,0x6d,0x4c,0x69,0x73,0x74,0x62,0x6f,0x78,
|
||||
0x22,0x20,0x2f,0x3e,0x0a,0x09,0x3c,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x4d,
|
||||
0x61,0x70,0x70,0x69,0x6e,0x67,0x20,0x57,0x69,0x6e,0x64,0x6f,0x77,0x54,0x79,0x70,
|
||||
0x65,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x4c,
|
||||
0x69,0x73,0x74,0x62,0x6f,0x78,0x49,0x74,0x65,0x6d,0x22,0x20,0x54,0x61,0x72,0x67,
|
||||
0x65,0x74,0x54,0x79,0x70,0x65,0x3d,0x22,0x43,0x45,0x47,0x55,0x49,0x2f,0x49,0x74,
|
||||
0x65,0x6d,0x45,0x6e,0x74,0x72,0x79,0x22,0x20,0x20,0x20,0x52,0x65,0x6e,0x64,0x65,
|
||||
0x72,0x65,0x72,0x3d,0x22,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x2f,0x49,0x74,
|
||||
0x65,0x6d,0x45,0x6e,0x74,0x72,0x79,0x22,0x20,0x20,0x20,0x4c,0x6f,0x6f,0x6b,0x4e,
|
||||
0x46,0x65,0x65,0x6c,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,
|
||||
0x6b,0x2f,0x4c,0x69,0x73,0x74,0x62,0x6f,0x78,0x49,0x74,0x65,0x6d,0x22,0x20,0x2f,
|
||||
0x3e,0x0a,0x09,0x3c,0x46,0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x4d,0x61,0x70,0x70,
|
||||
0x69,0x6e,0x67,0x20,0x57,0x69,0x6e,0x64,0x6f,0x77,0x54,0x79,0x70,0x65,0x3d,0x22,
|
||||
0x54,0x61,0x68,0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x47,0x72,0x6f,0x75,
|
||||
0x70,0x42,0x6f,0x78,0x22,0x20,0x54,0x61,0x72,0x67,0x65,0x74,0x54,0x79,0x70,0x65,
|
||||
0x3d,0x22,0x43,0x45,0x47,0x55,0x49,0x2f,0x47,0x72,0x6f,0x75,0x70,0x42,0x6f,0x78,
|
||||
0x22,0x20,0x20,0x20,0x52,0x65,0x6e,0x64,0x65,0x72,0x65,0x72,0x3d,0x22,0x46,0x61,
|
||||
0x6c,0x61,0x67,0x61,0x72,0x64,0x2f,0x44,0x65,0x66,0x61,0x75,0x6c,0x74,0x22,0x20,
|
||||
0x20,0x20,0x4c,0x6f,0x6f,0x6b,0x4e,0x46,0x65,0x65,0x6c,0x3d,0x22,0x54,0x61,0x68,
|
||||
0x61,0x72,0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x47,0x72,0x6f,0x75,0x70,0x42,0x6f,
|
||||
0x78,0x22,0x20,0x2f,0x3e,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x46,0x61,0x6c,
|
||||
0x61,0x67,0x61,0x72,0x64,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,0x20,0x57,0x69,0x6e,
|
||||
0x64,0x6f,0x77,0x54,0x79,0x70,0x65,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,0x65,0x7a,
|
||||
0x4c,0x6f,0x6f,0x6b,0x2f,0x54,0x72,0x65,0x65,0x22,0x20,0x54,0x61,0x72,0x67,0x65,
|
||||
0x74,0x54,0x79,0x70,0x65,0x3d,0x22,0x43,0x45,0x47,0x55,0x49,0x2f,0x54,0x72,0x65,
|
||||
0x65,0x22,0x20,0x20,0x20,0x52,0x65,0x6e,0x64,0x65,0x72,0x65,0x72,0x3d,0x22,0x46,
|
||||
0x61,0x6c,0x61,0x67,0x61,0x72,0x64,0x2f,0x54,0x72,0x65,0x65,0x22,0x20,0x20,0x20,
|
||||
0x4c,0x6f,0x6f,0x6b,0x4e,0x46,0x65,0x65,0x6c,0x3d,0x22,0x54,0x61,0x68,0x61,0x72,
|
||||
0x65,0x7a,0x4c,0x6f,0x6f,0x6b,0x2f,0x54,0x72,0x65,0x65,0x22,0x20,0x2f,0x3e,0x0a,
|
||||
0x3c,0x2f,0x47,0x55,0x49,0x53,0x63,0x68,0x65,0x6d,0x65,0x3e,0x0a};
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,147 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "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 (unsigned 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);
|
||||
|
||||
if (commands_map.find(command_name) == commands_map.end()) {
|
||||
THROW("invalid action bname %s", command_name.c_str());
|
||||
}
|
||||
int command_id = commands_map.find(command_name)->second;
|
||||
std::string keys = hotkey.substr(key_start + 1);
|
||||
parse_keys(command_id, keys);
|
||||
}
|
||||
@ -1,48 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_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
|
||||
@ -1,35 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_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
|
||||
@ -1,614 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
#include "inputs_channel.h"
|
||||
#include "utils.h"
|
||||
#include "debug.h"
|
||||
#include "red_client.h"
|
||||
#include "application.h"
|
||||
#include "display_channel.h"
|
||||
|
||||
#define SYNC_REMOTE_MODIFIERS
|
||||
|
||||
class SetInputsHandlerEvent: public Event {
|
||||
public:
|
||||
SetInputsHandlerEvent(InputsChannel& channel) : _channel (channel) {}
|
||||
|
||||
class AttachFunc: public ForEachChannelFunc {
|
||||
public:
|
||||
AttachFunc(InputsChannel& channel)
|
||||
: _channel (channel)
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool operator() (RedChannel& channel)
|
||||
{
|
||||
if (channel.get_type() == SPICE_CHANNEL_DISPLAY) {
|
||||
static_cast<DisplayChannel&>(channel).attach_inputs(&_channel);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
InputsChannel& _channel;
|
||||
};
|
||||
|
||||
virtual void response(AbstractProcessLoop& events_loop)
|
||||
{
|
||||
static_cast<Application*>(events_loop.get_owner())->set_key_handler(_channel);
|
||||
static_cast<Application*>(events_loop.get_owner())->set_mouse_handler(_channel);
|
||||
AttachFunc func(_channel);
|
||||
_channel.get_client().for_each_channel(func);
|
||||
}
|
||||
|
||||
private:
|
||||
InputsChannel& _channel;
|
||||
};
|
||||
|
||||
class KeyModifiersEvent: public Event {
|
||||
public:
|
||||
KeyModifiersEvent(InputsChannel& channel) : _channel (channel) {}
|
||||
|
||||
virtual void response(AbstractProcessLoop& events_loop)
|
||||
{
|
||||
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) {}
|
||||
|
||||
class DetachFunc: public ForEachChannelFunc {
|
||||
public:
|
||||
virtual bool operator() (RedChannel& channel)
|
||||
{
|
||||
if (channel.get_type() == SPICE_CHANNEL_DISPLAY) {
|
||||
static_cast<DisplayChannel&>(channel).detach_inputs();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
virtual void do_response(AbstractProcessLoop& events_loop)
|
||||
{
|
||||
static_cast<Application*>(events_loop.get_owner())->remove_key_handler(_channel);
|
||||
static_cast<Application*>(events_loop.get_owner())->remove_mouse_handler(_channel);
|
||||
DetachFunc detach_func;
|
||||
_channel.get_client().for_each_channel(detach_func);
|
||||
}
|
||||
|
||||
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(SPICE_MSGC_INPUTS_MOUSE_MOTION)
|
||||
, _channel (channel)
|
||||
{
|
||||
}
|
||||
|
||||
void MotionMessage::release()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
RedPeer::OutMessage& MotionMessage::peer_message()
|
||||
{
|
||||
|
||||
_channel.marshall_motion_event(_marshaller);
|
||||
|
||||
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(SPICE_MSGC_INPUTS_MOUSE_POSITION)
|
||||
, _channel (channel)
|
||||
{
|
||||
}
|
||||
|
||||
void PositionMessage::release()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
RedPeer::OutMessage& PositionMessage::peer_message()
|
||||
{
|
||||
_channel.marshall_position_event(_marshaller);
|
||||
return *this;
|
||||
}
|
||||
|
||||
class InputsMessHandler: public MessageHandlerImp<InputsChannel, SPICE_CHANNEL_INPUTS> {
|
||||
public:
|
||||
InputsMessHandler(InputsChannel& channel)
|
||||
: MessageHandlerImp<InputsChannel, SPICE_CHANNEL_INPUTS>(channel) {}
|
||||
};
|
||||
|
||||
InputsChannel::InputsChannel(RedClient& client, uint32_t id)
|
||||
: RedChannel(client, SPICE_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(SPICE_MSG_MIGRATE, &InputsChannel::handle_migrate);
|
||||
handler->set_handler(SPICE_MSG_SET_ACK, &InputsChannel::handle_set_ack);
|
||||
handler->set_handler(SPICE_MSG_PING, &InputsChannel::handle_ping);
|
||||
handler->set_handler(SPICE_MSG_WAIT_FOR_CHANNELS, &InputsChannel::handle_wait_for_channels);
|
||||
handler->set_handler(SPICE_MSG_DISCONNECTING, &InputsChannel::handle_disconnect);
|
||||
handler->set_handler(SPICE_MSG_NOTIFY, &InputsChannel::handle_notify);
|
||||
|
||||
handler->set_handler(SPICE_MSG_INPUTS_INIT, &InputsChannel::handle_init);
|
||||
handler->set_handler(SPICE_MSG_INPUTS_KEY_MODIFIERS, &InputsChannel::handle_modifiers);
|
||||
handler->set_handler(SPICE_MSG_INPUTS_MOUSE_MOTION_ACK, &InputsChannel::handle_motion_ack);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
SpiceMsgInputsInit* init = (SpiceMsgInputsInit*)message->data();
|
||||
_modifiers = init->keyboard_modifiers;
|
||||
AutoRef<SetInputsHandlerEvent> set_handler_event(new SetInputsHandlerEvent(*this));
|
||||
get_client().push_event(*set_handler_event);
|
||||
}
|
||||
|
||||
void InputsChannel::handle_modifiers(RedPeer::InMessage* message)
|
||||
{
|
||||
SpiceMsgInputsKeyModifiers* init = (SpiceMsgInputsKeyModifiers*)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 < SPICE_INPUT_MOTION_ACK_BUNCH) {
|
||||
LOG_WARN("invalid motion count");
|
||||
_motion_count = 0;
|
||||
} else {
|
||||
_motion_count -= SPICE_INPUT_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 SPICE_MOUSE_MODE_CLIENT:
|
||||
post_message(new PositionMessage(*this));
|
||||
break;
|
||||
case SPICE_MOUSE_MODE_SERVER:
|
||||
post_message(new MotionMessage(*this));
|
||||
break;
|
||||
default:
|
||||
THROW("invalid mouse mode");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputsChannel::marshall_motion_event(SpiceMarshaller *marshaller)
|
||||
{
|
||||
SpiceMsgcMouseMotion 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;
|
||||
|
||||
_marshallers->msgc_inputs_mouse_motion(marshaller, &motion);
|
||||
}
|
||||
|
||||
void InputsChannel::marshall_position_event(SpiceMarshaller *marshaller)
|
||||
{
|
||||
SpiceMsgcMousePosition 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;
|
||||
_marshallers->msgc_inputs_mouse_position(marshaller, &position);
|
||||
}
|
||||
|
||||
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 < SPICE_INPUT_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 < SPICE_INPUT_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(SPICE_MSGC_INPUTS_MOUSE_PRESS);
|
||||
SpiceMsgcMousePress event;
|
||||
event.button = button;
|
||||
event.buttons_state = buttons_state;
|
||||
_marshallers->msgc_inputs_mouse_press(message->marshaller(), &event);
|
||||
|
||||
post_message(message);
|
||||
}
|
||||
|
||||
void InputsChannel::on_mouse_up(int button, int buttons_state)
|
||||
{
|
||||
Message* message;
|
||||
|
||||
message = new Message(SPICE_MSGC_INPUTS_MOUSE_RELEASE);
|
||||
SpiceMsgcMouseRelease event;
|
||||
event.button = button;
|
||||
event.buttons_state = buttons_state;
|
||||
_marshallers->msgc_inputs_mouse_release(message->marshaller(), &event);
|
||||
post_message(message);
|
||||
}
|
||||
|
||||
InputsChannel::KeyInfo InputsChannel::_scan_table[REDKEY_NUM_KEYS];
|
||||
|
||||
uint32_t InputsChannel::get_make_scan_code(RedKey key)
|
||||
{
|
||||
return _scan_table[key].make_scan;
|
||||
}
|
||||
|
||||
uint32_t InputsChannel::get_break_scan_code(RedKey key)
|
||||
{
|
||||
return _scan_table[key].break_scan;
|
||||
}
|
||||
|
||||
void InputsChannel::on_key_down(RedKey key)
|
||||
{
|
||||
uint32_t scan_code = get_make_scan_code(key);
|
||||
if (!scan_code) {
|
||||
LOG_WARN("no make code for %d", key);
|
||||
return;
|
||||
}
|
||||
|
||||
Message* message = new Message(SPICE_MSGC_INPUTS_KEY_DOWN);
|
||||
SpiceMsgcKeyDown event;
|
||||
event.code = scan_code;
|
||||
_marshallers->msgc_inputs_key_down(message->marshaller(), &event);
|
||||
|
||||
post_message(message);
|
||||
}
|
||||
|
||||
void InputsChannel::on_key_up(RedKey key)
|
||||
{
|
||||
uint32_t scan_code = get_break_scan_code(key);
|
||||
if (!scan_code) {
|
||||
LOG_WARN("no break code for %d", key);
|
||||
return;
|
||||
}
|
||||
|
||||
Message* message = new Message(SPICE_MSGC_INPUTS_KEY_UP);
|
||||
SpiceMsgcKeyUp event;
|
||||
event.code = scan_code;
|
||||
_marshallers->msgc_inputs_key_up(message->marshaller(), &event);
|
||||
post_message(message);
|
||||
}
|
||||
|
||||
void InputsChannel::set_local_modifiers()
|
||||
{
|
||||
unsigned int modifiers = 0;
|
||||
|
||||
if (_modifiers & SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK) {
|
||||
modifiers |= Platform::SCROLL_LOCK_MODIFIER;
|
||||
}
|
||||
|
||||
if (_modifiers & SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK) {
|
||||
modifiers |= Platform::NUM_LOCK_MODIFIER;
|
||||
}
|
||||
|
||||
if (_modifiers & SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK) {
|
||||
modifiers |= Platform::CAPS_LOCK_MODIFIER;
|
||||
}
|
||||
|
||||
Platform::set_keyboard_lock_modifiers(modifiers);
|
||||
}
|
||||
|
||||
void InputsChannel::on_focus_in()
|
||||
{
|
||||
Lock lock(_update_modifiers_lock);
|
||||
_active_modifiers_event = false;
|
||||
_on_focus_modifiers = Platform::get_keyboard_lock_modifiers();
|
||||
|
||||
#ifdef SYNC_REMOTE_MODIFIERS
|
||||
Message* message = new Message(SPICE_MSGC_INPUTS_KEY_MODIFIERS);
|
||||
SpiceMsgcKeyModifiers modifiers;
|
||||
modifiers.modifiers = _on_focus_modifiers;
|
||||
_marshallers->msgc_inputs_key_modifiers(message->marshaller(), &modifiers);
|
||||
post_message(message);
|
||||
#else
|
||||
set_local_modifiers();
|
||||
#endif
|
||||
}
|
||||
|
||||
void InputsChannel::on_focus_out()
|
||||
{
|
||||
Lock lock(_update_modifiers_lock);
|
||||
_active_modifiers_event = true;
|
||||
#ifndef SYNC_REMOTE_MODIFIERS
|
||||
_modifiers = _on_focus_modifiers;
|
||||
set_local_modifiers();
|
||||
#endif
|
||||
}
|
||||
|
||||
void InputsChannel::init_scan_code(int index)
|
||||
{
|
||||
ASSERT((index & 0x80) == 0);
|
||||
_scan_table[index].make_scan = index;
|
||||
_scan_table[index].break_scan = index | 0x80;
|
||||
}
|
||||
|
||||
void InputsChannel::init_korean_scan_code(int index)
|
||||
{
|
||||
_scan_table[index].make_scan = index;
|
||||
_scan_table[index].break_scan = index;
|
||||
}
|
||||
|
||||
void InputsChannel::init_escape_scan_code(int index)
|
||||
{
|
||||
ASSERT(((index - REDKEY_ESCAPE_BASE) & 0x80) == 0);
|
||||
_scan_table[index].make_scan = 0xe0 | ((index - REDKEY_ESCAPE_BASE) << 8);
|
||||
_scan_table[index].break_scan = _scan_table[index].make_scan | 0x8000;
|
||||
}
|
||||
|
||||
void InputsChannel::init_pause_scan_code()
|
||||
{
|
||||
_scan_table[REDKEY_PAUSE].make_scan = 0x451de1;
|
||||
_scan_table[REDKEY_PAUSE].break_scan = 0xc59de1;
|
||||
}
|
||||
|
||||
void InputsChannel::init_scan_table()
|
||||
{
|
||||
memset(_scan_table, 0, sizeof(_scan_table));
|
||||
init_scan_code(REDKEY_ESCAPE);
|
||||
init_scan_code(REDKEY_1);
|
||||
init_scan_code(REDKEY_2);
|
||||
init_scan_code(REDKEY_3);
|
||||
init_scan_code(REDKEY_4);
|
||||
init_scan_code(REDKEY_5);
|
||||
init_scan_code(REDKEY_6);
|
||||
init_scan_code(REDKEY_7);
|
||||
init_scan_code(REDKEY_8);
|
||||
init_scan_code(REDKEY_9);
|
||||
init_scan_code(REDKEY_0);
|
||||
init_scan_code(REDKEY_MINUS);
|
||||
init_scan_code(REDKEY_EQUALS);
|
||||
init_scan_code(REDKEY_BACKSPACE);
|
||||
init_scan_code(REDKEY_TAB);
|
||||
init_scan_code(REDKEY_Q);
|
||||
init_scan_code(REDKEY_W);
|
||||
init_scan_code(REDKEY_E);
|
||||
init_scan_code(REDKEY_R);
|
||||
init_scan_code(REDKEY_T);
|
||||
init_scan_code(REDKEY_Y);
|
||||
init_scan_code(REDKEY_U);
|
||||
init_scan_code(REDKEY_I);
|
||||
init_scan_code(REDKEY_O);
|
||||
init_scan_code(REDKEY_P);
|
||||
init_scan_code(REDKEY_L_BRACKET);
|
||||
init_scan_code(REDKEY_R_BRACKET);
|
||||
init_scan_code(REDKEY_ENTER);
|
||||
init_scan_code(REDKEY_L_CTRL);
|
||||
init_scan_code(REDKEY_A);
|
||||
init_scan_code(REDKEY_S);
|
||||
init_scan_code(REDKEY_D);
|
||||
init_scan_code(REDKEY_F);
|
||||
init_scan_code(REDKEY_G);
|
||||
init_scan_code(REDKEY_H);
|
||||
init_scan_code(REDKEY_J);
|
||||
init_scan_code(REDKEY_K);
|
||||
init_scan_code(REDKEY_L);
|
||||
init_scan_code(REDKEY_SEMICOLON);
|
||||
init_scan_code(REDKEY_QUOTE);
|
||||
init_scan_code(REDKEY_BACK_QUOTE);
|
||||
init_scan_code(REDKEY_L_SHIFT);
|
||||
init_scan_code(REDKEY_BACK_SLASH);
|
||||
init_scan_code(REDKEY_Z);
|
||||
init_scan_code(REDKEY_X);
|
||||
init_scan_code(REDKEY_C);
|
||||
init_scan_code(REDKEY_V);
|
||||
init_scan_code(REDKEY_B);
|
||||
init_scan_code(REDKEY_N);
|
||||
init_scan_code(REDKEY_M);
|
||||
init_scan_code(REDKEY_COMMA);
|
||||
init_scan_code(REDKEY_PERIOD);
|
||||
init_scan_code(REDKEY_SLASH);
|
||||
init_scan_code(REDKEY_R_SHIFT);
|
||||
init_scan_code(REDKEY_PAD_MULTIPLY);
|
||||
init_scan_code(REDKEY_L_ALT);
|
||||
init_scan_code(REDKEY_SPACE);
|
||||
init_scan_code(REDKEY_CAPS_LOCK);
|
||||
init_scan_code(REDKEY_F1);
|
||||
init_scan_code(REDKEY_F2);
|
||||
init_scan_code(REDKEY_F3);
|
||||
init_scan_code(REDKEY_F4);
|
||||
init_scan_code(REDKEY_F5);
|
||||
init_scan_code(REDKEY_F6);
|
||||
init_scan_code(REDKEY_F7);
|
||||
init_scan_code(REDKEY_F8);
|
||||
init_scan_code(REDKEY_F9);
|
||||
init_scan_code(REDKEY_F10);
|
||||
init_scan_code(REDKEY_NUM_LOCK);
|
||||
init_scan_code(REDKEY_SCROLL_LOCK);
|
||||
init_scan_code(REDKEY_PAD_7);
|
||||
init_scan_code(REDKEY_PAD_8);
|
||||
init_scan_code(REDKEY_PAD_9);
|
||||
init_scan_code(REDKEY_PAD_MINUS);
|
||||
init_scan_code(REDKEY_PAD_4);
|
||||
init_scan_code(REDKEY_PAD_5);
|
||||
init_scan_code(REDKEY_PAD_6);
|
||||
init_scan_code(REDKEY_PAD_PLUS);
|
||||
init_scan_code(REDKEY_PAD_1);
|
||||
init_scan_code(REDKEY_PAD_2);
|
||||
init_scan_code(REDKEY_PAD_3);
|
||||
init_scan_code(REDKEY_PAD_0);
|
||||
init_scan_code(REDKEY_PAD_POINT);
|
||||
|
||||
init_scan_code(REDKEY_EUROPEAN);
|
||||
init_scan_code(REDKEY_F11);
|
||||
init_scan_code(REDKEY_F12);
|
||||
|
||||
init_scan_code(REDKEY_JAPANESE_HIRAGANA_KATAKANA);
|
||||
init_scan_code(REDKEY_JAPANESE_BACKSLASH);
|
||||
init_scan_code(REDKEY_JAPANESE_HENKAN);
|
||||
init_scan_code(REDKEY_JAPANESE_MUHENKAN);
|
||||
init_scan_code(REDKEY_JAPANESE_YEN);
|
||||
|
||||
init_korean_scan_code(REDKEY_KOREAN_HANGUL);
|
||||
init_korean_scan_code(REDKEY_KOREAN_HANGUL_HANJA);
|
||||
|
||||
init_escape_scan_code(REDKEY_ESCAPE_BASE);
|
||||
init_escape_scan_code(REDKEY_PAD_ENTER);
|
||||
init_escape_scan_code(REDKEY_R_CTRL);
|
||||
init_escape_scan_code(REDKEY_MUTE);
|
||||
init_escape_scan_code(REDKEY_FAKE_L_SHIFT);
|
||||
init_escape_scan_code(REDKEY_VOLUME_DOWN);
|
||||
init_escape_scan_code(REDKEY_VOLUME_UP);
|
||||
init_escape_scan_code(REDKEY_PAD_DIVIDE);
|
||||
init_escape_scan_code(REDKEY_FAKE_R_SHIFT);
|
||||
init_escape_scan_code(REDKEY_CTRL_PRINT_SCREEN);
|
||||
init_escape_scan_code(REDKEY_R_ALT);
|
||||
init_escape_scan_code(REDKEY_CTRL_BREAK);
|
||||
init_escape_scan_code(REDKEY_HOME);
|
||||
init_escape_scan_code(REDKEY_UP);
|
||||
init_escape_scan_code(REDKEY_PAGEUP);
|
||||
init_escape_scan_code(REDKEY_LEFT);
|
||||
init_escape_scan_code(REDKEY_RIGHT);
|
||||
init_escape_scan_code(REDKEY_END);
|
||||
init_escape_scan_code(REDKEY_DOWN);
|
||||
init_escape_scan_code(REDKEY_PAGEDOWN);
|
||||
init_escape_scan_code(REDKEY_INSERT);
|
||||
init_escape_scan_code(REDKEY_DELETE);
|
||||
init_escape_scan_code(REDKEY_LEFT_CMD);
|
||||
init_escape_scan_code(REDKEY_RIGHT_CMD);
|
||||
init_escape_scan_code(REDKEY_MENU);
|
||||
|
||||
init_pause_scan_code();
|
||||
}
|
||||
|
||||
class InitGlobals {
|
||||
public:
|
||||
InitGlobals()
|
||||
{
|
||||
InputsChannel::init_scan_table();
|
||||
}
|
||||
};
|
||||
|
||||
static InitGlobals init_globals;
|
||||
|
||||
class InputsFactory: public ChannelFactory {
|
||||
public:
|
||||
InputsFactory() : ChannelFactory(SPICE_CHANNEL_INPUTS) {}
|
||||
virtual RedChannel* construct(RedClient& client, uint32_t id)
|
||||
{
|
||||
return new InputsChannel(client, id);
|
||||
}
|
||||
};
|
||||
|
||||
static InputsFactory factory;
|
||||
|
||||
ChannelFactory& InputsChannel::Factory()
|
||||
{
|
||||
return factory;
|
||||
}
|
||||
@ -1,96 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_INPUTS_CHANNEL
|
||||
#define _H_INPUTS_CHANNEL
|
||||
|
||||
#include "red_channel.h"
|
||||
#include "inputs_handler.h"
|
||||
|
||||
class ChannelFactory;
|
||||
|
||||
class InputsChannel: public RedChannel, public KeyHandler, public MouseHandler {
|
||||
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_down(int button, int buttons_state);
|
||||
virtual void on_mouse_up(int button, int buttons_state);
|
||||
virtual void on_key_down(RedKey key);
|
||||
virtual void on_key_up(RedKey key);
|
||||
virtual void on_focus_in();
|
||||
virtual void on_focus_out();
|
||||
|
||||
void on_mouse_position(int x, int y, int buttons_state, int display_id);
|
||||
|
||||
static ChannelFactory& Factory();
|
||||
|
||||
protected:
|
||||
virtual void on_connect();
|
||||
virtual void on_disconnect();
|
||||
virtual void on_migrate();
|
||||
|
||||
private:
|
||||
void marshall_motion_event(SpiceMarshaller *marshaller);
|
||||
void marshall_position_event(SpiceMarshaller *marshaller);
|
||||
void set_local_modifiers();
|
||||
|
||||
void handle_init(RedPeer::InMessage* message);
|
||||
void handle_modifiers(RedPeer::InMessage* message);
|
||||
void handle_motion_ack(RedPeer::InMessage* message);
|
||||
|
||||
static uint32_t get_make_scan_code(RedKey key);
|
||||
static uint32_t get_break_scan_code(RedKey key);
|
||||
static void init_scan_code(int index);
|
||||
static void init_korean_scan_code(int index);
|
||||
static void init_escape_scan_code(int index);
|
||||
static void init_pause_scan_code();
|
||||
static void init_scan_table();
|
||||
|
||||
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;
|
||||
uint32_t _on_focus_modifiers;
|
||||
Mutex _update_modifiers_lock;
|
||||
bool _active_modifiers_event;
|
||||
|
||||
struct KeyInfo {
|
||||
uint32_t make_scan;
|
||||
uint32_t break_scan;
|
||||
};
|
||||
|
||||
static KeyInfo _scan_table[REDKEY_NUM_KEYS];
|
||||
|
||||
friend class InitGlobals;
|
||||
friend class MotionMessage;
|
||||
friend class PositionMessage;
|
||||
friend class KeyModifiersEvent;
|
||||
friend class SetInputsHandlerEvent;
|
||||
friend class RemoveInputsHandlerEvent;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@ -1,42 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_INPUTS_HANDLER
|
||||
#define _H_INPUTS_HANDLER
|
||||
|
||||
#include "red_key.h"
|
||||
|
||||
class KeyHandler {
|
||||
public:
|
||||
virtual ~KeyHandler() {}
|
||||
virtual void on_key_down(RedKey key) {}
|
||||
virtual void on_key_up(RedKey key) {}
|
||||
virtual void on_char(uint32_t ch) {}
|
||||
virtual void on_focus_in() {}
|
||||
virtual void on_focus_out() {}
|
||||
virtual bool permit_focus_loss() { return true;}
|
||||
};
|
||||
|
||||
class MouseHandler {
|
||||
public:
|
||||
virtual ~MouseHandler() {}
|
||||
virtual void on_mouse_motion(int dx, int dy, int buttons_state) {}
|
||||
virtual void on_mouse_down(int button, int buttons_state) {}
|
||||
virtual void on_mouse_up(int button, int buttons_state) {}
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,153 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
#include "jpeg_decoder.h"
|
||||
#include "debug.h"
|
||||
#include "utils.h"
|
||||
|
||||
#if !defined(jpeg_boolean)
|
||||
#define jpeg_boolean boolean
|
||||
#endif
|
||||
|
||||
static void op_begin_decode(SpiceJpegDecoder *decoder,
|
||||
uint8_t* data,
|
||||
int data_size,
|
||||
int* out_width,
|
||||
int* out_height)
|
||||
{
|
||||
JpegDecoder* _decoder = static_cast<JpegDecoder*>(decoder);
|
||||
_decoder->begin_decode(data, data_size, *out_width, *out_height);
|
||||
}
|
||||
|
||||
static void op_decode(SpiceJpegDecoder *decoder,
|
||||
uint8_t* dest,
|
||||
int stride,
|
||||
int format)
|
||||
{
|
||||
JpegDecoder* _decoder = static_cast<JpegDecoder*>(decoder);
|
||||
_decoder->decode(dest, stride, format);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
static void jpeg_decoder_init_source(j_decompress_ptr cinfo)
|
||||
{
|
||||
}
|
||||
|
||||
static SPICE_GNUC_NORETURN jpeg_boolean jpeg_decoder_fill_input_buffer(j_decompress_ptr cinfo)
|
||||
{
|
||||
PANIC("no more data for jpeg");
|
||||
}
|
||||
|
||||
static void jpeg_decoder_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
|
||||
{
|
||||
ASSERT(num_bytes < (long)cinfo->src->bytes_in_buffer);
|
||||
cinfo->src->next_input_byte += num_bytes;
|
||||
cinfo->src->bytes_in_buffer -= num_bytes;
|
||||
}
|
||||
|
||||
static void jpeg_decoder_term_source (j_decompress_ptr cinfo)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
JpegDecoder::JpegDecoder()
|
||||
: _data (NULL)
|
||||
, _data_size (0)
|
||||
{
|
||||
_cinfo.err = jpeg_std_error(&_jerr);
|
||||
jpeg_create_decompress(&_cinfo);
|
||||
|
||||
_cinfo.src = &_jsrc;
|
||||
_cinfo.src->init_source = jpeg_decoder_init_source;
|
||||
_cinfo.src->fill_input_buffer = jpeg_decoder_fill_input_buffer;
|
||||
_cinfo.src->skip_input_data = jpeg_decoder_skip_input_data;
|
||||
_cinfo.src->resync_to_restart = jpeg_resync_to_restart;
|
||||
_cinfo.src->term_source = jpeg_decoder_term_source;
|
||||
|
||||
static SpiceJpegDecoderOps decoder_ops = {
|
||||
op_begin_decode,
|
||||
op_decode,
|
||||
};
|
||||
|
||||
ops = &decoder_ops;
|
||||
}
|
||||
|
||||
JpegDecoder::~JpegDecoder()
|
||||
{
|
||||
jpeg_destroy_decompress(&_cinfo);
|
||||
}
|
||||
|
||||
void JpegDecoder::begin_decode(uint8_t* data, int data_size, int& out_width, int& out_height)
|
||||
{
|
||||
ASSERT(data);
|
||||
ASSERT(data_size);
|
||||
|
||||
if (_data) {
|
||||
jpeg_abort_decompress(&_cinfo);
|
||||
}
|
||||
|
||||
_data = data;
|
||||
_data_size = data_size;
|
||||
|
||||
_cinfo.src->next_input_byte = _data;
|
||||
_cinfo.src->bytes_in_buffer = _data_size;
|
||||
|
||||
jpeg_read_header(&_cinfo, TRUE);
|
||||
|
||||
_cinfo.out_color_space = JCS_RGB;
|
||||
_width = _cinfo.image_width;
|
||||
_height = _cinfo.image_height;
|
||||
|
||||
out_width = _width;
|
||||
out_height = _height;
|
||||
}
|
||||
|
||||
void JpegDecoder::decode(uint8_t *dest, int stride, int format)
|
||||
{
|
||||
uint8_t* scan_line = new uint8_t[_width*3];
|
||||
RGBConverter* rgb_converter;
|
||||
|
||||
switch (format) {
|
||||
case SPICE_BITMAP_FMT_24BIT:
|
||||
rgb_converter = &_rgb2bgr;
|
||||
break;
|
||||
case SPICE_BITMAP_FMT_32BIT:
|
||||
rgb_converter = &_rgb2bgrx;
|
||||
break;
|
||||
default:
|
||||
THROW("bad bitmap format, %d", format);
|
||||
}
|
||||
|
||||
jpeg_start_decompress(&_cinfo);
|
||||
|
||||
for (int row = 0; row < _height; row++) {
|
||||
jpeg_read_scanlines(&_cinfo, &scan_line, 1);
|
||||
rgb_converter->convert(scan_line, dest, _width);
|
||||
dest += stride;
|
||||
}
|
||||
|
||||
delete [] scan_line;
|
||||
|
||||
jpeg_finish_decompress(&_cinfo);
|
||||
}
|
||||
@ -1,96 +0,0 @@
|
||||
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
Copyright (C) 2010 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_JPEG_DECODER
|
||||
#define _H_JPEG_DECODER
|
||||
|
||||
#include "common.h"
|
||||
#include "red_canvas_base.h"
|
||||
|
||||
#if defined(WIN32) && !defined(__MINGW32__)
|
||||
/* We need some hacks to avoid warnings from the jpeg headers */
|
||||
#define XMD_H
|
||||
#undef FAR
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
#include <jpeglib.h>
|
||||
#ifdef HAVE_STDLIB_H
|
||||
/* on mingw, there is a hack,
|
||||
and we also include config.h from spice-common, which redefine it */
|
||||
#undef HAVE_STDLIB_H
|
||||
#endif
|
||||
}
|
||||
|
||||
class RGBConverter {
|
||||
public:
|
||||
virtual ~RGBConverter() {}
|
||||
virtual void convert(uint8_t* src, uint8_t* dest, int width) = 0;
|
||||
};
|
||||
|
||||
class RGBToBGRConverter : public RGBConverter {
|
||||
public:
|
||||
void convert(uint8_t* src, uint8_t* dest, int width)
|
||||
{
|
||||
for (int x = 0; x < width; x++) {
|
||||
*dest++ = src[2];
|
||||
*dest++ = src[1];
|
||||
*dest++ = src[0];
|
||||
src += 3;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class RGBToBGRXConverter : public RGBConverter {
|
||||
public:
|
||||
void convert(uint8_t* src, uint8_t* dest, int width)
|
||||
{
|
||||
for (int x = 0; x < width; x++) {
|
||||
*dest++ = src[2];
|
||||
*dest++ = src[1];
|
||||
*dest++ = src[0];
|
||||
*dest++ = 0;
|
||||
src += 3;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class JpegDecoder : public SpiceJpegDecoder {
|
||||
public:
|
||||
JpegDecoder();
|
||||
~JpegDecoder();
|
||||
|
||||
void begin_decode(uint8_t* data, int data_size, int& out_width, int& out_height);
|
||||
/* format is SPICE_BITMAP_FMT_<X> for the dest; currently, only
|
||||
x=32BIT and x=24BIT are supported */
|
||||
void decode(uint8_t* dest, int stride, int format);
|
||||
|
||||
private:
|
||||
struct jpeg_decompress_struct _cinfo;
|
||||
struct jpeg_error_mgr _jerr;
|
||||
struct jpeg_source_mgr _jsrc;
|
||||
|
||||
uint8_t* _data;
|
||||
int _data_size;
|
||||
int _width;
|
||||
int _height;
|
||||
|
||||
RGBToBGRConverter _rgb2bgr;
|
||||
RGBToBGRXConverter _rgb2bgrx;
|
||||
};
|
||||
#endif
|
||||
147
client/menu.cpp
147
client/menu.cpp
@ -1,147 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
#include "menu.h"
|
||||
#include "utils.h"
|
||||
#include "debug.h"
|
||||
|
||||
Menu::Menu(CommandTarget& target, const std::string& name, int id)
|
||||
: _refs (1)
|
||||
, _target (target)
|
||||
, _name (name)
|
||||
, _id (id)
|
||||
{
|
||||
}
|
||||
|
||||
Menu::~Menu()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
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, int state)
|
||||
{
|
||||
MenuCommand* cmd = new MenuCommand(name, cmd_id, state);
|
||||
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);
|
||||
}
|
||||
|
||||
void Menu::remove_command(int cmd_id)
|
||||
{
|
||||
for (unsigned int i = 0; i < _items.size(); i++) {
|
||||
if (_items[i].type == MENU_ITEM_TYPE_COMMAND &&
|
||||
((MenuCommand*)_items[i].obj)->get_cmd_id() == cmd_id) {
|
||||
delete (MenuCommand*)_items[i].obj;
|
||||
_items.erase(_items.begin() + i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::remove_sub(Menu* menu)
|
||||
{
|
||||
for (unsigned int i = 0; i < _items.size(); i++) {
|
||||
if (_items[i].type == MENU_ITEM_TYPE_MENU && (Menu*)_items[i].obj == menu) {
|
||||
((Menu*)_items[i].obj)->unref();
|
||||
_items.erase(_items.begin() + i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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, int& state)
|
||||
{
|
||||
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();
|
||||
state = cmd->get_state();
|
||||
}
|
||||
|
||||
Menu* Menu::sub_at(int pos)
|
||||
{
|
||||
if (_items[pos].type != MENU_ITEM_TYPE_MENU) {
|
||||
THROW("incorrect item type");
|
||||
}
|
||||
return ((Menu*)_items[pos].obj)->ref();
|
||||
}
|
||||
|
||||
Menu* Menu::find_sub(int id)
|
||||
{
|
||||
Menu* sub;
|
||||
|
||||
if (_id == id) {
|
||||
return ref();
|
||||
}
|
||||
for (unsigned int i = 0; i < _items.size(); i++) {
|
||||
if (_items[i].type == MENU_ITEM_TYPE_MENU && (sub = ((Menu*)_items[i].obj)->find_sub(id))) {
|
||||
return sub;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void Menu::clear()
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
_items.clear();
|
||||
}
|
||||
102
client/menu.h
102
client/menu.h
@ -1,102 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_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, int id = 0);
|
||||
|
||||
enum ItemType {
|
||||
MENU_ITEM_TYPE_INVALID,
|
||||
MENU_ITEM_TYPE_COMMAND,
|
||||
MENU_ITEM_TYPE_MENU,
|
||||
MENU_ITEM_TYPE_SEPARATOR,
|
||||
};
|
||||
|
||||
enum ItemState {
|
||||
MENU_ITEM_STATE_CHECKED = 1 << 0,
|
||||
MENU_ITEM_STATE_DIM = 1 << 1,
|
||||
};
|
||||
|
||||
Menu* ref() { _refs++; return this;}
|
||||
void unref() { if (!--_refs) delete this;}
|
||||
|
||||
void set_name(const std::string& name) { _name = name;}
|
||||
const std::string& get_name() { return _name;}
|
||||
CommandTarget& get_target() { return _target;}
|
||||
int get_id() { return _id;}
|
||||
|
||||
void add_command(const std::string& name, int cmd_id, int state = 0);
|
||||
void add_separator();
|
||||
void add_sub(Menu* sub);
|
||||
|
||||
void remove_command(int cmd_id);
|
||||
void remove_sub(Menu* menu);
|
||||
|
||||
ItemType item_type_at(int pos);
|
||||
void command_at(int pos, std::string& name, int& cmd_id, int& state);
|
||||
Menu* sub_at(int pos);
|
||||
Menu* find_sub(int id);
|
||||
|
||||
void clear();
|
||||
|
||||
private:
|
||||
virtual ~Menu();
|
||||
|
||||
class MenuCommand {
|
||||
public:
|
||||
MenuCommand(const std::string& name, int cmd_id, int state)
|
||||
: _name (name)
|
||||
, _cmd_id (cmd_id)
|
||||
, _state (state)
|
||||
{
|
||||
}
|
||||
|
||||
const std::string& get_name() { return _name;}
|
||||
int get_cmd_id() { return _cmd_id;}
|
||||
int get_state() { return _state;}
|
||||
|
||||
private:
|
||||
std::string _name;
|
||||
int _cmd_id;
|
||||
int _state;
|
||||
};
|
||||
|
||||
struct MenuItem {
|
||||
ItemType type;
|
||||
void *obj;
|
||||
};
|
||||
|
||||
void add_item(MenuItem& item);
|
||||
|
||||
private:
|
||||
int _refs;
|
||||
CommandTarget& _target;
|
||||
std::string _name;
|
||||
std::vector<MenuItem> _items;
|
||||
int _id;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,277 +0,0 @@
|
||||
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
Copyright (C) 2010 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
#include "debug.h"
|
||||
#include "utils.h"
|
||||
#include "mjpeg_decoder.h"
|
||||
|
||||
#if !defined(jpeg_boolean)
|
||||
#define jpeg_boolean boolean
|
||||
#endif
|
||||
|
||||
enum {
|
||||
STATE_READ_HEADER,
|
||||
STATE_START_DECOMPRESS,
|
||||
STATE_READ_SCANLINES,
|
||||
STATE_FINISH_DECOMPRESS
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
|
||||
static void init_source(j_decompress_ptr cinfo)
|
||||
{
|
||||
}
|
||||
|
||||
static jpeg_boolean fill_input_buffer(j_decompress_ptr cinfo)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void mjpeg_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
|
||||
{
|
||||
MJpegDecoder *decoder = (MJpegDecoder *)cinfo;
|
||||
if (num_bytes > 0) {
|
||||
if (cinfo->src->bytes_in_buffer >= (size_t)num_bytes) {
|
||||
cinfo->src->next_input_byte += (size_t) num_bytes;
|
||||
cinfo->src->bytes_in_buffer -= (size_t) num_bytes;
|
||||
} else {
|
||||
decoder->_extra_skip = num_bytes - cinfo->src->bytes_in_buffer;
|
||||
cinfo->src->bytes_in_buffer = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void term_source (j_decompress_ptr cinfo)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
MJpegDecoder::MJpegDecoder(int width, int height,
|
||||
int stride,
|
||||
uint8_t *frame,
|
||||
bool back_compat) :
|
||||
_data(NULL)
|
||||
, _data_size(0)
|
||||
, _data_start(0)
|
||||
, _data_end(0)
|
||||
, _extra_skip(0)
|
||||
, _width(width)
|
||||
, _height(height)
|
||||
, _stride(stride)
|
||||
, _frame(frame)
|
||||
, _back_compat(back_compat)
|
||||
, _y(0)
|
||||
, _state(0)
|
||||
{
|
||||
memset(&_cinfo, 0, sizeof(_cinfo));
|
||||
_cinfo.err = jpeg_std_error (&_jerr);
|
||||
jpeg_create_decompress (&_cinfo);
|
||||
|
||||
_cinfo.src = &_jsrc;
|
||||
_cinfo.src->init_source = init_source;
|
||||
_cinfo.src->fill_input_buffer = fill_input_buffer;
|
||||
_cinfo.src->skip_input_data = mjpeg_skip_input_data;
|
||||
_cinfo.src->resync_to_restart = jpeg_resync_to_restart;
|
||||
_cinfo.src->term_source = term_source;
|
||||
|
||||
_scanline = new uint8_t[width * 3];
|
||||
}
|
||||
|
||||
MJpegDecoder::~MJpegDecoder()
|
||||
{
|
||||
jpeg_destroy_decompress(&_cinfo);
|
||||
delete [] _scanline;
|
||||
if (_data) {
|
||||
delete [] _data;
|
||||
}
|
||||
}
|
||||
|
||||
void MJpegDecoder::convert_scanline(void)
|
||||
{
|
||||
uint32_t *row;
|
||||
uint32_t c;
|
||||
uint8_t *s;
|
||||
unsigned x;
|
||||
|
||||
ASSERT(_width % 2 == 0);
|
||||
ASSERT(_height % 2 == 0);
|
||||
|
||||
row = (uint32_t *)(_frame + _y * _stride);
|
||||
s = _scanline;
|
||||
|
||||
|
||||
if (_back_compat) {
|
||||
/* We need to check for the old major and for backwards compat
|
||||
a) swap r and b (done)
|
||||
b) to-yuv with right values and then from-yuv with old wrong values (TODO)
|
||||
*/
|
||||
for (x = 0; x < _width; x++) {
|
||||
c = s[2] << 16 | s[1] << 8 | s[0];
|
||||
s += 3;
|
||||
*row++ = c;
|
||||
}
|
||||
} else {
|
||||
for (x = 0; x < _width; x++) {
|
||||
c = s[0] << 16 | s[1] << 8 | s[2];
|
||||
s += 3;
|
||||
*row++ = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MJpegDecoder::append_data(uint8_t *data, size_t length)
|
||||
{
|
||||
uint8_t *new_data;
|
||||
size_t data_len;
|
||||
|
||||
if (length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_data_size - _data_end < length) {
|
||||
/* Can't fits in tail, need to make space */
|
||||
|
||||
data_len = _data_end - _data_start;
|
||||
if (_data_size - data_len < length) {
|
||||
/* Can't fit at all, grow a bit */
|
||||
_data_size = _data_size + length * 2;
|
||||
new_data = new uint8_t[_data_size];
|
||||
memcpy (new_data, _data + _data_start, data_len);
|
||||
delete [] _data;
|
||||
_data = new_data;
|
||||
} else {
|
||||
/* Just needs to compact */
|
||||
memmove (_data, _data + _data_start, data_len);
|
||||
}
|
||||
_data_start = 0;
|
||||
_data_end = data_len;
|
||||
}
|
||||
|
||||
memcpy (_data + _data_end, data, length);
|
||||
_data_end += length;
|
||||
}
|
||||
|
||||
bool MJpegDecoder::decode_data(uint8_t *data, size_t length)
|
||||
{
|
||||
bool got_picture;
|
||||
int res;
|
||||
|
||||
got_picture = false;
|
||||
|
||||
if (_extra_skip > 0) {
|
||||
if (_extra_skip >= length) {
|
||||
_extra_skip -= length;
|
||||
return false;
|
||||
} else {
|
||||
data += _extra_skip;
|
||||
length -= _extra_skip;
|
||||
_extra_skip = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (_data_end - _data_start == 0) {
|
||||
/* No current data, pass in without copy */
|
||||
|
||||
_jsrc.next_input_byte = data;
|
||||
_jsrc.bytes_in_buffer = length;
|
||||
} else {
|
||||
/* Need to combine the new and old data */
|
||||
append_data(data, length);
|
||||
|
||||
_jsrc.next_input_byte = _data + _data_start;
|
||||
_jsrc.bytes_in_buffer = _data_end - _data_start;
|
||||
}
|
||||
|
||||
switch (_state) {
|
||||
case STATE_READ_HEADER:
|
||||
res = jpeg_read_header(&_cinfo, TRUE);
|
||||
if (res == JPEG_SUSPENDED) {
|
||||
break;
|
||||
}
|
||||
|
||||
_cinfo.do_fancy_upsampling = FALSE;
|
||||
_cinfo.do_block_smoothing = FALSE;
|
||||
_cinfo.out_color_space = JCS_RGB;
|
||||
|
||||
PANIC_ON(_cinfo.image_width != _width);
|
||||
PANIC_ON(_cinfo.image_height != _height);
|
||||
|
||||
_state = STATE_START_DECOMPRESS;
|
||||
|
||||
/* fall through */
|
||||
case STATE_START_DECOMPRESS:
|
||||
res = jpeg_start_decompress (&_cinfo);
|
||||
|
||||
if (!res) {
|
||||
break;
|
||||
}
|
||||
|
||||
_state = STATE_READ_SCANLINES;
|
||||
|
||||
/* fall through */
|
||||
case STATE_READ_SCANLINES:
|
||||
res = 0;
|
||||
while (_y < _height) {
|
||||
res = jpeg_read_scanlines(&_cinfo, &_scanline, 1);
|
||||
|
||||
if (res == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
convert_scanline();
|
||||
_y++;
|
||||
}
|
||||
if (res == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
_state = STATE_FINISH_DECOMPRESS;
|
||||
|
||||
/* fall through */
|
||||
case STATE_FINISH_DECOMPRESS:
|
||||
res = jpeg_finish_decompress (&_cinfo);
|
||||
|
||||
if (!res) {
|
||||
break;
|
||||
}
|
||||
|
||||
_y = 0;
|
||||
_state = STATE_READ_HEADER;
|
||||
got_picture = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (_jsrc.next_input_byte == data) {
|
||||
/* We read directly from the user, store remaining data in
|
||||
buffer for next time */
|
||||
size_t read_size = _jsrc.next_input_byte - data;
|
||||
|
||||
append_data(data + read_size, length - read_size);
|
||||
} else {
|
||||
_data_start = _jsrc.next_input_byte - _data;
|
||||
_data_end = _data_start + _jsrc.bytes_in_buffer;
|
||||
}
|
||||
|
||||
return got_picture;
|
||||
}
|
||||
@ -1,74 +0,0 @@
|
||||
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
Copyright (C) 2010 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_MJPEG_DECODER
|
||||
#define _H_MJPEG_DECODER
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#ifdef WIN32
|
||||
/* We need some hacks to avoid warnings from the jpeg headers */
|
||||
#define XMD_H
|
||||
#undef FAR
|
||||
#endif
|
||||
extern "C" {
|
||||
#include <jpeglib.h>
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
void mjpeg_skip_input_data(j_decompress_ptr cinfo, long num_bytes);
|
||||
}
|
||||
|
||||
class MJpegDecoder {
|
||||
public:
|
||||
MJpegDecoder(int width, int height, int stride,
|
||||
uint8_t *frame, bool back_compat);
|
||||
~MJpegDecoder();
|
||||
|
||||
bool decode_data(uint8_t *data, size_t length);
|
||||
|
||||
private:
|
||||
|
||||
friend void mjpeg_skip_input_data(j_decompress_ptr cinfo, long num_bytes);
|
||||
|
||||
void convert_scanline(void);
|
||||
void append_data(uint8_t *data, size_t length);
|
||||
|
||||
struct jpeg_decompress_struct _cinfo;
|
||||
struct jpeg_error_mgr _jerr;
|
||||
struct jpeg_source_mgr _jsrc;
|
||||
|
||||
uint8_t *_data;
|
||||
size_t _data_size;
|
||||
size_t _data_start;
|
||||
size_t _data_end;
|
||||
size_t _extra_skip;
|
||||
|
||||
unsigned _width;
|
||||
unsigned _height;
|
||||
int _stride;
|
||||
uint8_t *_frame;
|
||||
bool _back_compat;
|
||||
|
||||
unsigned _y;
|
||||
uint8_t *_scanline;
|
||||
|
||||
int _state;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,49 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
#include "monitor.h"
|
||||
#include "debug.h"
|
||||
#include "platform.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;
|
||||
}
|
||||
|
||||
void Monitor::set_mode(int width, int height)
|
||||
{
|
||||
do_set_mode(width, height);
|
||||
Platform::reset_cursor_pos();
|
||||
}
|
||||
void Monitor::restore()
|
||||
{
|
||||
do_restore();
|
||||
Platform::reset_cursor_pos();
|
||||
}
|
||||
@ -1,57 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_MONITOR
|
||||
#define _H_MONITOR
|
||||
|
||||
#include "common/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;}
|
||||
|
||||
void set_mode(int width, int height);
|
||||
void restore();
|
||||
virtual int get_depth() = 0;
|
||||
virtual SpicePoint get_position() = 0;
|
||||
virtual SpicePoint 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() {}
|
||||
virtual void do_set_mode(int width, int height) = 0;
|
||||
virtual void do_restore() = 0;
|
||||
|
||||
private:
|
||||
int _id;
|
||||
bool _free;
|
||||
|
||||
protected:
|
||||
static uint32_t self_monitors_change;
|
||||
|
||||
friend class Platform;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,58 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_PIXELS_SOURCE
|
||||
#define _H_PIXELS_SOURCE
|
||||
|
||||
#include "common/draw.h"
|
||||
|
||||
#define PIXELES_SOURCE_OPAQUE_SIZE (20 * sizeof(void*))
|
||||
|
||||
class PixelsSource {
|
||||
public:
|
||||
PixelsSource();
|
||||
virtual ~PixelsSource();
|
||||
|
||||
virtual SpicePoint get_size() = 0;
|
||||
void set_origin(int x, int y) { _origin.x = x; _origin.y = y;}
|
||||
const SpicePoint& get_origin() { return _origin;}
|
||||
|
||||
protected:
|
||||
const uint8_t* get_opaque() const { return _opaque;}
|
||||
|
||||
private:
|
||||
SpicePoint _origin;
|
||||
uint8_t _opaque[PIXELES_SOURCE_OPAQUE_SIZE];
|
||||
|
||||
friend class RedDrawable;
|
||||
};
|
||||
|
||||
class ImageFromRes: public PixelsSource {
|
||||
public:
|
||||
ImageFromRes(int res_id);
|
||||
virtual ~ImageFromRes();
|
||||
virtual SpicePoint get_size();
|
||||
};
|
||||
|
||||
class AlphaImageFromRes: public PixelsSource {
|
||||
public:
|
||||
AlphaImageFromRes(int res_id);
|
||||
virtual ~AlphaImageFromRes();
|
||||
virtual SpicePoint get_size();
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,205 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_PLATFORM
|
||||
#define _H_PLATFORM
|
||||
|
||||
#include "cursor.h"
|
||||
#include "process_loop.h"
|
||||
#include "event_sources.h"
|
||||
#include "process_loop.h"
|
||||
|
||||
class WaveRecordAbstract;
|
||||
class WavePlaybackAbstract;
|
||||
class Icon;
|
||||
|
||||
class Monitor;
|
||||
typedef std::list<Monitor*> MonitorsList;
|
||||
|
||||
class Platform {
|
||||
public:
|
||||
static void init();
|
||||
static void set_process_loop(ProcessLoop& main_process_loop);
|
||||
static void msleep(unsigned int millisec);
|
||||
static void yield();
|
||||
static uint64_t get_monolithic_time();
|
||||
static void get_temp_dir(std::string& path);
|
||||
static void get_app_data_dir(std::string& path, const std::string& app_name);
|
||||
static void path_append(std::string& path, const std::string& partial_path);
|
||||
static uint64_t get_process_id();
|
||||
static uint64_t get_thread_id();
|
||||
static SPICE_GNUC_PRINTF(1, 2) void term_printf(const char* format, ...);
|
||||
static void error_beep();
|
||||
|
||||
static const MonitorsList& init_monitors();
|
||||
static void destroy_monitors();
|
||||
static bool is_monitors_pos_valid();
|
||||
|
||||
static void send_quit_request();
|
||||
|
||||
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 RecordClient;
|
||||
static WaveRecordAbstract* create_recorder(RecordClient& client,
|
||||
uint32_t samples_per_sec,
|
||||
uint32_t bits_per_sample,
|
||||
uint32_t channels,
|
||||
uint32_t frame_size);
|
||||
static WavePlaybackAbstract* create_player(uint32_t samples_per_sec,
|
||||
uint32_t bits_per_sample,
|
||||
uint32_t channels,
|
||||
uint32_t frame_size);
|
||||
|
||||
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_lock_modifiers();
|
||||
static void set_keyboard_lock_modifiers(uint32_t modifiers);
|
||||
|
||||
enum {
|
||||
L_SHIFT_MODIFIER_SHIFT,
|
||||
R_SHIFT_MODIFIER_SHIFT,
|
||||
L_CTRL_MODIFIER_SHIFT,
|
||||
R_CTRL_MODIFIER_SHIFT,
|
||||
L_ALT_MODIFIER_SHIFT,
|
||||
R_ALT_MODIFIER_SHIFT,
|
||||
|
||||
L_SHIFT_MODIFIER = (1 << L_SHIFT_MODIFIER_SHIFT),
|
||||
R_SHIFT_MODIFIER = (1 << R_SHIFT_MODIFIER_SHIFT),
|
||||
L_CTRL_MODIFIER = (1 << L_CTRL_MODIFIER_SHIFT),
|
||||
R_CTRL_MODIFIER = (1 << R_CTRL_MODIFIER_SHIFT),
|
||||
L_ALT_MODIFIER = (1 << L_ALT_MODIFIER_SHIFT),
|
||||
R_ALT_MODIFIER = (1 << R_ALT_MODIFIER_SHIFT),
|
||||
};
|
||||
|
||||
static uint32_t get_keyboard_modifiers();
|
||||
|
||||
static void reset_cursor_pos();
|
||||
|
||||
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 DisplayModeListener;
|
||||
static void set_display_mode_listner(DisplayModeListener* listener);
|
||||
|
||||
class ClipboardListener;
|
||||
static void set_clipboard_listener(ClipboardListener* listener);
|
||||
|
||||
static bool on_clipboard_grab(uint32_t *types, uint32_t type_count);
|
||||
static bool on_clipboard_notify(uint32_t type, const uint8_t* data, int32_t size);
|
||||
static bool on_clipboard_request(uint32_t type);
|
||||
static void on_clipboard_release();
|
||||
|
||||
enum { owner_none, owner_guest, owner_client };
|
||||
|
||||
static void set_clipboard_owner(int new_owner);
|
||||
static int get_clipboard_owner() { return _clipboard_owner; }
|
||||
|
||||
private:
|
||||
static void set_clipboard_owner_unlocked(int new_owner);
|
||||
|
||||
static int _clipboard_owner;
|
||||
};
|
||||
|
||||
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::ClipboardListener {
|
||||
public:
|
||||
virtual ~ClipboardListener() {}
|
||||
virtual void on_clipboard_grab(uint32_t *types, uint32_t type_count) = 0;
|
||||
virtual void on_clipboard_request(uint32_t type) = 0;
|
||||
virtual void on_clipboard_notify(uint32_t type, uint8_t* data, int32_t size) = 0;
|
||||
virtual void on_clipboard_release() = 0;
|
||||
};
|
||||
|
||||
class Platform::RecordClient {
|
||||
public:
|
||||
virtual ~RecordClient() {}
|
||||
virtual void add_event_source(EventSources::File& evnet_source) = 0;
|
||||
virtual void remove_event_source(EventSources::File& evnet_source) = 0;
|
||||
virtual void add_event_source(EventSources::Trigger& evnet_source) = 0;
|
||||
virtual void remove_event_source(EventSources::Trigger& evnet_source) = 0;
|
||||
virtual void push_frame(uint8_t *frame) = 0;
|
||||
};
|
||||
|
||||
class Platform::DisplayModeListener {
|
||||
public:
|
||||
virtual ~DisplayModeListener() {}
|
||||
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
|
||||
@ -1,364 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "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, SPICE_CHANNEL_PLAYBACK> {
|
||||
public:
|
||||
PlaybackHandler(PlaybackChannel& channel)
|
||||
: MessageHandlerImp<PlaybackChannel, SPICE_CHANNEL_PLAYBACK>(channel) {}
|
||||
};
|
||||
|
||||
PlaybackChannel::PlaybackChannel(RedClient& client, uint32_t id)
|
||||
: RedChannel(client, SPICE_CHANNEL_PLAYBACK, id, new PlaybackHandler(*this),
|
||||
Platform::PRIORITY_HIGH)
|
||||
, _wave_player (NULL)
|
||||
, _mode (SPICE_AUDIO_DATA_MODE_INVALID)
|
||||
, _codec(NULL)
|
||||
, _playing (false)
|
||||
{
|
||||
#ifdef WAVE_CAPTURE
|
||||
init_wave();
|
||||
#endif
|
||||
PlaybackHandler* handler = static_cast<PlaybackHandler*>(get_message_handler());
|
||||
|
||||
handler->set_handler(SPICE_MSG_MIGRATE, &PlaybackChannel::handle_migrate);
|
||||
handler->set_handler(SPICE_MSG_SET_ACK, &PlaybackChannel::handle_set_ack);
|
||||
handler->set_handler(SPICE_MSG_PING, &PlaybackChannel::handle_ping);
|
||||
handler->set_handler(SPICE_MSG_WAIT_FOR_CHANNELS, &PlaybackChannel::handle_wait_for_channels);
|
||||
handler->set_handler(SPICE_MSG_DISCONNECTING, &PlaybackChannel::handle_disconnect);
|
||||
handler->set_handler(SPICE_MSG_NOTIFY, &PlaybackChannel::handle_notify);
|
||||
|
||||
handler->set_handler(SPICE_MSG_PLAYBACK_MODE, &PlaybackChannel::handle_mode);
|
||||
|
||||
if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1, SND_CODEC_ANY_FREQUENCY))
|
||||
set_capability(SPICE_PLAYBACK_CAP_CELT_0_5_1);
|
||||
if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, SND_CODEC_ANY_FREQUENCY))
|
||||
set_capability(SPICE_PLAYBACK_CAP_OPUS);
|
||||
}
|
||||
|
||||
void PlaybackChannel::clear()
|
||||
{
|
||||
if (_wave_player) {
|
||||
_playing = false;
|
||||
_wave_player->stop();
|
||||
delete _wave_player;
|
||||
_wave_player = NULL;
|
||||
}
|
||||
_mode = SPICE_AUDIO_DATA_MODE_INVALID;
|
||||
|
||||
snd_codec_destroy(&_codec);
|
||||
}
|
||||
|
||||
void PlaybackChannel::on_disconnect()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
PlaybackChannel::~PlaybackChannel(void)
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
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 == SPICE_AUDIO_DATA_MODE_RAW) {
|
||||
handler->set_handler(SPICE_MSG_PLAYBACK_DATA, &PlaybackChannel::handle_raw_data);
|
||||
} else if (snd_codec_is_capable(_mode, SND_CODEC_ANY_FREQUENCY)) {
|
||||
handler->set_handler(SPICE_MSG_PLAYBACK_DATA, &PlaybackChannel::handle_compressed_data);
|
||||
} else {
|
||||
THROW("invalid mode");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void PlaybackChannel::handle_mode(RedPeer::InMessage* message)
|
||||
{
|
||||
SpiceMsgPlaybackMode* playback_mode = (SpiceMsgPlaybackMode*)message->data();
|
||||
if (playback_mode->mode != SPICE_AUDIO_DATA_MODE_RAW
|
||||
&& !snd_codec_is_capable(playback_mode->mode, SND_CODEC_ANY_FREQUENCY) ) {
|
||||
THROW("invalid mode");
|
||||
}
|
||||
|
||||
_mode = playback_mode->mode;
|
||||
if (_playing) {
|
||||
set_data_handler();
|
||||
return;
|
||||
}
|
||||
|
||||
PlaybackHandler* handler = static_cast<PlaybackHandler*>(get_message_handler());
|
||||
handler->set_handler(SPICE_MSG_PLAYBACK_START, &PlaybackChannel::handle_start);
|
||||
}
|
||||
|
||||
void PlaybackChannel::null_handler(RedPeer::InMessage* message)
|
||||
{
|
||||
}
|
||||
|
||||
void PlaybackChannel::disable()
|
||||
{
|
||||
PlaybackHandler* handler = static_cast<PlaybackHandler*>(get_message_handler());
|
||||
|
||||
handler->set_handler(SPICE_MSG_PLAYBACK_START, &PlaybackChannel::null_handler);
|
||||
handler->set_handler(SPICE_MSG_PLAYBACK_STOP, &PlaybackChannel::null_handler);
|
||||
handler->set_handler(SPICE_MSG_PLAYBACK_MODE, &PlaybackChannel::null_handler);
|
||||
handler->set_handler(SPICE_MSG_PLAYBACK_DATA, &PlaybackChannel::null_handler);
|
||||
}
|
||||
|
||||
void PlaybackChannel::handle_start(RedPeer::InMessage* message)
|
||||
{
|
||||
PlaybackHandler* handler = static_cast<PlaybackHandler*>(get_message_handler());
|
||||
SpiceMsgPlaybackStart* start = (SpiceMsgPlaybackStart*)message->data();
|
||||
|
||||
handler->set_handler(SPICE_MSG_PLAYBACK_START, NULL);
|
||||
handler->set_handler(SPICE_MSG_PLAYBACK_STOP, &PlaybackChannel::handle_stop);
|
||||
|
||||
#ifdef WAVE_CAPTURE
|
||||
start_wave();
|
||||
#endif
|
||||
if (!_wave_player) {
|
||||
if (start->format != SPICE_AUDIO_FMT_S16) {
|
||||
THROW("unexpected format");
|
||||
}
|
||||
if (start->channels != 2) {
|
||||
THROW("unexpected number of channels");
|
||||
}
|
||||
int bits_per_sample = 16;
|
||||
int frame_size = SND_CODEC_MAX_FRAME_SIZE;
|
||||
|
||||
if (_mode != SPICE_AUDIO_DATA_MODE_RAW) {
|
||||
if (snd_codec_create(&_codec, _mode, start->frequency, SND_CODEC_DECODE) != SND_CODEC_OK)
|
||||
THROW("create decoder");
|
||||
frame_size = snd_codec_frame_size(_codec);
|
||||
}
|
||||
|
||||
try {
|
||||
_wave_player = Platform::create_player(start->frequency, bits_per_sample,
|
||||
start->channels, frame_size);
|
||||
} catch (...) {
|
||||
LOG_WARN("create player failed");
|
||||
//todo: support disconnecting single channel
|
||||
disable();
|
||||
return;
|
||||
}
|
||||
|
||||
_frame_bytes = frame_size * start->channels * bits_per_sample / 8;
|
||||
}
|
||||
_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(SPICE_MSG_PLAYBACK_STOP, NULL);
|
||||
handler->set_handler(SPICE_MSG_PLAYBACK_DATA, NULL);
|
||||
handler->set_handler(SPICE_MSG_PLAYBACK_START, &PlaybackChannel::handle_start);
|
||||
|
||||
#ifdef WAVE_CAPTURE
|
||||
end_wave();
|
||||
#endif
|
||||
_wave_player->stop();
|
||||
_playing = false;
|
||||
}
|
||||
|
||||
void PlaybackChannel::handle_raw_data(RedPeer::InMessage* message)
|
||||
{
|
||||
SpiceMsgPlaybackPacket* packet = (SpiceMsgPlaybackPacket*)message->data();
|
||||
uint8_t* data = packet->data;
|
||||
uint32_t size = packet->data_size;
|
||||
#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_compressed_data(RedPeer::InMessage* message)
|
||||
{
|
||||
SpiceMsgPlaybackPacket* packet = (SpiceMsgPlaybackPacket*)message->data();
|
||||
uint8_t* data = packet->data;
|
||||
uint32_t size = packet->data_size;
|
||||
int pcm_size = _frame_bytes;
|
||||
uint8_t pcm[_frame_bytes];
|
||||
|
||||
if (snd_codec_decode(_codec, data, size, pcm, &pcm_size) != SND_CODEC_OK)
|
||||
THROW("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(SPICE_CHANNEL_PLAYBACK) {}
|
||||
virtual RedChannel* construct(RedClient& client, uint32_t id)
|
||||
{
|
||||
return new PlaybackChannel(client, id);
|
||||
}
|
||||
};
|
||||
|
||||
static PlaybackFactory factory;
|
||||
|
||||
ChannelFactory& PlaybackChannel::Factory()
|
||||
{
|
||||
return factory;
|
||||
}
|
||||
@ -1,406 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
#include "process_loop.h"
|
||||
#include "debug.h"
|
||||
#include "platform.h"
|
||||
#include "utils.h"
|
||||
|
||||
SyncEvent::SyncEvent()
|
||||
: _err (false)
|
||||
, _ready (false)
|
||||
{
|
||||
}
|
||||
|
||||
SyncEvent::~SyncEvent()
|
||||
{
|
||||
}
|
||||
|
||||
void SyncEvent::response(AbstractProcessLoop& events_loop)
|
||||
{
|
||||
try {
|
||||
do_response(events_loop);
|
||||
} catch (Exception& e) {
|
||||
LOG_WARN("unhandled exception: %s", e.what());
|
||||
_err = true;
|
||||
} catch (...) {
|
||||
_err = true;
|
||||
}
|
||||
Lock lock(_mutex);
|
||||
_ready = true;
|
||||
_condition.notify_one();
|
||||
}
|
||||
|
||||
void SyncEvent::wait()
|
||||
{
|
||||
#ifdef RED_DEBUG
|
||||
ASSERT(_process_loop && !_process_loop->is_same_thread(pthread_self()));
|
||||
#endif
|
||||
Lock lock(_mutex);
|
||||
while (!_ready) {
|
||||
_condition.wait(lock);
|
||||
}
|
||||
}
|
||||
|
||||
class ProcessLoop::QuitEvent: public Event {
|
||||
public:
|
||||
QuitEvent(int error_code) : _error_code(error_code) {}
|
||||
virtual void response(AbstractProcessLoop& events_loop);
|
||||
private:
|
||||
int _error_code;
|
||||
};
|
||||
|
||||
void ProcessLoop::QuitEvent::response(AbstractProcessLoop& events_loop)
|
||||
{
|
||||
events_loop.do_quit(_error_code);
|
||||
}
|
||||
|
||||
/* EventsQueue */
|
||||
|
||||
EventsQueue::EventsQueue(AbstractProcessLoop& owner)
|
||||
: _events_gen (0)
|
||||
, _owner (owner)
|
||||
{
|
||||
}
|
||||
|
||||
EventsQueue::~EventsQueue()
|
||||
{
|
||||
clear_queue();
|
||||
}
|
||||
|
||||
void EventsQueue::clear_queue()
|
||||
{
|
||||
Lock lock(_events_lock);
|
||||
while (!_events.empty()) {
|
||||
Event* event = _events.front();
|
||||
_events.pop_front();
|
||||
event->unref();
|
||||
}
|
||||
}
|
||||
|
||||
int EventsQueue::push_event(Event* event)
|
||||
{
|
||||
Lock lock(_events_lock);
|
||||
_events.push_back(event);
|
||||
event->set_generation(_events_gen);
|
||||
event->ref();
|
||||
#ifdef RED_DEBUG
|
||||
event->set_process_loop(&_owner);
|
||||
#endif
|
||||
return _events.size();
|
||||
}
|
||||
|
||||
void EventsQueue::process_events()
|
||||
{
|
||||
_events_gen++;
|
||||
|
||||
for (;;) {
|
||||
Event* event;
|
||||
Lock lock(_events_lock);
|
||||
if (_events.empty()) {
|
||||
return;
|
||||
}
|
||||
event = _events.front();
|
||||
if (event->get_generation() == _events_gen) {
|
||||
return;
|
||||
}
|
||||
_events.pop_front();
|
||||
|
||||
lock.unlock();
|
||||
event->response(_owner);
|
||||
event->unref();
|
||||
}
|
||||
}
|
||||
|
||||
bool EventsQueue::is_empty()
|
||||
{
|
||||
Lock lock(_events_lock);
|
||||
return _events.empty();
|
||||
}
|
||||
|
||||
/* Timers Queue */
|
||||
|
||||
Timer::Timer()
|
||||
: _is_armed (false)
|
||||
{
|
||||
}
|
||||
|
||||
Timer::~Timer()
|
||||
{
|
||||
}
|
||||
|
||||
void Timer::arm(uint32_t msec)
|
||||
{
|
||||
_interval = msec;
|
||||
_expiration = get_now();
|
||||
calc_next_expiration_time();
|
||||
_is_armed = true;
|
||||
}
|
||||
|
||||
void Timer::disarm()
|
||||
{
|
||||
_is_armed = false;
|
||||
}
|
||||
|
||||
#define TIMER_COMPENSATION
|
||||
|
||||
void Timer::calc_next_expiration_time(uint64_t now)
|
||||
{
|
||||
#ifndef TIMER_COMPENSATION
|
||||
_expiratoin = now;
|
||||
#endif
|
||||
calc_next_expiration_time();
|
||||
#ifdef TIMER_COMPENSATION
|
||||
if (_expiration <= now) {
|
||||
_expiration = now;
|
||||
calc_next_expiration_time();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
uint64_t Timer::get_now()
|
||||
{
|
||||
return (Platform::get_monolithic_time() / 1000 / 1000);
|
||||
}
|
||||
|
||||
TimersQueue::TimersQueue(AbstractProcessLoop& owner)
|
||||
: _owner (owner)
|
||||
{
|
||||
}
|
||||
|
||||
TimersQueue::~TimersQueue()
|
||||
{
|
||||
clear_queue();
|
||||
}
|
||||
|
||||
void TimersQueue::clear_queue()
|
||||
{
|
||||
RecurciveLock lock(_timers_lock);
|
||||
TimersSet::iterator iter;
|
||||
for (iter = _armed_timers.begin(); iter != _armed_timers.end(); iter++) {
|
||||
(*iter)->disarm();
|
||||
}
|
||||
_armed_timers.clear();
|
||||
}
|
||||
|
||||
void TimersQueue::activate_interval_timer(Timer* timer, unsigned int millisec)
|
||||
{
|
||||
RecurciveLock lock(_timers_lock);
|
||||
timer->ref();
|
||||
deactivate_interval_timer(timer);
|
||||
timer->arm(millisec);
|
||||
_armed_timers.insert(timer);
|
||||
}
|
||||
|
||||
void TimersQueue::deactivate_interval_timer(Timer* timer)
|
||||
{
|
||||
RecurciveLock lock(_timers_lock);
|
||||
if (timer->is_armed()) {
|
||||
#ifdef RED_DEBUG
|
||||
int ret =
|
||||
#endif
|
||||
_armed_timers.erase(timer);
|
||||
ASSERT(ret);
|
||||
timer->disarm();
|
||||
timer->unref();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int TimersQueue::get_soonest_timeout()
|
||||
{
|
||||
RecurciveLock lock(_timers_lock);
|
||||
TimersSet::iterator iter;
|
||||
iter = _armed_timers.begin();
|
||||
if (iter == _armed_timers.end()) {
|
||||
return INFINITE;
|
||||
}
|
||||
|
||||
uint64_t now = Timer::get_now();
|
||||
uint64_t next_time = (*iter)->get_expiration();
|
||||
|
||||
if (next_time <= now) {
|
||||
return 0;
|
||||
}
|
||||
return (int)(next_time - now);
|
||||
}
|
||||
|
||||
|
||||
void TimersQueue::timers_action()
|
||||
{
|
||||
RecurciveLock lock(_timers_lock);
|
||||
uint64_t now = Timer::get_now();
|
||||
TimersSet::iterator iter;
|
||||
|
||||
while (((iter = _armed_timers.begin()) != _armed_timers.end()) &&
|
||||
((*iter)->get_expiration() <= now)) {
|
||||
Timer* timer = *iter;
|
||||
_armed_timers.erase(iter);
|
||||
timer->calc_next_expiration_time(now);
|
||||
_armed_timers.insert(timer);
|
||||
timer->response(_owner);
|
||||
}
|
||||
}
|
||||
|
||||
ProcessLoop::ProcessLoop(void* owner)
|
||||
: _events_queue (*this)
|
||||
, _timers_queue (*this)
|
||||
, _owner (owner)
|
||||
, _quitting (false)
|
||||
, _exit_code (0)
|
||||
, _started (false)
|
||||
{
|
||||
_event_sources.add_trigger(_wakeup_trigger);
|
||||
}
|
||||
|
||||
ProcessLoop::~ProcessLoop()
|
||||
{
|
||||
_event_sources.remove_trigger(_wakeup_trigger);
|
||||
}
|
||||
|
||||
int ProcessLoop::run()
|
||||
{
|
||||
_thread = pthread_self();
|
||||
_started = true;
|
||||
on_start_running();
|
||||
for (;;) {
|
||||
if (_event_sources.wait_events(_timers_queue.get_soonest_timeout())) {
|
||||
_quitting = true;
|
||||
break;
|
||||
}
|
||||
_timers_queue.timers_action();
|
||||
process_events_queue();
|
||||
if (_quitting) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return _exit_code;
|
||||
}
|
||||
|
||||
void ProcessLoop::do_quit(int error_code)
|
||||
{
|
||||
ASSERT(!_started || pthread_equal(pthread_self(), _thread));
|
||||
if (_quitting) {
|
||||
return;
|
||||
}
|
||||
_quitting = true;
|
||||
_exit_code = error_code;
|
||||
}
|
||||
|
||||
void ProcessLoop::quit(int error_code)
|
||||
{
|
||||
AutoRef<QuitEvent> quit_event(new QuitEvent(error_code));
|
||||
push_event(*quit_event);
|
||||
}
|
||||
|
||||
void ProcessLoop::process_events_queue()
|
||||
{
|
||||
ASSERT(!_started || pthread_equal(pthread_self(), _thread));
|
||||
_events_queue.process_events();
|
||||
if (!_events_queue.is_empty()) {
|
||||
wakeup();
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessLoop::wakeup()
|
||||
{
|
||||
_wakeup_trigger.trigger();
|
||||
}
|
||||
|
||||
void ProcessLoop::add_trigger(EventSources::Trigger& trigger)
|
||||
{
|
||||
ASSERT(!_started || pthread_equal(pthread_self(), _thread));
|
||||
_event_sources.add_trigger(trigger);
|
||||
}
|
||||
|
||||
void ProcessLoop::remove_trigger(EventSources::Trigger& trigger)
|
||||
{
|
||||
ASSERT(!_started || pthread_equal(pthread_self(), _thread));
|
||||
_event_sources.remove_trigger(trigger);
|
||||
}
|
||||
|
||||
void ProcessLoop::add_socket(EventSources::Socket& socket)
|
||||
{
|
||||
ASSERT(!_started || pthread_equal(pthread_self(), _thread));
|
||||
_event_sources.add_socket(socket);
|
||||
}
|
||||
|
||||
void ProcessLoop::remove_socket(EventSources::Socket& socket)
|
||||
{
|
||||
ASSERT(!_started || pthread_equal(pthread_self(), _thread));
|
||||
_event_sources.remove_socket(socket);
|
||||
}
|
||||
|
||||
void ProcessLoop::add_file(EventSources::File& file)
|
||||
{
|
||||
ASSERT(!_started || pthread_equal(pthread_self(), _thread));
|
||||
_event_sources.add_file(file);
|
||||
}
|
||||
|
||||
void ProcessLoop::remove_file(EventSources::File& file)
|
||||
{
|
||||
ASSERT(!_started || pthread_equal(pthread_self(), _thread));
|
||||
_event_sources.remove_file(file);
|
||||
}
|
||||
|
||||
void ProcessLoop::add_handle(EventSources::Handle& handle)
|
||||
{
|
||||
ASSERT(!_started || pthread_equal(pthread_self(), _thread));
|
||||
_event_sources.add_handle(handle);
|
||||
}
|
||||
|
||||
void ProcessLoop::remove_handle(EventSources::Handle& handle)
|
||||
{
|
||||
ASSERT(!_started || pthread_equal(pthread_self(), _thread));
|
||||
_event_sources.remove_handle(handle);
|
||||
}
|
||||
|
||||
void ProcessLoop::push_event(Event* event)
|
||||
{
|
||||
int queue_size = _events_queue.push_event(event);
|
||||
if (queue_size == 1) { // queue was empty before the push
|
||||
wakeup();
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessLoop::activate_interval_timer(Timer* timer, unsigned int millisec)
|
||||
{
|
||||
_timers_queue.activate_interval_timer(timer, millisec);
|
||||
|
||||
if (_started && !pthread_equal(pthread_self(), _thread)) {
|
||||
wakeup();
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessLoop::deactivate_interval_timer(Timer* timer)
|
||||
{
|
||||
_timers_queue.deactivate_interval_timer(timer);
|
||||
}
|
||||
|
||||
unsigned ProcessLoop::get_soonest_timeout()
|
||||
{
|
||||
return _timers_queue.get_soonest_timeout();
|
||||
}
|
||||
|
||||
void ProcessLoop::timers_action()
|
||||
{
|
||||
_timers_queue.timers_action();
|
||||
}
|
||||
@ -1,244 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_PROCESS_LOOP
|
||||
#define _H_PROCESS_LOOP
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "event_sources.h"
|
||||
#include "threads.h"
|
||||
|
||||
class AbstractProcessLoop {
|
||||
public:
|
||||
virtual ~AbstractProcessLoop() {}
|
||||
virtual int run() = 0;
|
||||
virtual void do_quit(int error_code) = 0;
|
||||
virtual void* get_owner() = 0;
|
||||
virtual bool is_same_thread(pthread_t thread) = 0;
|
||||
};
|
||||
|
||||
class EventBase {
|
||||
public:
|
||||
EventBase() : _refs (1) {}
|
||||
|
||||
virtual void response(AbstractProcessLoop& events_loop) = 0;
|
||||
|
||||
EventBase* ref() { ++_refs; return this;}
|
||||
void unref() {if (--_refs == 0) delete this;}
|
||||
|
||||
protected:
|
||||
virtual ~EventBase() {}
|
||||
|
||||
private:
|
||||
AtomicCount _refs;
|
||||
};
|
||||
|
||||
class EventsQueue;
|
||||
|
||||
class Event: public EventBase {
|
||||
#ifdef RED_DEBUG
|
||||
public:
|
||||
Event() : _process_loop (NULL) {}
|
||||
|
||||
private:
|
||||
void set_process_loop(AbstractProcessLoop* process_loop) { _process_loop = process_loop;}
|
||||
|
||||
protected:
|
||||
AbstractProcessLoop* _process_loop;
|
||||
#endif
|
||||
|
||||
private:
|
||||
void set_generation(uint32_t gen) { _generation = gen;}
|
||||
uint32_t get_generation() { return _generation;}
|
||||
|
||||
private:
|
||||
uint32_t _generation;
|
||||
|
||||
friend class EventsQueue;
|
||||
};
|
||||
|
||||
class EventsQueue {
|
||||
public:
|
||||
EventsQueue(AbstractProcessLoop& owner);
|
||||
virtual ~EventsQueue();
|
||||
/* return the size of the queue (post-push) */
|
||||
int push_event(Event* event);
|
||||
void process_events();
|
||||
bool is_empty();
|
||||
|
||||
private:
|
||||
void clear_queue();
|
||||
|
||||
private:
|
||||
std::list<Event*> _events;
|
||||
Mutex _events_lock;
|
||||
uint32_t _events_gen;
|
||||
|
||||
AbstractProcessLoop& _owner;
|
||||
};
|
||||
|
||||
class SyncEvent: public Event {
|
||||
public:
|
||||
SyncEvent();
|
||||
|
||||
void wait();
|
||||
bool success() { return !_err;}
|
||||
|
||||
virtual void do_response(AbstractProcessLoop& events_loop) {}
|
||||
|
||||
protected:
|
||||
virtual ~SyncEvent();
|
||||
|
||||
private:
|
||||
virtual void response(AbstractProcessLoop& events_loop);
|
||||
|
||||
private:
|
||||
Mutex _mutex;
|
||||
Condition _condition;
|
||||
bool _err;
|
||||
bool _ready;
|
||||
};
|
||||
|
||||
class TimersQueue;
|
||||
|
||||
class Timer: public EventBase {
|
||||
public:
|
||||
Timer();
|
||||
bool is_armed() {return _is_armed;}
|
||||
|
||||
protected:
|
||||
virtual ~Timer();
|
||||
|
||||
private:
|
||||
void arm(uint32_t msec);
|
||||
void disarm();
|
||||
uint64_t get_expiration() const { return _expiration;}
|
||||
void calc_next_expiration_time() { _expiration += _interval;}
|
||||
void calc_next_expiration_time(uint64_t now);
|
||||
|
||||
static uint64_t get_now();
|
||||
|
||||
private:
|
||||
bool _is_armed;
|
||||
uint32_t _interval;
|
||||
uint64_t _expiration;
|
||||
|
||||
class Compare {
|
||||
public:
|
||||
bool operator () (const Timer* timer1, const Timer* timer2) const
|
||||
{
|
||||
if (timer1->get_expiration() < timer2->get_expiration()) {
|
||||
return true;
|
||||
} else if (timer1->get_expiration() > timer2->get_expiration()) {
|
||||
return false;
|
||||
} else { // elements must be unique (for insertion into set)
|
||||
return timer1 < timer2;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
friend class TimersQueue;
|
||||
};
|
||||
|
||||
class TimersQueue {
|
||||
public:
|
||||
TimersQueue(AbstractProcessLoop& owner);
|
||||
virtual ~TimersQueue();
|
||||
|
||||
void activate_interval_timer(Timer* timer, unsigned int millisec);
|
||||
void deactivate_interval_timer(Timer* timer);
|
||||
|
||||
unsigned int get_soonest_timeout();
|
||||
void timers_action();
|
||||
|
||||
private:
|
||||
void clear_queue();
|
||||
|
||||
private:
|
||||
typedef std::set<Timer*, Timer::Compare> TimersSet;
|
||||
TimersSet _armed_timers;
|
||||
RecurciveMutex _timers_lock;
|
||||
AbstractProcessLoop& _owner;
|
||||
};
|
||||
|
||||
class ProcessLoop: public AbstractProcessLoop {
|
||||
public:
|
||||
class QuitEvent;
|
||||
ProcessLoop(void* owner);
|
||||
virtual ~ProcessLoop();
|
||||
int run();
|
||||
|
||||
void quit(int error_code);
|
||||
|
||||
/* Event sources to track. Note: the following methods are not thread safe, thus,
|
||||
they mustn't be called from other thread than the process loop thread. */
|
||||
void add_trigger(EventSources::Trigger& trigger);
|
||||
void remove_trigger(EventSources::Trigger& trigger);
|
||||
void add_socket(EventSources::Socket& socket);
|
||||
void remove_socket(EventSources::Socket& socket);
|
||||
void add_file(EventSources::File& file);
|
||||
void remove_file(EventSources::File& file);
|
||||
void add_handle(EventSources::Handle& handle);
|
||||
void remove_handle(EventSources::Handle& handle);
|
||||
|
||||
/* events queue */
|
||||
void push_event(Event* event);
|
||||
|
||||
void activate_interval_timer(Timer* timer, unsigned int millisec);
|
||||
void deactivate_interval_timer(Timer* timer);
|
||||
|
||||
void process_events_queue();
|
||||
/* can be used for handling timers in modal loop state in Windows (mainly,
|
||||
for updating the screen) */
|
||||
unsigned int get_soonest_timeout();
|
||||
void timers_action();
|
||||
|
||||
void* get_owner() { return _owner;}
|
||||
|
||||
bool is_same_thread(pthread_t thread) { return _started && pthread_equal(_thread, thread);}
|
||||
|
||||
protected:
|
||||
class WakeupTrigger: public EventSources::Trigger {
|
||||
public:
|
||||
virtual void on_event() {}
|
||||
};
|
||||
|
||||
virtual void on_start_running() {}
|
||||
void wakeup();
|
||||
void do_quit(int error_code);
|
||||
|
||||
friend class QuitEvent; // allowing access to quit
|
||||
|
||||
private:
|
||||
EventSources _event_sources;
|
||||
EventsQueue _events_queue;
|
||||
TimersQueue _timers_queue;
|
||||
|
||||
WakeupTrigger _wakeup_trigger;
|
||||
|
||||
void* _owner;
|
||||
|
||||
bool _quitting;
|
||||
int _exit_code;
|
||||
bool _started;
|
||||
pthread_t _thread;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,119 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_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
|
||||
@ -1,293 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "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 *_message;}
|
||||
virtual void release();
|
||||
|
||||
private:
|
||||
RecordChannel& _channel;
|
||||
RedPeer::OutMessage *_message;
|
||||
};
|
||||
|
||||
RecordSamplesMessage::RecordSamplesMessage(RecordChannel& channel)
|
||||
: _channel (channel)
|
||||
, _message (new Message(SPICE_MSGC_RECORD_DATA))
|
||||
{
|
||||
}
|
||||
|
||||
RecordSamplesMessage::~RecordSamplesMessage()
|
||||
{
|
||||
delete _message;
|
||||
}
|
||||
|
||||
void RecordSamplesMessage::release()
|
||||
{
|
||||
_channel.release_message(this);
|
||||
}
|
||||
|
||||
class RecordHandler: public MessageHandlerImp<RecordChannel, SPICE_CHANNEL_RECORD> {
|
||||
public:
|
||||
RecordHandler(RecordChannel& channel)
|
||||
: MessageHandlerImp<RecordChannel, SPICE_CHANNEL_RECORD>(channel) {}
|
||||
};
|
||||
|
||||
RecordChannel::RecordChannel(RedClient& client, uint32_t id)
|
||||
: RedChannel(client, SPICE_CHANNEL_RECORD, id, new RecordHandler(*this))
|
||||
, _wave_recorder (NULL)
|
||||
, _mode (SPICE_AUDIO_DATA_MODE_INVALID)
|
||||
, _codec(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(SPICE_MSG_MIGRATE, &RecordChannel::handle_migrate);
|
||||
handler->set_handler(SPICE_MSG_SET_ACK, &RecordChannel::handle_set_ack);
|
||||
handler->set_handler(SPICE_MSG_PING, &RecordChannel::handle_ping);
|
||||
handler->set_handler(SPICE_MSG_WAIT_FOR_CHANNELS, &RecordChannel::handle_wait_for_channels);
|
||||
handler->set_handler(SPICE_MSG_DISCONNECTING, &RecordChannel::handle_disconnect);
|
||||
handler->set_handler(SPICE_MSG_NOTIFY, &RecordChannel::handle_notify);
|
||||
|
||||
handler->set_handler(SPICE_MSG_RECORD_START, &RecordChannel::handle_start);
|
||||
|
||||
if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1, SND_CODEC_ANY_FREQUENCY))
|
||||
set_capability(SPICE_RECORD_CAP_CELT_0_5_1);
|
||||
if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, SND_CODEC_ANY_FREQUENCY))
|
||||
set_capability(SPICE_RECORD_CAP_OPUS);
|
||||
}
|
||||
|
||||
RecordChannel::~RecordChannel(void)
|
||||
{
|
||||
while (!_messages.empty()) {
|
||||
RecordSamplesMessage *mes;
|
||||
mes = *_messages.begin();
|
||||
_messages.pop_front();
|
||||
delete mes;
|
||||
}
|
||||
clear();
|
||||
}
|
||||
|
||||
bool RecordChannel::abort(void)
|
||||
{
|
||||
return (!_wave_recorder || _wave_recorder->abort()) && RedChannel::abort();
|
||||
}
|
||||
|
||||
void RecordChannel::set_desired_mode(int frequency)
|
||||
{
|
||||
if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, frequency) &&
|
||||
test_capability(SPICE_RECORD_CAP_OPUS))
|
||||
_mode = SPICE_AUDIO_DATA_MODE_OPUS;
|
||||
else if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1, frequency) &&
|
||||
test_capability(SPICE_RECORD_CAP_CELT_0_5_1))
|
||||
_mode = SPICE_AUDIO_DATA_MODE_CELT_0_5_1;
|
||||
else
|
||||
_mode = SPICE_AUDIO_DATA_MODE_RAW;
|
||||
}
|
||||
|
||||
void RecordChannel::send_record_mode()
|
||||
{
|
||||
Message* message = new Message(SPICE_MSGC_RECORD_MODE);
|
||||
SpiceMsgcRecordMode mode;
|
||||
mode.time = get_mm_time();
|
||||
mode.mode = _mode;
|
||||
_marshallers->msgc_record_mode(message->marshaller(), &mode);
|
||||
post_message(message);
|
||||
}
|
||||
|
||||
void RecordChannel::on_disconnect()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void RecordChannel::send_start_mark()
|
||||
{
|
||||
Message* message = new Message(SPICE_MSGC_RECORD_START_MARK);
|
||||
SpiceMsgcRecordStartMark start_mark;
|
||||
start_mark.time = get_mm_time();
|
||||
_marshallers->msgc_record_start_mark(message->marshaller(), &start_mark);
|
||||
post_message(message);
|
||||
}
|
||||
|
||||
void RecordChannel::handle_start(RedPeer::InMessage* message)
|
||||
{
|
||||
RecordHandler* handler = static_cast<RecordHandler*>(get_message_handler());
|
||||
SpiceMsgRecordStart* start = (SpiceMsgRecordStart*)message->data();
|
||||
|
||||
handler->set_handler(SPICE_MSG_RECORD_START, NULL);
|
||||
handler->set_handler(SPICE_MSG_RECORD_STOP, &RecordChannel::handle_stop);
|
||||
ASSERT(!_wave_recorder);
|
||||
|
||||
// for now support only one setting
|
||||
if (start->format != SPICE_AUDIO_FMT_S16) {
|
||||
THROW("unexpected format");
|
||||
}
|
||||
if (start->channels != 2) {
|
||||
THROW("unexpected number of channels");
|
||||
}
|
||||
|
||||
set_desired_mode(start->frequency);
|
||||
|
||||
int frame_size = SND_CODEC_MAX_FRAME_SIZE;
|
||||
if (_mode != SPICE_AUDIO_DATA_MODE_RAW) {
|
||||
if (snd_codec_create(&_codec, _mode, start->frequency, SND_CODEC_ENCODE) != SND_CODEC_OK)
|
||||
THROW("create encoder failed");
|
||||
frame_size = snd_codec_frame_size(_codec);
|
||||
}
|
||||
|
||||
int bits_per_sample = 16;
|
||||
try {
|
||||
_wave_recorder = Platform::create_recorder(*this, start->frequency,
|
||||
bits_per_sample,
|
||||
start->channels,
|
||||
frame_size);
|
||||
} catch (...) {
|
||||
LOG_WARN("create recorder failed");
|
||||
return;
|
||||
}
|
||||
|
||||
_frame_bytes = frame_size * bits_per_sample * start->channels / 8;
|
||||
|
||||
send_record_mode();
|
||||
send_start_mark();
|
||||
_wave_recorder->start();
|
||||
}
|
||||
|
||||
void RecordChannel::clear()
|
||||
{
|
||||
if (_wave_recorder) {
|
||||
_wave_recorder->stop();
|
||||
delete _wave_recorder;
|
||||
_wave_recorder = NULL;
|
||||
}
|
||||
snd_codec_destroy(&_codec);
|
||||
}
|
||||
|
||||
void RecordChannel::handle_stop(RedPeer::InMessage* message)
|
||||
{
|
||||
RecordHandler* handler = static_cast<RecordHandler*>(get_message_handler());
|
||||
handler->set_handler(SPICE_MSG_RECORD_START, &RecordChannel::handle_start);
|
||||
handler->set_handler(SPICE_MSG_RECORD_STOP, NULL);
|
||||
if (!_wave_recorder) {
|
||||
return;
|
||||
}
|
||||
clear();
|
||||
}
|
||||
|
||||
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_event_source(EventSources::File& event_source)
|
||||
{
|
||||
get_process_loop().add_file(event_source);
|
||||
}
|
||||
|
||||
void RecordChannel::remove_event_source(EventSources::File& event_source)
|
||||
{
|
||||
get_process_loop().remove_file(event_source);
|
||||
}
|
||||
|
||||
void RecordChannel::add_event_source(EventSources::Trigger& event_source)
|
||||
{
|
||||
get_process_loop().add_trigger(event_source);
|
||||
}
|
||||
|
||||
void RecordChannel::remove_event_source(EventSources::Trigger& event_source)
|
||||
{
|
||||
get_process_loop().remove_trigger(event_source);
|
||||
}
|
||||
|
||||
void RecordChannel::push_frame(uint8_t *frame)
|
||||
{
|
||||
RecordSamplesMessage *message;
|
||||
if (!(message = get_message())) {
|
||||
DBG(0, "blocked");
|
||||
return;
|
||||
}
|
||||
int n;
|
||||
|
||||
|
||||
if (_mode == SPICE_AUDIO_DATA_MODE_RAW) {
|
||||
n = _frame_bytes;
|
||||
} else {
|
||||
n = sizeof(compressed_buf);
|
||||
if (snd_codec_encode(_codec, frame, _frame_bytes, compressed_buf, &n) != SND_CODEC_OK)
|
||||
THROW("encode failed");
|
||||
frame = compressed_buf;
|
||||
}
|
||||
|
||||
RedPeer::OutMessage& peer_message = message->peer_message();
|
||||
peer_message.reset(SPICE_MSGC_RECORD_DATA);
|
||||
SpiceMsgcRecordPacket packet;
|
||||
packet.time = get_mm_time();
|
||||
_marshallers->msgc_record_data(peer_message.marshaller(), &packet);
|
||||
spice_marshaller_add(peer_message.marshaller(), frame, n);
|
||||
post_message(message);
|
||||
}
|
||||
|
||||
class RecordFactory: public ChannelFactory {
|
||||
public:
|
||||
RecordFactory() : ChannelFactory(SPICE_CHANNEL_RECORD) {}
|
||||
virtual RedChannel* construct(RedClient& client, uint32_t id)
|
||||
{
|
||||
return new RecordChannel(client, id);
|
||||
}
|
||||
};
|
||||
|
||||
static RecordFactory factory;
|
||||
|
||||
ChannelFactory& RecordChannel::Factory()
|
||||
{
|
||||
return factory;
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2011 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef _H_RED_CANVAS_BASE
|
||||
#define _H_RED_CANVAS_BASE
|
||||
|
||||
#define SPICE_CANVAS_INTERNAL
|
||||
#define SW_CANVAS_CACHE
|
||||
#include "common/canvas_base.h"
|
||||
#undef SW_CANVAS_CACHE
|
||||
#undef SPICE_CANVAS_INTERNAL
|
||||
|
||||
#endif
|
||||
@ -1,887 +0,0 @@
|
||||
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "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"
|
||||
|
||||
void MigrationDisconnectSrcEvent::response(AbstractProcessLoop& events_loop)
|
||||
{
|
||||
static_cast<RedChannel*>(events_loop.get_owner())->do_migration_disconnect_src();
|
||||
}
|
||||
|
||||
void MigrationConnectTargetEvent::response(AbstractProcessLoop& events_loop)
|
||||
{
|
||||
static_cast<RedChannel*>(events_loop.get_owner())->do_migration_connect_target();
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
}
|
||||
|
||||
static const char *spice_link_error_string(int err)
|
||||
{
|
||||
switch (err) {
|
||||
case SPICE_LINK_ERR_OK: return "no error";
|
||||
case SPICE_LINK_ERR_ERROR: return "general error";
|
||||
case SPICE_LINK_ERR_INVALID_MAGIC: return "invalid magic";
|
||||
case SPICE_LINK_ERR_INVALID_DATA: return "invalid data";
|
||||
case SPICE_LINK_ERR_VERSION_MISMATCH: return "version mismatch";
|
||||
case SPICE_LINK_ERR_NEED_SECURED: return "need secured connection";
|
||||
case SPICE_LINK_ERR_NEED_UNSECURED: return "need unsecured connection";
|
||||
case SPICE_LINK_ERR_PERMISSION_DENIED: return "permission denied";
|
||||
case SPICE_LINK_ERR_BAD_CONNECTION_ID: return "bad connection id";
|
||||
case SPICE_LINK_ERR_CHANNEL_NOT_AVAILABLE: return "channel not available";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void RedChannelBase::link(uint32_t connection_id, const std::string& password,
|
||||
int protocol)
|
||||
{
|
||||
SpiceLinkHeader header;
|
||||
SpiceLinkMess link_mess;
|
||||
SpiceLinkReply* reply;
|
||||
uint32_t link_res;
|
||||
uint32_t i;
|
||||
|
||||
BIO *bioKey;
|
||||
uint8_t *buffer, *p;
|
||||
uint32_t expected_major;
|
||||
|
||||
header.magic = SPICE_MAGIC;
|
||||
header.size = sizeof(link_mess);
|
||||
if (protocol == 1) {
|
||||
/* protocol 1 == major 1, old 0.4 protocol, last active minor */
|
||||
expected_major = header.major_version = 1;
|
||||
header.minor_version = 3;
|
||||
} else if (protocol == 2) {
|
||||
/* protocol 2 == current */
|
||||
expected_major = header.major_version = SPICE_VERSION_MAJOR;
|
||||
header.minor_version = SPICE_VERSION_MINOR;
|
||||
} else {
|
||||
THROW("unsupported protocol version specified");
|
||||
}
|
||||
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);
|
||||
|
||||
buffer =
|
||||
new uint8_t[sizeof(header) + sizeof(link_mess) +
|
||||
_common_caps.size() * sizeof(uint32_t) +
|
||||
_caps.size() * sizeof(uint32_t)];
|
||||
p = buffer;
|
||||
|
||||
memcpy(p, (uint8_t*)&header, sizeof(header));
|
||||
p += sizeof(header);
|
||||
memcpy(p, (uint8_t*)&link_mess, sizeof(link_mess));
|
||||
p += sizeof(link_mess);
|
||||
for (i = 0; i < _common_caps.size(); i++) {
|
||||
*(uint32_t *)p = _common_caps[i];
|
||||
p += sizeof(uint32_t);
|
||||
}
|
||||
|
||||
for (i = 0; i < _caps.size(); i++) {
|
||||
*(uint32_t *)p = _caps[i];
|
||||
p += sizeof(uint32_t);
|
||||
}
|
||||
|
||||
send(buffer, p - buffer);
|
||||
delete [] buffer;
|
||||
|
||||
receive((uint8_t*)&header, sizeof(header));
|
||||
|
||||
if (header.magic != SPICE_MAGIC) {
|
||||
THROW_ERR(SPICEC_ERROR_CODE_CONNECT_FAILED, "bad magic");
|
||||
}
|
||||
|
||||
if (header.major_version != expected_major) {
|
||||
THROW_ERR(SPICEC_ERROR_CODE_VERSION_MISMATCH,
|
||||
"version mismatch: expect %u got %u",
|
||||
expected_major,
|
||||
header.major_version);
|
||||
}
|
||||
|
||||
_remote_major = header.major_version;
|
||||
_remote_minor = header.minor_version;
|
||||
|
||||
AutoArray<uint8_t> reply_buf(new uint8_t[header.size]);
|
||||
receive(reply_buf.get(), header.size);
|
||||
|
||||
reply = (SpiceLinkReply *)reply_buf.get();
|
||||
|
||||
if (reply->error != SPICE_LINK_ERR_OK) {
|
||||
THROW_ERR(SPICEC_ERROR_CODE_CONNECT_FAILED, "connect error %u - %s",
|
||||
reply->error, spice_link_error_string(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) {
|
||||
EVP_PKEY *pubkey;
|
||||
int nRSASize;
|
||||
RSA *rsa;
|
||||
|
||||
BIO_write(bioKey, reply->pub_key, SPICE_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 {
|
||||
EVP_PKEY_free(pubkey);
|
||||
BIO_free(bioKey);
|
||||
THROW("could not encrypt password");
|
||||
}
|
||||
|
||||
memset(bufEncrypted.get(), 0, nRSASize);
|
||||
EVP_PKEY_free(pubkey);
|
||||
} else {
|
||||
THROW("Could not initiate BIO");
|
||||
}
|
||||
|
||||
BIO_free(bioKey);
|
||||
|
||||
receive((uint8_t*)&link_res, sizeof(link_res));
|
||||
if (link_res != SPICE_LINK_ERR_OK) {
|
||||
int error_code = (link_res == SPICE_LINK_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,
|
||||
const char* host, std::string password)
|
||||
{
|
||||
int protocol = options.protocol;
|
||||
|
||||
if (protocol == 0) { /* AUTO, try major 2 first */
|
||||
protocol = 2;
|
||||
}
|
||||
|
||||
retry:
|
||||
try {
|
||||
if (options.allow_unsecure()) {
|
||||
try {
|
||||
RedPeer::connect_unsecure(host, options.unsecure_port);
|
||||
link(connection_id, password, protocol);
|
||||
return;
|
||||
} catch (Exception& e) {
|
||||
// On protocol version mismatch, don't connect_secure with the same version
|
||||
if (e.get_error_code() == SPICEC_ERROR_CODE_VERSION_MISMATCH ||
|
||||
!options.allow_secure()) {
|
||||
throw;
|
||||
}
|
||||
RedPeer::close();
|
||||
}
|
||||
}
|
||||
ASSERT(options.allow_secure());
|
||||
RedPeer::connect_secure(options, host);
|
||||
link(connection_id, password, protocol);
|
||||
} catch (Exception& e) {
|
||||
// On protocol version mismatch, retry with older version
|
||||
if (e.get_error_code() == SPICEC_ERROR_CODE_VERSION_MISMATCH &&
|
||||
protocol == 2 && options.protocol == 0) {
|
||||
RedPeer::cleanup();
|
||||
protocol = 1;
|
||||
goto retry;
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void RedChannelBase::swap(RedChannelBase* other)
|
||||
{
|
||||
int tmp_ver;
|
||||
|
||||
RedPeer::swap(other);
|
||||
tmp_ver = _remote_major;
|
||||
_remote_major = other->_remote_major;
|
||||
other->_remote_major = tmp_ver;
|
||||
|
||||
tmp_ver = _remote_minor;
|
||||
_remote_minor = other->_remote_minor;
|
||||
other->_remote_minor = tmp_ver;
|
||||
}
|
||||
|
||||
SendTrigger::SendTrigger(RedChannel& channel)
|
||||
: _channel (channel)
|
||||
{
|
||||
}
|
||||
|
||||
void SendTrigger::on_event()
|
||||
{
|
||||
_channel.on_send_trigger();
|
||||
}
|
||||
|
||||
SPICE_GNUC_NORETURN 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())
|
||||
, _marshallers (NULL)
|
||||
, _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)
|
||||
, _loop (this)
|
||||
, _send_trigger (*this)
|
||||
, _disconnect_stamp (0)
|
||||
, _disconnect_reason (SPICE_LINK_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("unhandled exception: %s", e.what());
|
||||
} catch (std::exception& e) {
|
||||
LOG_ERROR("unhandled 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::CompoundInMessage *RedChannel::receive()
|
||||
{
|
||||
CompoundInMessage *message = RedChannelBase::receive();
|
||||
on_message_received();
|
||||
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::disconnect_migration_src()
|
||||
{
|
||||
clear_outgoing_messages();
|
||||
|
||||
Lock lock(_action_lock);
|
||||
if (_state == CONNECTING_STATE || _state == CONNECTED_STATE) {
|
||||
AutoRef<MigrationDisconnectSrcEvent> migrate_event(new MigrationDisconnectSrcEvent());
|
||||
_loop.push_event(*migrate_event);
|
||||
}
|
||||
}
|
||||
|
||||
void RedChannel::connect_migration_target()
|
||||
{
|
||||
LOG_INFO("");
|
||||
AutoRef<MigrationConnectTargetEvent> migrate_event(new MigrationConnectTargetEvent());
|
||||
_loop.push_event(*migrate_event);
|
||||
}
|
||||
|
||||
void RedChannel::do_migration_disconnect_src()
|
||||
{
|
||||
if (_socket_in_loop) {
|
||||
_socket_in_loop = false;
|
||||
_loop.remove_socket(*this);
|
||||
}
|
||||
|
||||
clear_outgoing_messages();
|
||||
if (_outgoing_message) {
|
||||
_outgoing_message->release();
|
||||
_outgoing_message = NULL;
|
||||
}
|
||||
_incomming_header_pos = 0;
|
||||
if (_incomming_message) {
|
||||
_incomming_message->unref();
|
||||
_incomming_message = NULL;
|
||||
}
|
||||
|
||||
on_disconnect_mig_src();
|
||||
get_client().migrate_channel(*this);
|
||||
get_client().on_channel_disconnect_mig_src_completed(*this);
|
||||
}
|
||||
|
||||
void RedChannel::do_migration_connect_target()
|
||||
{
|
||||
LOG_INFO("");
|
||||
ASSERT(get_client().get_protocol() != 0);
|
||||
if (get_client().get_protocol() == 1) {
|
||||
_marshallers = spice_message_marshallers_get1();
|
||||
} else {
|
||||
_marshallers = spice_message_marshallers_get();
|
||||
}
|
||||
_loop.add_socket(*this);
|
||||
_socket_in_loop = true;
|
||||
on_connect_mig_target();
|
||||
set_state(CONNECTED_STATE);
|
||||
on_event();
|
||||
}
|
||||
|
||||
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(),
|
||||
_client.get_protocol(),
|
||||
_client.get_host_auth_options(),
|
||||
_client.get_connection_ciphers());
|
||||
RedChannelBase::connect(con_options, _client.get_connection_id(),
|
||||
_client.get_host().c_str(),
|
||||
_client.get_password().c_str());
|
||||
/* If automatic protocol, remember the first connect protocol type */
|
||||
if (_client.get_protocol() == 0) {
|
||||
if (get_peer_major() == 1) {
|
||||
_client.set_protocol(1);
|
||||
} else {
|
||||
/* Major is 2 or unstable high value, use 2 */
|
||||
_client.set_protocol(2);
|
||||
}
|
||||
}
|
||||
/* Initialize when we know the remote major version */
|
||||
if (_client.get_peer_major() == 1) {
|
||||
_marshallers = spice_message_marshallers_get1();
|
||||
} else {
|
||||
_marshallers = spice_message_marshallers_get();
|
||||
}
|
||||
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;
|
||||
if (_incomming_message) {
|
||||
_incomming_message->unref();
|
||||
_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_received()
|
||||
{
|
||||
if (_message_ack_count && !--_message_ack_count) {
|
||||
post_message(new Message(SPICE_MSGC_ACK));
|
||||
_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::receive_messages()
|
||||
{
|
||||
for (;;) {
|
||||
uint32_t n = RedPeer::receive((uint8_t*)&_incomming_header, sizeof(SpiceDataHeader));
|
||||
if (n != sizeof(SpiceDataHeader)) {
|
||||
_incomming_header_pos = n;
|
||||
return;
|
||||
}
|
||||
AutoRef<CompoundInMessage> message(new CompoundInMessage(_incomming_header.serial,
|
||||
_incomming_header.type,
|
||||
_incomming_header.size,
|
||||
_incomming_header.sub_list));
|
||||
n = RedPeer::receive((*message)->data(), (*message)->compound_size());
|
||||
if (n != (*message)->compound_size()) {
|
||||
_incomming_message = message.release();
|
||||
_incomming_message_pos = n;
|
||||
return;
|
||||
}
|
||||
on_message_received();
|
||||
_message_handler->handle_message(*(*message));
|
||||
on_message_complition((*message)->serial());
|
||||
}
|
||||
}
|
||||
|
||||
void RedChannel::on_event()
|
||||
{
|
||||
if (_outgoing_message) {
|
||||
RedPeer::OutMessage& peer_message = _outgoing_message->peer_message();
|
||||
|
||||
_outgoing_pos += do_send(peer_message, _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::receive(((uint8_t*)&_incomming_header) +
|
||||
_incomming_header_pos,
|
||||
sizeof(SpiceDataHeader) - _incomming_header_pos);
|
||||
if (_incomming_header_pos != sizeof(SpiceDataHeader)) {
|
||||
return;
|
||||
}
|
||||
_incomming_header_pos = 0;
|
||||
_incomming_message = new CompoundInMessage(_incomming_header.serial,
|
||||
_incomming_header.type,
|
||||
_incomming_header.size,
|
||||
_incomming_header.sub_list);
|
||||
_incomming_message_pos = 0;
|
||||
}
|
||||
|
||||
if (_incomming_message) {
|
||||
_incomming_message_pos += RedPeer::receive(_incomming_message->data() +
|
||||
_incomming_message_pos,
|
||||
_incomming_message->compound_size() -
|
||||
_incomming_message_pos);
|
||||
if (_incomming_message_pos != _incomming_message->compound_size()) {
|
||||
return;
|
||||
}
|
||||
AutoRef<CompoundInMessage> message(_incomming_message);
|
||||
_incomming_message = NULL;
|
||||
on_message_received();
|
||||
_message_handler->handle_message(*(*message));
|
||||
on_message_complition((*message)->serial());
|
||||
}
|
||||
receive_messages();
|
||||
}
|
||||
|
||||
void RedChannel::send_migrate_flush_mark()
|
||||
{
|
||||
if (_outgoing_message) {
|
||||
RedPeer::OutMessage& peer_message = _outgoing_message->peer_message();
|
||||
do_send(peer_message, _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(SPICE_MSGC_MIGRATE_FLUSH_MARK));
|
||||
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);
|
||||
SpiceMsgMigrate* migrate = (SpiceMsgMigrate*)message->data();
|
||||
if (migrate->flags & SPICE_MIGRATE_NEED_FLUSH) {
|
||||
send_migrate_flush_mark();
|
||||
}
|
||||
AutoRef<CompoundInMessage> data_message;
|
||||
if (migrate->flags & SPICE_MIGRATE_NEED_DATA_TRANSFER) {
|
||||
data_message.reset(receive());
|
||||
}
|
||||
_client.migrate_channel(*this);
|
||||
if (migrate->flags & SPICE_MIGRATE_NEED_DATA_TRANSFER) {
|
||||
if ((*data_message)->type() != SPICE_MSG_MIGRATE_DATA) {
|
||||
THROW("expect SPICE_MSG_MIGRATE_DATA");
|
||||
}
|
||||
std::auto_ptr<RedPeer::OutMessage> message(new RedPeer::OutMessage(SPICE_MSGC_MIGRATE_DATA));
|
||||
spice_marshaller_add(message->marshaller(), (*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)
|
||||
{
|
||||
SpiceMsgSetAck* ack = (SpiceMsgSetAck*)message->data();
|
||||
_message_ack_window = _message_ack_count = ack->window;
|
||||
Message *response = new Message(SPICE_MSGC_ACK_SYNC);
|
||||
SpiceMsgcAckSync sync;
|
||||
sync.generation = ack->generation;
|
||||
_marshallers->msgc_ack_sync(response->marshaller(), &sync);
|
||||
post_message(response);
|
||||
}
|
||||
|
||||
void RedChannel::handle_ping(RedPeer::InMessage* message)
|
||||
{
|
||||
SpiceMsgPing *ping = (SpiceMsgPing *)message->data();
|
||||
Message *pong = new Message(SPICE_MSGC_PONG);
|
||||
_marshallers->msgc_pong(pong->marshaller(), ping);
|
||||
post_message(pong);
|
||||
}
|
||||
|
||||
void RedChannel::handle_disconnect(RedPeer::InMessage* message)
|
||||
{
|
||||
SpiceMsgDisconnect *disconnect = (SpiceMsgDisconnect *)message->data();
|
||||
_disconnect_stamp = disconnect->time_stamp;
|
||||
_disconnect_reason = disconnect->reason;
|
||||
}
|
||||
|
||||
void RedChannel::handle_notify(RedPeer::InMessage* message)
|
||||
{
|
||||
SpiceMsgNotify *notify = (SpiceMsgNotify *)message->data();
|
||||
const char *severity;
|
||||
const char *visibility;
|
||||
char *message_str = (char *)"";
|
||||
const char *message_prefix = "";
|
||||
|
||||
static const char* severity_strings[] = {"info", "warn", "error"};
|
||||
static const char* visibility_strings[] = {"!", "!!", "!!!"};
|
||||
|
||||
|
||||
if (notify->severity > SPICE_NOTIFY_SEVERITY_ERROR) {
|
||||
THROW("bad severity");
|
||||
}
|
||||
severity = severity_strings[notify->severity];
|
||||
|
||||
if (notify->visibilty > SPICE_NOTIFY_VISIBILITY_HIGH) {
|
||||
THROW("bad visibility");
|
||||
}
|
||||
visibility = visibility_strings[notify->visibilty];
|
||||
|
||||
|
||||
if (notify->message_len) {
|
||||
if ((message->size() - sizeof(*notify) < notify->message_len)) {
|
||||
THROW("access violation");
|
||||
}
|
||||
message_str = new char[notify->message_len + 1];
|
||||
memcpy(message_str, notify->message, notify->message_len);
|
||||
message_str[notify->message_len] = 0;
|
||||
message_prefix = ": ";
|
||||
}
|
||||
|
||||
|
||||
LOG_INFO("remote channel %u:%u %s%s #%u%s%s",
|
||||
get_type(), get_id(),
|
||||
severity, visibility,
|
||||
notify->what,
|
||||
message_prefix, message_str);
|
||||
if (notify->message_len) {
|
||||
delete [] message_str;
|
||||
}
|
||||
}
|
||||
|
||||
void RedChannel::handle_wait_for_channels(RedPeer::InMessage* message)
|
||||
{
|
||||
SpiceMsgWaitForChannels *wait = (SpiceMsgWaitForChannels *)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);
|
||||
}
|
||||
@ -1,353 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_REDCHANNEL
|
||||
#define _H_REDCHANNEL
|
||||
|
||||
#include "common/client_demarshallers.h"
|
||||
#include "common/client_marshallers.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "utils.h"
|
||||
#include "threads.h"
|
||||
#include "red_peer.h"
|
||||
#include "platform.h"
|
||||
#include "process_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, const char *host,
|
||||
std::string password);
|
||||
|
||||
const ChannelCaps& get_common_caps() { return _common_caps;}
|
||||
const ChannelCaps& get_caps() {return _caps;}
|
||||
|
||||
uint32_t get_peer_major() { return _remote_major;}
|
||||
uint32_t get_peer_minor() { return _remote_minor;}
|
||||
|
||||
virtual void swap(RedChannelBase* other);
|
||||
|
||||
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, int protocol);
|
||||
|
||||
private:
|
||||
uint8_t _type;
|
||||
uint8_t _id;
|
||||
|
||||
ChannelCaps _common_caps;
|
||||
ChannelCaps _caps;
|
||||
|
||||
ChannelCaps _remote_common_caps;
|
||||
ChannelCaps _remote_caps;
|
||||
|
||||
uint32_t _remote_major;
|
||||
uint32_t _remote_minor;
|
||||
};
|
||||
|
||||
class SendTrigger: public EventSources::Trigger {
|
||||
public:
|
||||
SendTrigger(RedChannel& channel);
|
||||
|
||||
virtual void on_event();
|
||||
|
||||
private:
|
||||
RedChannel& _channel;
|
||||
};
|
||||
|
||||
class AbortTrigger: public EventSources::Trigger {
|
||||
public:
|
||||
virtual void on_event();
|
||||
};
|
||||
|
||||
class MigrationDisconnectSrcEvent: public Event {
|
||||
public:
|
||||
virtual void response(AbstractProcessLoop& events_loop);
|
||||
};
|
||||
|
||||
class MigrationConnectTargetEvent: public Event {
|
||||
public:
|
||||
virtual void response(AbstractProcessLoop& events_loop);
|
||||
};
|
||||
|
||||
struct SyncInfo {
|
||||
Mutex* lock;
|
||||
Condition* condition;
|
||||
uint64_t* message_serial;
|
||||
};
|
||||
|
||||
class RedChannel: public RedChannelBase {
|
||||
public:
|
||||
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 void disconnect_migration_src();
|
||||
virtual void connect_migration_target();
|
||||
|
||||
virtual CompoundInMessage *receive();
|
||||
|
||||
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;}
|
||||
ProcessLoop& get_process_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() {}
|
||||
virtual void on_disconnect_mig_src() { on_disconnect();}
|
||||
virtual void on_connect_mig_target() { on_connect();}
|
||||
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);
|
||||
|
||||
SpiceMessageMarshallers *_marshallers;
|
||||
|
||||
private:
|
||||
void set_state(int state);
|
||||
void run();
|
||||
void send_migrate_flush_mark();
|
||||
void send_messages();
|
||||
void receive_messages();
|
||||
void on_send_trigger();
|
||||
virtual void on_event();
|
||||
void on_message_received();
|
||||
void on_message_complition(uint64_t serial);
|
||||
void do_migration_disconnect_src();
|
||||
void do_migration_connect_target();
|
||||
|
||||
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;
|
||||
|
||||
SpiceDataHeader _incomming_header;
|
||||
uint32_t _incomming_header_pos;
|
||||
RedPeer::CompoundInMessage* _incomming_message;
|
||||
uint32_t _incomming_message_pos;
|
||||
|
||||
uint32_t _message_ack_count;
|
||||
uint32_t _message_ack_window;
|
||||
|
||||
ProcessLoop _loop;
|
||||
SendTrigger _send_trigger;
|
||||
AbortTrigger _abort_trigger;
|
||||
|
||||
uint64_t _disconnect_stamp;
|
||||
uint64_t _disconnect_reason;
|
||||
|
||||
friend class SendTrigger;
|
||||
friend class MigrationDisconnectSrcEvent;
|
||||
friend class MigrationConnectTargetEvent;
|
||||
};
|
||||
|
||||
|
||||
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)
|
||||
: RedChannel::OutMessage()
|
||||
, RedPeer::OutMessage(type)
|
||||
{
|
||||
}
|
||||
|
||||
virtual RedPeer::OutMessage& peer_message() { return *this;}
|
||||
virtual void release() {delete this;}
|
||||
};
|
||||
|
||||
|
||||
class RedChannel::MessageHandler {
|
||||
public:
|
||||
MessageHandler() {}
|
||||
virtual ~MessageHandler() {}
|
||||
virtual void handle_message(RedPeer::CompoundInMessage& message) = 0;
|
||||
};
|
||||
|
||||
|
||||
template <class HandlerClass, unsigned int channel_id>
|
||||
class MessageHandlerImp: public RedChannel::MessageHandler {
|
||||
public:
|
||||
MessageHandlerImp(HandlerClass& obj);
|
||||
~MessageHandlerImp() { delete [] _handlers; };
|
||||
virtual void handle_message(RedPeer::CompoundInMessage& message);
|
||||
typedef void (HandlerClass::*Handler)(RedPeer::InMessage* message);
|
||||
void set_handler(unsigned int id, Handler handler);
|
||||
|
||||
private:
|
||||
HandlerClass& _obj;
|
||||
unsigned int _max_messages;
|
||||
spice_parse_channel_func_t _parser;
|
||||
Handler *_handlers;
|
||||
};
|
||||
|
||||
template <class HandlerClass, unsigned int channel_id>
|
||||
MessageHandlerImp<HandlerClass, channel_id>::MessageHandlerImp(HandlerClass& obj)
|
||||
: _obj (obj)
|
||||
, _parser (NULL)
|
||||
{
|
||||
/* max_messages is always from current as its larger than for backwards compat */
|
||||
spice_get_server_channel_parser(channel_id, &_max_messages);
|
||||
_handlers = new Handler[_max_messages + 1];
|
||||
memset(_handlers, 0, sizeof(Handler) * (_max_messages + 1));
|
||||
}
|
||||
|
||||
template <class HandlerClass, unsigned int channel_id>
|
||||
void MessageHandlerImp<HandlerClass, channel_id>::handle_message(RedPeer::CompoundInMessage&
|
||||
message)
|
||||
{
|
||||
uint8_t *msg;
|
||||
uint8_t *parsed;
|
||||
uint16_t type;
|
||||
uint32_t size;
|
||||
size_t parsed_size;
|
||||
message_destructor_t parsed_free;
|
||||
|
||||
if (_parser == NULL) {
|
||||
/* We need to do this lazily rather than at constuction because we
|
||||
don't know the major until we've connected */
|
||||
if (_obj.get_peer_major() == 1) {
|
||||
_parser = spice_get_server_channel_parser1(channel_id, NULL);
|
||||
} else {
|
||||
_parser = spice_get_server_channel_parser(channel_id, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (message.sub_list()) {
|
||||
SpiceSubMessageList *sub_list;
|
||||
sub_list = (SpiceSubMessageList *)(message.data() + message.sub_list());
|
||||
for (int i = 0; i < sub_list->size; i++) {
|
||||
SpiceSubMessage *sub = (SpiceSubMessage *)(message.data() + sub_list->sub_messages[i]);
|
||||
msg = (uint8_t *)(sub + 1);
|
||||
type = sub->type;
|
||||
size = sub->size;
|
||||
parsed = _parser(msg, msg + size, type, _obj.get_peer_minor(), &parsed_size, &parsed_free);
|
||||
|
||||
if (parsed == NULL) {
|
||||
THROW("failed to parse message type %d", type);
|
||||
}
|
||||
|
||||
RedPeer::InMessage sub_message(type, parsed_size, parsed);
|
||||
(_obj.*_handlers[type])(&sub_message);
|
||||
|
||||
parsed_free(parsed);
|
||||
}
|
||||
}
|
||||
|
||||
msg = message.data();
|
||||
type = message.type();
|
||||
size = message.size();
|
||||
parsed = _parser(msg, msg + size, type, _obj.get_peer_minor(), &parsed_size, &parsed_free);
|
||||
|
||||
if (parsed == NULL) {
|
||||
THROW("failed to parse message channel %d type %d", channel_id, type);
|
||||
}
|
||||
|
||||
RedPeer::InMessage main_message(type, parsed_size, parsed);
|
||||
(_obj.*_handlers[type])(&main_message);
|
||||
parsed_free(parsed);
|
||||
}
|
||||
|
||||
template <class HandlerClass, unsigned int channel_id>
|
||||
void MessageHandlerImp<HandlerClass, channel_id>::set_handler(unsigned int id, Handler handler)
|
||||
{
|
||||
if (id > _max_messages) {
|
||||
THROW("bad handler id");
|
||||
}
|
||||
_handlers[id] = handler;
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,387 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_REDCLIENT
|
||||
#define _H_REDCLIENT
|
||||
|
||||
#include <list>
|
||||
#include "common/messages.h"
|
||||
|
||||
#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 <spice/vd_agent.h>
|
||||
#include "process_loop.h"
|
||||
|
||||
class Application;
|
||||
|
||||
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 SpiceMsgMainMigrationBegin* 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;
|
||||
RedPeer::HostAuthOptions _auth_options;
|
||||
std::string _con_ciphers;
|
||||
Thread* _thread;
|
||||
Mutex _lock;
|
||||
Condition _cond;
|
||||
int _pending_con;
|
||||
int _protocol;
|
||||
};
|
||||
|
||||
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 SPICE_GNUC_NORETURN 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 RedClient;
|
||||
|
||||
class AgentTimer: public Timer {
|
||||
public:
|
||||
virtual void response(AbstractProcessLoop& events_loop);
|
||||
AgentTimer(RedClient *client) : _client(client) {};
|
||||
private:
|
||||
RedClient *_client;
|
||||
};
|
||||
|
||||
typedef std::map< int, RedPeer::ConnectionOptions::Type> PeerConnectionOptMap;
|
||||
|
||||
class ForEachChannelFunc {
|
||||
public:
|
||||
virtual bool operator() (RedChannel& channel) = 0;
|
||||
};
|
||||
|
||||
class DisplaySetting {
|
||||
public:
|
||||
DisplaySetting() : _disable_wallpaper (false)
|
||||
, _disable_font_smooth (false)
|
||||
, _disable_animation (false)
|
||||
, _set_color_depth (false)
|
||||
{}
|
||||
|
||||
bool is_empty() {return !(_disable_wallpaper || _disable_font_smooth ||
|
||||
_disable_animation || _set_color_depth);}
|
||||
|
||||
public:
|
||||
bool _disable_wallpaper;
|
||||
bool _disable_font_smooth;
|
||||
bool _disable_animation;
|
||||
bool _set_color_depth;
|
||||
uint32_t _color_depth;
|
||||
};
|
||||
|
||||
class ClipboardGrabEvent : public Event {
|
||||
public:
|
||||
ClipboardGrabEvent(uint32_t *types, uint32_t type_count)
|
||||
{
|
||||
_types = new uint32_t [type_count];
|
||||
memcpy(_types, types, type_count * sizeof(uint32_t));
|
||||
_type_count = type_count;
|
||||
}
|
||||
~ClipboardGrabEvent()
|
||||
{
|
||||
delete[] _types;
|
||||
}
|
||||
|
||||
virtual void response(AbstractProcessLoop& events_loop);
|
||||
|
||||
private:
|
||||
uint32_t *_types;
|
||||
uint32_t _type_count;
|
||||
};
|
||||
|
||||
class ClipboardRequestEvent : public Event {
|
||||
public:
|
||||
ClipboardRequestEvent(uint32_t type) : _type (type) {}
|
||||
virtual void response(AbstractProcessLoop& events_loop);
|
||||
|
||||
private:
|
||||
uint32_t _type;
|
||||
};
|
||||
|
||||
class ClipboardNotifyEvent : public Event {
|
||||
public:
|
||||
ClipboardNotifyEvent(uint32_t type, uint8_t *data, uint32_t size)
|
||||
{
|
||||
_type = type;
|
||||
_data = new uint8_t [size];
|
||||
memcpy(_data, data, size);
|
||||
_size = size;
|
||||
}
|
||||
~ClipboardNotifyEvent()
|
||||
{
|
||||
delete[] _data;
|
||||
}
|
||||
|
||||
virtual void response(AbstractProcessLoop& events_loop);
|
||||
|
||||
private:
|
||||
uint32_t _type;
|
||||
uint8_t *_data;
|
||||
uint32_t _size;
|
||||
};
|
||||
|
||||
class ClipboardReleaseEvent : public Event {
|
||||
public:
|
||||
ClipboardReleaseEvent() {}
|
||||
virtual void response(AbstractProcessLoop& events_loop);
|
||||
};
|
||||
|
||||
class MigrateEndEvent: public Event {
|
||||
public:
|
||||
virtual void response(AbstractProcessLoop& events_loop);
|
||||
};
|
||||
|
||||
class RedClient: public RedChannel,
|
||||
public Platform::ClipboardListener {
|
||||
public:
|
||||
friend class RedChannel;
|
||||
friend class Migrate;
|
||||
friend class ClipboardGrabEvent;
|
||||
friend class ClipboardRequestEvent;
|
||||
friend class ClipboardNotifyEvent;
|
||||
friend class ClipboardReleaseEvent;
|
||||
friend class MigrateEndEvent;
|
||||
|
||||
RedClient(Application& application);
|
||||
~RedClient();
|
||||
|
||||
void register_channel_factory(ChannelFactory& factory);
|
||||
|
||||
virtual void connect();
|
||||
virtual void disconnect();
|
||||
virtual bool abort();
|
||||
|
||||
void connect(bool wait_main_disconnect);
|
||||
|
||||
void push_event(Event* event);
|
||||
void activate_interval_timer(Timer* timer, unsigned int millisec);
|
||||
void deactivate_interval_timer(Timer* timer);
|
||||
|
||||
void set_target(const std::string& host, int port, int sport, int protocol = 0);
|
||||
void set_password(const std::string& password) { _password = password;}
|
||||
void set_auto_display_res(bool auto_display_res) { _auto_display_res = auto_display_res;}
|
||||
void set_display_setting(DisplaySetting& setting) { _display_setting = setting;}
|
||||
const std::string& get_password() { return _password;}
|
||||
const std::string& get_host() { return _host;}
|
||||
int get_port() { return _port;}
|
||||
int get_sport() { return _sport;}
|
||||
int get_protocol() { return _protocol;}
|
||||
void set_protocol(int protocol) { _protocol = protocol;}
|
||||
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);
|
||||
RedPeer::HostAuthOptions& get_host_auth_options() { return _host_auth_opt;}
|
||||
const std::string& get_connection_ciphers() { return _con_ciphers;}
|
||||
void get_sync_info(uint8_t channel_type, uint8_t channel_id, SyncInfo& info);
|
||||
void wait_for_channels(int wait_list_size, SpiceWaitForChannel* wait_list);
|
||||
PixmapCache& get_pixmap_cache() {return _pixmap_cache;}
|
||||
uint64_t get_pixmap_cache_size() { return _pixmap_cache_size;}
|
||||
void on_display_mode_change();
|
||||
void on_clipboard_grab(uint32_t *types, uint32_t type_count);
|
||||
void on_clipboard_request(uint32_t type);
|
||||
void on_clipboard_notify(uint32_t type, uint8_t* data, int32_t size);
|
||||
void on_clipboard_release();
|
||||
|
||||
void for_each_channel(ForEachChannelFunc& func);
|
||||
void on_mouse_capture_trigger(RedScreen& screen);
|
||||
|
||||
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();
|
||||
void send_main_attach_channels(void);
|
||||
|
||||
protected:
|
||||
virtual void on_connecting();
|
||||
virtual void on_connect();
|
||||
virtual void on_disconnect();
|
||||
virtual void on_connect_mig_target() {}
|
||||
virtual void on_disconnect_mig_src();
|
||||
|
||||
private:
|
||||
void on_channel_disconnected(RedChannel& channel);
|
||||
void on_channel_disconnect_mig_src_completed(RedChannel& channel);
|
||||
void send_migrate_end();
|
||||
void migrate_channel(RedChannel& channel);
|
||||
void send_agent_announce_capabilities(bool request);
|
||||
void send_agent_monitors_config();
|
||||
void send_agent_display_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_migrate_end(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 handle_migrate_switch_host(RedPeer::InMessage* message);
|
||||
void dispatch_agent_message(VDAgentMessage* msg, void* data);
|
||||
|
||||
bool init_guest_display();
|
||||
void on_agent_reply(VDAgentReply* reply);
|
||||
void on_agent_announce_capabilities(VDAgentAnnounceCapabilities* caps,
|
||||
uint32_t msg_size);
|
||||
void do_send_agent_clipboard();
|
||||
void send_agent_clipboard_message(uint32_t message_type, uint32_t size = 0, void* data = NULL);
|
||||
void send_agent_clipboard_notify_message(uint32_t type, uint8_t *data, uint32_t size);
|
||||
|
||||
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 _host;
|
||||
int _port;
|
||||
int _sport;
|
||||
int _protocol;
|
||||
std::string _password;
|
||||
uint32_t _connection_id;
|
||||
uint32_t _mouse_mode;
|
||||
Mutex _notify_lock;
|
||||
bool _notify_disconnect;
|
||||
bool _auto_display_res;
|
||||
DisplaySetting _display_setting;
|
||||
uint32_t _agent_reply_wait_type;
|
||||
|
||||
bool _aborting;
|
||||
bool _msg_attach_channels_sent;
|
||||
|
||||
bool _agent_connected;
|
||||
bool _agent_mon_config_sent;
|
||||
bool _agent_disp_config_sent;
|
||||
//FIXME: rename to in/out, extract all agent stuff?
|
||||
VDAgentMessage* _agent_msg;
|
||||
uint8_t* _agent_msg_data;
|
||||
uint32_t _agent_msg_pos;
|
||||
VDAgentMessage* _agent_out_msg;
|
||||
uint32_t _agent_out_msg_size;
|
||||
uint32_t _agent_out_msg_pos;
|
||||
uint32_t _agent_tokens;
|
||||
AutoRef<AgentTimer> _agent_timer;
|
||||
uint32_t _agent_caps_size;
|
||||
uint32_t *_agent_caps;
|
||||
|
||||
PeerConnectionOptMap _con_opt_map;
|
||||
RedPeer::HostAuthOptions _host_auth_opt;
|
||||
std::string _con_ciphers;
|
||||
Migrate _migrate;
|
||||
Mutex _channels_lock;
|
||||
typedef std::list<ChannelFactory*> Factorys;
|
||||
Factorys _factorys;
|
||||
typedef std::list<RedChannel*> Channels;
|
||||
Channels _channels;
|
||||
Channels _pending_mig_disconnect_channels;
|
||||
PixmapCache _pixmap_cache;
|
||||
uint64_t _pixmap_cache_size;
|
||||
Mutex _sync_lock;
|
||||
Condition _sync_condition;
|
||||
uint64_t _sync_info[SPICE_END_CHANNEL][256];
|
||||
|
||||
GlzDecoderWindowDebug _glz_debug;
|
||||
GlzDecoderWindow _glz_window;
|
||||
unsigned int _glz_window_size; // in pixels
|
||||
|
||||
Mutex _mm_clock_lock;
|
||||
uint64_t _mm_clock_last_update;
|
||||
uint32_t _mm_time;
|
||||
|
||||
bool _during_migration;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,122 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_RED_DRAWABLE
|
||||
#define _H_RED_DRAWABLE
|
||||
|
||||
#include "common/pixman_utils.h"
|
||||
#include "pixels_source.h"
|
||||
#include "utils.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 Format {
|
||||
ARGB32,
|
||||
RGB32,
|
||||
RGB16_555,
|
||||
RGB16_565,
|
||||
A1,
|
||||
};
|
||||
|
||||
static int format_copy_compatible(Format src, Format dest) {
|
||||
return src == dest || (src == ARGB32 && dest == RGB32);
|
||||
}
|
||||
|
||||
static int format_to_bpp(Format format) {
|
||||
if (format == RedDrawable::A1) {
|
||||
return 1;
|
||||
} else if (format == RGB16_555 || format == RGB16_565) {
|
||||
return 16;
|
||||
} else {
|
||||
return 32;
|
||||
}
|
||||
}
|
||||
|
||||
static pixman_format_code_t format_to_pixman(Format format) {
|
||||
switch (format) {
|
||||
case RedDrawable::ARGB32:
|
||||
return PIXMAN_a8r8g8b8;
|
||||
case RedDrawable::RGB32:
|
||||
return PIXMAN_x8r8g8b8;
|
||||
case RedDrawable::RGB16_555:
|
||||
return PIXMAN_x1r5g5b5;
|
||||
case RedDrawable::RGB16_565:
|
||||
return PIXMAN_r5g6b5;
|
||||
case RedDrawable::A1:
|
||||
return PIXMAN_a1;
|
||||
default:
|
||||
THROW("unsupported format %d", format);
|
||||
}
|
||||
}
|
||||
|
||||
static Format format_from_surface(uint32_t format) {
|
||||
switch (format) {
|
||||
case SPICE_SURFACE_FMT_16_555:
|
||||
return RedDrawable::RGB16_555;
|
||||
case SPICE_SURFACE_FMT_16_565:
|
||||
return RedDrawable::RGB16_565;
|
||||
case SPICE_SURFACE_FMT_32_xRGB:
|
||||
return RedDrawable::RGB32;
|
||||
case SPICE_SURFACE_FMT_32_ARGB:
|
||||
return RedDrawable::ARGB32;
|
||||
default:
|
||||
THROW("Unsupported RedPixman format");
|
||||
}
|
||||
}
|
||||
|
||||
enum CombineOP {
|
||||
OP_COPY,
|
||||
OP_AND,
|
||||
OP_XOR,
|
||||
};
|
||||
|
||||
virtual RedDrawable::Format get_format() = 0;
|
||||
void copy_pixels(const PixelsSource& src, int src_x, int src_y, const SpiceRect& dest);
|
||||
void blend_pixels(const PixelsSource& src, int src_x, int src_y, const SpiceRect& dest);
|
||||
void combine_pixels(const PixelsSource& src, int src_x, int src_y, const SpiceRect& dest,
|
||||
CombineOP op);
|
||||
void fill_rect(const SpiceRect& rect, rgb32_t color);
|
||||
void frame_rect(const SpiceRect& rect, rgb32_t color);
|
||||
void erase_rect(const SpiceRect& rect, rgb32_t color);
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,97 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#ifdef __MINGW32__
|
||||
#undef HAVE_STDLIB_H
|
||||
#endif
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
#include <stdint.h>
|
||||
|
||||
#include "common/region.h"
|
||||
#define SPICE_CANVAS_INTERNAL
|
||||
#define SW_CANVAS_CACHE
|
||||
#include "common/gdi_canvas.c"
|
||||
#undef SW_CANVAS_CACHE
|
||||
#undef SPICE_CANVAS_INTERNAL
|
||||
|
||||
#include "common.h"
|
||||
#include "red_gdi_canvas.h"
|
||||
#include "utils.h"
|
||||
#include "debug.h"
|
||||
#include "red_pixmap_gdi.h"
|
||||
|
||||
GDICanvas::GDICanvas(int width, int height, uint32_t format,
|
||||
PixmapCache& pixmap_cache, PaletteCache& palette_cache,
|
||||
GlzDecoderWindow &glz_decoder_window, SurfacesCache &csurfaces)
|
||||
: Canvas (pixmap_cache, palette_cache, glz_decoder_window, csurfaces)
|
||||
, _pixmap (0)
|
||||
{
|
||||
_pixmap = new RedPixmapGdi(width, height,
|
||||
RedDrawable::format_from_surface(format),
|
||||
true);
|
||||
if (!(_canvas = gdi_canvas_create(width, height, _pixmap->get_dc(),
|
||||
&_pixmap->get_mutex(),
|
||||
format, &pixmap_cache.base,
|
||||
&palette_cache.base,
|
||||
&csurfaces,
|
||||
&glz_decoder(),
|
||||
&jpeg_decoder(),
|
||||
&zlib_decoder()))) {
|
||||
THROW("create canvas failed");
|
||||
}
|
||||
}
|
||||
|
||||
GDICanvas::~GDICanvas()
|
||||
{
|
||||
_canvas->ops->destroy(_canvas);
|
||||
_canvas = NULL;
|
||||
delete _pixmap;
|
||||
_pixmap = NULL;
|
||||
}
|
||||
|
||||
void GDICanvas::copy_pixels(const QRegion& region, RedDrawable& dest_dc)
|
||||
{
|
||||
pixman_box32_t *rects;
|
||||
int num_rects;
|
||||
|
||||
rects = pixman_region32_rectangles((pixman_region32_t *)®ion, &num_rects);
|
||||
for (int i = 0; i < num_rects; i++) {
|
||||
SpiceRect r;
|
||||
|
||||
r.left = rects[i].x1;
|
||||
r.top = rects[i].y1;
|
||||
r.right = rects[i].x2;
|
||||
r.bottom = rects[i].y2;
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
CanvasType GDICanvas::get_pixmap_type()
|
||||
{
|
||||
return CANVAS_TYPE_GDI;
|
||||
}
|
||||
@ -1,53 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_GDICANVAS
|
||||
#define _H_GDICANVAS
|
||||
|
||||
#include "canvas.h"
|
||||
#define SPICE_CANVAS_INTERNAL
|
||||
#define SW_CANVAS_CACHE
|
||||
#include "common/gdi_canvas.h"
|
||||
#undef SW_CANVAS_CACHE
|
||||
#undef SPICE_CANVAS_INTERNAL
|
||||
#include "red_pixmap_gdi.h"
|
||||
|
||||
class RedPixmap;
|
||||
|
||||
|
||||
class GDICanvas: public Canvas {
|
||||
public:
|
||||
GDICanvas(int width, int height, uint32_t format,
|
||||
PixmapCache& pixmap_cache, PaletteCache& palette_cache,
|
||||
GlzDecoderWindow &glz_decoder_window, SurfacesCache &csurfaces);
|
||||
virtual ~GDICanvas();
|
||||
|
||||
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 CanvasType get_pixmap_type();
|
||||
|
||||
private:
|
||||
RedPixmapGdi *_pixmap;
|
||||
RedPixmapGdi *_helper_pixmap;
|
||||
HDC _dc;
|
||||
HBITMAP _prev_bitmap;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,123 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <GL/glx.h>
|
||||
#include "common/region.h"
|
||||
|
||||
#define SPICE_CANVAS_INTERNAL
|
||||
#define SW_CANVAS_CACHE
|
||||
#include "common/gl_canvas.c"
|
||||
#undef SW_CANVAS_CACHE
|
||||
#undef SPICE_CANVAS_INTERNAL
|
||||
|
||||
#include "common.h"
|
||||
#include "red_gl_canvas.h"
|
||||
#include "utils.h"
|
||||
#include "debug.h"
|
||||
#include "red_pixmap_gl.h"
|
||||
|
||||
GCanvas::GCanvas(int width, int height, uint32_t format, RedWindow *win,
|
||||
RenderType rendertype,
|
||||
PixmapCache& pixmap_cache, PaletteCache& palette_cache,
|
||||
GlzDecoderWindow &glz_decoder_window, SurfacesCache &csurfaces)
|
||||
: Canvas(pixmap_cache, palette_cache, glz_decoder_window, csurfaces)
|
||||
, _pixmap (0)
|
||||
, _textures_lost (false)
|
||||
{
|
||||
_pixmap = new RedPixmapGL(width, height,
|
||||
RedDrawable::format_from_surface(format),
|
||||
true, win, rendertype);
|
||||
if (!(_canvas = gl_canvas_create(width, height,
|
||||
SPICE_SURFACE_FMT_DEPTH(format),
|
||||
&pixmap_cache.base,
|
||||
&palette_cache.base,
|
||||
&csurfaces,
|
||||
&glz_decoder(),
|
||||
&jpeg_decoder(),
|
||||
&zlib_decoder()))) {
|
||||
THROW("create canvas failed");
|
||||
}
|
||||
}
|
||||
|
||||
GCanvas::~GCanvas()
|
||||
{
|
||||
gl_canvas_set_textures_lost (_canvas, (int)_textures_lost);
|
||||
_canvas->ops->destroy(_canvas);
|
||||
_canvas = NULL;
|
||||
delete _pixmap;
|
||||
_pixmap = NULL;
|
||||
}
|
||||
|
||||
void GCanvas::copy_pixels(const QRegion& region, RedDrawable& dest_dc)
|
||||
{
|
||||
pixman_box32_t *rects;
|
||||
int num_rects;
|
||||
|
||||
pre_gl_copy();
|
||||
rects = pixman_region32_rectangles((pixman_region32_t *)®ion, &num_rects);
|
||||
for (int i = 0; i < num_rects; i++) {
|
||||
SpiceRect r;
|
||||
|
||||
r.left = rects[i].x1;
|
||||
r.top = rects[i].y1;
|
||||
r.right = rects[i].x2;
|
||||
r.bottom = rects[i].y2;
|
||||
|
||||
dest_dc.copy_pixels(*_pixmap, r.left, r.top, r);
|
||||
}
|
||||
post_gl_copy();
|
||||
}
|
||||
|
||||
void GCanvas::copy_pixels(const QRegion& region, RedDrawable* dest_dc, const PixmapHeader* pixmap)
|
||||
{
|
||||
copy_pixels(region, *dest_dc);
|
||||
}
|
||||
|
||||
void GCanvas::touched_bbox(const SpiceRect *bbox)
|
||||
{
|
||||
_pixmap->update_texture(bbox);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
@ -1,66 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_GCANVAS
|
||||
#define _H_GCANVAS
|
||||
|
||||
#include "canvas.h"
|
||||
#define SPICE_CANVAS_INTERNAL
|
||||
#define SW_CANVAS_CACHE
|
||||
#include "common/sw_canvas.h"
|
||||
#include "common/gl_canvas.h"
|
||||
#undef SW_CANVAS_CACHE
|
||||
#undef SPICE_CANVAS_INTERNAL
|
||||
|
||||
#include "red_pixmap_gl.h"
|
||||
#include "red_window.h"
|
||||
|
||||
class RedPixmapGL;
|
||||
|
||||
class GCanvas: public Canvas {
|
||||
public:
|
||||
GCanvas(int width, int height, uint32_t format, RedWindow *win,
|
||||
RenderType rendertype,
|
||||
PixmapCache& pixmap_cache, PaletteCache& palette_cache,
|
||||
GlzDecoderWindow &glz_decoder_window, SurfacesCache &csurfaces);
|
||||
virtual ~GCanvas();
|
||||
|
||||
void set_mode();
|
||||
void clear();
|
||||
void thread_touch() {}
|
||||
void copy_pixels(const QRegion& region, RedDrawable* dc,
|
||||
const PixmapHeader* pixmap);
|
||||
void copy_pixels(const QRegion& region, RedDrawable& dc);
|
||||
virtual void textures_lost();
|
||||
virtual CanvasType get_pixmap_type();
|
||||
virtual void touch_context();
|
||||
virtual void pre_gl_copy();
|
||||
virtual void post_gl_copy();
|
||||
void touched_bbox(const SpiceRect *bbox);
|
||||
|
||||
private:
|
||||
void create_pixmap(int width, int height, RedWindow *win,
|
||||
RenderType rendertype);
|
||||
void destroy_pixmap();
|
||||
void destroy();
|
||||
|
||||
private:
|
||||
RedPixmapGL *_pixmap;
|
||||
bool _textures_lost;
|
||||
};
|
||||
|
||||
#endif
|
||||
152
client/red_key.h
152
client/red_key.h
@ -1,152 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_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_MUTE = REDKEY_ESCAPE_BASE + 0x20,
|
||||
REDKEY_FAKE_L_SHIFT = REDKEY_ESCAPE_BASE + 0x2a,
|
||||
REDKEY_VOLUME_DOWN = REDKEY_ESCAPE_BASE + 0x2e,
|
||||
REDKEY_VOLUME_UP = REDKEY_ESCAPE_BASE + 0x30,
|
||||
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
|
||||
@ -1,463 +0,0 @@
|
||||
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/x509v3.h>
|
||||
#include <spice/protocol.h>
|
||||
#include "common/ssl_verify.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "red_peer.h"
|
||||
#include "utils.h"
|
||||
#include "debug.h"
|
||||
#include "platform_utils.h"
|
||||
|
||||
static void SPICE_GNUC_NORETURN ssl_error()
|
||||
{
|
||||
unsigned long last_error = ERR_peek_last_error();
|
||||
|
||||
ERR_print_errors_fp(stderr);
|
||||
THROW_ERR(SPICEC_ERROR_CODE_SSL_ERROR, "SSL Error: %s", ERR_error_string(last_error, NULL));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void RedPeer::connect_to_peer(const char* host, int portnr)
|
||||
{
|
||||
struct addrinfo ai, *result = NULL, *e;
|
||||
char uaddr[INET6_ADDRSTRLEN+1];
|
||||
char uport[33], port[33];
|
||||
int err = 0, rc, no_delay = 1;
|
||||
ASSERT(_ctx == NULL && _ssl == NULL && _peer == INVALID_SOCKET);
|
||||
try {
|
||||
memset(&ai,0, sizeof(ai));
|
||||
ai.ai_flags = AI_CANONNAME;
|
||||
#ifdef AI_ADDRCONFIG
|
||||
ai.ai_flags |= AI_ADDRCONFIG;
|
||||
#endif
|
||||
ai.ai_family = PF_UNSPEC;
|
||||
ai.ai_socktype = SOCK_STREAM;
|
||||
snprintf(port, sizeof(port), "%d", portnr);
|
||||
rc = getaddrinfo(host, port, &ai, &result);
|
||||
if (rc != 0) {
|
||||
THROW_ERR(SPICEC_ERROR_CODE_GETHOSTBYNAME_FAILED, "cannot resolve host address %s", host);
|
||||
}
|
||||
Lock lock(_lock);
|
||||
_peer = INVALID_SOCKET;
|
||||
for (e = result; e != NULL; e = e->ai_next) {
|
||||
if ((_peer = socket(e->ai_family, e->ai_socktype, e->ai_protocol)) == INVALID_SOCKET) {
|
||||
int err = sock_error();
|
||||
THROW_ERR(SPICEC_ERROR_CODE_SOCKET_FAILED, "failed to create socket: %s (%d)",
|
||||
sock_err_message(err), err);
|
||||
}
|
||||
if (setsockopt(_peer, IPPROTO_TCP, TCP_NODELAY, (const char*)&no_delay, sizeof(no_delay)) ==
|
||||
SOCKET_ERROR) {
|
||||
LOG_WARN("set TCP_NODELAY failed");
|
||||
}
|
||||
|
||||
getnameinfo((struct sockaddr*)e->ai_addr, e->ai_addrlen,
|
||||
uaddr,INET6_ADDRSTRLEN, uport,32,
|
||||
NI_NUMERICHOST | NI_NUMERICSERV);
|
||||
DBG(0, "Trying %s %s", uaddr, uport);
|
||||
|
||||
if (::connect(_peer, e->ai_addr, e->ai_addrlen) == SOCKET_ERROR) {
|
||||
err = sock_error();
|
||||
LOG_INFO("Connect failed: %s (%d)",
|
||||
sock_err_message(err), err);
|
||||
closesocket(_peer);
|
||||
_peer = INVALID_SOCKET;
|
||||
continue;
|
||||
}
|
||||
DBG(0, "Connected to %s %s", uaddr, uport);
|
||||
break;
|
||||
}
|
||||
lock.unlock();
|
||||
freeaddrinfo(result);
|
||||
if (_peer == INVALID_SOCKET) {
|
||||
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 portnr)
|
||||
{
|
||||
connect_to_peer(host, portnr);
|
||||
ASSERT(_ctx == NULL && _ssl == NULL && _peer != INVALID_SOCKET);
|
||||
LOG_INFO("Connected to %s %d", host, portnr);
|
||||
}
|
||||
|
||||
void RedPeer::connect_secure(const ConnectionOptions& options, const char* host)
|
||||
{
|
||||
int return_code;
|
||||
SPICE_SSL_VERIFY_OP auth_flags;
|
||||
SpiceOpenSSLVerify* verify = NULL;
|
||||
int portnr = options.secure_port;
|
||||
|
||||
connect_to_peer(host, portnr);
|
||||
ASSERT(_ctx == NULL && _ssl == NULL && _peer != INVALID_SOCKET);
|
||||
LOG_INFO("Connected to %s %d", host, portnr);
|
||||
|
||||
try {
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
|
||||
const SSL_METHOD *ssl_method = TLSv1_method();
|
||||
#else
|
||||
SSL_METHOD *ssl_method = TLSv1_method();
|
||||
#endif
|
||||
_ctx = SSL_CTX_new(ssl_method);
|
||||
if (_ctx == NULL) {
|
||||
ssl_error();
|
||||
}
|
||||
|
||||
auth_flags = options.host_auth.type_flags;
|
||||
if ((auth_flags & SPICE_SSL_VERIFY_OP_HOSTNAME) ||
|
||||
(auth_flags & SPICE_SSL_VERIFY_OP_SUBJECT)) {
|
||||
std::string CA_file = options.host_auth.CA_file;
|
||||
ASSERT(!CA_file.empty());
|
||||
|
||||
return_code = SSL_CTX_load_verify_locations(_ctx, CA_file.c_str(), NULL);
|
||||
if (return_code != 1) {
|
||||
if (auth_flags & SPICE_SSL_VERIFY_OP_PUBKEY) {
|
||||
LOG_WARN("SSL_CTX_load_verify_locations failed, CA_file=%s. "
|
||||
"only pubkey authentication is active", CA_file.c_str());
|
||||
auth_flags = SPICE_SSL_VERIFY_OP_PUBKEY;
|
||||
}
|
||||
else {
|
||||
LOG_ERROR("SSL_CTX_load_verify_locations failed CA_file=%s", CA_file.c_str());
|
||||
ssl_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return_code = SSL_CTX_set_cipher_list(_ctx, options.ciphers.c_str());
|
||||
if (return_code != 1) {
|
||||
LOG_ERROR("SSL_CTX_set_cipher_list failed, ciphers=%s", options.ciphers.c_str());
|
||||
ssl_error();
|
||||
}
|
||||
|
||||
_ssl = SSL_new(_ctx);
|
||||
if (!_ssl) {
|
||||
THROW("create ssl failed");
|
||||
}
|
||||
|
||||
verify = spice_openssl_verify_new(
|
||||
_ssl, auth_flags,
|
||||
host,
|
||||
(char*)&options.host_auth.host_pubkey[0],
|
||||
options.host_auth.host_pubkey.size(),
|
||||
options.host_auth.host_subject.c_str());
|
||||
|
||||
BIO* sbio = BIO_new_socket(_peer, BIO_NOCLOSE);
|
||||
if (!sbio) {
|
||||
THROW("alloc new socket bio failed");
|
||||
}
|
||||
|
||||
SSL_set_bio(_ssl, sbio, sbio);
|
||||
|
||||
return_code = SSL_connect(_ssl);
|
||||
if (return_code <= 0) {
|
||||
int ssl_error_code = SSL_get_error(_ssl, return_code);
|
||||
LOG_ERROR("failed to connect w/SSL, ssl_error %s",
|
||||
ERR_error_string(ssl_error_code, NULL));
|
||||
ssl_error();
|
||||
}
|
||||
} catch (...) {
|
||||
Lock lock(_lock);
|
||||
spice_openssl_verify_free(verify);
|
||||
cleanup();
|
||||
throw;
|
||||
}
|
||||
|
||||
spice_openssl_verify_free(verify);
|
||||
}
|
||||
|
||||
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::receive(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::CompoundInMessage* RedPeer::receive()
|
||||
{
|
||||
SpiceDataHeader header;
|
||||
AutoRef<CompoundInMessage> message;
|
||||
|
||||
receive((uint8_t*)&header, sizeof(SpiceDataHeader));
|
||||
message.reset(new CompoundInMessage(header.serial, header.type, header.size, header.sub_list));
|
||||
receive((*message)->data(), (*message)->compound_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::do_send(RedPeer::OutMessage& message, uint32_t skip_bytes)
|
||||
{
|
||||
uint8_t *data;
|
||||
int free_data;
|
||||
size_t len;
|
||||
uint32_t res;
|
||||
|
||||
data = spice_marshaller_linearize(message.marshaller(), skip_bytes,
|
||||
&len, &free_data);
|
||||
|
||||
res = send(data, len);
|
||||
|
||||
if (free_data) {
|
||||
free(data);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
uint32_t RedPeer::send(RedPeer::OutMessage& message)
|
||||
{
|
||||
|
||||
message.header().serial = ++_serial;
|
||||
message.header().size = message.message_size() - sizeof(SpiceDataHeader);
|
||||
|
||||
return do_send(message, 0);
|
||||
}
|
||||
|
||||
RedPeer::OutMessage::OutMessage(uint32_t type)
|
||||
: _marshaller (spice_marshaller_new())
|
||||
{
|
||||
SpiceDataHeader *header;
|
||||
header = (SpiceDataHeader *)
|
||||
spice_marshaller_reserve_space(_marshaller, sizeof(SpiceDataHeader));
|
||||
spice_marshaller_set_base(_marshaller, sizeof(SpiceDataHeader));
|
||||
|
||||
header->type = type;
|
||||
header->sub_list = 0;
|
||||
}
|
||||
|
||||
void RedPeer::OutMessage::reset(uint32_t type)
|
||||
{
|
||||
spice_marshaller_reset(_marshaller);
|
||||
|
||||
SpiceDataHeader *header;
|
||||
header = (SpiceDataHeader *)
|
||||
spice_marshaller_reserve_space(_marshaller, sizeof(SpiceDataHeader));
|
||||
spice_marshaller_set_base(_marshaller, sizeof(SpiceDataHeader));
|
||||
|
||||
header->type = type;
|
||||
header->sub_list = 0;
|
||||
}
|
||||
|
||||
RedPeer::OutMessage::~OutMessage()
|
||||
{
|
||||
spice_marshaller_destroy(_marshaller);
|
||||
}
|
||||
@ -1,209 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_REDPEER
|
||||
#define _H_REDPEER
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
|
||||
#include <spice/protocol.h>
|
||||
#include "common/marshaller.h"
|
||||
#include "common/ssl_verify.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "process_loop.h"
|
||||
#include "threads.h"
|
||||
#include "platform_utils.h"
|
||||
#include "debug.h"
|
||||
|
||||
class RedPeer: protected EventSources::Socket {
|
||||
public:
|
||||
RedPeer();
|
||||
virtual ~RedPeer();
|
||||
|
||||
class InMessage;
|
||||
class CompoundInMessage;
|
||||
class OutMessage;
|
||||
class DisconnectedException {};
|
||||
|
||||
class HostAuthOptions {
|
||||
public:
|
||||
|
||||
typedef std::vector<uint8_t> PublicKey;
|
||||
typedef std::pair<std::string, std::string> CertFieldValuePair;
|
||||
typedef std::list<CertFieldValuePair> CertFieldValueList;
|
||||
|
||||
HostAuthOptions() : type_flags(SPICE_SSL_VERIFY_OP_NONE) {}
|
||||
|
||||
public:
|
||||
|
||||
SPICE_SSL_VERIFY_OP type_flags;
|
||||
|
||||
PublicKey host_pubkey;
|
||||
std::string host_subject;
|
||||
std::string CA_file;
|
||||
};
|
||||
|
||||
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,
|
||||
int in_protocol,
|
||||
const HostAuthOptions& in_host_auth,
|
||||
const std::string& in_ciphers)
|
||||
: type (in_type)
|
||||
, unsecure_port (in_port)
|
||||
, secure_port (in_sport)
|
||||
, protocol (in_protocol)
|
||||
, host_auth (in_host_auth)
|
||||
, ciphers (in_ciphers)
|
||||
{
|
||||
}
|
||||
|
||||
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;
|
||||
int protocol; // 0 == auto
|
||||
HostAuthOptions host_auth; // for secure connection
|
||||
std::string ciphers;
|
||||
};
|
||||
|
||||
void connect_unsecure(const char* host, int port);
|
||||
void connect_secure(const ConnectionOptions& options, const char* host);
|
||||
|
||||
void disconnect();
|
||||
void swap(RedPeer* other);
|
||||
void close();
|
||||
void enable() { _shut = false;}
|
||||
|
||||
virtual CompoundInMessage* receive();
|
||||
uint32_t do_send(OutMessage& message, uint32_t skip_bytes);
|
||||
uint32_t send(OutMessage& message);
|
||||
|
||||
uint32_t receive(uint8_t* buf, uint32_t size);
|
||||
uint32_t send(uint8_t* buf, uint32_t size);
|
||||
|
||||
protected:
|
||||
virtual void on_event() {}
|
||||
virtual int get_socket() { return _peer;}
|
||||
void cleanup();
|
||||
|
||||
private:
|
||||
void connect_to_peer(const char* host, int port);
|
||||
void shutdown();
|
||||
|
||||
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::CompoundInMessage: public RedPeer::InMessage {
|
||||
public:
|
||||
CompoundInMessage(uint64_t _serial, uint16_t type, uint32_t size, uint32_t sub_list)
|
||||
: InMessage(type, size, new uint8_t[size])
|
||||
, _refs (1)
|
||||
, _serial (_serial)
|
||||
, _sub_list (sub_list)
|
||||
{
|
||||
}
|
||||
|
||||
RedPeer::InMessage* ref() { _refs++; return this;}
|
||||
void unref() {if (!--_refs) delete this;}
|
||||
|
||||
uint64_t serial() { return _serial;}
|
||||
uint32_t sub_list() { return _sub_list;}
|
||||
|
||||
virtual uint32_t size() { return _sub_list ? _sub_list : _size;}
|
||||
uint32_t compound_size() {return _size;}
|
||||
|
||||
protected:
|
||||
virtual ~CompoundInMessage() { delete[] _data;}
|
||||
|
||||
private:
|
||||
int _refs;
|
||||
uint64_t _serial;
|
||||
uint32_t _sub_list;
|
||||
};
|
||||
|
||||
class RedPeer::OutMessage {
|
||||
public:
|
||||
OutMessage(uint32_t type);
|
||||
virtual ~OutMessage();
|
||||
|
||||
SpiceMarshaller *marshaller() { return _marshaller;}
|
||||
void reset(uint32_t type);
|
||||
|
||||
private:
|
||||
uint32_t message_size() { return spice_marshaller_get_total_size(_marshaller);}
|
||||
uint8_t* base() { return spice_marshaller_get_ptr(_marshaller);}
|
||||
SpiceDataHeader& header() { return *(SpiceDataHeader *)base();}
|
||||
|
||||
protected:
|
||||
SpiceMarshaller *_marshaller;
|
||||
|
||||
friend class RedPeer;
|
||||
friend class RedChannel;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,67 +0,0 @@
|
||||
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_RED_PIXMAP
|
||||
#define _H_RED_PIXMAP
|
||||
|
||||
#include "red_drawable.h"
|
||||
#include "utils.h"
|
||||
|
||||
class RedPixmap: public RedDrawable {
|
||||
public:
|
||||
RedPixmap(int width, int height, Format format, bool top_bottom);
|
||||
virtual ~RedPixmap();
|
||||
|
||||
virtual SpicePoint get_size() { SpicePoint 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();
|
||||
virtual RedDrawable::Format get_format() { return _format; }
|
||||
|
||||
bool equal(const RedPixmap &other, const SpiceRect &rect) const {
|
||||
spice_debug("l:%d r:%d t:%d b:%d", rect.left, rect.right, rect.top, rect.bottom);
|
||||
for (int x = rect.left; x < rect.right; ++x)
|
||||
for (int y = rect.top; y < rect.bottom; ++y) {
|
||||
for (int i = 0; i < 3; i++) { // ignore alpha
|
||||
int p = x * 4 + y * _stride + i;
|
||||
if (other._data[p] != _data[p]) {
|
||||
spice_printerr("equal fails at (+%d+%d) +%d+%d:%d in %dx%d",
|
||||
rect.left, rect.top, x-rect.left, y-rect.top, i,
|
||||
_width-rect.left, _height-rect.top);
|
||||
if (getenv("DIFFBP"))
|
||||
SPICE_BREAKPOINT();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
Format _format;
|
||||
int _width;
|
||||
int _height;
|
||||
int _stride;
|
||||
bool _top_bottom;
|
||||
uint8_t* _data;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,35 +0,0 @@
|
||||
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_RED_PIXMAP_GDI
|
||||
#define _H_RED_PIXMAP_GDI
|
||||
|
||||
#include "red_pixmap.h"
|
||||
|
||||
class RecurciveMutex;
|
||||
|
||||
class RedPixmapGdi: public RedPixmap {
|
||||
public:
|
||||
RedPixmapGdi(int width, int height, Format format, bool top_bottom);
|
||||
HDC get_dc();
|
||||
void *get_memptr();
|
||||
~RedPixmapGdi();
|
||||
RecurciveMutex& get_mutex();
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,49 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_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,
|
||||
RedWindow *win, RenderType rendertype);
|
||||
|
||||
void textures_lost();
|
||||
void touch_context();
|
||||
void update_texture(const SpiceRect *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
|
||||
@ -1,30 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_RED_PIXMAP_SW
|
||||
#define _H_RED_PIXMAP_SW
|
||||
|
||||
#include "red_pixmap.h"
|
||||
#include "red_window.h"
|
||||
|
||||
class RedPixmapSw: public RedPixmap {
|
||||
public:
|
||||
RedPixmapSw(int width, int height, Format format, bool top_bottom, RedWindow *window);
|
||||
~RedPixmapSw();
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,112 +0,0 @@
|
||||
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#ifdef WIN32
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
|
||||
#include "common/region.h"
|
||||
#define SPICE_CANVAS_INTERNAL
|
||||
#define SW_CANVAS_CACHE
|
||||
#include "common/sw_canvas.c"
|
||||
#undef SW_CANVAS_CACHE
|
||||
#undef SPICE_CANVAS_INTERNAL
|
||||
|
||||
#include "common.h"
|
||||
#include "red_window.h"
|
||||
#include "red_sw_canvas.h"
|
||||
#include "utils.h"
|
||||
#include "debug.h"
|
||||
#include "red_pixmap_sw.h"
|
||||
|
||||
SCanvas::SCanvas(bool onscreen,
|
||||
int width, int height, uint32_t format, RedWindow *win,
|
||||
PixmapCache& pixmap_cache, PaletteCache& palette_cache,
|
||||
GlzDecoderWindow &glz_decoder_window, SurfacesCache& csurfaces)
|
||||
: Canvas (pixmap_cache, palette_cache, glz_decoder_window, csurfaces)
|
||||
, _pixmap (0)
|
||||
{
|
||||
if (onscreen) {
|
||||
_pixmap = new RedPixmapSw(width, height,
|
||||
RedDrawable::format_from_surface(format),
|
||||
true, win);
|
||||
_canvas = canvas_create_for_data(width, height, format,
|
||||
_pixmap->get_data(),
|
||||
_pixmap->get_stride(),
|
||||
&pixmap_cache.base,
|
||||
&palette_cache.base,
|
||||
&csurfaces,
|
||||
&glz_decoder(),
|
||||
&jpeg_decoder(),
|
||||
&zlib_decoder());
|
||||
} else {
|
||||
_canvas = canvas_create(width, height, format,
|
||||
&pixmap_cache.base,
|
||||
&palette_cache.base,
|
||||
&csurfaces,
|
||||
&glz_decoder(),
|
||||
&jpeg_decoder(),
|
||||
&zlib_decoder());
|
||||
}
|
||||
if (_canvas == NULL) {
|
||||
THROW("create canvas failed");
|
||||
}
|
||||
}
|
||||
|
||||
SCanvas::~SCanvas()
|
||||
{
|
||||
_canvas->ops->destroy(_canvas);
|
||||
_canvas = NULL;
|
||||
if (_pixmap) {
|
||||
delete _pixmap;
|
||||
_pixmap = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void SCanvas::copy_pixels(const QRegion& region, RedDrawable& dest_dc)
|
||||
{
|
||||
pixman_box32_t *rects;
|
||||
int num_rects;
|
||||
|
||||
ASSERT(_pixmap != NULL);
|
||||
|
||||
rects = pixman_region32_rectangles((pixman_region32_t *)®ion, &num_rects);
|
||||
for (int i = 0; i < num_rects; i++) {
|
||||
SpiceRect r;
|
||||
|
||||
r.left = rects[i].x1;
|
||||
r.top = rects[i].y1;
|
||||
r.right = rects[i].x2;
|
||||
r.bottom = rects[i].y2;
|
||||
dest_dc.copy_pixels(*_pixmap, r.left, r.top, r);
|
||||
}
|
||||
}
|
||||
|
||||
void SCanvas::copy_pixels(const QRegion& region, RedDrawable* dest_dc, const PixmapHeader* pixmap)
|
||||
{
|
||||
copy_pixels(region, *dest_dc);
|
||||
}
|
||||
|
||||
CanvasType SCanvas::get_pixmap_type()
|
||||
{
|
||||
return CANVAS_TYPE_SW;
|
||||
}
|
||||
@ -1,50 +0,0 @@
|
||||
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_CCANVAS
|
||||
#define _H_CCANVAS
|
||||
|
||||
#include "canvas.h"
|
||||
#define SPICE_CANVAS_INTERNAL
|
||||
#define SW_CANVAS_CACHE
|
||||
#include "common/sw_canvas.h"
|
||||
#undef SW_CANVAS_CACHE
|
||||
#undef SPICE_CANVAS_INTERNAL
|
||||
|
||||
class RedPixmap;
|
||||
|
||||
class SCanvas: public Canvas {
|
||||
public:
|
||||
SCanvas(bool onscreen,
|
||||
int width, int height, uint32_t format, RedWindow *win,
|
||||
PixmapCache& pixmap_cache, PaletteCache& palette_cache,
|
||||
GlzDecoderWindow &glz_decoder_window, SurfacesCache &csurfaces);
|
||||
virtual ~SCanvas();
|
||||
|
||||
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 CanvasType get_pixmap_type();
|
||||
|
||||
private:
|
||||
RedPixmap *_pixmap;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,37 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_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
|
||||
@ -1,200 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_RED_WINDOW
|
||||
#define _H_RED_WINDOW
|
||||
|
||||
#include "red_drawable.h"
|
||||
#include <spice/protocol.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::string& title);
|
||||
void set_icon(Icon *icon);
|
||||
virtual RedDrawable::Format get_format();
|
||||
|
||||
enum Type {
|
||||
TYPE_INVALID,
|
||||
TYPE_NORMAL,
|
||||
TYPE_FULLSCREEN,
|
||||
};
|
||||
void set_type(Type type) { _type = type;}
|
||||
SpicePoint get_position();
|
||||
virtual SpicePoint get_size();
|
||||
bool get_mouse_anchor_point(SpicePoint& pt);
|
||||
|
||||
void set_mouse_position(int x, int y);
|
||||
void set_cursor(LocalCursor* local_cursor);
|
||||
void hide_cursor();
|
||||
void show_cursor();
|
||||
void capture_mouse();
|
||||
void release_mouse();
|
||||
void start_key_interception();
|
||||
void stop_key_interception();
|
||||
int set_menu(Menu* menu);
|
||||
|
||||
#ifdef USE_OPENGL
|
||||
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);
|
||||
|
||||
void set_type_gl();
|
||||
void unset_type_gl();
|
||||
|
||||
void swap_gl();
|
||||
#endif
|
||||
|
||||
int get_screen_num();
|
||||
|
||||
static void init();
|
||||
static void cleanup();
|
||||
|
||||
Listener& get_listener() { return _listener;}
|
||||
|
||||
private:
|
||||
void on_focus_in();
|
||||
void on_focus_out();
|
||||
void on_pointer_enter(int x, int y, unsigned int buttons_state);
|
||||
void on_pointer_leave();
|
||||
|
||||
void do_start_key_interception();
|
||||
void do_stop_key_interception();
|
||||
|
||||
private:
|
||||
Listener& _listener;
|
||||
SpicePoint _window_size;
|
||||
Type _type;
|
||||
LocalCursor* _local_cursor;
|
||||
bool _cursor_visible;
|
||||
bool _trace_key_interception;
|
||||
bool _key_interception_on;
|
||||
Menu* _menu;
|
||||
|
||||
friend class RedWindow_p;
|
||||
};
|
||||
|
||||
class RedWindow::Listener {
|
||||
public:
|
||||
virtual ~Listener() {}
|
||||
virtual void on_exposed_rect(const SpiceRect& area) = 0;
|
||||
|
||||
virtual void on_pointer_enter(int x, int y, unsigned int buttons_state) = 0;
|
||||
virtual void on_pointer_motion(int x, int y, unsigned int buttons_state) = 0;
|
||||
virtual void on_pointer_leave() = 0;
|
||||
virtual void on_mouse_button_press(SpiceMouseButton button, unsigned int buttons_state) = 0;
|
||||
virtual void on_mouse_button_release(SpiceMouseButton button, unsigned int buttons_state) = 0;
|
||||
|
||||
virtual void on_key_press(RedKey key) = 0;
|
||||
virtual void on_key_release(RedKey key) = 0;
|
||||
virtual void on_char(uint32_t ch) = 0;
|
||||
|
||||
virtual void on_deactivate() = 0;
|
||||
virtual void on_activate() = 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(SpiceRect& bbox) const;
|
||||
bool contains_point(int x, int y) const;
|
||||
|
||||
};*/
|
||||
|
||||
template <class REGION>
|
||||
static bool find_anchor_point(const REGION& region, SpicePoint& pt)
|
||||
{
|
||||
static const unsigned int lookup_size = 20;
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
SpiceRect 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;
|
||||
}
|
||||
|
||||
SpiceRect 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
|
||||
@ -1,944 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
#include "screen.h"
|
||||
#include "application.h"
|
||||
#include "screen_layer.h"
|
||||
#include "utils.h"
|
||||
#include "debug.h"
|
||||
#include "monitor.h"
|
||||
#include "red_pixmap_sw.h"
|
||||
#include "resource.h"
|
||||
#include "icon.h"
|
||||
|
||||
class UpdateEvent: public Event {
|
||||
public:
|
||||
UpdateEvent(int screen) : _screen (screen) {}
|
||||
|
||||
virtual void response(AbstractProcessLoop& events_loop)
|
||||
{
|
||||
Application* app = static_cast<Application*>(events_loop.get_owner());
|
||||
RedScreen* screen = app->find_screen(_screen);
|
||||
if (screen) {
|
||||
screen->update();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
int _screen;
|
||||
};
|
||||
|
||||
class LayerChangedEvent: public Event {
|
||||
public:
|
||||
LayerChangedEvent (int screen) : _screen (screen) {}
|
||||
|
||||
virtual void response(AbstractProcessLoop& events_loop)
|
||||
{
|
||||
Application* app = static_cast<Application*>(events_loop.get_owner());
|
||||
RedScreen* screen = app->find_screen(_screen);
|
||||
if (screen) {
|
||||
Lock lock(screen->_layer_changed_lock);
|
||||
screen->_active_layer_change_event = false;
|
||||
lock.unlock();
|
||||
if (screen->_pointer_on_screen) {
|
||||
screen->update_pointer_layer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
int _screen;
|
||||
};
|
||||
|
||||
|
||||
void UpdateTimer::response(AbstractProcessLoop& events_loop)
|
||||
{
|
||||
_screen->periodic_update();
|
||||
}
|
||||
|
||||
RedScreen::RedScreen(Application& owner, int id, const std::string& name, int width, int height)
|
||||
: _owner (owner)
|
||||
, _id (id)
|
||||
, _refs (1)
|
||||
, _window (*this)
|
||||
, _active (false)
|
||||
, _full_screen (false)
|
||||
, _out_of_sync (false)
|
||||
, _frame_area (false)
|
||||
, _periodic_update (false)
|
||||
, _key_interception (false)
|
||||
, _update_by_timer (true)
|
||||
, _size_locked (false)
|
||||
, _menu_needs_update (false)
|
||||
, _force_update_timer (0)
|
||||
, _update_timer (new UpdateTimer(this))
|
||||
, _composit_area (NULL)
|
||||
, _update_mark (1)
|
||||
, _monitor (NULL)
|
||||
, _default_cursor (NULL)
|
||||
, _inactive_cursor (NULL)
|
||||
, _pixel_format_index (0)
|
||||
, _update_interrupt_trigger (NULL)
|
||||
, _pointer_layer (NULL)
|
||||
, _mouse_captured (false)
|
||||
, _active_layer_change_event (false)
|
||||
, _pointer_on_screen (false)
|
||||
{
|
||||
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();
|
||||
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);
|
||||
update_menu();
|
||||
AutoRef<Icon> icon(Platform::load_icon(RED_ICON_RES_ID));
|
||||
_window.set_icon(*icon);
|
||||
_window.start_key_interception();
|
||||
}
|
||||
|
||||
RedScreen::~RedScreen()
|
||||
{
|
||||
bool captured = is_mouse_captured();
|
||||
_window.stop_key_interception();
|
||||
relase_mouse();
|
||||
destroy_composit_area();
|
||||
_owner.deactivate_interval_timer(*_update_timer);
|
||||
_owner.on_screen_destroyed(_id, captured);
|
||||
region_destroy(&_dirty_region);
|
||||
if (_default_cursor) {
|
||||
_default_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 RedPixmapSw(_size.x, _size.y, _window.get_format(),
|
||||
false, &_window);
|
||||
}
|
||||
|
||||
void RedScreen::adjust_window_rect(int x, int y)
|
||||
{
|
||||
_window.move_and_resize(x, y, _size.x, _size.y);
|
||||
}
|
||||
|
||||
void RedScreen::resize(int width, int height)
|
||||
{
|
||||
RecurciveLock lock(_update_lock);
|
||||
_size.x = width;
|
||||
_size.y = height;
|
||||
create_composit_area();
|
||||
if (_full_screen) {
|
||||
__show_full_screen();
|
||||
} else {
|
||||
bool cuptur = is_mouse_captured();
|
||||
if (cuptur) {
|
||||
relase_mouse();
|
||||
}
|
||||
_window.resize(_size.x, _size.y);
|
||||
if (_active && cuptur) {
|
||||
capture_mouse();
|
||||
}
|
||||
}
|
||||
notify_new_size();
|
||||
}
|
||||
|
||||
void RedScreen::lock_size()
|
||||
{
|
||||
ASSERT(!_size_locked);
|
||||
_size_locked = true;
|
||||
}
|
||||
|
||||
void RedScreen::unlock_size()
|
||||
{
|
||||
_size_locked = false;
|
||||
_owner.on_screen_unlocked(*this);
|
||||
}
|
||||
|
||||
void RedScreen::set_name(const std::string& name)
|
||||
{
|
||||
if (!name.empty()) {
|
||||
string_printf(_name, name.c_str(), _id);
|
||||
}
|
||||
_window.set_title(_name);
|
||||
}
|
||||
|
||||
void RedScreen::on_layer_changed(ScreenLayer& layer)
|
||||
{
|
||||
Lock lock(_layer_changed_lock);
|
||||
if (_active_layer_change_event) {
|
||||
return;
|
||||
}
|
||||
_active_layer_change_event = true;
|
||||
AutoRef<LayerChangedEvent> change_event(new LayerChangedEvent(_id));
|
||||
_owner.push_event(*change_event);
|
||||
}
|
||||
|
||||
void RedScreen::attach_layer(ScreenLayer& layer)
|
||||
{
|
||||
RecurciveLock lock(_update_lock);
|
||||
int order = layer.z_order();
|
||||
|
||||
if ((int)_layers.size() < order + 1) {
|
||||
_layers.resize(order + 1);
|
||||
}
|
||||
if (_layers[order]) {
|
||||
THROW("layer in use");
|
||||
}
|
||||
layer.set_screen(this);
|
||||
_layers[order] = &layer;
|
||||
ref();
|
||||
lock.unlock();
|
||||
layer.invalidate();
|
||||
if (_pointer_on_screen) {
|
||||
update_pointer_layer();
|
||||
}
|
||||
}
|
||||
|
||||
void RedScreen::detach_layer(ScreenLayer& layer)
|
||||
{
|
||||
bool need_pointer_layer_update = false;
|
||||
if (_pointer_layer == &layer) {
|
||||
_pointer_layer->on_pointer_leave();
|
||||
_pointer_layer = NULL;
|
||||
need_pointer_layer_update = true;
|
||||
}
|
||||
|
||||
RecurciveLock lock(_update_lock);
|
||||
|
||||
int order = layer.z_order();
|
||||
|
||||
if ((int)_layers.size() < order + 1 || _layers[order] != &layer) {
|
||||
THROW("not found");
|
||||
}
|
||||
QRegion layer_area;
|
||||
region_clone(&layer_area, &layer.area());
|
||||
_layers[order]->set_screen(NULL);
|
||||
_layers[order] = NULL;
|
||||
lock.unlock();
|
||||
invalidate(layer_area);
|
||||
region_destroy(&layer_area);
|
||||
unref();
|
||||
if (need_pointer_layer_update && !update_pointer_layer()) {
|
||||
_window.set_cursor(_inactive_cursor);
|
||||
}
|
||||
}
|
||||
|
||||
void RedScreen::composit_to_screen(RedDrawable& win_dc, const QRegion& region)
|
||||
{
|
||||
pixman_box32_t *rects;
|
||||
int num_rects;
|
||||
|
||||
rects = pixman_region32_rectangles((pixman_region32_t *)®ion, &num_rects);
|
||||
for (int i = 0; i < num_rects; i++) {
|
||||
SpiceRect r;
|
||||
|
||||
r.left = rects[i].x1;
|
||||
r.top = rects[i].y1;
|
||||
r.right = rects[i].x2;
|
||||
r.bottom = rects[i].y2;
|
||||
win_dc.copy_pixels(*_composit_area, r.left, r.top, r);
|
||||
}
|
||||
}
|
||||
|
||||
void RedScreen::notify_new_size()
|
||||
{
|
||||
for (int i = 0; i < (int)_layers.size(); i++) {
|
||||
if (!_layers[i]) {
|
||||
continue;
|
||||
}
|
||||
_layers[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;
|
||||
SpiceRect 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 = _layers.size() - 1; i >= 0; i--) {
|
||||
ScreenLayer* layer;
|
||||
|
||||
if (!(layer = _layers[i])) {
|
||||
continue;
|
||||
}
|
||||
layer->begin_update(direct_rgn, composit_rgn);
|
||||
}
|
||||
}
|
||||
|
||||
inline void RedScreen::update_done()
|
||||
{
|
||||
for (unsigned int i = 0; i < _layers.size(); i++) {
|
||||
ScreenLayer* layer;
|
||||
|
||||
if (!(layer = _layers[i])) {
|
||||
continue;
|
||||
}
|
||||
layer->on_update_completion(_update_mark - 1);
|
||||
}
|
||||
#ifdef USE_OPENGL
|
||||
_window.swap_gl();
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void RedScreen::update_composit(QRegion& composit_rgn)
|
||||
{
|
||||
erase_background(*_composit_area, composit_rgn);
|
||||
for (int i = 0; i < (int)_layers.size(); i++) {
|
||||
ScreenLayer* layer;
|
||||
|
||||
if (!(layer = _layers[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)_layers.size(); i++) {
|
||||
ScreenLayer* layer;
|
||||
|
||||
if (!(layer = _layers[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 (!_force_update_timer) {
|
||||
_owner.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();
|
||||
_owner.activate_interval_timer(*_update_timer, 1000 / 30);
|
||||
}
|
||||
|
||||
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 SpiceRect& 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 SpiceRect& rect, bool urgent)
|
||||
{
|
||||
uint64_t update_mark;
|
||||
if (_invalidate(rect, urgent, update_mark)) {
|
||||
if (!urgent && _update_by_timer) {
|
||||
activate_timer();
|
||||
} else {
|
||||
if (update_by_interrupt()) {
|
||||
interrupt_update();
|
||||
} else {
|
||||
AutoRef<UpdateEvent> update_event(new UpdateEvent(_id));
|
||||
_owner.push_event(*update_event);
|
||||
}
|
||||
}
|
||||
}
|
||||
return update_mark;
|
||||
}
|
||||
|
||||
void RedScreen::invalidate(const QRegion ®ion)
|
||||
{
|
||||
pixman_box32_t *rects, *end;
|
||||
int num_rects;
|
||||
|
||||
rects = pixman_region32_rectangles((pixman_region32_t *)®ion, &num_rects);
|
||||
end = rects + num_rects;
|
||||
|
||||
while (rects != end) {
|
||||
SpiceRect r;
|
||||
|
||||
r.left = rects->x1;
|
||||
r.top = rects->y1;
|
||||
r.right = rects->x2;
|
||||
r.bottom = rects->y2;
|
||||
rects++;
|
||||
|
||||
invalidate(r, false);
|
||||
}
|
||||
}
|
||||
|
||||
inline void RedScreen::erase_background(RedDrawable& dc, const QRegion& composit_rgn)
|
||||
{
|
||||
pixman_box32_t *rects;
|
||||
int num_rects;
|
||||
|
||||
rects = pixman_region32_rectangles((pixman_region32_t *)&composit_rgn, &num_rects);
|
||||
for (int i = 0; i < num_rects; i++) {
|
||||
SpiceRect r;
|
||||
|
||||
r.left = rects[i].x1;
|
||||
r.top = rects[i].y1;
|
||||
r.right = rects[i].x2;
|
||||
r.bottom = rects[i].y2;
|
||||
|
||||
dc.fill_rect(r, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void RedScreen::reset_mouse_pos()
|
||||
{
|
||||
_window.set_mouse_position(_mouse_anchor_point.x, _mouse_anchor_point.y);
|
||||
}
|
||||
|
||||
void RedScreen::capture_mouse()
|
||||
{
|
||||
if (_mouse_captured || !_window.get_mouse_anchor_point(_mouse_anchor_point)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_pointer_layer) {
|
||||
_pointer_layer->on_pointer_leave();
|
||||
_pointer_layer = NULL;
|
||||
}
|
||||
_pointer_on_screen = false;
|
||||
_mouse_captured = true;
|
||||
_window.hide_cursor();
|
||||
reset_mouse_pos();
|
||||
_window.capture_mouse();
|
||||
}
|
||||
|
||||
void RedScreen::relase_mouse()
|
||||
{
|
||||
if (!_mouse_captured) {
|
||||
return;
|
||||
}
|
||||
_mouse_captured = false;
|
||||
_window.release_mouse();
|
||||
update_pointer_layer();
|
||||
}
|
||||
|
||||
void RedScreen::set_cursor(LocalCursor* cursor)
|
||||
{
|
||||
if (_mouse_captured) {
|
||||
return;
|
||||
}
|
||||
|
||||
_window.set_cursor(cursor);
|
||||
}
|
||||
|
||||
void RedScreen::hide_cursor()
|
||||
{
|
||||
_window.hide_cursor();
|
||||
}
|
||||
|
||||
ScreenLayer* RedScreen::find_pointer_layer()
|
||||
{
|
||||
for (int i = _layers.size() - 1; i >= 0; i--) {
|
||||
ScreenLayer* layer;
|
||||
|
||||
if (!(layer = _layers[i])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (layer->pointer_test(_pointer_pos.x, _pointer_pos.y)) {
|
||||
return layer;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool RedScreen::update_pointer_layer()
|
||||
{
|
||||
ASSERT(!_mouse_captured);
|
||||
|
||||
ScreenLayer* now = find_pointer_layer();
|
||||
|
||||
if (now == _pointer_layer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_pointer_layer) {
|
||||
_pointer_layer->on_pointer_leave();
|
||||
}
|
||||
|
||||
_pointer_layer = find_pointer_layer();
|
||||
|
||||
if (_pointer_layer) {
|
||||
_pointer_layer->on_pointer_enter(_pointer_pos.x, _pointer_pos.y, _mouse_botton_state);
|
||||
} else {
|
||||
set_cursor(_inactive_cursor);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RedScreen::on_pointer_enter(int x, int y, unsigned int buttons_state)
|
||||
{
|
||||
if (_mouse_captured) {
|
||||
return;
|
||||
}
|
||||
|
||||
_pointer_on_screen = true;
|
||||
_pointer_pos.x = x;
|
||||
_pointer_pos.y = y;
|
||||
_mouse_botton_state = buttons_state;
|
||||
|
||||
ScreenLayer* layer = find_pointer_layer();
|
||||
if (!layer) {
|
||||
set_cursor(_inactive_cursor);
|
||||
return;
|
||||
}
|
||||
|
||||
_pointer_layer = layer;
|
||||
_pointer_layer->on_pointer_enter(_pointer_pos.x, _pointer_pos.y, buttons_state);
|
||||
|
||||
if (_full_screen) {
|
||||
/* allowing enterance to key interception mode without
|
||||
requiring the user to press the window
|
||||
*/
|
||||
activate();
|
||||
}
|
||||
}
|
||||
|
||||
void RedScreen::on_mouse_motion(int x, int y, unsigned int buttons_state)
|
||||
{
|
||||
if (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();
|
||||
}
|
||||
}
|
||||
|
||||
void RedScreen::on_pointer_motion(int x, int y, unsigned int buttons_state)
|
||||
{
|
||||
if (_mouse_captured) {
|
||||
on_mouse_motion(x, y, buttons_state);
|
||||
return;
|
||||
}
|
||||
|
||||
_pointer_pos.x = x;
|
||||
_pointer_pos.y = y;
|
||||
_mouse_botton_state = buttons_state;
|
||||
|
||||
if (update_pointer_layer() || !_pointer_layer) {
|
||||
return;
|
||||
}
|
||||
|
||||
_pointer_layer->on_pointer_motion(x, y, buttons_state);
|
||||
}
|
||||
|
||||
void RedScreen::on_mouse_button_press(SpiceMouseButton button, unsigned int buttons_state)
|
||||
{
|
||||
if (_mouse_captured) {
|
||||
_owner.on_mouse_down(button, buttons_state);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_pointer_layer) {
|
||||
return;
|
||||
}
|
||||
|
||||
_pointer_layer->on_mouse_button_press(button, buttons_state);
|
||||
}
|
||||
|
||||
void RedScreen::on_mouse_button_release(SpiceMouseButton button, unsigned int buttons_state)
|
||||
{
|
||||
if (_mouse_captured) {
|
||||
_owner.on_mouse_up(button, buttons_state);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_pointer_layer) {
|
||||
return;
|
||||
}
|
||||
_pointer_layer->on_mouse_button_release(button, buttons_state);
|
||||
}
|
||||
|
||||
void RedScreen::on_pointer_leave()
|
||||
{
|
||||
// ASSERT(!_mouse_captured);
|
||||
|
||||
if (_pointer_layer) {
|
||||
_pointer_layer->on_pointer_leave();
|
||||
_pointer_layer = NULL;
|
||||
}
|
||||
_pointer_on_screen = false;
|
||||
}
|
||||
|
||||
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_char(uint32_t ch)
|
||||
{
|
||||
_owner.on_char(ch);
|
||||
}
|
||||
|
||||
void RedScreen::on_deactivate()
|
||||
{
|
||||
relase_mouse();
|
||||
_active = false;
|
||||
_owner.on_deactivate_screen(this);
|
||||
}
|
||||
|
||||
void RedScreen::on_activate()
|
||||
{
|
||||
_active = true;
|
||||
_owner.on_activate_screen(this);
|
||||
}
|
||||
|
||||
void RedScreen::on_start_key_interception()
|
||||
{
|
||||
_key_interception = true;
|
||||
_owner.on_start_screen_key_interception(this);
|
||||
}
|
||||
|
||||
void RedScreen::on_stop_key_interception()
|
||||
{
|
||||
_key_interception = false;
|
||||
_owner.on_stop_screen_key_interception(this);
|
||||
}
|
||||
|
||||
void RedScreen::enter_modal_loop()
|
||||
{
|
||||
_force_update_timer++;
|
||||
activate_timer();
|
||||
}
|
||||
|
||||
void RedScreen::exit_modal_loop()
|
||||
{
|
||||
ASSERT(_force_update_timer > 0)
|
||||
_force_update_timer--;
|
||||
}
|
||||
|
||||
void RedScreen::pre_migrate()
|
||||
{
|
||||
for (int i = 0; i < (int)_layers.size(); i++) {
|
||||
if (!_layers[i]) {
|
||||
continue;
|
||||
}
|
||||
_layers[i]->pre_migrate();
|
||||
}
|
||||
}
|
||||
|
||||
void RedScreen::post_migrate()
|
||||
{
|
||||
for (int i = 0; i < (int)_layers.size(); i++) {
|
||||
if (!_layers[i]) {
|
||||
continue;
|
||||
}
|
||||
_layers[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();
|
||||
if (_menu_needs_update) {
|
||||
update_menu();
|
||||
}
|
||||
_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;
|
||||
}
|
||||
SpicePoint position = _monitor->get_position();
|
||||
SpicePoint 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);
|
||||
#ifndef WIN32
|
||||
/* performing hide during resolution changes resulted in
|
||||
missing WM_KEYUP events */
|
||||
hide();
|
||||
#endif
|
||||
save_position();
|
||||
_full_screen = true;
|
||||
__show_full_screen();
|
||||
}
|
||||
|
||||
void RedScreen::minimize()
|
||||
{
|
||||
_window.minimize();
|
||||
}
|
||||
|
||||
void RedScreen::position_full_screen(const SpicePoint& 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::update_menu()
|
||||
{
|
||||
AutoRef<Menu> menu(_owner.get_app_menu());
|
||||
int ret = _window.set_menu(*menu);
|
||||
_menu_needs_update = (ret != 0); /* try again if menu update failed */
|
||||
}
|
||||
|
||||
void RedScreen::on_exposed_rect(const SpiceRect& 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_OPENGL
|
||||
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(EventSources::Trigger *trigger)
|
||||
{
|
||||
_update_interrupt_trigger = trigger;
|
||||
}
|
||||
|
||||
bool RedScreen::update_by_interrupt()
|
||||
{
|
||||
return _update_interrupt_trigger != NULL;
|
||||
}
|
||||
|
||||
void RedScreen::interrupt_update()
|
||||
{
|
||||
_update_interrupt_trigger->trigger();
|
||||
}
|
||||
|
||||
#ifdef USE_OPENGL
|
||||
void RedScreen::set_type_gl()
|
||||
{
|
||||
_window.set_type_gl();
|
||||
}
|
||||
|
||||
void RedScreen::unset_type_gl()
|
||||
{
|
||||
_window.unset_type_gl();
|
||||
}
|
||||
#endif // USE_OPENGL
|
||||
211
client/screen.h
211
client/screen.h
@ -1,211 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_SCREEN
|
||||
#define _H_SCREEN
|
||||
|
||||
#include "common/region.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "red_key.h"
|
||||
#ifdef USE_OPENGL
|
||||
#include "GL/gl.h"
|
||||
#endif // USE_OPENGL
|
||||
|
||||
#include "red_window.h"
|
||||
#include "platform.h"
|
||||
#include "process_loop.h"
|
||||
#include "threads.h"
|
||||
#include "utils.h"
|
||||
|
||||
class Application;
|
||||
class ScreenLayer;
|
||||
class Monitor;
|
||||
class RedScreen;
|
||||
|
||||
enum {
|
||||
SCREEN_LAYER_DISPLAY,
|
||||
SCREEN_LAYER_CURSOR,
|
||||
SCREEN_LAYER_GUI_BARIER,
|
||||
SCREEN_LAYER_GUI,
|
||||
SCREEN_LAYER_INFO,
|
||||
};
|
||||
|
||||
class UpdateTimer: public Timer {
|
||||
public:
|
||||
UpdateTimer(RedScreen* screen) : _screen (screen) {}
|
||||
virtual void response(AbstractProcessLoop& events_loop);
|
||||
private:
|
||||
RedScreen* _screen;
|
||||
};
|
||||
|
||||
class RedScreen: public RedWindow::Listener {
|
||||
public:
|
||||
RedScreen(Application& owner, int id, const std::string& name, int width, int height);
|
||||
|
||||
RedScreen* ref();
|
||||
void unref();
|
||||
|
||||
void attach_layer(ScreenLayer& layer);
|
||||
void detach_layer(ScreenLayer& layer);
|
||||
void on_layer_changed(ScreenLayer& layer);
|
||||
/* When resizing on full screen mode, the monitor must be configured
|
||||
* correctly before calling resize*/
|
||||
void resize(int width, int height);
|
||||
void set_name(const std::string& name);
|
||||
uint64_t invalidate(const SpiceRect& rect, bool urgent);
|
||||
void invalidate(const QRegion ®ion);
|
||||
void capture_mouse();
|
||||
void relase_mouse();
|
||||
bool is_mouse_captured() { return _mouse_captured;}
|
||||
bool intercepts_sys_key() { return _key_interception;}
|
||||
SpicePoint get_size() { return _size;}
|
||||
bool has_monitor() { return _monitor != 0;}
|
||||
void lock_size();
|
||||
void unlock_size();
|
||||
bool is_size_locked() { return _size_locked;}
|
||||
void set_monitor(Monitor *monitor) { _monitor = monitor;}
|
||||
Monitor* get_monitor() { return _monitor;}
|
||||
RedWindow* get_window() { return &_window;}
|
||||
bool is_out_of_sync() { return _out_of_sync;}
|
||||
void set_cursor(LocalCursor* cursor);
|
||||
void hide_cursor();
|
||||
void exit_full_screen();
|
||||
void minimize();
|
||||
void show(bool activate, RedScreen* pos);
|
||||
void show_full_screen();
|
||||
void position_full_screen(const SpicePoint& position);
|
||||
void hide();
|
||||
void show();
|
||||
void activate();
|
||||
void external_show();
|
||||
void update_menu();
|
||||
|
||||
int get_id() { return _id;}
|
||||
int get_screen_id();
|
||||
|
||||
#ifdef USE_OPENGL
|
||||
void untouch_context();
|
||||
bool need_recreate_context_gl();
|
||||
void set_type_gl();
|
||||
void unset_type_gl();
|
||||
#endif
|
||||
void set_update_interrupt_trigger(EventSources::Trigger *trigger);
|
||||
bool update_by_interrupt();
|
||||
void interrupt_update();
|
||||
|
||||
void update();
|
||||
|
||||
private:
|
||||
friend class UpdateEvent;
|
||||
friend class UpdateTimer;
|
||||
|
||||
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();
|
||||
void __show_full_screen();
|
||||
|
||||
bool _invalidate(const SpiceRect& 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();
|
||||
ScreenLayer* find_pointer_layer();
|
||||
bool update_pointer_layer();
|
||||
|
||||
virtual void on_exposed_rect(const SpiceRect& area);
|
||||
virtual void on_pointer_enter(int x, int y, unsigned int buttons_state);
|
||||
virtual void on_pointer_motion(int x, int y, unsigned int buttons_state);
|
||||
virtual void on_pointer_leave();
|
||||
void on_mouse_motion(int x, int y, unsigned int buttons_state);
|
||||
virtual void on_mouse_button_press(SpiceMouseButton button, unsigned int buttons_state);
|
||||
virtual void on_mouse_button_release(SpiceMouseButton button, unsigned int buttons_state);
|
||||
|
||||
virtual void on_key_press(RedKey key);
|
||||
virtual void on_key_release(RedKey key);
|
||||
virtual void on_char(uint32_t ch);
|
||||
|
||||
virtual void on_deactivate();
|
||||
virtual void on_activate();
|
||||
|
||||
virtual void on_start_key_interception();
|
||||
virtual void on_stop_key_interception();
|
||||
virtual void enter_modal_loop();
|
||||
virtual void exit_modal_loop();
|
||||
|
||||
virtual void pre_migrate();
|
||||
virtual void post_migrate();
|
||||
|
||||
private:
|
||||
Application& _owner;
|
||||
int _id;
|
||||
AtomicCount _refs;
|
||||
std::string _name;
|
||||
RedWindow _window;
|
||||
std::vector<ScreenLayer*> _layers;
|
||||
QRegion _dirty_region;
|
||||
RecurciveMutex _update_lock;
|
||||
bool _active;
|
||||
bool _full_screen;
|
||||
bool _out_of_sync;
|
||||
bool _frame_area;
|
||||
bool _periodic_update;
|
||||
bool _key_interception;
|
||||
bool _update_by_timer;
|
||||
bool _size_locked;
|
||||
bool _menu_needs_update;
|
||||
int _force_update_timer;
|
||||
AutoRef<UpdateTimer> _update_timer;
|
||||
RedDrawable* _composit_area;
|
||||
uint64_t _update_mark;
|
||||
|
||||
SpicePoint _size;
|
||||
SpicePoint _origin;
|
||||
SpicePoint _mouse_anchor_point;
|
||||
SpicePoint _save_pos;
|
||||
Monitor* _monitor;
|
||||
|
||||
LocalCursor* _default_cursor;
|
||||
LocalCursor* _inactive_cursor;
|
||||
|
||||
int _pixel_format_index;
|
||||
EventSources::Trigger *_update_interrupt_trigger;
|
||||
|
||||
ScreenLayer* _pointer_layer;
|
||||
bool _mouse_captured;
|
||||
Mutex _layer_changed_lock;
|
||||
bool _active_layer_change_event;
|
||||
bool _pointer_on_screen;
|
||||
SpicePoint _pointer_pos;
|
||||
unsigned int _mouse_botton_state;
|
||||
|
||||
friend class LayerChangedEvent;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,239 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
#include "screen_layer.h"
|
||||
#include "utils.h"
|
||||
#include "screen.h"
|
||||
#include "application.h"
|
||||
#include "debug.h"
|
||||
|
||||
class AttachLayerEvent: public SyncEvent {
|
||||
public:
|
||||
AttachLayerEvent(ScreenLayer& layer, int screen_id)
|
||||
: _layer (layer)
|
||||
, _screen_id (screen_id)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void do_response(AbstractProcessLoop& events_loop);
|
||||
|
||||
private:
|
||||
ScreenLayer& _layer;
|
||||
int _screen_id;
|
||||
};
|
||||
|
||||
void AttachLayerEvent::do_response(AbstractProcessLoop& events_loop)
|
||||
{
|
||||
Application* app = (Application*)(events_loop.get_owner());
|
||||
AutoRef<RedScreen> screen(app->get_screen(_screen_id));
|
||||
(*screen)->attach_layer(_layer);
|
||||
}
|
||||
|
||||
class DetachLayerEvent: public SyncEvent {
|
||||
public:
|
||||
DetachLayerEvent(ScreenLayer& _layer) : _layer (_layer) {}
|
||||
|
||||
virtual void do_response(AbstractProcessLoop& events_loop);
|
||||
|
||||
private:
|
||||
ScreenLayer& _layer;
|
||||
};
|
||||
|
||||
void DetachLayerEvent::do_response(AbstractProcessLoop& events_loop)
|
||||
{
|
||||
_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 SpiceRect& r, bool urgent)
|
||||
{
|
||||
return _screen->invalidate(r, urgent);
|
||||
}
|
||||
|
||||
uint64_t ScreenLayer::invalidate(const SpiceRect& r, bool urgent)
|
||||
{
|
||||
if (!_screen) {
|
||||
return 0;
|
||||
}
|
||||
return invalidate_rect(r, urgent);
|
||||
}
|
||||
|
||||
void ScreenLayer::invalidate(const QRegion& region)
|
||||
{
|
||||
pixman_box32_t *rects, *end;
|
||||
int num_rects;
|
||||
|
||||
if (!_screen) {
|
||||
return;
|
||||
}
|
||||
|
||||
rects = pixman_region32_rectangles((pixman_region32_t *)®ion, &num_rects);
|
||||
end = rects + num_rects;
|
||||
|
||||
while (rects != end) {
|
||||
SpiceRect r;
|
||||
|
||||
r.left = rects->x1;
|
||||
r.top = rects->y1;
|
||||
r.right = rects->x2;
|
||||
r.bottom = rects->y2;
|
||||
rects++;
|
||||
|
||||
invalidate_rect(r, false);
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenLayer::invalidate()
|
||||
{
|
||||
invalidate(_area);
|
||||
}
|
||||
|
||||
void ScreenLayer::notify_changed()
|
||||
{
|
||||
if (_screen) {
|
||||
_screen->on_layer_changed(*this);
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenLayer::set_area(const QRegion& area)
|
||||
{
|
||||
Lock lock(_area_lock);
|
||||
invalidate();
|
||||
region_destroy(&_area);
|
||||
region_clone(&_area, &area);
|
||||
invalidate();
|
||||
notify_changed();
|
||||
}
|
||||
|
||||
void ScreenLayer::clear_area()
|
||||
{
|
||||
Lock lock(_area_lock);
|
||||
invalidate();
|
||||
region_clear(&_area);
|
||||
notify_changed();
|
||||
}
|
||||
|
||||
void ScreenLayer::set_rect_area(const SpiceRect& r)
|
||||
{
|
||||
Lock lock(_area_lock);
|
||||
invalidate();
|
||||
region_clear(&_area);
|
||||
region_add(&_area, &r);
|
||||
invalidate();
|
||||
notify_changed();
|
||||
}
|
||||
|
||||
void ScreenLayer::offset_area(int dx, int dy)
|
||||
{
|
||||
Lock lock(_area_lock);
|
||||
invalidate();
|
||||
region_offset(&_area, dx, dy);
|
||||
invalidate();
|
||||
notify_changed();
|
||||
}
|
||||
|
||||
void ScreenLayer::add_rect_area(const SpiceRect& r)
|
||||
{
|
||||
Lock lock(_area_lock);
|
||||
region_add(&_area, &r);
|
||||
notify_changed();
|
||||
}
|
||||
|
||||
void ScreenLayer::remove_rect_area(const SpiceRect& r)
|
||||
{
|
||||
Lock lock(_area_lock);
|
||||
invalidate();
|
||||
region_remove(&_area, &r);
|
||||
notify_changed();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
bool ScreenLayer::contains_point(int x, int y)
|
||||
{
|
||||
Lock lock(_area_lock);
|
||||
return !!region_contains_point(&_area, x, y);
|
||||
}
|
||||
|
||||
void ScreenLayer::attach_to_screen(Application& application, int screen_id)
|
||||
{
|
||||
if (_screen) {
|
||||
return;
|
||||
}
|
||||
AutoRef<AttachLayerEvent> event(new AttachLayerEvent(*this, screen_id));
|
||||
application.push_event(*event);
|
||||
(*event)->wait();
|
||||
if (!(*event)->success()) {
|
||||
THROW("attach failed");
|
||||
}
|
||||
ASSERT(_screen);
|
||||
}
|
||||
|
||||
void ScreenLayer::detach_from_screen(Application& application)
|
||||
{
|
||||
if (!_screen) {
|
||||
return;
|
||||
}
|
||||
AutoRef<DetachLayerEvent> event(new DetachLayerEvent(*this));
|
||||
application.push_event(*event);
|
||||
(*event)->wait();
|
||||
if (!(*event)->success()) {
|
||||
THROW("detach failed");
|
||||
}
|
||||
ASSERT(!_screen);
|
||||
}
|
||||
@ -1,87 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_SCREEN_LAYER
|
||||
#define _H_SCREEN_LAYER
|
||||
|
||||
#include "common/region.h"
|
||||
#include "threads.h"
|
||||
|
||||
class RedScreen;
|
||||
class Application;
|
||||
|
||||
class ScreenLayer {
|
||||
public:
|
||||
ScreenLayer(int z_order, bool opaque);
|
||||
virtual ~ScreenLayer();
|
||||
|
||||
void attach_to_screen(Application& application, int screen_id);
|
||||
void detach_from_screen(Application& application);
|
||||
|
||||
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 SpiceRect& r);
|
||||
void add_rect_area(const SpiceRect& r);
|
||||
void remove_rect_area(const SpiceRect& r);
|
||||
void begin_update(QRegion& direct_rgn, QRegion& composit_rgn);
|
||||
void invalidate();
|
||||
uint64_t invalidate(const SpiceRect& r, bool urgent = false);
|
||||
void invalidate(const QRegion& r);
|
||||
bool contains_point(int x, int y);
|
||||
|
||||
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) {}
|
||||
|
||||
virtual bool pointer_test(int x, int y) { return false;}
|
||||
virtual void on_pointer_enter(int x, int y, unsigned int buttons_state) {}
|
||||
virtual void on_pointer_motion(int x, int y, unsigned int buttons_state) {}
|
||||
virtual void on_pointer_leave() {}
|
||||
virtual void on_mouse_button_press(int button, int buttons_state) {}
|
||||
virtual void on_mouse_button_release(int button, int buttons_state) {}
|
||||
|
||||
private:
|
||||
uint64_t invalidate_rect(const SpiceRect& r, bool urgent);
|
||||
void notify_changed();
|
||||
|
||||
private:
|
||||
RedScreen* _screen;
|
||||
int _z_order;
|
||||
bool _opaque;
|
||||
bool _using_ogl;
|
||||
Mutex _area_lock;
|
||||
QRegion _area;
|
||||
QRegion _direct_area;
|
||||
QRegion _composit_area;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,223 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_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 Base = EmptyBase>
|
||||
class SharedCache : public Base {
|
||||
public:
|
||||
SharedCache()
|
||||
: _aborting (false)
|
||||
{
|
||||
memset(_hash, 0, sizeof(_hash));
|
||||
}
|
||||
|
||||
~SharedCache()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void add(uint64_t id, T* data, bool is_lossy = FALSE)
|
||||
{
|
||||
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, is_lossy);
|
||||
_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);
|
||||
}
|
||||
}
|
||||
|
||||
T* get_lossless(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;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// item has been retreived. Now checking if lossless
|
||||
for (;;) {
|
||||
if (item->lossy) {
|
||||
if (_aborting) {
|
||||
THROW("%s aborting", Treat::name());
|
||||
}
|
||||
_replace_data_cond.wait(lock);
|
||||
continue;
|
||||
}
|
||||
|
||||
return Treat::get(item->data);
|
||||
}
|
||||
}
|
||||
|
||||
void replace(uint64_t id, T* data, bool is_lossy = FALSE)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
item->replace(data, is_lossy);
|
||||
break;
|
||||
}
|
||||
_replace_data_cond.notify_all();
|
||||
}
|
||||
|
||||
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 %" PRIu64 ", 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, bool is_lossy = FALSE)
|
||||
: id (in_id)
|
||||
, refs (1)
|
||||
, next (NULL)
|
||||
, data (Treat::get(data))
|
||||
, lossy (is_lossy) {}
|
||||
|
||||
~Item()
|
||||
{
|
||||
Treat::release(data);
|
||||
}
|
||||
|
||||
void replace(T* new_data, bool is_lossy = FALSE)
|
||||
{
|
||||
Treat::release(data);
|
||||
data = Treat::get(new_data);
|
||||
lossy = is_lossy;
|
||||
}
|
||||
|
||||
uint64_t id;
|
||||
int refs;
|
||||
Item* next;
|
||||
T* data;
|
||||
bool lossy;
|
||||
};
|
||||
|
||||
Item* _hash[HASH_SIZE];
|
||||
Mutex _lock;
|
||||
Condition _new_item_cond;
|
||||
Condition _replace_data_cond;
|
||||
bool _aborting;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,553 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2010 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <spice/enums.h>
|
||||
#include "common/mutex.h"
|
||||
|
||||
#include "red_client.h"
|
||||
|
||||
extern "C" {
|
||||
#include <vscard_common.h>
|
||||
#include <vreader.h>
|
||||
#include <vcard_emul.h>
|
||||
#include <vevent.h>
|
||||
}
|
||||
|
||||
#include "smartcard_channel.h"
|
||||
|
||||
#define APDUBufSize 270
|
||||
|
||||
#define MAX_ATR_LEN 40
|
||||
|
||||
//#define DEBUG_SMARTCARD
|
||||
|
||||
#ifdef DEBUG_SMARTCARD
|
||||
void PrintByteArray(uint8_t *arrBytes, unsigned int nSize)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i < nSize; i++) {
|
||||
DBG(0, "%X ", arrBytes[i]);
|
||||
}
|
||||
DBG(0, "\n");
|
||||
}
|
||||
#define DEBUG_PRINT_BYTE_ARRAY(arrBytes, nSize) PrintByteArray(arrBytes, nSize)
|
||||
#else
|
||||
#define DEBUG_PRINT_BYTE_ARRAY(arrBytes, nSize)
|
||||
#endif
|
||||
|
||||
SmartCardChannel* g_smartcard_channel = NULL; // used for insert/remove of virtual card. Can be
|
||||
// changed if we let Application store the SmartCard instance.
|
||||
|
||||
class SmartCardHandler: public MessageHandlerImp<SmartCardChannel, SPICE_CHANNEL_SMARTCARD> {
|
||||
public:
|
||||
SmartCardHandler(SmartCardChannel& channel)
|
||||
: MessageHandlerImp<SmartCardChannel, SPICE_CHANNEL_SMARTCARD>(channel) {}
|
||||
};
|
||||
|
||||
|
||||
SmartCardChannel::SmartCardChannel(RedClient& client, uint32_t id)
|
||||
: RedChannel(client, SPICE_CHANNEL_SMARTCARD, id, new SmartCardHandler(*this))
|
||||
, _next_sync_vevent(VEVENT_LAST)
|
||||
{
|
||||
SmartCardHandler* handler = static_cast<SmartCardHandler*>(get_message_handler());
|
||||
|
||||
g_smartcard_channel = this;
|
||||
handler->set_handler(SPICE_MSG_SMARTCARD_DATA,
|
||||
&SmartCardChannel::handle_smartcard_data);
|
||||
}
|
||||
|
||||
ReaderData* SmartCardChannel::reader_data_from_vreader(VReader* vreader)
|
||||
{
|
||||
if (vreader == NULL) {
|
||||
assert(_readers_by_vreader.size() > 0);
|
||||
return _readers_by_vreader.begin()->second;
|
||||
}
|
||||
if (_readers_by_vreader.count(vreader) > 0) {
|
||||
return _readers_by_vreader.find(vreader)->second;
|
||||
}
|
||||
assert(_unallocated_readers_by_vreader.count(vreader) > 0);
|
||||
return _unallocated_readers_by_vreader.find(vreader)->second;
|
||||
}
|
||||
|
||||
ReaderData* SmartCardChannel::reader_data_from_reader_id(uint32_t reader_id)
|
||||
{
|
||||
if (_readers_by_id.count(reader_id) > 0) {
|
||||
return _readers_by_id.find(reader_id)->second;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** On VEVENT_READER_INSERT we call this, send a VSC_ReaderAdd, and wait for a VSC_ReaderAddResponse
|
||||
*/
|
||||
void SmartCardChannel::add_unallocated_reader(VReader* vreader, const char* name)
|
||||
{
|
||||
ReaderData* data = new ReaderData();
|
||||
|
||||
data->vreader = vreader;
|
||||
data->reader_id = VSCARD_UNDEFINED_READER_ID;
|
||||
data->name = spice_strdup(name);
|
||||
_unallocated_readers_by_vreader.insert(std::pair<VReader*, ReaderData*>(vreader, data));
|
||||
LOG_INFO("adding unallocated reader %p", data);
|
||||
}
|
||||
|
||||
/** called upon the VSC_ReaderAddResponse
|
||||
*/
|
||||
ReaderData* SmartCardChannel::add_reader(uint32_t reader_id)
|
||||
{
|
||||
ReaderData* data;
|
||||
size_t unallocated = _unallocated_readers_by_vreader.size();
|
||||
|
||||
assert(unallocated > 0);
|
||||
data = _unallocated_readers_by_vreader.begin()->second;
|
||||
data->reader_id = reader_id;
|
||||
LOG_INFO("adding %p->%d", data, reader_id);
|
||||
_readers_by_vreader.insert(
|
||||
std::pair<VReader*, ReaderData*>(data->vreader, data));
|
||||
assert(_readers_by_vreader.count(data->vreader) == 1);
|
||||
_readers_by_id.insert(std::pair<uint32_t, ReaderData*>(reader_id, data));
|
||||
assert(_readers_by_id.count(reader_id) == 1);
|
||||
_unallocated_readers_by_vreader.erase(_unallocated_readers_by_vreader.begin());
|
||||
assert(_unallocated_readers_by_vreader.size() == unallocated - 1);
|
||||
assert(_unallocated_readers_by_vreader.count(data->vreader) == 0);
|
||||
return data;
|
||||
}
|
||||
|
||||
void* SmartCardChannel::cac_card_events_thread_entry(void* data)
|
||||
{
|
||||
static_cast<SmartCardChannel*>(data)->cac_card_events_thread_main();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
VEventEvent::VEventEvent(SmartCardChannel* smartcard_channel, VEvent* vevent)
|
||||
: _smartcard_channel(smartcard_channel)
|
||||
, _vreader(vevent->reader)
|
||||
, _vevent(vevent)
|
||||
{
|
||||
}
|
||||
|
||||
VEventEvent::~VEventEvent()
|
||||
{
|
||||
vevent_delete(_vevent);
|
||||
}
|
||||
|
||||
void ReaderAddEvent::response(AbstractProcessLoop& events_loop)
|
||||
{
|
||||
static int num = 0;
|
||||
char name[20];
|
||||
|
||||
sprintf(name, "test%4d", num++);
|
||||
_smartcard_channel->add_unallocated_reader(_vreader, name);
|
||||
_smartcard_channel->send_reader_added(name);
|
||||
}
|
||||
|
||||
void ReaderRemoveEvent::response(AbstractProcessLoop& events_loop)
|
||||
{
|
||||
ReaderData* data;
|
||||
|
||||
data = _smartcard_channel->reader_data_from_vreader(_vreader);
|
||||
_smartcard_channel->send_reader_removed(data->reader_id);
|
||||
_smartcard_channel->remove_reader(data);
|
||||
}
|
||||
|
||||
void CardInsertEvent::response(AbstractProcessLoop& events_loop)
|
||||
{
|
||||
ReaderData* data = _smartcard_channel->reader_data_from_vreader(
|
||||
_vreader);
|
||||
|
||||
if (data->reader_id == VSCARD_UNDEFINED_READER_ID) {
|
||||
data->card_insert_pending = true;
|
||||
} else {
|
||||
_smartcard_channel->send_atr(_vreader);
|
||||
}
|
||||
}
|
||||
|
||||
void CardRemoveEvent::response(AbstractProcessLoop& events_loop)
|
||||
{
|
||||
ReaderData* data = _smartcard_channel->reader_data_from_vreader(
|
||||
_vreader);
|
||||
|
||||
ASSERT(data->reader_id != VSCARD_UNDEFINED_READER_ID);
|
||||
_smartcard_channel->send_message(data->reader_id, VSC_CardRemove, NULL, 0);
|
||||
}
|
||||
|
||||
void SmartCardChannel::remove_reader(ReaderData* data)
|
||||
{
|
||||
// TODO - untested code (caccard doesn't produce these events yet)
|
||||
if (_readers_by_vreader.count(data->vreader) > 0) {
|
||||
_readers_by_vreader.erase(_readers_by_vreader.find(data->vreader));
|
||||
_readers_by_id.erase(_readers_by_id.find(data->reader_id));
|
||||
} else {
|
||||
_unallocated_readers_by_vreader.erase(
|
||||
_unallocated_readers_by_vreader.find(data->vreader));
|
||||
}
|
||||
free(data->name);
|
||||
delete data;
|
||||
}
|
||||
|
||||
/* Sync events need to be sent one by one, waiting for VSC_Error
|
||||
* messages from the server in between. */
|
||||
void SmartCardChannel::push_sync_event(VEventType type, Event *event)
|
||||
{
|
||||
event->ref();
|
||||
_sync_events.push_back(SmartCardEvent(type, event));
|
||||
if (_next_sync_vevent != VEVENT_LAST) {
|
||||
return;
|
||||
}
|
||||
send_next_sync_event();
|
||||
}
|
||||
|
||||
void SmartCardChannel::send_next_sync_event()
|
||||
{
|
||||
if (_sync_events.empty()) {
|
||||
_next_sync_vevent = VEVENT_LAST;
|
||||
return;
|
||||
}
|
||||
SmartCardEvent sync_event = _sync_events.front();
|
||||
_sync_events.pop_front();
|
||||
get_client().push_event(sync_event.second);
|
||||
sync_event.second->unref();
|
||||
_next_sync_vevent = sync_event.first;
|
||||
}
|
||||
|
||||
void SmartCardChannel::handle_reader_add_response(VSCMsgHeader *vheader,
|
||||
VSCMsgError *error)
|
||||
{
|
||||
ReaderData* data;
|
||||
|
||||
if (error->code == VSC_SUCCESS) {
|
||||
data = add_reader(vheader->reader_id);
|
||||
if (data->card_insert_pending) {
|
||||
data->card_insert_pending = false;
|
||||
send_atr(data->vreader);
|
||||
}
|
||||
} else {
|
||||
LOG_WARN("VSC Error: reader %d, code %d",
|
||||
vheader->reader_id, error->code);
|
||||
}
|
||||
}
|
||||
|
||||
void SmartCardChannel::handle_error_message(VSCMsgHeader *vheader,
|
||||
VSCMsgError *error)
|
||||
{
|
||||
switch (_next_sync_vevent) {
|
||||
case VEVENT_READER_INSERT:
|
||||
handle_reader_add_response(vheader, error);
|
||||
break;
|
||||
case VEVENT_CARD_INSERT:
|
||||
case VEVENT_CARD_REMOVE:
|
||||
case VEVENT_READER_REMOVE:
|
||||
break;
|
||||
default:
|
||||
LOG_WARN("Unexpected Error message: %d", error->code);
|
||||
}
|
||||
send_next_sync_event();
|
||||
}
|
||||
|
||||
void SmartCardChannel::cac_card_events_thread_main()
|
||||
{
|
||||
VEvent *vevent = NULL;
|
||||
bool cont = true;
|
||||
|
||||
while (cont) {
|
||||
vevent = vevent_wait_next_vevent();
|
||||
if (vevent == NULL) {
|
||||
break;
|
||||
}
|
||||
switch (vevent->type) {
|
||||
case VEVENT_READER_INSERT:
|
||||
LOG_INFO("VEVENT_READER_INSERT");
|
||||
{
|
||||
AutoRef<ReaderAddEvent> event(new ReaderAddEvent(this, vevent));
|
||||
push_sync_event(vevent->type, *event);
|
||||
}
|
||||
break;
|
||||
case VEVENT_READER_REMOVE:
|
||||
LOG_INFO("VEVENT_READER_REMOVE");
|
||||
{
|
||||
AutoRef<ReaderRemoveEvent> event(new ReaderRemoveEvent(this, vevent));
|
||||
push_sync_event(vevent->type, *event);
|
||||
}
|
||||
break;
|
||||
case VEVENT_CARD_INSERT:
|
||||
LOG_INFO("VEVENT_CARD_INSERT");
|
||||
{
|
||||
AutoRef<CardInsertEvent> event(new CardInsertEvent(this, vevent));
|
||||
push_sync_event(vevent->type, *event);
|
||||
}
|
||||
break;
|
||||
case VEVENT_CARD_REMOVE:
|
||||
LOG_INFO("VEVENT_CARD_REMOVE");
|
||||
{
|
||||
AutoRef<CardRemoveEvent> event(new CardRemoveEvent(this, vevent));
|
||||
push_sync_event(vevent->type, *event);
|
||||
}
|
||||
break;
|
||||
case VEVENT_LAST:
|
||||
cont = false;
|
||||
default:
|
||||
/* anything except VEVENT_LAST and default
|
||||
* gets to VEventEvent which does vevent_delete in VEventEvent~ */
|
||||
vevent_delete(vevent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void virtual_card_insert()
|
||||
{
|
||||
if (g_smartcard_channel == NULL) {
|
||||
return;
|
||||
}
|
||||
g_smartcard_channel->virtual_card_insert();
|
||||
}
|
||||
|
||||
void SmartCardChannel::virtual_card_insert()
|
||||
{
|
||||
if (_readers_by_id.size() == 0) {
|
||||
return;
|
||||
}
|
||||
vcard_emul_force_card_insert(_readers_by_id.begin()->second->vreader);
|
||||
}
|
||||
|
||||
void virtual_card_remove()
|
||||
{
|
||||
if (g_smartcard_channel == NULL) {
|
||||
return;
|
||||
}
|
||||
g_smartcard_channel->virtual_card_remove();
|
||||
}
|
||||
|
||||
void SmartCardChannel::virtual_card_remove()
|
||||
{
|
||||
if (_readers_by_id.size() == 0) {
|
||||
return;
|
||||
}
|
||||
vcard_emul_force_card_remove(_readers_by_id.begin()->second->vreader);
|
||||
}
|
||||
|
||||
#define CERTIFICATES_DEFAULT_DB "/etc/pki/nssdb"
|
||||
#define CERTIFICATES_ARGS_TEMPLATE "db=\"%s\" use_hw=no soft=(,Virtual Card,CAC,,%s,%s,%s)"
|
||||
|
||||
SmartcardOptions::SmartcardOptions() :
|
||||
dbname(CERTIFICATES_DEFAULT_DB),
|
||||
enable(false)
|
||||
{
|
||||
}
|
||||
|
||||
static VCardEmulError init_vcard_local_certs(const char* dbname, const char* cert1,
|
||||
const char* cert2, const char* cert3)
|
||||
{
|
||||
char emul_args[200];
|
||||
VCardEmulOptions *options = NULL;
|
||||
|
||||
snprintf(emul_args, sizeof(emul_args) - 1, CERTIFICATES_ARGS_TEMPLATE,
|
||||
dbname, cert1, cert2, cert3);
|
||||
options = vcard_emul_options(emul_args);
|
||||
if (options == NULL) {
|
||||
LOG_WARN("not using certificates due to initialization error");
|
||||
}
|
||||
return vcard_emul_init(options);
|
||||
}
|
||||
|
||||
static bool g_vcard_inited = false;
|
||||
|
||||
void smartcard_init(const SmartcardOptions* options)
|
||||
{
|
||||
if (g_vcard_inited) {
|
||||
return;
|
||||
}
|
||||
if (options->certs.size() == 3) {
|
||||
const char* dbname = options->dbname.c_str();
|
||||
if (init_vcard_local_certs(dbname, options->certs[0].c_str(),
|
||||
options->certs[1].c_str(), options->certs[2].c_str()) != VCARD_EMUL_OK) {
|
||||
throw Exception("smartcard: emulated card initialization failed (check certs/db)");
|
||||
}
|
||||
} else {
|
||||
if (options->certs.size() > 0) {
|
||||
LOG_WARN("Ignoring smartcard certificates - must be exactly three for virtual card emulation");
|
||||
}
|
||||
if (vcard_emul_init(NULL) != VCARD_EMUL_OK) {
|
||||
throw Exception("smartcard: vcard initialization failed (check hardware/drivers)");
|
||||
}
|
||||
}
|
||||
g_vcard_inited = true;
|
||||
}
|
||||
|
||||
void SmartCardChannel::on_connect()
|
||||
{
|
||||
_event_thread = new Thread(SmartCardChannel::cac_card_events_thread_entry, this);
|
||||
}
|
||||
|
||||
void SmartCardChannel::on_disconnect()
|
||||
{
|
||||
VEvent *stop_event;
|
||||
|
||||
if (_event_thread == NULL) {
|
||||
return;
|
||||
}
|
||||
stop_event = vevent_new(VEVENT_LAST, NULL, NULL);
|
||||
vevent_queue_vevent(stop_event);
|
||||
_event_thread->join();
|
||||
delete _event_thread;
|
||||
_event_thread = NULL;
|
||||
}
|
||||
|
||||
|
||||
void SmartCardChannel::send_reader_removed(uint32_t reader_id)
|
||||
{
|
||||
send_message(reader_id, VSC_ReaderRemove, NULL, 0);
|
||||
}
|
||||
|
||||
void SmartCardChannel::send_reader_added(const char* reader_name)
|
||||
{
|
||||
send_message(VSCARD_UNDEFINED_READER_ID,
|
||||
VSC_ReaderAdd, (uint8_t*)reader_name, strlen(reader_name));
|
||||
}
|
||||
|
||||
void SmartCardChannel::send_atr(VReader* vreader)
|
||||
{
|
||||
unsigned char atr[ MAX_ATR_LEN];
|
||||
int atr_len = MAX_ATR_LEN;
|
||||
uint32_t reader_id = reader_data_from_vreader(vreader)->reader_id;
|
||||
|
||||
assert(reader_id != VSCARD_UNDEFINED_READER_ID);
|
||||
vreader_power_on(vreader, atr, &atr_len);
|
||||
DBG(0, "ATR: ");
|
||||
DEBUG_PRINT_BYTE_ARRAY(atr, atr_len);
|
||||
send_message(reader_id, VSC_ATR, (uint8_t*)atr, atr_len);
|
||||
}
|
||||
|
||||
void SmartCardChannel::send_message(uint32_t reader_id, VSCMsgType type, uint8_t* data, uint32_t len)
|
||||
{
|
||||
VSCMsgHeader mhHeader;
|
||||
Message* msg = new Message(SPICE_MSGC_SMARTCARD_DATA);
|
||||
SpiceMarshaller* m = msg->marshaller();
|
||||
|
||||
mhHeader.type = type;
|
||||
mhHeader.length = len;
|
||||
mhHeader.reader_id = reader_id;
|
||||
spice_marshaller_add(m, (uint8_t*)&mhHeader, sizeof(mhHeader));
|
||||
spice_marshaller_add(m, data, len);
|
||||
post_message(msg);
|
||||
}
|
||||
|
||||
VSCMessageEvent::VSCMessageEvent(SmartCardChannel* smartcard_channel,
|
||||
VSCMsgHeader* vheader)
|
||||
: _smartcard_channel(smartcard_channel)
|
||||
, _vheader(NULL)
|
||||
{
|
||||
_vheader = (VSCMsgHeader*)spice_memdup(vheader,
|
||||
sizeof(VSCMsgHeader) + vheader->length);
|
||||
ASSERT(_vheader);
|
||||
}
|
||||
|
||||
VSCMessageEvent::~VSCMessageEvent()
|
||||
{
|
||||
free(_vheader);
|
||||
}
|
||||
|
||||
void VSCMessageEvent::response(AbstractProcessLoop& loop)
|
||||
{
|
||||
static int recv_count = 0;
|
||||
int dwSendLength;
|
||||
int dwRecvLength;
|
||||
uint8_t* pbSendBuffer = _vheader->data;
|
||||
uint8_t pbRecvBuffer[APDUBufSize+sizeof(uint32_t)];
|
||||
VReaderStatus reader_status;
|
||||
uint32_t rv;
|
||||
|
||||
switch (_vheader->type) {
|
||||
case VSC_APDU:
|
||||
break;
|
||||
case VSC_Error:
|
||||
_smartcard_channel->handle_error_message(
|
||||
_vheader,
|
||||
(VSCMsgError*)_vheader->data);
|
||||
return;
|
||||
case VSC_Init:
|
||||
break;
|
||||
default:
|
||||
LOG_WARN("unhandled VSC %d of length %d, reader %d",
|
||||
_vheader->type, _vheader->length, _vheader->reader_id);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Transmit recieved APDU */
|
||||
dwSendLength = _vheader->length;
|
||||
dwRecvLength = sizeof(pbRecvBuffer);
|
||||
|
||||
DBG(0, " %3d: recv APDU: ", recv_count++);
|
||||
DEBUG_PRINT_BYTE_ARRAY(pbSendBuffer, _vheader->length);
|
||||
|
||||
ReaderData* reader_data = _smartcard_channel->reader_data_from_reader_id(
|
||||
_vheader->reader_id);
|
||||
if (reader_data == NULL) {
|
||||
LOG_WARN("got message for non existant reader");
|
||||
return;
|
||||
}
|
||||
|
||||
VReader* vreader = reader_data->vreader;
|
||||
|
||||
reader_status = vreader_xfr_bytes(vreader,
|
||||
pbSendBuffer, dwSendLength,
|
||||
pbRecvBuffer, &dwRecvLength);
|
||||
if (reader_status == VREADER_OK) {
|
||||
DBG(0, " sent APDU: ");
|
||||
DEBUG_PRINT_BYTE_ARRAY(pbRecvBuffer, dwRecvLength);
|
||||
_smartcard_channel->send_message (
|
||||
_vheader->reader_id,
|
||||
VSC_APDU,
|
||||
pbRecvBuffer,
|
||||
dwRecvLength
|
||||
);
|
||||
} else {
|
||||
rv = reader_status; /* warning: not meaningful */
|
||||
_smartcard_channel->send_message (
|
||||
_vheader->reader_id,
|
||||
VSC_Error,
|
||||
(uint8_t*)&rv,
|
||||
sizeof (uint32_t)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void SmartCardChannel::handle_smartcard_data(RedPeer::InMessage* message)
|
||||
{
|
||||
VSCMsgHeader* mhHeader = (VSCMsgHeader*)(message->data());
|
||||
|
||||
AutoRef<VSCMessageEvent> event(new VSCMessageEvent(this, mhHeader));
|
||||
get_client().push_event(*event);
|
||||
}
|
||||
|
||||
class SmartCardFactory: public ChannelFactory {
|
||||
public:
|
||||
SmartCardFactory() : ChannelFactory(SPICE_CHANNEL_SMARTCARD) {}
|
||||
virtual RedChannel* construct(RedClient& client, uint32_t id)
|
||||
{
|
||||
return new SmartCardChannel(client, id);
|
||||
}
|
||||
};
|
||||
|
||||
static SmartCardFactory factory;
|
||||
|
||||
ChannelFactory& SmartCardChannel::Factory()
|
||||
{
|
||||
return factory;
|
||||
}
|
||||
@ -1,159 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2010 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef __SMART_CARD_H__
|
||||
#define __SMART_CARD_H__
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <vreadert.h>
|
||||
#include <vscard_common.h>
|
||||
#include <eventt.h>
|
||||
|
||||
#include "red_channel.h"
|
||||
#include "red_peer.h"
|
||||
|
||||
class Application;
|
||||
|
||||
struct SmartcardOptions {
|
||||
std::vector<std::string> certs;
|
||||
std::string dbname;
|
||||
bool enable;
|
||||
SmartcardOptions();
|
||||
};
|
||||
|
||||
void smartcard_init(const SmartcardOptions* options);
|
||||
|
||||
struct ReaderData {
|
||||
ReaderData() :
|
||||
vreader(NULL),
|
||||
reader_id(VSCARD_UNDEFINED_READER_ID),
|
||||
name(NULL),
|
||||
card_insert_pending(false)
|
||||
{}
|
||||
VReader *vreader;
|
||||
uint32_t reader_id;
|
||||
char* name;
|
||||
bool card_insert_pending;
|
||||
};
|
||||
|
||||
void virtual_card_remove();
|
||||
void virtual_card_insert();
|
||||
|
||||
class SmartCardChannel;
|
||||
|
||||
class VEventEvent : public Event {
|
||||
public:
|
||||
VEventEvent(SmartCardChannel* smartcard_channel, VEvent* vevent);
|
||||
~VEventEvent();
|
||||
SmartCardChannel* _smartcard_channel;
|
||||
VReader* _vreader;
|
||||
VEvent* _vevent;
|
||||
};
|
||||
|
||||
class ReaderAddEvent: public VEventEvent {
|
||||
public:
|
||||
ReaderAddEvent(SmartCardChannel* smartcard_channel, VEvent* vevent)
|
||||
: VEventEvent(smartcard_channel, vevent) {}
|
||||
virtual void response(AbstractProcessLoop& events_loop);
|
||||
};
|
||||
|
||||
class ReaderRemoveEvent: public VEventEvent {
|
||||
public:
|
||||
ReaderRemoveEvent(SmartCardChannel* smartcard_channel, VEvent* vevent)
|
||||
: VEventEvent(smartcard_channel, vevent) {}
|
||||
virtual void response(AbstractProcessLoop& events_loop);
|
||||
};
|
||||
|
||||
class CardInsertEvent: public VEventEvent {
|
||||
public:
|
||||
CardInsertEvent(SmartCardChannel* smartcard_channel, VEvent* vevent)
|
||||
: VEventEvent(smartcard_channel, vevent) {}
|
||||
virtual void response(AbstractProcessLoop& events_loop);
|
||||
};
|
||||
|
||||
class CardRemoveEvent: public VEventEvent {
|
||||
public:
|
||||
CardRemoveEvent(SmartCardChannel* smartcard_channel, VEvent* vevent)
|
||||
: VEventEvent(smartcard_channel, vevent) {}
|
||||
virtual void response(AbstractProcessLoop& events_loop);
|
||||
};
|
||||
|
||||
class VSCMessageEvent: public Event {
|
||||
public:
|
||||
VSCMessageEvent(SmartCardChannel* smartcard_channel,
|
||||
VSCMsgHeader* vheader);
|
||||
~VSCMessageEvent();
|
||||
SmartCardChannel* _smartcard_channel;
|
||||
VSCMsgHeader* _vheader;
|
||||
virtual void response(AbstractProcessLoop& events_loop);
|
||||
};
|
||||
|
||||
typedef std::pair<VEventType, Event*> SmartCardEvent;
|
||||
|
||||
class SmartCardChannel : public RedChannel {
|
||||
|
||||
public:
|
||||
SmartCardChannel(RedClient& client, uint32_t id);
|
||||
void handle_smartcard_data(RedPeer::InMessage* message);
|
||||
|
||||
void virtual_card_remove();
|
||||
void virtual_card_insert();
|
||||
static ChannelFactory& Factory();
|
||||
protected:
|
||||
virtual void on_connect();
|
||||
virtual void on_disconnect();
|
||||
|
||||
private:
|
||||
static void* cac_card_events_thread_entry(void* data);
|
||||
void cac_card_events_thread_main();
|
||||
void send_message(uint32_t reader_id, VSCMsgType type, uint8_t* data, uint32_t len);
|
||||
|
||||
Thread* _event_thread;
|
||||
|
||||
Application* _app;
|
||||
|
||||
VReaderList *_reader_list;
|
||||
typedef std::map<uint32_t, ReaderData*> readers_by_id_t;
|
||||
readers_by_id_t _readers_by_id;
|
||||
typedef std::map<VReader*, ReaderData*> readers_by_vreader_t;
|
||||
readers_by_vreader_t _readers_by_vreader;
|
||||
readers_by_vreader_t _unallocated_readers_by_vreader;
|
||||
VEventType _next_sync_vevent;
|
||||
std::list<SmartCardEvent> _sync_events;
|
||||
|
||||
void push_sync_event(VEventType type, Event *event);
|
||||
void send_next_sync_event();
|
||||
void handle_reader_add_response(VSCMsgHeader *vheader, VSCMsgError *error);
|
||||
void handle_error_message(VSCMsgHeader *vheader, VSCMsgError *error);
|
||||
|
||||
ReaderData* reader_data_from_vreader(VReader* vreader);
|
||||
ReaderData* reader_data_from_reader_id(uint32_t reader_id);
|
||||
void add_unallocated_reader(VReader* vreader, const char* name);
|
||||
ReaderData* add_reader(uint32_t reader_id);
|
||||
void remove_reader(ReaderData* data);
|
||||
void send_reader_added(const char* reader_name);
|
||||
void send_reader_removed(uint32_t reader_id);
|
||||
void send_atr(VReader* vreader);
|
||||
|
||||
friend class ReaderAddEvent;
|
||||
friend class ReaderRemoveEvent;
|
||||
friend class CardInsertEvent;
|
||||
friend class CardRemoveEvent;
|
||||
friend class VSCMessageEvent;
|
||||
};
|
||||
|
||||
#endif // __SMART_CARD_H__
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user