This chapter contains examples of using the PhAB plugin API to create resource editors. There are two examples:
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 "rstring.h" #include "abimport.h" #include "proto.h" 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( 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 ); 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: { if( instance->state != RESPLUGIN_STATE_MATCH_MASTER && instance->state != RESPLUGIN_STATE_MATCH_DEFAULT_MASTER ) { get_value_and_apply( instance ); set_state( instance, instance->value_master ); } } break; case RESPLUGIN_ACTION_CLOSE: { 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->state = RESPLUGIN_STATE_MATCH_DEFAULT; else instance->state = RESPLUGIN_STATE_MATCH_DEFAULT_MASTER; } else { if( strcmp( value, instance->value_master ) ) instance->state = RESPLUGIN_STATE_NO_MATCH; else instance->state = RESPLUGIN_STATE_MATCH_MASTER; } instance->exp->set_state( instance->convenience_handle, instance->state ); }
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 <photon/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 ); }