UPD: FileSystemWatcher:

- Use only a single thread
- Instead of polling sleep indefinitely when no events
- Watch all tabs (currently always enabled)
This commit is contained in:
cobines 2011-03-25 05:29:04 +00:00
commit 69805fa9bc
7 changed files with 1016 additions and 536 deletions

View file

@ -43,7 +43,7 @@ uses
Graphics, Forms, Menus, Controls, StdCtrls, ExtCtrls, ActnList,
Buttons, SysUtils, Classes, SynEdit, LCLType, ComCtrls,
KASToolBar, KASBarMenu, KASBarFiles,
uCmdBox, uFileSystemWatcher, uFilePanelSelect,
uCmdBox, uFilePanelSelect,
uFileView, uColumnsFileView, uFileSource, uFileViewNotebook, uFile,
uOperationsManager, uFileSourceOperation, uDrivesList, uTerminal, uClassesEx,
uXmlConfig, uDrive, uDriveWatcher;
@ -488,15 +488,12 @@ type
procedure tbChangeDirClick(Sender: TObject);
procedure tbCopyClick(Sender: TObject);
procedure tbEditClick(Sender: TObject);
procedure FramePanelOnWatcherNotifyEvent(Sender: TObject; NotifyData: PtrInt);
procedure OnUniqueInstanceMessage(Sender: TObject; Params: array of UTF8String; ParamCount: Integer);
procedure tbPasteClick(Sender: TObject);
procedure AllProgressOnUpdateTimer(Sender: TObject);
private
{ Private declarations }
PanelSelected: TFilePanelSelect;
LeftFrameWatcher,
RightFrameWatcher: TFileSystemWatcher;
DrivesList : TDrivesList;
MainSplitterHintWnd: THintWindow;
HiddenToTray: Boolean;
@ -587,7 +584,6 @@ type
function ExecCmd(Cmd:string; param:string='') : Boolean;
function ExecCmdEx(Sender: TObject; NumberOfButton:Integer) : Boolean;
procedure ToggleConsole;
procedure ToggleFileSystemWatcher;
procedure UpdateWindowView;
procedure MinimizeWindow;
procedure LoadWindowState;
@ -636,7 +632,7 @@ implementation
uses
LCLIntf, Dialogs, uGlobs, uLng, fConfigToolBar, uMasks, fCopyMoveDlg, uQuickViewPanel,
uShowMsg, fHotDir, uDCUtils, uLog, uGlobsPaths, LCLProc, uOSUtils, uOSForms, uPixMapManager,
uDragDropEx, StrUtils, uKeyboard, uFileSystemFileSource, fViewOperations,
uDragDropEx, uKeyboard, uFileSystemFileSource, fViewOperations,
uFileSourceOperationTypes, uFileSourceCopyOperation, uFileSourceMoveOperation,
fFileOpDlg, uFileSourceProperty, uFileSourceExecuteOperation, uArchiveFileSource,
uShellExecute, uActs, fSymLink, fHardLink, uExceptions, uUniqueInstance, Clipbrd,
@ -688,8 +684,6 @@ begin
InitPropStorage(Self);
DrivesList := nil;
LeftFrameWatcher:= nil;
RightFrameWatcher:= nil;
FDropParams := nil;
cmdConsole:= nil;
@ -1045,13 +1039,6 @@ var
begin
DebugLn('Destroying main form');
// Disable file watcher.
if Assigned(LeftFrameWatcher) then
FreeAndNil(LeftFrameWatcher);
if Assigned(RightFrameWatcher) then
FreeAndNil(RightFrameWatcher);
DebugLn('File watchers finished');
TDriveWatcher.RemoveObserver(@OnDriveWatcherEvent);
TDriveWatcher.Finalize;
DebugLn('Drive watcher finished');
@ -1769,17 +1756,7 @@ begin
if Assigned(Page) then
begin
if Page.LockState = tlsPathResets then // if locked with directory change
Page.FileView.CurrentPath := Page.LockPath
else if (Notebook = nbLeft) and (FrameLeft <> nil) then
begin
if Assigned(LeftFrameWatcher) and (LeftFrameWatcher.WatchPath <> FrameLeft.CurrentPath) then
LeftFrameWatcher.WatchPath:= FrameLeft.CurrentPath;
end
else if (Notebook = nbRight) and (FrameRight <> nil) then
begin
if Assigned(RightFrameWatcher) and (RightFrameWatcher.WatchPath <> FrameRight.CurrentPath) then
RightFrameWatcher.WatchPath:= FrameRight.CurrentPath;
end;
Page.FileView.CurrentPath := Page.LockPath;
// Update selected drive only on non-active panel,
// because active panel is updated on focus change.
@ -3028,18 +3005,10 @@ begin
if Page.IsActive then
begin
ToggleFileSystemWatcher;
if Assigned(FileView.FileSource) then
begin
// update file system watcher directory
if FileView.FileSource.IsClass(TFileSystemFileSource) then
begin
if (ANoteBook = nbLeft) and Assigned(LeftFrameWatcher) then
LeftFrameWatcher.WatchPath:= FileView.CurrentPath
else if (ANoteBook = nbRight) and Assigned(RightFrameWatcher) then
RightFrameWatcher.WatchPath:= FileView.CurrentPath;
if glsDirHistory.IndexOf(FileView.CurrentPath) = -1 then
glsDirHistory.Insert(0, FileView.CurrentPath);
end;
@ -3576,41 +3545,6 @@ begin
ConsoleSplitter.Visible:= gTermWindow;
end;
procedure TfrmMain.ToggleFileSystemWatcher;
var
WatchFilter: TWatchFilter;
procedure ToggleWatcher(FileView: TFileView; var Watcher: TFileSystemWatcher; AnOwner: TComponent);
begin
if (WatchFilter <> []) and
Assigned(FileView) and Assigned(FileView.FileSource) and
(FileView.FileSource.IsClass(TFileSystemFileSource)) then
begin
if not Assigned(Watcher) then
begin
Watcher:= TFileSystemWatcher.Create(AnOwner, FileView.CurrentPath, WatchFilter);
Watcher.OnWatcherNotifyEvent:= @FramePanelOnWatcherNotifyEvent;
Watcher.Active:= True;
end;
end
else
begin
if Assigned(Watcher) then
FreeAndNil(Watcher);
end;
end;
begin
WatchFilter:= [];
if (watch_file_name_change in gWatchDirs) then
Include(WatchFilter, wfFileNameChange);
if (watch_attributes_change in gWatchDirs) then
Include(WatchFilter, wfAttributesChange);
ToggleWatcher(FrameLeft, LeftFrameWatcher, nbLeft);
ToggleWatcher(FrameRight, RightFrameWatcher, nbRight);
end;
procedure TfrmMain.UpdateWindowView;
procedure UpdateNoteBook(NoteBook: TFileViewNotebook);
@ -3848,7 +3782,6 @@ begin
(pnlKeys.Controls[I] as TSpeedButton).Flat := gInterfaceFlat;
end;
ToggleFileSystemWatcher;
ShowTrayIcon(gAlwaysShowTrayIcon);
UpdateFreeSpace(fpLeft);
@ -3978,33 +3911,6 @@ begin
end;
end;
procedure TfrmMain.FramePanelOnWatcherNotifyEvent(Sender: TObject; NotifyData: PtrInt);
var
sDrive,
sWatchDirsExclude: String;
FileView: TFileView;
begin
// if not active and refresh only in foreground then exit
if (watch_only_foreground in gWatchDirs) and (not Application.Active) then Exit;
if not (Sender is TFileViewNotebook) then Exit;
FileView := (Sender as TFileViewNotebook).ActiveView;
if not Assigned(FileView) then Exit;
// if current path in exclude list then exit
if (watch_exclude_dirs in gWatchDirs) and (gWatchDirsExclude <> '') then
begin
sWatchDirsExclude:= gWatchDirsExclude;
repeat
sDrive:= Copy2SymbDel(sWatchDirsExclude, ';');
if IsInPath(UTF8UpperCase(sDrive), UTF8UpperCase(FileView.CurrentPath), True) then
Exit;
until sWatchDirsExclude = '';
end;
FileView.Reload;
end;
procedure TfrmMain.OnUniqueInstanceMessage(Sender: TObject; Params: array of UTF8String; ParamCount: Integer);
var
I: Integer;

View file

@ -16,7 +16,6 @@ uses
uDisplayFile,
uColumns,
uFileSorting,
uFunctionThread,
uXmlConfig,
uClassesEx,
uTypes,
@ -3001,6 +3000,8 @@ procedure TColumnsFileView.UpdateView;
var
bLoadingFilelist: Boolean;
begin
inherited;
bLoadingFilelist := GetCurrentWorkType = fvwtCreate;
StopWorkers;
@ -4237,4 +4238,4 @@ begin
end;
end.

View file

@ -109,14 +109,13 @@ type
implementation
uses
uOSUtils, uFindEx, uDateTimeUtils,
uOSUtils, uFindEx, uDateTimeUtils, uGlobs,
{$IFDEF MSWINDOWS}
uMyWindows, Windows,
{$ENDIF}
{$IFDEF UNIX}
BaseUnix, uUsersGroups, FileUtil, uMyUnix,
{$ENDIF}
uFileSystemWatcher,
uFileSystemListOperation,
uFileSystemCopyOperation,
uFileSystemMoveOperation,
@ -757,4 +756,4 @@ begin
end;
end.

View file

@ -8,7 +8,7 @@ uses
Classes, SysUtils, Controls, ExtCtrls, contnrs, fgl,
uFile, uDisplayFile, uFileSource, uMethodsList, uDragDropEx, uXmlConfig,
uClassesEx, uFileSorting, uFileViewHistory, uFileProperty, uFileViewWorker,
uFunctionThread;
uFunctionThread, uFileSystemWatcher;
type
@ -74,6 +74,7 @@ type
}
FRequestedActiveFile: String;
FFileFilter: String;
FWatchPath: String;
FMethods: TMethodsList;
@ -92,14 +93,17 @@ type
function GetFileSourcesCount: Integer;
function GetPath(FileSourceIndex, PathIndex: Integer): UTF8String;
function GetPathsCount(FileSourceIndex: Integer): Integer;
function GetWatcherActive: Boolean;
procedure SetFileFilter(const NewFilter: String);
{en
Assigns the built lists to the file view and displays new the file list.
}
procedure SetFileList(var NewDisplayFiles: TDisplayFiles;
var NewFileSourceFiles: TFiles);
procedure EnableWatcher(Enable: Boolean);
procedure ReloadEvent(const aFileSource: IFileSource; const ReloadedPaths: TPathsArray);
procedure WatcherEvent(const WatchPath: String; NotifyData, UserData: Pointer);
protected
FFiles: TDisplayFiles; //<en List of displayed files
@ -233,7 +237,7 @@ type
procedure LoadConfiguration(AConfig: TXmlConfig; ANode: TXmlNode); virtual;
procedure SaveConfiguration(AConfig: TXmlConfig; ANode: TXmlNode); virtual;
procedure UpdateView; virtual abstract;
procedure UpdateView; virtual;
{en
Moves the selection focus to the file specified by aFilePath.
@ -308,6 +312,7 @@ type
}
property SelectedFiles: TFiles read GetSelectedFiles;
property Sorting: TFileSortings read FSortings write SetSorting;
property WatcherActive: Boolean read GetWatcherActive;
property NotebookPage: TCustomPage read GetNotebookPage;
property OnBeforeChangePath : TOnBeforeChangePath read FOnBeforeChangePath write FOnBeforeChangePath;
@ -364,7 +369,7 @@ type
implementation
uses
Dialogs, LCLProc,
Dialogs, LCLProc, Forms, strutils,
uActs, uLng, uShowMsg, uFileSystemFileSource, uFileSourceUtil, uDCUtils, uGlobs;
constructor TFileView.Create(AOwner: TWinControl; AFileSource: IFileSource; APath: String);
@ -414,6 +419,7 @@ begin
FWorkersThread := nil;
FReloading := False;
FFileViewWorkers := TFileViewWorkers.Create(False);
FWatchPath := EmptyStr;
inherited Create(AOwner);
Parent := AOwner;
@ -545,8 +551,10 @@ procedure TFileView.SetCurrentPath(NewPath: String);
begin
if (NewPath <> CurrentPath) and BeforeChangePath(FileSource, NewPath) then
begin
EnableWatcher(False);
FHistory.AddPath(NewPath); // Sets CurrentPath.
AfterChangePath;
EnableWatcher(True);
{$IFDEF DEBUG_HISTORY}
FHistory.DebugShow;
{$ENDIF}
@ -966,6 +974,11 @@ begin
end;
end;
procedure TFileView.UpdateView;
begin
EnableWatcher(IsFileSystemWatcher);
end;
function TFileView.BeforeChangePath(NewFileSource: IFileSource; NewPath: String): Boolean;
begin
if NewPath <> '' then
@ -1055,6 +1068,7 @@ begin
begin
if Assigned(FileSource) and IsNewFileSource then
FileSource.RemoveReloadEventListener(@ReloadEvent);
EnableWatcher(False);
FHistory.Add(aFileSource, aPath);
@ -1066,6 +1080,7 @@ begin
end;
AfterChangePath;
EnableWatcher(True);
{$IFDEF DEBUG_HISTORY}
FHistory.DebugShow;
@ -1094,6 +1109,7 @@ begin
begin
if IsNewFileSource then
FileSource.RemoveReloadEventListener(@ReloadEvent);
EnableWatcher(False);
FHistory.DeleteFromCurrentFileSource;
@ -1105,6 +1121,7 @@ begin
end;
AfterChangePath;
EnableWatcher(True);
{$IFDEF DEBUG_HISTORY}
FHistory.DebugShow;
@ -1117,27 +1134,27 @@ procedure TFileView.RemoveAllFileSources;
begin
if FileSourcesCount > 0 then
begin
if BeforeChangePath(nil, '') then
begin
FileSource.RemoveReloadEventListener(@ReloadEvent);
FHistory.Clear;
FileSource.RemoveReloadEventListener(@ReloadEvent);
EnableWatcher(False);
FHistory.Clear;
AfterChangePath;
AfterChangePath;
{$IFDEF DEBUG_HISTORY}
FHistory.DebugShow;
{$ENDIF}
end;
{$IFDEF DEBUG_HISTORY}
FHistory.DebugShow;
{$ENDIF}
end;
end;
procedure TFileView.AssignFileSources(const otherFileView: TFileView);
begin
FileSource.RemoveReloadEventListener(@ReloadEvent);
EnableWatcher(False);
FHistory.Assign(otherFileView.FHistory);
Reload;
UpdateView;
FileSource.AddReloadEventListener(@ReloadEvent);
EnableWatcher(True);
end;
function TFileView.GetCurrentFileSource: IFileSource;
@ -1187,6 +1204,11 @@ begin
end;
end;
function TFileView.GetWatcherActive: Boolean;
begin
Result := FWatchPath <> EmptyStr;
end;
procedure TFileView.SetFileFilter(const NewFilter: String);
begin
FFileFilter := NewFilter;
@ -1217,6 +1239,51 @@ begin
DoOnReload;
end;
procedure TFileView.EnableWatcher(Enable: Boolean);
var
sDrive, sWatchDirsExclude: String;
WatchFilter: TFSWatchFilter;
begin
if Enable then
begin
if WatcherActive then
EnableWatcher(False);
if IsFileSystemWatcher and
Assigned(FileSource) and
FileSource.IsClass(TFileSystemFileSource) then
begin
// If current path is in exclude list then exit.
if (watch_exclude_dirs in gWatchDirs) and (gWatchDirsExclude <> '') then
begin
sWatchDirsExclude := gWatchDirsExclude;
repeat
sDrive := Copy2SymbDel(sWatchDirsExclude, ';');
if IsInPath(UTF8UpperCase(sDrive), UTF8UpperCase(CurrentPath), True) then
Exit;
until sWatchDirsExclude = '';
end;
WatchFilter := [];
if watch_file_name_change in gWatchDirs then
Include(WatchFilter, wfFileNameChange);
if watch_attributes_change in gWatchDirs then
Include(WatchFilter, wfAttributesChange);
if WatchFilter <> [] then
begin
FWatchPath := CurrentPath;
TFileSystemWatcher.AddWatch(FWatchPath, WatchFilter, @WatcherEvent);
end;
end;
end
else
begin
TFileSystemWatcher.RemoveWatch(FWatchPath, @WatcherEvent);
FWatchPath := EmptyStr;
end;
end;
procedure TFileView.ReloadEvent(const aFileSource: IFileSource; const ReloadedPaths: TPathsArray);
begin
// Reload file view, but only if the file source is currently viewed.
@ -1224,6 +1291,19 @@ begin
Reload(ReloadedPaths);
end;
procedure TFileView.WatcherEvent(const WatchPath: String; NotifyData, UserData: Pointer);
var
Paths: TPathsArray;
begin
// if not active and refresh only in foreground then exit
if (watch_only_foreground in gWatchDirs) and (not Application.Active) then
Exit;
SetLength(Paths, 1);
Paths[0] := WatchPath;
Reload(Paths);
end;
procedure TFileView.GoToHistoryIndex(aFileSourceIndex, aPathIndex: Integer);
var
IsNewFileSource: Boolean;
@ -1235,6 +1315,7 @@ begin
begin
if Assigned(FileSource) and IsNewFileSource then
FileSource.RemoveReloadEventListener(@ReloadEvent);
EnableWatcher(False);
FHistory.SetIndexes(aFileSourceIndex, aPathIndex);
@ -1246,6 +1327,7 @@ begin
end;
AfterChangePath;
EnableWatcher(True);
{$IFDEF DEBUG_HISTORY}
FHistory.DebugShow;
@ -1362,4 +1444,4 @@ begin
end;
end.

View file

@ -72,7 +72,7 @@ uses
type
TFakeClass = class
public
procedure OnWatcherNotifyEvent(Sender: TObject; NotifyData: PtrInt);
procedure OnWatcherNotifyEvent(const WatchPath: String; NotifyData, UserData: Pointer);
procedure OnUDisksNotify(Reason: TUDisksMethod; const ObjectPath: UTF8String);
end;
{$ENDIF}
@ -110,7 +110,6 @@ var
FObservers: TDriveWatcherObserverList = nil;
InitializeCounter: Integer = 0;
{$IFDEF LINUX}
EtcDirWatcher: TFileSystemWatcher = nil;
FakeClass: TFakeClass = nil;
IsUDisksAvailable: Boolean = False;
{$ENDIF}
@ -202,9 +201,7 @@ begin
else
begin
DebugLn('Detecting devices through /etc/mtab.');
EtcDirWatcher:= TFileSystemWatcher.Create(nil, '/etc', [wfFileNameChange]);
EtcDirWatcher.OnWatcherNotifyEvent:= @FakeClass.OnWatcherNotifyEvent;
EtcDirWatcher.Active:= True;
TFileSystemWatcher.AddWatch('/etc', [wfFileNameChange], @FakeClass.OnWatcherNotifyEvent);
end;
{$ENDIF}
@ -233,8 +230,7 @@ begin
uUDisks.Finalize;
IsUDisksAvailable := False;
end;
if Assigned(EtcDirWatcher) then
FreeAndNil(EtcDirWatcher);
TFileSystemWatcher.RemoveWatch('/etc', @FakeClass.OnWatcherNotifyEvent);
if Assigned(FakeClass) then
FreeAndNil(FakeClass);
{$ENDIF}
@ -933,7 +929,7 @@ end;
{$ENDIF}
{$IFDEF LINUX}
procedure TFakeClass.OnWatcherNotifyEvent(Sender: TObject; NotifyData: PtrInt);
procedure TFakeClass.OnWatcherNotifyEvent(const WatchPath: String; NotifyData, UserData: Pointer);
var
ev: pinotify_event absolute NotifyData;
ADrive: PDrive = nil;

File diff suppressed because it is too large Load diff

View file

@ -296,6 +296,8 @@ function InitPropStorage(Owner: TComponent): TIniPropStorageEx;
procedure FontToFontOptions(Font: TFont; out Options: TDCFontOptions);
procedure FontOptionsToFont(Options: TDCFontOptions; Font: TFont);
function IsFileSystemWatcher: Boolean;
const
cMaxStringItems=50;
@ -1878,8 +1880,13 @@ begin
Result := True;
end;
function IsFileSystemWatcher: Boolean;
begin
Result := ([watch_file_name_change, watch_attributes_change] * gWatchDirs <> []);
end;
initialization
finalization
DestroyGlobs;
end.
end.