]> git.saurik.com Git - wxWidgets.git/blob - include/wx/msw/private/fswatcher.h
Rebake from clean wx tree.
[wxWidgets.git] / include / wx / msw / private / fswatcher.h
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: wx/msw/private/fswatcher.h
3 // Purpose: File system watcher impl classes
4 // Author: Bartosz Bekier
5 // Created: 2009-05-26
6 // RCS-ID: $Id$
7 // Copyright: (c) 2009 Bartosz Bekier <bartosz.bekier@gmail.com>
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 #ifndef WX_MSW_PRIVATE_FSWATCHER_H_
12 #define WX_MSW_PRIVATE_FSWATCHER_H_
13
14 #include "wx/filename.h"
15 #include "wx/vector.h"
16 #include "wx/msw/private.h"
17
18 // ============================================================================
19 // wxFSWatcherEntry implementation & helper declarations
20 // ============================================================================
21
22 class wxFSWatcherImplMSW;
23
24 class wxFSWatchEntryMSW : public wxFSWatchInfo
25 {
26 public:
27 enum
28 {
29 BUFFER_SIZE = 4096 // TODO parametrize
30 };
31
32 wxFSWatchEntryMSW(const wxFSWatchInfo& winfo) :
33 wxFSWatchInfo(winfo)
34 {
35 // get handle for this path
36 m_handle = OpenDir(m_path);
37 m_overlapped = (OVERLAPPED*)calloc(1, sizeof(OVERLAPPED));
38 wxZeroMemory(m_buffer);
39 }
40
41 virtual ~wxFSWatchEntryMSW()
42 {
43 wxLogTrace(wxTRACE_FSWATCHER, "Deleting entry '%s'", m_path);
44
45 if (m_handle != INVALID_HANDLE_VALUE)
46 {
47 if (!CloseHandle(m_handle))
48 {
49 wxLogSysError(_("Unable to close the handle for '%s'"),
50 m_path);
51 }
52 }
53 delete m_overlapped;
54 }
55
56 bool IsOk() const
57 {
58 return m_handle != INVALID_HANDLE_VALUE;
59 }
60
61 HANDLE GetHandle() const
62 {
63 return m_handle;
64 }
65
66 void* GetBuffer()
67 {
68 return m_buffer;
69 }
70
71 OVERLAPPED* GetOverlapped() const
72 {
73 return m_overlapped;
74 }
75
76 private:
77 // opens dir with all flags, attributes etc. necessary to be later
78 // asynchronous watched with ReadDirectoryChangesW
79 static HANDLE OpenDir(const wxString& path)
80 {
81 HANDLE handle = CreateFile(path.t_str(),
82 FILE_LIST_DIRECTORY,
83 FILE_SHARE_READ |
84 FILE_SHARE_WRITE |
85 FILE_SHARE_DELETE,
86 NULL,
87 OPEN_EXISTING,
88 FILE_FLAG_BACKUP_SEMANTICS |
89 FILE_FLAG_OVERLAPPED,
90 NULL);
91 if (handle == INVALID_HANDLE_VALUE)
92 {
93 wxLogSysError(_("Failed to open directory \"%s\" for monitoring."),
94 path);
95 }
96
97 return handle;
98 }
99
100 HANDLE m_handle; // handle to opened directory
101 char m_buffer[BUFFER_SIZE]; // buffer for fs events
102 OVERLAPPED* m_overlapped;
103
104 wxDECLARE_NO_COPY_CLASS(wxFSWatchEntryMSW);
105 };
106
107
108 // ============================================================================
109 // wxFSWatcherImplMSW helper classes implementations
110 // ============================================================================
111
112 class wxIOCPService
113 {
114 public:
115 wxIOCPService() :
116 m_iocp(INVALID_HANDLE_VALUE)
117 {
118 Init();
119 }
120
121 ~wxIOCPService()
122 {
123 if (m_iocp != INVALID_HANDLE_VALUE)
124 {
125 if (!CloseHandle(m_iocp))
126 {
127 wxLogSysError(_("Unable to close I/O completion port handle"));
128 }
129 }
130 m_watches.clear();
131 }
132
133 // associates a wxFSWatchEntryMSW with completion port
134 bool Add(wxSharedPtr<wxFSWatchEntryMSW> watch)
135 {
136 wxCHECK_MSG( m_iocp != INVALID_HANDLE_VALUE, false, "IOCP not init" );
137 wxCHECK_MSG( watch->IsOk(), false, "Invalid watch" );
138
139 // associate with IOCP
140 HANDLE ret = CreateIoCompletionPort(watch->GetHandle(), m_iocp,
141 (ULONG_PTR)watch.get(), 0);
142 if (ret == NULL)
143 {
144 wxLogSysError(_("Unable to associate handle with "
145 "I/O completion port"));
146 return false;
147 }
148 else if (ret != m_iocp)
149 {
150 wxFAIL_MSG(_("Unexpectedly new I/O completion port was created"));
151 return false;
152 }
153
154 // add to watch map
155 wxFSWatchEntries::value_type val(watch->GetPath(), watch);
156 return m_watches.insert(val).second;
157 }
158
159 // post completion packet
160 bool PostEmptyStatus()
161 {
162 wxCHECK_MSG( m_iocp != INVALID_HANDLE_VALUE, false, "IOCP not init" );
163
164 int ret = PostQueuedCompletionStatus(m_iocp, 0, 0, NULL);
165 if (!ret)
166 {
167 wxLogSysError(_("Unable to post completion status"));
168 }
169
170 return ret != 0;
171 }
172
173 // Wait for completion status to arrive.
174 // This function can block forever in it's wait for completion status.
175 // Use PostEmptyStatus() to wake it up (and end the worker thread)
176 bool GetStatus(unsigned long* count, wxFSWatchEntryMSW** watch,
177 OVERLAPPED** overlapped)
178 {
179 wxCHECK_MSG( m_iocp != INVALID_HANDLE_VALUE, false, "IOCP not init" );
180 wxCHECK_MSG( count != NULL, false, "Null out parameter 'count'");
181 wxCHECK_MSG( watch != NULL, false, "Null out parameter 'watch'");
182 wxCHECK_MSG( overlapped != NULL, false,
183 "Null out parameter 'overlapped'");
184
185 int ret = GetQueuedCompletionStatus(m_iocp, count, (ULONG_PTR *)watch,
186 overlapped, INFINITE);
187 if (!ret)
188 {
189 wxLogSysError(_("Unable to dequeue completion packet"));
190 }
191 return ret != 0;
192 }
193
194 protected:
195 bool Init()
196 {
197 m_iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
198 if (m_iocp == NULL)
199 {
200 wxLogSysError(_("Unable to create I/O completion port"));
201 }
202 return m_iocp != NULL;
203 }
204
205 HANDLE m_iocp;
206 wxFSWatchEntries m_watches;
207 };
208
209
210 class wxIOCPThread : public wxThread
211 {
212 public:
213 wxIOCPThread(wxFSWatcherImplMSW* service, wxIOCPService* iocp);
214
215 // finishes this thread
216 bool Finish();
217
218 protected:
219 // structure to hold information needed to process one native event
220 // this is just a dummy holder, so it doesn't take ownership of it's data
221 struct wxEventProcessingData
222 {
223 wxEventProcessingData(const FILE_NOTIFY_INFORMATION* ne,
224 const wxFSWatchEntryMSW* watch) :
225 nativeEvent(ne), watch(watch)
226 {}
227
228 const FILE_NOTIFY_INFORMATION* nativeEvent;
229 const wxFSWatchEntryMSW* watch;
230 };
231
232 virtual ExitCode Entry();
233
234 // wait for events to occur, read them and send to interested parties
235 // returns false it empty status was read, which means we whould exit
236 // true otherwise
237 bool ReadEvents();
238
239 void ProcessNativeEvents(wxVector<wxEventProcessingData>& events);
240
241 void SendEvent(wxFileSystemWatcherEvent& evt);
242
243 static int Native2WatcherFlags(int flags);
244
245 static wxString FileNotifyInformationToString(
246 const FILE_NOTIFY_INFORMATION& e);
247
248 static wxFileName GetEventPath(const wxFSWatchEntryMSW& watch,
249 const FILE_NOTIFY_INFORMATION& e);
250
251 wxFSWatcherImplMSW* m_service;
252 wxIOCPService* m_iocp;
253 };
254
255 #endif /* WX_MSW_PRIVATE_FSWATCHER_H_ */