doublecmd/ufilesystemwatcher.pas
2009-03-22 09:54:52 +00:00

286 lines
7.7 KiB
ObjectPascal

{
Double Commander
-------------------------------------------------------------------------
This is a thread-component sends an event when a change in the file system occurs.
Copyright (C) 2009 Koblov Alexander (Alexx2000@mail.ru)
based on:
TDirWatcher by Bernhard Angerer (angerer@mail.ift.tuwien.ac.at)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
}
unit uFileSystemWatcher;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils;
type
TWatchFilter = set of (wfFileNameChange, wfAttributesChange);
TOnWatcherNotifyEvent = procedure(Sender: TObject; NotifyEvent: TWatchFilter) of object;
{ TWatcherThread }
TWatcherThread = class(TThread)
private
FOwner: TObject;
FOnWatcherNotifyEvent: TOnWatcherNotifyEvent;
FFileHandle,
FNotifyHandle: THandle;
FWatchPath: UTF8String;
FWatchFilter,
FNotifyEvent: TWatchFilter;
protected
procedure Execute; override;
procedure WatcherNotifyEvent;
public
constructor Create(aOwner: TObject; sPath: UTF8String; aWatchFilter: TWatchFilter);
destructor Destroy; override;
property OnWatcherNotifyEvent: TOnWatcherNotifyEvent write FOnWatcherNotifyEvent;
end;
{ TFileSystemWatcher }
TFileSystemWatcher = class
private
FOwner: TObject;
FActive: Boolean;
FWatchPath: UTF8String;
FWatchFilter: TWatchFilter;
FWatcherThread: TWatcherThread;
FOnWatcherNotifyEvent: TOnWatcherNotifyEvent;
procedure SetActive(const AValue: Boolean);
procedure SetWatchFilter(const AValue: TWatchFilter);
procedure SetWatchPath(const AValue: UTF8String);
public
constructor Create(aOwner: TObject; sPath: UTF8String; aWatchFilter: TWatchFilter);
destructor Destroy; override;
property Active: Boolean read FActive write SetActive;
property WatchPath: UTF8String read FWatchPath write SetWatchPath;
property WatchFilter: TWatchFilter read FWatchFilter write SetWatchFilter;
property OnWatcherNotifyEvent: TOnWatcherNotifyEvent write FOnWatcherNotifyEvent;
end;
implementation
uses
{$IF DEFINED(MSWINDOWS)}
Windows
{$ELSEIF DEFINED(LINUX)}
inotify, Unix, BaseUnix, UnixUtil, UnixType
{$ENDIF};
{ TWatcherThread }
procedure TWatcherThread.Execute;
{$IF DEFINED(MSWINDOWS)}
var
hNotifyFilter: LongWord;
wsPath: WideString;
begin
hNotifyFilter:= 0;
if wfFileNameChange in FWatchFilter then
hNotifyFilter:= hNotifyFilter or FILE_NOTIFY_CHANGE_FILE_NAME;
if wfAttributesChange in FWatchFilter then
hNotifyFilter:= hNotifyFilter or FILE_NOTIFY_CHANGE_ATTRIBUTES;
wsPath:= UTF8Decode(FWatchPath);
FNotifyHandle:= FindFirstChangeNotificationW(PWideChar(wsPath), False, hNotifyFilter);
if FNotifyHandle <> INVALID_HANDLE_VALUE then
repeat
if WaitForSingleObject(FNotifyHandle, 100) = WAIT_OBJECT_0 then
begin
Synchronize(@WatcherNotifyEvent);
end;
if not FindNextChangeNotification(FNotifyHandle) then
begin
RaiseLastOSError;
Break;
end;
until Terminated;
end;
{$ELSEIF DEFINED(LINUX)}
var
bytes_to_parse, p: Integer;
buf: PChar;
ev: pinotify_event;
hNotifyFilter: uint32_t;
begin
hNotifyFilter:= 0;
if wfFileNameChange in FWatchFilter then
hNotifyFilter:= hNotifyFilter or IN_CREATE or IN_DELETE or IN_DELETE_SELF or IN_MOVE or IN_MOVE_SELF;
if wfAttributesChange in FWatchFilter then
hNotifyFilter:= hNotifyFilter or IN_ATTRIB;
WriteLn('Start watching');
// create inotify instance
FFileHandle:= inotify_init();
if (FFileHandle < 0) then
begin
WriteLn('inotify_init(): ');
Exit;
end;
// WriteLn('After inotify_init()');
// add watch
FNotifyHandle:= inotify_add_watch(FFileHandle, PChar(FWatchPath), hNotifyFilter);
if (FNotifyHandle < 0) then
begin
WriteLn('inotify_add_watch(): ');
Exit;
end;
// WriteLn('After inotify_add_watch()');
// process events
repeat
// wait events
repeat
if (fpioctl(FFileHandle, $541B, @bytes_to_parse) = -1) then
begin
WriteLn('ioctl(): ');
Exit;
end;
Sleep(1);
if Terminated then Exit;
until (bytes_to_parse >= SizeOf(inotify_event));
// WriteLn('After fpioctl()');
// get memory for events and read them
buf := GetMem(bytes_to_parse);
if (fpread(FFileHandle, buf, bytes_to_parse) = -1) then
begin
WriteLn('read(): ');
Exit;
end;
// WriteLn('After fpread()');
// parse events and print them
p:= 0;
while (p < bytes_to_parse) do
begin
ev:= pinotify_event((buf + p));
WriteLn('wd = ',ev^.wd,', mask = ',ev^.mask,', cookie = ',ev^.cookie, 'name = ', PChar(@ev^.name));
// call event handler
Synchronize(@WatcherNotifyEvent);
p:= p + ev^.len + 16;
end;
// WriteLn('After print()');
// free memory
FreeMem(buf);
until Terminated;
end;
{$ELSE}
begin
end;
{$ENDIF}
procedure TWatcherThread.WatcherNotifyEvent;
begin
if Assigned(FOnWatcherNotifyEvent) then
FOnWatcherNotifyEvent(FOwner, FNotifyEvent);
end;
destructor TWatcherThread.Destroy;
{$IF DEFINED(MSWINDOWS)}
begin
if FNotifyHandle <> NULL then
FindCloseChangeNotification(FNotifyHandle);
inherited Destroy;
end;
{$ELSEIF DEFINED(LINUX)}
begin
// remove watch
inotify_rm_watch(FFileHandle, FNotifyHandle);
// close inotify instance
fpClose(FFileHandle);
inherited Destroy;
end;
{$ELSE}
begin
inherited Destroy;
end;
{$ENDIF}
constructor TWatcherThread.Create(aOwner: TObject; sPath: UTF8String; aWatchFilter: TWatchFilter);
begin
inherited Create(True); // create suspended
FOwner:= aOwner;
FWatchPath:= sPath;
FWatchFilter:= aWatchFilter;
FreeOnTerminate:= True;
end;
{ TFileSystemWatcher }
procedure TFileSystemWatcher.SetWatchPath(const AValue: UTF8String);
begin
if Assigned(FWatcherThread) then
FWatcherThread.Terminate;
FWatchPath:= AValue;
FWatcherThread:= TWatcherThread.Create(FOwner, FWatchPath, FWatchFilter);
FWatcherThread.OnWatcherNotifyEvent:= FOnWatcherNotifyEvent;
FWatcherThread.Resume;
end;
procedure TFileSystemWatcher.SetActive(const AValue: Boolean);
begin
if FActive = AValue then Exit;
if AValue then
begin
FWatcherThread:= TWatcherThread.Create(FOwner, FWatchPath, FWatchFilter);
FWatcherThread.OnWatcherNotifyEvent:= FOnWatcherNotifyEvent;
FWatcherThread.Resume;
FActive:= AValue;
end
else
begin
if Assigned(FWatcherThread) then
FWatcherThread.Terminate;
FActive:= AValue;
end;
end;
procedure TFileSystemWatcher.SetWatchFilter(const AValue: TWatchFilter);
begin
if FWatchFilter = AValue then Exit;
FWatchFilter:= AValue;
SetWatchPath(FWatchPath);
end;
constructor TFileSystemWatcher.Create(aOwner: TObject; sPath: UTF8String; aWatchFilter: TWatchFilter);
begin
FActive:= False;
FOwner:= aOwner;
FWatchPath:= sPath;
FWatchFilter:= aWatchFilter;
end;
destructor TFileSystemWatcher.Destroy;
begin
if Assigned(FWatcherThread) then
FWatcherThread.Terminate;
inherited Destroy;
end;
end.