Friday, December 11, 2020

Making high-resolution screenshots of Emacs frames

Emacs 27.1 can utilise Cairo drawing backend to take screenshots of itself via x-export-frames function. Unfortunately, the bare bone function is all we have here–there's no UI to it. Moreover, it doesn't support bitmap fonts, which means if you still use, say, Terminus, you get garbage in the output.

I wanted to share a screenshot of a Emacs frame on twitter. Twitter doesn't accept SVGs, for net income of $1.47bn isn't enough to support such a complex thing. The best way to obtain an arbitrary high-resolution png is to get it from a vector image. I found that postscript->png gives the best results & requires only ghostscript installed.

(defun my--screenshot-png(out)
"Save a screenshot of the current frame as a png file. Requires ghostscript."
(let ((ps (concat out ".tmp")))
(my--screenshot ps 'postscript)
(call-process "gs" nil (get-buffer-create "*Shell Command Output*") nil
"-sDEVICE=png16m" "-dBATCH" "-dNOPAUSE"
"-r300" "-dTextAlphaBits=4" "-dGraphicsAlphaBits=4"
(concat "-sOutputFile=" out) ps)
(delete-file ps)
))

We use 300 dpi here to render a png. my--screenshot function below temporally changes a frame font to Inconsolata:

(defun my--screenshot(out format)
(let ((fontdef (face-attribute 'default :font)))
(set-frame-font "Inconsolata 10")
(unwind-protect
(with-temp-file out
(insert (x-export-frames nil format)))
(set-frame-font fontdef))
))

The last bit left is to provide a prompt for a user where to save the screenshot:

(defun my-ss()
"Save a screenshot of the current frame in a file"
(interactive)
(let* ((out (expand-file-name (read-file-name "Output file name: ")))
(ext (file-name-extension out)))
(cond
((equal "png" ext)
(my--screenshot-png out))
((equal "ps" ext)
(my--screenshot out 'postscript))
(t
(my--screenshot out (intern ext)))
)))

E.g.:

M-x my-ss<RET>
Output file name: ~/Downloads/1.png<RET>

The physical image size here is 3133x3642.

How I read newsgroups in mutt

It took me a long time, but I've finally removed inn+newsstar from my machine. I don't use patches that add NNTP support to mutt any more. Yet, I still cannot put gmane away, for it's much more convenient to read mailing lists as newsgroups.

What if I just fetch last N posts from newsgroup foo.a & save them in an mbox file for viewing it mutt later on? Then I can do the same for newsgroups foo.b, foo.c, & so on.

How do I fetch? Turns out, there is a nice CLI NNTP client already, called sinntp. The following command downloads fresh articles from comp.lang.c into a conspicuously named mbox file comp.lang.c:

$ sinntp pull --server news.eternal-september.org comp.lang.c

If you run it again, it won't re-download the same articles again, for it saves reported high water mark in ~/.local/share/sinntp/comp.lang.c@news.eternal-september.org file.

This only solves a problem for 1 news server & 1 newsgroup. I read multitudes of them; should I write a simple shell script then? If you follow this blog, you may have noticed I try not to employ shell scripts but write makefiles instead.

$ cat ~/.config/nntp2mbox/news.eternal-september.org.conf
comp.lang.c

This states that I want to grab articles from comp.lang.c newsgroup from news.eternal-september.org news server.

$ cat ~/.config/nntp2mbox/news.gmane.io.conf
gmane.comp.gnu.make.devel
gmane.comp.gnu.make.general
#gmane.comp.web.chromium.extensions
gmane.comp.window-managers.fvwm

In this example, the server name is news.gmane.io & 1 of the newsgroups is commented out.

There's no more configuration, everything else is done by nntp2mbox makefile:

#!/usr/bin/make -f

# a number of article to pull
limit := 500
g :=

conf := $(or $(XDG_CONFIG_HOME),~/.config)/nntp2mbox
servers := $(wildcard $(conf)/*.conf)
self := $(lastword $(MAKEFILE_LIST))

all: $(servers:%.conf=%.server)

# read a list of newsgroups & run Make for each newsgroup
%.server: %.conf
awk '!/^#/ {print $$1 ".newsgroup"}' $< | grep $(call se,$(g)) | xargs -r $(make) -Bk server=$(notdir $(basename $<))

%.newsgroup:
sinntp pull --server $(server) --limit $(limit) $*

make = $(MAKE) --no-print-directory -f $(self)
se = '$(subst ','\'',$1)'

The following command downloads fresh articles from all the newsgroups (from all the news servers above) to the current directory:

$ nntp2mbox
awk '!/^#/ {print $1 ".newsgroup"}' /home/alex/.config/nntp2mbox/localhost.conf | grep '' | xargs -r /usr/bin/make --no-print-directory -f /home/alex/bin/nntp2mbox -Bk server=localhost
awk '!/^#/ {print $1 ".newsgroup"}' /home/alex/.config/nntp2mbox/news.eternal-september.org.conf | grep '' | xargs -r /usr/bin/make --no-print-directory -f /home/alex/bin/nntp2mbox -Bk server=news.eternal-september.org
sinntp pull --server news.eternal-september.org --limit 500 comp.lang.c
awk '!/^#/ {print $1 ".newsgroup"}' /home/alex/.config/nntp2mbox/news.gmane.io.conf | grep '' | xargs -r /usr/bin/make --no-print-directory -f /home/alex/bin/nntp2mbox -Bk server=news.gmane.io
sinntp pull --server news.gmane.io --limit 500 gmane.comp.gnu.make.devel
sinntp pull --server news.gmane.io --limit 500 gmane.comp.gnu.make.general
sinntp pull --server news.gmane.io --limit 500 gmane.comp.window-managers.fvwm

(Yes, it invokes Make recursively, which is a big no-no in many Make circles.)

$ ls
comp.lang.c gmane.comp.gnu.make.general
gmane.comp.gnu.make.devel gmane.comp.window-managers.fvwm

It even supports filtering by a newsgroup name:

$ nntp2mbox g=fvwm

I don't actually read comp.lang.c. If there's anything sane left in comp.* hierarchy, please let me know.

Thursday, December 10, 2020

Reading the Emacs User Survey 2020 Results

More than a month ago some guy made a survey of emacs users. A couple of days ago, he released the results alongside with raw data.

After importing Emacs-User-Survey-2020-clean.csv into sqlite (7,344 rows), the first thing I checked was if someone had mentioned any of my emacs packages &, I kid you not, I got 9 hits for wordnut! Yipee!

Then I started filtering by "For how many years have you been using Emacs?" column. The amount of matched old-timers was staggering (I expected to find next to none):

  • >= 20 years: 1,497 rows
  • >= 15: 2,058
  • >= 10: 2,975

Here's a tiny portion of interesting/hilarious entries:

Usage
(years)
Favourite
packages
Community
forums
Difficulties
while learning
42 Finnish Ispell usenet No cursor keys on ADM video terminal in 1978

God knows what is the ADM terminal & where did he get in Finland.

Usage
(years)
Difficulties
while learning
Wishings
41 When I got booted into TECO, I was like WTF is this?? Did my modem disconnect? brain transplant
Be written in Common Lisp
41 I often got stuck inside multiple ^R recursive edits but once I understood it was because of mini buffer exits I was ok. Nowadays it's less of an issue.
40 Get rid of "kill" from nomenclature, commands, etc. Why "kill-emacs" instead of "exit-emacs"? (I remapped that one decades ago; maybe it's changed in the district but my command still works). I dislike violence and wish kill buffers were no so named. This may seem minor, but if you don't think that language matters, perhaps you haven't heard the rants of the USA's current president.
40 Stop changing behaviors of next-line, search, etc.
39 Emacs' TECO macros were impenetrable. Nothing comes to mind. I drank the Kool-Aid a loong time ago.
35 stop styling my text with weird font lock crap. gets increasingly hard to turn off
like to see vector drawing and variable width fonts in core
35 not really… the only difficulty is it wasn't on all the machines I used. I first used it on a VAX 11/780 having access to a bootleg hacked version crafted to run on the VAX under VMS. The guys that created that version were cad guys in DEC (AI CAD group) and I was lucky to have it… rather than being stuck with EDT.
But then I started using an Apollo and lost it. So I tried to write my own Emacs. from scratch. I was sort of successful but who has that much extra time… then came the Sun and unix and the HP 9000 and then came Linux (finally) and I had Emacs most everywhere.
35 The learning curve is steep, but quick. I got nuthin.
34 my init file quickly became scrambled up because I pasted in code that I didn't understand nor know how to organize why did I have to figure out how to correctly compile emacs 27 on the latest ubuntu? Why wasn't a package immediately available on all OSs when 27.1 was released?

You'd think that after 34 years of using Emacs, one would be able to discern Emacs maintainers from maintainers of the emacs package in their Linux distro, but no.

Usage
(years)
Difficulties
while learning
Wishings
30 I liked it in the old days when, when you ran a repeating macro you would see all the changes zipping through on the screen.
29 Coming from Glosling Emacs, I didn't understand why ^T was so broken in GNU Emacs. In all honesty, when there's a new version of emacs I mostly spend time figuring out how to turn new abominations off or put them back to how they should be.
It would be a big improvement for me if Emacs stuck to text and didn't try to do things with images, tables etc. When I paste anything into Emacs, it should either turn into text or fail.
And more speed is always welcome.
27 This is 27 years ago and at that time everybody considered it cool to know emacs. So, no. Not really… I am professor in a computer science department and teach students. I provide for colleagues and friends a heavily adapted version of emacs that (I believe) is more user-friendly. Nevertheless, it is sad to see that students are not even interested anymore to learn emacs. So, I think the most pressing need is to have a simplified user interface that adheres to the usual standards (similar to what ergoemacs is trying to achieve). All basic functionality should be on menus, keyboard shortcuts should only be an add-on for power users.
26 Everything was hard. Copy paste. Saving files. The UI sort of sucks
25 Terrible, useless documentation
25 I don't understand elisp Responsiveness of the interface: Emacs sometimes feel slow.
23 More than 23 years ago, it was mandatory at my university. Key bindings and the like felt very alien.

What some people think about poor Richard Stallman:

  • RMS should resign so that politics stops guiding Emacs development. It is a tragedy that a great editor continues to be crippled because technical decisions are made for outdated ideological reasons. I would love to contribute but Emacs development is extremely hostile to any non-purist views.
  • Stop letting RMS block good ideas.
  • There appears to be a split between the core developers and the "package" developers. I am confused by the role that RMS still plays in Emacs stewardship, and puzzled that he is not familiar with org-mode.
  • RMS's computing habits are so completely beyond what's normal that he has no idea what modern users want in an editor. If you want emacs to be popular you have to ACTUALLY LISTEN TO FEEDBACK FROM NEW USERS instead of a bunch of greybeards going "oh well emacs is fine for me".
  • I've considered dropping emacs altogether a few times because of RMS's behavior. The one thing I would like emacs to do is to stop having any affiliation with him.
  • Ignore RMS's opinions going forward.
  • … from reading the exchanges on the mailing list and especially RMS' opposition to anything "newfangled" has discouraged me from even trying to contribute to the core.

Comments about the survey itself (I feel sorry for the guy who organized it):

  • I've refused to answer surveys that require proprietary JavaScript before. It's unacceptable for a community survey to demand cooperation with a corporation. I wouldn't've answered this survey were mailing this not an option. Of course, I'd issues sending this response, as I learned what server lay underneath the EMACSSURVEY.ORG domain MX records. It would be better if the SMTP servers were run differently, even by another business than that.
  • The last question on this page [What is the default keybinding to find a file?] stinks to high heaven. I don't know the answer, because my fingers do. But the real reason the last question stinks is that some doofus decided that answers placed there can only be some short number of characters, so I have to put my "I donoknow but mynfingers do" answer here instead of in that questions answer field. So I put a "nonsensical yet accurate" answer there.
  • Death to vi!
  • You are not enough experienced and whole survey is a joke with already set purposes, which we will find later.
    Would you be experienced you would know HTML, no Javascript is required. Would you be experienced, you would know who to hire, and not just linking to third party servers, thus exposing free software users to proprietary Javascript.
    Finally you are exposing their information to third party server which cannot be trusted.
    It is easy to edit few HTML elements and it would be to accept it over CGI and store in the database. I have rewritten the basic Perl form.cgi so many times for myself before 15+ years, and later wrote it for myself in Common Lisp, and I just wait for few free time to rewrite it in Emacs Lisp. All what you need is emacs CGI package and Emacs to prepare HTML.
    But I guess you are not getting what I am speaking about.
  • RMS did nothing wrong.
  • Is JotForm Free Software?
  • I disagree in the way the survey has been released without the emacs mantainers.
  • I had to disable no-script, so I'm angry.

On a serious note, if you'd like to read what newbies really think of Emacs, filter "For how many years have you been using Emacs?" by 0, although it'll take a great deal of time (533 rows to examine).