Hello Micah and all, Here attached is a little description of a proposed addition to 'cli_c' in order to implement synchronous APPMSG's. Please let me know what you think. If it is OK, I will go ahead and commit it to the CVS.
Thanks, -Christian -- Christian Grigis | SMARTDATA SA Software Engineer | PSE-B / EPFL Phone: +41-21-693-84-98 | CH - 1015 Lausanne mailto:[EMAIL PROTECTED] | http://www.smartdata.ch
Synchronous messages between PicoGUI applications. -------------------------------------------------- The idea is to add a functionality similar to the already existing pgAppMessage() call, but synchronous: the sender application is blocked until the receiver has answered. This eases the use of APPMSG's when the sender is expecting a return code or some kind of result from the receiver, so that the sender does not need to implement its own small state machine to wait for the answer. It enables a simple kind of RPC between PicoGUI applications. The proposed API is the addition of a client library call, with the following signature: void * pgSyncAppMessage (pghandle dest, struct pgmemdata data); This would be used by the caller in the same way as pgAppMessage, but would block until an answer is received and returned by the call. The receiver will be notified by a normal PG_WE_APPMSG event, to which it will answer with a normal pgAppMessage() call. Now, in order to facilitate the sending of the answer, the receiver will need to know *who* sent the message. It appears that currently there is no way for the receiver of an APPMSG to know from where it came (the 'from' field pf struct pgEvent indicates the local *receiver* of the message). One possibility is to have the sender's pghandle included in the contents of the APPMSG sent. Another one is to add a 'sender' field to the 'data' portion of the 'e' union in struct pgEvent. The former approach adds a small requirement on the application but does not need any change in the PicoGUI data structures; the latter one is more transparent, but requires some minor changes inside PicoGUI. Either way, the idea to handle that sender ID transparently by the pgSyncAppMessage() call is illustraed in the following pseudo-code: - create a temporary "dummy" empty widget - use that widget's pghandle as the message sender - bind a APPMSG handler to that widget - enter a "pgEventLoop()"-like loop, receiving events and dispatching them, until the expected answer is received by the handler - delete the temporary widget - return the answer's data The receiver, accordingly, will: - receive the APPMSG - extract the sender pghandle - prepare an answer - send the answer to that pghandle All this can easily be implemented completely on the app side, as illustrated by the attached source code. The idea is however to have it integrated into 'cli_c', so as to remove the need to duplicate it in every client app needing it.
#include <stdio.h>
#include <string.h>
#include <picogui.h>
#include "appmsg.h"
pghandle app = -1;
int b_handler (struct pgEvent * evt)
{
printf ("received ACTIVATE on 'b' from 0x%08x\n", evt->from);
return 0;
}
int s_handler (struct pgEvent * evt)
{
int result = -1;
printf ("received ACTIVATE on 's' from 0x%08x\n", evt->from);
result = send_msg ("app/recv", "Hello");
if (result < 0) {
printf ("Error while sending message\n");
} else {
printf ("Return code: %d\n", result);
}
return 0;
}
int temp_handler (struct pgEvent * evt)
{
appmsg ** panswer = (appmsg **) evt->extra;
printf ("received APPMSG from 0x%08x\n", evt->from);
printf ("extra = 0x%08x\n", evt->extra);
if (evt->e.data.size > 0) {
* panswer = (appmsg *) malloc (evt->e.data.size);
if (* panswer == NULL) {
printf ("Error allocating memory\n");
exit (1);
}
memcpy (* panswer, evt->e.data.pointer, evt->e.data.size);
}
return 1;
}
void * pgSyncAppMessage (pghandle dest, struct pgmemdata data)
{
pghandle source;
void * answer;
if (data.size < sizeof (pghandle)) return NULL;
source = pgCreateWidget (PG_WIDGET_LABEL);
printf ("binding 0x%08x with extra = 0x%08x\n", source, & answer);
pgBind (source, PG_WE_APPMSG, temp_handler, & answer);
* ((pghandle *) data.pointer) = source;
pgAppMessage (dest, data);
answer = NULL;
do {
struct pgEvent evt = * pgGetEvent ();
pgDispatchEvent (& evt);
} while (! answer);
pgDelete (source);
return answer;
}
int send_msg (char * dest_name, char * msg)
{
int status = 0;
int result = -1;
do {
pghandle dest = pgFindWidget (dest_name);
int * answer;
if (dest == 0) {
status = 1;
break;
}
{
appmsg * newmsg = (appmsg *) malloc (sizeof (appmsg));
if (newmsg == NULL) {
status = 2;
break;
}
strncpy (newmsg->msg, msg, MAX_MSG_LENGTH);
newmsg->msg [MAX_MSG_LENGTH] = (char) 0;
answer = (int *)
pgSyncAppMessage (dest, pgFromTempMemory (newmsg, sizeof (appmsg)));
}
result = ntohl (* answer);
free (answer);
break;
} while (0);
switch (status) {
case 0:
break;
case 1 :
printf ("cannot find destination widget\n");
break;
case 2:
printf ("cannot allocate message memory\n");
break;
default:
printf ("unknown error: %d\n", status);
break;
}
return status == 0 ? result : -1;
}
int main (int argc, char * argv [])
{
pghandle b, s;
pgInit (argc, argv);
app = pgRegisterApp (PG_APP_NORMAL, "send", 0);
printf ("app handle: 0x%08x\n", app);
b = pgNewWidget (PG_WIDGET_BUTTON, 0, 0);
printf ("b handle: 0x%08x\n", b);
pgSetWidget (PGDEFAULT,
PG_WP_TEXT, pgNewString ("Click me"),
0);
pgBind (PGDEFAULT, PG_WE_ACTIVATE, b_handler, NULL);
s = pgNewWidget (PG_WIDGET_BUTTON, 0, 0);
printf ("s handle: 0x%08x\n", s);
pgSetWidget (PGDEFAULT,
PG_WP_TEXT, pgNewString ("Send"),
0);
pgBind (PGDEFAULT, PG_WE_ACTIVATE, s_handler, NULL);
pgEventLoop ();
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <picogui.h>
#include "appmsg.h"
pghandle app = -1;
int msg_handler (struct pgEvent * evt)
{
appmsg * newmsg;
pghandle from;
int * answer;
printf ("Received APPMSG\n");
if (evt->e.data.size != sizeof (appmsg)) {
printf ("ERROR: incorrect size");
return 0;
}
newmsg = (appmsg *) evt->e.data.pointer;
printf (" size: 0x%04x bytes\n", evt->e.data.size);
from = newmsg->source;
printf (" from: 0x%08x\n", from);
printf (" to: 0x%08x\n", evt->from);
printf (" contents: %s\n", newmsg->msg);
answer = (int *) malloc (sizeof (int));
if (answer != NULL) {
* answer = htonl ((random () < RAND_MAX / 2));
sleep (1);
printf ("Sending result: %d\n", ntohl (* answer));
pgAppMessage (from, pgFromTempMemory (answer, sizeof (answer)));
}
return 0;
}
int main (int argc, char * argv [])
{
pgInit (argc, argv);
app = pgRegisterApp (PG_APP_NORMAL, "recv", 0);
printf ("app handle: 0x%08x\n", app);
pgSetWidget (PGDEFAULT,
PG_WP_NAME, pgNewString ("app/recv"),
0);
pgBind (PGDEFAULT, PG_WE_APPMSG, msg_handler, NULL);
pgEventLoop ();
return 0;
}
#define MAX_MSG_LENGTH 80
typedef struct {
pghandle source;
char msg [MAX_MSG_LENGTH + 1];
} appmsg;
pgp00000.pgp
Description: PGP signature
