This Trac instance is not used for development anymore!

We migrated our development workflow to git and Gitea.
To test the future redirection, replace trac by ariadne in the page URL.

Changeset 399 for ps


Ignore:
Timestamp:
06/04/04 19:44:17 (21 years ago)
Author:
janwas
Message:

now emulates FAM API so other code can use one interface. implemented but untested.

Location:
ps/trunk/source/lib/sysdep/win
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • ps/trunk/source/lib/sysdep/win/wfam.cpp

    r262 r399  
    1 // Windows-specific directory change notification
     1// SGI File Alteration Monitor for Win32
    22// Copyright (c) 2004 Jan Wassenberg
    33//
     
    1919
    2020#include "wfam.h"
    21 
     21#include "lib.h"
    2222#include "win_internal.h"
    2323
    24 #if 0
    25 
    26 static const size_t CHANGE_BUF_SIZE = 15000;
    27     // better be enough - if too small, we miss changes made to a directory.
    28 
    29 
    30 // don't worry about size: the user only passes around a pointer
    31 // to this struct, due to the pImpl idiom. this is heap-allocated.
    32 struct FAMRequest_
    33 {
    34     std::string dir_name;
    35     HANDLE hDir;
    36 
    37     // history to detect series of notifications, so we can skip
    38     // redundant reloads (slow)
    39     std::string last_path;
    40     DWORD last_action;  // FILE_ACTION_* codes or 0
    41     DWORD last_ticks;   // timestamp via GetTickCount
    42 
    43     OVERLAPPED ovl;
    44         // we don't use any of its fields.
    45         // overlapped I/O completation notification is via IOCP.
    46         // rationale: see below.
    47     char changes[CHANGE_BUF_SIZE];
    48 
    49 
    50     FAMRequest_(const char* _dir_name)
    51         : dir_name(_dir_name), last_path("")
    52     {
    53         last_action = 0;
    54         last_ticks = 0;
    55 
    56         memset(&ovl, 0, sizeof(ovl));
    57 
    58         // changes[] doesn't need init
    59     }
    60 };
    61 
    62 
    63 // don't worry about size: the user only passes around a pointer
    64 // to this struct, due to the pImpl idiom. this is heap-allocated.
    65 struct FAMConnection_
    66 {
    67     std::string app_name;
    68 
    69 
    70     HANDLE hIOCP;
    71 
    72 // queue necessary - race condition if pass to app and re-issue
    73 // needs to be FIFO, and don't want to constantly shuffle items (can be rather large)
    74 // around => list
    75     typedef std::list<FAMEvent> Events;
    76     Events pending_events;
    77 
    78     // list of all pending requests to detect duplicates and
    79     // for easier cleanup. only store pointer in container -
    80     // they're not copy-equivalent.
    81     typedef std::map<std::string, FAMRequest*> Requests;
    82     typedef Requests::iterator RequestIt;
    83     Requests requests;
    84 
    85     FAMConnection_(const char* _app_name)
    86         : app_name(_app_name)
    87     {
    88         hIOCP = 0;
    89         // not INVALID_HANDLE_VALUE! (CreateIoCompletionPort requirement)
    90     }
    91 
    92     ~FAMConnection_()
    93     {
    94         CloseHandle(hIOCP);
    95         hIOCP = INVALID_HANDLE_VALUE;
    96 
    97         // container holds dynamically allocated Watch structs
    98         //  for(WatchIt it = watches.begin(); it != watches.end(); ++it)
    99         //      delete it->second;
    100     }
    101 };
    102 
    103 
    104 
     24#include <assert.h>
     25
     26#include <string>
     27#include <map>
     28#include <list>
     29
     30
     31// no module init/shutdown necessary: all global data is allocated
     32// as part of a FAMConnection, which must be FAMClose-d by caller.
     33
     34
     35// rationale for polling:
     36// much simpler than pure asynchronous notification: no need for a
     37// worker thread, mutex, and in/out queues. polling isn't inefficient:
     38// we do not examine each file; we only need to check if Windows
     39// has sent a change notification via ReadDirectoryChangesW.
     40//
     41// the main reason, however, is that user code will want to poll anyway,
     42// instead of select() from a worker thread: handling asynchronous file
     43// changes is much more work, requiring everything to be thread-safe.
     44// we currently poll once a frame, so that file changes will happen
     45// at a defined time.
    10546
    10647
     
    11455// - callback notification: notification function is called when the thread
    11556//   that initiated the I/O (ReadDirectoryChangesW) enters an alertable
    116 //   wait state (e.g. with SleepEx). it would be nice to be able to
    117 //   check for notifications from the mainline - would obviate
    118 //   the separate worker thread for RDC and 2 queues to drive it.
    119 //   unfortunately, cannot come up with a robust yet quick way of
    120 //   working off all pending APCs - SleepEx(1) is a hack. even worse,
    121 //   it was noted in a previous project that APCs are sometimes delivered
    122 //   from within Windows APIs, without having used SleepEx
    123 //   (it seems threads enter an "AWS" sometimes when calling the kernel).
     57//   wait state (e.g. with SleepEx). we need to poll for notifications
     58//   from the mainline (see above). unfortunately, cannot come up with
     59//   a robust yet quick way of working off all pending APCs -
     60//   SleepEx(1) is a hack. even worse, it was noted in a previous project
     61//   that APCs are sometimes delivered from within Windows APIs, without
     62//   having used SleepEx (it seems threads enter an "AWS" sometimes when
     63//   calling the kernel).
    12464//
    12565// IOCPs work well and are elegant; have not yet noticed any drawbacks.
    126 
    127 
    128 
    129    
    130 
    131 
    132 // ReadDirectoryChangesW must be called again after each time it returns data,
    133 // so we need to pass along the associated Request.
    134 // since we issue RDC immediately, instead of sending a bogus packet
    135 // to the IOCP that triggers the issue, we don't need the key parameter
    136 // for anything - use it to pass along the Request.
    137 // cleaner than assuming &ovl = &Request, or stuffing it in an unused
    138 // member of OVERLAPPED.
    139 
    140 
    141 
    142 
    143 
    144 int dir_watch_abort(const char* const dir)
    145 {
    146     // find watch
    147 /*  const std::string dir_s(dir);
    148     WatchIt it = watches.find(dir_s);
    149     if(it == watches.end())
    150         return -1;
    151 
    152     delete it->second;
    153     watches.erase(it);
    154 */
    155     return 0;
    156 }
    157 
    158 
    159 // it'd be nice to have only 1 call site of ReadDirectoryChangesW, namely
    160 // in the notification "callback". however, posting a dummy event to the IOCP
    161 // and having the callback issue RDC is a bit ugly, and loses changes made
    162 // before the poll routine is first called.
    163 
    164 static int dir_watch_issue(FAMRequest_* fr)
    165 {
    166     // (re-)request change notification from now on
    167     const DWORD filter = FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME |
    168                          FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE |
    169                          FILE_NOTIFY_CHANGE_CREATION;
    170     BOOL ret = ReadDirectoryChangesW(fr->hDir, fr->changes, CHANGE_BUF_SIZE, FALSE, filter, 0, &fr->ovl, 0);
    171     return ret? 0 : -1;
    172 }
    173 
    174 
    175 int dir_add_watch(const char* const dir, const bool watch_subdirs)
    176 {
    177 return 0;
    178 }
    179 
    180 void FAMCancelMonitor(FAMConnection*, FAMRequest* req)
    181 {
    182 }
    183 
    184 
    185 
    186 
    187 
    188 
    189 
    190 
    191 
    192 
    193 
    194 
    195 
    196 
    197 
    198 
    199 
    200 
    201 
    202 
    203 
    204 
    205 
    206 
    207 
    208 
    209 
    210 
    211 
    212 
    213 
    214 
    215 
    216 
    217 
    218 
    219 
    220 
    221 
    222 void wfam_shutdown()
    223 {
    224 }
    225 
    226 
    227 
    228 int FAMOpen2(FAMConnection* const fc, const char* app_name)
     66// the completion key is used to associate Watch with the directory handle.
     67
     68
     69// don't worry about size: the user only passes around a pointer
     70// to this struct, due to the pImpl idiom. this is heap-allocated.
     71struct Watch
     72{
     73    const std::string dir_name;
     74    HANDLE hDir;
     75
     76    // history to detect series of notifications, so we can skip
     77    // redundant reloads (slow)
     78    std::string last_path;
     79    DWORD last_action;  // FILE_ACTION_* codes or 0
     80    DWORD last_ticks;   // timestamp via GetTickCount
     81
     82    OVERLAPPED ovl;
     83        // fields aren't used.
     84        // overlapped I/O completation notification is via IOCP.
     85
     86    char change_buf[15000];
     87        // better be big enough - if too small,
     88        // we miss changes made to a directory.
     89        // issue code uses sizeof(change_buf) to determine size.
     90
     91    // these are returned in FAMEvent. could get them via FAMNextEvent's
     92    // fc parameter and associating packets with FAMRequest,
     93    // but storing them here is more convenient.
     94    FAMConnection* fc;
     95    FAMRequest* fr;
     96
     97
     98    Watch(const std::string& _dir_name, HANDLE _hDir)
     99        : dir_name(_dir_name), last_path("")
     100    {
     101        hDir = _hDir;
     102
     103        last_action = 0;
     104        last_ticks = 0;
     105
     106        memset(&ovl, 0, sizeof(ovl));
     107
     108        // change_buf[] doesn't need init
     109    }
     110
     111    ~Watch()
     112    {
     113        CloseHandle(hDir);
     114        hDir = INVALID_HANDLE_VALUE;
     115    }
     116};
     117
     118
     119// list of all active watches to detect duplicates and
     120// for easier cleanup. only store pointer in container -
     121// they're not copy-equivalent.
     122typedef std::map<std::string, Watch*> Watches;
     123typedef Watches::iterator WatchIt;
     124
     125typedef std::list<FAMEvent> Events;
     126
     127// don't worry about size: the user only passes around a pointer
     128// to this struct, due to the pImpl idiom. this is heap-allocated.
     129struct AppState
     130{
     131    std::string app_name;
     132
     133    HANDLE hIOCP;
     134
     135    Events pending_events;
     136        // rationale:
     137        // we need a queue, instead of just taking events from the change_buf,
     138        // because we need to re-issue the watch immediately after it returns
     139        // data. of course we can't have the app read from the buffer while
     140        // waiting for RDC to write to the buffer - race condition.
     141        // an alternative to a queue would be to allocate another buffer,
     142        // but that's more complicated, and this way is cleaner anyway.
     143        //
     144        // FAMEvents are somewhat large (~300 bytes), and FIFO,
     145        // so make it a list.
     146
     147    // list of all active watches to detect duplicates and
     148    // for easier cleanup. only store pointer in container -
     149    // they're not copy-equivalent.
     150    Watches watches;
     151
     152    AppState(const char* _app_name)
     153        : app_name(_app_name)
     154    {
     155        hIOCP = 0;
     156        // not INVALID_HANDLE_VALUE! (CreateIoCompletionPort requirement)
     157    }
     158
     159    ~AppState()
     160    {
     161        CloseHandle(hIOCP);
     162        hIOCP = INVALID_HANDLE_VALUE;
     163
     164        // free all (dynamically allocated) Watch-es
     165        for(WatchIt it = watches.begin(); it != watches.end(); ++it)
     166            delete it->second;
     167    }
     168};
     169
     170
     171// macros to return pointers to the above from the FAM* structs (pImpl)
     172// (macro instead of function so we can bail out of the "calling" function)
     173#define GET_APP_STATE(fc, ptr_name)\
     174    AppState* const ptr_name = (AppState*)fc->internal;\
     175    if(!ptr_name)\
     176    {\
     177        debug_warn("no FAM connection");\
     178        return -1;\
     179    }
     180
     181#define GET_WATCH(fr, ptr_name)\
     182    Watch* const ptr_name = (Watch*)fr->internal;\
     183    if(!ptr_name)\
     184    {\
     185        debug_warn("FAMRequest.internal invalid!");\
     186        return -1;\
     187    }
     188
     189
     190int FAMOpen2(FAMConnection* const fc, const char* const app_name)
    229191{
    230192    try
    231193    {
    232         fc->internal = new FAMConnection_(app_name);
     194        fc->internal = new AppState(app_name);
    233195    }
    234196    catch(std::bad_alloc)
     
    247209int FAMClose(FAMConnection* const fc)
    248210{
    249     FAMConnection_*& fc_ = (FAMConnection_*)fc->internal;
    250     if(!fc_)
    251     {
    252         debug_warn("FAMClose: already closed");
    253         return -1;
    254     }
    255 
    256     delete fc_;
    257     fc_ = 0;
     211    GET_APP_STATE(fc, state);
     212
     213    delete state;
     214    fc->internal = 0;
    258215    return 0;
    259216}
    260217
    261218
    262 int FAMMonitorDirectory(FAMConnection* fc, char* dir, FAMRequest* fr, void* user)
    263 {
    264     FAMConnection_* fc_ = (FAMConnection_*)fc->internal;
    265 
    266 /*
     219// HACK - see call site
     220static void get_packet(AppState*);
     221
     222
     223int FAMMonitorDirectory(FAMConnection* const fc, char* const _dir, FAMRequest* const fr, void* const user)
     224{
     225    GET_APP_STATE(fc, state);
     226    Watches& watches = state->watches;
     227    HANDLE& hIOCP = state->hIOCP;
     228
     229    const std::string dir(_dir);
     230
    267231    // make sure dir is not already being watched
    268     const std::string dir_s(dir);
    269     WatchIt it = watches.find(dir_s);
     232    WatchIt it = watches.find(dir);
    270233    if(it != watches.end())
    271234        return -1;
    272 */
    273     HANDLE hDir  = INVALID_HANDLE_VALUE;
    274     HANDLE& hIOCP = fc_->hIOCP;
    275235
    276236    // open handle to directory
    277237    const DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
    278238    const DWORD flags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
    279     hDir = CreateFile(dir, FILE_LIST_DIRECTORY, share, 0, OPEN_EXISTING, flags, 0);
     239    HANDLE hDir = CreateFile(_dir, FILE_LIST_DIRECTORY, share, 0, OPEN_EXISTING, flags, 0);
    280240    if(hDir == INVALID_HANDLE_VALUE)
    281241        return -1;
    282242
    283     // create IOCP (if not already done) and bind dir to it
    284     hIOCP = CreateIoCompletionPort(hDir, hIOCP, KEY_NORMAL, 0);
     243    // create Watch and associate with FAM structs
     244    Watch* const w = new Watch(dir, hDir);
     245    watches[dir] = w;
     246    w->fc = fc;
     247    w->fr = fr;
     248
     249    // associate Watch* with the directory handle. when we receive a packet
     250    // from the IOCP, we will need to re-issue the watch and find the
     251    // corresponding FAMRequest.
     252    const ULONG_PTR key = (ULONG_PTR)w;
     253
     254    // create IOCP (if not already done) and attach hDir to it
     255    hIOCP = CreateIoCompletionPort(hDir, hIOCP, key, 0);
    285256    if(hIOCP == 0 || hIOCP == INVALID_HANDLE_VALUE)
    286257    {
    287 fail:
     258        delete w;
    288259        CloseHandle(hDir);
    289260        return -1;
    290261    }
    291262
    292 
    293 
    294     // insert
    295     Watch* const w = new Watch(hDir, watch_subdirs);
    296     watches[dir_s] = w;
    297 
    298     dir_watch_issue(w);
     263    // post a dummy kickoff packet; the IOCP polling code will "re"issue
     264    // the corresponding watch. this keeps the ReadDirectoryChangesW call
     265    // and directory <--> Watch association code in one place.
     266    //
     267    // we call get_packet so that it's issued immediately,
     268    // instead of only at the next call to FAMPending.
     269    PostQueuedCompletionStatus(hIOCP, 0, key, 0);
     270    get_packet(state);
     271
     272    fr->internal = w;
    299273
    300274    return 0;
    301 
    302 }
    303 
    304 
    305 // added bonus: can actually "poll" for changes here - obviates a worker
    306 // thread, mutex, and 2 queues.
    307 
    308 
    309 
    310 static int extract_events(FAMConnection* conn, FAMRequest* req)
    311 {
    312     FAMConnection_ conn_ = (FAMConnection_*)conn->internal;
    313     const FILE_NOTIFY_INFORMATION* fni = (const FILE_NOTIFY_INFORMATION*)req->changes;
    314     Events& events = fc_->pending_events;
    315 
     275}
     276
     277
     278int FAMCancelMonitor(FAMConnection* const fc, FAMRequest* const fr)
     279{
     280    GET_APP_STATE(fc, state);
     281    GET_WATCH(fr, w)
     282
     283    // TODO
     284
     285    return -1;
     286}
     287
     288
     289static int extract_events(Watch* const w)
     290{
     291    FAMConnection* const fc = w->fc;
     292    FAMRequest* const fr = w->fr;
     293
     294    GET_APP_STATE(fc, state);
     295    Events& events = state->pending_events;
     296
     297    // will be modified for each event and added to events
    316298    FAMEvent event_template;
    317     event_template.conn = conn;
    318     event_template.req = req;
     299    event_template.fc = fc;
     300    event_template.fr = *fr;
     301
     302    // points to current FILE_NOTIFY_INFORMATION;
     303    // char* simplifies advancing to the next (variable length) FNI.
     304    char* pos = w->change_buf;
    319305
    320306    // for every packet in buffer: (there's at least one)
    321307    for(;;)
    322308    {
     309        const FILE_NOTIFY_INFORMATION* const fni = (const FILE_NOTIFY_INFORMATION*)pos;
     310
     311        events.push_back(event_template);
     312        FAMEvent& event = events.back();
     313            // fields are set below; we need to add the event here
     314            // so that we have a place to put the converted filename.
     315
     316
     317        //
     318        // interpret action
     319        //
     320
     321        const char* actions[] = { "", "FILE_ACTION_ADDED", "FILE_ACTION_REMOVED", "FILE_ACTION_MODIFIED", "FILE_ACTION_RENAMED_OLD_NAME", "FILE_ACTION_RENAMED_NEW_NAME" };
     322        const char* action = actions[fni->Action];
     323
    323324        // many apps save by creating a temp file, deleting the original,
    324325        // and renaming the temp file. that leads to 2 reloads, which is slow.
     
    326327        // the notification order is always the same.
    327328
    328         // TODO:
    329 
    330         const char* actions[] = { "", "FILE_ACTION_ADDED", "FILE_ACTION_REMOVED", "FILE_ACTION_MODIFIED", "FILE_ACTION_RENAMED_OLD_NAME", "FILE_ACTION_RENAMED_NEW_NAME" };
    331         const char* action = actions[fni->Action];
    332 
    333         // convert Windows BSTR-style path to
    334         // portable C string path for the resource manager.
    335         // HACK: convert in place, we copy it into
    336         char fn[MAX_PATH];
    337         char* p = fn;
     329        // TODO
     330
     331        FAMCodes code;
     332        switch(fni->Action)
     333        {
     334        case FILE_ACTION_ADDED:
     335        case FILE_ACTION_RENAMED_NEW_NAME:
     336            code = FAMCreated;
     337            break;
     338        case FILE_ACTION_REMOVED:
     339        case FILE_ACTION_RENAMED_OLD_NAME:
     340            code = FAMDeleted;
     341            break;
     342        case FILE_ACTION_MODIFIED:
     343            code = FAMChanged;
     344            break;
     345        };
     346
     347        event.code = code;
     348
     349
     350        //
     351        // convert filename from Windows BSTR to portable C string
     352        //
     353
     354        char* fn = event.filename;
    338355        const int num_chars = fni->FileNameLength/2;
    339356        for(int i = 0; i < num_chars; i++)
     
    342359            if(c == '\\')
    343360                c = '/';
    344             *p++ = c;
     361            *fn++ = c;
    345362        }
    346         *p = '\0';
    347 
    348         // don't want to expose details
    349 
    350         events.push_back(event_template);
     363        *fn = '\0';
    351364
    352365
     
    354367        // advance to next FILE_NOTIFY_INFORMATION (variable length)
    355368        if(ofs)
    356             (char*&)fni += ofs;
     369            pos += ofs;
    357370        // this was the last entry - no more elements left in buffer.
    358371        else
     
    361374
    362375    return 0;
    363 
    364 
    365     res_reload(fn);
    366 
    367     return 0;
    368 }
    369 
    370 
    371 int FAMPending(FAMConnection* fc)
    372 {
    373     FAMConnection_* const fc_ = (FAMConnection_*)fc->internal;
    374     Events& pending_events = fc_->pending_events;
    375 
     376}
     377
     378
     379// if a packet is pending, extract its events and re-issue its watch.
     380void get_packet(AppState* const state)
     381{
     382    // poll for change notifications from all pending FAMRequests
     383    DWORD bytes_transferred;
     384        // used to determine if packet is valid or a kickoff
     385    ULONG_PTR key;
     386    OVERLAPPED* povl;
     387    BOOL got_packet = GetQueuedCompletionStatus(state->hIOCP, &bytes_transferred, &key, &povl, 0);
     388    if(!got_packet) // no new packet - done
     389        return;
     390
     391    Watch* w = (Watch*)key;
     392
     393    // this is an actual packet, not just a kickoff for issuing the watch.
     394    // extract the events and push them onto AppState's queue.
     395    if(bytes_transferred != 0)
     396        extract_events(w);
     397
     398    // (re-)issue change notification request.
     399    // it's safe to reuse Watch.change_buf, because we copied out all events.
     400    const DWORD filter = FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME |
     401                         FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE |
     402                         FILE_NOTIFY_CHANGE_CREATION;
     403    const DWORD buf_size = sizeof(w->change_buf);
     404    BOOL ret = ReadDirectoryChangesW(w->hDir, w->change_buf, buf_size, FALSE, filter, 0, &w->ovl, 0);
     405    if(!ret)
     406        debug_warn("ReadDirectoryChangesW failed");
     407}
     408
     409
     410int FAMPending(FAMConnection* const fc)
     411{
     412    GET_APP_STATE(fc, state);
     413    Events& pending_events = state->pending_events;
     414
     415    // still have events in the queue?
     416    // (slight optimization; no need to call get_packet if so)
    376417    if(!pending_events.empty())
    377418        return 1;
    378419
    379     // check if new buffer has been filled
    380     DWORD bytes_transferred;    // unused
    381     ULONG_PTR key;
    382     OVERLAPPED* povl;
    383     BOOL got_packet = GetQueuedCompletionStatus(fc_->hIOCP, &bytes_transferred, &key, &povl, 0);
    384     if(!got_packet)
    385         return 0;
    386 
    387     CHECK_ERR(extract_events(conn, req));
    388 
    389 //  dir_watch_issue(buf);
    390 
    391 
    392 
    393 
    394     fc->fni = (FILE_NOTIFY_INFORMATION*)w->buf;
    395     return 1;
     420    get_packet(state);
     421
     422    return !pending_events.empty();
    396423}
    397424 
     
    399426int FAMNextEvent(FAMConnection* const fc, FAMEvent* const fe)
    400427{
    401     FAMConnection_* const fc_ = (FAMConnection_*)fc->internal;
    402     Events& pending_events = fc_->pending_events;
    403 
    404     if(!fe)
    405     {
    406         debug_warn("FAMNextEvent: fe = 0");
    407         return ERR_INVALID_PARAM;
    408     }
     428    GET_APP_STATE(fc, state);
     429    Events& pending_events = state->pending_events;
    409430
    410431    if(pending_events.empty())
     
    418439    return 0;
    419440}
    420  
    421 
    422 #endif
  • ps/trunk/source/lib/sysdep/win/wfam.h

    r262 r399  
    1 #include "posix.h"
     1// SGI File Alteration Monitor for Win32
     2// Copyright (c) 2004 Jan Wassenberg
     3//
     4// This program is free software; you can redistribute it and/or
     5// modify it under the terms of the GNU General Public License as
     6// published by the Free Software Foundation; either version 2 of the
     7// License, or (at your option) any later version.
     8//
     9// This program is distributed in the hope that it will be useful, but
     10// WITHOUT ANY WARRANTY; without even the implied warranty of
     11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12// General Public License for more details.
     13//
     14// Contact info:
     15//   Jan.Wassenberg@stud.uni-karlsruhe.de
     16//   http://www.stud.uni-karlsruhe.de/~urkt/
     17
     18#ifndef WFAM_H__
     19#define WFAM_H__
     20
     21
     22#include "posix.h"  // PATH_MAX
     23
     24
     25// FAM documentation: http://techpubs.sgi.com/library/tpl/cgi-bin/getdoc.cgi?coll=0650&db=bks&fname=/SGI_Developer/books/IIDsktp_IG/sgi_html/ch08.html
     26
    227
    328// opaque structs are too hard to keep in sync with the real definition,
    4 // and we don't want to expose the internals. therefore, use the pImpl pattern.
     29// and we don't want to expose the internals. therefore, use the pImpl idiom.
    530
    631struct FAMRequest
    732{
    833    void* internal;
     34    // reqnum not needed, since FAMMonitorDirectory2 isn't supported.
    935};
    1036
     
    1440};
    1541
    16 
    17 
    18 enum FAMChangeCode { FAMDeleted, FAMCreated, FAMChanged };
     42enum FAMCodes { FAMDeleted, FAMCreated, FAMChanged };
    1943
    2044typedef struct
     
    2448    char filename[PATH_MAX];
    2549    void* user;
    26     FAMChangeCode code;
     50    FAMCodes code;
    2751}
    2852FAMEvent;
     
    3054
    3155extern int FAMOpen2(FAMConnection*, const char* app_name);
    32 extern void FAMClose(FAMConnection*);
     56extern int FAMClose(FAMConnection*);
    3357
    3458extern int FAMMonitorDirectory(FAMConnection*, const char* dir, FAMRequest* req, void* user);
    35 extern void FAMCancelMonitor(FAMConnection*, FAMRequest* req);
     59extern int FAMCancelMonitor(FAMConnection*, FAMRequest* req);
    3660
    3761extern int FAMPending(FAMConnection*);
    3862extern int FAMNextEvent(FAMConnection*, FAMEvent* event);
    3963
     64
     65#endif  // #ifndef WFAM_H__
Note: See TracChangeset for help on using the changeset viewer.