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;

Attachment: pgp00000.pgp
Description: PGP signature

Reply via email to