Throughout this guide, your programs have been started by the iol command, which opens a console and connects your program to it. Programs written in C or C++ have another option: libiol, a small library that lets a program open ioL consoles itself, without the iol command.
Why would you want to? Perhaps your program wants to open several consoles at once, or to keep its own terminal free for something else. Or perhaps it's a launcher that starts other programs in consoles of their own. libiol gives you that control, at a lower level than the iol command.
This appendix assumes you already know C. Everything in it works the same way from C++.
Include the library's header, and link with -liol:
#include <iol.h>
gcc -o clicks clicks.c -liol
The library gives you three ways to work with a console:
The first two ways both start with iol_open_terminal:
iol_terminal iol_open_terminal(
const char* rescom, const char* title,
const char* containerKey, short errRedirect,
int (*child_main)(void*), void* child_arg
);
| Parameter | What it's for |
|---|---|
| rescom | the console's console.rescom, as set by iol -r |
| title | the console's starting title, as set by iol -t; NULL for none |
| containerKey | the key of a clientFrame to open the console inside, as with iol -k; NULL for a console of its own |
| errRedirect | 0 to have the console's error messages appear on your program's standard error; 1 to read them yourself, from errin |
| child_main, child_arg | a function to run as the console's program, and the argument to pass it; NULL to talk to the console directly |
It returns an iol_terminal, which describes the new console:
| Field | Holds |
|---|---|
| pid | the console's process ID. A negative value means the console couldn't be opened. |
| in | a file descriptor to read the console's input from: whatever putLn sends |
| out | a file descriptor to send markup to |
| errin | if errRedirect was set, a file descriptor to read the console's error messages from, including those from putError; otherwise -1 |
| errout | the standard error of a function started with iol_deploy. It's 2, so the function's error messages go to your program's standard error. To send them somewhere else, put another file descriptor here before calling iol_deploy. |
| data | for the library's own use. Don't read or change it. |
Pass NULL for child_main, and your program talks to the console itself, through in and out. Everything works as it would for a program run by iol: you write markup, and read lines of input. Here's a button that counts its clicks:
#include <iol.h>
#include <stdio.h>
int main(void)
{
iol_terminal t = iol_open_terminal("clicks", "Click Counter", NULL, 0, NULL, NULL);
if (t.pid < 0) {
fprintf(stderr, "Couldn't open an ioL console.\n");
return 1;
}
FILE *out = fdopen(t.out, "w");
FILE *in = fdopen(t.in, "r");
fprintf(out, "<button {Click me} onClick=<putLn {click}>> <count:span {No clicks yet.}>\n");
fflush(out);
char line[100];
int clicks = 0;
while (fgets(line, sizeof line, in)) {
clicks++;
fprintf(out, "<count {Clicks so far: %d}>\n", clicks);
fflush(out);
}
return 0;
}
Wrapping the descriptors with fdopen lets you use the familiar fprintf and fgets. Notice the fflush after each piece of markup: out isn't your program's standard output, so it's up to you to make sure the markup is sent straight away.
The console closes when your program closes its connection to it, which happens when your program ends.
A program working this way isn't limited to one console. Call iol_open_terminal again, and you have a second console, with its own in and out.
Alternatively, pass a function as child_main, then call iol_deploy to start it. The function becomes the console's program: its standard input and output are connected to the console, so it prints markup and reads input exactly as a program started by iol would. child_arg is passed to it:
#include <iol.h>
#include <stdio.h>
static int greeter(void *arg)
{
const char *name = arg;
printf("<box {Hello, %s!}>\n", name);
printf("<button {Done} onClick=<putLn {done}>>\n");
fflush(stdout);
char line[100];
fgets(line, sizeof line, stdin);
return 0;
}
int main(void)
{
iol_terminal t = iol_open_terminal("greeter", "Greeter", NULL, 0, greeter, "world");
if (t.pid < 0) {
fprintf(stderr, "Couldn't open an ioL console.\n");
return 1;
}
iol_deploy(&t);
return 0;
}
A few things to know about the function:
Once you've called iol_deploy, the in and out descriptors in the iol_terminal are no longer valid: the console belongs to the function now. If you want to talk to the console yourself, don't call iol_deploy.
iol_exec_terminal works the other way round. It turns the process that calls it into an ioL console. The console's program is whatever is connected to that process's standard input and output, just as with iol -:
int iol_exec_terminal(const char* rescom, const char* title, const char* containerKey);
The parameters mean the same as for iol_open_terminal. If the console starts, the function never returns. If it does return, the console couldn't be started.
A common way to use it is to fork, connect pipes to the new process's standard input and output, and have it call iol_exec_terminal. Your program then talks to the console through the other ends of the pipes:
#include <iol.h>
#include <stdio.h>
#include <unistd.h>
int main(void)
{
int to_console[2], from_console[2];
pipe(to_console);
pipe(from_console);
if (fork() == 0) {
/* The new process becomes the console. */
dup2(to_console[0], 0);
dup2(from_console[1], 1);
close(to_console[1]);
close(from_console[0]);
iol_exec_terminal("forked", "Forked Console", NULL);
_exit(1); /* only reached if the console couldn't start */
}
close(to_console[0]);
close(from_console[1]);
FILE *out = fdopen(to_console[1], "w");
FILE *in = fdopen(from_console[0], "r");
fprintf(out, "<button {Ping} onClick=<putLn {ping}>> <reply:span>\n");
fflush(out);
char line[100];
while (fgets(line, sizeof line, in)) {
fprintf(out, "<reply {pong}>\n");
fflush(out);
}
return 0;
}
These functions change how the next console is opened, or how your program behaves. Call them before opening the console they're meant to affect.
| Function | Does |
|---|---|
| iol_unbuffer_this_process() | Turns off standard i/o buffering in your program, and in programs it later runs with the exec family of functions, so their output is sent as soon as it's printed. |
| iol_disable_tc() | Turns off Turing completeness in the next console to be opened, like iol -s. Use it for content you don't trust, such as a document or a simple form. The chapter on hosting other programs lists what's disabled. |
| iol_unset_quiet() | Has the welcome message printed when a new console is opened. Consoles opened by the library are quiet by default, as with iol -q. |
| iol_set_wrapper_flag() | Tells ioL, when a console is opened, that your program is the leader, owner and wrapping container of the programs and consoles it starts. TODO: describe the effects. |