Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Buffer is read only after closing the launcher #7

Closed
sarg opened this issue Dec 3, 2024 · 10 comments
Closed

Buffer is read only after closing the launcher #7

sarg opened this issue Dec 3, 2024 · 10 comments

Comments

@sarg
Copy link
Contributor

sarg commented Dec 3, 2024

I've started using the URL launcher through the emacsclient-wrapper as recommend by README. I've noticed that after I close the launcher (either aborting it or selecting something), the next keypress would not be sent to qutebrowser window, raising a warning "Buffer is read only".

I've debugged it:

  • if I invoke (qutebrowser-launcher) from M-x, the issue is not present
  • if I close the launcher and wait a bit before making the first keypress, the issue is then not present as well

It seems that the problem is caused by the delay which takes emacsclient to actually exit after executing the statements from -e. If you call emacsclient -e "(call-interactively #'find-file)" and press Esc to close the dialog, you'll notice that it takes 1-2 more seconds to return control back.

@lrustand
Copy link
Owner

lrustand commented Dec 3, 2024

This is a problem I have been having as well, but I was not sure what was actually going on. I thought maybe it could be some weird interaction between EXWM and qutebrowser, but have not been sure how to debug it further. I didn't notice that it is timing related, I had thought that it need one or to key presses first to regain focus.

Since you seem to have found the cause of this behaviour we can hopefully figure out how to avoid it. I will try to look into ways to make Qutebrowser put it into the background or make emacsclient return immediately.

@sarg
Copy link
Contributor Author

sarg commented Dec 3, 2024

Maybe a better way would be to build a bi-directional communication channel between emacs and qutebrowser and avoid using emacsclient altogether. I.e. custom RPC "extension" could be injected by emacs using config.source and then emacs would connect to it as usual.

@lrustand
Copy link
Owner

lrustand commented Dec 3, 2024

I like the idea of a bi-directional communication. This would be an enabler for other features as well, and it would be great if we were able to fetch various pieces of information from qutebrowser.

I have been experimenting a little with implementing a hooks feature, which would need some supporting python code installed in the config.py or otherwise sourced in the qutebrowser instance. So I have also had the thought of injecting the python code automatically, but I'm not 100% sure it is a good idea.

Do you have any ideas for how we could implement an RPC feature?

@lrustand
Copy link
Owner

lrustand commented Dec 3, 2024

I did find this:
https://github.com/tkf/python-epc
https://github.com/kiwanami/emacs-epc
Which could look promising, but I'm a little hesitant of introducing dependencies to third-party python libraries. I think this would hurt the portability and add even more to an already too involved setup process, and I think it is generally a bad thing when emacs packages have external dependencies.

EDIT: I noticed now that the last commit in python-epc is 6 years ago, so it is probably not being maintained anymore.

@lrustand
Copy link
Owner

lrustand commented Dec 4, 2024

After thinking a bit more about this I think a simple approach based on unix sockets would be best. To achieve full bi-directional communication two sockets might be needed to avoid complicated synchronization issues.

I think the tricky thing is how to integrate the listener part inside qutebrowser.

@sarg
Copy link
Contributor Author

sarg commented Dec 4, 2024

Maybe subclassing IPCServer would be enough?

from qutebrowser.api import message
from qutebrowser.misc.ipc import IPCServer
from PyQt6.QtNetwork import *
from PyQt6.QtCore import *

class EmacsIPCServer(IPCServer):
    def _handle_data(self, data):
        message.info(str(data))
        socket = self._get_socket()
        stream = QTextStream(socket)
        stream << "Challenge Accepted\n"
        stream.flush()
        socket.waitForBytesWritten(1000)

server = EmacsIPCServer("/tmp/emacs-ipc")
server.listen()
$ socat - UNIX-CONNECT:/tmp/emacs-ipc 
hello
Challenge Accepted

@lrustand
Copy link
Owner

lrustand commented Dec 4, 2024

Nice!! I'll play around a little and implement some basic functionality. It could also bee nice to have an IPC the other way as well, but maybe not as important. One way is already a massive improvement from what we had before.

EDIT: What I mean with "one way" is that we now can have two-way communication in a request-response scheme but only with emacs on the request side. If we wanted request-response initiated by qutebrowser also, I think we would need an IPC in emacs also.

@lrustand
Copy link
Owner

lrustand commented Dec 5, 2024

I've played around with this a little, and it works great for a while, but the socket keeps disappearing quite frequently. It seems the socket gets deleted any time there is a larger event in qutebrowser, such as page reload, window open or close, etc. I'm not sure what exactly is causing this, I can't see anything in the IPC source code that suggests this should be happening.

Maybe it is a consequence of the context we are starting the IPC from? It seems as the IPC server is bound to the currently focused window when sourcing the file. Maybe the server gets killed when there is no reference to the object anymore, and that it could survive if we were able to assign it to a variable somewhere globally.

EDIT: This seems to possibly work:

from qutebrowser.api import message
from qutebrowser.misc.ipc import IPCServer
from PyQt6.QtNetwork import *
from PyQt6.QtCore import *

from qutebrowser.utils import objreg

class EmacsIPCServer(IPCServer):
    def _handle_data(self, data):
        message.info(str(data))
        socket = self._get_socket()
        stream = QTextStream(socket)
        stream << "Challenge Accepted\n"
        stream.flush()

server = EmacsIPCServer("/tmp/emacs-ipc")
objreg.register(name = "my-ipc", obj = server)
server.listen()

@lrustand
Copy link
Owner

lrustand commented Dec 6, 2024

Started working on the RPC feature, draft PR is here: #9.

@lrustand
Copy link
Owner

This issue will be fixed by emacs-exwm/exwm#94.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants