pazz / alot

Terminal-based Mail User Agent
GNU General Public License v3.0
702 stars 163 forks source link

Command to force redraw the screen. #850

Closed lucc closed 7 years ago

lucc commented 8 years ago

I am looking for a command to force alot to redraw the whole screen. refresh, call ui.update() and call ui.current_buffer.rebuild() don't do the trick. Is there anything else? Do we need a new command?

I have two problems where I need this.

One is when some process outside of alot writes to the notmuch database (fetchmail and a notmuch hook for example). Then a message is printed to random positions on the screen in alot: A Xapian exception occurred opening database: Unable to get write lock on /home/luc/mail/.notmuch/xapian: already locked. You can reproduce this by running a silly loop in another terminal (while true; do notmuch tag +some_strange_dummy_tag -some_strange_dummy_tag -- '*'; done) and then open alot and try to change a tag.

The other case is when I view text attachments in a pager. I have this mailcap file:

text/*;    less %s;
text/*;    cat  %s; copiousoutput;
text/html; elinks       %s; nametemplate=%s.html;
text/html; elinks -dump %s; nametemplate=%s.html; copiousoutput;

When I return from less (I hit enter in a thread buffer on some attachment) the screen is not cleared and redrawn.

I must admit that call ui.current_buffer.rebuild() did work for the second problem sometimes but it was not consistent and sometimes did make the arrow keys unusable until I quit the buffer. Also binding it to a key did not work.

TomasTomecek commented 8 years ago

urwid on its own has ctrl+l to redraw the screen:

https://github.com/urwid/urwid/blob/fd02b5e6f9b701d8a7f17846f4811ebd5cae587e/urwid/command_map.py#L43

lucc commented 8 years ago

@TomasTomecek that looks interesting. ctrl-l does not do anything for me, see log below. Can we activate this?

From this snippet of the debug log I assume that ctrl-l does nothing in alot:

...
DEBUG:ui:Got key (['ctrl l'], [12])
DEBUG:ui:Got key (['ctrl l'], [12])
DEBUG:ui:Got key (['q'], [113])
DEBUG:ui:cmdline: 'bclose'
...
pazz commented 8 years ago

@lucc: the first issue is a problem with libnotmuch printing to stderr. It was fixed a long time ago and the problem sneaked in again when the bindings were updated. I sent a patch to the notmuch list yesterday, that fixes this issue with the bindings. (https://notmuchmail.org/pipermail/notmuch/2016/021975.html).

regarding the C-l in urwid: I think it'd be easiest to check what it actually triggers, and then add that call to alots refresh command if its not already done..

lucc commented 7 years ago

I found a workaround for the specific problem with less in the mailcap file: There is a mailcap option called "needsterminal" and if I add it like this the screen is redrawn nicely when I exit less:

text/*; less %s; needsterminal;
text/*; cat %s; copiousoutput;

The database lock message is also not shown any more (at least when I tested it today). So I myself do not have this issue any longer. If nobody else wants to kep it open, we can close it.

pazz commented 7 years ago

thanks for the update. i'll close this and we can reopen the next time upstream notmuch decides it's a good idea to print to stder from the lib. shrugs

deepcube commented 2 months ago

I added a hook to run notmuch new in the foreground so I can see the progress (notmuch hook calls lieer to sync):

def getmail(ui=None):
    ui.notify("syncing mail..")
    subprocess.call("notmuch new".split())

I found this ticket because after that runs the output is never cleared until a line would be drawn there (e.g. a search with enough results to fill the entire terminal).

I looked into urwid, ctrl l is mapped to Command.REDRAW_SCREEN which is handled in MainLoop.process_input:

                if command_map[key] == Command.REDRAW_SCREEN:
                    self.screen.clear()

So I added that clear in UI.update() when redraw=True:

        # force a screen redraw
        if self.mainloop.screen.started and redraw:
            self.mainloop.screen.clear()
            self.mainloop.draw_screen()

And that solves the problem. But looking deeper I think that might clear the screen too often. It may make sense to create a separate command as mentioned above.

So I don't have a full solution right now, but a hack that works for me, and hopefully knowing what urwid does can help the next person who wants to look at this.