alien-everywhere/shimming/alienkeyboardservice/alienkeyboard-maliit-shim

444 lines
14 KiB
Python
Executable File

#!/usr/bin/python3
import os
import asyncio
from pywayland.client import Display
from pywayland.protocol.wayland import (
WlSeat,
WlShell,
WlShm,
)
from protocols.text_input_unstable_v3 import ZwpTextInputManagerV3
import dbussy as dbus
from dbussy import \
DBUS, \
Introspection
import ravel
socket_path = os.environ["XDG_RUNTIME_DIR"] + "/maliit-server"
if not os.path.exists(socket_path):
os.makedirs(socket_path)
my_socket_name = "unix:path=" + socket_path + "/dbus-socket"
my_interface_name = "com.meego.inputmethod.uiserver1"
client_interface_name = "com.meego.inputmethod.inputcontext1"
idle_timeout = 15 # seconds, short value for testing
ClientInterface = ravel.def_proxy_interface \
(
ravel.INTERFACE.CLIENT,
name = "ClientInterface",
introspected =
Introspection.Interface
(
name = client_interface_name,
methods =
[
Introspection.Interface.Method
(
name = "setLanguage",
args =
[
Introspection.Interface.Method.Arg
(
name = "lang",
type = dbus.BasicType(dbus.TYPE.STRING),
direction = Introspection.DIRECTION.IN
),
],
),
Introspection.Interface.Method
(
name = "setRedirectKeys",
args =
[
Introspection.Interface.Method.Arg
(
name = "redi",
type = dbus.BasicType(dbus.TYPE.BOOLEAN),
direction = Introspection.DIRECTION.IN
),
],
),
Introspection.Interface.Method
(
name = "updateInputMethodArea",
args =
[
Introspection.Interface.Method.Arg
(
name = "x",
type = dbus.BasicType(dbus.TYPE.INT32),
direction = Introspection.DIRECTION.IN
),
Introspection.Interface.Method.Arg
(
name = "y",
type = dbus.BasicType(dbus.TYPE.INT32),
direction = Introspection.DIRECTION.IN
),
Introspection.Interface.Method.Arg
(
name = "width",
type = dbus.BasicType(dbus.TYPE.INT32),
direction = Introspection.DIRECTION.IN
),
Introspection.Interface.Method.Arg
(
name = "height",
type = dbus.BasicType(dbus.TYPE.INT32),
direction = Introspection.DIRECTION.IN
),
],
),
Introspection.Interface.Method
(
name = "commitString",
args =
[
Introspection.Interface.Method.Arg
(
name = "string",
type = dbus.BasicType(dbus.TYPE.STRING),
direction = Introspection.DIRECTION.IN
),
Introspection.Interface.Method.Arg
(
name = "replacementStart",
type = dbus.BasicType(dbus.TYPE.INT32),
direction = Introspection.DIRECTION.IN
),
Introspection.Interface.Method.Arg
(
name = "replacementLength",
type = dbus.BasicType(dbus.TYPE.INT32),
direction = Introspection.DIRECTION.IN
),
Introspection.Interface.Method.Arg
(
name = "cursorPos",
type = dbus.BasicType(dbus.TYPE.INT32),
direction = Introspection.DIRECTION.IN
),
],
),
],
),
is_async = True
)
useless_objects_bus_name = "org.maliit.server"
useless_objects_iface_name = "org.maliit.Server.Address"
@ravel.interface(ravel.INTERFACE.SERVER, name = useless_objects_iface_name)
class UselessObjectServer :
__slots__ = ("bus",)
def __init__(self, bus) :
self.bus = bus
#end __init__
@ravel.propgetter \
(
name = "address",
type = dbus.BasicType(dbus.TYPE.STRING),
change_notification = dbus.Introspection.PROP_CHANGE_NOTIFICATION.NEW_VALUE,
)
def get_address(self) :
return "unix:path=" + socket_path + "/dbus-socket"
#end get_address
object_created = ravel.def_signal_stub \
(
name = "object_created",
in_signature = [],
)
object_deleted = ravel.def_signal_stub \
(
name = "object_deleted",
in_signature = [],
)
#end UselessObjectServer
background_tasks = set()
def run_in_background(invocation):
task = asyncio.create_task(invocation)
background_tasks.add(task)
task.add_done_callback(background_tasks.discard)
@ravel.interface(ravel.INTERFACE.SERVER, name = my_interface_name)
class DirectConnectServer :
__slots__ = ("bus", "_last_request", "intf", "wayland")
def __init__(self, bus, wayland) :
self.bus = bus
self.wayland = wayland
self.intf = ClientInterface \
(
connection = self.bus.connection,
dest = "com.meego.inputmethod.inputcontext1", # no D-Bus daemon to care
)["/com/meego/inputmethod/inputcontext"]
#end __init__
# @ravel.method \
# (
# name = "reset",
# in_signature = "",
# out_signature = "",
# arg_keys = [],
# )
# def reset(self) :
# print("got reset() message")
# #end reset
@ravel.method \
(
name = "updateWidgetInformation",
in_signature = "a{sv}b",
out_signature = "",
arg_keys = ["vardict", "boolean"],
)
def updateWidgetInformation(self, vardict, boolean) :
# print("got updateWidgetInformation() message")
# print(vardict)
surrounding_text = vardict["surroundingText"][1]
cursor_position = vardict["cursorPosition"][1]
self.wayland["text_input"].set_surrounding_text(surrounding_text, cursor_position, cursor_position)
self.wayland["text_input"].commit()
self.wayland["display"].flush()
#end updateWidgetInformation
# @ravel.method \
# (
# name = "activateContext",
# in_signature = "",
# out_signature = "",
# arg_keys = [],
# )
# def activateContext(self) :
# print("got activateContext() message")
# #end activateContext
@ravel.method \
(
name = "appOrientationChanged",
in_signature = "i",
out_signature = "",
arg_keys = ["orientation"],
)
def appOrientationChanged(self, orientation) :
print("got appOrientationChanged() message")
#end appOrientationChanged
@ravel.method \
(
name = "showInputMethod",
in_signature = "",
out_signature = "",
arg_keys = [],
)
def showInputMethod(self) :
# print("got showInputMethod() message, setting input size")
self.wayland["text_input"].set_surrounding_text("magictext", 0, 0)
self.wayland["display"].flush()
self.wayland["text_input"].set_content_type(0, 0)
self.wayland["text_input"].enable()
self.wayland["text_input"].commit()
self.wayland["display"].flush()
run_in_background(self.intf.updateInputMethodArea(0, 1360, 1080, 670))
#end showInputMethod
@ravel.method \
(
name = "hideInputMethod",
in_signature = "",
out_signature = "",
arg_keys = [],
)
def hideInputMethod(self) :
# print("got hideInputMethod() message, setting input size")
self.wayland["text_input"].disable()
self.wayland["text_input"].commit()
self.wayland["display"].flush()
run_in_background(self.intf.updateInputMethodArea(0, 0, 0, 0))
#end hideInputMethod
# @ravel.method \
# (
# name = "setGlobalCorrectionEnabled",
# in_signature = "b",
# out_signature = "",
# arg_keys = ["enabled"],
# )
# def setGlobalCorrectionEnabled(self, enabled) :
# print("got setGlobalCorrectionEnabled() message")
# #end setGlobalCorrectionEnabled
# @ravel.method \
# (
# name = "setDetectableAutoRepeat",
# in_signature = "b",
# out_signature = "",
# arg_keys = ["detectable"],
# )
# def setDetectableAutoRepeat(self, detectable) :
# print("got setDetectableAutoRepeat() message")
# #end setDetectableAutoRepeat
#end DirectConnectServer
def handle_enter(text_input, surface):
# print("got enter event for surface ")
return 0
def handle_leave(text_input, surface):
# print("got leave event for surface ")
return 0
def handle_preedit_string(text_input, string, cursor_begin, cursor_end):
# print("got preedit str ")
return 0
def handle_commit_string(text_input, string):
# print("commit str: " + string + " sending to dbus")
wayland = text_input.user_data
run_in_background(wayland["dbus_server"].intf.commitString(string, 0, 0, 0))
return 0
def handle_delete_surrounding_text(text_input, before_len, after_len):
# print("got delete surrounding text")
return 0
def handle_done(text_input, serial):
# print("got a done event serial " + str(serial))
return 0
def handle_seat_capabilities(wl_seat, capabilities):
wayland = wl_seat.user_data
if "text_input_manager" not in wayland:
raise Exception("text_input_manager protocol is not supported by compositor")
wayland["text_input"] = wayland["text_input_manager"].get_text_input(wl_seat)
wayland["text_input"].user_data = wayland
wayland["text_input"].dispatcher["enter"] = handle_enter
wayland["text_input"].dispatcher["leave"] = handle_leave
# wayland["text_input"].dispatcher["preedit_string"] = handle_preedit_string
wayland["text_input"].dispatcher["commit_string"] = handle_commit_string
wayland["text_input"].dispatcher["delete_surrounding_text"] = handle_delete_surrounding_text
wayland["text_input"].dispatcher["done"] = handle_done
wayland["display"].flush()
return 1
def handle_registry_global(wl_registry, id_num, iface_name, version):
wayland = wl_registry.user_data
if iface_name == "wl_seat":
wayland["seat"] = wl_registry.bind(id_num, WlSeat, version)
wayland["seat"].user_data = wayland
wayland["seat"].dispatcher["capabilities"] = handle_seat_capabilities
elif iface_name == "zwp_text_input_manager_v3":
wayland["text_input_manager"] = wl_registry.bind(id_num, ZwpTextInputManagerV3, version)
wayland["display"].flush()
return 1
async def setup_dbus(wayland):
listen = ravel.Server(address = my_socket_name)
clients = {}
@ravel.signal \
(
in_signature = "",
bus_keyword = "conn"
)
def connection_terminated(conn) :
# handler for signal sent by libdbus when client disconnects.
print("connection from PID %s terminated\n" % conn.connection.unix_process_id)
del clients[id(conn)]
#end connection_terminated
while True :
conn = await listen.await_new_connection(timeout = idle_timeout / 3)
if conn != None :
print("new connection on DBus")
server = DirectConnectServer(conn, wayland)
wayland["dbus_server"] = server
conn.register \
(
path = "/com/meego/inputmethod/uiserver1",
fallback = True,
interface = server
)
conn.listen_signal \
(
path = "/",
fallback = True,
interface = DBUS.INTERFACE_LOCAL,
name = "Disconnected",
func = connection_terminated
)
clients[id(conn)] = conn
await server.intf.setLanguage('')
#end if
#end while
async def run_dbus_loop(wayland):
server = await setup_dbus(wayland)
display = Display()
display.connect()
loop = asyncio.new_event_loop()
loop.add_reader(display.get_fd(), lambda: display.dispatch(block=True))
wayland = {}
wayland["display"] = display
wayland["registry"] = wayland["display"].get_registry()
wayland["registry"].user_data = wayland
wayland["registry"].dispatcher["global"] = handle_registry_global
wayland["display"].flush()
bus = ravel.session_bus()
bus.attach_asyncio(loop)
bus.request_name \
(
bus_name = useless_objects_bus_name,
flags = DBUS.NAME_FLAG_DO_NOT_QUEUE
)
bus.register \
(
path = "/org/maliit/server",
fallback = True,
interface = UselessObjectServer(bus)
)
loop.create_task(run_dbus_loop(wayland))
loop.run_forever()