In this blog post, I will show you how to read from stdin in a GTK application written with gjs.
Since I was already using greenclip(with rofi) for clipboard history, I would like to use Bender in a similar way. greenclip print | bender
.
As a beginner to GJS-GTK, my first challenge was to bootstrap a GTK app and read the data from stdin.
For the first prototype, I used the InputStream.read_bytes like below
Using read_bytes
const cli = new Gio.ApplicationCommandLine();
const inputStream = cli.get_stdin();
let str = "";
const bytes = inputStream?.read_bytes(8192, null);
inputStream?.close(null);
const data = bytes?.get_data();
if (data) {
str = new TextDecoder("utf-8").decode(data);
}
This comes with some limitations,
- Its synchronous/blocking
- we have to specify the no of bytes
I settled with read_bytes
because, when I tried to use read_all_async
or read_all
I was getting errors like below in GJS 1.74.2.
Function Gio.InputStream.read_all_async() cannot be called: argument 'buffer' with type array is not introspectable because it has a type not supported for (out caller-allocates)
TypeError: method Gio.InputStream.read_all_async: At least 4 arguments required, but only 3 passed
Once I had an initial prototype, I started looking into better ways to read from stdin in GTK applications.
A quick search of the above errors led me to some discussions on the Gnome forums like this and this which lead me to a bug report #501 on GJS & a merge request #787
Based on merge request #787 I decided to give a try to MemoryOutputStream.splice_async
.
Using MemoryOutputStream.splice_async
I started replacing the read_byes with splice_async
& splice_finish
and it worked fine.
const cli = new Gio.ApplicationCommandLine();
const inputStream = cli.get_stdin();
if (inputStream) {
const outputStream = Gio.MemoryOutputStream.new_resizable();
outputStream.splice_async(
inputStream,
Gio.OutputStreamSpliceFlags.CLOSE_TARGET,
GLib.PRIORITY_DEFAULT,
null,
(outputStream, result) => {
let data;
let str = "";
let bytes;
try {
outputStream?.splice_finish(result);
bytes = outputStream?.steal_as_bytes();
} catch (err) {
console.debug(err);
}
data = bytes?.get_data();
if (data) {
str = new TextDecoder("utf-8").decode(data);
}
console.debug("body:", str);
},
);
}
For now, this looks better than using read_bytes
and I don’t need to worry about the hard coded bytes count, and it’s non-blocking.
Hope this is helpful. Did I make any mistake or is there a better way, feel free for drop an email.
Versions of Language/packages used in this post.
Library/Language | Version |
---|---|
GJS | 1.74.2 |