diff options
author | Yonit Halperin <yhalperi@redhat.com> | 2011-09-18 21:50:06 +0300 |
---|---|---|
committer | Yonit Halperin <yhalperi@redhat.com> | 2011-11-02 11:30:29 +0200 |
commit | 00ff038cc90ef49f350a2fc033115d10b72995a7 (patch) | |
tree | af8d4fca74bdb525fb330d3477f105be7a622ce4 | |
parent | 87664af999a534090c37ffb53aec2a6cd970cdcc (diff) |
client: display channel migration
(cherry picked from commit cad3c585444f940f60c12789f4174f2d32bec70f branch 0.8)
Conflicts:
client/display_channel.cpp
-rw-r--r-- | client/display_channel.cpp | 157 | ||||
-rw-r--r-- | client/display_channel.h | 10 |
2 files changed, 153 insertions, 14 deletions
diff --git a/client/display_channel.cpp b/client/display_channel.cpp index b166c70..f7fdbbc 100644 --- a/client/display_channel.cpp +++ b/client/display_channel.cpp @@ -87,6 +87,28 @@ private: DisplayChannel& _channel; }; +class DestroyAllSurfacesEvent: public SyncEvent { +public: + DestroyAllSurfacesEvent(DisplayChannel& channel, bool include_primary = true) + : _channel(channel) + , _include_primary(include_primary) + { + } + + virtual void do_response(AbstractProcessLoop& events_loop) + { + if (_include_primary) { + _channel.do_destroy_all_surfaces(); + } else { + _channel.do_destroy_off_screen_surfaces(); + } + } + +private: + DisplayChannel& _channel; + bool _include_primary; +}; + class CreateSurfaceEvent: public SyncEvent { public: CreateSurfaceEvent(DisplayChannel& channel, int surface_id, int width, int height, @@ -547,6 +569,21 @@ void ResetTimer::response(AbstractProcessLoop& events_loop) _client.deactivate_interval_timer(this); } +#define MIGRATION_PRIMARY_SURFACE_TIMEOUT (1000 * 5) + +class MigPrimarySurfaceTimer: public Timer { +public: + virtual void response(AbstractProcessLoop& events_loop) + { + DisplayChannel *channel = static_cast<DisplayChannel*>(events_loop.get_owner()); + if (channel->_mig_wait_primary) { + channel->destroy_primary_surface(); + channel->_mig_wait_primary = false; + } + channel->get_process_loop().deactivate_interval_timer(this); + } +}; + class DisplayHandler: public MessageHandlerImp<DisplayChannel, SPICE_CHANNEL_DISPLAY> { public: DisplayHandler(DisplayChannel& channel) @@ -574,6 +611,7 @@ DisplayChannel::DisplayChannel(RedClient& client, uint32_t id, , _gl_interrupt_recreate (*this) #endif , _interrupt_update (*this) + , _mig_wait_primary (false) { DisplayHandler* handler = static_cast<DisplayHandler*>(get_message_handler()); @@ -621,11 +659,11 @@ DisplayChannel::~DisplayChannel() screen()->set_update_interrupt_trigger(NULL); } - //destroy_canvas(); fixme destroy all - destroy_strams(); + destroy_streams(); + do_destroy_all_surfaces(); } -void DisplayChannel::destroy_strams() +void DisplayChannel::destroy_streams() { Lock lock(_streams_lock); for (unsigned int i = 0; i < _streams.size(); i++) { @@ -1024,6 +1062,75 @@ void DisplayChannel::on_disconnect() (*sync_event)->wait(); } +void DisplayChannel::do_destroy_all_surfaces() +{ + SurfacesCache::iterator s_iter; + + for (s_iter = _surfaces_cache.begin(); s_iter != _surfaces_cache.end(); s_iter++) { + delete (*s_iter).second; + } + _surfaces_cache.clear(); +} + +void DisplayChannel::do_destroy_off_screen_surfaces() +{ + SurfacesCache::iterator s_iter; + Canvas *primary_canvas = NULL; + + for (s_iter = _surfaces_cache.begin(); s_iter != _surfaces_cache.end(); s_iter++) { + if (s_iter->first == 0) { + primary_canvas = s_iter->second; + } else { + delete s_iter->second; + } + } + _surfaces_cache.clear(); + if (primary_canvas) { + _surfaces_cache[0] = primary_canvas; + } +} + +void DisplayChannel::destroy_all_surfaces() +{ + AutoRef<DestroyAllSurfacesEvent> destroy_event(new DestroyAllSurfacesEvent(*this)); + + get_client().push_event(*destroy_event); + (*destroy_event)->wait(); + if (!(*destroy_event)->success()) { + THROW("destroy all surfaces failed"); + } +} + +void DisplayChannel::destroy_off_screen_surfaces() +{ + AutoRef<DestroyAllSurfacesEvent> destroy_event(new DestroyAllSurfacesEvent(*this, false)); + + get_client().push_event(*destroy_event); + (*destroy_event)->wait(); + if (!(*destroy_event)->success()) { + THROW("destroy all surfaces failed"); + } +} + +void DisplayChannel::on_disconnect_mig_src() +{ + _palette_cache.clear(); + destroy_streams(); + if (screen()) { + screen()->set_update_interrupt_trigger(NULL); + } + _update_mark = 0; + _next_timer_time = 0; + get_client().deactivate_interval_timer(*_streams_timer); + destroy_off_screen_surfaces(); + // Not clrearing the primary surface till we receive a new one (or a timeout). + if (_surfaces_cache.exist(0)) { + AutoRef<MigPrimarySurfaceTimer> mig_timer(new MigPrimarySurfaceTimer()); + get_process_loop().activate_interval_timer(*mig_timer, MIGRATION_PRIMARY_SURFACE_TIMEOUT); + _mig_wait_primary = true; + } +} + bool DisplayChannel::create_sw_canvas(int surface_id, int width, int height, uint32_t format) { try { @@ -1365,26 +1472,50 @@ void DisplayChannel::handle_stream_destroy(RedPeer::InMessage* message) void DisplayChannel::handle_stream_destroy_all(RedPeer::InMessage* message) { - destroy_strams(); + destroy_streams(); } void DisplayChannel::create_primary_surface(int width, int height, uint32_t format) { + bool do_create_primary = true; #ifdef USE_OPENGL - Canvas *canvas; + Canvas *canvas; #endif - _mark = false; - attach_to_screen(get_client().get_application(), get_id()); - clear_area(); + _mark = false; + + /* + * trying to avoid artifacts when the display hasn't changed much + * between the disconnection from the migration src and the + * connection to the target. + */ + if (_mig_wait_primary) { + ASSERT(_surfaces_cache.exist(0)); + if (_x_res != width || _y_res != height || format != format) { + LOG_INFO("destroy the primary surface of the mig src session"); + destroy_primary_surface(); + } else { + LOG_INFO("keep the primary surface of the mig src session"); + _surfaces_cache[0]->clear(); + clear_area(); + do_create_primary = false; + } + } - AutoRef<CreatePrimarySurfaceEvent> event(new CreatePrimarySurfaceEvent(*this, width, height, + if (do_create_primary) { + LOG_INFO(""); + attach_to_screen(get_client().get_application(), get_id()); + clear_area(); + AutoRef<CreatePrimarySurfaceEvent> event(new CreatePrimarySurfaceEvent(*this, width, height, format)); - get_client().push_event(*event); - (*event)->wait(); - if (!(*event)->success()) { - THROW("Create primary surface failed"); + get_client().push_event(*event); + (*event)->wait(); + if (!(*event)->success()) { + THROW("Create primary surface failed"); + } } + _mig_wait_primary = false; + _x_res = width; _y_res = height; _format = format; diff --git a/client/display_channel.h b/client/display_channel.h index f30311d..30a76e2 100644 --- a/client/display_channel.h +++ b/client/display_channel.h @@ -114,6 +114,7 @@ public: protected: virtual void on_connect(); virtual void on_disconnect(); + virtual void on_disconnect_mig_src(); private: void set_draw_handlers(); @@ -129,13 +130,17 @@ private: 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_strams(); + 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); @@ -217,11 +222,13 @@ private: #endif InterruptUpdate _interrupt_update; + bool _mig_wait_primary; 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; @@ -229,6 +236,7 @@ private: friend class StreamsTimer; friend class AttachChannelsEvent; friend class DetachChannelsEvent; + friend class MigPrimarySurfaceTimer; }; #endif |