Casycom is normally used as a complete framework, providing the message loop and requiring a root app object, but sometimes an application already uses an event-driven framework and its message loop. This tutorial demonstrates how to integrate casycom message loop into another.

void OnChildSignal (int signo) { wait (NULL); }

int main (void)
{
    casycom_init();
    signal (SIGCHLD, OnChildSignal);
    casycom_enable_externs();

To manually initialize casycom in non-framework mode, call casycom_init. One notable difference with framework mode operation is that signal handlers are not installed. Normally casycom catches all the signals and converts them to messages to the App object. With only casycom_init, explicit signal handlers must be present, such as this SIGCHLD handler to clean up the launched object server.

typedef struct _PingCaller {
    Proxy	pingp;
    Proxy	externp;
} PingCaller;

const iid_t eil_Ping[] = { &i_Ping, NULL };

void* PingCaller_Create (const Msg* msg)
{
    PingCaller* o = (PingCaller*) xalloc (sizeof(PingCaller));
    o->externp = casycom_create_proxy (&i_Extern, msg->h.dest);
    PExtern_LaunchPipe ("ipcom", "-p", eil_Ping);
}

void PingCaller_ExternR_Connected (PingCaller* o)
{
    o->pingp = casycom_create_proxy (&i_Ping, o->externp.src);
    PPing_Ping (&o->pingp, 1);
}

void PingCaller_PingR_Ping (PingCaller* o, uint32_t u)
{
    casycom_quit (EXIT_SUCCESS);
}

const DPingR d_PingCaller_PingR = {
    .interface = &i_PingR,
    DMETHOD (PingCaller, PingR_Ping)
};
const DExternR d_PingCaller_ExternR = {
    .interface = &i_ExternR,
    DMETHOD (PingCaller, ExternR_Connected)
};
const Factory f_PingCaller = {
    .Create	= PingCaller_Create,
    .dtable	= { &d_PingCaller_PingR, &d_PingCaller_ExternR, NULL }
};

int main (void)
{
    ...
    casycom_register (&f_PingCaller);
    casycom_create_object (&i_PingR);
    ...
}

Because there is no App, no casycom objects exist initially. Objects are normally created when they are needed to receive a message. But an object can also be created manually by one of the interfaces it supports. Casycom always requires messages to be passed between objects, so an object must be created to send the Ping message. Here, the PingCaller object is defined, and then created using casycom_create_object by instantiating interface PingR.

This way there is no single app object, but multiple objects can be created to serve the same purpose. PingCaller uses its constructor to create an Extern connection and to launch ipcom as a private object server. Then it waits for the connection and sends a ping through it.

int main (void)
{
    ...
    while (!casycom_is_quitting()) {
	bool haveMessages = casycom_loop_once();
	size_t nFds = Timer_WatchListSize();
	if (nFds) {
	    struct pollfd fds [nFds];
	    int timeout = 0;
	    nFds = Timer_WatchListForPoll (fds, nFds, haveMessages ? NULL : &timeout);
	    poll (fds, nFds, timeout);
	}
    }
    return casycom_exit_code();
}

Once a casycom object exists and there are messages in the queue, a message loop will have to run to process them. With another message loop normally in control, the casycom loop functionality will have to be interleaved into it as illustrated above.

Here a top-level message loop iterates and waits in poll when nothing needs doing. The loop will have its own exit conditions, but with casycom in use, it also needs to check casycom_is_quitting. This is required to correctly handle errors set by casycom_error.

Casycom messages will then have to be processed somewhere in the loop. casycom_loop_once will process the input queue, swap it with the output queue, and then stop, returning true when the queues are not empty. When messages are available, poll should not block, but only check the fds for the requested conditions.

The rest of the loop deals with file descriptors and timers being watched by casycom. The message loop must provide some way of waiting for them, such as by using poll. Timer_WatchListSize returns the number of fds and timers being watched. Timer_WatchListForPoll will fill the given pollfd array and will optionally set a timeout to support pure timers in the list. Additional file descriptors can be appended at this point and control passed to poll until something happens.