[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[virt-tools-list] [PATCH v2] Virt-manager: Add configurable grab keys



Hi,
this is the patch to add configurable grab keys to the virt-manager I did in my spare time for my own purposes originally (and also it's partially based on a request from a collegue in our office). It requires at least Gtk-VNC 0.4.0 since git commit 378721ec1 of Gtk-VNC introduced this feature. It's been tested and this patch is for the latest mercurial codebase of VMM and a bug 616355 ( [RFE] Add configurable grab key sequences for VMM) has been filed by myself some time ago for this request.

This is the second version of the patch that's including the exception handling for case the user is using some older version of Gtk-VNC (pre-0.4.0) that doesn't support the configurable grab keys. For the VMM interface itself, a new tab in preferences dialog, called "Keys", has been added showing the current grab keys combination and new GConf entry is being written when edited. Also, when you press the "Define" button on this tab a new dialog window is being opened and you have to press all the keys you want to use as grab keys and when you have all the keys you want to use in your combination pressed you have to click OK button to allow VMM to remember it.

Also, one slight issue is when you opened the console window already since the grab key combination is being read only on init() apparently so when changing the grab keys combination the restart of virt-manager is recommended.

I also considered the advices Cole gave me when implementing this new version and the check-pylint was returning no errors and/or warnings as well.

Differences between version 1 and version 2 (this one):
- Implement the grab_keys_supported() method to check for configurable grab keys support in the GTK-VNC version currently installed - Grab key sequence is configurable in Prefs-> VM Details and "Change" button is disabled when GTK-VNC doesn't support it - tooltip is being shown - Exception handling when somebody manually edited the grab-keys in GConf has been added to ignore invalid (non-integer) key codes

Please write me your feedback on this version.

Thanks,
Michal

--
Michal Novotny<minovotn redhat com>, RHCE
Virtualization Team (xen userspace), Red Hat

diff -r 25507d46ecbb src/virtManager/config.py
--- a/src/virtManager/config.py	Thu Aug 19 11:26:07 2010 -0400
+++ b/src/virtManager/config.py	Mon Aug 23 16:47:24 2010 +0200
@@ -288,6 +288,45 @@
         self.conf.notify_add(self.conf_dir + "/vmlist-fields/network_traffic",
                              cb)
 
+    # Check whether we have GTK-VNC that supports configurable grab keys
+    # installed on the system
+    def grab_keys_supported(self):
+        try:
+            import gtkvnc
+            return hasattr(gtkvnc.Display, "set_grab_keys")
+        except:
+            return False
+
+    # Keys preferences
+    def get_keys_combination(self, syms = False):
+        val = self.conf.get_string(self.conf_dir + "/keys/grab-keys")
+        if syms == True:
+            return val
+
+        # If val is None we return it
+        if val is None:
+            return None
+
+        # We convert keysyms to names
+        keystr = None
+        for k in val.split(','):
+            try:
+                key = int(k)
+            except:
+                key = None
+
+            if key is not None:
+                if keystr is None:
+                    keystr = gtk.gdk.keyval_name(key)
+                else:
+                    keystr = keystr + "+" + gtk.gdk.keyval_name(key)
+
+        return keystr
+
+    def set_keys_combination(self, val):
+        # Val have to be a list of integers
+        val = ','.join(map(str, val))
+        self.conf.set_string(self.conf_dir + "/keys/grab-keys", val)
 
     # Confirmation preferences
     def get_confirm_forcepoweroff(self):
diff -r 25507d46ecbb src/virtManager/console.py
--- a/src/virtManager/console.py	Thu Aug 19 11:26:07 2010 -0400
+++ b/src/virtManager/console.py	Mon Aug 23 16:47:24 2010 +0200
@@ -84,6 +84,21 @@
         self.vncViewer = gtkvnc.Display()
         self.window.get_widget("console-vnc-viewport").add(self.vncViewer)
 
+        # Set default grab key combination if found and supported
+        if self.config.grab_keys_supported():
+            try:
+                grab_keys = self.config.get_keys_combination(True)
+                if grab_keys is not None:
+                    # If somebody edited this in GConf it would fail so
+                    # we encapsulate this into try/except block
+                    try:
+                        keys = map(int, grab_keys.split(','))
+                        self.vncViewer.set_grab_keys( keys )
+                    except:
+                        logging.debug("Error in grab_keys configuration in GConf")
+            except Exception, e:
+                logging.debug("Error when getting the grab keys combination: %s" % str(e))
+
         self.init_vnc()
 
         finish_img = gtk.image_new_from_stock(gtk.STOCK_YES,
@@ -165,7 +180,24 @@
         self._enable_modifiers()
 
     def notify_grabbed(self, src):
-        self.topwin.set_title(_("Press Ctrl+Alt to release pointer.") +
+        keystr = None
+        if self.config.grab_keys_supported():
+            try:
+                keys = src.get_grab_keys()
+                for k in keys:
+                    if keystr is None:
+                        keystr = gtk.gdk.keyval_name(k)
+                    else:
+                        keystr = keystr + "+" + gtk.gdk.keyval_name(k)
+            except:
+                pass
+
+        # If grab keys are set to None then preserve old behaviour since
+        # the GTK-VNC - we're using older version of GTK-VNC
+        if keystr is None:
+            keystr = "Control_L+Alt_L"
+
+        self.topwin.set_title(_("Press %s to release pointer.") % keystr +
                               " " + self.title)
 
         if (not self.config.show_console_grab_notify() or
@@ -182,7 +214,7 @@
                                                         0,
                                                         '',
                                                         _("Pointer grabbed"),
-                                                        _("The mouse pointer has been restricted to the virtual console window. To release the pointer, press the key pair: Ctrl+Alt"),
+                                                        _("The mouse pointer has been restricted to the virtual console window. To release the pointer, press the key pair") + " " + keystr,
                                                         ["dismiss", _("Do not show this notification in the future.")],
                                                         {"desktop-entry": "virt-manager",
                                                         "x": x+200, "y": y},
diff -r 25507d46ecbb src/virtManager/preferences.py
--- a/src/virtManager/preferences.py	Thu Aug 19 11:26:07 2010 -0400
+++ b/src/virtManager/preferences.py	Mon Aug 23 16:47:24 2010 +0200
@@ -65,6 +65,7 @@
         self.refresh_sound_remote()
         self.refresh_disk_poll()
         self.refresh_net_poll()
+        self.refresh_grabkeys_combination()
         self.refresh_confirm_forcepoweroff()
         self.refresh_confirm_poweroff()
         self.refresh_confirm_pause()
@@ -90,6 +91,7 @@
             "on_prefs_confirm_pause_toggled": self.change_confirm_pause,
             "on_prefs_confirm_removedev_toggled": self.change_confirm_removedev,
             "on_prefs_confirm_interface_toggled": self.change_confirm_interface,
+            "on_prefs_btn_keys_define_clicked": self.change_grab_keys,
             })
         util.bind_escape_key_close(self)
 
@@ -144,6 +146,20 @@
                          ignore4=None):
         self.window.get_widget("prefs-stats-enable-net").set_active(self.config.get_stats_enable_net_poll())
 
+    def refresh_grabkeys_combination(self, ignore1=None, ignore2=None,
+                           ignore3=None, ignore4=None):
+        val = self.config.get_keys_combination()
+        # If unset we preset the default combination (Ctrl+Alt)
+        if val is None:
+            val = "Control_L+Alt_L"
+        self.window.get_widget("prefs-keys-grab-sequence").set_text(val)
+        # If not supported disable the 'Change' button
+        if not self.config.grab_keys_supported():
+            self.window.get_widget("prefs-keys-grab-changebtn").set_has_tooltip(True)
+            self.window.get_widget("prefs-keys-grab-changebtn").set_tooltip_text(
+                                  _("Installed version of GTK-VNC doesn't support configurable grab keys") )
+            self.window.get_widget("prefs-keys-grab-changebtn").set_sensitive(False)
+
     def refresh_confirm_forcepoweroff(self, ignore1=None, ignore2=None,
                                       ignore3=None, ignore4=None):
         self.window.get_widget("prefs-confirm-forcepoweroff").set_active(self.config.get_confirm_forcepoweroff())
@@ -160,6 +176,54 @@
                                   ignore3=None, ignore4=None):
         self.window.get_widget("prefs-confirm-interface").set_active(self.config.get_confirm_interface())
 
+    def grabkeys_get_string(self, keysyms):
+        keystr = None
+        for k in keysyms:
+            if keystr is None:
+                keystr = gtk.gdk.keyval_name(k)
+            else:
+                keystr = keystr + "+" + gtk.gdk.keyval_name(k)
+        # Disallow none
+        if keystr is None:
+            keystr = ""
+        return keystr
+
+    def grabkeys_dlg_press(self, src, ev, defs):
+        label = defs['label']
+        # Try to get the index, it fails when not found
+        try:
+            defs['keysyms'].index(ev.keyval)
+        except:
+            defs['keysyms'].append(ev.keyval)
+
+        label.set_text( self.grabkeys_get_string(defs['keysyms']) )
+
+    def grabkeys_dlg_release(self, src, ev, defs):
+        label = defs['label']
+        defs['keysyms'].remove(ev.keyval)
+        label.set_text( self.grabkeys_get_string(defs['keysyms']) )
+
+    def change_grab_keys(self, src):
+        dialog = gtk.Dialog ( _("Key combination configurator"),
+                              None,
+                              gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
+                              (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
+                               gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
+        label = gtk.Label( _("Please press desired grab key combination") )
+        dialog.set_size_request(300, 100)
+        (dialog.get_content_area()).add(label)
+        defs = { 'label': label, 'keysyms': [] }
+        dialog.connect("key-press-event", self.grabkeys_dlg_press, defs)
+        dialog.connect("key-release-event", self.grabkeys_dlg_release, defs)
+        dialog.show_all()
+        result = dialog.run()
+
+        if result == gtk.RESPONSE_ACCEPT:
+            self.config.set_keys_combination( defs['keysyms'] )
+
+        self.refresh_grabkeys_combination()
+        dialog.destroy()
+
     def change_view_system_tray(self, src):
         self.config.set_view_system_tray(src.get_active())
 
diff -r 25507d46ecbb src/vmm-preferences.glade
--- a/src/vmm-preferences.glade	Thu Aug 19 11:26:07 2010 -0400
+++ b/src/vmm-preferences.glade	Mon Aug 23 16:47:24 2010 +0200
@@ -383,15 +383,6 @@
                               </packing>
                             </child>
                             <child>
-                              <widget class="GtkLabel" id="label5">
-                                <property name="visible">True</property>
-                                <property name="xalign">0</property>
-                                <property name="label" translatable="yes">Automatically _open consoles:</property>
-                                <property name="use_underline">True</property>
-                                <property name="mnemonic_widget">prefs-console-popup</property>
-                              </widget>
-                            </child>
-                            <child>
                               <widget class="GtkComboBox" id="prefs-console-popup">
                                 <property name="visible">True</property>
                                 <property name="items" translatable="yes">Never
@@ -405,6 +396,15 @@
                                 <property name="x_padding">5</property>
                               </packing>
                             </child>
+                            <child>
+                              <widget class="GtkLabel" id="label5">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="label" translatable="yes">Automatically _open consoles:</property>
+                                <property name="use_underline">True</property>
+                                <property name="mnemonic_widget">prefs-console-popup</property>
+                              </widget>
+                            </child>
                           </widget>
                         </child>
                       </widget>
@@ -510,6 +510,75 @@
                     <property name="position">1</property>
                   </packing>
                 </child>
+                <child>
+                  <widget class="GtkFrame" id="frame7">
+                    <property name="visible">True</property>
+                    <property name="label_xalign">0</property>
+                    <property name="shadow_type">none</property>
+                    <child>
+                      <widget class="GtkAlignment" id="alignment8">
+                        <property name="visible">True</property>
+                        <property name="left_padding">12</property>
+                        <child>
+                          <widget class="GtkTable" id="table7">
+                            <property name="visible">True</property>
+                            <property name="column_spacing">8</property>
+                            <child>
+                              <widget class="GtkHBox" id="hbox1">
+                                <property name="visible">True</property>
+                                <child>
+                                  <widget class="GtkLabel" id="label24">
+                                    <property name="visible">True</property>
+                                    <property name="label" translatable="yes">Grab keys:</property>
+                                  </widget>
+                                  <packing>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <widget class="GtkLabel" id="prefs-keys-grab-sequence">
+                                    <property name="visible">True</property>
+                                    <property name="label" translatable="yes">Not supported</property>
+                                  </widget>
+                                  <packing>
+                                    <property name="padding">8</property>
+                                    <property name="position">1</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <widget class="GtkButton" id="prefs-keys-grab-changebtn">
+                                    <property name="label" translatable="yes">Change...</property>
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="receives_default">True</property>
+                                    <signal name="clicked" handler="on_prefs_btn_keys_define_clicked"/>
+                                  </widget>
+                                  <packing>
+                                    <property name="position">2</property>
+                                  </packing>
+                                </child>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="label25">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">&lt;b&gt;Key sequences&lt;/b&gt;</property>
+                        <property name="use_markup">True</property>
+                      </widget>
+                      <packing>
+                        <property name="type">label_item</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
               </widget>
               <packing>
                 <property name="position">2</property>

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]