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

[virt-tools-list] [PATCH] 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.

Please write me your feedback.

Thanks,
Michal

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

diff -r 17c8e3d3ee10 src/virtManager/config.py
--- a/src/virtManager/config.py	Sat Aug 07 15:40:49 2010 +0000
+++ b/src/virtManager/config.py	Tue Aug 10 13:54:05 2010 +0200
@@ -289,6 +289,33 @@
                              cb)
 
 
+    # 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
+        # We don't allow return of None, we use default of Ctrl + Alt instead
+        if val is None:
+            return "Control_L+Alt_L"
+
+        # We convert keysyms to names
+        keystr = None
+        for k in val.split(','):
+            if keystr is None:
+                keystr = gtk.gdk.keyval_name(int(k))
+            else:
+                keystr = keystr + "+" + gtk.gdk.keyval_name(int(k))
+        # Disallow None
+        if keystr is None:
+            keystr = ""
+
+        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):
         return self.conf.get_bool(self.conf_dir + "/confirm/forcepoweroff")
diff -r 17c8e3d3ee10 src/virtManager/console.py
--- a/src/virtManager/console.py	Sat Aug 07 15:40:49 2010 +0000
+++ b/src/virtManager/console.py	Tue Aug 10 13:54:05 2010 +0200
@@ -84,6 +84,18 @@
         self.vncViewer = gtkvnc.Display()
         self.window.get_widget("console-vnc-viewport").add(self.vncViewer)
 
+        # Set default grab key combination if found
+        # We encapsulate it into the try/except block for case the
+        # Gtk-VNC is older and it's not supporting it
+        try:
+            grab_keys = self.config.get_keys_combination(True)
+            if grab_keys is not None:
+                keys = map(int, grab_keys.split(','))
+                self.vncViewer.set_grab_keys( keys )
+        except Exception, e:
+            logging.debug("Error when getting the grab keys combination: %s" % str(e))
+            pass
+
         self.init_vnc()
 
         finish_img = gtk.image_new_from_stock(gtk.STOCK_YES,
@@ -165,7 +177,18 @@
         self._enable_modifiers()
 
     def notify_grabbed(self, src):
-        self.topwin.set_title(_("Press Ctrl+Alt to release pointer.") +
+        try:
+            keys = src.get_grab_keys()
+            keystr = None
+            for k in keys:
+                if keystr is None:
+                    keystr = gtk.gdk.keyval_name(k)
+                else:
+                    keystr = keystr + "+" + gtk.gdk.keyval_name(k)
+        except:
+            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 +205,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 17c8e3d3ee10 src/virtManager/preferences.py
--- a/src/virtManager/preferences.py	Sat Aug 07 15:40:49 2010 +0000
+++ b/src/virtManager/preferences.py	Tue Aug 10 13:54:05 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,10 @@
                          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):
+        self.window.get_widget("prefs-keys-combo").set_text(self.config.get_keys_combination())
+
     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 +166,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 recorder",
+                              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 17c8e3d3ee10 src/vmm-preferences.glade
--- a/src/vmm-preferences.glade	Sat Aug 07 15:40:49 2010 +0000
+++ b/src/vmm-preferences.glade	Tue Aug 10 13:54:05 2010 +0200
@@ -82,6 +82,119 @@
               </packing>
             </child>
             <child>
+              <widget class="GtkVBox" id="vbox7">
+                <property name="visible">True</property>
+                <property name="border_width">12</property>
+                <property name="orientation">vertical</property>
+                <child>
+                  <widget class="GtkLabel" id="label25">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">Please use the following key combination to grab
+the keys in the guest window:</property>
+                  </widget>
+                  <packing>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="prefs-keys-combo">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">[KEY COMBINATION]</property>
+                  </widget>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkHBox" id="hbox1">
+                    <property name="visible">True</property>
+                    <child>
+                      <widget class="GtkAspectFrame" id="aspectframe1">
+                        <property name="visible">True</property>
+                        <property name="label_xalign">0</property>
+                        <property name="shadow_type">none</property>
+                        <child>
+                          <placeholder/>
+                        </child>
+                      </widget>
+                      <packing>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkAspectFrame" id="aspectframe2">
+                        <property name="visible">True</property>
+                        <property name="label_xalign">0</property>
+                        <property name="shadow_type">none</property>
+                        <child>
+                          <placeholder/>
+                        </child>
+                      </widget>
+                      <packing>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkVBox" id="vbox8">
+                        <property name="visible">True</property>
+                        <property name="orientation">vertical</property>
+                        <child>
+                          <widget class="GtkAspectFrame" id="aspectframe3">
+                            <property name="visible">True</property>
+                            <property name="label_xalign">0</property>
+                            <property name="shadow_type">none</property>
+                            <child>
+                              <placeholder/>
+                            </child>
+                          </widget>
+                          <packing>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="GtkAlignment" id="alignment8">
+                            <property name="visible">True</property>
+                            <child>
+                              <widget class="GtkButton" id="prefs-btn-keys-define">
+                                <property name="label" translatable="yes">Define</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>
+                            </child>
+                          </widget>
+                          <packing>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </widget>
+                      <packing>
+                        <property name="position">2</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="label1">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Keys</property>
+              </widget>
+              <packing>
+                <property name="position">1</property>
+                <property name="tab_fill">False</property>
+                <property name="type">tab</property>
+              </packing>
+            </child>
+            <child>
               <widget class="GtkVBox" id="vbox2">
                 <property name="visible">True</property>
                 <property name="border_width">12</property>

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