diff --git a/Makefile.in b/Makefile.in index 2483738bb3..de571fc4db 100644 --- a/Makefile.in +++ b/Makefile.in @@ -2253,7 +2253,8 @@ COND_TOOLKIT_OSX_CARBON_BASE_OSX_SRC = \ src/unix/threadpsx.cpp \ src/unix/utilsunx.cpp \ src/unix/wakeuppipe.cpp \ - src/unix/fswatcher_kqueue.cpp + src/unix/fswatcher_kqueue.cpp \ + src/osx/fswatcher_fsevents.cpp @COND_TOOLKIT_OSX_CARBON@BASE_OSX_SRC = $(COND_TOOLKIT_OSX_CARBON_BASE_OSX_SRC) COND_TOOLKIT_OSX_COCOA_BASE_OSX_SRC = \ src/osx/core/mimetype.cpp \ @@ -2277,7 +2278,8 @@ COND_TOOLKIT_OSX_COCOA_BASE_OSX_SRC = \ src/unix/threadpsx.cpp \ src/unix/utilsunx.cpp \ src/unix/wakeuppipe.cpp \ - src/unix/fswatcher_kqueue.cpp + src/unix/fswatcher_kqueue.cpp \ + src/osx/fswatcher_fsevents.cpp @COND_TOOLKIT_OSX_COCOA@BASE_OSX_SRC = $(COND_TOOLKIT_OSX_COCOA_BASE_OSX_SRC) COND_TOOLKIT_OSX_IPHONE_BASE_OSX_SRC = \ src/osx/core/mimetype.cpp \ @@ -2301,7 +2303,8 @@ COND_TOOLKIT_OSX_IPHONE_BASE_OSX_SRC = \ src/unix/threadpsx.cpp \ src/unix/utilsunx.cpp \ src/unix/wakeuppipe.cpp \ - src/unix/fswatcher_kqueue.cpp + src/unix/fswatcher_kqueue.cpp \ + src/osx/fswatcher_fsevents.cpp @COND_TOOLKIT_OSX_IPHONE@BASE_OSX_SRC = $(COND_TOOLKIT_OSX_IPHONE_BASE_OSX_SRC) COND_TOOLKIT_COCOA_BASE_OSX_SRC = \ src/common/fdiodispatcher.cpp \ @@ -2442,7 +2445,8 @@ COND_TOOLKIT_OSX_CARBON_BASE_OSX_HDR = \ wx/unix/stdpaths.h \ wx/unix/stackwalk.h \ wx/unix/tls.h \ - wx/unix/fswatcher_kqueue.h + wx/unix/fswatcher_kqueue.h \ + wx/osx/fswatcher_fsevents.h @COND_TOOLKIT_OSX_CARBON@BASE_OSX_HDR = $(COND_TOOLKIT_OSX_CARBON_BASE_OSX_HDR) COND_TOOLKIT_OSX_COCOA_BASE_OSX_HDR = \ wx/osx/core/cfdataref.h \ @@ -2463,7 +2467,8 @@ COND_TOOLKIT_OSX_COCOA_BASE_OSX_HDR = \ wx/unix/stdpaths.h \ wx/unix/stackwalk.h \ wx/unix/tls.h \ - wx/unix/fswatcher_kqueue.h + wx/unix/fswatcher_kqueue.h \ + wx/osx/fswatcher_fsevents.h @COND_TOOLKIT_OSX_COCOA@BASE_OSX_HDR = $(COND_TOOLKIT_OSX_COCOA_BASE_OSX_HDR) COND_TOOLKIT_COCOA_BASE_OSX_HDR = \ wx/unix/app.h \ @@ -4513,7 +4518,8 @@ COND_PLATFORM_MACOSX_1___BASE_PLATFORM_SRC_OBJECTS = \ monodll_threadpsx.o \ monodll_utilsunx.o \ monodll_wakeuppipe.o \ - monodll_fswatcher_kqueue.o + monodll_fswatcher_kqueue.o \ + monodll_fswatcher_fsevents.o @COND_PLATFORM_MACOSX_1@__BASE_PLATFORM_SRC_OBJECTS = $(COND_PLATFORM_MACOSX_1___BASE_PLATFORM_SRC_OBJECTS) @COND_PLATFORM_MSDOS_1@__BASE_PLATFORM_SRC_OBJECTS = \ @COND_PLATFORM_MSDOS_1@ monodll_msdos_dir.o monodll_msdos_mimetype.o \ @@ -6888,7 +6894,8 @@ COND_PLATFORM_MACOSX_1___BASE_PLATFORM_SRC_OBJECTS_1 = \ monolib_threadpsx.o \ monolib_utilsunx.o \ monolib_wakeuppipe.o \ - monolib_fswatcher_kqueue.o + monolib_fswatcher_kqueue.o \ + monolib_fswatcher_fsevents.o @COND_PLATFORM_MACOSX_1@__BASE_PLATFORM_SRC_OBJECTS_1 = $(COND_PLATFORM_MACOSX_1___BASE_PLATFORM_SRC_OBJECTS_1) @COND_PLATFORM_MSDOS_1@__BASE_PLATFORM_SRC_OBJECTS_1 \ @COND_PLATFORM_MSDOS_1@ = monolib_msdos_dir.o monolib_msdos_mimetype.o \ @@ -9316,7 +9323,8 @@ COND_PLATFORM_MACOSX_1___BASE_PLATFORM_SRC_OBJECTS_2 = \ basedll_threadpsx.o \ basedll_utilsunx.o \ basedll_wakeuppipe.o \ - basedll_fswatcher_kqueue.o + basedll_fswatcher_kqueue.o \ + basedll_fswatcher_fsevents.o @COND_PLATFORM_MACOSX_1@__BASE_PLATFORM_SRC_OBJECTS_2 = $(COND_PLATFORM_MACOSX_1___BASE_PLATFORM_SRC_OBJECTS_2) @COND_PLATFORM_MSDOS_1@__BASE_PLATFORM_SRC_OBJECTS_2 \ @COND_PLATFORM_MSDOS_1@ = basedll_msdos_dir.o basedll_msdos_mimetype.o \ @@ -9404,7 +9412,8 @@ COND_PLATFORM_MACOSX_1___BASE_PLATFORM_SRC_OBJECTS_3 = \ baselib_threadpsx.o \ baselib_utilsunx.o \ baselib_wakeuppipe.o \ - baselib_fswatcher_kqueue.o + baselib_fswatcher_kqueue.o \ + baselib_fswatcher_fsevents.o @COND_PLATFORM_MACOSX_1@__BASE_PLATFORM_SRC_OBJECTS_3 = $(COND_PLATFORM_MACOSX_1___BASE_PLATFORM_SRC_OBJECTS_3) @COND_PLATFORM_MSDOS_1@__BASE_PLATFORM_SRC_OBJECTS_3 \ @COND_PLATFORM_MSDOS_1@ = baselib_msdos_dir.o baselib_msdos_mimetype.o \ @@ -17740,6 +17749,9 @@ monodll_strconv_cf.o: $(srcdir)/src/osx/core/strconv_cf.cpp $(MONODLL_ODEP) monodll_utilsexc_base.o: $(srcdir)/src/osx/core/utilsexc_base.cpp $(MONODLL_ODEP) $(CXXC) -c -o $@ $(MONODLL_CXXFLAGS) $(srcdir)/src/osx/core/utilsexc_base.cpp +monodll_fswatcher_fsevents.o: $(srcdir)/src/osx/fswatcher_fsevents.cpp $(MONODLL_ODEP) + $(CXXC) -c -o $@ $(MONODLL_CXXFLAGS) $(srcdir)/src/osx/fswatcher_fsevents.cpp + monodll_msdos_dir.o: $(srcdir)/src/msdos/dir.cpp $(MONODLL_ODEP) $(CXXC) -c -o $@ $(MONODLL_CXXFLAGS) $(srcdir)/src/msdos/dir.cpp @@ -23662,6 +23674,9 @@ monolib_strconv_cf.o: $(srcdir)/src/osx/core/strconv_cf.cpp $(MONOLIB_ODEP) monolib_utilsexc_base.o: $(srcdir)/src/osx/core/utilsexc_base.cpp $(MONOLIB_ODEP) $(CXXC) -c -o $@ $(MONOLIB_CXXFLAGS) $(srcdir)/src/osx/core/utilsexc_base.cpp +monolib_fswatcher_fsevents.o: $(srcdir)/src/osx/fswatcher_fsevents.cpp $(MONOLIB_ODEP) + $(CXXC) -c -o $@ $(MONOLIB_CXXFLAGS) $(srcdir)/src/osx/fswatcher_fsevents.cpp + monolib_msdos_dir.o: $(srcdir)/src/msdos/dir.cpp $(MONOLIB_ODEP) $(CXXC) -c -o $@ $(MONOLIB_CXXFLAGS) $(srcdir)/src/msdos/dir.cpp @@ -29584,6 +29599,9 @@ basedll_strconv_cf.o: $(srcdir)/src/osx/core/strconv_cf.cpp $(BASEDLL_ODEP) basedll_utilsexc_base.o: $(srcdir)/src/osx/core/utilsexc_base.cpp $(BASEDLL_ODEP) $(CXXC) -c -o $@ $(BASEDLL_CXXFLAGS) $(srcdir)/src/osx/core/utilsexc_base.cpp +basedll_fswatcher_fsevents.o: $(srcdir)/src/osx/fswatcher_fsevents.cpp $(BASEDLL_ODEP) + $(CXXC) -c -o $@ $(BASEDLL_CXXFLAGS) $(srcdir)/src/osx/fswatcher_fsevents.cpp + basedll_msdos_dir.o: $(srcdir)/src/msdos/dir.cpp $(BASEDLL_ODEP) $(CXXC) -c -o $@ $(BASEDLL_CXXFLAGS) $(srcdir)/src/msdos/dir.cpp @@ -30064,6 +30082,9 @@ baselib_strconv_cf.o: $(srcdir)/src/osx/core/strconv_cf.cpp $(BASELIB_ODEP) baselib_utilsexc_base.o: $(srcdir)/src/osx/core/utilsexc_base.cpp $(BASELIB_ODEP) $(CXXC) -c -o $@ $(BASELIB_CXXFLAGS) $(srcdir)/src/osx/core/utilsexc_base.cpp +baselib_fswatcher_fsevents.o: $(srcdir)/src/osx/fswatcher_fsevents.cpp $(BASELIB_ODEP) + $(CXXC) -c -o $@ $(BASELIB_CXXFLAGS) $(srcdir)/src/osx/fswatcher_fsevents.cpp + baselib_msdos_dir.o: $(srcdir)/src/msdos/dir.cpp $(BASELIB_ODEP) $(CXXC) -c -o $@ $(BASELIB_CXXFLAGS) $(srcdir)/src/msdos/dir.cpp diff --git a/build/bakefiles/files.bkl b/build/bakefiles/files.bkl index ae2b56d178..4526caee00 100644 --- a/build/bakefiles/files.bkl +++ b/build/bakefiles/files.bkl @@ -210,10 +210,12 @@ IMPORTANT: please read docs/tech/tn0016.txt before modifying this file! src/osx/core/mimetype.cpp $(BASE_COREFOUNDATION_SRC) $(BASE_UNIX_AND_DARWIN_SRC) + src/osx/fswatcher_fsevents.cpp $(BASE_COREFOUNDATION_HDR) $(BASE_UNIX_AND_DARWIN_HDR) + wx/osx/fswatcher_fsevents.h diff --git a/build/files b/build/files index cb3c4505a2..d582b8ef3e 100644 --- a/build/files +++ b/build/files @@ -151,10 +151,12 @@ BASE_COREFOUNDATION_HDR = # Base files used by OS X ports (not Carbon) BASE_OSX_SHARED_SRC = src/osx/core/mimetype.cpp + src/osx/fswatcher_fsevents.cpp $(BASE_COREFOUNDATION_SRC) $(BASE_UNIX_AND_DARWIN_SRC) BASE_OSX_SHARED_HDR = + wx/osx/fswatcher_fsevents.h $(BASE_COREFOUNDATION_HDR) $(BASE_UNIX_AND_DARWIN_HDR) diff --git a/build/osx/wxcarbon.xcodeproj/project.pbxproj b/build/osx/wxcarbon.xcodeproj/project.pbxproj index dd26a40844..c60de66008 100644 --- a/build/osx/wxcarbon.xcodeproj/project.pbxproj +++ b/build/osx/wxcarbon.xcodeproj/project.pbxproj @@ -877,6 +877,8 @@ 8292D346BFC33D6E8D3CDDC0 /* xh_sttxt.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B3F1680BBE8331A7B745638C /* xh_sttxt.cpp */; }; 82FA4AA043213728AC266700 /* wizard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8F08F70E1EF239999A4D2AC4 /* wizard.cpp */; }; 82FA4AA043213728AC266701 /* wizard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8F08F70E1EF239999A4D2AC4 /* wizard.cpp */; }; + 830A61EA04FD367C9EB6A757 /* fswatcher_fsevents.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5B83407D156C3CC3A66F05A4 /* fswatcher_fsevents.cpp */; }; + 830A61EA04FD367C9EB6A758 /* fswatcher_fsevents.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5B83407D156C3CC3A66F05A4 /* fswatcher_fsevents.cpp */; }; 834F2ADD0520313FBAC4F927 /* LexCsound.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 0A59A5C2305D3D1C8049BE71 /* LexCsound.cxx */; }; 834F2ADD0520313FBAC4F928 /* LexCsound.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 0A59A5C2305D3D1C8049BE71 /* LexCsound.cxx */; }; 83616D33080E3F0F9FA5FBB4 /* xh_html.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 889FFA9573A835F280A21CB4 /* xh_html.cpp */; }; @@ -2023,6 +2025,7 @@ 5AACC1EC2E2A33B3ABF5EDCA /* xh_radbt.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = xh_radbt.cpp; path = ../../src/xrc/xh_radbt.cpp; sourceTree = ""; }; 5AFB85719CBC3D60BA2EDC2E /* CharClassify.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = CharClassify.cxx; path = ../../src/stc/scintilla/src/CharClassify.cxx; sourceTree = ""; }; 5B32A13D5B3336098B1B9765 /* pngtrans.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = pngtrans.c; path = ../../src/png/pngtrans.c; sourceTree = ""; }; + 5B83407D156C3CC3A66F05A4 /* fswatcher_fsevents.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = fswatcher_fsevents.cpp; path = ../../src/osx/fswatcher_fsevents.cpp; sourceTree = ""; }; 5B9586328A1F3C4BA0390AA5 /* time.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = time.cpp; path = ../../src/common/time.cpp; sourceTree = ""; }; 5BD6231188AB329CAA5E1171 /* evtloop_cf.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = evtloop_cf.cpp; path = ../../src/osx/core/evtloop_cf.cpp; sourceTree = ""; }; 5BEC6B3CAFB532CBB9F95D74 /* jutils.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = jutils.c; path = ../../src/jpeg/jutils.c; sourceTree = ""; }; @@ -3555,6 +3558,7 @@ DC75C7251C1732B0B07C7BD3 /* utilsunx.cpp */, B38F3D4DC6D139BA93401F7A /* wakeuppipe.cpp */, C019CE87CF9931B0B77C0823 /* fswatcher_kqueue.cpp */, + 5B83407D156C3CC3A66F05A4 /* fswatcher_fsevents.cpp */, 7A34C5BBBA543DC0A50DE1B6 /* event.cpp */, C9A305CEC03B3085B159B617 /* fs_mem.cpp */, E968913A9A593B258BD8EACB /* msgout.cpp */, @@ -3908,6 +3912,7 @@ B5C7FD8C27F43F3289A77FCA /* utilsunx.cpp in Sources */, F9C5EAC42CCF3267B4100BAF /* wakeuppipe.cpp in Sources */, FF7DB2884F6E3C5DB4BDF61E /* fswatcher_kqueue.cpp in Sources */, + 830A61EA04FD367C9EB6A758 /* fswatcher_fsevents.cpp in Sources */, 55D893FDD00633FEA82ABD82 /* event.cpp in Sources */, 131B879180AE3FB481F81CC8 /* fs_mem.cpp in Sources */, 05814571E7A83F5DBFB6E4C5 /* msgout.cpp in Sources */, @@ -4776,6 +4781,7 @@ B5C7FD8C27F43F3289A77FC9 /* utilsunx.cpp in Sources */, F9C5EAC42CCF3267B4100BAE /* wakeuppipe.cpp in Sources */, FF7DB2884F6E3C5DB4BDF61D /* fswatcher_kqueue.cpp in Sources */, + 830A61EA04FD367C9EB6A757 /* fswatcher_fsevents.cpp in Sources */, 55D893FDD00633FEA82ABD81 /* event.cpp in Sources */, 131B879180AE3FB481F81CC7 /* fs_mem.cpp in Sources */, 05814571E7A83F5DBFB6E4C4 /* msgout.cpp in Sources */, diff --git a/build/osx/wxcocoa.xcodeproj/project.pbxproj b/build/osx/wxcocoa.xcodeproj/project.pbxproj index 7ae569fdc8..2a22cfb63d 100644 --- a/build/osx/wxcocoa.xcodeproj/project.pbxproj +++ b/build/osx/wxcocoa.xcodeproj/project.pbxproj @@ -798,6 +798,10 @@ 3D762A0BBF1B39B88A769632 /* helpwnd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DBD5DB511C53218B3EF1625 /* helpwnd.cpp */; }; 3D762A0BBF1B39B88A769633 /* helpwnd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DBD5DB511C53218B3EF1625 /* helpwnd.cpp */; }; 3D762A0BBF1B39B88A769634 /* helpwnd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DBD5DB511C53218B3EF1625 /* helpwnd.cpp */; }; + 3DE2CD678CEB39C2B1E09ACA /* power.mm in Sources */ = {isa = PBXBuildFile; fileRef = 60DFD5962DE3379F801AF78F /* power.mm */; }; + 3DE2CD678CEB39C2B1E09ACB /* power.mm in Sources */ = {isa = PBXBuildFile; fileRef = 60DFD5962DE3379F801AF78F /* power.mm */; }; + 3DE2CD678CEB39C2B1E09ACC /* power.mm in Sources */ = {isa = PBXBuildFile; fileRef = 60DFD5962DE3379F801AF78F /* power.mm */; }; + 3DE2CD678CEB39C2B1E09ACD /* power.mm in Sources */ = {isa = PBXBuildFile; fileRef = 60DFD5962DE3379F801AF78F /* power.mm */; }; 3E6AA08E72A030D39D867D4B /* ScintillaWX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8E6F9D4319F639BE89E5A82F /* ScintillaWX.cpp */; }; 3E6AA08E72A030D39D867D4C /* ScintillaWX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8E6F9D4319F639BE89E5A82F /* ScintillaWX.cpp */; }; 3E6AA08E72A030D39D867D4D /* ScintillaWX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8E6F9D4319F639BE89E5A82F /* ScintillaWX.cpp */; }; @@ -1483,6 +1487,9 @@ 82FA4AA043213728AC266700 /* wizard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8F08F70E1EF239999A4D2AC4 /* wizard.cpp */; }; 82FA4AA043213728AC266701 /* wizard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8F08F70E1EF239999A4D2AC4 /* wizard.cpp */; }; 82FA4AA043213728AC266702 /* wizard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8F08F70E1EF239999A4D2AC4 /* wizard.cpp */; }; + 830A61EA04FD367C9EB6A757 /* fswatcher_fsevents.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5B83407D156C3CC3A66F05A4 /* fswatcher_fsevents.cpp */; }; + 830A61EA04FD367C9EB6A758 /* fswatcher_fsevents.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5B83407D156C3CC3A66F05A4 /* fswatcher_fsevents.cpp */; }; + 830A61EA04FD367C9EB6A759 /* fswatcher_fsevents.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5B83407D156C3CC3A66F05A4 /* fswatcher_fsevents.cpp */; }; 834F2ADD0520313FBAC4F927 /* LexCsound.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 0A59A5C2305D3D1C8049BE71 /* LexCsound.cxx */; }; 834F2ADD0520313FBAC4F928 /* LexCsound.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 0A59A5C2305D3D1C8049BE71 /* LexCsound.cxx */; }; 834F2ADD0520313FBAC4F929 /* LexCsound.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 0A59A5C2305D3D1C8049BE71 /* LexCsound.cxx */; }; @@ -1771,10 +1778,6 @@ A1A7B833061C35B4AABD093C /* preferencesg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D8F06DEA1AA339ED819B3812 /* preferencesg.cpp */; }; A1A7B833061C35B4AABD093D /* preferencesg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D8F06DEA1AA339ED819B3812 /* preferencesg.cpp */; }; A1A7B833061C35B4AABD093E /* preferencesg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D8F06DEA1AA339ED819B3812 /* preferencesg.cpp */; }; - A1A7C58E276F6F2B247F0813 /* power.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0714536835B5227019E29D06 /* power.mm */; }; - A1A7C58E276F6F2B247F0814 /* power.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0714536835B5227019E29D06 /* power.mm */; }; - A1A7C58E276F6F2B247F0815 /* power.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0714536835B5227019E29D06 /* power.mm */; }; - A1A7C58E276F6F2B247F0816 /* power.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0714536835B5227019E29D06 /* power.mm */; }; A1A7D793B034398B8696EF33 /* utils.mm in Sources */ = {isa = PBXBuildFile; fileRef = 789F45D14FF23E248FCFB5FA /* utils.mm */; }; A1A7D793B034398B8696EF34 /* utils.mm in Sources */ = {isa = PBXBuildFile; fileRef = 789F45D14FF23E248FCFB5FA /* utils.mm */; }; A1A7D793B034398B8696EF35 /* utils.mm in Sources */ = {isa = PBXBuildFile; fileRef = 789F45D14FF23E248FCFB5FA /* utils.mm */; }; @@ -4114,6 +4117,7 @@ 5AACC1EC2E2A33B3ABF5EDCA /* xh_radbt.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = xh_radbt.cpp; path = ../../src/xrc/xh_radbt.cpp; sourceTree = ""; }; 5AFB85719CBC3D60BA2EDC2E /* CharClassify.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = CharClassify.cxx; path = ../../src/stc/scintilla/src/CharClassify.cxx; sourceTree = ""; }; 5B32A13D5B3336098B1B9765 /* pngtrans.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = pngtrans.c; path = ../../src/png/pngtrans.c; sourceTree = ""; }; + 5B83407D156C3CC3A66F05A4 /* fswatcher_fsevents.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = fswatcher_fsevents.cpp; path = ../../src/osx/fswatcher_fsevents.cpp; sourceTree = ""; }; 5B9586328A1F3C4BA0390AA5 /* time.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = time.cpp; path = ../../src/common/time.cpp; sourceTree = ""; }; 5BD6231188AB329CAA5E1171 /* evtloop_cf.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = evtloop_cf.cpp; path = ../../src/osx/core/evtloop_cf.cpp; sourceTree = ""; }; 5BEC6B3CAFB532CBB9F95D74 /* jutils.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = jutils.c; path = ../../src/jpeg/jutils.c; sourceTree = ""; }; @@ -4145,6 +4149,7 @@ 600740717F7E320F8CA78384 /* scrolbar_osx.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = scrolbar_osx.cpp; path = ../../src/osx/scrolbar_osx.cpp; sourceTree = ""; }; 604D9B79D41F32339AEC0EA0 /* libwx_osx_cocoau_xrc.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libwx_osx_cocoau_xrc.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; 607EF0043E723B7B9BE101EA /* wxprintf.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = wxprintf.cpp; path = ../../src/common/wxprintf.cpp; sourceTree = ""; }; + 60DFD5962DE3379F801AF78F /* power.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = power.mm; path = ../../src/osx/cocoa/power.mm; sourceTree = ""; }; 60EE4448A28D38F5ADE17B5A /* xh_filectrl.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = xh_filectrl.cpp; path = ../../src/xrc/xh_filectrl.cpp; sourceTree = ""; }; 61548D0FE1353D7C846DD721 /* menuitem.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = menuitem.mm; path = ../../src/osx/cocoa/menuitem.mm; sourceTree = ""; }; 61658C3EABB4341AA38C691E /* m_pre.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = m_pre.cpp; path = ../../src/html/m_pre.cpp; sourceTree = ""; }; @@ -4218,7 +4223,6 @@ 777385D10CCC350C90F02824 /* textentry_osx.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = textentry_osx.cpp; path = ../../src/osx/textentry_osx.cpp; sourceTree = ""; }; 77D6E66F72443765A2FBE263 /* aboutdlgg.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = aboutdlgg.cpp; path = ../../src/generic/aboutdlgg.cpp; sourceTree = ""; }; 77F5E7BCD9B2307D8DBCC052 /* font.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = font.cpp; path = ../../src/osx/carbon/font.cpp; sourceTree = ""; }; - 0714536835B5227019E29D06 /* power.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = power.mm; path = ../../src/osx/cocoa/power.mm; sourceTree = ""; }; 789F45D14FF23E248FCFB5FA /* utils.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = utils.mm; path = ../../src/osx/cocoa/utils.mm; sourceTree = ""; }; 78D7866F95C73A28BB540606 /* LexBash.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = LexBash.cxx; path = ../../src/stc/scintilla/lexers/LexBash.cxx; sourceTree = ""; }; 7906BD74118A3B4DAC515BC2 /* odcombo.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = odcombo.cpp; path = ../../src/generic/odcombo.cpp; sourceTree = ""; }; @@ -5964,12 +5968,13 @@ DC75C7251C1732B0B07C7BD3 /* utilsunx.cpp */, B38F3D4DC6D139BA93401F7A /* wakeuppipe.cpp */, C019CE87CF9931B0B77C0823 /* fswatcher_kqueue.cpp */, + 5B83407D156C3CC3A66F05A4 /* fswatcher_fsevents.cpp */, 7A34C5BBBA543DC0A50DE1B6 /* event.cpp */, C9A305CEC03B3085B159B617 /* fs_mem.cpp */, E968913A9A593B258BD8EACB /* msgout.cpp */, 4188821BBA833CCAA678B234 /* utilscmn.cpp */, - 0714536835B5227019E29D06 /* power.mm */, 789F45D14FF23E248FCFB5FA /* utils.mm */, + 60DFD5962DE3379F801AF78F /* power.mm */, ); name = base; sourceTree = ""; @@ -7365,12 +7370,13 @@ B5C7FD8C27F43F3289A77FCB /* utilsunx.cpp in Sources */, F9C5EAC42CCF3267B4100BB0 /* wakeuppipe.cpp in Sources */, FF7DB2884F6E3C5DB4BDF61F /* fswatcher_kqueue.cpp in Sources */, + 830A61EA04FD367C9EB6A759 /* fswatcher_fsevents.cpp in Sources */, 55D893FDD00633FEA82ABD83 /* event.cpp in Sources */, 131B879180AE3FB481F81CC9 /* fs_mem.cpp in Sources */, 05814571E7A83F5DBFB6E4C6 /* msgout.cpp in Sources */, 80665EEAE8613DF8A93A7986 /* utilscmn.cpp in Sources */, - A1A7C58E276F6F2B247F0815 /* power.mm in Sources */, A1A7D793B034398B8696EF35 /* utils.mm in Sources */, + 3DE2CD678CEB39C2B1E09ACC /* power.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -7402,8 +7408,8 @@ 131B879180AE3FB481F81CCA /* fs_mem.cpp in Sources */, 05814571E7A83F5DBFB6E4C7 /* msgout.cpp in Sources */, 80665EEAE8613DF8A93A7987 /* utilscmn.cpp in Sources */, - A1A7C58E276F6F2B247F0816 /* power.mm in Sources */, A1A7D793B034398B8696EF36 /* utils.mm in Sources */, + 3DE2CD678CEB39C2B1E09ACD /* power.mm in Sources */, F4C0CEADEDC23610BF6983D8 /* artmac.cpp in Sources */, 296692A7A3783E3A83D005C8 /* brush.cpp in Sources */, 86AED49CEAFC3637B1F10539 /* dialog_osx.cpp in Sources */, @@ -8068,12 +8074,13 @@ B5C7FD8C27F43F3289A77FCA /* utilsunx.cpp in Sources */, F9C5EAC42CCF3267B4100BAF /* wakeuppipe.cpp in Sources */, FF7DB2884F6E3C5DB4BDF61E /* fswatcher_kqueue.cpp in Sources */, + 830A61EA04FD367C9EB6A758 /* fswatcher_fsevents.cpp in Sources */, 55D893FDD00633FEA82ABD82 /* event.cpp in Sources */, 131B879180AE3FB481F81CC8 /* fs_mem.cpp in Sources */, 05814571E7A83F5DBFB6E4C5 /* msgout.cpp in Sources */, 80665EEAE8613DF8A93A7985 /* utilscmn.cpp in Sources */, - A1A7C58E276F6F2B247F0814 /* power.mm in Sources */, A1A7D793B034398B8696EF34 /* utils.mm in Sources */, + 3DE2CD678CEB39C2B1E09ACB /* power.mm in Sources */, F4C0CEADEDC23610BF6983D7 /* artmac.cpp in Sources */, 296692A7A3783E3A83D005C7 /* brush.cpp in Sources */, 86AED49CEAFC3637B1F10538 /* dialog_osx.cpp in Sources */, @@ -9258,12 +9265,13 @@ B5C7FD8C27F43F3289A77FC9 /* utilsunx.cpp in Sources */, F9C5EAC42CCF3267B4100BAE /* wakeuppipe.cpp in Sources */, FF7DB2884F6E3C5DB4BDF61D /* fswatcher_kqueue.cpp in Sources */, + 830A61EA04FD367C9EB6A757 /* fswatcher_fsevents.cpp in Sources */, 55D893FDD00633FEA82ABD81 /* event.cpp in Sources */, 131B879180AE3FB481F81CC7 /* fs_mem.cpp in Sources */, 05814571E7A83F5DBFB6E4C4 /* msgout.cpp in Sources */, 80665EEAE8613DF8A93A7984 /* utilscmn.cpp in Sources */, - A1A7C58E276F6F2B247F0813 /* power.mm in Sources */, A1A7D793B034398B8696EF33 /* utils.mm in Sources */, + 3DE2CD678CEB39C2B1E09ACA /* power.mm in Sources */, F4C0CEADEDC23610BF6983D6 /* artmac.cpp in Sources */, 296692A7A3783E3A83D005C6 /* brush.cpp in Sources */, 86AED49CEAFC3637B1F10537 /* dialog_osx.cpp in Sources */, diff --git a/build/osx/wxiphone.xcodeproj/project.pbxproj b/build/osx/wxiphone.xcodeproj/project.pbxproj index 6b916c5a74..90662597d5 100644 --- a/build/osx/wxiphone.xcodeproj/project.pbxproj +++ b/build/osx/wxiphone.xcodeproj/project.pbxproj @@ -432,6 +432,7 @@ 825EAD51920B387DB4F8C426 /* LexAsn1.cxx in Sources */ = {isa = PBXBuildFile; fileRef = A46D50BEBF523B3F88831086 /* LexAsn1.cxx */; }; 8292D346BFC33D6E8D3CDDBF /* xh_sttxt.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B3F1680BBE8331A7B745638C /* xh_sttxt.cpp */; }; 82FA4AA043213728AC266700 /* wizard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8F08F70E1EF239999A4D2AC4 /* wizard.cpp */; }; + 830A61EA04FD367C9EB6A757 /* fswatcher_fsevents.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5B83407D156C3CC3A66F05A4 /* fswatcher_fsevents.cpp */; }; 834F2ADD0520313FBAC4F927 /* LexCsound.cxx in Sources */ = {isa = PBXBuildFile; fileRef = 0A59A5C2305D3D1C8049BE71 /* LexCsound.cxx */; }; 83616D33080E3F0F9FA5FBB4 /* xh_html.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 889FFA9573A835F280A21CB4 /* xh_html.cpp */; }; 83C492B87F4A3A97930F227A /* ExternalLexer.cxx in Sources */ = {isa = PBXBuildFile; fileRef = FD6D2664C05131C3A06E98B4 /* ExternalLexer.cxx */; }; @@ -1126,6 +1127,7 @@ 5AACC1EC2E2A33B3ABF5EDCA /* xh_radbt.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = xh_radbt.cpp; path = ../../src/xrc/xh_radbt.cpp; sourceTree = ""; }; 5AFB85719CBC3D60BA2EDC2E /* CharClassify.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = CharClassify.cxx; path = ../../src/stc/scintilla/src/CharClassify.cxx; sourceTree = ""; }; 5B32A13D5B3336098B1B9765 /* pngtrans.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = pngtrans.c; path = ../../src/png/pngtrans.c; sourceTree = ""; }; + 5B83407D156C3CC3A66F05A4 /* fswatcher_fsevents.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = fswatcher_fsevents.cpp; path = ../../src/osx/fswatcher_fsevents.cpp; sourceTree = ""; }; 5B9586328A1F3C4BA0390AA5 /* time.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = time.cpp; path = ../../src/common/time.cpp; sourceTree = ""; }; 5BD6231188AB329CAA5E1171 /* evtloop_cf.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = evtloop_cf.cpp; path = ../../src/osx/core/evtloop_cf.cpp; sourceTree = ""; }; 5BEC6B3CAFB532CBB9F95D74 /* jutils.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = jutils.c; path = ../../src/jpeg/jutils.c; sourceTree = ""; }; @@ -2619,6 +2621,7 @@ DC75C7251C1732B0B07C7BD3 /* utilsunx.cpp */, B38F3D4DC6D139BA93401F7A /* wakeuppipe.cpp */, C019CE87CF9931B0B77C0823 /* fswatcher_kqueue.cpp */, + 5B83407D156C3CC3A66F05A4 /* fswatcher_fsevents.cpp */, 7A34C5BBBA543DC0A50DE1B6 /* event.cpp */, C9A305CEC03B3085B159B617 /* fs_mem.cpp */, E968913A9A593B258BD8EACB /* msgout.cpp */, @@ -2930,6 +2933,7 @@ B5C7FD8C27F43F3289A77FC9 /* utilsunx.cpp in Sources */, F9C5EAC42CCF3267B4100BAE /* wakeuppipe.cpp in Sources */, FF7DB2884F6E3C5DB4BDF61D /* fswatcher_kqueue.cpp in Sources */, + 830A61EA04FD367C9EB6A757 /* fswatcher_fsevents.cpp in Sources */, 55D893FDD00633FEA82ABD81 /* event.cpp in Sources */, 131B879180AE3FB481F81CC7 /* fs_mem.cpp in Sources */, 05814571E7A83F5DBFB6E4C4 /* msgout.cpp in Sources */, diff --git a/docs/changes.txt b/docs/changes.txt index 690b17f601..d1182e9302 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -161,6 +161,7 @@ wxMSW: wxOSX/Cocoa: +- Use more efficient FSEvents (10.7+) in wxFileSystemWatcher (Roberto Perpuly). - Implement wxWindow::Disable() for non-native controls too (Steve Browne). - Fix wxEVT_CHAR for non-BMP Unicode characters (ARATA Mizuki). - Add support for wxEVT_COMBOBOX_DROPDOWN and wxEVT_COMBOBOX_CLOSEUP diff --git a/include/wx/fswatcher.h b/include/wx/fswatcher.h index 7f0fcbd72e..a29ff834e6 100644 --- a/include/wx/fswatcher.h +++ b/include/wx/fswatcher.h @@ -52,7 +52,7 @@ enum wxFSW_EVENT_RENAME | wxFSW_EVENT_MODIFY | wxFSW_EVENT_ACCESS | wxFSW_EVENT_ATTRIB | wxFSW_EVENT_WARNING | wxFSW_EVENT_ERROR -#ifdef wxHAS_INOTIFY +#if defined(wxHAS_INOTIFY) || defined(wxHAVE_FSEVENTS_FILE_NOTIFICATIONS) ,wxFSW_EVENT_UNMOUNT = 0x2000 #endif }; @@ -395,6 +395,10 @@ protected: #ifdef wxHAS_INOTIFY #include "wx/unix/fswatcher_inotify.h" #define wxFileSystemWatcher wxInotifyFileSystemWatcher +#elif defined(wxHAS_KQUEUE) && defined(wxHAVE_FSEVENTS_FILE_NOTIFICATIONS) + #include "wx/unix/fswatcher_kqueue.h" + #include "wx/osx/fswatcher_fsevents.h" + #define wxFileSystemWatcher wxFsEventsFileSystemWatcher #elif defined(wxHAS_KQUEUE) #include "wx/unix/fswatcher_kqueue.h" #define wxFileSystemWatcher wxKqueueFileSystemWatcher diff --git a/include/wx/osx/cocoa/chkconf.h b/include/wx/osx/cocoa/chkconf.h index 03fd501925..4cb5623b49 100644 --- a/include/wx/osx/cocoa/chkconf.h +++ b/include/wx/osx/cocoa/chkconf.h @@ -50,6 +50,16 @@ #define wxOSX_USE_QUICKTIME 0 #define wxOSX_USE_AUDIOTOOLBOX 1 +/* + Use the more efficient FSEvents API instead of kqueue + events for file system watcher, but only on OS X >= 10.7 since + that version introduced a flag that allows watching files as + well as sub directories. + */ +#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7 + #define wxHAVE_FSEVENTS_FILE_NOTIFICATIONS 1 +#endif + /* * turning off capabilities that don't work under cocoa yet */ diff --git a/include/wx/osx/fswatcher_fsevents.h b/include/wx/osx/fswatcher_fsevents.h new file mode 100644 index 0000000000..bbaa95d951 --- /dev/null +++ b/include/wx/osx/fswatcher_fsevents.h @@ -0,0 +1,88 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: wx/osx/fswatcher_fsevents.h +// Purpose: File System watcher that uses the FSEvents API +// of OS X to efficiently watch trees +// Author: Roberto Perpuly +// Created: 2015-04-24 +// Copyright: (c) 2015 Roberto Perpuly +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#ifndef _WX_FSWATCHER_FSEVENTS_H_ +#define _WX_FSWATCHER_FSEVENTS_H_ + +#include "wx/defs.h" + +#if wxUSE_FSWATCHER + +#include +#include "wx/unix/fswatcher_kqueue.h" + +WX_DECLARE_STRING_HASH_MAP(FSEventStreamRef, FSEventStreamRefMap); + +/* + The FSEvents watcher uses the newer FSEvents service + that is available in OS X, the service allows for + efficient watching of entire directory hierarchies. + Note that adding a single file watch (or directory + watch) still use kqueue events. + + We take care to only use this on OS X >= 10.7, as that + version introduced the ability to get file-level notifications. + + See the following docs that outline the FSEvents API + + https://developer.apple.com/library/mac/documentation/Darwin/Conceptual/FSEvents_ProgGuide/UsingtheFSEventsFramework/UsingtheFSEventsFramework.html + + https://developer.apple.com/library/mac/documentation/Darwin/Reference/FSEvents_Ref/index.html +*/ +class WXDLLIMPEXP_BASE wxFsEventsFileSystemWatcher : + public wxKqueueFileSystemWatcher +{ +public: + wxFsEventsFileSystemWatcher(); + + wxFsEventsFileSystemWatcher(const wxFileName& path, + int events = wxFSW_EVENT_ALL); + + ~wxFsEventsFileSystemWatcher(); + + // reimplement adding a tree so that it does not use + // kqueue at all + bool AddTree(const wxFileName& path, int events = wxFSW_EVENT_ALL, + const wxString& filespec = wxEmptyString) wxOVERRIDE; + + // reimplement removing a tree so that we + // cleanup the opened fs streams + bool RemoveTree(const wxFileName& path) wxOVERRIDE; + + // reimplement remove all so that we cleanup + // watches from kqeueue and from FSEvents + bool RemoveAll() wxOVERRIDE; + + // post an file change event to the owner + void PostChange(const wxFileName& oldFileName, + const wxFileName& newFileName, int event); + + // post a warning event to the owner + void PostWarning(wxFSWWarningType warning, const wxString& msg); + + // post an error event to the owner + void PostError(const wxString& msg); + + // reimplement count to include the FS stream watches + int GetWatchedPathsCount() const; + + // reimplement to include paths from FS stream watches + int GetWatchedPaths(wxArrayString* paths) const; + +private: + + // map of path => FSEventStreamRef + FSEventStreamRefMap m_streams; + +}; + +#endif /* wxUSE_FSWATCHER */ + +#endif /* _WX_FSWATCHER_FSEVENTS_H_ */ diff --git a/interface/wx/fswatcher.h b/interface/wx/fswatcher.h index cd4cd29a6f..40078e7e46 100644 --- a/interface/wx/fswatcher.h +++ b/interface/wx/fswatcher.h @@ -78,10 +78,11 @@ public: Additionally a file mask can be specified to include only files matching that particular mask. - This method is implemented efficiently on MSW, but should be used with - care on other platforms for directories with lots of children (e.g. the - root directory) as it calls Add() for each subdirectory, potentially - creating a lot of watches and taking a long time to execute. + This method is implemented efficiently on MSW and OS X >= 10.7, but + should be used with care on other platforms for directories with lots + of children (e.g. the root directory) as it calls Add() for each + subdirectory, potentially creating a lot of watches and taking a long + time to execute. Note that on platforms that use symbolic links, you will probably want to have called wxFileName::DontFollowLink on @a path. This is especially @@ -242,8 +243,9 @@ enum wxFSWFlags Notice that under MSW this event is sometimes -- although not always -- followed by a ::wxFSW_EVENT_MODIFY for the new file. - Under OS X this event is currently not detected and instead separate - ::wxFSW_EVENT_CREATE and ::wxFSW_EVENT_DELETE events are. + Under OS X this event is only detected when watching entire trees. When + watching directories, separate ::wxFSW_EVENT_CREATE and + ::wxFSW_EVENT_DELETE events are detected instead. */ wxFSW_EVENT_RENAME = 0x04, @@ -253,7 +255,7 @@ enum wxFSWFlags Depending on the program doing the file modification, multiple such events can be reported for a single logical file update. - Under OS X this event is currently not detected. + Under OS X this event is only detected when watching entire trees. */ wxFSW_EVENT_MODIFY = 0x08, @@ -267,7 +269,8 @@ enum wxFSWFlags /** The item's metadata was changed, e.g.\ its permissions or timestamps. - This event is currently only detected under Linux. + This event is currently only detected under Linux and OS X. + Under OS X this event is only detected when watching entire trees. @since 2.9.5 */ @@ -279,7 +282,8 @@ enum wxFSWFlags wxFSW_EVENT_UNMOUNT cannot be set; unmount events are produced automatically. This flag is therefore not included in wxFSW_EVENT_ALL. - This event is currently only detected under Linux. + This event is currently only detected under Linux and OS X. + Under OS X this event is only detected when watching entire trees. @since 2.9.5 */ diff --git a/src/osx/fswatcher_fsevents.cpp b/src/osx/fswatcher_fsevents.cpp new file mode 100644 index 0000000000..849beb97ec --- /dev/null +++ b/src/osx/fswatcher_fsevents.cpp @@ -0,0 +1,509 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/osx/fswatcher_fsevents.cpp +// Purpose: File System watcher that uses the FSEvents API +// of OS X to efficiently watch trees +// Author: Roberto Perpuly +// Created: 2015-04-24 +// Copyright: (c) 2015 Roberto Perpuly +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#if wxUSE_FSWATCHER && defined(wxHAVE_FSEVENTS_FILE_NOTIFICATIONS) + +#include "wx/fswatcher.h" +#include "wx/osx/core/cfstring.h" +#include + +// A small class that we will give the FSEvents +// framework, which will be forwarded to the function +// that gets called when files change. +class wxFSEventWatcherContext +{ +public: + + // Watcher pointer will not be owned by this class. + wxFSEventWatcherContext(wxFsEventsFileSystemWatcher* watcher, + int watcherEventFlags, + const wxString& filespec) + : m_watcher(watcher) + , m_watcherEventFlags(watcherEventFlags) + , m_filespec(filespec) + { + + } + + // Will return true if the given event file and flags + // match the filespec and event flags given to the + // AddTree method. + bool IsDesiredEvent(const wxFileName& eventFileName, int eventFlags) + { + // warning and errors are always sent to the event handler + if ( eventFlags & wxFSW_EVENT_ERROR ) + { + return true; + } + if ( eventFlags & wxFSW_EVENT_WARNING ) + { + return true; + } + + if ( (m_watcherEventFlags & eventFlags) == 0 ) + { + // event handler does not want to see this event + return false; + } + + return m_filespec.empty() || + wxMatchWild(m_filespec, eventFileName.GetFullName()); + } + + wxFsEventsFileSystemWatcher* m_watcher; + + // The event flags that the event handler + // desires to be notified of. + int m_watcherEventFlags; + + // The filespec that the event handler + // desires to be notified of. + wxString m_filespec; + +private: + + wxDECLARE_NO_COPY_CLASS(wxFSEventWatcherContext); +}; + +// Translate kFSEventStreamEventFlag* flags +// to wxFSW_EVENT_* flags. +// warning and msg are out parameters, filled in when +// there is an error in the stream. +static int wxFSEventsToWatcherFlags(FSEventStreamEventFlags flags, +wxFSWWarningType& warning, wxString& msg) +{ + msg = ""; + warning = wxFSW_WARNING_NONE; + + // see https://developer.apple.com/library/mac/documentation/Darwin/Reference/FSEvents_Ref/index.html + // for event flag meanings + int ret = 0; + int warnings = + kFSEventStreamEventFlagMustScanSubDirs + | kFSEventStreamEventFlagUserDropped + | kFSEventStreamEventFlagKernelDropped + | kFSEventStreamEventFlagMount + ; + + int errors = kFSEventStreamEventFlagRootChanged; + + // The following flags are not handled: + // kFSEventStreamEventFlagHistoryDone (we never ask for old events) + // kFSEventStreamEventFlagEventIdsWrapped ( we don't keep track nor + // expose event IDs) + + int created = kFSEventStreamEventFlagItemCreated; + int deleted = kFSEventStreamEventFlagItemRemoved; + int renamed = kFSEventStreamEventFlagItemRenamed; + int modified = kFSEventStreamEventFlagItemModified; + int attrib = kFSEventStreamEventFlagItemChangeOwner + | kFSEventStreamEventFlagItemFinderInfoMod + | kFSEventStreamEventFlagItemInodeMetaMod + | kFSEventStreamEventFlagItemXattrMod; + + if ( created & flags ) + { + ret |= wxFSW_EVENT_CREATE; + } + if ( deleted & flags ) + { + ret |= wxFSW_EVENT_DELETE; + } + if ( renamed & flags ) + { + ret |= wxFSW_EVENT_RENAME; + } + if ( modified & flags ) + { + ret |= wxFSW_EVENT_MODIFY; + } + if ( attrib & flags ) + { + ret |= wxFSW_EVENT_ATTRIB; + } + if ( kFSEventStreamEventFlagUnmount & flags ) + { + ret |= wxFSW_EVENT_UNMOUNT; + } + if ( warnings & flags ) + { + warning = wxFSW_WARNING_GENERAL; + ret |= wxFSW_EVENT_WARNING; + if (flags & kFSEventStreamEventFlagMustScanSubDirs) + { + msg += "Must re-scan sub directories."; + } + if (flags & kFSEventStreamEventFlagUserDropped) + { + msg += "User dropped events"; + warning = wxFSW_WARNING_OVERFLOW; + } + if (flags & kFSEventStreamEventFlagKernelDropped) + { + msg += "Kernel dropped events"; + warning = wxFSW_WARNING_OVERFLOW; + } + if (flags & kFSEventStreamEventFlagMount) + { + msg += "A volume was mounted underneath the watched directory."; + } + } + if ( errors & flags ) + { + ret |= wxFSW_EVENT_ERROR; + msg = "Path being watched has been renamed"; + } + + // don't think that FSEvents tells us about wxFSW_EVENT_ACCESS + return ret; +} + +// Fills in eventFileName appropriately based on whether the +// event was on a file or a directory. +static void FileNameFromEvent(wxFileName& eventFileName, char* path, + FSEventStreamEventFlags flags) +{ + wxString strPath(path); + if ( flags & kFSEventStreamEventFlagItemIsFile ) + { + eventFileName.Assign(strPath); + } + if ( flags & kFSEventStreamEventFlagItemIsDir ) + { + eventFileName.AssignDir(strPath); + } +} + +// This is the function that the FsEvents API +// will call to notify us that a file has been changed. +static void wxFSEventCallback(ConstFSEventStreamRef WXUNUSED(streamRef), void *clientCallBackInfo, + size_t numEvents, void *eventPaths, const FSEventStreamEventFlags eventFlags[], + const FSEventStreamEventId WXUNUSED(eventIds)[]) +{ + wxFSEventWatcherContext* context = + (wxFSEventWatcherContext*) clientCallBackInfo; + + char** paths = (char**) eventPaths; + int lastWxEventFlags = 0; + wxFileName lastEventFileName; + wxString msg; + wxFSWWarningType warning = wxFSW_WARNING_NONE; + wxFileName eventFileName; + for ( size_t i = 0; i < numEvents; i++ ) + { + FSEventStreamEventFlags flags = eventFlags[i]; + FileNameFromEvent(eventFileName, paths[i], flags); + int wxEventFlags = wxFSEventsToWatcherFlags(flags, warning, msg); + if ( context->IsDesiredEvent(eventFileName, wxEventFlags) ) + { + // This is a naive way of looking for file renames + // wx presents a renames with a from and to paths + // but fs events events do not give us this (it only + // provides that a file was renamed, not what the new + // name is). + // We deduce the old and new paths by looking for consecutive + // renames. This is very naive and won't catch simulatenous + // renames inside the latency period, nor renames from/to + // a directory that is not inside the watched paths. + if (wxEventFlags == wxFSW_EVENT_RENAME && lastWxEventFlags == wxFSW_EVENT_RENAME) + { + context->m_watcher->PostChange(lastEventFileName, eventFileName, wxEventFlags); + } + else if (flags == kFSEventStreamEventFlagRootChanged) + { + // send two events: the delete event and the error event + context->m_watcher->PostChange(eventFileName, eventFileName, wxFSW_EVENT_DELETE); + context->m_watcher->PostError(msg); + } + else if (wxEventFlags != wxFSW_EVENT_RENAME) + { + context->m_watcher->PostChange(eventFileName, eventFileName, wxEventFlags); + } + else + { + // This is a "rename" event that we only saw once, meaning that + // a file was renamed to somewhere inside the watched tree + // OR a file was renamed to somewhere outside the watched tree. + if (!eventFileName.IsDir()) + { + int fileEventType = eventFileName.FileExists() ? wxFSW_EVENT_CREATE : wxFSW_EVENT_DELETE; + context->m_watcher->PostChange(eventFileName, eventFileName, fileEventType); + } + if (eventFileName.IsDir()) + { + int dirEventType = eventFileName.DirExists() ? wxFSW_EVENT_CREATE : wxFSW_EVENT_DELETE; + context->m_watcher->PostChange(eventFileName, eventFileName, dirEventType); + } + } + + if (wxEventFlags & wxFSW_EVENT_WARNING) + { + context->m_watcher->PostWarning(warning, msg); + } + + // A single rename (without the second rename) may be due + // to the file being renamed into a directory outside of the + // watch path. + lastWxEventFlags = wxEventFlags; + lastEventFileName = eventFileName; + } + } +} + +static void wxDeleteContext(const void* context) +{ + wxFSEventWatcherContext* watcherContext = + (wxFSEventWatcherContext*) context; + delete watcherContext; +} + +wxFsEventsFileSystemWatcher::wxFsEventsFileSystemWatcher() +: wxKqueueFileSystemWatcher() +{ + +} + +wxFsEventsFileSystemWatcher::wxFsEventsFileSystemWatcher(const wxFileName& path, + int events) +: wxKqueueFileSystemWatcher(path, events) +{ + +} + +wxFsEventsFileSystemWatcher::~wxFsEventsFileSystemWatcher() +{ + +} + +bool wxFsEventsFileSystemWatcher::AddTree(const wxFileName& path, int events, + const wxString& filespec) +{ + if (!path.DirExists()) + { + return false; + } + wxString canonical = GetCanonicalPath(path); + if ( canonical.empty() ) + { + return false; + } + CFRunLoopRef cfLoop = CFRunLoopGetCurrent(); + wxASSERT_MSG( + cfLoop, + "there must be a current event loop; this file watcher needs it." + ); + if ( ! cfLoop ) + { + return false; + } + + if ( m_streams.find(canonical) != m_streams.end() ) + { + // How to take into account filespec + // if client adds a watch for /home/*.cpp + // and then on another call wants to add a + // call to /home/*.h + // Ideally we should not create another watch + // however we would need to keep both filespecs + // around, which we don't do now. + return false; + } + + // Will need to pass the desired event flags + // and filespec to our callback via the context + // we make sure to give the context a cleanup + // callback. + FSEventStreamContext ctx; + wxFSEventWatcherContext* watcherContext = new wxFSEventWatcherContext( + this, events, filespec.Clone() + ); + ctx.version = 0; + ctx.info = watcherContext; + ctx.retain = NULL; + ctx.release = &wxDeleteContext; + ctx.copyDescription = NULL; + CFTimeInterval latency = 0.2; + + wxMacUniCharBuffer pathChars(path.GetPath()); + CFStringRef pathRef = CFStringCreateWithCharacters( + kCFAllocatorDefault, + pathChars.GetBuffer(), + pathChars.GetChars() + ); + CFArrayRef pathRefs = CFArrayCreate( + kCFAllocatorDefault, (const void**)&pathRef, 1, NULL + ); + FSEventStreamCreateFlags flags = kFSEventStreamCreateFlagWatchRoot + | kFSEventStreamCreateFlagFileEvents; + + FSEventStreamRef stream = FSEventStreamCreate( + kCFAllocatorDefault, + &wxFSEventCallback, + &ctx, + pathRefs, kFSEventStreamEventIdSinceNow, + latency, flags); + bool started = false; + if ( stream ) + { + FSEventStreamScheduleWithRunLoop(stream, cfLoop, kCFRunLoopDefaultMode); + started = FSEventStreamStart(stream); + if ( started ) + { + m_streams[canonical] = stream; + } + } + + // cleanup the paths, as we own the pointers + CFRelease(pathRef); + CFRelease(pathRefs); + + wxASSERT_MSG(stream, "could not create FS stream"); + return started; +} + +bool wxFsEventsFileSystemWatcher::RemoveTree(const wxFileName& path) +{ + wxString canonical = GetCanonicalPath(path); + if ( canonical.empty() ) + { + return false; + } + + // Remove any kqueue watches created with Add() + // RemoveTree() should remove all watches no matter + // if they are tree watches or single directory watches. + wxArrayString dirsWatched; + wxKqueueFileSystemWatcher::GetWatchedPaths(&dirsWatched); + for ( size_t i = 0; i < dirsWatched.size(); i++ ) + { + if (dirsWatched[i].Find(canonical) == 0) + { + wxKqueueFileSystemWatcher::Remove(dirsWatched[i]); + } + } + + FSEventStreamRefMap::iterator it = m_streams.find(canonical); + bool removed = false; + if ( it != m_streams.end() ) + { + FSEventStreamStop(it->second); + FSEventStreamInvalidate(it->second); + FSEventStreamRelease(it->second); + m_streams.erase(it); + removed = true; + } + return removed; +} + +bool wxFsEventsFileSystemWatcher::RemoveAll() +{ + // remove all watches created with Add() + bool ret = wxKqueueFileSystemWatcher::RemoveAll(); + FSEventStreamRefMap::iterator it = m_streams.begin(); + while ( it != m_streams.end() ) + { + FSEventStreamStop(it->second); + FSEventStreamInvalidate(it->second); + FSEventStreamRelease(it->second); + it++; + ret |= true; + } + m_streams.clear(); + return ret; +} + +void wxFsEventsFileSystemWatcher::PostChange(const wxFileName& oldFileName, + const wxFileName& newFileName, int event) +{ + wxASSERT_MSG(this->GetOwner(), "owner must exist"); + if ( !this->GetOwner() ) + { + return; + } + + // FSEvents flags are a bit mask, but wx FSW events + // are not, meaning that FSEvent flag might be + // kFSEventStreamEventFlagItemCreated | kFSEventStreamEventFlagItemInodeMetaMod + // this means we must send 2 events not 1. + int allEvents[6] = { + wxFSW_EVENT_CREATE, + wxFSW_EVENT_DELETE, + wxFSW_EVENT_RENAME, + wxFSW_EVENT_MODIFY, + wxFSW_EVENT_ACCESS, + wxFSW_EVENT_ATTRIB + }; + + for ( int i = 0; i < WXSIZEOF(allEvents); i++ ) + { + if ( event & allEvents[i] ) + { + wxFileSystemWatcherEvent* evt = new wxFileSystemWatcherEvent( + allEvents[i], oldFileName, newFileName + ); + wxQueueEvent(this->GetOwner(), evt); + } + } +} + +void wxFsEventsFileSystemWatcher::PostWarning(wxFSWWarningType warning, + const wxString& msg) +{ + wxFileSystemWatcherEvent* evt = new wxFileSystemWatcherEvent( + wxFSW_EVENT_WARNING, warning, msg + ); + wxASSERT_MSG(this->GetOwner(), "owner must exist"); + if (this->GetOwner()) + { + wxQueueEvent(this->GetOwner(), evt); + } +} + +void wxFsEventsFileSystemWatcher::PostError(const wxString& msg) +{ + wxFileSystemWatcherEvent* evt = new wxFileSystemWatcherEvent( + wxFSW_EVENT_ERROR, wxFSW_WARNING_NONE, msg + ); + wxASSERT_MSG(this->GetOwner(), "owner must exist"); + if (this->GetOwner()) + { + wxQueueEvent(this->GetOwner(), evt); + } +} + +int wxFsEventsFileSystemWatcher::GetWatchedPathsCount() const +{ + return m_streams.size() + wxFileSystemWatcherBase::GetWatchedPathsCount(); +} + +int wxFsEventsFileSystemWatcher::GetWatchedPaths(wxArrayString* paths) const +{ + wxCHECK_MSG( paths != NULL, -1, "Null array passed to retrieve paths"); + if ( !paths ) + { + return 0; + } + wxFileSystemWatcherBase::GetWatchedPaths(paths); + FSEventStreamRefMap::const_iterator it = m_streams.begin(); + for ( ; it != m_streams.end(); it++ ) + { + paths->push_back(it->first); + } + return paths->size(); +} + +#endif // wxUSE_FSWATCHER diff --git a/src/unix/fswatcher_kqueue.cpp b/src/unix/fswatcher_kqueue.cpp index 5ac07964b7..0fd8023474 100644 --- a/src/unix/fswatcher_kqueue.cpp +++ b/src/unix/fswatcher_kqueue.cpp @@ -279,8 +279,8 @@ protected: { wxASSERT_MSG(e.udata, "Null user data associated with kevent!"); - wxLogTrace(wxTRACE_FSWATCHER, "Event: ident=%d, filter=%d, flags=%u, " - "fflags=%u, data=%d, user_data=%p", + wxLogTrace(wxTRACE_FSWATCHER, "Event: ident=%llu, filter=%d, flags=%u, " + "fflags=%u, data=%lld, user_data=%lp", e.ident, e.filter, e.flags, e.fflags, e.data, e.udata); // for ease of use diff --git a/tests/fswatcher/fswatchertest.cpp b/tests/fswatcher/fswatchertest.cpp index 5968eae7d8..6561eb46ee 100644 --- a/tests/fswatcher/fswatchertest.cpp +++ b/tests/fswatcher/fswatchertest.cpp @@ -831,12 +831,12 @@ void FileSystemWatcherTestCase::TestTrees() CPPUNIT_ASSERT(m_watcher); size_t treeitems = 1; // the trunk -#ifndef __WINDOWS__ - // When there's no file mask, wxMSW sets a single watch +#if !defined(__WINDOWS__) && !defined(wxHAVE_FSEVENTS_FILE_NOTIFICATIONS) + // When there's no file mask, wxMSW and wxOSX set a single watch // on the trunk which is implemented recursively. // wxGTK always sets an additional watch for each subdir treeitems += subdirs + 1; // +1 for 'child' -#endif // __WINDOWS__ +#endif // !__WINDOWS__ && !wxHAVE_FSEVENTS_FILE_NOTIFICATIONS // Store the initial count; there may already be some watches const int initial = m_watcher->GetWatchedPathsCount(); @@ -862,9 +862,9 @@ void FileSystemWatcherTestCase::TestTrees() // Except that in wxMSW this isn't true: each watch will be a // single, recursive dir; so fudge the count size_t fudge = 0; -#ifdef __WINDOWS__ +#if defined(__WINDOWS__) || defined(wxHAVE_FSEVENTS_FILE_NOTIFICATIONS) fudge = 1; -#endif // __WINDOWS__ +#endif // __WINDOWS__ || wxHAVE_FSEVENTS_FILE_NOTIFICATIONS m_watcher->AddTree(dir); CPPUNIT_ASSERT_EQUAL(plustree + fudge, m_watcher->GetWatchedPathsCount()); m_watcher->RemoveTree(child); @@ -900,7 +900,12 @@ void FileSystemWatcherTestCase::TestTrees() // When we use a filter, both wxMSW and wxGTK implementations set // an additional watch for each subdir (+1 for the root dir itself // and another +1 for "child"). + // On OS X, if we use FSEvents then we still only have 1 watch. +#ifdef wxHAVE_FSEVENTS_FILE_NOTIFICATIONS + const size_t treeitems = 1; +#else const size_t treeitems = subdirs + 2; +#endif m_watcher->AddTree(dir, wxFSW_EVENT_ALL, "*.txt"); const int plustree = m_watcher->GetWatchedPathsCount();