# This file is part of Xpra.
# Copyright (C) 2010 Nathaniel Smith <njs@pobox.com>
# Copyright (C) 2011-2014 Antoine Martin <antoine@devloop.org.uk>
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.


from xpra.platform.keyboard_base import KeyboardBase
from xpra.keyboard.mask import MODIFIER_MAP
from xpra.log import Logger
log = Logger("keyboard", "posix")

try:
    from xpra.x11.bindings.keyboard_bindings import X11KeyboardBindings   #@UnresolvedImport
    keyboard_bindings = X11KeyboardBindings()
except Exception, e:
    log.warn("failed load posix keyboard bindings: %s", e)
    keyboard_bindings = None


class Keyboard(KeyboardBase):

    def exec_get_keyboard_data(self, cmd):
        # Find the client's current keymap so we can send it to the server:
        try:
            from xpra.scripts.exec_util import safe_exec
            returncode, out, _ = safe_exec(cmd)
            if returncode==0:
                return out.decode('utf-8')
            log.error("'%s' failed with exit code %s", cmd, returncode)
        except Exception, e:
            log.error("error running '%s': %s", cmd, e)
        return None

    def get_keymap_modifiers(self):
        if keyboard_bindings:
            try:
                mod_mappings = keyboard_bindings.get_modifier_mappings()
                if mod_mappings:
                    #ie: {"shift" : ["Shift_L", "Shift_R"], "mod1" : "Meta_L", ...]}
                    log.debug("modifier mappings=%s", mod_mappings)
                    meanings = {}
                    for modifier,keys in mod_mappings.items():
                        for _,keyname in keys:
                            meanings[keyname] = modifier
                    return  meanings, [], []
            except Exception, e:
                log.error("failed to use native get_modifier_mappings: %s", e, exc_info=True)
        return self.modifiers_fallback()

    def modifiers_fallback(self):
        xmodmap_pm = self.exec_get_keyboard_data(["xmodmap", "-pm"])
        if not xmodmap_pm:
            log.warn("bindings are not available and 'xmodmap -pm' also failed, expect keyboard mapping problems")
            return {}, [], []
        #parse it so we can feed it back to xmodmap (ala "xmodmap -pke")
        meanings = {}
        for line in xmodmap_pm.splitlines()[1:]:
            if not line:
                continue
            parts = line.split()
            #ie: ['shift', 'Shift_L', '(0x32),', 'Shift_R', '(0x3e)']
            if len(parts)>1:
                nohex = [x for x in parts[1:] if not x.startswith("(")]
                for x in nohex:
                    #ie: meanings['Shift_L']=shift
                    meanings[x] = parts[0]
        log.debug("get_keymap_modifiers parsed: meanings=%s", meanings)
        return  meanings, [], []

    def get_x11_keymap(self):
        if keyboard_bindings:
            try:
                return keyboard_bindings.get_keycode_mappings()
            except Exception, e:
                log.error("failed to use raw x11 keymap: %s", e)
        return  {}

    def get_keymap_spec(self):
        xkbmap_print = self.exec_get_keyboard_data(["setxkbmap", "-print"])
        if xkbmap_print is None:
            log.error("your keyboard mapping will probably be incorrect unless you are using a 'us' layout");
        xkbmap_query = self.exec_get_keyboard_data(["setxkbmap", "-query"])
        if xkbmap_query is None and xkbmap_print is not None:
            log.error("the server will try to guess your keyboard mapping, which works reasonably well in most cases");
            log.error("however, upgrading 'setxkbmap' to a version that supports the '-query' parameter is preferred");
        return xkbmap_print, xkbmap_query

    def get_keyboard_repeat(self):
        if keyboard_bindings:
            try:
                delay, interval = keyboard_bindings.get_key_repeat_rate()
                return delay,interval
            except Exception, e:
                log.error("failed to get keyboard repeat rate: %s", e)
        return None

    def update_modifier_map(self, display, xkbmap_mod_meanings):
        try:
            from xpra.x11.gtk_x11.keys import grok_modifier_map
            self.modifier_map = grok_modifier_map(display, xkbmap_mod_meanings)
        except ImportError:
            self.modifier_map = MODIFIER_MAP
        log.debug("update_modifier_map(%s, %s) modifier_map=%s", display, xkbmap_mod_meanings, self.modifier_map)
