xpra icon
Bug tracker and wiki

Ticket #86: tray-select-layout.patch

File tray-select-layout.patch, 9.0 KB (added by Antoine Martin, 6 years ago)

allows the user to switch the server-side layout using the system tray "keyboard" sub menu

  • xpra/client/keyboard_helper.py

     
    1010
    1111from xpra.keyboard.mask import DEFAULT_MODIFIER_MEANINGS, DEFAULT_MODIFIER_NUISANCE
    1212from xpra.platform.keyboard import Keyboard
     13from xpra.keyboard.layouts import parse_xkbmap_query
    1314from xpra.util import nn, nonl
    1415
    1516
     
    243244    def query_xkbmap(self):
    244245        self.xkbmap_layout, self.xkbmap_variant, self.xkbmap_variants = self.keyboard.get_layout_spec()
    245246        self.xkbmap_print, self.xkbmap_query = self.keyboard.get_keymap_spec()
     247        if not self.xkbmap_layout and not self.xkbmap_variant and self.xkbmap_query:
     248            #we can set the layout and variant from the xkb query string:
     249            props = parse_xkbmap_query(self.xkbmap_query)
     250            self.xkbmap_layout = props.get("layout", "").split(",")[0]
     251            self.xkbmap_variant = props.get("variant", "").split(",")[0]
    246252        self.xkbmap_keycodes = self.get_full_keymap()
    247253        self.xkbmap_x11_keycodes = self.keyboard.get_x11_keymap()
    248254        self.xkbmap_mod_meanings, self.xkbmap_mod_managed, self.xkbmap_mod_pointermissing = self.keyboard.get_keymap_modifiers()
  • xpra/client/gtk_base/gtk_tray_menu_base.py

     
    723723    def make_layoutsmenuitem(self):
    724724        keyboard = self.menuitem("Keyboard", "keyboard.png", "Select your keyboard layout", None)
    725725        keyboard.set_sensitive(False)
     726        kh = self.client.keyboard_helper
     727        if kh is None:
     728            set_tooltip_text(keyboard, "Keyboard is disabled")
     729            return keyboard
    726730        self.layout_submenu = gtk.Menu()
    727731        keyboard.set_submenu(self.layout_submenu)
    728732        self.popup_menu_workaround(self.layout_submenu)
     
    732736                item = ensure_item_selected(self.layout_submenu, item)
    733737                layout = item.keyboard_layout
    734738                variant = item.keyboard_variant
    735                 if layout!=self.client.xkbmap_layout or variant!=self.client.xkbmap_variant:
    736                     log("keyboard layout selected: %s / %s", layout, variant)
    737                     self.client.xkbmap_layout = layout
    738                     self.client.xkbmap_variant = variant
     739                if layout!=kh.xkbmap_layout or variant!=kh.xkbmap_variant:
     740                    log.info("selected keyboard layout has changed from %s / %s to %s / %s", kh.xkbmap_layout, kh.xkbmap_variant, layout, variant)
     741                    kh.xkbmap_layout = layout
     742                    kh.xkbmap_variant = variant
    739743                    self.client.send_layout()
    740744            l = self.checkitem(title, set_layout)
    741745            l.set_draw_as_radio(True)
     
    745749        def keysort(key):
    746750            c,l = key
    747751            return c.lower()+l.lower()
    748         layout,variant,variants = self.client.keyboard_helper.keyboard.get_layout_spec()
     752        layout,variant,variants = kh.keyboard.get_layout_spec()
    749753        if layout and len(variants)>1:
    750754            #just show all the variants to choose from this layout
    751755            self.layout_submenu.append(kbitem("%s - Default" % layout, layout, None))
     
    773777                else:
    774778                    #no variants:
    775779                    self.layout_submenu.append(kbitem(name, layout, None))
    776         keyboard_helper = self.client.keyboard_helper
    777780        def set_selected_layout(*args):
    778             if keyboard_helper.xkbmap_layout or keyboard_helper.xkbmap_print or keyboard_helper.xkbmap_query:
     781            layout = None
     782            variant = None
     783            log.info("set_selected_layout(%s) xkbmap_query=%s", args, kh.xkbmap_query)
     784            if kh.xkbmap_query:
     785                from xpra.keyboard.layouts import parse_xkbmap_query
     786                props = parse_xkbmap_query(kh.xkbmap_query)
     787                layouts = props.get("layout", "").split(",")
     788                variants = props.get("variant", "").split(",")
     789                log.info("layouts=%s, variants=%s", layouts, variants)
     790                if len(layouts)>1:
     791                    #remove all existing entries:
     792                    for x in self.layout_submenu.get_children():
     793                        self.layout_submenu.remove(x)
     794                    #show menu options for each layout:
     795                    for i in range(len(layouts)):
     796                        l = str(layouts[i])
     797                        v = ""
     798                        if i<len(variants):
     799                            v = str(variants[i])
     800                        if v:
     801                            self.layout_submenu.append(kbitem("%s - %s" % (l, v), l, v))
     802                        else:
     803                            self.layout_submenu.append(kbitem(l, l, v))
     804                if len(layouts)>0:
     805                    layout = layouts[0]
     806                    if len(variants)>0:
     807                        variant = variants[0]
     808            elif kh.xkbmap_layout or kh.xkbmap_print:
    779809                #we have detected a layout
    780810                #so no need to let the user override it
    781811                keyboard.hide()
    782812                return
    783813            keyboard.set_sensitive(True)
    784             layout = keyboard_helper.xkbmap_layout
    785             variant = keyboard_helper.xkbmap_variant
    786814            def is_match(checkitem):
     815                log.info("is_match(%s) keyboard_layout=%s / %s, keyboard_variant=%s / %s", checkitem, checkitem.keyboard_layout, layout, checkitem.keyboard_variant, variant)
    787816                return checkitem.keyboard_layout==layout and checkitem.keyboard_variant==variant
    788817            set_checkeditems(self.layout_submenu, is_match)
    789818        self.client.connect("handshake-complete", set_selected_layout)
  • xpra/x11/xkbhelper.py

     
    8181        settings = parse_xkbmap_query(xkbmap_query)
    8282        #construct the command line arguments for setxkbmap:
    8383        args = ["setxkbmap"]
     84        SETTINGS_USED = ["rules", "model", "layout"]
     85        if len([x for x in SETTINGS_USED if x in settings])==0:
     86            log.warn("do_set_keymap could not find rules, model or layout in the xkbmap query string..")
     87        else:
     88            #try to honour the layout and variant set if this differs from the first option
     89            if xkbmap_layout:
     90                layouts = settings.get("layout", "").split(",")
     91                variants = settings.get("variant", "").split(",")
     92                if len(layouts)>0 and xkbmap_layout!=layouts[0] or xkbmap_variant!=variants[0]:
     93                    log.info("keyboard: need to change layout variant from %s / %s to %s / %s", layouts[0], variants[0], xkbmap_layout, xkbmap_variant)
     94                    #find the index:
     95                    try:
     96                        index = layouts.index(xkbmap_layout)
     97                    except:
     98                        log.warn("invalid layout specified: layout '%s' not found in xkb query layout list: %s", xkbmap_layout, layouts)
     99                        index = -1
     100                    if index>0:
     101                        log.info("layout index=%s", index)
     102                        #ensure variants list has as many entries as layouts:
     103                        while len(variants)>len(layouts):
     104                            variants.append("")
     105                        #make new lists re-ordered:
     106                        log.info("layouts=%s, variants=%s", layouts, variants)
     107                        layouts = [xkbmap_layout]+layouts[:index]+layouts[index+1:]
     108                        variants = [xkbmap_variant]+variants[:index]+variants[index+1:]
     109                        log.info("layouts=%s, variants=%s", layouts, variants)
     110                        #now update the settings:
     111                        settings["layout"] = ",".join(layouts)
     112                        if len([x for x in variants if len(x)>0])>0:
     113                            settings["variants"] = ",".join(variants)
     114
     115        #generate command line:                       
    84116        used_settings = {}
    85117        for setting in ["rules", "model", "layout"]:
    86118            if setting in settings:
     
    87119                value = settings.get(setting)
    88120                args += ["-%s" % setting, value]
    89121                used_settings[setting] = value
    90         if len(args)==1:
    91             log.warn("do_set_keymap could not find rules, model or layout in the xkbmap query string..")
    92         else:
    93             log.info("setting keymap: %s", ", ".join(["%s=%s" % (std(k), std(v)) for k,v in used_settings.items()]))
     122        log.info("setting keymap: %s", ", ".join(["%s=%s" % (std(k), std(v)) for k,v in used_settings.items()]))
     123                   
    94124        exec_keymap_command(args)
    95125        #try to set the options:
    96126        if "options" in settings: