# # Copyright (C) 2010 Red Hat, Inc. # Copyright (C) 2010 Cole Robinson # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301 USA. # import os import sys import logging import virtManager import virtManager.guidiff running_config, gobject, GObject, gtk = virtManager.guidiff.get_imports() def _safe_wrapper(func, *args): gtk.gdk.threads_enter() try: return func(*args) finally: gtk.gdk.threads_leave() class vmmGObject(GObject): @staticmethod def type_register(*args, **kwargs): if not hasattr(gobject, "type_register"): return gobject.type_register(*args, **kwargs) @staticmethod def signal_new(klass, signal, args): if hasattr(gobject, "signal_new"): gobject.signal_new(signal, klass, gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, args) else: klass.fake_signal_listeners[signal] = [] _leak_check = True fake_signal_listeners = {} def __init__(self): GObject.__init__(self) self.config = running_config self._gobject_handles = [] self._gobject_timeouts = [] self._gconf_handles = [] self._signal_id_map = {} self._next_signal_id = 1 self.object_key = str(self) # Config might not be available if we error early in startup if self.config and self._leak_check: self.config.add_object(self.object_key) def cleanup(self): # Do any cleanup required to drop reference counts so object is # actually reaped by python. Usually means unregistering callbacks try: for h in self._gconf_handles[:]: self.remove_gconf_handle(h) for h in self._gobject_handles[:]: self.disconnect(h) for h in self._gobject_timeouts[:]: self.remove_gobject_timeout(h) except: logging.exception("Error cleaning up %s" % self) # Custom signal implementations # These functions duplicate gobject signal behavior since it's # needed for some TUI functionality def _get_signal_listeners(self, signal_name): return self.fake_signal_listeners[signal_name] def _add_signal_listener(self, signal_name, cb, *args): sigid = self._next_signal_id self._next_signal_id += 1 self._signal_id_map[sigid] = (signal_name, cb, args) self.fake_signal_listeners[signal_name].append((cb, args)) return sigid def _remove_signal_listener(self, sigid): signame, cb, args = self._signal_id_map[sigid] listener_data = (cb, args) if listener_data not in self.fake_signal_listeners[signame]: raise RuntimeError("Didn't find signal listener to remove: %d" % sigid) self.fake_signal_listeners[signame].remove(listener_data) del(self._signal_id_map[sigid]) def connect(self, name, callback, *args): if hasattr(GObject, "connect"): ret = GObject.connect(self, name, callback, *args) self._gobject_handles.append(ret) return ret else: return self._add_signal_listener(name, callback, *args) def disconnect(self, handle): if hasattr(GObject, "disconnect"): ret = GObject.disconnect(self, handle) self._gobject_handles.remove(handle) return ret else: return self._remove_signal_listener(handle) def add_gconf_handle(self, handle): self._gconf_handles.append(handle) def remove_gconf_handle(self, handle): self.config.remove_notifier(handle) self._gconf_handles.remove(handle) def add_gobject_timeout(self, handle): self._gobject_timeouts.append(handle) def remove_gobject_timeout(self, handle): if not hasattr(gobject, "source_remove"): return gobject.source_remove(handle) self._gobject_timeouts.remove(handle) def _logtrace(self, msg): import traceback logging.debug("%s (%s %s)\n:%s" % (msg, self.object_key, self.refcount(), "".join(traceback.format_stack()))) def refcount(self): # Function generates 2 temporary refs, so adjust total accordingly return (sys.getrefcount(self) - 2) def get_hal_helper(self, init=True): from virtManager import halhelper return halhelper.get_hal_helper(init=init) def connect_once(self, signal, func, *args): id_list = [] def wrap_func(*wrapargs): if id_list: self.disconnect(id_list[0]) return func(*wrapargs) conn_id = self.connect(signal, wrap_func, *args) id_list.append(conn_id) return conn_id def connect_opt_out(self, signal, func, *args): id_list = [] def wrap_func(*wrapargs): ret = func(*wrapargs) if ret and id_list: self.disconnect(id_list[0]) conn_id = self.connect(signal, wrap_func, *args) id_list.append(conn_id) return conn_id def idle_emit(self, signal, *args): """ Safe wrapper for using 'self.emit' with gobject.idle_add """ def emitwrap(_s, *_a): self.emit(_s, *_a) return False self.safe_idle_add(emitwrap, signal, *args) def safe_idle_add(self, func, *args): """ Make sure idle functions are run thread safe """ if not hasattr(gobject, "idle_add"): return func(*args) return gobject.idle_add(_safe_wrapper, func, *args) def safe_timeout_add(self, timeout, func, *args): """ Make sure timeout functions are run thread safe """ if not hasattr(gobject, "timeout_add"): return return gobject.timeout_add(timeout, _safe_wrapper, func, *args) def emit(self, signal_name, *args): if hasattr(GObject, "emit"): return GObject.emit(self, signal_name, *args) else: for cb, customargs in self._get_signal_listeners(signal_name): cbargs = (self,) + args + customargs cb(*cbargs) return def __del__(self): if hasattr(GObject, "__del__"): getattr(GObject, "__del__")(self) try: if self.config: self.config.remove_object(self.object_key) except: logging.exception("Error removing %s" % self.object_key) class vmmGObjectUI(vmmGObject): def __init__(self, filename, windowname): vmmGObject.__init__(self) self.windowname = windowname self.window = None self.topwin = None self.gladefile = None self.err = None if filename: self.gladefile = os.path.join(self.config.get_glade_dir(), filename) self.window = gtk.glade.XML(self.gladefile, self.windowname, domain="virt-manager") self.topwin = self.widget(self.windowname) self.topwin.hide() self.err = virtManager.error.vmmErrorDialog(self.topwin) def widget(self, name): return self.window.get_widget(name) def cleanup(self): vmmGObject.cleanup(self) self.window = None self.topwin.destroy() self.topwin = None self.gladefile = None self.err = None def close(self, ignore1=None, ignore2=None): pass def bind_escape_key_close(self): def close_on_escape(src_ignore, event): if gtk.gdk.keyval_name(event.keyval) == "Escape": self.close() self.topwin.connect("key-press-event", close_on_escape)