In the midst of playing with
tycat and
terminology on a
virtual console*, it became apparent that
xdg-open wasn't doing its job in regards to opening PDF documents.
bash# xdg-open /tmp/dw.pdf
Filename "file:///tmp/dw.pdf" does not exist or is not a regular file
A simple check of the default handler shows that everything is fine:
bash# xdg-mime query default application/pdf
evince.desktop
..but for fun, let's try to change it and see what happens.
bash# xdg-mime default apvlv.desktop application/pdf
bash# xdg-open /tmp/dw.pdf
Filename "file:///tmp/dw.pdf" does not exist or is not a regular file
Nothing.
Grepping for the error in
/usr/bin/xdg-open comes up empty. It's odd that two unrelated programs (
evince and
apvlv) would have the incorrect error message (the file does exist and is regular), or that they would both fail to handle file:// URIs. How does
xdg-open actually route these requests?
Turns out it depends on your window manager. If you are running Enlightenment,
xdg-open performs the following:
open_enlightenment()
{
enlightenment_open "$1"
if [ $? -eq 0 ]; then
exit_success
else
exit_failure_operation_failed
fi
}
Replacing enlightenment_open "$1" with open_generic "$1" works. Can we invoke open_generic when enlightenment_open fails? Turns out we can't, because enlightenment_open returns 0 (success) even when the file isn't opened.
Fixing enlightenment_open might do the trick. The relevant code (enlightenment.git/ src/bin/e_open.c) is here:
char **itr;
int ret = EXIT_SUCCESS;
for (itr = cmds; *itr != NULL; itr++)
{
/* Question: should we execute them in parallel? */
int r = system(*itr);
if (r < 0)
fprintf(stderr, "ERROR: %s executing %s\n", strerror(errno),
*itr);
free(*itr);
if (r > 0) /* Question: should we stop the loop on first faiure? */
ret = r;
}
free(cmds);
return ret;
It turns out that the status of what enlightenment_open is calling is ignored. But what is it calling? Adding the line fprintf(stderr, "Trying %s\n", *itr); after the system() call uncovers what is actually happening:
bash# make src/bin/enlightenment_open
CC src/bin/src_bin_enlightenment_open-e_open.o
CCLD src/bin/enlightenment_open
bash# src/bin/enlightenment_open /tmp/dw.pdf
Filename "file:///tmp/dw.pdf" does not exist or is not a regular file
TRYING evince-previewer 'file:///tmp/dw.pdf
Where the hell did evince-previewer come from? To make a long story short, xdg-open (and enlightenment_open, and all other *_open tools) uses /usr/bin/mimeopen as a backend.
This utility operates on files, not mime-types:
bash# mimeopen -a /tmp/dw.pdf
Please choose an application
1) Print Preview (evince-previewer)
2) Document Viewer (evince)
3) vprerex (vprerex)
4) GNU Image Manipulation Program (gimp)
use application #
Cancelled
OK, so that is where evince-previewer is coming from. A quick test shows that the actual bug is caused by evince-previewer not understanding file URIs:
bash# evince-previewer file:///tmp/dw.pdf
Filename "file:///tmp/dw.pdf" does not exist or is not a regular file
bash# evince file:///tmp/dw.pdf
(evince:21480): Gtk-WARNING **: gtk_widget_size_allocate(): attempt to allocate widget with width -82 and height 15
That Gtk warning accompanies evince loading and displaying the file just fine.
Changing the handler for PDFs using mimeopen turns out to work just fine:
bash# mimeopen -d /tmp/dw.pdf
Please choose a default application for files of type application/pdf
1) Print Preview (evince-previewer)
2) Document Viewer (evince)
3) vprerex (vprerex)
4) GNU Image Manipulation Program (gimp)
5) Other...
use application #2
Opening "/tmp/dw.pdf" with Document Viewer (application/pdf)
(evince:21510): Gtk-WARNING **: gtk_widget_size_allocate(): attempt to allocate widget with width -82 and height 15
bash# xdg-open /tmp/dw.pdf
(evince:21550): Gtk-WARNING **: gtk_widget_size_allocate(): attempt to allocate widget with width -82 and height 15
xdg-open /tmp/a.pdf
(evince:21602): Gtk-WARNING **: gtk_widget_size_allocate(): attempt to allocate widget with width -82 and height 1
Of course, that doesn't mean that evince-previewer is the only bug. Since evince-previewer returns 1 (failure), enlightenment_open should be returning 1 instead of 0:
int ret = EXIT_FAILURE;
for (itr = cmds; *itr != NULL; itr++)
{
int r = system(*itr);
if (r < 0)
fprintf(stderr, "ERROR: %s executing %s\n", strerror(errno),
*itr);
free(*itr);
ret = r;
}
...and xdg-open should check the return value and fallback to open_generic:
enlightenment_open "$1"
if [ $? -eq 0 ]; then
exit_success
else
open_generic "$1"
if [ $? -eq 0 ]; then
exit_success
else
exit_failure_operation_failed
fi
fi
Let's not even get started on why
xdg-open doesn't propagate changes back to
mimeopen. Wait, why doesn't it?
UPDATE: looks like there's something deeply wrong with with the
xdg-* utilities:
bash# xdg-settings get default-web-browser
xdg-settings: unknown desktop environment
The problem here is that
xdg-settings performs its
own check for desktop environment (modularity, people!) and, of course, implements this poorly. If the desktop environment is detected (as is Enlightenment), but is not KDE|Gnome|XFCE|MATE, it returns "
unknown desktop environment". If the environment is
not detected, a generic handler is called. There are some very confused people working on this software.
The fix is to modify
xdg-settings, changing the default
case statement
*)
exit_failure_operation_impossible "unknown desktop environment"
;;
to
*)
dispatch_specific generic "$@"
;;
*
For those interested: install
tslib and
rebuild EFL from
source, then run the following on a virtual terminal:
ELM_ENGINE=fb /usr/local/bin/terminology -f=nexus/20
Yes, the -b option for a backgroud image will work, as will tytls, tyop, and tycat.