从今天开始,Emacs里面可以运行任何你想要的程序 (Linux & Windows & Mac & BSD)

赞,Windows目前主要的工作是:

  1. 直接运行DBus Daemon或者用其他IPC替换DBus
  2. 找到代码中路径是否包括 / ,换成跨平台的路径链接代码
  3. 增加Windows二进制文件的支持(通常是exe)

应该就可以直接跑起来了。

1 个赞

考察了一下,DBus可以用WebSocket来替代,Elisp和Python都有相应的WebSocket库,可以用来做Python和Elisp两边的双向通讯。

2 个赞

我试着搞一下

花了一个小时尝试了一下,WebSocket Elisp和Python两边通讯应该没啥问题。

但是WebSocket Python端几乎是强制异步纤程,和Qt的多线程比较难以融合。

DBus IPC目前没啥好的替换方式,现在比较可行的方式是,直接在Windows和Mac上把DBus Daemon给弄起来,应该就可以直接跑了。

2 个赞

我今天测试了最新版的EAF,是可以直接支持Gnome3和Wayland的。

2 个赞

在 macos 上编译了一个带 dbus 的 emacs,然后 brew install dbus 并且 brew services start dbus 了, (require 'eaf) 出现了报错, (dbus-error (“No connection to bus” :session)), 看到是 (dbus-register-signal (:session “com.lazycat.eaf” … 那行出来的。

怀疑 mac 上的 dbus 是不是正常运行了,能测试么?

Emacs本身就带DBus功能,但是DBus需要系统先启动一个DBus Daemon, 而Linux是默认就启动DBus Daemon.

现在需要解决的问题是,让Windows和Mac默认启动DBus Daemon。

之前论坛有人折腾过 Mac 上 dbus,你可以找找

mac 上的 dbus 搞定了,不过 eaf 出不来,具体表现, 比如 eaf-open-demo 命令,然后 qt 的那个窗口出不来。实际观察,是出现了一个 PyQt 的窗口,感觉也覆盖到 emacs 上了,因为此时 emacs 是不能操作的,但是没有任何内容。把这个窗口退掉之后, emacs 又可以正常响应和操作了。我录了一个gif eaf

如果mac下dbus没问题,应该就是上面我画圈的问题,导致gpu混合的镜像没法显示出来。

这个搞定就可以了

要命了,试了下,暂时还没到 QGraphicsScene 这里,是 eaf.py 里面用的 qapplication 的exec_() 作为 mainloop, 但是dbus 本身用的 DBusGMainLoop, 然后 mac 下面的 dbus 接到消息就不给转发到对应的函数来处理(dbus-monitor 能看到这个消息发到 dbus 了,但是没走到 eaf 里面的对应函数中,一直卡在这等待超时)。这个地方换成 glib 的 mainloop 就可以接到消息转发。但是后面的 PyQt 程序就跑不起来了。

用 QtDBus 替换了一下相应的 dbus method, 然后没管那些 signal, 试了一下,dbus 可以通信了,但是好像 reparent 在 macos 下面依然不行。我去掉 reparent 调用之后,eaf 窗口出来了。

你可以写一下最小的Qt程序,先别管EAF,然后获得Emacs的Windows id, 然后用 QWindow::setParent 试一下。

是那个 fromWinId 调用就挂了。emacs 传过来的 xid 也不对,我拿 hs 抓的 id, 传进去也不对。google 了一下,好像说 macosx 下面的 formWinId 参数是一个指针还是什么,然后跨进程无法获取。 Qt 文档也很贼,写啥 on platforms which support it …, 要不干脆点说 macos 不支持,也就死心了。 下面的代码执行到 fromWinId 就直接 seg fault 了。

import sys
from PyQt5.QtWidgets import QMainWindow, QPushButton, QApplication
from PyQt5.QtCore import QSize
from PyQt5.QtGui import QWindow

class HelloWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)

        self.setMinimumSize(QSize(640, 480))
        self.setWindowTitle("Hello world")

        btn = QPushButton(self)
        self.setCentralWidget(btn)
        btn.setText("Push to reparent")
        btn.clicked.connect(self.reparent)

    def reparent(self):
        try:
            qwindow = self.windowHandle()
            emacs_wid = 98
            print("qwindow is ", qwindow)
            emacs_window = QWindow.fromWinId(emacs_wid)
            print("emacs window is ", emacs_window)
            qwindow.setParent(emacs_window)
        except Exception:
            import traceback
            traceback.print_exc()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    mainWin = HelloWindow()
    mainWin.show()
    sys.exit(app.exec_())

我觉得通信可以直接拿万金油tcp来搞啊,所有平台都受用。上面拿msgpack来做通信格式

dbus有跨进程信号处理,不仅仅是发消息

mac下看看有没有替换 qwindow::setparent的api?

可以做特殊处理

Mac如果你的DBus搞定了,消息通了都好说。 剩下的问题是:

  1. Mac下是否有本地API可以替换 window::setParent 实现跨进程粘贴? 这样我们可以针对Mac平台提供代码分支处理
  2. 在跨进程粘贴没有问题后,看看 QGraphicsScene 是否可以工作,这个不工作没法让EAF的QT窗口实现Emacs的 “统一Buffer, 不同Windows” 的窗口模型

目前我看到的技术障碍就这些,加油!

2 个赞

Windows 移植进展:

用 websocket + jsonrpc 写了一个 demo 代替dbus 后 ,eaf 已经基本能够跑起来了 下图是浏览器的效果

12 个赞

牛逼啊。

EAF中的DBus主要有三个作用:

  1. Elisp通过DBus方法调用Python进程方法,主要是EAF启动或者按键传递的时候用
  2. Python进程通过DBus信号传递消息给Emacs,让Emacs来处理一些事情,比如聚焦到Emacs、发送Minibuffer消息、在Emacs中执行代码等
  3. Python进程通过DBus方法实时调用Emacs的某些方法,比如EAF会实时检测Emacs的主题状态,跟着一起变EAF应用的背景颜色

加油啊,看起来Windows运行良好,如果上面三种通讯方式都搞定,就可以默认替换DBus,来减少跨平台运行的门槛。

4 个赞