Commit 158542ec authored by Daiki Ueno's avatar Daiki Ueno
Browse files

Replace eekboard-xml and eekboard-inscript with eekxml and mim2remap.

parent 360da8a3
......@@ -4,6 +4,7 @@
*.o
*.so
*~
*.pyc
Makefile
Makefile.in
.deps
......
......@@ -340,7 +340,7 @@ data/icons/scalable/Makefile
data/themes/Makefile
data/keyboards/Makefile
examples/Makefile
examples/eekboard-inscript/Makefile
examples/eekxml/Makefile
examples/simple-client/Makefile
eek/eek-${EEK_API_VERSION}.pc
eek/eek-clutter-${EEK_API_VERSION}.pc
......
......@@ -487,7 +487,7 @@ eek_element_set_bounds (EekElement *element,
/**
* eek_element_get_bounds:
* @element: an #EekElement
* @bounds: pointer where bounding box of @element will be stored
* @bounds: (out): pointer where bounding box of @element will be stored
*
* Get the bounding box of @element. Note that if @element has
* parent, position of @bounds are relative to the parent. To obtain
......
......@@ -540,7 +540,7 @@ eek_key_set_symbol_matrix (EekKey *key,
* @key: an #EekKey
*
* Get the symbol matrix of @key.
* Returns: #EekSymbolMatrix or %NULL
* Returns: (transfer none): #EekSymbolMatrix or %NULL
*/
EekSymbolMatrix *
eek_key_get_symbol_matrix (EekKey *key)
......
......@@ -489,11 +489,11 @@ eek_theme_get_property (GObject *object,
/**
* eek_theme_new:
* @application_stylesheet: The highest priority stylesheet, representing application-specific
* @application_stylesheet: (allow-none): The highest priority stylesheet, representing application-specific
* styling; this is associated with the CSS "author" stylesheet, may be %NULL
* @theme_stylesheet: The second priority stylesheet, representing theme-specific styling ;
* @theme_stylesheet: (allow-none): The second priority stylesheet, representing theme-specific styling ;
* this is associated with the CSS "user" stylesheet, may be %NULL
* @default_stylesheet: The lowest priority stylesheet, representing global default styling;
* @default_stylesheet: (allow-none): The lowest priority stylesheet, representing global default styling;
* this is associated with the CSS "user agent" stylesheet, may be %NULL
*
* Return value: the newly created theme object
......
SUBDIRS = eekboard-inscript simple-client
SUBDIRS = eekxml simple-client
bin_SCRIPTS = eekboard-inscript
keyboarddir = $(pkgdatadir)/keyboards
eekboard_inscript_datadir = $(datarootdir)/eekboard-inscript
eekboard_inscript_data_PYTHON = inscript.py main.py
eekboard-inscript: eekboard-inscript.in
$(AM_V_GEN) sed -e 's!@''PYTHON@!'$(PYTHON)'!' \
-e 's!@EEKBOARD_KEYBOARDDIR@!'$(keyboarddir)'!' \
-e 's!@M17N_DIR@!'$(datadir)/m17n'!' \
-e 's!@EEKBOARD_INSCRIPT_DATADIR@!'$(eekboard_inscript_datadir)'!' < $< > $@
CLEANFILES = eekboard-inscript
EXTRA_DIST = eekboard-inscript.in
#!/bin/sh
# Copyright (C) 2011 Daiki Ueno <ueno@unixuser.org>
# Copyright (C) 2011 Red Hat, Inc.
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2 of
# the License, or (at your option) any later version.
# This library 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
# Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301 USA
export EEKBOARD_KEYBOARDDIR=@EEKBOARD_KEYBOARDDIR@
export M17N_DIR=@M17N_DIR@
exec @PYTHON@ @EEKBOARD_INSCRIPT_DATADIR@/main.py $@
#!/usr/bin/env python
# Copyright (C) 2011 Daiki Ueno <ueno@unixuser.org>
# Copyright (C) 2011 Red Hat, Inc.
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2 of
# the License, or (at your option) any later version.
# This library 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
# Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301 USA
import eekboard
import gobject, gtk, virtkey
import sys, os.path, re
KEYCODE_TABLE = {
'1': 10, '2': 11, '3': 12, '4': 13, '5': 14, '6': 15, '7': 16, '8': 17,
'9': 18, '0': 19, '-': 20, '=': 21, 'q': 24, 'w': 25, 'e': 26, 'r': 27,
't': 28, 'y': 29, 'u': 30, 'i': 31, 'o': 32, 'p': 33, '[': 34, ']': 35,
'a': 38, 's': 39, 'd': 40, 'f': 41, 'g': 42, 'h': 43, 'j': 44, 'k': 45,
'l': 46, ';': 47, '\'': 48, '`': 49, '\\': 51, 'z': 52, 'x': 53, 'c': 54,
'v': 55, 'b': 56, 'n': 57, 'm': 58, ',': 59, '.': 60, '/': 61
}
MARK_UPPER = '~!@#$%^&*()_+{}|:"<>?'
MARK_LOWER = '`1234567890-=[]\\;\',./'
INSCRIPT_MAPS = (
"as-inscript",
"bn-inscript",
"gu-inscript",
"hi-inscript",
"kn-inscript",
"ml-inscript",
"mr-inscript",
"or-inscript",
"pa-inscript",
"sd-inscript",
"ta-inscript",
"te-inscript",
"kn-inscript2",
"kok-inscript2-deva",
"mai-inscript2",
"ml-inscript2",
"mni-inscript2-beng",
"mni-inscript2-mtei",
"mr-inscript2",
"ne-inscript2-deva",
"or-inscript2",
"pa-inscript2-guru",
"sa-inscript2",
"sat-inscript2-deva",
"sat-inscript2-olck",
"sd-inscript2-deva",
"ta-inscript2",
"te-inscript2")
class MapFile(object):
MAPENTRY_PATTERN = re.compile(r'\A\s*\((?:\((.*?)\)|"(.*?)")\s*"(.*?)"\)')
def __init__(self, path):
self.__dict = dict()
with open(path, 'r') as fp:
for line in fp:
match = re.match(self.MAPENTRY_PATTERN, line)
if match:
insert = match.group(3).decode('UTF-8')
if match.group(1):
keyseq = re.sub(r'\\(.)', r'\1', match.group(1))
self.__add_symbol_entry(keyseq, insert)
else:
keyseq = re.sub(r'\\(.)', r'\1', match.group(2))
self.__add_text_entry(keyseq, insert)
def get_entry_for_keycode(self, keycode):
return self.__dict.get(keycode)
def __add_entry(self, letter, level, insert):
if letter.isupper():
level |= 1
letter = letter.lower()
elif letter in MARK_UPPER:
level |= 1
letter = MARK_LOWER[MARK_UPPER.index(letter)]
keycode = KEYCODE_TABLE[letter]
if keycode not in self.__dict:
self.__dict[keycode] = list([None,None,None,None])
self.__dict[keycode][level] = insert
def __add_symbol_entry(self, symbol, insert):
level = 0
if symbol.startswith('G-'):
level |= 2
symbol = symbol[2:]
if not symbol.startswith('KP_'):
self.__add_entry(symbol, level, insert)
def __add_text_entry(self, text, insert):
self.__add_entry(text, 0, insert)
class Keyboard(gobject.GObject):
__gtype_name__ = "PYInscriptKeyboard"
__gsignals__ = {
'quit': (
gobject.SIGNAL_RUN_LAST,
gobject.TYPE_NONE,
()),
}
def __init__(self, client_name, map_path, kbd_path):
super(Keyboard, self).__init__()
self.__keyboard = self.__create_keyboard(map_path, kbd_path)
self.__eekboard = eekboard.Eekboard()
self.__context = self.__eekboard.create_context(client_name)
keyboard_id = self.__context.add_keyboard(self.__keyboard)
self.__context.set_keyboard(keyboard_id)
self.__keyboard.connect('key-pressed', self.__key_pressed_cb)
self.__keyboard.connect('key-released', self.__key_released_cb)
self.__virtkey = virtkey.virtkey()
self.__english = False
self.__eekboard.connect('destroyed', self.__destroyed_cb)
self.__context.connect('destroyed', self.__destroyed_cb)
self.__context.connect('notify::keyboard-visible', self.__notify_keyboard_visible_cb)
def __create_keyboard(self, map_path, kbd_path):
def __each_key(element, data):
keycode = element.get_keycode()
# keycode 37 is used to toggle English/Inscript
if keycode == 37:
matrix = eekboard.SymbolMatrix.new(2, 1)
keysym = eekboard.Keysym.new(0)
keysym.set_label("Ind")
keysym.set_category(eekboard.SymbolCategory.FUNCTION)
matrix.set_symbol(0, 0, keysym)
keysym = eekboard.Keysym.new(0)
keysym.set_label("Eng")
keysym.set_category(eekboard.SymbolCategory.FUNCTION)
matrix.set_symbol(1, 0, keysym)
element.set_symbol_matrix(matrix)
return
# group(0) is us keyboard
matrix = eekboard.SymbolMatrix.new(2, 4)
for l in xrange(4):
keysym = element.get_symbol_at_index(0, l, 0, 0)
matrix.set_symbol(0, l, keysym)
# group(1) is inscript keyboard
entry = data.get_entry_for_keycode(keycode)
for l in xrange(4):
if entry and entry[l]:
try:
keyval = gtk.gdk.unicode_to_keyval(ord(entry[l]))
keysym = eekboard.Keysym.new(keyval)
except:
keysym = eekboard.Keysym.new(0)
keysym.set_label(entry[l].encode('UTF-8'))
keysym.set_category(eekboard.SymbolCategory.LETTER)
print >> sys.stderr, "can't convert %s (%d) to keyval" % (entry[l], keycode)
else:
keysym = element.get_symbol_at_index(1, l, 0, 0)
matrix.set_symbol(1, l, keysym)
element.set_symbol_matrix(matrix)
def __each_section(element, data):
element.foreach_child(__each_key, data)
mapfile = MapFile(map_path)
keyboard = eekboard.XmlKeyboard(kbd_path,
eekboard.MODIFIER_BEHAVIOR_LATCH)
keyboard.foreach_child(__each_section, mapfile)
return keyboard
def __destroyed_cb(self, *args):
self.emit('quit')
def __notify_keyboard_visible_cb(self, obj, pspec):
if not obj.get_property(pspec.name):
self.emit('quit')
def enable(self):
self.__eekboard.push_context(self.__context)
def disable(self):
self.__eekboard.pop_context(self.__context)
def show(self):
self.__context.show_keyboard()
def set_group(self, group):
self.__group = group
self.__context.set_group(self.__group)
def __key_pressed_cb(self, keyboard, key):
if key.get_keycode() == 37:
return
symbol = key.get_symbol()
if isinstance(symbol, eekboard.Keysym):
xkeysym = symbol.get_xkeysym()
modifiers = self.__keyboard.get_modifiers()
self.__virtkey.latch_mod(modifiers)
self.__virtkey.press_keysym(xkeysym)
self.__virtkey.unlatch_mod(modifiers)
def __key_released_cb(self, keyboard, key):
if key.get_keycode() == 37:
if self.__english:
self.__context.set_group(self.__group)
self.__english = False
else:
self.__context.set_group(0)
self.__english = True
return
symbol = key.get_symbol()
if isinstance(symbol, eekboard.Keysym):
xkeysym = symbol.get_xkeysym()
self.__virtkey.release_keysym(xkeysym)
# Copyright (C) 2011 Daiki Ueno <ueno@unixuser.org>
# Copyright (C) 2011 Red Hat, Inc.
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2 of
# the License, or (at your option) any later version.
# This library 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
# Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301 USA
import inscript
import gtk
from optparse import OptionParser
import sys, os, os.path, glob
parser = OptionParser()
parser.add_option("-n", "--name=LANGCODE", dest="langcode",
help="Specify language code to LANGCODE",
metavar="LANGCODE")
parser.add_option("-l", "--list", dest="list", default=False,
action="store_true",
help="List available language codes")
(options, args) = parser.parse_args()
if options.list:
pat = os.path.join(os.getenv("M17N_DIR"), "*.mim")
for fname in sorted(glob.glob(pat)):
mname = os.path.basename(fname[:-4])
if mname in inscript.INSCRIPT_MAPS:
print mname
exit(0)
if options.langcode is None:
print >> sys.stderr, "Specify language code with -n"
exit(1)
map_path = os.path.join(os.getenv("M17N_DIR"), options.langcode + ".mim")
if not os.path.exists(map_path):
print >> sys.stderr, "%s not found" % map_path
exit(1)
kbd_path = os.path.join(os.getenv("EEKBOARD_KEYBOARDDIR"), "us-qwerty.xml")
if not os.path.exists(kbd_path):
print >> sys.stderr, "%s not found" % kbd_path
exit(1)
keyboard = inscript.Keyboard("eekboard-inscript", map_path, kbd_path)
keyboard.connect('quit', lambda *args: gtk.main_quit())
keyboard.set_group(1)
keyboard.enable()
keyboard.show()
gtk.main()
bin_SCRIPTS = eekxml
EXTRA_DIST = mim2remap
eekxml: eekxml.in
$(AM_V_GEN) sed '1s!@''PYTHON@!'$(PYTHON)'!' $< > $@
#!@PYTHON@
# -*- python -*-
# Copyright (C) 2011 Daiki Ueno <ueno@unixuser.org>
# Copyright (C) 2011 Red Hat, Inc.
# 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 3 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, see
# <http://www.gnu.org/licenses/>.
import argparse
import json
from gi.repository import GLib, Gtk, Eek, EekXkl, EekGtk, Gio
DEFAULT_WIDTH = 640
DEFAULT_HEIGHT = 480
# XXX: level3 shift is not supported yet in remap files
def remap(keyboard, mapping):
def __each_key(element, data):
matrix = element.get_symbol_matrix()
for level in xrange(matrix.num_levels):
symbol = matrix.get_symbol(0, level)
if isinstance(symbol, Eek.Keysym):
mapped = data.get(symbol.get_name(), None)
if mapped:
if mapped.has_key('xkeysym'):
replace = Eek.Keysym.new(mapped['xkeysym'])
else:
replace = Eek.Symbol.new(mapped['name'])
replace.set_category(Eek.SymbolCategory.LETTER)
if mapped.has_key('label'):
replace.set_label(mapped['label'])
if mapped.has_key('category'):
replace.set_category(mapped['category'])
matrix.set_symbol(0, level, replace)
def __each_section(element, data):
element.foreach_child(__each_key, data)
keyboard.foreach_child(__each_section, mapping)
def create_keyboard(args):
if args.file:
_file = Gio.file_new_for_path(args.file)
layout = Eek.XmlLayout.new(_file.read(None))
else:
layout = EekXkl.Layout.new()
if args.model:
layout.set_model(args.model)
if args.layout:
layout.set_layouts(args.layout)
if args.variant:
layout.set_variants(args.variant)
if args.option:
layout.set_options(args.option)
keyboard = Eek.Keyboard.new(layout, DEFAULT_WIDTH, DEFAULT_HEIGHT)
if args.remap_file:
with open(args.remap_file) as remap_file:
mapping = json.load(remap_file)
remap(keyboard, mapping)
return keyboard
def show(args):
keyboard = create_keyboard(args)
keyboard.set_modifier_behavior(Eek.ModifierBehavior.LATCH)
widget = EekGtk.Keyboard.new(keyboard)
if args.theme:
theme = Eek.Theme.new(args.theme, None, None)
widget.set_theme(theme)
window = Gtk.Window.new(Gtk.WindowType.TOPLEVEL)
bounds = keyboard.get_bounds()
window.set_default_size(bounds.width, bounds.height)
window.connect('destroy', lambda *args: Gtk.main_quit())
window.add(widget)
window.show_all()
Gtk.main()
def dump(args):
keyboard = create_keyboard(args)
output = GLib.String()
keyboard.output(output, 0)
print output.str
# Add a simple function to print usage, for the 'help' command
def usage(args, parser):
parser.print_help()
def parse_cmdline():
parser = argparse.ArgumentParser(
description='manipulate XML files used by eekboard')
subparsers = parser.add_subparsers(help='sub-command help')
parser_help = subparsers.add_parser('help', help = 'show usage')
parser_help.set_defaults(command = lambda args: usage(args, parser=parser))
parser_common = subparsers.add_parser('common', add_help=False)
parser_common.add_argument('--model', help='specify XKB model')
parser_common.add_argument('--layout', help='specify XKB layout')
parser_common.add_argument('--variant', help='specify XKB variant')
parser_common.add_argument('--option', help='specify XKB option')
parser_common.add_argument('--file', help='specify XML layout file')
parser_common.add_argument('--remap-file', help='remap keysyms with the file')
parser_show = subparsers.add_parser('show',
help='show help',
parents=[parser_common])
parser_show.add_argument('--theme', help='use the theme file')
parser_show.add_argument('--group', help='switch to the given group')
parser_show.set_defaults(command = show)
parser_dump = subparsers.add_parser('dump',
help='dump help',
parents=[parser_common])
parser_dump.set_defaults(command = dump)
return parser.parse_args()
if __name__ == '__main__':
args = parse_cmdline()
args.command(args)
#!/usr/bin/env python
# Copyright (C) 2011 Daiki Ueno <ueno@unixuser.org>
# Copyright (C) 2011 Red Hat, Inc.
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2 of
# the License, or (at your option) any later version.
# This library 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
# Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301 USA
import json
import gtk.gdk
import sys, os.path, re
class MapFile(object):
MAPENTRY_PATTERN = re.compile(r'\A\s*\((?:\((.*?)\)|"(.*?)")\s*(?:"(.*?)"|\?(.*?))\)')
def __init__(self, path):
self.__dict = dict()
with open(path, 'r') as fp:
for line in fp:
match = re.match(self.MAPENTRY_PATTERN, line)
if match:
if match.group(1):
keyseq = re.sub(r'\\(.)', r'\1', match.group(1))
# should check G-* and A-*
else:
keyseq = re.sub(r'\\(.)', r'\1', match.group(2))
# combination of keys is not supported
if len(keyseq) > 1:
continue
try:
keyval = gtk.gdk.unicode_to_keyval(ord(keyseq))
keyseq = gtk.gdk.keyval_name(keyval)
except:
pass
if match.group(3):
insert = match.group(3).decode('UTF-8')
else:
insert = match.group(4).decode('UTF-8')
replace = {}
if len(insert) > 1:
replace = { 'name': insert,
'label': insert }
else:
try:
keyval = gtk.gdk.unicode_to_keyval(ord(insert))
name = gtk.gdk.keyval_name(keyval)
replace = { 'name': name,
'label': insert,
'xkeysym': keyval }
except:
replace = { 'name': insert,
'label': insert }
self.__dict[keyseq] = replace
def __str__(self):
return json.dumps(self.__dict)
if __name__ == "__main__":
mapfile = MapFile(sys.argv[1])
print mapfile
......@@ -18,8 +18,7 @@
bin_PROGRAMS = \
eekboard \
eekboard-server \
eekboard-xml
eekboard-server