Skip to content

Commit

Permalink
vmnetx.ui.view: Fix missing guest mouse cursor in some sessions
Browse files Browse the repository at this point in the history
We can't use channel.connect() to connect a GObject signal because
SpiceChannel overrides connect() to mean "initiate a TCP connection".
Instead, we were using channel.connect_object(..., channel), but this
creates a reference cycle between the channel PyGObject and the
underlying GObject.  With connect(), the PyGObject would be destroyed
after _new_channel() and recreated (pointing to the same GObject) before
the _request_fd() callback.  With connect_object(), the PyGObject is
referenced from the signal handler closure and so is never freed.
However, if the Python GC runs between _new_channel() and _request_fd(),
it will detect a reference cycle and clear the PyGObject's reference to
the GObject.  This was happening to the cursor channel on RHEL 6;
_request_fd() would receive an invalid PyGObject and then swallow the
resulting TypeError.  As a result, the cursor channel would never be
connected, and the guest cursor (for VMs supporting a hardware cursor)
would always be invisible.

Use connect() instead of connect_object(), explicitly calling it through
the superclass.
  • Loading branch information
bgilbert committed May 23, 2014
1 parent 0f45078 commit 529b638
Showing 1 changed file with 6 additions and 6 deletions.
12 changes: 6 additions & 6 deletions vmnetx/ui/view.py
Expand Up @@ -20,6 +20,7 @@
from distutils.version import LooseVersion from distutils.version import LooseVersion
import glib import glib
import gobject import gobject
from gobject import GObject
import gtk import gtk
import logging import logging
import pango import pango
Expand Down Expand Up @@ -285,23 +286,22 @@ def _connect_viewer(self, password):
except RuntimeError: except RuntimeError:
# No local PulseAudio, etc. # No local PulseAudio, etc.
pass pass
self._session.connect_object('channel-new', self._new_channel, GObject.connect(self._session, 'channel-new', self._new_channel)
self._session)
self._session.open_fd(-1) self._session.open_fd(-1)


def _new_channel(self, session, channel): def _new_channel(self, session, channel):
if session != self._session: if session != self._session:
# Stale channel; ignore # Stale channel; ignore
return return
channel.connect_object('open-fd', self._request_fd, channel) GObject.connect(channel, 'open-fd', self._request_fd)
channel.connect_object('channel-event', self._channel_event, channel) GObject.connect(channel, 'channel-event', self._channel_event)
type = SpiceClientGtk.spice_channel_type_to_string( type = SpiceClientGtk.spice_channel_type_to_string(
channel.get_property('channel-type')) channel.get_property('channel-type'))
if type == 'display': if type == 'display':
# Create the display but don't show it until configured by # Create the display but don't show it until configured by
# the server # the server
channel.connect_object('display-primary-create', GObject.connect(channel, 'display-primary-create',
self._display_create, channel) self._display_create)
self._destroy_display() self._destroy_display()
self._display_channel = channel self._display_channel = channel
self._display = SpiceClientGtk.Display(self._session, self._display = SpiceClientGtk.Display(self._session,
Expand Down

0 comments on commit 529b638

Please sign in to comment.