Middleware, development tools, realtime operating system
software and services for superior embedded design


Home
QNX Community Resources
QNX Documentation Library
Resource editor plugin examples

Resource editor plugin examples

QNX Software Systems
Developer Resources
Blogs
Board support packages
Foundry27 projects
Forums
Hardware support listing
Online video tutorials
Product documentation
Technical Articles

Resource editor plugin examples

This chapter contains examples of using the PhAB plugin API to create resource editors. There are two examples:

A string editor

The following is an example resource editor plugin for editing a string resource. This is taken from the actual PhAB source code. Not shown is the PhAB project which contains this source file and implements the widgets within a picture module.

#include <Pt.h>
#include <Ap.h>


/* Local headers */
#include <res_plugin_api.h>
#include <aoi/aoi.h>
#include "abimport.h"
#include "proto.h"

typedef struct {
  int n_master;
  void *value_master;
  int n_default;
  void *default_value;
  PhABHandle_t phab;
  PhABResExportFrugal_t *exp;
  PtWidget_t *frugal;
  } PluginFrugalInstance_t;

typedef struct {
  int n_master;
  void *value_master;
  int n_default;
  void *default_value;
  PhABHandle_t phab;
  PhABResExportFull_t *exp;
  PhabResWindowHandle_t convenience_handle;
  int disabled;
  PtWidget_t *full, *full_widget;
  } PluginFullInstance_t;

static int plugin_loading( const char *path );
static void plugin_unloading( void );
static void plugin_frugal_set_data( ResPluginHandle_t handle, int n, const void *value,
                                    const ResPluginFormatData_t *format );
static void plugin_full_set_data( ResPluginHandle_t handle, int n, const void *value,
                                  const ResPluginFormatData_t *format );
static void plugin_frugal_destroy( ResPluginHandle_t handle );
static void plugin_full_destroy( ResPluginHandle_t handle );
static ResPluginHandle_t plugin_frugal_create
        ( PhABHandle_t phab , const PhABResExportFrugal_t *exp,
          const ResPluginFormatData_t *format,
          int n_default, const void *default_value,
          PtWidget_t *parent,
          int n, const void *value );
static ResPluginHandle_t plugin_full_create
        ( PhABHandle_t phab , const PhABResExportFull_t *exp,
          const ResPluginFormatData_t *format,
          int n_default, const void *default_value,
          PhArea_t *area, char *caption,
          int n, const void *value );
static void plugin_full_disable( ResPluginHandle_t handle );
static void plugin_full_block( ResPluginHandle_t handle, int block );
static void plugin_full_to_front( ResPluginHandle_t handle );
static int plugin_full_any_changes( ResPluginHandle_t handle );
static void plugin_full_get_changes( ResPluginHandle_t handle, int *pn,
                                     void **pvalue );
static void plugin_full_get_area( ResPluginHandle_t handle, PhArea_t *area );
static void plugin_full_notify( ResPluginAction_t action, void *data );

/* callbacks */
static int plugin_frugal_changed( PtWidget_t *widget, void *data,
                                  PtCallbackInfo_t *cbinfo );
static int plugin_full_resize( PtWidget_t *widget, void *data,
                               PtCallbackInfo_t *cbinfo );
static int plugin_full_changed( PtWidget_t *widget, void *data,
                                PtCallbackInfo_t *cbinfo );
static int plugin_full_done( PtWidget_t *widget, void *data,
                             PtCallbackInfo_t *cbinfo );
static void set_state( PluginFullInstance_t *instance, char *value );

static ResPluginFullEditor_t full_editors[1] =
  {
    {
      {
      "string",
      RES_DATATYPE_STRING,
      RESPLUGIN_DATATYPE_VERSION,
      plugin_full_set_data,
      plugin_full_destroy
      },
    plugin_full_create,
    plugin_full_disable,
    plugin_full_block,
    plugin_full_to_front,
    plugin_full_any_changes,
    plugin_full_get_changes,
    plugin_full_get_area
    }
  };

static ResPluginFrugalEditor_t frugal_editors[1] =
  {
    {
      {
      "string",
      RES_DATATYPE_STRING,
      RESPLUGIN_DATATYPE_VERSION,
      plugin_frugal_set_data,
      plugin_frugal_destroy
      },
    plugin_frugal_create,
    }
  };

static ResPlugin_t tab = {
  plugin_loading,
  plugin_unloading,
  1,
  full_editors,
  1,
  frugal_editors
  };

AOInterface_t string_interfaces[] = {
  { "PhAB Resource Editors", RESPLUGIN_VERSION, &tab },
  { 0, 0, 0 }
  };


static int loaded_count = 0;
static ApDBase_t *db = NULL;

static int plugin_loading( const char *path ) {
  if( loaded_count == 0 && ApAddContext( &AbContext, path ) )
    return -1;

  db = ApOpenDBase( ABM_string_db );
  ++loaded_count;
  return 0;
  }

static void plugin_unloading( void ) {
  if( -- loaded_count == 0 ) {
    ApCloseDBase( db );
    db = NULL;
    ApRemoveContext( &AbContext );
    }
  }

static ResPluginHandle_t plugin_frugal_create
        ( PhABHandle_t phab , const PhABResExportFrugal_t *exp,
          const ResPluginFormatData_t *format,
          int n_default, const void *default_value,
          PtWidget_t *parent,
          int n, const void *value ) {

  PtArg_t args[2];
  PhRect_t offset = { { -1, -1 }, { -1, -1 } };
  PluginFrugalInstance_t *instance;

  instance = calloc( 1, sizeof( *instance ) );
  if( !instance ) return NULL;

  instance->n_default = n_default;
  instance->default_value = default_value;
  instance->n_master = n;
  instance->value_master = value;

  instance->phab = phab;
  instance->exp = exp;

  PtSetArg( &args[0], Pt_ARG_ANCHOR_OFFSETS, &offset, 0 );
  PtSetArg( &args[1], Pt_ARG_TEXT_STRING, value, 0 );
  instance->frugal = ApCreateWidget( db, "string_frugal", 0, 0, 2, args );
  PtAddCallback( instance->frugal, Pt_CB_TEXT_CHANGED, plugin_frugal_changed, instance );
  return ( ResPluginHandle_t ) instance;
  }

static void plugin_frugal_set_data( ResPluginHandle_t handle, int n, const void *value,
                                    const ResPluginFormatData_t *format )
{
  PluginFrugalInstance_t *instance = ( PluginFrugalInstance_t * ) handle;
  instance->n_master = n;
  instance->value_master = value;
  PtSetResource( instance->frugal, Pt_ARG_TEXT_STRING, value, 0 );
}


static int plugin_frugal_changed( PtWidget_t *widget, void *data,
                                  PtCallbackInfo_t *cbinfo ) {
  PluginFrugalInstance_t *instance = ( PluginFrugalInstance_t * ) data;
  char *p;
  PtGetResource( widget, Pt_ARG_TEXT_STRING, &p, 0 );
  instance->n_master = strlen( p );
  instance->value_master = strdup( p );
  if( instance->exp->common.apply( instance->phab, instance->n_master,
                                         instance->value_master ) ) {
    /* we matched the default */
    free( instance->value_master );
    instance->value_master = instance->default_value;
    instance->n_master = instance->n_default;
    }
  return Pt_CONTINUE;
  }

static void plugin_frugal_destroy( ResPluginHandle_t handle )
{
  free( ( PluginFrugalInstance_t * ) handle );
}


static ResPluginHandle_t plugin_full_create
        ( PhABHandle_t phab , const PhABResExportFull_t *exp,
          const ResPluginFormatData_t *format,
          int n_default, const void *default_value,
          PhArea_t *area, char *caption,
          int n, const void *value )
{
  PluginFullInstance_t *instance;
  PtWidget_t *parent;
  PhRect_t offset = { { 0, 0 }, { 0, 0 } };
  PhArea_t tarea;
  PtArg_t args[1];
  int start = 0, end = n;

  instance = calloc( 1, sizeof( *instance ) );
  if( !instance ) return NULL;

  instance->n_default = n_default;
  instance->default_value = default_value;
  instance->n_master = n;
  instance->value_master = value;

  instance->phab = phab;
  instance->exp = exp;

  if( !area ) {
    PhRect_t rect;
    PhWindowQueryVisible( 0, 0, 0, &rect );
    tarea.pos.x = rect.ul.x + 100;;
    tarea.pos.y = rect.ul.y + 100;
    tarea.size.w = 340;
    tarea.size.h = 150;
    area = &tarea;
    }
  instance->convenience_handle = exp->create_window( phab, area, caption, 0,
                                                           &parent, plugin_full_notify,
                                                           instance );

  PtSetParentWidget( parent );
  PtSetArg( &args[0], Pt_ARG_ANCHOR_OFFSETS, &offset, 0 );
  instance->full= ApCreateWidgetFamily( db, "string_full_container", 0, 0, 1, args );
  instance->full_widget = PtWidgetChildFront( instance->full );
  PtAddCallback( instance->full, Pt_CB_RESIZE, plugin_full_resize, instance );
  PtAddCallback( instance->full_widget, Pt_CB_TEXT_CHANGED, plugin_full_changed,
                 instance );
  PtAddCallback( instance->full_widget, Pt_CB_ACTIVATE, plugin_full_done, instance );

  PtSetResource( instance->full_widget, Pt_ARG_TEXT_STRING, value, 0 );
  PtTextSetSelection( instance->full_widget, &start, &end );
  PtRealizeWidget( instance->full );

  set_state( instance, value );

  return ( ResPluginHandle_t ) instance;
}

static void plugin_full_destroy( ResPluginHandle_t handle )
{
  PluginFullInstance_t *instance = ( PluginFullInstance_t * ) handle;
  instance->exp->destroy( instance->convenience_handle );
  free( instance );
}

static void plugin_full_disable( ResPluginHandle_t handle )
{
  PluginFullInstance_t *instance = ( PluginFullInstance_t * ) handle;
  PtArg_t args[3];
  instance->disabled = 1;
  PtSetArg( &args[0], Pt_ARG_FLAGS, Pt_GHOST|Pt_BLOCKED, Pt_GHOST|Pt_BLOCKED );
  PtSetArg( &args[1], Pt_ARG_CURSOR_TYPE, Ph_CURSOR_NOINPUT, 0 );
  PtSetArg( &args[2], Pt_ARG_CURSOR_OVERRIDE, 1, 0 );
  PtSetResources( instance->full, 3, args );
  PtSetResource( instance->full_widget, Pt_ARG_FLAGS, Pt_GHOST|Pt_BLOCKED,
                 Pt_GHOST|Pt_BLOCKED );
  instance->exp->set_state( instance->convenience_handle,
                                  RESPLUGIN_STATE_DISABLED );
}

static void plugin_full_block( ResPluginHandle_t handle, int block )
{
  PluginFullInstance_t *instance = ( PluginFullInstance_t * ) handle;
  instance->exp->set_state( instance->convenience_handle, block ?
                                  RESPLUGIN_STATE_BLOCKED : RESPLUGIN_STATE_UNBLOCKED );
}

static int plugin_full_any_changes( ResPluginHandle_t handle )
{
  PluginFullInstance_t *instance = ( PluginFullInstance_t * ) handle;
  char *p;
  PtGetResource( instance->full_widget, Pt_ARG_TEXT_STRING, &p, 0 );
  if( !strcmp( p, instance->value_master ) ) return RESPLUGIN_NO_CHANGES;
  return RESPLUGIN_CHANGES;
}

static void plugin_full_get_changes( ResPluginHandle_t handle, int *pn, void **pvalue )
{
  PluginFullInstance_t *instance = ( PluginFullInstance_t * ) handle;
  char *p;
  PtGetResource( instance->full_widget, Pt_ARG_TEXT_STRING, &p, 0 );
  *pn = strlen( p );
  *pvalue = strdup( p );
}

static void plugin_full_get_area( ResPluginHandle_t handle, PhArea_t *area )
{
  PluginFullInstance_t *instance = ( PluginFullInstance_t * ) handle;
  instance->exp->get_area( instance->convenience_handle, area );
}

static void plugin_full_to_front( ResPluginHandle_t handle )
{
  PluginFullInstance_t *instance = ( PluginFullInstance_t * ) handle;
  instance->exp->to_front( instance->convenience_handle );
}

static void plugin_full_set_data( ResPluginHandle_t handle, int n, const void *value,
                                  const ResPluginFormatData_t *format ) {
  PluginFullInstance_t *instance = ( PluginFullInstance_t * ) handle;
  int start = 0, end = n;

  PtSetResource( instance->full_widget, Pt_ARG_TEXT_STRING, value, 0 );
  PtTextSetSelection( instance->full_widget, &start, &end );

  instance->n_master = n;
  instance->value_master = value;
  set_state( instance, value );
  if( instance->disabled ) {
    PtArg_t args[3];
    instance->disabled = 0;
    PtSetArg( &args[0], Pt_ARG_FLAGS, 0, Pt_GHOST|Pt_BLOCKED );
    PtSetArg( &args[1], Pt_ARG_CURSOR_TYPE, Ph_CURSOR_INHERIT, 0 );
    PtSetArg( &args[2], Pt_ARG_CURSOR_OVERRIDE, 0, 0 );
    PtSetResources( instance->full, 3, args );
    PtSetResource( instance->full_widget, Pt_ARG_FLAGS, 0, Pt_GHOST|Pt_BLOCKED );
    }
  }

static int plugin_full_resize( PtWidget_t *widget, void *data,
                               PtCallbackInfo_t *cbinfo ) {
  PtContainerCallback_t *cb = cbinfo->cbdata;
  if( cb->old_dim.w != cb->new_dim.w || cb->old_dim.h != cb->new_dim.h ) {
    PluginFullInstance_t *instance = ( PluginFullInstance_t * ) data;
    PhDim_t dim;
    PhPoint_t pos;
    /* center the instance->full_widget */
    PtWidgetDim( instance->full_widget, &dim );
    pos.x = ( cb->new_dim.w - dim.w ) / 2;
    pos.y = ( cb->new_dim.h - dim.h ) / 2;
    PtSetResource( instance->full_widget, Pt_ARG_POS, &pos, 0 );
    }
  return Pt_CONTINUE;
  }

static int plugin_full_changed( PtWidget_t *widget, void *data, PtCallbackInfo_t *cbinfo ) {
  PluginFullInstance_t *instance = ( PluginFullInstance_t * ) data;
  char *p;
  PtGetResource( widget, Pt_ARG_TEXT_STRING, &p, 0 );
  set_state( instance, p );
  return Pt_CONTINUE;
  }


static void get_value_and_apply( PluginFullInstance_t *instance ) {
  char *p;
  PtGetResource( instance->full_widget, Pt_ARG_TEXT_STRING, &p, 0 );
  instance->n_master = strlen( p );
  instance->value_master = strdup( p );
  if( instance->exp->common.apply( instance->phab, instance->n_master,
                                         instance->value_master ) ) {
    /* we matched the default */
    free( instance->value_master );
    instance->value_master = instance->default_value;
    instance->n_master = instance->n_default;
    }
  }


static int plugin_full_done( PtWidget_t *widget, void *data,
                             PtCallbackInfo_t *cbinfo ) {
  PluginFullInstance_t *instance = ( PluginFullInstance_t * ) data;
  get_value_and_apply( instance );
  if( instance->exp->closing( instance->phab ) ) {
    instance->exp->destroy( instance->convenience_handle );
    free( instance );
    }
  return Pt_CONTINUE;
  }

static void plugin_full_notify( ResPluginAction_t action, void *notify_data ) {
  PluginFullInstance_t *instance = ( PluginFullInstance_t * ) notify_data;
  switch( action ) {
    case RESPLUGIN_ACTION_APPLY: {
      get_value_and_apply( instance );
      set_state( instance, instance->value_master );
      }
      break;
    case RESPLUGIN_ACTION_CLOSE: {
      if( instance->exp->closing( instance->phab ) ) {
        instance->exp->destroy( instance->convenience_handle );
        free( instance );
        }
      }
      break;
    case RESPLUGIN_ACTION_DEFAULT: {
      PtSetResource( instance->full_widget, Pt_ARG_TEXT_STRING,
                     instance->default_value, 0 );
      set_state( instance, instance->default_value );
      }
      break;
    }
  }


static void set_state( PluginFullInstance_t *instance, char *value ) {
  if( !strcmp( value, instance->default_value ) ) {
    if( strcmp( value, instance->value_master ) )
      instance->exp->set_state( instance->convenience_handle,
                                      RESPLUGIN_STATE_MATCH_DEFAULT );
    else instance->exp->set_state( instance->convenience_handle,
                                         RESPLUGIN_STATE_MATCH_DEFAULT_MASTER );
    }
  else {
    if( strcmp( value, instance->value_master ) )
      instance->exp->set_state( instance->convenience_handle,
                                      RESPLUGIN_STATE_NO_MATCH );
    else instance->exp->set_state( instance->convenience_handle,
                                         RESPLUGIN_STATE_MATCH_MASTER );
    }
  }

An external editor

The following is an example of using an external application as a resource editor in PhAB. We will use ped to edit the Pt_ARG_TEXT_STRING resource for the PtButton widget.

To use this example, compile the two files listed below, external_multi.h and external_multi.c, into a DLL named external_multi.so.Copy the external_multi.so file into the plugins/resource directory below the location that contains PhAB's executable (usually /usr/photon/appbuilder/plugins/resource).Edit the res_editors.def in the PhAB executable directory to contain the lines:

e=external_multi
p=
F=multi@external_multi.so

Edit the file def_res.def in the same directory to contain the line:

external_multi

Edit the ptpalette.pal file in the same directory to specify external_multi as the resource editor for Pt_ARG_TEXT_STRING for the PtButton widget. To do this, in the PtButton section of the file, change the Pt_ARG_TEXT_STRING line to:

r=Pt_ARG_TEXT_STRING,Button  Text,3011,3002,0,multi/external_multi,NULL

Code listing for external_multi.h

#ifndef __ALREADY_INCLUDED_H
#define __ALREADY_INCLUDED_H

#include <res_plugin_api.h>
#include <aoi/aoi.h>

#include <pthread.h>

typedef struct {
  int n_master;
  void *value_master;
  int n_default;
    void *default_value;

    PhABHandle_t phab;
    PhABResExportFull_t *exp;

    int pid, phab_waiting;
    char *path;
    pthread_t monitor;
  } PluginFullInstance_t;

#endif

Code listing for external_multi.c

#include <Pt.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/netmgr.h>
#include <time.h>
#include <signal.h>


/* Local headers */
#include "external_multi.h"

static int plugin_loading( const char *path );
static void plugin_unloading( void );
static void plugin_full_set_data( ResPluginHandle_t handle, int n, const void *value,
                                  const ResPluginFormatData_t *format );
static void plugin_full_destroy( ResPluginHandle_t handle );
static ResPluginHandle_t plugin_full_create
        ( PhABHandle_t phab , const PhABResExportFull_t *exp,
          const ResPluginFormatData_t *format,
          int n_default, const void *default_value,
          PhArea_t *area, char *caption,
          int n, const void *value );
static void plugin_full_disable( ResPluginHandle_t handle );
static void plugin_full_block( ResPluginHandle_t handle, int block );
static void plugin_full_to_front( ResPluginHandle_t handle );
static int plugin_full_any_changes( ResPluginHandle_t handle );
static void plugin_full_get_changes( ResPluginHandle_t handle, int *pn, void **pvalue );
static void plugin_full_get_area( ResPluginHandle_t handle, PhArea_t *area );


static void child_exit( int sig );
static void *monitor_f( void * data );
static void get_changes_from_file( PluginFullInstance_t *instance, int *pn,
                                   char **value );
static void apply_from_file( PluginFullInstance_t *instance );
static void emit_wm_event( PluginFullInstance_t *instance, unsigned long event_type );
static void was_file_changed( PluginFullInstance_t *instance, int *modification_time );

static ResPluginFullEditor_t full_editors[1] =
    {
        {
        {   "multi",
            RES_DATATYPE_MULTI,
            RESPLUGIN_DATATYPE_VERSION,
            plugin_full_set_data,
            plugin_full_destroy },
        plugin_full_create,
        plugin_full_disable,
        plugin_full_block,
        plugin_full_to_front,
        plugin_full_any_changes,
        plugin_full_get_changes,
        plugin_full_get_area
        }
    };

static ResPlugin_t tab = {
    plugin_loading,
    plugin_unloading,
    1,
    full_editors,
    0,
    NULL
    };

AOInterface_t interfaces[] = {
    { "PhAB Resource Editors", RESPLUGIN_VERSION, &tab },
    { 0, 0, 0 }
    };


static int plugin_loading( const char *path ) {
    signal( SIGCHLD, child_exit );
    return 0;
    }

static void plugin_unloading( void ) {
    }

static ResPluginHandle_t plugin_full_create
        ( PhABHandle_t phab , const PhABResExportFull_t *exp,
          const ResPluginFormatData_t *format,
          int n_default, const void *default_value,
          PhArea_t *area, char *caption,
          int n, const void *value )
{
    PluginFullInstance_t *instance;
    FILE *fp;
    char path[PATH_MAX];
    time_t t;
    char *argv[3];

    time( &t );

    /* put the data in a file */
    sprintf( path, "/tmp/%ld.txt", (long) t );
    fp = fopen( path, "w" );
    if( !fp ) return NULL;
    if( value ) fwrite( (char*)value, 1, n, fp );
    fclose( fp );

    instance = calloc( 1, sizeof( *instance ) );
    if( !instance ) return NULL;

    instance->n_default = n_default;
    instance->default_value = default_value;
    instance->n_master = n;
    instance->value_master = value;

    instance->phab = phab;
    instance->exp = exp;

    argv[0] = "ped";
    argv[1] = path;
    argv[2] = NULL;
    instance->pid = spawnp( argv[0], 0, NULL, NULL, argv, NULL );
    if( instance->pid == -1 ) {
        free( instance );
        unlink( path );
        return NULL;
        }

    instance->path = strdup( path );
    pthread_create( &instance->monitor, NULL, monitor_f, (void*) instance );

    return ( ResPluginHandle_t ) instance;
}

static void plugin_full_destroy( ResPluginHandle_t handle )
{
    PluginFullInstance_t *instance = ( PluginFullInstance_t * ) handle;

    if( instance->pid != -1 ) {
        /* the child process is still alive, but the data inside has been asked
           for already */
        kill( instance->pid, SIGTERM );
        }

    pthread_cancel( instance->monitor );
    pthread_detach( instance->monitor );

    unlink( instance->path );
    free( instance->path );
    free( instance );
}

static void plugin_full_disable( ResPluginHandle_t handle )
{
    PluginFullInstance_t *instance = ( PluginFullInstance_t * ) handle;

    /* the changes inside the editor has already been taken into account */

    /* close the external application if still open */
    /* we need to do this because we cannot put a new data into the external
       application ( ped ), without re-spawing it */
    if( instance->pid != -1 ) {
        kill( instance->pid, SIGTERM );
        instance->pid = -1;

        pthread_cancel( instance->monitor );
        pthread_detach( instance->monitor );
        }

    /* we have a disabled instance ( the instance pointer still valid ),
       but the pid and monitor have been destroyed */
}

static void plugin_full_block( ResPluginHandle_t handle, int block )
{
}

static int plugin_full_any_changes( ResPluginHandle_t handle )
{
    PluginFullInstance_t *instance = ( PluginFullInstance_t * ) handle;

    if ( instance->pid == -1 ) return RESPLUGIN_NO_CHANGES;
    else {
        emit_wm_event( instance, Ph_WM_TOFRONT );
        emit_wm_event( instance, Ph_WM_CLOSE );
        instance->phab_waiting = 1;
        return RESPLUGIN_WAIT;
        }
}

static void plugin_full_get_changes( ResPluginHandle_t handle, int *pn, void **pvalue )
{
    PluginFullInstance_t *instance = ( PluginFullInstance_t * ) handle;

    /* the changes are in the instance->path */
    get_changes_from_file( instance, pn, ( char ** ) pvalue );
}

static void plugin_full_get_area( ResPluginHandle_t handle, PhArea_t *area )
{
    /* not used, let the external editor memorize/restore its own area */
}

static void plugin_full_to_front( ResPluginHandle_t handle )
{
    PluginFullInstance_t *instance = ( PluginFullInstance_t * ) handle;
    emit_wm_event( instance, Ph_WM_TOFRONT );
}

static void plugin_full_set_data( ResPluginHandle_t handle, int n, const void *value,
                                  const ResPluginFormatData_t *format ) {
    PluginFullInstance_t *instance = ( PluginFullInstance_t * ) handle;
    FILE *fp;

    instance->n_master = n;
    instance->value_master = value;

    /* re-spawn the external application and the monitor thread */
    fp = fopen( instance->path, "w" );
    if( value ) fwrite( (char*)value, 1, n, fp );
    fclose( fp );

    instance->pid = spawnl( P_NOWAIT, "ped", "ped", instance->path, NULL );
    pthread_create( &instance->monitor, NULL, monitor_f, (void*) instance );
    }


static void child_exit( int sig ) {
    int status;
    wait( &status );
    }

static void *monitor_f( void * data ) {
    PluginFullInstance_t *instance = ( PluginFullInstance_t * ) data;
    int mod = 0;

    for( ; ; ) {

        /* check if instance->pid still exists */
        if( kill( instance->pid, 0 ) == -1 ) {
            /* the child has exited */
            instance->pid = -1;
            if( instance->phab_waiting ) {
                int n, answer;
                char *value;
                get_changes_from_file( instance, &n, &value );
                if( n == instance->n_master && !memcmp( value,
                                                                   instance->value_master,
                                                                   n ) )
                    answer = RESPLUGIN_NO_CHANGES;
                else answer = RESPLUGIN_CHANGES;

                PtEnter( Pt_EVENT_PROCESS_ALLOW );
                instance->exp->answer_changes( instance->phab, answer, n, value );
                /* the answer_changes() is implying that the editor has been closed */
                PtLeave( Pt_EVENT_PROCESS_ALLOW );
                }
            else {

                /* check the modification time on the file again,
                   because maybe the user did a Exit->Save or not->Save */
                was_file_changed( instance, &mod );

                PtEnter( Pt_EVENT_PROCESS_ALLOW );
                instance->exp->closing( instance->phab );
                PtLeave( Pt_EVENT_PROCESS_ALLOW );
                }

            unlink( instance->path );
            free( instance->path );
            free( instance );
            pthread_detach( pthread_self() );
            return NULL;
            }

        /* check if the instance->path has changed */
        was_file_changed( instance, &mod );

        delay( 200 );
        }

    pthread_detach( pthread_self() );
    return NULL;
    }

static void was_file_changed( PluginFullInstance_t *instance, int *modification_time ) {
    struct stat st;
    if( stat( instance->path, &st ) == 0 ) {
        if( *modification_time != st.st_mtime ) {

            if( *modification_time ) {
                /* the file was changed - call into phab with the apply method */
                apply_from_file( instance );
                }

            *modification_time = st.st_mtime;
            }
        }
    }

static void get_changes_from_file( PluginFullInstance_t *instance, int *pn,
                                   char **value ) {
    FILE *fp;
    int n;
    char *v;

    fp = fopen( instance->path, "r" );
    if( !fp ) return;

    fseek( fp, 0, SEEK_END );
    n = ftell( fp );
    fseek( fp, 0, SEEK_SET );

    v = malloc( n + 1 );
    fread( v, 1, n, fp );
    v[ n ] = 0;

    fclose( fp );

    *pn = n;
    *value = v;
    }

static void apply_from_file( PluginFullInstance_t *instance ) {
    int n;
    char *values;

    get_changes_from_file( instance, &n, &values );
    instance->n_master = n;
    instance->value_master = values;

    PtEnter( Pt_EVENT_PROCESS_ALLOW );
  if( instance->exp->common.apply( instance->phab, instance->n_master,
      instance->value_master ) ) {
    /* we matched the default */
    free( instance->value_master );
    instance->value_master = instance->default_value;
    instance->n_master = instance->n_default;
    }
    PtLeave( Pt_EVENT_PROCESS_ALLOW );
    }

static PhConnectId_t get_connect_id( pid_t pid )
{
   PhConnectInfo_t buf;
   PhConnectId_t id = 0;

   while ((id = PhGetConnectInfo(id, &buf)) != -1
          && (buf.pid != pid ||
                         ND_NODE_CMP(buf.nid, ND_LOCAL_NODE)))
      ++id;

   return id;
}

static void emit_wm_event( PluginFullInstance_t *instance, unsigned long event_type ) {
    PhWindowEvent_t event;
    PhConnectId_t connection_id;

    connection_id = get_connect_id( instance->pid );

    memset( &event, 0, sizeof (event) );
    event.event_f = event_type;
    PtForwardWindowTaskEvent( connection_id, &event );
    }