]> glassweightruler.freedombox.rocks Git - waydroid.git/blobdiff - tools/actions/initializer.py
gpu: Disable nouveau support
[waydroid.git] / tools / actions / initializer.py
index 89bbf576b149287f7ebf00a85cc28b0c651c6b82..2ce6cb4bad1ce404c1869f8b7729af519892f644 100644 (file)
@@ -5,15 +5,11 @@ import os
 from tools import helpers
 import tools.config
 
-from tkinter import *
-from tkinter import ttk
-
 import sys
 import threading
-class Daemon(threading.Thread):
-    def __init__(self):
-        super().__init__()
-        self.daemon = True
+import multiprocessing
+import select
+import queue
 
 def is_initialized(args):
     return os.path.isfile(args.config) and os.path.isdir(tools.config.defaults["rootfs"])
@@ -97,7 +93,7 @@ def setup_config(args):
     cfg["waydroid"]["hwbinder"] = args.HWBINDER_DRIVER
     tools.config.save(args, cfg)
 
-def do_init(args):
+def init(args):
     if not is_initialized(args) or args.force:
         setup_config(args)
         status = "STOPPED"
@@ -123,86 +119,230 @@ def do_init(args):
     else:
         logging.info("Already initialized")
 
-def init(args):
-    if args.gui:
-        gui_init(args)
-    else:
-        do_init(args)
-
-def gui_init(args):
-    if is_initialized(args) and not args.force:
+def wait_for_init(args):
+    helpers.ipc.create_channel("init")
+    helpers.ipc.create_channel("remote_init_output")
+    while True:
+        print('WayDroid waiting for initialization...')
+        msg = helpers.ipc.read_one(channel="init")
+        if msg == "done":
+            if is_initialized(args):
+                break
+            else:
+                continue
+        if msg.startswith("cmd"):
+            remote_init_server(args, msg)
+            continue
+
+def background_remote_init_process(args):
+    with helpers.ipc.open_channel("remote_init_output", "wb") as channel_out:
+        class StdoutRedirect(logging.StreamHandler):
+            def write(self, s):
+                channel_out.write(str.encode(s))
+            def flush(self):
+                pass
+            def emit(self, record):
+                if record.levelno >= logging.INFO:
+                    self.write(self.format(record) + self.terminator)
+
+        out = StdoutRedirect()
+        sys.stdout = sys.stderr = out
+        logging.getLogger().addHandler(out)
+
+        ctl_queue = queue.Queue()
+        def try_init(args):
+            try:
+                init(args)
+            except Exception as e:
+                print(str(e))
+            finally:
+                ctl_queue.put(0)
+
+        def poll_pipe():
+            poller = select.poll()
+            poller.register(channel_out, select.POLLERR)
+            poller.poll()
+            # When reaching here the client was terminated
+            ctl_queue.put(0)
+
+        init_thread = threading.Thread(target=try_init, args=(args,))
+        init_thread.daemon = True
+        init_thread.start()
+
+        poll_thread = threading.Thread(target=poll_pipe)
+        poll_thread.daemon = True
+        poll_thread.start()
+
+        # Join any one of the two threads
+        # Then exit the subprocess to kill the remaining thread.
+        # Can you believe this is the only way to kill a thread in python???
+        ctl_queue.get()
+
+        sys.stdout = sys.__stdout__
+        sys.stderr = sys.__stderr__
+        logging.getLogger().removeHandler(out)
+
+def remote_init_server(args, cmd):
+    params = cmd.split('\f')[1:]
+    args.force = True
+    args.images_path = ""
+    args.rom_type = ""
+    args.system_channel = params[0]
+    args.vendor_channel = params[1]
+    args.system_type = params[2]
+
+    p = multiprocessing.Process(target=background_remote_init_process, args=(args,))
+    p.daemon = True
+    p.start()
+    p.join()
+
+def remote_init_client(args):
+    # Local imports cause Gtk is intrusive
+    import gi
+    gi.require_version("Gtk", "3.0")
+    from gi.repository import Gtk, GLib
+
+    if is_initialized(args):
+        helpers.ipc.notify(channel="init", msg="done")
         return
 
-    root = Tk()
-    root.title("Initialize Waydroid")
-    root.iconphoto(True, PhotoImage(file="/usr/lib/waydroid/data/AppIcon.png"))
-    frm = ttk.Frame(root, padding=10)
-    frm.grid()
-
-    systemChannel = StringVar(frm, args.system_channel or tools.config.channels_defaults["system_channel"])
-    ttk.Label(frm, text="System OTA").grid(row=0, column=0)
-    ttk.Entry(frm, textvariable=systemChannel).grid(row=0, column=1, ipadx=20)
-
-    vendorChannel = StringVar(frm, args.vendor_channel or tools.config.channels_defaults["vendor_channel"])
-    ttk.Label(frm, text="Vendor OTA").grid(row=1, column=0)
-    ttk.Entry(frm, textvariable=vendorChannel).grid(row=1, column=1, ipadx=20)
-
-    systemType = StringVar(frm)
-    systemTypes = ["VANILLA", "GAPPS"]
-    ttk.Label(frm, text="Android Type").grid(row=2, column=0)
-    ttk.OptionMenu(frm, systemType, args.system_type or systemTypes[0], *systemTypes).grid(row=2, column=1)
-
-    done = ttk.Button(frm, text="Done", command=root.destroy)
-
-    logBox = Text(frm, borderwidth=3, relief="sunken", height=5)
-    logBox.bind("<Key>", lambda e: "break")
-
-    class StdoutRedirect(logging.StreamHandler):
-        def write(self, s):
-            if s.startswith('\r'):
-                logBox.delete("end-1l", "end")
-                logBox.insert(END, '\n')
-                s = s[1:]
-
-            logBox.insert(END, s)
-            logBox.see(END)
-        def flush(self):
-            pass
-        def emit(self, record):
-            if record.levelno >= logging.INFO:
-                self.write(self.format(record) + self.terminator)
-
-    out = StdoutRedirect()
-    sys.stdout = sys.stderr = out
-    logging.getLogger().addHandler(out)
-
-    def runInit():
-        download["state"] = DISABLED
-        logBox.grid(row=4, columnspan=2)
-
-        args.system_channel = systemChannel.get()
-        args.vendor_channel = vendorChannel.get()
-        args.system_type = systemType.get()
-
-        class Runner(Daemon):
-            def run(self):
+    def notify_and_quit(caller):
+        if is_initialized(args):
+            helpers.ipc.notify(channel="init", msg="done")
+        GLib.idle_add(Gtk.main_quit)
+
+    class WaydroidInitWindow(Gtk.Window):
+        def __init__(self):
+            super().__init__(title="Initialize Waydroid")
+            self.set_default_size(600, 250)
+            self.set_icon_from_file(tools.config.tools_src + "/data/AppIcon.png")
+
+            grid = Gtk.Grid(row_spacing=6, column_spacing=6, margin=10, column_homogeneous=True)
+            grid.set_hexpand(True)
+            grid.set_vexpand(True)
+            self.add(grid)
+
+            sysOtaLabel = Gtk.Label("System OTA")
+            sysOtaEntry = Gtk.Entry()
+            sysOtaEntry.set_text(tools.config.channels_defaults["system_channel"])
+            grid.attach(sysOtaLabel, 0, 0, 1, 1)
+            grid.attach_next_to(sysOtaEntry ,sysOtaLabel, Gtk.PositionType.RIGHT, 2, 1)
+            self.sysOta = sysOtaEntry.get_buffer()
+
+            vndOtaLabel = Gtk.Label("Vendor OTA")
+            vndOtaEntry = Gtk.Entry()
+            vndOtaEntry.set_text(tools.config.channels_defaults["vendor_channel"])
+            grid.attach(vndOtaLabel, 0, 1, 1, 1)
+            grid.attach_next_to(vndOtaEntry, vndOtaLabel, Gtk.PositionType.RIGHT, 2, 1)
+            self.vndOta = vndOtaEntry.get_buffer()
+
+            sysTypeLabel = Gtk.Label("Android Type")
+            sysTypeCombo = Gtk.ComboBoxText()
+            sysTypeCombo.set_entry_text_column(0)
+            for t in ["VANILLA", "GAPPS"]:
+                sysTypeCombo.append_text(t)
+            sysTypeCombo.set_active(0)
+            grid.attach(sysTypeLabel, 0, 2, 1, 1)
+            grid.attach_next_to(sysTypeCombo, sysTypeLabel, Gtk.PositionType.RIGHT, 2, 1)
+            self.sysType = sysTypeCombo
+
+            downloadBtn = Gtk.Button("Download")
+            downloadBtn.connect("clicked", self.on_download_btn_clicked)
+            grid.attach(downloadBtn, 1,3,1,1)
+            self.downloadBtn = downloadBtn
+
+            doneBtn = Gtk.Button("Done")
+            doneBtn.connect("clicked", lambda x: self.destroy())
+            doneBtn.get_style_context().add_class('suggested-action')
+            grid.attach_next_to(doneBtn, downloadBtn, Gtk.PositionType.RIGHT, 1, 1)
+            self.doneBtn = doneBtn
+
+            outScrolledWindow = Gtk.ScrolledWindow()
+            outScrolledWindow.set_hexpand(True)
+            outScrolledWindow.set_vexpand(True)
+            outTextView = Gtk.TextView()
+            outTextView.set_property('editable', False)
+            outTextView.set_property('cursor-visible', False)
+            outScrolledWindow.add(outTextView)
+            grid.attach(outScrolledWindow, 0, 4, 3, 1)
+            self.outScrolledWindow = outScrolledWindow
+            self.outTextView = outTextView
+            self.outBuffer = outTextView.get_buffer()
+            self.outBuffer.create_mark("end", self.outBuffer.get_end_iter(), False)
+
+            self.open_channel = None
+
+        def scroll_to_bottom(self):
+            self.outTextView.scroll_mark_onscreen(self.outBuffer.get_mark("end"))
+
+        def on_download_btn_clicked(self, widget):
+            widget.set_sensitive(False)
+            self.doneBtn.hide()
+            self.outTextView.show()
+            init_params = (self.sysOta.get_text(), self.vndOta.get_text(), self.sysType.get_active_text())
+            init_runner = threading.Thread(target=self.run_init, args=init_params)
+            init_runner.daemon = True
+            init_runner.start()
+
+        def run_init(self, systemOta, vendorOta, systemType):
+            def draw_sync(s):
+                if s.startswith('\r'):
+                    last = self.outBuffer.get_iter_at_line(self.outBuffer.get_line_count()-1)
+                    last.backward_char()
+                    self.outBuffer.delete(last, self.outBuffer.get_end_iter())
+                self.outBuffer.insert(self.outBuffer.get_end_iter(), s)
+                self.scroll_to_bottom()
+            def draw(s):
+                GLib.idle_add(draw_sync, s)
+
+            if self.open_channel is not None:
+                self.open_channel.close()
+                # Wait for other end to re-open
+                tmp = helpers.ipc.open_channel("init", "w", buffering=1)
+                tmp.close()
+
+            draw("Waiting for waydroid container service...\n")
+            try:
+                helpers.ipc.notify_blocking(channel="init", msg="{}\f{}\f{}\f{}".format(
+                    "cmd", self.sysOta.get_text(), self.vndOta.get_text(), self.sysType.get_active_text()))
+            except:
+                draw("The waydroid container service is not listening\n")
+                GLib.idle_add(self.downloadBtn.set_sensitive, True)
+                return
+
+            with helpers.ipc.open_channel("remote_init_output", "rb") as channel:
+                self.open_channel = channel
+                GLib.idle_add(self.downloadBtn.set_sensitive, True)
+                line = ""
                 try:
-                    do_init(args)
-                    if is_initialized(args):
-                        done.grid(row=5, columnspan=2)
-                        print("Done")
-                    else:
-                        download["state"] = NORMAL
-                except Exception as e:
-                    print("ERROR: " + str(e))
-                    download["state"] = NORMAL
-
-        Runner().start()
-
-    download = ttk.Button(frm, text="Download", command=runInit)
-    download.grid(row=3, columnspan=2)
-    root.mainloop()
-
-    sys.stdout = sys.__stdout__
-    sys.stderr = sys.__stderr__
-    logging.getLogger().removeHandler(out)
+                    while True:
+                        data = channel.read(1)
+                        if len(data) == 0:
+                            draw(line)
+                            break
+                        c = data.decode()
+                        if c == '\r':
+                            draw(line)
+                            line = c
+                        else:
+                            line += c
+                            if c == '\n':
+                                draw(line)
+                                line = ""
+                except:
+                    draw("\nInterrupted\n")
+
+            if is_initialized(args):
+                GLib.idle_add(self.doneBtn.show)
+                draw("Done\n")
+
+
+    GLib.set_prgname("Waydroid")
+    win = WaydroidInitWindow()
+    win.connect("destroy", notify_and_quit)
+
+    win.show_all()
+    win.outTextView.hide()
+    win.doneBtn.hide()
+
+    Gtk.main()