444 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
			
		
		
	
	
			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()
 |