From 3d4859f1b1b7aaa3a1be31699fc9cc9295662848 Mon Sep 17 00:00:00 2001 From: Tobias Hieta Date: Tue, 13 Oct 2015 11:05:27 +0200 Subject: [PATCH] Initial public commit of Plex Media Player --- .clang-format | 47 + .gitignore | 11 + .gitmodules | 0 BuildQT5-windows.md | 41 + CMakeLists.txt | 59 + CMakeModules/AppleConfiguration.cmake | 21 + CMakeModules/BreakpadSymbols.cmake | 35 + CMakeModules/CPackConfiguration.cmake | 63 + CMakeModules/CPackGeneratedConfig.cmake.in | 3 + CMakeModules/CompleteBundle.cmake | 17 + CMakeModules/CompleteBundleOSX.cmake.in | 69 + CMakeModules/CompleteBundleWin.cmake.in | 10 + CMakeModules/DependencyConfiguration.cmake | 28 + CMakeModules/FindBreakpad.cmake | 69 + CMakeModules/FindCEC.cmake | 26 + CMakeModules/FindDL.cmake | 17 + CMakeModules/FindGLES2.cmake | 73 + CMakeModules/FindICU.cmake | 311 ++ CMakeModules/FindIconv.cmake | 57 + CMakeModules/FindMPV.cmake | 80 + CMakeModules/FindSDL2.cmake | 69 + CMakeModules/GCCConfiguration.cmake | 0 CMakeModules/GetDate.cmake | 13 + CMakeModules/GetGitRevisionDescription.cmake | 123 + .../GetGitRevisionDescription.cmake.in | 38 + CMakeModules/InputConfiguration.cmake | 71 + CMakeModules/LinuxConfiguration.cmake | 14 + CMakeModules/NameConfiguration.cmake | 16 + CMakeModules/PlayerConfiguration.cmake | 16 + CMakeModules/QtConfiguration.cmake | 57 + CMakeModules/VersionConfiguration.cmake | 30 + CMakeModules/WebClientResources.cmake | 36 + CMakeModules/WebClientVariables.cmake | 2 + CMakeModules/Win32Configuration.cmake | 14 + CMakeModules/utils.cmake | 70 + README.md | 52 + bundle/osx/Info.plist.in | 40 + bundle/osx/Konvergo.entitlements | 12 + bundle/osx/Plex.icns | Bin 0 -> 1256166 bytes bundle/osx/WebEngine.entitlements | 10 + bundle/osx/qt.conf | 4 + bundle/win/Plex.ico | Bin 0 -> 44821 bytes bundle/win/PlexMediaPlayer-angle.bat | 2 + bundle/win/iconres.rc | 1 + bundle/win/qt.conf | 7 + bundle/win/shortcut.qs | 15 + external/CMakeLists.txt | 7 + external/SPMediaKeyTap/CMakeLists.txt | 9 + external/SPMediaKeyTap/LICENSE | 8 + external/SPMediaKeyTap/README.md | 12 + .../NSObject+SPInvocationGrabbing.h | 30 + .../NSObject+SPInvocationGrabbing.m | 127 + external/SPMediaKeyTap/SPMediaKeyTap.h | 43 + external/SPMediaKeyTap/SPMediaKeyTap.m | 346 ++ external/plistparser/CMakeLists.txt | 8 + external/plistparser/LICENSE | 22 + external/plistparser/plistparser.cpp | 98 + external/plistparser/plistparser.h | 18 + external/plistparser/plistserializer.cpp | 83 + external/plistparser/plistserializer.h | 20 + external/qhttpserver/.gitignore | 30 + external/qhttpserver/CMakeLists.txt | 8 + external/qhttpserver/LICENSE | 19 + external/qhttpserver/README.md | 72 + external/qhttpserver/TODO | 7 + external/qhttpserver/docs/Doxyfile | 2314 ++++++++++ external/qhttpserver/docs/pages/examples.dox | 10 + external/qhttpserver/docs/pages/main-page.dox | 105 + .../examples/bodydata/bodydata.cpp | 76 + .../qhttpserver/examples/bodydata/bodydata.h | 39 + .../examples/bodydata/bodydata.pro | 19 + external/qhttpserver/examples/examples.pro | 5 + .../examples/greeting/greeting.cpp | 48 + .../qhttpserver/examples/greeting/greeting.h | 16 + .../examples/greeting/greeting.pro | 19 + .../examples/helloworld/helloworld.cpp | 37 + .../examples/helloworld/helloworld.h | 16 + .../examples/helloworld/helloworld.pro | 19 + external/qhttpserver/http-parser/.gitignore | 28 + external/qhttpserver/http-parser/.mailmap | 8 + external/qhttpserver/http-parser/.travis.yml | 13 + external/qhttpserver/http-parser/AUTHORS | 67 + .../qhttpserver/http-parser/CONTRIBUTIONS | 4 + external/qhttpserver/http-parser/LICENSE-MIT | 23 + external/qhttpserver/http-parser/README.md | 183 + external/qhttpserver/http-parser/bench.c | 111 + .../http-parser/contrib/parsertrace.c | 160 + .../http-parser/contrib/url_parser.c | 46 + .../qhttpserver/http-parser/http_parser.c | 2429 +++++++++++ .../qhttpserver/http-parser/http_parser.gyp | 111 + .../qhttpserver/http-parser/http_parser.h | 342 ++ external/qhttpserver/http-parser/test.c | 3852 +++++++++++++++++ external/qhttpserver/http-parser/url_parser.c | 44 + external/qhttpserver/konvergo-error.patch | 46 + external/qhttpserver/qhttpserver.pri | 3 + external/qhttpserver/qhttpserver.pro | 8 + external/qhttpserver/src/qhttpconnection.cpp | 293 ++ external/qhttpserver/src/qhttpconnection.h | 85 + external/qhttpserver/src/qhttprequest.cpp | 96 + external/qhttpserver/src/qhttprequest.h | 202 + external/qhttpserver/src/qhttpresponse.cpp | 196 + external/qhttpserver/src/qhttpresponse.h | 172 + external/qhttpserver/src/qhttpserver.cpp | 139 + external/qhttpserver/src/qhttpserver.h | 103 + external/qhttpserver/src/qhttpserver.h.orig | 99 + external/qhttpserver/src/qhttpserverapi.h | 56 + external/qhttpserver/src/qhttpserverfwd.h | 48 + external/qhttpserver/src/src.pro | 37 + external/qslog/CMakeLists.txt | 3 + external/qslog/LICENSE.txt | 24 + external/qslog/QsLog.cpp | 258 ++ external/qslog/QsLog.h | 142 + external/qslog/QsLogDest.cpp | 73 + external/qslog/QsLogDest.h | 101 + external/qslog/QsLogDestConsole.cpp | 52 + external/qslog/QsLogDestConsole.h | 53 + external/qslog/QsLogDestFile.cpp | 163 + external/qslog/QsLogDestFile.h | 106 + external/qslog/QsLogDestFunctor.cpp | 57 + external/qslog/QsLogDestFunctor.h | 60 + external/qslog/QsLogDisableForThisFile.h | 22 + external/qslog/QsLogLevel.h | 45 + external/qslog/QsLogReadme.txt | 65 + ...ngine-Add-a-backgroundColor-property.patch | 532 +++ ...enu-bar-at-all-in-lion-style-fullscr.patch | 27 + .../0003-Always-enable-viewport-stuff.patch | 29 + ...bengine-transparency-window-creation.patch | 44 + qt-patches/README.md | 14 + ...d-checks-to-usages-of-QWindow-screen.patch | 120 + ...dows-Improve-error-handling-with-EGL.patch | 151 + ...Extract-static-method-for-closestAcc.patch | 85 + ...um-create-wakeup-pipe-with-O_CLOEXEC.patch | 40 + resources/images/icon.png | Bin 0 -> 40769 bytes resources/images/splash.png | Bin 0 -> 6789 bytes resources/images/splash.svg | 26 + resources/inputmaps/LIRC.json | 33 + resources/inputmaps/apple-media-keys.json | 12 + resources/inputmaps/apple-remote.json | 19 + resources/inputmaps/cec.json | 30 + resources/inputmaps/keyboard.json | 44 + resources/inputmaps/wiimote.json | 13 + resources/inputmaps/xbox-controller.json | 14 + resources/settings/settings_description.json | 279 ++ resources/sounds/Back.wav | Bin 0 -> 12396 bytes resources/sounds/Click.wav | Bin 0 -> 24748 bytes resources/sounds/Cursor.wav | Bin 0 -> 12396 bytes resources/sounds/Notify.wav | Bin 0 -> 43822 bytes resources/sounds/Out.wav | Bin 0 -> 18478 bytes resources/sounds/Shutter.wav | Bin 0 -> 20780 bytes scripts/WindowsSign.cmd | 29 + scripts/build-konvergo-qt.sh | 32 + scripts/build-qt-resources.py | 57 + scripts/build-windows.bat | 41 + scripts/dump-syms.sh | 19 + scripts/dump_qt_symbols_osx.sh | 28 + scripts/fetch-binaries.py | 278 ++ scripts/fix-install-names.py | 74 + scripts/install-dylibs.py | 61 + src/.clang-format | 47 + src/CMakeLists.txt | 168 + src/ComponentManager.cpp | 86 + src/ComponentManager.h | 42 + src/KonvergoPCH.h | 9 + src/SignalManager.cpp | 86 + src/SignalManager.h | 33 + src/Version.cpp.in | 24 + src/Version.h | 13 + src/breakpad/BreakPad.h | 8 + src/breakpad/BreakPadDummy.cpp | 7 + src/breakpad/BreakPadLinux.cpp | 9 + src/breakpad/BreakPadOSX.cpp | 17 + src/breakpad/BreakPadWin32.cpp | 22 + src/breakpad/CMakeLists.txt | 16 + src/breakpad/CrashDumps.h | 24 + src/display/CMakeLists.txt | 14 + src/display/DisplayComponent.cpp | 225 + src/display/DisplayComponent.h | 52 + src/display/DisplayManager.cpp | 226 + src/display/DisplayManager.h | 134 + src/display/dummy/CMakeLists.txt | 1 + src/display/dummy/DisplayManagerDummy.cpp | 75 + src/display/dummy/DisplayManagerDummy.h | 25 + src/display/osx/CMakeLists.txt | 1 + src/display/osx/DisplayManagerOSX.cpp | 155 + src/display/osx/DisplayManagerOSX.h | 42 + src/display/rpi/CMakeLists.txt | 1 + src/display/rpi/DisplayManagerRPI.cpp | 182 + src/display/rpi/DisplayManagerRPI.h | 36 + src/display/win/CMakeLists.txt | 1 + src/display/win/DisplayManagerWin.cpp | 212 + src/display/win/DisplayManagerWin.h | 30 + src/display/x11/CMakeLists.txt | 1 + src/display/x11/DisplayManagerX11.cpp | 215 + src/display/x11/DisplayManagerX11.h | 33 + src/input/CMakeLists.txt | 28 + src/input/InputCEC.cpp | 361 ++ src/input/InputCEC.h | 55 + src/input/InputComponent.cpp | 104 + src/input/InputComponent.h | 94 + src/input/InputKeyboard.h | 31 + src/input/InputLIRC.cpp | 116 + src/input/InputLIRC.h | 33 + src/input/InputMapping.cpp | 161 + src/input/InputMapping.h | 37 + src/input/InputSDL.cpp | 275 ++ src/input/InputSDL.h | 73 + src/input/InputSocket.cpp | 42 + src/input/InputSocket.h | 33 + src/input/apple/AppleRemoteDelegate.h | 29 + src/input/apple/AppleRemoteDelegate.mm | 68 + src/input/apple/CMakeLists.txt | 7 + src/input/apple/HIDRemote/HIDRemote.h | 378 ++ src/input/apple/HIDRemote/HIDRemote.m | 1953 +++++++++ src/input/apple/HIDRemote/LICENSE.txt | 27 + src/input/apple/HIDRemote/README.txt | 18 + src/input/apple/InputAppleMediaKeys.h | 22 + src/input/apple/InputAppleMediaKeys.mm | 74 + src/input/apple/InputAppleRemote.h | 35 + src/input/apple/InputAppleRemote.mm | 85 + src/main.cpp | 276 ++ src/player/CMakeLists.txt | 2 + src/player/PlayerComponent.cpp | 842 ++++ src/player/PlayerComponent.h | 166 + src/player/PlayerQuickItem.cpp | 250 ++ src/player/PlayerQuickItem.h | 57 + src/plugins/RPI_jpeg/QRPIJpegHandler.cpp | 217 + src/plugins/RPI_jpeg/QRPIJpegHandler.h | 70 + src/plugins/RPI_jpeg/QRPIJpegPlugin.cpp | 36 + src/plugins/RPI_jpeg/QRPIJpegPlugin.h | 15 + src/plugins/RPI_jpeg/RPI_jpeg.pro | 30 + src/plugins/RPI_jpeg/brcmjpeg.cpp | 914 ++++ src/plugins/RPI_jpeg/brcmjpeg.h | 152 + src/plugins/RPI_jpeg/jpeg.json | 4 + src/power/CMakeLists.txt | 17 + src/power/PowerComponent.cpp | 97 + src/power/PowerComponent.h | 58 + src/power/PowerComponentMac.cpp | 24 + src/power/PowerComponentMac.h | 18 + src/power/PowerComponentOE.cpp | 81 + src/power/PowerComponentOE.h | 27 + src/power/PowerComponentWin.cpp | 15 + src/power/PowerComponentWin.h | 14 + src/power/PowerComponentX11.cpp | 55 + src/power/PowerComponentX11.h | 31 + src/remote/CMakeLists.txt | 3 + src/remote/GDMManager.cpp | 79 + src/remote/GDMManager.h | 30 + src/remote/RemoteComponent.cpp | 527 +++ src/remote/RemoteComponent.h | 73 + src/remote/RemoteSubscriber.cpp | 204 + src/remote/RemoteSubscriber.h | 87 + src/server/CMakeLists.txt | 3 + src/server/HTTPServer.cpp | 170 + src/server/HTTPServer.h | 34 + src/settings/AudioSettingsController.cpp | 61 + src/settings/AudioSettingsController.h | 22 + src/settings/CMakeLists.txt | 6 + src/settings/SettingsComponent.cpp | 505 +++ src/settings/SettingsComponent.h | 78 + src/settings/SettingsSection.cpp | 165 + src/settings/SettingsSection.h | 65 + src/settings/SettingsValue.h | 137 + src/shared/CMakeLists.txt | 11 + src/shared/LocalJsonClient.cpp | 35 + src/shared/LocalJsonClient.h | 26 + src/shared/LocalJsonServer.cpp | 104 + src/shared/LocalJsonServer.h | 37 + src/shared/Names.cpp.in | 15 + src/shared/Names.h | 16 + src/shared/Paths.cpp | 74 + src/shared/Paths.h | 18 + src/shared/UniqueApplication.h | 74 + src/system/CMakeLists.txt | 6 + src/system/OEUpdateManager.cpp | 67 + src/system/OEUpdateManager.h | 20 + src/system/SystemComponent.cpp | 267 ++ src/system/SystemComponent.h | 81 + src/system/UpdateManager.cpp | 184 + src/system/UpdateManager.h | 24 + src/system/UpdaterComponent.cpp | 198 + src/system/UpdaterComponent.h | 164 + src/system/openelec/CMakeLists.txt | 2 + src/system/openelec/OESystemComponent.cpp | 41 + src/system/openelec/OESystemComponent.h | 23 + src/tools/CMakeLists.txt | 2 + src/tools/helper/CMakeLists.txt | 30 + src/tools/helper/CrashUploader.cpp | 222 + src/tools/helper/CrashUploader.h | 41 + src/tools/helper/HelperMain.cpp | 66 + src/tools/helper/HelperSettings.h | 19 + src/tools/helper/HelperSocket.cpp | 57 + src/tools/helper/HelperSocket.h | 23 + src/tools/socket-client/CMakeLists.txt | 11 + src/tools/socket-client/SocketClient.cpp | 72 + src/ui/CMakeLists.txt | 2 + src/ui/KonvergoEngine.h | 20 + src/ui/KonvergoWindow.cpp | 349 ++ src/ui/KonvergoWindow.h | 76 + src/ui/errormessage.qml | 22 + src/ui/webview.qml | 147 + src/utils/CMakeLists.txt | 12 + src/utils/CachedRegexMatcher.cpp | 77 + src/utils/CachedRegexMatcher.h | 30 + src/utils/HelperLaunchd.cpp | 141 + src/utils/HelperLaunchd.h | 30 + src/utils/HelperLauncher.cpp | 182 + src/utils/HelperLauncher.h | 53 + src/utils/PlatformUtils.cpp | 19 + src/utils/PlatformUtils.h | 18 + src/utils/Utils.cpp | 98 + src/utils/Utils.h | 57 + src/utils/osx/CMakeLists.txt | 3 + src/utils/osx/OSXUtils.h | 13 + src/utils/osx/OSXUtils.mm | 24 + vagrant/ubuntu-x86_64/Vagrantfile | 21 + vagrant/ubuntu-x86_64/bootstrap.sh | 22 + vagrant/ubuntu-x86_64/build-deps.sh | 32 + vagrant/ubuntu-x86_64/sources.list | 4 + vagrant/windows-x86_64/Vagrantfile | 36 + vagrant/windows-x86_64/vagrant_insecure_key | 27 + .../windows-x86_64/vagrant_insecure_key.pub | 1 + 321 files changed, 33472 insertions(+) create mode 100644 .clang-format create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 BuildQT5-windows.md create mode 100644 CMakeLists.txt create mode 100644 CMakeModules/AppleConfiguration.cmake create mode 100644 CMakeModules/BreakpadSymbols.cmake create mode 100644 CMakeModules/CPackConfiguration.cmake create mode 100644 CMakeModules/CPackGeneratedConfig.cmake.in create mode 100644 CMakeModules/CompleteBundle.cmake create mode 100644 CMakeModules/CompleteBundleOSX.cmake.in create mode 100644 CMakeModules/CompleteBundleWin.cmake.in create mode 100644 CMakeModules/DependencyConfiguration.cmake create mode 100644 CMakeModules/FindBreakpad.cmake create mode 100644 CMakeModules/FindCEC.cmake create mode 100644 CMakeModules/FindDL.cmake create mode 100644 CMakeModules/FindGLES2.cmake create mode 100644 CMakeModules/FindICU.cmake create mode 100644 CMakeModules/FindIconv.cmake create mode 100644 CMakeModules/FindMPV.cmake create mode 100644 CMakeModules/FindSDL2.cmake create mode 100644 CMakeModules/GCCConfiguration.cmake create mode 100644 CMakeModules/GetDate.cmake create mode 100644 CMakeModules/GetGitRevisionDescription.cmake create mode 100644 CMakeModules/GetGitRevisionDescription.cmake.in create mode 100644 CMakeModules/InputConfiguration.cmake create mode 100644 CMakeModules/LinuxConfiguration.cmake create mode 100644 CMakeModules/NameConfiguration.cmake create mode 100644 CMakeModules/PlayerConfiguration.cmake create mode 100644 CMakeModules/QtConfiguration.cmake create mode 100644 CMakeModules/VersionConfiguration.cmake create mode 100644 CMakeModules/WebClientResources.cmake create mode 100644 CMakeModules/WebClientVariables.cmake create mode 100644 CMakeModules/Win32Configuration.cmake create mode 100644 CMakeModules/utils.cmake create mode 100644 README.md create mode 100644 bundle/osx/Info.plist.in create mode 100644 bundle/osx/Konvergo.entitlements create mode 100644 bundle/osx/Plex.icns create mode 100644 bundle/osx/WebEngine.entitlements create mode 100644 bundle/osx/qt.conf create mode 100644 bundle/win/Plex.ico create mode 100644 bundle/win/PlexMediaPlayer-angle.bat create mode 100644 bundle/win/iconres.rc create mode 100644 bundle/win/qt.conf create mode 100644 bundle/win/shortcut.qs create mode 100644 external/CMakeLists.txt create mode 100644 external/SPMediaKeyTap/CMakeLists.txt create mode 100644 external/SPMediaKeyTap/LICENSE create mode 100644 external/SPMediaKeyTap/README.md create mode 100644 external/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.h create mode 100644 external/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m create mode 100644 external/SPMediaKeyTap/SPMediaKeyTap.h create mode 100644 external/SPMediaKeyTap/SPMediaKeyTap.m create mode 100644 external/plistparser/CMakeLists.txt create mode 100644 external/plistparser/LICENSE create mode 100644 external/plistparser/plistparser.cpp create mode 100644 external/plistparser/plistparser.h create mode 100644 external/plistparser/plistserializer.cpp create mode 100644 external/plistparser/plistserializer.h create mode 100755 external/qhttpserver/.gitignore create mode 100644 external/qhttpserver/CMakeLists.txt create mode 100755 external/qhttpserver/LICENSE create mode 100755 external/qhttpserver/README.md create mode 100755 external/qhttpserver/TODO create mode 100755 external/qhttpserver/docs/Doxyfile create mode 100755 external/qhttpserver/docs/pages/examples.dox create mode 100755 external/qhttpserver/docs/pages/main-page.dox create mode 100755 external/qhttpserver/examples/bodydata/bodydata.cpp create mode 100755 external/qhttpserver/examples/bodydata/bodydata.h create mode 100755 external/qhttpserver/examples/bodydata/bodydata.pro create mode 100755 external/qhttpserver/examples/examples.pro create mode 100755 external/qhttpserver/examples/greeting/greeting.cpp create mode 100755 external/qhttpserver/examples/greeting/greeting.h create mode 100755 external/qhttpserver/examples/greeting/greeting.pro create mode 100755 external/qhttpserver/examples/helloworld/helloworld.cpp create mode 100755 external/qhttpserver/examples/helloworld/helloworld.h create mode 100755 external/qhttpserver/examples/helloworld/helloworld.pro create mode 100755 external/qhttpserver/http-parser/.gitignore create mode 100644 external/qhttpserver/http-parser/.mailmap create mode 100644 external/qhttpserver/http-parser/.travis.yml create mode 100755 external/qhttpserver/http-parser/AUTHORS create mode 100755 external/qhttpserver/http-parser/CONTRIBUTIONS create mode 100755 external/qhttpserver/http-parser/LICENSE-MIT create mode 100755 external/qhttpserver/http-parser/README.md create mode 100644 external/qhttpserver/http-parser/bench.c create mode 100644 external/qhttpserver/http-parser/contrib/parsertrace.c create mode 100644 external/qhttpserver/http-parser/contrib/url_parser.c create mode 100755 external/qhttpserver/http-parser/http_parser.c create mode 100755 external/qhttpserver/http-parser/http_parser.gyp create mode 100755 external/qhttpserver/http-parser/http_parser.h create mode 100755 external/qhttpserver/http-parser/test.c create mode 100755 external/qhttpserver/http-parser/url_parser.c create mode 100644 external/qhttpserver/konvergo-error.patch create mode 100755 external/qhttpserver/qhttpserver.pri create mode 100755 external/qhttpserver/qhttpserver.pro create mode 100755 external/qhttpserver/src/qhttpconnection.cpp create mode 100755 external/qhttpserver/src/qhttpconnection.h create mode 100755 external/qhttpserver/src/qhttprequest.cpp create mode 100755 external/qhttpserver/src/qhttprequest.h create mode 100755 external/qhttpserver/src/qhttpresponse.cpp create mode 100755 external/qhttpserver/src/qhttpresponse.h create mode 100755 external/qhttpserver/src/qhttpserver.cpp create mode 100755 external/qhttpserver/src/qhttpserver.h create mode 100755 external/qhttpserver/src/qhttpserver.h.orig create mode 100755 external/qhttpserver/src/qhttpserverapi.h create mode 100755 external/qhttpserver/src/qhttpserverfwd.h create mode 100755 external/qhttpserver/src/src.pro create mode 100644 external/qslog/CMakeLists.txt create mode 100644 external/qslog/LICENSE.txt create mode 100644 external/qslog/QsLog.cpp create mode 100644 external/qslog/QsLog.h create mode 100644 external/qslog/QsLogDest.cpp create mode 100644 external/qslog/QsLogDest.h create mode 100644 external/qslog/QsLogDestConsole.cpp create mode 100644 external/qslog/QsLogDestConsole.h create mode 100644 external/qslog/QsLogDestFile.cpp create mode 100644 external/qslog/QsLogDestFile.h create mode 100644 external/qslog/QsLogDestFunctor.cpp create mode 100644 external/qslog/QsLogDestFunctor.h create mode 100644 external/qslog/QsLogDisableForThisFile.h create mode 100644 external/qslog/QsLogLevel.h create mode 100644 external/qslog/QsLogReadme.txt create mode 100644 qt-patches/0001-qtwebengine-Add-a-backgroundColor-property.patch create mode 100644 qt-patches/0002-qtbase-Don-t-show-the-menu-bar-at-all-in-lion-style-fullscr.patch create mode 100644 qt-patches/0003-Always-enable-viewport-stuff.patch create mode 100644 qt-patches/0004-qtwebengine-transparency-window-creation.patch create mode 100644 qt-patches/README.md create mode 100644 qt-patches/qt-5.6-alpha/0001-Windows-Add-checks-to-usages-of-QWindow-screen.patch create mode 100644 qt-patches/qt-5.6-alpha/0002-windows-Improve-error-handling-with-EGL.patch create mode 100644 qt-patches/qt-5.6-alpha/0003-QPlatformWindow-Extract-static-method-for-closestAcc.patch create mode 100644 qt-patches/qt-5.6-alpha/0004-chromium-create-wakeup-pipe-with-O_CLOEXEC.patch create mode 100644 resources/images/icon.png create mode 100644 resources/images/splash.png create mode 100644 resources/images/splash.svg create mode 100644 resources/inputmaps/LIRC.json create mode 100644 resources/inputmaps/apple-media-keys.json create mode 100644 resources/inputmaps/apple-remote.json create mode 100644 resources/inputmaps/cec.json create mode 100644 resources/inputmaps/keyboard.json create mode 100644 resources/inputmaps/wiimote.json create mode 100644 resources/inputmaps/xbox-controller.json create mode 100644 resources/settings/settings_description.json create mode 100644 resources/sounds/Back.wav create mode 100644 resources/sounds/Click.wav create mode 100644 resources/sounds/Cursor.wav create mode 100644 resources/sounds/Notify.wav create mode 100644 resources/sounds/Out.wav create mode 100644 resources/sounds/Shutter.wav create mode 100644 scripts/WindowsSign.cmd create mode 100644 scripts/build-konvergo-qt.sh create mode 100755 scripts/build-qt-resources.py create mode 100644 scripts/build-windows.bat create mode 100755 scripts/dump-syms.sh create mode 100755 scripts/dump_qt_symbols_osx.sh create mode 100755 scripts/fetch-binaries.py create mode 100755 scripts/fix-install-names.py create mode 100755 scripts/install-dylibs.py create mode 100644 src/.clang-format create mode 100644 src/CMakeLists.txt create mode 100644 src/ComponentManager.cpp create mode 100644 src/ComponentManager.h create mode 100644 src/KonvergoPCH.h create mode 100644 src/SignalManager.cpp create mode 100644 src/SignalManager.h create mode 100644 src/Version.cpp.in create mode 100644 src/Version.h create mode 100644 src/breakpad/BreakPad.h create mode 100644 src/breakpad/BreakPadDummy.cpp create mode 100644 src/breakpad/BreakPadLinux.cpp create mode 100644 src/breakpad/BreakPadOSX.cpp create mode 100644 src/breakpad/BreakPadWin32.cpp create mode 100644 src/breakpad/CMakeLists.txt create mode 100644 src/breakpad/CrashDumps.h create mode 100644 src/display/CMakeLists.txt create mode 100644 src/display/DisplayComponent.cpp create mode 100644 src/display/DisplayComponent.h create mode 100755 src/display/DisplayManager.cpp create mode 100644 src/display/DisplayManager.h create mode 100644 src/display/dummy/CMakeLists.txt create mode 100644 src/display/dummy/DisplayManagerDummy.cpp create mode 100644 src/display/dummy/DisplayManagerDummy.h create mode 100644 src/display/osx/CMakeLists.txt create mode 100644 src/display/osx/DisplayManagerOSX.cpp create mode 100644 src/display/osx/DisplayManagerOSX.h create mode 100644 src/display/rpi/CMakeLists.txt create mode 100644 src/display/rpi/DisplayManagerRPI.cpp create mode 100644 src/display/rpi/DisplayManagerRPI.h create mode 100644 src/display/win/CMakeLists.txt create mode 100644 src/display/win/DisplayManagerWin.cpp create mode 100644 src/display/win/DisplayManagerWin.h create mode 100644 src/display/x11/CMakeLists.txt create mode 100755 src/display/x11/DisplayManagerX11.cpp create mode 100644 src/display/x11/DisplayManagerX11.h create mode 100644 src/input/CMakeLists.txt create mode 100644 src/input/InputCEC.cpp create mode 100644 src/input/InputCEC.h create mode 100644 src/input/InputComponent.cpp create mode 100644 src/input/InputComponent.h create mode 100644 src/input/InputKeyboard.h create mode 100644 src/input/InputLIRC.cpp create mode 100644 src/input/InputLIRC.h create mode 100644 src/input/InputMapping.cpp create mode 100644 src/input/InputMapping.h create mode 100644 src/input/InputSDL.cpp create mode 100644 src/input/InputSDL.h create mode 100644 src/input/InputSocket.cpp create mode 100644 src/input/InputSocket.h create mode 100644 src/input/apple/AppleRemoteDelegate.h create mode 100644 src/input/apple/AppleRemoteDelegate.mm create mode 100644 src/input/apple/CMakeLists.txt create mode 100644 src/input/apple/HIDRemote/HIDRemote.h create mode 100644 src/input/apple/HIDRemote/HIDRemote.m create mode 100644 src/input/apple/HIDRemote/LICENSE.txt create mode 100644 src/input/apple/HIDRemote/README.txt create mode 100644 src/input/apple/InputAppleMediaKeys.h create mode 100644 src/input/apple/InputAppleMediaKeys.mm create mode 100644 src/input/apple/InputAppleRemote.h create mode 100644 src/input/apple/InputAppleRemote.mm create mode 100644 src/main.cpp create mode 100644 src/player/CMakeLists.txt create mode 100644 src/player/PlayerComponent.cpp create mode 100644 src/player/PlayerComponent.h create mode 100644 src/player/PlayerQuickItem.cpp create mode 100644 src/player/PlayerQuickItem.h create mode 100644 src/plugins/RPI_jpeg/QRPIJpegHandler.cpp create mode 100644 src/plugins/RPI_jpeg/QRPIJpegHandler.h create mode 100644 src/plugins/RPI_jpeg/QRPIJpegPlugin.cpp create mode 100644 src/plugins/RPI_jpeg/QRPIJpegPlugin.h create mode 100644 src/plugins/RPI_jpeg/RPI_jpeg.pro create mode 100644 src/plugins/RPI_jpeg/brcmjpeg.cpp create mode 100644 src/plugins/RPI_jpeg/brcmjpeg.h create mode 100644 src/plugins/RPI_jpeg/jpeg.json create mode 100644 src/power/CMakeLists.txt create mode 100644 src/power/PowerComponent.cpp create mode 100644 src/power/PowerComponent.h create mode 100644 src/power/PowerComponentMac.cpp create mode 100644 src/power/PowerComponentMac.h create mode 100644 src/power/PowerComponentOE.cpp create mode 100644 src/power/PowerComponentOE.h create mode 100644 src/power/PowerComponentWin.cpp create mode 100644 src/power/PowerComponentWin.h create mode 100644 src/power/PowerComponentX11.cpp create mode 100644 src/power/PowerComponentX11.h create mode 100644 src/remote/CMakeLists.txt create mode 100644 src/remote/GDMManager.cpp create mode 100644 src/remote/GDMManager.h create mode 100644 src/remote/RemoteComponent.cpp create mode 100644 src/remote/RemoteComponent.h create mode 100644 src/remote/RemoteSubscriber.cpp create mode 100644 src/remote/RemoteSubscriber.h create mode 100644 src/server/CMakeLists.txt create mode 100644 src/server/HTTPServer.cpp create mode 100644 src/server/HTTPServer.h create mode 100644 src/settings/AudioSettingsController.cpp create mode 100644 src/settings/AudioSettingsController.h create mode 100644 src/settings/CMakeLists.txt create mode 100644 src/settings/SettingsComponent.cpp create mode 100644 src/settings/SettingsComponent.h create mode 100644 src/settings/SettingsSection.cpp create mode 100644 src/settings/SettingsSection.h create mode 100644 src/settings/SettingsValue.h create mode 100644 src/shared/CMakeLists.txt create mode 100644 src/shared/LocalJsonClient.cpp create mode 100644 src/shared/LocalJsonClient.h create mode 100644 src/shared/LocalJsonServer.cpp create mode 100644 src/shared/LocalJsonServer.h create mode 100644 src/shared/Names.cpp.in create mode 100644 src/shared/Names.h create mode 100644 src/shared/Paths.cpp create mode 100644 src/shared/Paths.h create mode 100644 src/shared/UniqueApplication.h create mode 100644 src/system/CMakeLists.txt create mode 100644 src/system/OEUpdateManager.cpp create mode 100644 src/system/OEUpdateManager.h create mode 100644 src/system/SystemComponent.cpp create mode 100644 src/system/SystemComponent.h create mode 100644 src/system/UpdateManager.cpp create mode 100644 src/system/UpdateManager.h create mode 100644 src/system/UpdaterComponent.cpp create mode 100644 src/system/UpdaterComponent.h create mode 100644 src/system/openelec/CMakeLists.txt create mode 100644 src/system/openelec/OESystemComponent.cpp create mode 100644 src/system/openelec/OESystemComponent.h create mode 100644 src/tools/CMakeLists.txt create mode 100644 src/tools/helper/CMakeLists.txt create mode 100644 src/tools/helper/CrashUploader.cpp create mode 100644 src/tools/helper/CrashUploader.h create mode 100644 src/tools/helper/HelperMain.cpp create mode 100644 src/tools/helper/HelperSettings.h create mode 100644 src/tools/helper/HelperSocket.cpp create mode 100644 src/tools/helper/HelperSocket.h create mode 100644 src/tools/socket-client/CMakeLists.txt create mode 100644 src/tools/socket-client/SocketClient.cpp create mode 100644 src/ui/CMakeLists.txt create mode 100644 src/ui/KonvergoEngine.h create mode 100644 src/ui/KonvergoWindow.cpp create mode 100644 src/ui/KonvergoWindow.h create mode 100644 src/ui/errormessage.qml create mode 100644 src/ui/webview.qml create mode 100644 src/utils/CMakeLists.txt create mode 100644 src/utils/CachedRegexMatcher.cpp create mode 100644 src/utils/CachedRegexMatcher.h create mode 100644 src/utils/HelperLaunchd.cpp create mode 100644 src/utils/HelperLaunchd.h create mode 100644 src/utils/HelperLauncher.cpp create mode 100644 src/utils/HelperLauncher.h create mode 100644 src/utils/PlatformUtils.cpp create mode 100644 src/utils/PlatformUtils.h create mode 100644 src/utils/Utils.cpp create mode 100644 src/utils/Utils.h create mode 100644 src/utils/osx/CMakeLists.txt create mode 100644 src/utils/osx/OSXUtils.h create mode 100644 src/utils/osx/OSXUtils.mm create mode 100644 vagrant/ubuntu-x86_64/Vagrantfile create mode 100755 vagrant/ubuntu-x86_64/bootstrap.sh create mode 100755 vagrant/ubuntu-x86_64/build-deps.sh create mode 100644 vagrant/ubuntu-x86_64/sources.list create mode 100644 vagrant/windows-x86_64/Vagrantfile create mode 100644 vagrant/windows-x86_64/vagrant_insecure_key create mode 100644 vagrant/windows-x86_64/vagrant_insecure_key.pub diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..236ef64 --- /dev/null +++ b/.clang-format @@ -0,0 +1,47 @@ +--- +# BasedOnStyle: LLVM +AccessModifierOffset: -2 +ConstructorInitializerIndentWidth: 2 +AlignEscapedNewlinesLeft: false +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakTemplateDeclarations: false +AlwaysBreakBeforeMultilineStrings: false +BreakBeforeBinaryOperators: false +BreakBeforeTernaryOperators: false +BreakConstructorInitializersBeforeComma: false +BinPackParameters: true +ColumnLimit: 100 +ConstructorInitializerAllOnOneLineOrOnePerLine: true +DerivePointerBinding: false +ExperimentalAutoDetectBinPacking: false +IndentCaseLabels: true +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: All +ObjCSpaceBeforeProtocolList: true +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 60 +PenaltyBreakString: 1000 +PenaltyBreakFirstLessLess: 120 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 1000 +PointerBindsToType: true +SpacesBeforeTrailingComments: 1 +Cpp11BracedListStyle: false +Standard: Cpp03 +IndentWidth: 2 +TabWidth: 2 +UseTab: Never +BreakBeforeBraces: Allman +IndentFunctionDeclarationAfterType: false +SpacesInParentheses: false +SpacesInAngles: false +SpaceInEmptyParentheses: false +SpacesInCStyleCastParentheses: false +SpaceAfterControlStatementKeyword: true +SpaceBeforeAssignmentOperators: true +ContinuationIndentWidth: 0 +... + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e31eefb --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +*.pro.user* +dependencies/* +CMakeLists.txt.user +CMakeLists.txt.user.* +build/* +.idea +.install_pkg +.openelec-unpack +QtCreatorDeployment.txt +vagrant/ubuntu-x86_64/.vagrant +vagrant/windows-x86_64/.vagrant diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..e69de29 diff --git a/BuildQT5-windows.md b/BuildQT5-windows.md new file mode 100644 index 0000000..d2e30f5 --- /dev/null +++ b/BuildQT5-windows.md @@ -0,0 +1,41 @@ +# How-To build Qt 5.5.0 on Windows +This procedure was ran on a freshly installed Win7 64 bits. + +This procedure assumes that the build root is `C:\qt\` but it can be changed in the beginning of each `.bat` file + +1. Install Visual Studio 2013 +- Install [Visual Studio 2013 Update 4](http://www.microsoft.com/fr-fr/download/details.aspx?id=44921) +- Install [Windows SDK 8](https://msdn.microsoft.com/en-us/windows/desktop/hh852363.aspx) +- Install [ActivePerl](http://www.activestate.com/activeperl/downloads) +- Install [Python 2.7.10](https://www.python.org/downloads/release/python-2710) +- Install the following gnutools: + - [Bison](http://gnuwin32.sourceforge.net/downlinks/bison.php) + - [GPerf](http://gnuwin32.sourceforge.net/downlinks/gperf.php) + - [Flex](http://gnuwin32.sourceforge.net/downlinks/flex.php) +- Grab [ICU sources](http://download.icu-project.org/files/icu4c/55.1/icu4c-55_1-src.zip) and unpack them in `C:\qt\icu` +- Grab [Openssl 1.0.2d](https://www.openssl.org/source/) and unpack them in `C:\qt\openssl` +- Grab [Qt Sources](http://download.qt.io/official_releases/qt/5.5/5.5.0/single/qt-everywhere-opensource-src-5.5.0.zip) and unpack them in `C:\qt\qt5` +- drop the three `.bat` files in the steps below in `C:\qt\` +- Open `C:\qt\icu\source\allionone\allinone.sln` with VS2013, you will be prompted for project conversion. Once the project is converted, just close it. +- run `c:\qt\buildicu.bat` +- run `c:\qt\buildopenssl.bat` +- run `c:\qt\buildqt.bat` + +The qt build will land in `C:\qt\qt5\build` + + +# buildicu.bat +``` +set BUILD_ROOT=C:\qt REM Set up \Microsoft Visual Studio 2013, where is \c amd64, \c x86, etc. CALL "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" x86_amd64 REM set compiler to multicore set CL=/MP REM Build icu cd %BUILD_ROOT%\icu msbuild source\allinone\allinone.sln /m /target:Build /property:Configuration=Release;Platform=x64 msbuild source\allinone\allinone.sln /m /target:Build /property:Configuration=Debug;Platform=x64 +cd .. +``` + +# buildopenssl.bat +``` +set BUILD_ROOT=C:\qt CALL "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" x86_amd64 cd openssl set CL=/MP perl Configure VC-WIN64A --prefix=%BUILD_ROOT%\openssl\build call ms\do_win64a nmake -f ms\ntdll.mak nmake -f ms\ntdll.mak install cd .. +``` + +# buildqt.bat +``` +set BUILD_ROOT=C:\qt REM Set up \Microsoft Visual Studio 2013, where is \c amd64, \c x86, etc. CALL "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" x86_amd64 REM set compiler to multicore set CL=/MP REM Add ICU dirs to the proper path (include, libs , bin) set INCLUDE=%INCLUDE%;%BUILD_ROOT%\icu\include set LIB=%LIB%;%BUILD_ROOT%\icu\lib64 set PATH=%PATH%;%BUILD_ROOT%\icu\bin64 REM Add OpenSSL dirs to the proper path (include, libs , bin) set INCLUDE=%INCLUDE%;%BUILD_ROOT%\openssl\build\include set LIB=%LIB%;%BUILD_ROOT%\openssl\build\lib set PATH=%PATH%;%BUILD_ROOT%\openssl\build\bin REM Add Pyhton dirs to the proper path (include, libs , bin) set PATH=%PATH%;c:\Python27\ REM Add GunWindirs to the proper path (include, libs , bin) set INCLUDE=%INCLUDE%;%BUILD_ROOT%\GnuWin32\include set LIB=%LIB%;%BUILD_ROOT%\GnuWin32\lib set PATH=%PATH%;=%BUILD_ROOT%\GnuWin32\bin SET QT_ROOT=%BUILD_ROOT%\qt5 SET PATH=%QT_ROOT%\qtbase\bin;%PATH% SET QMAKESPEC=win32-msvc2013 cd %QT_ROOT% CALL configure -prefix %QT_ROOT%\build -icu -opengl dynamic -release -nomake examples -opensource -confirm-license -no-gif -qt-libpng -qt-libjpeg -openssl -qt-pcre -no-cups -no-dbus -skip qtwebkit -skip qtconnectivity -skip qtdoc -skip qtgraphicaleffects -skip qtmultimedia -skip qtsensors -skip qtserialport -skip qtwebkit-examples -skip qtquick1 -skip qt3d nmake nmake install cd .. +``` \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..48eba22 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,59 @@ +project(PlexMediaPlayer CXX C) +cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR) + +cmake_policy(SET CMP0020 NEW) +if (POLICY CMP0058) +cmake_policy(SET CMP0058 NEW) +endif() +cmake_policy(SET CMP0017 NEW) + +option(OPENELEC "Make an OpenELEC build" OFF) + +if (OPENELEC) + add_definitions(-DKONVERGO_OPENELEC=1) + Message(STATUS "Making an OpenELEC build") +endif(OPENELEC) + +option(BUILD_TARGET "Destination target for the build" "") + +if (BUILD_TARGET STREQUAL "RPI") + add_definitions(-DTARGET_RPI=1) + set(RPI_LIBS bcm_host) + Message(STATUS "Build for Raspberry PI target") +endif(BUILD_TARGET STREQUAL "RPI") + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/CMakeModules/") +set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME Core) + +add_definitions(-DQS_LOG_LINE_NUMBERS -DQHTTPSERVER_EXPORT) + +include(DependencyConfiguration) +include(VersionConfiguration) +include(NameConfiguration) +include(utils) +include(QtConfiguration) +include(PlayerConfiguration) +include(InputConfiguration) +include(FindBreakpad) +include(BreakpadSymbols) + +if(APPLE) + include(AppleConfiguration) +elseif(WIN32) + include(Win32Configuration) +elseif(UNIX AND (NOT APPLE)) + include(LinuxConfiguration) +endif(APPLE) + +if(CMAKE_COMPILER_IS_GNUCC) + include(GCCConfiguration) +endif(CMAKE_COMPILER_IS_GNUCC) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNINGS}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNINGS}") + +add_subdirectory(external) +add_subdirectory(src) + +include(CPackConfiguration) diff --git a/CMakeModules/AppleConfiguration.cmake b/CMakeModules/AppleConfiguration.cmake new file mode 100644 index 0000000..98a6afe --- /dev/null +++ b/CMakeModules/AppleConfiguration.cmake @@ -0,0 +1,21 @@ +find_library(FOUNDATION Foundation) +find_library(APPKIT AppKit) +find_library(IOKIT IOKit) +find_library(COCOA Cocoa) +find_Library(CARBON Carbon) +find_library(SECURITY Security) + +set(OS_LIBS ${FOUNDATION} ${APPKIT} ${IOKIT} ${COCOA} ${SECURITY} ${CARBON} spmediakeytap) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.9") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=10.9") +set(WARNINGS "-Wall") + +set(HAVE_UPDATER 1) +find_program(UPDATER_PATH updater HINTS ${CMAKE_FIND_ROOT_PATH}/update_installer/ NO_DEFAULT_PATH) +if(${UPDATER_PATH} MATCHES "UPDATER_PATH-NOTFOUND") + set(HAVE_UPDATER 0) + message(STATUS "will build without the updater") +endif(${UPDATER_PATH} MATCHES "UPDATER_PATH-NOTFOUND") + +set(INSTALL_BIN_DIR .) +set(INSTALL_RESOURCE_DIR Contents/Resources) diff --git a/CMakeModules/BreakpadSymbols.cmake b/CMakeModules/BreakpadSymbols.cmake new file mode 100644 index 0000000..68cd9be --- /dev/null +++ b/CMakeModules/BreakpadSymbols.cmake @@ -0,0 +1,35 @@ +OPTION(GENERATE_SYMBOLS "Should we generate symbols for binaries?" ON) +function(dumpsyms target symfile) + find_program(DUMP_SYMS dump_syms HINTS /usr/bin/ ${DEPENDENCY_ROOT}/bin) + if(GENERATE_SYMBOLS AND NOT DUMP_SYMS) + message(STATUS "dump_syms not found") + endif() + if(GENERATE_SYMBOLS AND DUMP_SYMS) + + if(APPLE) + add_custom_command(TARGET ${target} POST_BUILD + COMMAND dsymutil -o ${MAIN_NAME}.dSYM $ + COMMENT Generating ${MAIN_NAME}.dSYM + BYPRODUCTS ${MAIN_NAME}.dSYM/Contents/Resources/DWARF/${target} ${MAIN_NAME}.dSYM/Contents/Info.plist + ) + endif(APPLE) + + unset(COMPRESS) + find_program(COMPRESS_XZ xz) + find_program(COMPRESS_BZ bzip2) + if(COMPRESS_XZ) + set(COMPRESS_EXT xz) + set(COMPRESS ${COMPRESS_XZ}) + elseif(COMPRESS_BZ) + set(COMPRESS_EXT bz2) + set(COMPRESS ${COMPRESS_BZ}) + endif(COMPRESS_XZ) + + add_custom_command( + TARGET ${target} POST_BUILD + BYPRODUCTS ${symfile}.${COMPRESS_EXT} + COMMAND ${CMAKE_SOURCE_DIR}/scripts/dump-syms.sh "${DUMP_SYMS}" "${COMPRESS}" "$" "${symfile}.${COMPRESS_EXT}" + ) + install(FILES ${symfile}.${COMPRESS_EXT} DESTINATION ${CMAKE_BINARY_DIR}) + endif(GENERATE_SYMBOLS AND DUMP_SYMS) +endfunction() diff --git a/CMakeModules/CPackConfiguration.cmake b/CMakeModules/CPackConfiguration.cmake new file mode 100644 index 0000000..8102d87 --- /dev/null +++ b/CMakeModules/CPackConfiguration.cmake @@ -0,0 +1,63 @@ +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Plex Media Player") +set(CPACK_PACKAGE_VENDOR "Plex") +set(CPACK_PACKAGE_VERSION_MAJOR ${VERSION_MAJOR}) +set(CPACK_PACKAGE_VERSION_MINOR ${VERSION_MINOR}) +set(CPACK_PACKAGE_VERSION_PATCH ${VERSION_NANO}) + +if(APPLE) + set(CPACK_SYSTEM_NAME "macosx-x86_64") +elseif(WIN32) + set(CPACK_SYSTEM_NAME "windows-x86") +else() + set(CPACK_SYSTEM_NAME linux-${CMAKE_HOST_SYSTEM_PROCESSOR}) +endif() +set(CPACK_PACKAGE_FILE_NAME "PlexMediaPlayer-${VERSION_STRING}-${CPACK_SYSTEM_NAME}") +set(CPACK_SOURCE_PACKAGE_FILE_NAME "PlexMediaPlayer-${VERSION_STRING}-src") + +set(CPACK_PACKAGE_INSTALL_DIRECTORY "PlexMediaPlayer") +set(CPACK_STRIP_FILES 1) + +set(CPACK_GENERATOR "ZIP") + +if(WIN32) + list(APPEND CPACK_GENERATOR "IFW") +endif(WIN32) + +# config IFW +set(CPACK_IFW_FRAMEWORK_VERSION 2.0.1) +set(CPACK_IFW_PACKAGE_NAME "Plex Media Player") +set(CPACK_IFW_PACKAGE_START_MENU_DIRECTORY "Plex Media Player") +set(CPACK_IFW_PACKAGE_TITLE "Plex Media Player Installer") +set(CPACK_IFW_PACKAGE_PUBLISHER "Plex") +set(CPACK_IFW_PRODUCT_URL "https://plex.tv") +set(CPACK_IFW_PACKAGE_ICON ${CMAKE_SOURCE_DIR}/bundle/win/Plex.ico) +set(CPACK_IFW_PACKAGE_WINDOW_ICON ${CMAKE_SOURCE_DIR}/resources/images/icon.png) + +if(APPLE) + set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 0) +endif(APPLE) + +configure_file(${CMAKE_SOURCE_DIR}/CMakeModules/CPackGeneratedConfig.cmake.in ${CMAKE_BINARY_DIR}/CPackGeneratedConfig.cmake) +set(CPACK_PROJECT_CONFIG_FILE ${CMAKE_BINARY_DIR}/CPackGeneratedConfig.cmake) + +include(CPack) + +cpack_add_component(Core DISPLAY_NAME "Plex Media Player" DESCRIPTION "Plex Media Player (Core Application)" REQUIRED) + +if(WIN32) + FILE(TO_CMAKE_PATH ${DEPENDENCY_ROOT} tmp) + install(FILES ${tmp}/bin/mpv-1.dll DESTINATION .) + install(FILES ${tmp}/lib/SDL2.dll DESTINATION .) + install(FILES ${tmp}/lib/libcec.dll DESTINATION .) + if(IS_DIRECTORY ${CMAKE_BINARY_DIR}/extradlls) + file(GLOB EXTRADLLS ${CMAKE_BINARY_DIR}/extradlls/*.dll) + install(FILES ${EXTRADLLS} DESTINATION .) + endif() + install(FILES ${CMAKE_SOURCE_DIR}/bundle/win/qt.conf DESTINATION .) + install(FILES ${CMAKE_SOURCE_DIR}/bundle/win/PlexMediaPlayer-angle.bat DESTINATION .) + #add_custom_command(TARGET package POST_BUILD COMMAND ${CMAKE_SOURCE_DIR}/scripts/WindowsSign.cmd ${CPACK_PACKAGE_DIRECTORY}/${CPACK_PACKAGE_FILE_NAME}.exe WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) + # group/component configuration + message(STATUS configure IFW) + include(CPackIFW) + cpack_ifw_configure_component(Core PRIORITY 1 SCRIPT ${CMAKE_SOURCE_DIR}/bundle/win/shortcut.qs) +endif(WIN32) \ No newline at end of file diff --git a/CMakeModules/CPackGeneratedConfig.cmake.in b/CMakeModules/CPackGeneratedConfig.cmake.in new file mode 100644 index 0000000..91d617e --- /dev/null +++ b/CMakeModules/CPackGeneratedConfig.cmake.in @@ -0,0 +1,3 @@ +if(CPACK_GENERATOR MATCHES "IFW") + set(CMAKE_EXECUTABLE_SUFFIX ".exe") +endif() diff --git a/CMakeModules/CompleteBundle.cmake b/CMakeModules/CompleteBundle.cmake new file mode 100644 index 0000000..949577d --- /dev/null +++ b/CMakeModules/CompleteBundle.cmake @@ -0,0 +1,17 @@ +if(APPLE) + set(SCRIPT CompleteBundleOSX) +elseif(WIN32) + set(SCRIPT CompleteBundleWin) +endif(APPLE) + +option(CODE_SIGN "code sign the app" OFF) +if(CODE_SIGN) + set(DO_SIGN 1) +else(CODE_SIGN) + set(DO_SIGN 0) +endif(CODE_SIGN) + +if(SCRIPT) + configure_file(${CMAKE_SOURCE_DIR}/CMakeModules/${SCRIPT}.cmake.in ${SCRIPT}.cmake @ONLY) + install(SCRIPT ${CMAKE_CURRENT_BINARY_DIR}/${SCRIPT}.cmake) +endif(SCRIPT) diff --git a/CMakeModules/CompleteBundleOSX.cmake.in b/CMakeModules/CompleteBundleOSX.cmake.in new file mode 100644 index 0000000..e9bf052 --- /dev/null +++ b/CMakeModules/CompleteBundleOSX.cmake.in @@ -0,0 +1,69 @@ +set(app "@EXE@") + +list(APPEND BINS "Contents/Resources/updater") +list(APPEND BINS "Contents/Resources/@HELPER_NAME@") + +set(args ${app}) +list(APPEND args "-verbose=2") +list(APPEND args "-qmldir=@SOURCE_ROOT@/src/ui") +foreach(BIN ${BINS}) + list(APPEND args "-executable=${app}/${BIN}") +endforeach(BIN ${BINS}) + +execute_process( + COMMAND @QTROOT@/bin/macdeployqt ${args} + WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX} +) + +#set(ENTITLEMENTS --entitlements @SOURCE_ROOT@/bundle/osx/Konvergo.entitlements) +set(CODESIGN codesign ${ENTITLEMENTS} --force --sign "Developer ID Application: Plex Inc.") + +macro(sign_binary BIN) + message(STATUS "Signing: ${BIN}") + execute_process( + COMMAND ${CODESIGN} "${BIN}" + RESULT_VARIABLE result + ) + if(NOT ${result} EQUAL 0) + message(FATAL_ERROR "Failed to sign ${BIN}") + endif(NOT ${result} EQUAL 0) +endmacro(sign_binary BIN) + +if(@DO_SIGN@) + + # we need to sign the webengine helper before the framework + # add --entitlements @SOURCE_ROOT@/bundle/osx/WebEngine.entitlements when we want to sandbox + set(WEB_PROC "${CMAKE_INSTALL_PREFIX}/${app}/Contents/Frameworks/QtWebEngineCore.framework/Versions/Current/Helpers/QtWebEngineProcess.app") + sign_binary(${WEB_PROC}) + + file(GLOB_RECURSE LIBS + FOLLOW_SYMLINKS + "${CMAKE_INSTALL_PREFIX}/${app}/*.dylib" + ) + file(GLOB FRAMEWORKS FOLLOW_SYMLINKS LIST_DIRECTORIES true "${CMAKE_INSTALL_PREFIX}/${app}/Contents/Frameworks/*") + foreach(LIB ${LIBS} ${FRAMEWORKS}) + sign_binary(${LIB}) + endforeach(LIB ${LIBS}) + + foreach(BIN ${BINS}) + sign_binary(${CMAKE_INSTALL_PREFIX}/${app}/${BIN}) + endforeach(BIN ${BINS}) + + sign_binary(${CMAKE_INSTALL_PREFIX}/${app}) + + message("Verifing signature") + execute_process( + COMMAND codesign --verbose=4 --verify "${CMAKE_INSTALL_PREFIX}/@EXE@" + RESULT_VARIABLE result + ) + if(NOT ${result} EQUAL 0) + message(FATAL_ERROR "Failed to verify binary!") + endif(NOT ${result} EQUAL 0) + execute_process( + COMMAND spctl --verbose=4 --assess --type execute "${CMAKE_INSTALL_PREFIX}/@EXE@" + RESULT_VARIABLE result + ) + if(NOT ${result} EQUAL 0) + message(FATAL_ERROR "Failed to verify binary!") + endif(NOT ${result} EQUAL 0) +endif(@DO_SIGN@) \ No newline at end of file diff --git a/CMakeModules/CompleteBundleWin.cmake.in b/CMakeModules/CompleteBundleWin.cmake.in new file mode 100644 index 0000000..8abe8a6 --- /dev/null +++ b/CMakeModules/CompleteBundleWin.cmake.in @@ -0,0 +1,10 @@ +execute_process(COMMAND @QTROOT@/bin/windeployqt.exe --qmldir @SOURCE_ROOT@/src/ui ${CMAKE_INSTALL_PREFIX}/PlexMediaPlayer.exe) + +if(@DO_SIGN@) + file(GLOB_RECURSE EXES ${CMAKE_INSTALL_PREFIX}/*.exe ${CMAKE_INSTALL_PREFIX}/*.dll) + set(ENV{errorlevel} 1) + foreach(e ${EXES}) + message("-- Signing: ${CMAKE_SOURCE_DIR}/scripts/WindowsSign.cmd ${e}") + execute_process(COMMAND ${CMAKE_SOURCE_DIR}/scripts/WindowsSign.cmd "${e}" RESULT_VARIABLE RES) + endforeach() +endif() \ No newline at end of file diff --git a/CMakeModules/DependencyConfiguration.cmake b/CMakeModules/DependencyConfiguration.cmake new file mode 100644 index 0000000..639e4cc --- /dev/null +++ b/CMakeModules/DependencyConfiguration.cmake @@ -0,0 +1,28 @@ +find_package(PkgConfig) +option(DISABLE_BUNDLED_DEPS "Disable the bundled deps on certain platforms" OFF) + +if(APPLE AND NOT DISABLE_BUNDLED_DEPS) + set(DEFAULT_ROOT "${CMAKE_SOURCE_DIR}/dependencies/konvergo-depends-darwin-x86_64-release") + set(DEPENDENCY_ROOT ${DEFAULT_ROOT} CACHE PATH "Path where the deps are located") +endif(APPLE AND NOT DISABLE_BUNDLED_DEPS) + +if(DEPENDENCY_ROOT) + message(STATUS "Going to use bundled deps in directory: ${DEPENDENCY_ROOT}") + set(CMAKE_FIND_ROOT_PATH ${DEPENDENCY_ROOT}) + set(CMAKE_PREFIX_PATH ${DEPENDENCY_ROOT}) + set(ENV{PKG_CONFIG_LIBDIR} ${CMAKE_FIND_ROOT_PATH}/lib/pkgconfig) + set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH TRUE) + include_directories(${CMAKE_FIND_ROOT_PATH}/include) +else(DEPENDENCY_ROOT) + message(STATUS "Not using bundled deps") +endif(DEPENDENCY_ROOT) + +find_package(Threads) + +# on windows we need to download the updater binary seperately +if(WIN32) + file(DOWNLOAD https://nightlies.plex.tv/directdl/plex-dependencies/konvergo-qt/updater.exe ${CMAKE_BINARY_DIR}/updater.exe + SHOW_PROGRESS + EXPECTED_HASH SHA1=d3b4f70d6542fa42c8edd2b9b93fd0916bf20f07 + TLS_VERIFY OFF) +endif(WIN32) \ No newline at end of file diff --git a/CMakeModules/FindBreakpad.cmake b/CMakeModules/FindBreakpad.cmake new file mode 100644 index 0000000..a7e0b50 --- /dev/null +++ b/CMakeModules/FindBreakpad.cmake @@ -0,0 +1,69 @@ +############################################################################### +# CMake module to search for the mpv libraries. +# +# WARNING: This module is experimental work in progress. +# +# Based one FindVLC.cmake by: +# Copyright (c) 2011 Michael Jansen +# Modified by Tobias Hieta +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. +# +############################################################################### + +# +### Global Configuration Section +# +SET(_BREAKPAD_REQUIRED_VARS BREAKPAD_INCLUDE_DIR BREAKPAD_LIBRARY) + +# +### BREAKPAD uses pkgconfig. +# +if(PKG_CONFIG_FOUND) + pkg_check_modules(PC_BREAKPAD QUIET breakpad-client) +endif(PKG_CONFIG_FOUND) + +# +### Look for the include files. +# +find_path( + BREAKPAD_INCLUDE_DIR + NAMES google_breakpad/common/breakpad_types.h + PATH_SUFFIXES breakpad + HINTS + ${PC_BREAKPAD_INCLUDEDIR} + ${PC_BREAKPAD_INCLUDE_DIRS} # Unused for BREAKPAD but anyway + DOC "BREAKPAD include directory" + ) +mark_as_advanced(BREAKPAD_INCLUDE_DIR) +set(BREAKPAD_INCLUDE_DIRS ${BREAKPAD_INCLUDE_DIR}) + +# +### Look for the libraries (BREAKPAD and BREAKPADsore) +# +find_library( + BREAKPAD_LIBRARY + NAMES breakpad_client + HINTS + ${PC_BREAKPAD_LIBDIR} + ${PC_BREAKPAD_LIBRARY_DIRS} # Unused for BREAKPAD but anyway + PATH_SUFFIXES lib${LIB_SUFFIX} + ) +get_filename_component(_BREAKPAD_LIBRARY_DIR ${BREAKPAD_LIBRARY} PATH) +mark_as_advanced(BREAKPAD_LIBRARY) + +set(BREAKPAD_LIBRARY_DIRS _BREAKPAD_CORE_LIBRARY_DIR _BREAKPAD_LIBRARY_DIR) +list(REMOVE_DUPLICATES BREAKPAD_LIBRARY_DIRS) +mark_as_advanced(BREAKPAD_LIBRARY_DIRS) + +# +### Check if everything was found and if the version is sufficient. +# +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + BREAKPAD + REQUIRED_VARS ${_BREAKPAD_REQUIRED_VARS} + VERSION_VAR BREAKPAD_VERSION_STRING + ) + diff --git a/CMakeModules/FindCEC.cmake b/CMakeModules/FindCEC.cmake new file mode 100644 index 0000000..2c71c07 --- /dev/null +++ b/CMakeModules/FindCEC.cmake @@ -0,0 +1,26 @@ +if(CEC_INCLUDE_DIR) + # Already in cache, be silent + set(CEC_FIND_QUIETLY TRUE) +endif(CEC_INCLUDE_DIR) + +if (PKG_CONFIG_FOUND) + pkg_check_modules(_CEC QUIET libcec>=2.0) +endif (PKG_CONFIG_FOUND) + +Find_Path(CEC_INCLUDE_DIR + NAMES cec.h + PATHS /usr/include usr/local/include + PATH_SUFFIXES libcec + HINTS ${_CEC_INCLUDEDIR} +) + +Find_Library(CEC_LIBRARY + NAMES cec + PATHS /usr/lib usr/local/lib + HINTS ${_CEC_LIBDIR} +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(CEC DEFAULT_MSG CEC_LIBRARY CEC_INCLUDE_DIR) + + diff --git a/CMakeModules/FindDL.cmake b/CMakeModules/FindDL.cmake new file mode 100644 index 0000000..8d1df7b --- /dev/null +++ b/CMakeModules/FindDL.cmake @@ -0,0 +1,17 @@ +# - find where dlopen and friends are located. +# DL_FOUND - system has dynamic linking interface available +# DL_INCLUDE_DIR - where dlfcn.h is located. +# DL_LIBRARIES - libraries needed to use dlopen + +include(CheckFunctionExists) + +find_path(DL_INCLUDE_DIR NAMES dlfcn.h) +find_library(DL_LIBRARIES NAMES dl) +if(DL_LIBRARIES) + set(DL_FOUND) +else(DL_LIBRARIES) + check_function_exists(dlopen DL_FOUND) + # If dlopen can be found without linking in dl then dlopen is part + # of libc, so don't need to link extra libs. + set(DL_LIBRARIES "") +endif(DL_LIBRARIES) diff --git a/CMakeModules/FindGLES2.cmake b/CMakeModules/FindGLES2.cmake new file mode 100644 index 0000000..d643e8a --- /dev/null +++ b/CMakeModules/FindGLES2.cmake @@ -0,0 +1,73 @@ +# Copyright (c) 2012, Guillermo A. Amaral B. (gamaral) . +# All rights reserved. +# +# This file is part of Marshmallow Game Engine. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +# EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# The views and conclusions contained in the software and documentation are +# those of the authors and should not be interpreted as representing official +# policies, either expressed or implied, of the project as a whole. +# +############################################################################### +# Find GLES2 +############################################################################### +# +# GLES2_FOUND +# GLES2_INCLUDE_DIR +# GLES2_LIBRARY +# +############################################################################### + +find_path(GLES2_INCLUDE_DIR GLES2/gl2.h + HINTS $ENV{GLES2DIR} + PATH_SUFFIXES include + PATHS ~/Library/Frameworks + /Library/Frameworks + /usr/local + /usr + /usr/X11R6 + /opt/local + /opt/vc + /opt +) + +find_library(GLES2_LIBRARY + GLESv2 + HINTS $ENV{GLES2DIR} + PATH_SUFFIXES lib64 lib + PATHS ~/Library/Frameworks + /Library/Frameworks + /usr/local + /usr + /usr/X11R6 + /opt/local + /opt/vc + /opt +) + +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(GLES2 DEFAULT_MSG GLES2_LIBRARY) + +mark_as_advanced(GLES2_LIBRARY GLES2_INCLUDE_DIR) + +set(OPENGL_FOUND ${GLES2_FOUND}) diff --git a/CMakeModules/FindICU.cmake b/CMakeModules/FindICU.cmake new file mode 100644 index 0000000..c246bb6 --- /dev/null +++ b/CMakeModules/FindICU.cmake @@ -0,0 +1,311 @@ +# This module can find the International Components for Unicode (ICU) Library +# +# Requirements: +# - CMake >= 2.8.3 (for new version of find_package_handle_standard_args) +# +# The following variables will be defined for your use: +# - ICU_FOUND : were all of your specified components found (include dependencies)? +# - ICU_INCLUDE_DIRS : ICU include directory +# - ICU_LIBRARIES : ICU libraries +# - ICU_VERSION : complete version of ICU (x.y.z) +# - ICU_MAJOR_VERSION : major version of ICU +# - ICU_MINOR_VERSION : minor version of ICU +# - ICU_PATCH_VERSION : patch version of ICU +# - ICU__FOUND : were found? (FALSE for non specified component if it is not a dependency) +# +# For windows or non standard installation, define ICU_ROOT variable to point to the root installation of ICU. Two ways: +# - run cmake with -DICU_ROOT= +# - define an environment variable with the same name before running cmake +# With cmake-gui, before pressing "Configure": +# 1) Press "Add Entry" button +# 2) Add a new entry defined as: +# - Name: ICU_ROOT +# - Type: choose PATH in the selection list +# - Press "..." button and select the root installation of ICU +# +# Example Usage: +# +# 1. Copy this file in the root of your project source directory +# 2. Then, tell CMake to search this non-standard module in your project directory by adding to your CMakeLists.txt: +# set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}) +# 3. Finally call find_package() once, here are some examples to pick from +# +# Require ICU 4.4 or later +# find_package(ICU 4.4 REQUIRED) +# +# if(ICU_FOUND) +# include_directories(${ICU_INCLUDE_DIRS}) +# add_executable(myapp myapp.c) +# target_link_libraries(myapp ${ICU_LIBRARIES}) +# endif(ICU_FOUND) + +#============================================================================= +# Copyright (c) 2011-2013, julp +# +# Distributed under the OSI-approved BSD License +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +#============================================================================= + +########## Private ########## +if(NOT DEFINED ICU_PUBLIC_VAR_NS) + set(ICU_PUBLIC_VAR_NS "ICU") # Prefix for all ICU relative public variables +endif(NOT DEFINED ICU_PUBLIC_VAR_NS) +if(NOT DEFINED ICU_PRIVATE_VAR_NS) + set(ICU_PRIVATE_VAR_NS "_${ICU_PUBLIC_VAR_NS}") # Prefix for all ICU relative internal variables +endif(NOT DEFINED ICU_PRIVATE_VAR_NS) +if(NOT DEFINED PC_ICU_PRIVATE_VAR_NS) + set(PC_ICU_PRIVATE_VAR_NS "_PC${ICU_PRIVATE_VAR_NS}") # Prefix for all pkg-config relative internal variables +endif(NOT DEFINED PC_ICU_PRIVATE_VAR_NS) + +function(icudebug _VARNAME) + if(${ICU_PUBLIC_VAR_NS}_DEBUG) + if(DEFINED ${ICU_PUBLIC_VAR_NS}_${_VARNAME}) + message("${ICU_PUBLIC_VAR_NS}_${_VARNAME} = ${${ICU_PUBLIC_VAR_NS}_${_VARNAME}}") + else(DEFINED ${ICU_PUBLIC_VAR_NS}_${_VARNAME}) + message("${ICU_PUBLIC_VAR_NS}_${_VARNAME} = ") + endif(DEFINED ${ICU_PUBLIC_VAR_NS}_${_VARNAME}) + endif(${ICU_PUBLIC_VAR_NS}_DEBUG) +endfunction(icudebug) + +set(${ICU_PRIVATE_VAR_NS}_ROOT "") +if(DEFINED ENV{ICU_ROOT}) + set(${ICU_PRIVATE_VAR_NS}_ROOT "$ENV{ICU_ROOT}") +endif(DEFINED ENV{ICU_ROOT}) +if (DEFINED ICU_ROOT) + set(${ICU_PRIVATE_VAR_NS}_ROOT "${ICU_ROOT}") +endif(DEFINED ICU_ROOT) + +set(${ICU_PRIVATE_VAR_NS}_BIN_SUFFIXES ) +set(${ICU_PRIVATE_VAR_NS}_LIB_SUFFIXES ) +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + list(APPEND ${ICU_PRIVATE_VAR_NS}_BIN_SUFFIXES "bin64") + list(APPEND ${ICU_PRIVATE_VAR_NS}_LIB_SUFFIXES "lib64") +endif(CMAKE_SIZEOF_VOID_P EQUAL 8) +list(APPEND ${ICU_PRIVATE_VAR_NS}_BIN_SUFFIXES "bin") +list(APPEND ${ICU_PRIVATE_VAR_NS}_LIB_SUFFIXES "lib") + +set(${ICU_PRIVATE_VAR_NS}_COMPONENTS ) +# ... +macro(icu_declare_component _NAME) + list(APPEND ${ICU_PRIVATE_VAR_NS}_COMPONENTS ${_NAME}) + set("${ICU_PRIVATE_VAR_NS}_COMPONENTS_${_NAME}" ${ARGN}) +endmacro(icu_declare_component) + +icu_declare_component(data icudata) +icu_declare_component(uc icuuc) # Common and Data libraries +icu_declare_component(i18n icui18n icuin) # Internationalization library +icu_declare_component(io icuio ustdio) # Stream and I/O Library +icu_declare_component(le icule) # Layout library +icu_declare_component(lx iculx) # Paragraph Layout library + +########## Public ########## +set(${ICU_PUBLIC_VAR_NS}_FOUND TRUE) +set(${ICU_PUBLIC_VAR_NS}_LIBRARIES ) +set(${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS ) +set(${ICU_PUBLIC_VAR_NS}_C_FLAGS "") +set(${ICU_PUBLIC_VAR_NS}_CXX_FLAGS "") +set(${ICU_PUBLIC_VAR_NS}_CPP_FLAGS "") +set(${ICU_PUBLIC_VAR_NS}_C_SHARED_FLAGS "") +set(${ICU_PUBLIC_VAR_NS}_CXX_SHARED_FLAGS "") +set(${ICU_PUBLIC_VAR_NS}_CPP_SHARED_FLAGS "") +foreach(${ICU_PRIVATE_VAR_NS}_COMPONENT ${${ICU_PRIVATE_VAR_NS}_COMPONENTS}) + string(TOUPPER "${${ICU_PRIVATE_VAR_NS}_COMPONENT}" ${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT) + set("${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_FOUND" FALSE) # may be done in the icu_declare_component macro +endforeach(${ICU_PRIVATE_VAR_NS}_COMPONENT) + +# Check components +if(NOT ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS) # uc required at least + set(${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS uc) +else(NOT ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS) + list(APPEND ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS uc) + list(REMOVE_DUPLICATES ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS) + foreach(${ICU_PRIVATE_VAR_NS}_COMPONENT ${${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS}) + if(NOT DEFINED ${ICU_PRIVATE_VAR_NS}_COMPONENTS_${${ICU_PRIVATE_VAR_NS}_COMPONENT}) + message(FATAL_ERROR "Unknown ICU component: ${${ICU_PRIVATE_VAR_NS}_COMPONENT}") + endif(NOT DEFINED ${ICU_PRIVATE_VAR_NS}_COMPONENTS_${${ICU_PRIVATE_VAR_NS}_COMPONENT}) + endforeach(${ICU_PRIVATE_VAR_NS}_COMPONENT) +endif(NOT ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS) + +# Includes +find_path( + ${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS + NAMES unicode/utypes.h utypes.h + HINTS ${${ICU_PRIVATE_VAR_NS}_ROOT} + PATH_SUFFIXES "include" + DOC "Include directories for ICU" +) + +if(${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS) + ########## ########## + if(EXISTS "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/unicode/uvernum.h") # ICU >= 4 + file(READ "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/unicode/uvernum.h" ${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS) + elseif(EXISTS "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/unicode/uversion.h") # ICU [2;4[ + file(READ "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/unicode/uversion.h" ${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS) + elseif(EXISTS "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/unicode/utypes.h") # ICU [1.4;2[ + file(READ "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/unicode/utypes.h" ${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS) + elseif(EXISTS "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/utypes.h") # ICU 1.3 + file(READ "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS}/utypes.h" ${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS) + else() + message(FATAL_ERROR "ICU version header not found") + endif() + + if(${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS MATCHES ".*# *define *ICU_VERSION *\"([0-9]+)\".*") # ICU 1.3 + # [1.3;1.4[ as #define ICU_VERSION "3" (no patch version, ie all 1.3.X versions will be detected as 1.3.0) + set(${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION "1") + set(${ICU_PUBLIC_VAR_NS}_MINOR_VERSION "${CMAKE_MATCH_1}") + set(${ICU_PUBLIC_VAR_NS}_PATCH_VERSION "0") + elseif(${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS MATCHES ".*# *define *U_ICU_VERSION_MAJOR_NUM *([0-9]+).*") + # + # Since version 4.9.1, ICU release version numbering was totaly changed, see: + # - http://site.icu-project.org/download/49 + # - http://userguide.icu-project.org/design#TOC-Version-Numbers-in-ICU + # + set(${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION "${CMAKE_MATCH_1}") + string(REGEX REPLACE ".*# *define *U_ICU_VERSION_MINOR_NUM *([0-9]+).*" "\\1" ${ICU_PUBLIC_VAR_NS}_MINOR_VERSION "${${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS}") + string(REGEX REPLACE ".*# *define *U_ICU_VERSION_PATCHLEVEL_NUM *([0-9]+).*" "\\1" ${ICU_PUBLIC_VAR_NS}_PATCH_VERSION "${${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS}") + elseif(${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS MATCHES ".*# *define *U_ICU_VERSION *\"(([0-9]+)(\\.[0-9]+)*)\".*") # ICU [1.4;1.8[ + # [1.4;1.8[ as #define U_ICU_VERSION "1.4.1.2" but it seems that some 1.4.1(?:\.\d)? have releasing error and appears as 1.4.0 + set(${ICU_PRIVATE_VAR_NS}_FULL_VERSION "${CMAKE_MATCH_1}") # copy CMAKE_MATCH_1, no longer valid on the following if + if(${ICU_PRIVATE_VAR_NS}_FULL_VERSION MATCHES "^([0-9]+)\\.([0-9]+)$") + set(${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION "${CMAKE_MATCH_1}") + set(${ICU_PUBLIC_VAR_NS}_MINOR_VERSION "${CMAKE_MATCH_2}") + set(${ICU_PUBLIC_VAR_NS}_PATCH_VERSION "0") + elseif(${ICU_PRIVATE_VAR_NS}_FULL_VERSION MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)") + set(${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION "${CMAKE_MATCH_1}") + set(${ICU_PUBLIC_VAR_NS}_MINOR_VERSION "${CMAKE_MATCH_2}") + set(${ICU_PUBLIC_VAR_NS}_PATCH_VERSION "${CMAKE_MATCH_3}") + endif() + else() + message(FATAL_ERROR "failed to detect ICU version") + endif() + set(${ICU_PUBLIC_VAR_NS}_VERSION "${${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION}.${${ICU_PUBLIC_VAR_NS}_MINOR_VERSION}.${${ICU_PUBLIC_VAR_NS}_PATCH_VERSION}") + ########## ########## + + # Check dependencies (implies pkg-config) + if(PKG_CONFIG_FOUND) + set(${ICU_PRIVATE_VAR_NS}_COMPONENTS_DUP ${${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS}) + foreach(${ICU_PRIVATE_VAR_NS}_COMPONENT ${${ICU_PRIVATE_VAR_NS}_COMPONENTS_DUP}) + pkg_check_modules(PC_ICU_PRIVATE_VAR_NS "icu-${${ICU_PRIVATE_VAR_NS}_COMPONENT}" QUIET) + + if(${PC_ICU_PRIVATE_VAR_NS}_FOUND) + foreach(${PC_ICU_PRIVATE_VAR_NS}_LIBRARY ${PC_ICU_LIBRARIES}) + string(REGEX REPLACE "^icu" "" ${PC_ICU_PRIVATE_VAR_NS}_STRIPPED_LIBRARY ${${PC_ICU_PRIVATE_VAR_NS}_LIBRARY}) + list(APPEND ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS ${${PC_ICU_PRIVATE_VAR_NS}_STRIPPED_LIBRARY}) + endforeach(${PC_ICU_PRIVATE_VAR_NS}_LIBRARY) + endif(${PC_ICU_PRIVATE_VAR_NS}_FOUND) + endforeach(${ICU_PRIVATE_VAR_NS}_COMPONENT) + list(REMOVE_DUPLICATES ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS) + endif(PKG_CONFIG_FOUND) + + # Check libraries + foreach(${ICU_PRIVATE_VAR_NS}_COMPONENT ${${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS}) + set(${ICU_PRIVATE_VAR_NS}_POSSIBLE_RELEASE_NAMES ) + set(${ICU_PRIVATE_VAR_NS}_POSSIBLE_DEBUG_NAMES ) + foreach(${ICU_PRIVATE_VAR_NS}_BASE_NAME ${${ICU_PRIVATE_VAR_NS}_COMPONENTS_${${ICU_PRIVATE_VAR_NS}_COMPONENT}}) + list(APPEND ${ICU_PRIVATE_VAR_NS}_POSSIBLE_RELEASE_NAMES "${${ICU_PRIVATE_VAR_NS}_BASE_NAME}") + list(APPEND ${ICU_PRIVATE_VAR_NS}_POSSIBLE_DEBUG_NAMES "${${ICU_PRIVATE_VAR_NS}_BASE_NAME}d") + list(APPEND ${ICU_PRIVATE_VAR_NS}_POSSIBLE_RELEASE_NAMES "${${ICU_PRIVATE_VAR_NS}_BASE_NAME}${ICU_MAJOR_VERSION}${ICU_MINOR_VERSION}") + list(APPEND ${ICU_PRIVATE_VAR_NS}_POSSIBLE_DEBUG_NAMES "${${ICU_PRIVATE_VAR_NS}_BASE_NAME}${ICU_MAJOR_VERSION}${ICU_MINOR_VERSION}d") + endforeach(${ICU_PRIVATE_VAR_NS}_BASE_NAME) + + find_library( + ${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT} + NAMES ${${ICU_PRIVATE_VAR_NS}_POSSIBLE_RELEASE_NAMES} + HINTS ${${ICU_PRIVATE_VAR_NS}_ROOT} + PATH_SUFFIXES ${_ICU_LIB_SUFFIXES} + DOC "Release libraries for ICU" + ) + find_library( + ${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT} + NAMES ${${ICU_PRIVATE_VAR_NS}_POSSIBLE_DEBUG_NAMES} + HINTS ${${ICU_PRIVATE_VAR_NS}_ROOT} + PATH_SUFFIXES ${_ICU_LIB_SUFFIXES} + DOC "Debug libraries for ICU" + ) + + string(TOUPPER "${${ICU_PRIVATE_VAR_NS}_COMPONENT}" ${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT) + if(NOT ${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT} AND NOT ${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT}) # both not found + set("${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_FOUND" FALSE) + set("${ICU_PUBLIC_VAR_NS}_FOUND" FALSE) + else(NOT ${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT} AND NOT ${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT}) # one or both found + set("${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_FOUND" TRUE) + if(NOT ${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT}) # release not found => we are in debug + set(${ICU_PRIVATE_VAR_NS}_LIB_${${ICU_PRIVATE_VAR_NS}_COMPONENT} "${${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT}}") + elseif(NOT ${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT}) # debug not found => we are in release + set(${ICU_PRIVATE_VAR_NS}_LIB_${${ICU_PRIVATE_VAR_NS}_COMPONENT} "${${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT}}") + else() # both found + set( + ${ICU_PRIVATE_VAR_NS}_LIB_${${ICU_PRIVATE_VAR_NS}_COMPONENT} + optimized ${${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT}} + debug ${${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT}} + ) + endif() + list(APPEND ${ICU_PUBLIC_VAR_NS}_LIBRARIES ${${ICU_PRIVATE_VAR_NS}_LIB_${${ICU_PRIVATE_VAR_NS}_COMPONENT}}) + endif(NOT ${ICU_PRIVATE_VAR_NS}_LIB_RELEASE_${${ICU_PRIVATE_VAR_NS}_COMPONENT} AND NOT ${ICU_PRIVATE_VAR_NS}_LIB_DEBUG_${${ICU_PRIVATE_VAR_NS}_COMPONENT}) + endforeach(${ICU_PRIVATE_VAR_NS}_COMPONENT) + + # Try to find out compiler flags + find_program(${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE icu-config HINTS ${${ICU_PRIVATE_VAR_NS}_ROOT}) + if(${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE) + execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cflags OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_C_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cxxflags OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_CXX_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cppflags OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_CPP_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE) + + execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cflags-dynamic OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_C_SHARED_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cxxflags-dynamic OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_CXX_SHARED_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cppflags-dynamic OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_CPP_SHARED_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE) + endif(${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE) + + # Check find_package arguments + include(FindPackageHandleStandardArgs) + if(${ICU_PUBLIC_VAR_NS}_FIND_REQUIRED AND NOT ${ICU_PUBLIC_VAR_NS}_FIND_QUIETLY) + find_package_handle_standard_args( + ${ICU_PUBLIC_VAR_NS} + REQUIRED_VARS ${ICU_PUBLIC_VAR_NS}_LIBRARIES ${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS + VERSION_VAR ${ICU_PUBLIC_VAR_NS}_VERSION + ) + else(${ICU_PUBLIC_VAR_NS}_FIND_REQUIRED AND NOT ${ICU_PUBLIC_VAR_NS}_FIND_QUIETLY) + find_package_handle_standard_args(${ICU_PUBLIC_VAR_NS} "ICU not found" ${ICU_PUBLIC_VAR_NS}_LIBRARIES ${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS) + endif(${ICU_PUBLIC_VAR_NS}_FIND_REQUIRED AND NOT ${ICU_PUBLIC_VAR_NS}_FIND_QUIETLY) +else(${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS) + if(${ICU_PUBLIC_VAR_NS}_FIND_REQUIRED AND NOT ${ICU_PUBLIC_VAR_NS}_FIND_QUIETLY) + message(FATAL_ERROR "Could not find ICU include directory") + endif(${ICU_PUBLIC_VAR_NS}_FIND_REQUIRED AND NOT ${ICU_PUBLIC_VAR_NS}_FIND_QUIETLY) +endif(${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS) + +mark_as_advanced( + ${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS + ${ICU_PUBLIC_VAR_NS}_LIBRARIES +) + +# IN (args) +icudebug("FIND_COMPONENTS") +icudebug("FIND_REQUIRED") +icudebug("FIND_QUIETLY") +icudebug("FIND_VERSION") +# OUT +# Found +icudebug("FOUND") +icudebug("UC_FOUND") +icudebug("I18N_FOUND") +icudebug("IO_FOUND") +icudebug("LE_FOUND") +icudebug("LX_FOUND") +icudebug("DATA_FOUND") +# Flags +icudebug("C_FLAGS") +icudebug("CPP_FLAGS") +icudebug("CXX_FLAGS") +icudebug("C_SHARED_FLAGS") +icudebug("CPP_SHARED_FLAGS") +icudebug("CXX_SHARED_FLAGS") +# Linking +icudebug("INCLUDE_DIRS") +icudebug("LIBRARIES") +# Version +icudebug("MAJOR_VERSION") +icudebug("MINOR_VERSION") +icudebug("PATCH_VERSION") +icudebug("VERSION") diff --git a/CMakeModules/FindIconv.cmake b/CMakeModules/FindIconv.cmake new file mode 100644 index 0000000..24cdae3 --- /dev/null +++ b/CMakeModules/FindIconv.cmake @@ -0,0 +1,57 @@ +# - Try to find Iconv +# Once done this will define +# +# ICONV_FOUND - system has Iconv +# ICONV_INCLUDE_DIR - the Iconv include directory +# ICONV_LIBRARIES - Link these to use Iconv +# ICONV_SECOND_ARGUMENT_IS_CONST - the second argument for iconv() is const +# +include(CheckCXXSourceCompiles) + +IF (ICONV_INCLUDE_DIR AND ICONV_LIBRARIES) + # Already in cache, be silent + SET(ICONV_FIND_QUIETLY TRUE) +ENDIF (ICONV_INCLUDE_DIR AND ICONV_LIBRARIES) + +FIND_PATH(ICONV_INCLUDE_DIR iconv.h) + +FIND_LIBRARY(ICONV_LIBRARIES NAMES iconv libiconv libiconv-2 c) + +IF(ICONV_INCLUDE_DIR AND ICONV_LIBRARIES) + SET(ICONV_FOUND TRUE) +ENDIF(ICONV_INCLUDE_DIR AND ICONV_LIBRARIES) + +set(CMAKE_REQUIRED_INCLUDES ${ICONV_INCLUDE_DIR}) +set(CMAKE_REQUIRED_LIBRARIES ${ICONV_LIBRARIES}) +IF(ICONV_FOUND) + check_cxx_source_compiles(" + #include + int main(){ + iconv_t conv = 0; + const char* in = 0; + size_t ilen = 0; + char* out = 0; + size_t olen = 0; + iconv(conv, &in, &ilen, &out, &olen); + return 0; + } +" ICONV_SECOND_ARGUMENT_IS_CONST ) +ENDIF(ICONV_FOUND) +set(CMAKE_REQUIRED_INCLUDES) +set(CMAKE_REQUIRED_LIBRARIES) + +IF(ICONV_FOUND) + IF(NOT ICONV_FIND_QUIETLY) + MESSAGE(STATUS "Found Iconv: ${ICONV_LIBRARIES}") + ENDIF(NOT ICONV_FIND_QUIETLY) +ELSE(ICONV_FOUND) + IF(Iconv_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find Iconv") + ENDIF(Iconv_FIND_REQUIRED) +ENDIF(ICONV_FOUND) + +MARK_AS_ADVANCED( + ICONV_INCLUDE_DIR + ICONV_LIBRARIES + ICONV_SECOND_ARGUMENT_IS_CONST +) diff --git a/CMakeModules/FindMPV.cmake b/CMakeModules/FindMPV.cmake new file mode 100644 index 0000000..bea1003 --- /dev/null +++ b/CMakeModules/FindMPV.cmake @@ -0,0 +1,80 @@ +############################################################################### +# CMake module to search for the mpv libraries. +# +# WARNING: This module is experimental work in progress. +# +# Based one FindVLC.cmake by: +# Copyright (c) 2011 Michael Jansen +# Modified by Tobias Hieta +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. +# +############################################################################### + +# +### Global Configuration Section +# +SET(_MPV_REQUIRED_VARS MPV_INCLUDE_DIR MPV_LIBRARY) + +# +### MPV uses pkgconfig. +# +if(PKG_CONFIG_FOUND) + pkg_check_modules(PC_MPV QUIET mpv) +endif(PKG_CONFIG_FOUND) + +if(PC_MPV_FOUND) + # + ### Look for the include files. + # + find_path( + MPV_INCLUDE_DIR + NAMES mpv/client.h + HINTS + ${PC_MPV_INCLUDEDIR} + ${PC_MPV_INCLUDE_DIRS} # Unused for MPV but anyway + DOC "MPV include directory" + ) + + # + ### Look for the libraries + # + set(_MPV_LIBRARY_NAMES mpv) + if(PC_MPV_LIBRARIES) + set(_MPV_LIBRARY_NAMES ${PC_MPV_LIBRARIES}) + endif(PC_MPV_LIBRARIES) + + foreach(l ${_MPV_LIBRARY_NAMES}) + find_library( + MPV_LIBRARY_${l} + NAMES ${l} + HINTS + ${PC_MPV_LIBDIR} + ${PC_MPV_LIBRARY_DIRS} # Unused for MPV but anyway + PATH_SUFFIXES lib${LIB_SUFFIX} + ) + list(APPEND MPV_LIBRARY ${MPV_LIBRARY_${l}}) + endforeach() + get_filename_component(_MPV_LIBRARY_DIR ${MPV_LIBRARY_mpv} PATH) + mark_as_advanced(MPV_LIBRARY) + + set(MPV_LIBRARY_DIRS _MPV_LIBRARY_DIR) + list(REMOVE_DUPLICATES MPV_LIBRARY_DIRS) + +endif() + +mark_as_advanced(MPV_INCLUDE_DIR) +mark_as_advanced(MPV_LIBRARY_DIRS) +set(MPV_INCLUDE_DIRS ${MPV_INCLUDE_DIR}) + +# +### Check if everything was found and if the version is sufficient. +# +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + MPV + REQUIRED_VARS ${_MPV_REQUIRED_VARS} + VERSION_VAR MPV_VERSION_STRING + ) + diff --git a/CMakeModules/FindSDL2.cmake b/CMakeModules/FindSDL2.cmake new file mode 100644 index 0000000..9c8a3bd --- /dev/null +++ b/CMakeModules/FindSDL2.cmake @@ -0,0 +1,69 @@ +############################################################################### +# CMake module to search for the SDL libraries. +# +# WARNING: This module is experimental work in progress. +# +# Based one FindVLC.cmake by: +# Copyright (c) 2011 Michael Jansen +# Modified by Tobias Hieta +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. +# +############################################################################### + +# +### Global Configuration Section +# +SET(_SDL2_REQUIRED_VARS SDL2_INCLUDE_DIR SDL2_LIBRARY) + +# +### SDL uses pkgconfig. +# +if(PKG_CONFIG_FOUND) + pkg_check_modules(PC_SDL QUIET sdl2) +endif(PKG_CONFIG_FOUND) + +# +### Look for the include files. +# +find_path( + SDL2_INCLUDE_DIR + NAMES SDL.h + PATH_SUFFIXES SDL2 + HINTS + ${PC_SDL2_INCLUDEDIR} + ${PC_SDL2_INCLUDE_DIRS} # Unused for SDL but anyway + DOC "SDL2 include directory" + ) +mark_as_advanced(SDL2_INCLUDE_DIR) +set(SDL2_INCLUDE_DIRS ${SDL_INCLUDE_DIR}) + +# +### Look for the libraries (SDL and SDLsore) +# +find_library( + SDL2_LIBRARY + NAMES SDL2 + HINTS + ${PC_SDL2_LIBDIR} + ${PC_SDL2_LIBRARY_DIRS} # Unused for SDL but anyway + PATH_SUFFIXES lib${LIB_SUFFIX} + ) +get_filename_component(_SDL2_LIBRARY_DIR "${SDL2_LIBRARY}" PATH) +mark_as_advanced(SDL2_LIBRARY) + +set(SDL2_LIBRARY_DIRS _SDL2_LIBRARY_DIR) +list(REMOVE_DUPLICATES SDL2_LIBRARY_DIRS) +mark_as_advanced(SDL2_LIBRARY_DIRS) + +# +### Check if everything was found and if the version is sufficient. +# +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + SDL2 + REQUIRED_VARS ${_SDL2_REQUIRED_VARS} + VERSION_VAR SDL2_VERSION_STRING + ) + diff --git a/CMakeModules/GCCConfiguration.cmake b/CMakeModules/GCCConfiguration.cmake new file mode 100644 index 0000000..e69de29 diff --git a/CMakeModules/GetDate.cmake b/CMakeModules/GetDate.cmake new file mode 100644 index 0000000..b2f8ade --- /dev/null +++ b/CMakeModules/GetDate.cmake @@ -0,0 +1,13 @@ +MACRO (TODAY RESULT) + IF (WIN32) + #EXECUTE_PROCESS(COMMAND "cmd" " /C date /T" OUTPUT_VARIABLE ${RESULT}) + #string(REGEX REPLACE "(..)/(..)/(....).*" "\\3\\2\\1" ${RESULT} ${${RESULT}}) + set(${RESULT}) + ELSEIF(UNIX) + EXECUTE_PROCESS(COMMAND "date" "+%d/%m/%Y" OUTPUT_VARIABLE ${RESULT}) + string(REGEX REPLACE "(..)/(..)/(....).*" "\\3\\2\\1" ${RESULT} ${${RESULT}}) + ELSE (WIN32) + MESSAGE(SEND_ERROR "date not implemented") + SET(${RESULT} 000000) + ENDIF (WIN32) +ENDMACRO (TODAY) diff --git a/CMakeModules/GetGitRevisionDescription.cmake b/CMakeModules/GetGitRevisionDescription.cmake new file mode 100644 index 0000000..1bf0230 --- /dev/null +++ b/CMakeModules/GetGitRevisionDescription.cmake @@ -0,0 +1,123 @@ +# - Returns a version string from Git +# +# These functions force a re-configure on each git commit so that you can +# trust the values of the variables in your build system. +# +# get_git_head_revision( [ ...]) +# +# Returns the refspec and sha hash of the current head revision +# +# git_describe( [ ...]) +# +# Returns the results of git describe on the source tree, and adjusting +# the output so that it tests false if an error occurs. +# +# git_get_exact_tag( [ ...]) +# +# Returns the results of git describe --exact-match on the source tree, +# and adjusting the output so that it tests false if there was no exact +# matching tag. +# +# Requires CMake 2.6 or newer (uses the 'function' command) +# +# Original Author: +# 2009-2010 Ryan Pavlik +# http://academic.cleardefinition.com +# Iowa State University HCI Graduate Program/VRAC +# +# Copyright Iowa State University 2009-2010. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +if(__get_git_revision_description) + return() +endif() +set(__get_git_revision_description YES) + +# We must run the following at "include" time, not at function call time, +# to find the path to this module rather than the path to a calling list file +get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) + +function(get_git_head_revision _refspecvar _hashvar) + set(GIT_PARENT_DIR "${CMAKE_SOURCE_DIR}") + set(GIT_DIR "${GIT_PARENT_DIR}/.git") + while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories + set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}") + get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH) + if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT) + # We have reached the root directory, we are not in git + set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE) + set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE) + return() + endif() + set(GIT_DIR "${GIT_PARENT_DIR}/.git") + endwhile() + set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") + if(NOT EXISTS "${GIT_DATA}") + file(MAKE_DIRECTORY "${GIT_DATA}") + endif() + + if(NOT EXISTS "${GIT_DIR}/HEAD") + return() + endif() + set(HEAD_FILE "${GIT_DATA}/HEAD") + configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY) + + configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" + "${GIT_DATA}/grabRef.cmake" + @ONLY) + include("${GIT_DATA}/grabRef.cmake") + + set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE) + set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE) +endfunction() + +function(git_describe _var) + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + get_git_head_revision(refspec hash) + if(NOT GIT_FOUND) + set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) + return() + endif() + if(NOT hash) + set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) + return() + endif() + + # TODO sanitize + #if((${ARGN}" MATCHES "&&") OR + # (ARGN MATCHES "||") OR + # (ARGN MATCHES "\\;")) + # message("Please report the following error to the project!") + # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") + #endif() + + #message(STATUS "Arguments to execute_process: ${ARGN}") + + execute_process(COMMAND + "${GIT_EXECUTABLE}" + describe + ${hash} + ${ARGN} + WORKING_DIRECTORY + "${CMAKE_SOURCE_DIR}" + RESULT_VARIABLE + res + OUTPUT_VARIABLE + out + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT res EQUAL 0) + set(out "${out}-${res}-NOTFOUND") + endif() + + set(${_var} "${out}" PARENT_SCOPE) +endfunction() + +function(git_get_exact_tag _var) + git_describe(out --exact-match ${ARGN}) + set(${_var} "${out}" PARENT_SCOPE) +endfunction() diff --git a/CMakeModules/GetGitRevisionDescription.cmake.in b/CMakeModules/GetGitRevisionDescription.cmake.in new file mode 100644 index 0000000..888ce13 --- /dev/null +++ b/CMakeModules/GetGitRevisionDescription.cmake.in @@ -0,0 +1,38 @@ +# +# Internal file for GetGitRevisionDescription.cmake +# +# Requires CMake 2.6 or newer (uses the 'function' command) +# +# Original Author: +# 2009-2010 Ryan Pavlik +# http://academic.cleardefinition.com +# Iowa State University HCI Graduate Program/VRAC +# +# Copyright Iowa State University 2009-2010. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +set(HEAD_HASH) + +file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024) + +string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS) +if(HEAD_CONTENTS MATCHES "ref") + # named branch + string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}") + if(EXISTS "@GIT_DIR@/${HEAD_REF}") + configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) + elseif(EXISTS "@GIT_DIR@/logs/${HEAD_REF}") + configure_file("@GIT_DIR@/logs/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) + set(HEAD_HASH "${HEAD_REF}") + endif() +else() + # detached HEAD + configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY) +endif() + +if(NOT HEAD_HASH) + file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024) + string(STRIP "${HEAD_HASH}" HEAD_HASH) +endif() diff --git a/CMakeModules/InputConfiguration.cmake b/CMakeModules/InputConfiguration.cmake new file mode 100644 index 0000000..59cc7db --- /dev/null +++ b/CMakeModules/InputConfiguration.cmake @@ -0,0 +1,71 @@ +OPTION(ENABLE_SDL2 "Enable SDL2 for joystick handling" ON) +if(ENABLE_SDL2) + find_package(SDL2) + if(SDL2_FOUND) + list(APPEND ENABLED_INPUTS SDL2) + + if(NOT WIN32) + find_package(Iconv) + + if(NOT ICONV_FOUND) + unset(SDL2_FOUND) + endif(NOT ICONV_FOUND) + + find_package(DL) + + if(NOT DL_FOUND) + unset(SDL2_FOUND) + endif(NOT DL_FOUND) + + list(APPEND SDL2_LIBRARY ${ICONV_LIBRARIES} ${DL_LIBRARIES}) + endif() + + if(APPLE) + find_package(Iconv) + + if(NOT ICONV_FOUND) + unset(SDL2_FOUND) + endif(NOT ICONV_FOUND) + + list(APPEND SDL2_LIBRARY ${ICONV_LIBRARIES}) + find_library(FORCEFEEDBACK ForceFeedback) + find_library(CARBON Carbon) + list(APPEND SDL2_LIBRARY ${FORCEFEEDBACK} ${CARBON}) + endif(APPLE) + + if(SDL2_FOUND) + add_definitions(-DHAVE_SDL) + include_directories(${SDL2_INCLUDE_DIR}) + set(EXTRA_LIBS ${SDL2_LIBRARY}) + endif(SDL2_FOUND) + + endif(SDL2_FOUND) + +endif(ENABLE_SDL2) + +OPTION(ENABLE_CEC "Enable HDMI/CEC support with libCEC" ON) +if(ENABLE_CEC) + find_package(CEC) + if(CEC_FOUND) + list(APPEND ENABLED_INPUTS CEC) + add_definitions(-DHAVE_CEC) + include_directories(${CEC_INCLUDE_DIR}) + set(EXTRA_LIBS ${EXTRA_LIBS} ${CEC_LIBRARY}) + endif(CEC_FOUND) + +endif(ENABLE_CEC) + +if(UNIX AND NOT APPLE) + OPTION(ENABLE_LIRC "Enable LIRC for Linux IR handling" ON) + if(ENABLE_LIRC) + list(APPEND ENABLED_INPUTS LIRC) + add_definitions(-DHAVE_LIRC) + endif(ENABLE_LIRC) +endif(UNIX AND NOT APPLE) + +if(APPLE) + list(APPEND ENABLED_INPUTS "AppleRemote") +endif(APPLE) + +string(REPLACE ";" " " _STR "${ENABLED_INPUTS}") +message(STATUS "Enabled Inputs: " ${_STR}) diff --git a/CMakeModules/LinuxConfiguration.cmake b/CMakeModules/LinuxConfiguration.cmake new file mode 100644 index 0000000..93bbb19 --- /dev/null +++ b/CMakeModules/LinuxConfiguration.cmake @@ -0,0 +1,14 @@ +find_package(X11) +if(X11_FOUND AND X11_Xrandr_FOUND) + include_directories(X11_X11_INCLUDE_PATH X11_Xrandr_INCLUDE_PATH) + set(X11XRANDR_FOUND 1) + add_definitions(-DUSE_X11XRANDR) +endif() + +if (NOT BUILD_TARGET STREQUAL "RPI") + set(USE_X11POWER ON) + add_definitions(-DUSE_X11POWER) +endif() + +set(INSTALL_BIN_DIR bin) +set(INSTALL_RESOURCE_DIR share) diff --git a/CMakeModules/NameConfiguration.cmake b/CMakeModules/NameConfiguration.cmake new file mode 100644 index 0000000..dda3da1 --- /dev/null +++ b/CMakeModules/NameConfiguration.cmake @@ -0,0 +1,16 @@ +set(HELPER_TARGET PMPHelper) +set(MAIN_TARGET PlexMediaPlayer) + +# Name of the output binary, defaults are only used on Linux +set(HELPER_NAME pmphelper) +set(MAIN_NAME plexmediaplayer) + +if(APPLE) + set(HELPER_NAME "PMP Helper") + set(MAIN_NAME "Plex Media Player") +elseif(WIN32) + set(HELPER_NAME "PMPHelper") + set(MAIN_NAME "PlexMediaPlayer") +endif(APPLE) + +configure_file(src/shared/Names.cpp.in src/shared/Names.cpp @ONLY) \ No newline at end of file diff --git a/CMakeModules/PlayerConfiguration.cmake b/CMakeModules/PlayerConfiguration.cmake new file mode 100644 index 0000000..b01e6e5 --- /dev/null +++ b/CMakeModules/PlayerConfiguration.cmake @@ -0,0 +1,16 @@ +# We want OpenGL or OpenGLES2 +find_package(OpenGL) +if(NOT OPENGL_FOUND) + find_package(GLES2) + if(NOT GLES2_FOUND) + message(FATAL_ERROR "OpenGL or GLES2 is required") + else(NOT GLES2_FOUND) + set(OPENGL_LIBS ${GLES2_LIBRARY}) + endif(NOT GLES2_FOUND) +else(NOT OPENGL_FOUND) + set(OPENGL_LIBS ${OPENGL_gl_LIBRARY}) +endif(NOT OPENGL_FOUND) + +find_package(MPV REQUIRED) + +include_directories(${MPV_INCLUDE_DIR}) diff --git a/CMakeModules/QtConfiguration.cmake b/CMakeModules/QtConfiguration.cmake new file mode 100644 index 0000000..fb752dc --- /dev/null +++ b/CMakeModules/QtConfiguration.cmake @@ -0,0 +1,57 @@ +set(QTROOT "/usr/local/Qt/Qt5.5" CACHE PATH "Root of the QT binaries.") +set(REQUIRED_QT_VERSION "5.5.0") + +set(QTCONFIGROOT ${QTROOT}/lib/cmake/Qt5) +set(components Core Network WebChannel Qml Quick Xml WebEngine) + +if(OPENELEC) + set(components ${components} DBus) +endif(OPENELEC) + +foreach(COMP ${components}) + set(mod Qt5${COMP}) + + # look for the config files in the QtConfigRoot defined above + set(${mod}_DIR ${QTCONFIGROOT}${COMP}) + + # look for the actual package + find_package(${mod} ${REQUIRED_QT_VERSION} REQUIRED) + + include_directories(${${mod}_INCLUDE_DIRS}) + if(OPENELEC) + include_directories(${${mod}_PRIVATE_INCLUDE_DIRS}) + endif(OPENELEC) + + list(APPEND QT5_LIBRARIES ${${mod}_LIBRARIES}) + list(APPEND QT5_CFLAGS ${${mod}_EXECUTABLE_COMPILE_FLAGS}) +endforeach(COMP ${components}) + +list(REMOVE_DUPLICATES QT5_CFLAGS) + +message(STATUS "Qt version: ${Qt5Core_VERSION_STRING}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${QT5_CFLAGS}") + +set(CMAKE_REQUIRED_INCLUDES ${Qt5WebEngine_INCLUDE_DIRS};${Qt5WebEngine_PRIVATE_INCLUDE_DIRS}) +set(CMAKE_REQUIRED_LIBRARIES ${QT5_LIBRARIES}) + +OPTION(SKIP_QT_TEST "Skip tests for required Qt features" OFF) + +if(NOT SKIP_QT_TEST) + include(CheckCXXSourceCompiles) + check_cxx_source_compiles(" + #include + #include + int main() + { + QQuickWebEngineView* view = new QQuickWebEngineView(NULL); + view->setBackgroundColor(QColor(\"transparent\")); + } + " WebEngineBackgroundProperty) + + if(NOT WebEngineBackgroundProperty) + message(FATAL_ERROR "QQuickWebEngineView doesn't have the background property." + "This will break video playback. As of Qt 5.5 you need to manually patch and build Qt to get this property." + "With the release of Qt5.6 it will no longer be required. See qt-patches/README for more details.") + endif(NOT WebEngineBackgroundProperty) + +endif(NOT SKIP_QT_TEST) diff --git a/CMakeModules/VersionConfiguration.cmake b/CMakeModules/VersionConfiguration.cmake new file mode 100644 index 0000000..6a9a2df --- /dev/null +++ b/CMakeModules/VersionConfiguration.cmake @@ -0,0 +1,30 @@ +# Get the current date. +include(GetDate) +include(WebClientVariables) +today(CURRENT_DATE) + +# Get git revision version +include(GetGitRevisionDescription) +get_git_head_revision(REFSPEC FULL_GIT_REVISION) +if(FULL_GIT_REVISION STREQUAL "GITDIR-NOTFOUND") + set(GIT_REVISION "git") +else(FULL_GIT_REVISION STREQUAL "GITDIR-NOTFOUND") + string(SUBSTRING ${FULL_GIT_REVISION} 0 8 GIT_REVISION) +endif(FULL_GIT_REVISION STREQUAL "GITDIR-NOTFOUND") + +# Get the build number if available +if(DEFINED ENV{BUILD_NUMBER}) + set(VERSION_BUILD "$ENV{BUILD_NUMBER}") + set(VERSION_BUILD_NR "$ENV{BUILD_NUMBER}") +else() + set(VERSION_BUILD "dev") + set(VERSION_BUILD_NR "0") +endif() + +set(VERSION_MAJOR 1) +set(VERSION_MINOR 0) +set(VERSION_NANO 0) + +set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_NANO}.${VERSION_BUILD}-${GIT_REVISION}") +set(CANONICAL_VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_NANO}-${GIT_REVISION}") +configure_file(src/Version.cpp.in src/Version.cpp) \ No newline at end of file diff --git a/CMakeModules/WebClientResources.cmake b/CMakeModules/WebClientResources.cmake new file mode 100644 index 0000000..2a927fe --- /dev/null +++ b/CMakeModules/WebClientResources.cmake @@ -0,0 +1,36 @@ +include(WebClientVariables) + +option(SKIP_WEB_CLIENT "Skip downloading the web client" OFF) + +if(NOT SKIP_WEB_CLIENT) + set(WEB_CLIENT_CPP web-client-${WEB_CLIENT_VERSION}.cpp) + set(WEB_CLIENT_URL https://nightlies.plex.tv/directdl/plex-web-client-plexmediaplayer/master/plex-web-client-konvergo-${WEB_CLIENT_VERSION}.cpp.bz2) + + message(STATUS "web-client version: ${WEB_CLIENT_VERSION}") + + file( + DOWNLOAD ${WEB_CLIENT_URL} ${CMAKE_CURRENT_BINARY_DIR}/${WEB_CLIENT_CPP}.bz2 + EXPECTED_HASH SHA1=${WEB_CLIENT_HASH} + TIMEOUT 100 + SHOW_PROGRESS + TLS_VERIFY ON + ) + + find_program(BUNZIP2 bunzip2${CMAKE_EXECUTABLE_SUFFIX}) + if(${BUNZIP2} MATCHES NOT_FOUND) + message(FATAL_ERROR "Can't fid bunzip2") + endif(${BUNZIP2} MATCHES NOT_FOUND) + + add_custom_command( + OUTPUT ${WEB_CLIENT_CPP} + COMMAND ${BUNZIP2} -k -f ${CMAKE_CURRENT_BINARY_DIR}/${WEB_CLIENT_CPP}.bz2 + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${WEB_CLIENT_CPP}.bz2 + COMMENT "Unpacking: ${WEB_CLIENT_CPP}.bz2" + ) + + add_custom_target(UnpackWebClientResource + DEPENDS ${WEB_CLIENT_CPP} + ) +else(NOT SKIP_WEB_CLIENT) + message(WARNING "Skipping web-client, you will not a functioning end product") +endif(NOT SKIP_WEB_CLIENT) diff --git a/CMakeModules/WebClientVariables.cmake b/CMakeModules/WebClientVariables.cmake new file mode 100644 index 0000000..d9fd3ac --- /dev/null +++ b/CMakeModules/WebClientVariables.cmake @@ -0,0 +1,2 @@ +set(WEB_CLIENT_VERSION a1def70) +set(WEB_CLIENT_HASH 16d2ef0a5b46fb6746aed6d3f3f7234d463f2c36) diff --git a/CMakeModules/Win32Configuration.cmake b/CMakeModules/Win32Configuration.cmake new file mode 100644 index 0000000..d91b113 --- /dev/null +++ b/CMakeModules/Win32Configuration.cmake @@ -0,0 +1,14 @@ +set(INSTALL_BIN_DIR .) +set(INSTALL_RESOURCE_DIR resources) +set(HAVE_UPDATER 1) + + +# Add install rules for required system runtimes such as MSVCRxx.dll +SET (CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP ON) +INCLUDE(InstallRequiredSystemLibraries) +IF (CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS) + INSTALL(FILES ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS} + DESTINATION ${INSTALL_BIN_DIR} + PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ + COMPONENT Runtime) +ENDIF (CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS) diff --git a/CMakeModules/utils.cmake b/CMakeModules/utils.cmake new file mode 100644 index 0000000..21c92e8 --- /dev/null +++ b/CMakeModules/utils.cmake @@ -0,0 +1,70 @@ +############################################################# +function(set_bundle_dir) + set(args SOURCES DEST EXCLUDE) + include(CMakeParseArguments) + cmake_parse_arguments(BD "" "" "${args}" ${ARGN}) + + foreach(_BDIR ${BD_SOURCES}) + file(GLOB _DIRCONTENTS ${_BDIR}/*) + foreach(_BDFILE ${_DIRCONTENTS}) + get_filename_component(_BDFILE_NAME ${_BDFILE} NAME) + + set(PROCESS_FILE 1) + foreach(EX_FILE ${BD_EXCLUDE}) + string(REGEX MATCH ${EX_FILE} DID_MATCH ${_BDFILE}) + if(NOT "${DID_MATCH}" STREQUAL "") + set(PROCESS_FILE 0) + endif(NOT "${DID_MATCH}" STREQUAL "") + endforeach(EX_FILE ${BD_EXCLUDE}) + + if(PROCESS_FILE STREQUAL "1") + if(IS_DIRECTORY ${_BDFILE}) + set_bundle_dir(SOURCES ${_BDFILE} DEST ${BD_DEST}/${_BDFILE_NAME} EXCLUDE ${BD_EXCLUDE}) + else(IS_DIRECTORY ${_BDFILE}) + #message("set_bundle_dir : setting package_location ${_BDFILE} = ${BD_DEST}") + set_source_files_properties(${_BDFILE} PROPERTIES MACOSX_PACKAGE_LOCATION ${BD_DEST}) + get_property(BUNDLED_FILES GLOBAL PROPERTY CONFIG_BUNDLED_FILES) + set_property(GLOBAL PROPERTY CONFIG_BUNDLED_FILES ${BUNDLED_FILES} ${_BDFILE}) + + string(REPLACE "/" "\\\\" GNAME ${BD_DEST}) + source_group(${GNAME} FILES ${_BDFILE}) + endif(IS_DIRECTORY ${_BDFILE}) + endif() + endforeach(_BDFILE ${_DIRCONTENTS}) + endforeach(_BDIR ${BD_SOURCES}) +endfunction(set_bundle_dir) + +############################################################# +macro(find_all_sources DIRECTORY VARIABLE) + aux_source_directory(${DIRECTORY} ${VARIABLE}) + file(GLOB headers ${DIRECTORY}/*h) + list(APPEND ${VARIABLE} ${headers}) +endmacro() + +############################################################# +# function to collect all the sources from sub-directories +# into a single list +function(add_sources) + get_property(is_defined GLOBAL PROPERTY SRCS_LIST DEFINED) + if(NOT is_defined) + define_property(GLOBAL PROPERTY SRCS_LIST + BRIEF_DOCS "List of source files" + FULL_DOCS "List of source files to be compiled in one library") + endif() + # make absolute paths + set(SRCS) + foreach(s IN LISTS ARGN) + if(NOT IS_ABSOLUTE "${s}") + get_filename_component(s "${s}" ABSOLUTE) + endif() + list(APPEND SRCS "${s}") + endforeach() + + string(REPLACE ${CMAKE_SOURCE_DIR}/src/ "" SUBDIR ${CMAKE_CURRENT_SOURCE_DIR}) + string(TOLOWER ${SUBDIR} SUBDIR) + string(REPLACE "/" "\\\\" LIBNAME ${SUBDIR}) + source_group(${LIBNAME} FILES ${SRCS}) + + # add it to the global list. + set_property(GLOBAL APPEND PROPERTY SRCS_LIST ${SRCS}) +endfunction(add_sources) diff --git a/README.md b/README.md new file mode 100644 index 0000000..4df57ce --- /dev/null +++ b/README.md @@ -0,0 +1,52 @@ +## Building + +You need: + +* Qt 5.6 alpha +* cmake 3.0 or newer +* ninja is recommended for building + +Special Qt requirements: + +* On Windows, you must apply qt-patches/0003-Always-enable-viewport-stuff.patch for + correct window scaling. Applying the patches in qt-patches/qt-5.6-alpha/ fixes + some stability issues. +* On OSX, you should apply qt-patches/0002-qtbase-Don-t-show-the-menu-bar-at-all-in-lion-style-fullscr.patch + to improve the user experience in fullscreen. +* You can try to use Qt 5.5, but then you also need to apply the following patches: + qt-patches/0001-qtwebengine-Add-a-backgroundColor-property.patch + qt-patches/0004-qtwebengine-transparency-window-creation.patch + Without them, video playback will not work. + +Get dependencies: + +* scripts/fetch-binaries.py -p darwin-x86_64 + +If you're happy just building from the command line then run CMake for the ninja build tool: + +* mkdir build ; cd build +* cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DQTROOT=/path/to/qt -DCMAKE_INSTALL_PREFIX=output .. + +Build (ninja): + +* ninja + +Make a distributable package: + +* ninja install (be patient, it's slow) + +Or if you prefer working in Xcode, run CMake for the xcode build): + +* mkdir build ; cd build +* cmake -GXcode -DQTROOT=/path/to/qt .. + + +### Debugging Web Client + +You can run a locally hosted development version of the web app within the Konvergo application. If the main app window is open you can also run Chrome side by side to debug. + +* Run the `grunt server:konvergo` from the `web-client` submodule. This will run a dev version of the web client +* Update the `starturl` in `~/Library/Application Support/Plex Media Player/Plex Media Player.conf` to point to `http://localhost:3333/app/dev-konvergo.html` +* Run the `Plex Media Player.app` +* Tail the `~/Library/Logs/Plex Media Player/Plex Media Player.log`, optionally grepped with `WebClient` to see `console.log`s +* Open Chrome and point to `http://localhost:3333/app/dev-konvergo.html`. This should open a Qt channel to the main `Plex Media Player.app` and function as normal - but with the ability to add breakpoints and inspect code diff --git a/bundle/osx/Info.plist.in b/bundle/osx/Info.plist.in new file mode 100644 index 0000000..89697f1 --- /dev/null +++ b/bundle/osx/Info.plist.in @@ -0,0 +1,40 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${MACOSX_BUNDLE_EXECUTABLE_NAME} + CFBundleGetInfoString + ${MACOSX_BUNDLE_INFO_STRING} + CFBundleIconFile + ${MACOSX_BUNDLE_ICON_FILE} + CFBundleIdentifier + ${MACOSX_BUNDLE_GUI_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleLongVersionString + ${MACOSX_BUNDLE_LONG_VERSION_STRING} + CFBundleName + ${MACOSX_BUNDLE_BUNDLE_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + ${MACOSX_BUNDLE_SHORT_VERSION_STRING} + CFBundleSignature + ???? + CFBundleVersion + ${MACOSX_BUNDLE_BUNDLE_VERSION} + CSResourcesFileMapped + + NSHumanReadableCopyright + ${MACOSX_BUNDLE_COPYRIGHT} + NSPrincipalClass + NSApplication + NSHighResolutionCapable + True + LSMinimumSystemVersion + 10.9 + + diff --git a/bundle/osx/Konvergo.entitlements b/bundle/osx/Konvergo.entitlements new file mode 100644 index 0000000..7a2230d --- /dev/null +++ b/bundle/osx/Konvergo.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.network.client + + com.apple.security.network.server + + + diff --git a/bundle/osx/Plex.icns b/bundle/osx/Plex.icns new file mode 100644 index 0000000000000000000000000000000000000000..58cc3b3c0ab91513ca3a1cb7f06ab9c56d2f7f5e GIT binary patch literal 1256166 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%0Avq0Z+L227|1csP5koVQ1V{%ye{i<9@Z%Rl*Pn6A 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%0Dod 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w{Y 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 z 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

XFy3ppu#g**3!Ls?TS}oO7;{k6e*7 zdyfH$wRQ7(*ZwTpa`3HtEjDpkFa*-|0t=Ec{Ajk}loBt9js%SslBErL=H*zz^PUCy zT!oiD7JRP7JxVQ)dx(`1JY(xKWwH^h-YWc z1v-qb38i&~J5?lG*0QcL&J9P5d3=TJ)TspqSKM5(O&)uWiwimQyNXZWjZB)Ps-t;5 zosLusDCtHQCcmxyNDLa$7ARMy@~^&25jFrLj~ohb5cKH~9D9?9*C zQ-N&^L6a?icv69eC!@<08~K~GhS8HVT(Xfnh`rCta?tMpf;{d`_m;s<#74CK=9=Sx zH6Kd60F(*Ev(`vAyf@?af^&b>)IMk1oJPsnA2Yk3NN~!bc<-4kLD-cGF^&y0d8sSW z7NPNra^pu|Of3Qkb#rBe!-3XqI@IK|%K=;xIq=8xV90-P+(f%h_f4#YfoH9ZRF8ra z&7CsX@C_tN9KwjmTP8p$7mvBIc2E656K0vH#VAcZzIABQXo-^A%W~R@9~zXY zv3{?4KsWQ|B<{>{1B4jxN^!M%#SaoB)GjQIone9t}W_S>Wam@PYqxUdXY(gT`GP2V1{tkxcg z4D!Qk0(!ZasLIZmDnmDrEm#_JItQ@O77Rl;BVB@UnP6Bj+>b`yJ3c_mNB9N6?&8`8 z36@vqBP_o{{72RDW4PVRLHF?OGy$Hm_Q}wsxa{^JB0k3}61f0|*GK-~s>_UXLqNla zK0i8jvU77L$;#D(??3`@kS2GDRql41b;p@g8aH1mVaBTv-;oMt)&vbM|Dj`FZD75| z@RBPGULnxj495mf=w#4C1b|QmkA_kb__B^UA9lM^fJU=Jztv{u; zCOebG=_NXY7CF&bcUW%gF~$uU^ns9wwp!)BWJj<$4}w7gLLq(SD~3wU+M4KJx6G|h zph8V5uW&rb;aT^D<;0ODqs-0=R#tLufJ;h&b~((r6l51G8nwiiY10{X__k*oHzupT z>a*^&jbibqXPz;s8kl``3SmkU&QUiv`RQv0Bti~1Do|cP78kk{6+=B~T;oiEPj-*? zNV4B#*&kvS=>avKeM~y5n@B2w5cjom)TIf`X|`#tFPg8e0Vbg2T5Sg7G9ioXHZ{N;$GLcBH=0RJ$6^ht35iT;^3y&Zzf4oW2Z=R(Viezo{Ns$EmnOl)? z+uey^0GWyzZm@X*?GCdii6vh{{JbIs!+zq_=drENqVi+&XJ7XAcz!3&B^U&5;wA1u z*w&o;P^qfpM2+os-e`MnLT26gP9(5E$GQWt&}w35O~jkPaO`vs&}7iO4kGf-xBgBr ziMwv+*iC8r?iQtpcYQuDppo8}c`tTK!Py^Rgk~kwMx8PE{ToCJqw1_6=cGXfXYGm4 zzQZq92irc$$ZD62QwuQX!(nGC^7Y`cvpQ4O*P7uyVkcp`eIc17ni;pgSh=Q(cOf?R z^Oip1F95QQZlLyJwqu&lE{qM#$m7r9o6Q<4sDpRNJr`@JE)(L@SS8`jm5g~~>Ww-@ zl#c_BpNt$rzf_oEDERdQdox5(PNxkpY}k+#mp@Jjg0$-?fhS#<^B^$wKfPimFy{d}9_+v!!WD91k|S1d ztACagV~}E+)i5Zc6N}P4idY)eK)tRxVV5hR9>Pg(w!|*qV9xp0*(RNkk=>6`oTjx! z=1qZ>#5gO|?Dfo=cOR!pXLK3uHwNihDvDm}Z;qfG`20gHv(m9OSbkH2BLp*pS{#p8 zv8D&BAFoe_B-V`G0Eb>K=1T@c>YS_!T$TtiEc3VtzBMZU#iY9Y0pK)p*oburM}!sbDX z9w};BJkgW-G>>?oZ^Q%PlBP2QlV5h$H^{6(8iqENQ2R04Rl!|O@bCQXUw-<|FB&!E z?>E0A^c}8u=a`LqH|pN|=7jfDZ?U{SJv;N(acufqDSUYDgI)jY7jaMUfm#0K|MMMB zzgz#?f8PXZo4d}1HR^`+V+j$H9}*Iz&GF;O=KH3GhB-d5vY6KwbHXt3CC23OUah65 zha-*)mrTi-=U>98{v^q>)x`_oc;d2vG-2{hW2F1JRh_3$BV-TzY8y-~Dih55QI7cx zNi20z6*gvHhNMN=V<$LAIc_}#@j0dmhbN4}DmQU*Pfad87YHHXayOcG*>Cmbc#UwT z5D~l65<8cDaHuYr`X@8zk>CN5m=2B3Ot1H7XKNMG&;uc^>5)H2G^dNY;Q>7kavxpbKu(lc1VSP`gWVK+Cm<_2cPpSV-ptS0Z(tWyF%BDUoMV$Py-G9X^8!12k^vj? z^holh6hpM8@hg|VU2SNvkZ3QsQi~E}w{OY8c+c>l8dR+KQ4<~~vZWAZODhAk2*wKq z$&l5yXhDIjabRW+yp2jS*+D}IbipORb)++jcAc<+@Vi0G3WL}F#_}P|K zFn*zY#GOD94r*2$Nv0OgnU?`fN7|`AAWVYpE5z-53UQ(xY)LlnY7}uxaLy(s&kotL za>dLQEaL>ma(5l*a^y^Hb#(C$9cz<38NvD-wbr3D9NdbdGARznF+8al6=~v&HjHdI zXsP)s?;hLT*v*hS<3lbPi8R2?jxZ;OT?R)AjNs(A5cR@f`&{bpG}ab^IyG*htY0ph zSl|xxI!AW{+I_GJWcuxY)z3Zs6~E!nA3-RF8r)I1qy4MbWtG7-5*s%dKZ{$JKffu!oJkcwykJZQFO~6=XVywgp-uqC zzhrQ-pd}m9IQxVO3tbWN_cu~!96C@pQuCsmwRNd!gm=76Vfn;Q1U`mz#kv!?c)@|ocFDsTm(R=Y zLO3@DaBN&>ugNeO&$EEcPuk8NJF`-!5MNCfJ7u-HiaSbZNA;cnuV%M9Mb5QNbz>UTK& zWe!+Wy}V0~nf$>A4S*N6{hGCd2@WCBT*sa@OS2)|Lce}I3K?=(I8PUS*F>oE3$TuZ zjKGO;nE7`S-!pPJR=9kCkKY|Kk#yex;)O)Eb`M3bbASPltv)6v$EO#HFqJ%0V7Dw} z4s}p;uBE#X?az8QU`eH5vTZpWc5v=ombh3*o#BtsW`}PneS3QP(y#u;(?9WBzEbkn zPPaw9Htq(u%eis9U41*H*7PX&k?m66TjMSyZ&aSi{Q}!{NpItLA;kyd{saHr_dfl% z|Lgat9#*Yap9|bv{+uQ2+~2Ec`M`3&`vIk}^@xxy&+7QYY9#3U(X{)<#qbb$AN$bq zIM&a6sKwpsF5325x4b30{JdlJda-C8#Ce!lM8>zP*jhd#UL%Gp6!-Y? zUOjFB{dQu_7F(q4k*`xZ(6et~j8vg1O`6XL z;gUm4z4*P2;!V2!a*>25cIIlnXvI8)V$5r~*X~8& zTrHD%gUqp=M2+U;SBpmoF4!BN}26o@e>InM^L<)W#*il_;3H_yLET zSZYAQ0)IxCE*#_8*(dizeL8cJ?pm79y^Cz03vb5O$;!M4H?wP7MFE-<5ZqAAL(B2* zMEbE-D!xR+naX(3W-qD(z#vlpk?Fk6C&=zq_P`lr_g>&91Kbp)7j)79bVNvc$*6i| z4Jbx0c-;Y&A&6VvI^S~|t_jQ8A}eF&XzS|YXZ@gh&bnWAD->?meo#P;N9N-n}Oc+e@n#*IP3cEON^^`z)F440Bv>5h31EU6$>dUjlicSCY zZ~e;CulnuhJ$SopUWhN1mnd+-1%^Ex8XAo-ILS!T4>%p4%mUZY+Ki zkmn7d8i_n)KZJEJazDnbs}mjia=cR5Ob-K`Q=auKI#6DRF8RH2>=0+r#m@@INa5L` zpS-VOuUH7U$5cgNS!JSu(T34D>g#11W)5*-? z085CwQ^N2OZ!>4kg9IW{a#I6Pb{J)=ky3rD_^#1m+UoFkjmbrA+1@q{@PKElG3 z&pPb_IqjRNYmq&>xBXe5s|%MBk&4&&EW)?Kr~`#p@L;m$)+jw3TS$M#ju+DFxVK#W zAFB3|d;#!W`(D@fhp1zh?bqR+<8*HDI(WyX_qeL4eLLjrHWQOxNug^G+dK?yC&2MV zHI$W7hgITmLlJoHCD$6e--zr>^*rD`6u2mD8zSPgg5ehN!nI=SPB^RYim4-McReKI z=7Wzs078Ig<0aYb#SBp_ERgCLo^_cwW7a>kgPEvAOwiKaDcr>hJ+|Q=g1yhFi#kAq zAFTJvN-XJmy(UJku4z^}`wCeYbu|n_@tTddF6L#5N4>t4L;`Nf!n~n)C8+3sWli4Esx&Q-YzBS(JC-ylxHN|_>vEbvUtC6YA znWWmnWo^JP9Zt@E1KbM9ULR&+?YDqpF7HHpNiaB6Q0UA~X)QMMm^nt@KN6i2NXM$d zBw_U0R0n0cRA1`zWW!K=!dZl&UgrwErYpD7x<_6YO59B8LiFs7>NjMOZ?2Qvx!A)p zbnlqBC!8WlEoU3-OffN}E`t*{l~6?PgC_Nq+U^UIvmD#^4ylxbSZi3_qt5XjGdWmO z=PW^8>vNfq{oxRb@cd0b`qihu`7izILG)RQt?#o;vADlExoG!$j|Q#Sp2d@A`A#G9 zZS1Z8&HCehtvZ(Q<^-Qh;J^Iued+1X{PY)<+joGj%o^&sB|jVD%fkixc`s|>^UQF7 zE<8u|auhcjCSqoDpm!2oeE-Qid%PSD8&u}QcXf>B0!GRFz+{KHbh-GW_}-Um(#0Ch z&kY$Ca-Jz7Whq|c%+i93FCD-Va(d$DPjdyEJpPAQ;QFkkX0Y%FAvppr564d*LGi12 zzYiAd5CeBMSkV^U5=wkb+r^&65vBw5lDJ8Pyku_V0EYR*(6xaHSH+W5B9|Y@N-V4s z1RAd#?eO`$(kml$k%Y_z+_cr`(AhYmKLbihPh!ng!4mdAj8uM+!S3_YeVOy0nuNzs zV4Ivj?4e3A@PP^vTQqWF^(c21Dyz>=y0E%vFu_Ff>LB$NBgsHriys7`HVmWu4xH%C z6T+;UI3uR9&aj!A`lFjZiZ1~6*4*~@egiJpDjdx{PC1*J^Khan7wRK;JUw>S94_m+ zp!rx~*dWP6#9^EK4p^|EPo2TR1v)n#oKzxKZ~NJvCp~0T8s1SxRJIA6y1A zNghDPv>_9;QVnFy#=6{KS;?%$7W=xkA!fbfOrvKh;l;PGh%z`PJpLnJerD5ig~!wM zK9!IN$y>$-cz{KgXPJo8PpN>>cdy0J@Dt;p<};lyL&O}C*=wuD$gVNSZMWH;ef9^K zm_fWWhkIpEDfjX7g~s$CYohAAXr*gkDAvs?A5e?ziTWg8x+S}M19YX95~z&+m4?5I z@p<<-NDy)Mc{2)P8v7x;Bh7%uPQBqX`VGE{&6DGc;ToTkNskREth&>pEQ z8k@eXKFMZZvGZIompEhAl{Jor(GK-r^Kh+Q8^6yoeIiL;&?q=HX7_>58CVL^5Y3Chx<9JSeN5DH=&#}e+=D-`;M?8;gH({u+gxyEJIq;t9d5iY$CXdw{+lsIK zIrqYS7F_|Xr!+j69g zd5@#7Shokl>hnBq4A@~h0_we;g1%U<52lOD`IEZ!Na5EGo{AZ;E=>!cXBK@~?~lUx zk23l5*(=e+G8`jf_tHi#dDgg@1|$}d;YTlm^n6i?(Z9C2!0HW&bGYn-llUD1Wk#ZW zEw-VqcTjtt_mx`8x&5EEH2G#`dF>ggl%L)(XC_L^6m?dCzPo|cESQX>Hs$gNvhS6V4Ws>K5y{b%Ubek zmewbLH#b%>9E1GWcW={+rcrTTn;@yir6!)d)N;_GG7i%+mMa3lQy@v^mpj4&+>-TEnO=?#m-B!hVr&A>`X`&?#j)-SQt8LrCc9_bQV~}W{atDVu32Le zkCnU~)bp0H2zGF&+?{Qd1T^O=@>~HWCS+i&mwP>@MkhdsiHX!6${2fR_7j`!Bb11q zus_~c+1BiCIg9s6QX+_-*kz3GFcav`9ya##P=UNSsCmYH>{z_8O+E#?3nAdj{XV;G z9a}B`_}~81Pe1g%`aef!BqZ{4*Vka4i+#cV-qi0M@InF0t^PjZzO~)z?DC!Mp=o7$ zQFy@jV$3H3ys(CEdOdGn%+KgQ`1dd9Km5lN#UK8CABc}XDW5kYuREoHJR`bj z-*yS|{V6d|E1p8Z@Q46rsOA~)mSs$yu?4H^dDCA|FDduTlYcy!j~eYYF6{hq6};UBJ4A`7uH@;t(4*|pg?5oUC}LGTc3bMr06 z80{1o8o%Pl$ub<;cn84iF}P)zGy{0UJX~kOO*7uilRthRLqs{=L7Mf#q~TmHAO<6SK7`>n#R^Fs;a@em5wa<ZwuVw!ythd zLzt*!nlaAm@Qlmzjeq&BsAc{LgpcA2fYrz8^UTXJ0!LCCd+rl*@yBtIO%DCqRlmc0 zXos&39~H@{oj^dbPE>sy&SjR(vaU_1xqb%&NX_OWcY&#ec!<%M@iTXl8Mb<)AW1ij z3vw%v44do(*v#LvQG6+Nb{-X&jXsqA};^ki~{&CbCAX$Sh#z_)m zyDv6E`3Xij#Aiw|F~_QkR*I>|z zUWVN{8!(`rOc(e?xv;#b+@{X+IfPy-3|^px)TtmNv`Ee;b-7QGycNIc*MIHl$N$Qo zdolkv1>jyj&h!q~{_65VK56 zSWF+J@UQ2{+aJkZS*DgOWvdAUX_Qn;EG!lzCJJI!|dmy1uMnZIf2Y$^8JP? zLw*&gFv%XSC61^U@QrAkmwjwx3BF}5Fmy39wu(KGagWil7jBc zb#OPtmpzz2y0bS4Ku+plouByiB9?uN!Lmfhuy2gk<{+7zvqbwdO7gT4L+w*+_(PRz zcwLbaxyIA&lwec{xt$XLH8N_@wEjkmb(Ko=WlZQY-Xc=xa({%v$MprkY{<-N`0bW> z4{nX%Wy?VWp~GvA-jtOc+rty)Je+bfH0d00Segfaz8N)xE_Ix|99q{f80K(tQ?7iC zp+3Lt!H*+&36i!PUHB|Y9<7UY7}8kz;+79V#V@xC^9_ehFMI4vO6opylP3=!c;qs1 z0+J28BwjXf0CGT$zhQtOQ-1KlGhRb@E{Wd06m4H zcYpAJ#e&}nn4>!sYy>%iwQybtJ}~f~`o(3(SeTtSSHzCg?G=FHJepyJ#`fSa^3Zwh)#buY(UOS$ja%M2eD;*b6x-~IIa{_F3{?Wo1&9?-gG zX}V9i{pYEPnJ0MeEh%{@$dUj*-N8qdH3EI}mkP6d407S+webU}V?Z^h|KU84FNm!o z!O2(Ifrp%WX0>=H*8#FEcK4=`_4&?G^{Y|&96=wkrBfEsL~9GK^7uLBbTtS8s=wvP zvs6lk<3mC`((H)=GQ_4CL0(ubMm0Z{xFK0y;~fB&u?|4g*eXN(Xgp_sfITx`+*8v6 zu*#EIj#1snQL|pVh;98olQN?E?CT(|{aA@tvRJq$kc@@jcLWCUKxAj+SbGLQFpi3a z#l{)Qd~JI&Pwk0L0?Bcj(Y0Vw{z1hdUJSw-?it&l#sONksb6N80@Dhu>(Zo&MjsyH z@#;q=JRbMLjUDyMF@T)JN4_9F^y|m<1;9-=@3*$wH2ZOG?AYLtJ*ut?a{>Sr!89(< zy37r3y*JA6$Twny-iSjZC;r~|R(w-qfX{v%YR#xV;9RTA?05M%z^nC8c&%Lw}u1*TDc3$oO@M6V)cc=iED7EN7qQ4Ql;S z=EgU;`JLFs&_M_@Pp$K23_MN%%8p?Q(VRDaFs`g;5Lk4_HxH=G@FrnNmf;x8Sfitj zMYHLnj};uLaIKx;5Lss*E*`UuHxbe>K%%u4Zrk8u>%L($|EyK^X<$sGHW$3y3l6~) zgQNnTAXRgX^&pYokmZB8L$CuGV-21XIOxoWZN*|+iUef6FdObm4Q8>SQt<0sO8p0a z>}Q{T-4A}l{&!wB%`>nw=Z3sHOfjz+7m`=TJmS6Z2ym+t^T_sq>kf8jc%~nBxCi9V znVyTS{Wm|pNMVb#$TfQDOY(FuM%lq&326lO}CelCD&Zh)NAqR79~wYY&3h5Q~VQcuB!(gcD>>g- zrjns0JzC#0Z~N67vuryzjpRSTC^>!H=y>*>hqt6Q!$?`%JR)0Yz4ueSXBFaLA`)-Z z(f~W-e}TC9Sg znq;luz^+U2U|qr6jKww2u33w^)FXi#xc$SFKcpE$xez9_uvdkZ{sfFq`VRnWi{=#1 zf_p#SxhYFT1a?0|>%aK6bl|q!m=?5Jl@&aN`z2smAM?Pv+1et?v zKGmzIxPLJmyz=>F1h&Dxum1ukoO|~}En9`DJyquA!b9&ziyRnx;H9e={q<%I6rv#( zh8=8rmbD}()1$SRC(o-s8kL9$`HY@Zu|WXIuk zBh2SQ!oR~BMdsbtZ1E$)I*Up!+$tes>Nl1p%Gz763*q%eL%6J&sPgj%ENt(R%m?!j z95PLqo4{}Q^}qP?JATVIE_J@+&h+*ng+B}DL-a3Lt>dG5k75_z2RawM$2BjIH|3B0 ze-m3k3%s|^!Y$*yg}yVmf8~$;;LCsizkH9jkAJ%6J&V~E{L2du&^(|lo1PtiZymc- zyCpJsg~eNMgwa^q13E@>CRz<|`3@&?xj%~oN;_@pVBK;Su8Dcqx%U$+V6YR+J8iz# zbRqXr0_!)p_4Zrs$WY!t%V~}Cvmw6TyIm{jc?+*qbOZilfr?ER`FSwt>1F-=Lk${R z?_cp8TMM+dmnHIf03Zsj^5Ht|c=SXMg9wf;xUSK}9^D7Ob76W3mI$buN+q(qmwh_V z8Mg8a-3DBDZNsXqoNp9yj4#qXsDfR@_+kxZDCnOrh^UB5s}SF7O;ZtDK&f@?0TFu2 zF2Zp{(bQ;HYgp|SXBeVOJG|)Wt~eMz48FO?)S_1)zS&Se8jbCJa!!NQl|Ey0r(QTG zYVv0+sHt#jgEDf#2Rl0FxIky{qq9H$KLEV8#N! zaq{2<1Y^kIL<`-N`HyN;08%(+>JL&(R(HQ-8sGr$nDhQrO1*v~@x2bTSJ zPx4Y?3XPyfUyU=Ez%9(=-*Z2-Z`hv4%xRTUPc4yw`!q1;X8Il=e%DVS_Oh~Wuq(dj z+Re@nF=&b_o`xU)OeZULo#tf^+=Oeg_V%h&CxR=Nmql>*_u<@U&d8eo zOQluQAPXmbrYEC1WLptlb7I0m#Kj%7EKYCQS?>((DDR z+Wt&m3Atg4AuX3#PUC+LYTQu17{v>=t-fh38^;+&>fj0=x($C`5crEv^hXy|@?={VpdnVra zC4F(C;LwBk(tl|5&^*X3sbxF>{R)JB>VN%JFaN6k0I=G!p&KL9=`)f%PqtEiAMt*a z*GFZ3M#`f_JkRyKxxo1sTczfYnkh1OCXagWG3~F|^1dL0Ue*LM-MP1w%+cix&XRat zGBbC}`;3@KdCl69%cPg0xV(FaWpd#F87+9>WJrefHe(2Q)+HV-USRWH^B(xtY&Jz4 z`b>?mur1{=H(?^bx}uj8X@${jCWxR$teuTJoi2YHCvVnbXIBY_Q1z}&Q=ZqLUDG#d z9Vd7dNwrtHvG??aN>!Y+j zM8lEcJ+h{5$&ka!Pz$8?#_YIMEK(5<8A4sIr4k>tHzTO?wm2~L-Pp014_ zvvXlcqv}b@s9CXB2@=?c0z2kWC@z-6HNv-9Is~_ zc`~wad;JI{(>&^hDdRIDKdQMK00uo@h)89$rwni)r5O!|{lQ`X1(=uPK935EG}Ziz zlO|%gqgGmZ(CCm)20!fFiDLDWAfSW5D!k)b1rD?dukb0+vK};VuRpW}j)q+=h)1ts zMa&Vr>t(dpAtH1xI4?0+NkMFt3IO4WTX@g7IYfR8w<}<)uD(5?E zMiE-cT2QvXs?97fNRe-VpC^&qD5lN z*X3nrB%q8wf6s6F*_Yq=qrbTFpQ)`8|4igpNU!L=lDePE9hkYlQuhN>Z%aL{mwkF% zY->K@KBhg1O5;PXHn97sfFIPyVhg*B$GGoQl>f;;|I>f|PXN3EydU&1(BAq*MvmVO zJlnGUynD%E%4u#Ifb4(MjiW^p9)PR(1840Yx`8)R~bXB*Jm8rXhUE1zHDEh1rfbAbmJ z-@lMrLjm*96nYV2yGl9!UZ}?0o6_@#d!}suDQI0*qYSiUdhMHr>acI#0YY5^1&Q;~Mb;CPk0NKCjhOGj zeyR?A_7EI3_}q1n2>`Ai_3 z#S{RI)YxcZP6?SD+85L`u}Rmw;&-|dxM&>QkKO$E{{V2if_EE4Aj`iuVYWBqTMCC| zu{c~NZnjxIbiB~^p}uWp{C#w>q%#jrPNiV!XU?O;ZGs1d%Dh0sO*`({w^VVP==Q;Y z?g!OH#SxL09dQdhcYwB8jzotLnRd$i^A#E{VttZbIAU3t)@LG`pCu0 z0Z@R`_hhpIc*-A1=u6^~6YlnU5W0n2{M}s2I=QQ5MknJaP;=0ha^H$L*tOBR<)zBX zk=W~wD3zDCrP@sgD&;EnB63OX%+x0WO?~;l=q$~~(XM<30J1bpPsAH_F27*PoXf<$ za?#To5ohnZCms=P58ewGnVJ^L@s`P+iz*`oXJwigy<->Q9^ ze=q+TuJKFy;-sKM58|tx=I$VOfyLOydoUaQ%VPc8|Jhex{@b7W-n@HVLC?u$PJZk8 z8J_n5n+YV(NX9%|6D8JXv5y+hlK=U5WxifB`SWh^>r%L^06?7RGf`6;o*6spSg@1|PBmm;@4X?L zYudqM4TBZB^*j&Q2x{l)M4=}OH*Fg199kHB=^sD>-B9Y7d(=x^#fMmR;_DAS0l7y^|14y@3n=1NvKGZU zNZbV(G0a{u&u@Uon3dc@l}HhA3ar?{`xMMi?+*ZbAC9AOv(z^m;$wE~P_`Y8f_=!= zE)H(Rb4(_jO7a|F#7I+^d7k8BGbB~~8Upme=ddHeF_U3it~GJUA;JTf2LsP_%AXSq zI`zs!B~2B9wRCEz{mjIPIeb=K6-Fi3FyojFW;*cD}hrVy^);tOafn z#R%{WqT)SW`wB)JLkYSV+907$QjU_=_nw1of!P>y5C95~PcaHj4bL=A@=z>X?xvJP zUVRV8O)3^e3}0oB`y_aSQr$#GUo-+|4=8y)3M#jzOaNnxW4zeeaO+!At3i#c`2v`@Raopxi$(C#|80{qt z#W^F!qhO%!)$^Gr$(7nE5Y4D1J2Uc8C#2-WaHk%&lQAy$R$bVsyg+I+7?eBLCYc&| z-qGuapXPq|PyWoy_kQ=a1E-GAVU~s1HXmE6_$KC-`JLk2)_tGeP%_-FKrHAu+QEH3$%>g)e2$IOdC4PgqT-* zVVHLvf0z!HAXeTd`Ga*;02>z!u774Q9*j6zw9GPf8m(prFCZvkrI!&cBrIyJ1Z09E zsV}ehK$0SCn&;&S9SOL&zUU^iZ`GAc)Fq5a-@wCxzI6`!3<7<0J&Ox_(p6Yjz(ZMQ zO@Rw`af{Tv&zYj+OX+TH*GN$UrGfEIcTb4t`$aH31HSAES>O91$3II*&*y{=(dFYS zM)a}S?+DD@I-6k3In#YEqllJ|LMN39tDvZ3&$!$16kDIcreV6tU(|L%ThnK%_nx4f z)l;8^Xoheyn&64zV;dke=pP(4&PM9Br4128;=O;xl{ zH<@|-?0D~w@lm2pa}Was2JS8%{sQSPCfM0AxCKFd1$FmnxaaiIzxO>7*rxf1m+ z0%I%c^QKhJ!@0Lh#d~1|)|D?L^`Q%X*!5x1>wE@*NDlOKc@Xy+bt4cwr`JL5b3Si|`Z zx@zD(%t`BM?k4q+AIvZ3{h$4VUwiq+&(mjm%`=lH*HJ$heg2rFo}qku<2eDRe**HR znD-E#f1U!bhbeE7oB4_~Z&GJ?HoFGtCU=hV>C@rrf=O#x zZ?9MPJ8K6&TGN50l1TXhGv4ab9*FkZ2PJEW466`c*Z!tvVd1Gmh4K?l z4}CzaX%$zEWzMvp4!Qdhfro7_v(QyAi1szL78WBln9Lbof3OfD{erY8k^XBxDdKo? zGP#KB6J4BDfvnRu-ngC5`xPoZ*RHMy-Rf>^)*(B&R&A$G%ZC7oh^(1*f#-~n_4HOP z8SD|hld9<#wNZ|B7mPv9|Fu;rv8&fr$+Xg|OItJDq+N9gNJ)PKQQmI*qJN)jf!Isy zh1i)Hk%*GcSEH~4P4UqX68F4L@iQ*lbGwPf4T<%d{mptPO9|rC0cL27t6o9i45CNP z7g59U^>Ln9*Uvs2Y7^IoFL6A}5R`Rc_koVTSWk$gF!;b*arT8C6rUvKT$+IS?o7bN2|;Ks2$_#nMF-?ud8XK3b;5K*Ktzk~!A~L2j;u$9p<1SQl{nqPX8iq&@OP z+#?&`;8(x@+b{pjM8(F_pqnQBxO2%t~R}?7z<>ylc zbmfmOs8$ysaeZvXt>aW|$ET{sG2IK!*%M+B8cpxd)UfOnveU?jCU%e5*li8xs7Ei0 zTw`FH7z}Ccfl(rP{XC~k(c4oAPszCk(6X+4jq^tq+fxUU;b5@ik1llfg73Zadkm3i z5tk=$Ru>Hj?5PWl0@EGEIZh#f6B>=qK- zPzQ&V9;}xR+~Wypt;M%oy>reerj!iUYn7<66mAnAwf|B70pMq+Rz)| zPArQ<5vD>s0lKF4jE;>{Ne|rWn`I0K9!*80?TcafGO5RlGT|O55r~YO`FLn_Ad4ov zurtnMMXfxn$)dq0H%3=ZE7g^t5-8~3^eck7*>raTM6csi2tc31m&l zn!!yUXB|LebYyvMMPqwi(Xgpe>r9wHjjVjIwvYb7;Q|+H>BJ*M(qidCZoYYkw>zsp z@J`sBi4V?L-%<_PSbL=I?N#(`_<_F#=NCr`OpOaceaxQtEV{4sm{kZbE35v&j}lI1 za}{?j)QT+5(&j@;H-mAG4VaBEL>Lmpq+v|$d_&H93ZZm*!_K*nez<3QRx!-tg)t?! zY5uwu_xfOoQÎ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

e|Y9GL3Q!8VJV4wPWG)j344a}@y)oqqE-|g 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%0D=hT1eysbFp4Ch)Yiw93?RelG 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ºoW`? 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�bnk0*(-antzC@lJogZli$tM#o_dIp(%F*( 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 GYOWs!CawOk&- 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 z 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�pOkX`AE*wFm@kQLN{PbgOSDJ=tNXr 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≪djXzUEHo(KHRp;Ix=Py+1(d&RUV(R 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
  • -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
  • bp&!4Cc!e6q30I=;N>Vq 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 G`RhO02%u>I literal 0 HcmV?d00001 diff --git a/bundle/osx/WebEngine.entitlements b/bundle/osx/WebEngine.entitlements new file mode 100644 index 0000000..794eada --- /dev/null +++ b/bundle/osx/WebEngine.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.inherit + + + diff --git a/bundle/osx/qt.conf b/bundle/osx/qt.conf new file mode 100644 index 0000000..df50e9d --- /dev/null +++ b/bundle/osx/qt.conf @@ -0,0 +1,4 @@ +[Paths] +Plugins = ../PlugIns +Imports = ../Resources/qml +Qml2Imports = ../Resources/qml diff --git a/bundle/win/Plex.ico b/bundle/win/Plex.ico new file mode 100644 index 0000000000000000000000000000000000000000..33034c71c40c84183a53e2a392f91fe7e30eaf80 GIT binary patch literal 44821 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 + +@interface SPInvocationGrabber : NSObject { + id _object; + NSInvocation *_invocation; + int frameCount; + char **frameStrings; + BOOL backgroundAfterForward; + BOOL onMainAfterForward; + BOOL waitUntilDone; +} +-(id)initWithObject:(id)obj; +-(id)initWithObject:(id)obj stacktraceSaving:(BOOL)saveStack; +@property (readonly, retain, nonatomic) id object; +@property (readonly, retain, nonatomic) NSInvocation *invocation; +@property BOOL backgroundAfterForward; +@property BOOL onMainAfterForward; +@property BOOL waitUntilDone; +-(void)invoke; // will release object and invocation +-(void)printBacktrace; +-(void)saveBacktrace; +@end + +@interface NSObject (SPInvocationGrabbing) +-(id)grab; +-(id)invokeAfter:(NSTimeInterval)delta; +-(id)nextRunloop; +-(id)inBackground; +-(id)onMainAsync:(BOOL)async; +@end diff --git a/external/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m b/external/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m new file mode 100644 index 0000000..ce3b9f4 --- /dev/null +++ b/external/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m @@ -0,0 +1,127 @@ +#import "NSObject+SPInvocationGrabbing.h" +#import + +#pragma mark Invocation grabbing +@interface SPInvocationGrabber () +@property (readwrite, retain, nonatomic) id object; +@property (readwrite, retain, nonatomic) NSInvocation *invocation; + +@end + +@implementation SPInvocationGrabber +- (id)initWithObject:(id)obj; +{ + return [self initWithObject:obj stacktraceSaving:YES]; +} + +-(id)initWithObject:(id)obj stacktraceSaving:(BOOL)saveStack; +{ + self.object = obj; + + if(saveStack) + [self saveBacktrace]; + + return self; +} +-(void)dealloc; +{ + free(frameStrings); + self.object = nil; + self.invocation = nil; + [super dealloc]; +} +@synthesize invocation = _invocation, object = _object; + +@synthesize backgroundAfterForward, onMainAfterForward, waitUntilDone; +- (void)runInBackground; +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + @try { + [self invoke]; + } + @finally { + [pool drain]; + } +} + + +- (void)forwardInvocation:(NSInvocation *)anInvocation { + [anInvocation retainArguments]; + anInvocation.target = _object; + self.invocation = anInvocation; + + if(backgroundAfterForward) + [NSThread detachNewThreadSelector:@selector(runInBackground) toTarget:self withObject:nil]; + else if(onMainAfterForward) + [self performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:waitUntilDone]; +} +- (NSMethodSignature *)methodSignatureForSelector:(SEL)inSelector { + NSMethodSignature *signature = [super methodSignatureForSelector:inSelector]; + if (signature == NULL) + signature = [_object methodSignatureForSelector:inSelector]; + + return signature; +} + +- (void)invoke; +{ + + @try { + [_invocation invoke]; + } + @catch (NSException * e) { + NSLog(@"SPInvocationGrabber's target raised %@:\n\t%@\nInvocation was originally scheduled at:", e.name, e); + [self printBacktrace]; + printf("\n"); + [e raise]; + } + + self.invocation = nil; + self.object = nil; +} + +-(void)saveBacktrace; +{ + void *backtraceFrames[128]; + frameCount = backtrace(&backtraceFrames[0], 128); + frameStrings = backtrace_symbols(&backtraceFrames[0], frameCount); +} +-(void)printBacktrace; +{ + for(int x = 3; x < frameCount; x++) { + if(frameStrings[x] == NULL) { break; } + printf("%s\n", frameStrings[x]); + } +} +@end + +@implementation NSObject (SPInvocationGrabbing) +-(id)grab; +{ + return [[[SPInvocationGrabber alloc] initWithObject:self] autorelease]; +} +-(id)invokeAfter:(NSTimeInterval)delta; +{ + id grabber = [self grab]; + [NSTimer scheduledTimerWithTimeInterval:delta target:grabber selector:@selector(invoke) userInfo:nil repeats:NO]; + return grabber; +} +- (id)nextRunloop; +{ + return [self invokeAfter:0]; +} +-(id)inBackground; +{ + SPInvocationGrabber *grabber = [self grab]; + grabber.backgroundAfterForward = YES; + return grabber; +} +-(id)onMainAsync:(BOOL)async; +{ + SPInvocationGrabber *grabber = [self grab]; + grabber.onMainAfterForward = YES; + grabber.waitUntilDone = !async; + return grabber; +} + +@end diff --git a/external/SPMediaKeyTap/SPMediaKeyTap.h b/external/SPMediaKeyTap/SPMediaKeyTap.h new file mode 100644 index 0000000..aa974d2 --- /dev/null +++ b/external/SPMediaKeyTap/SPMediaKeyTap.h @@ -0,0 +1,43 @@ +#include +#import +#import + +// http://overooped.com/post/2593597587/mediakeys + +#define SPSystemDefinedEventMediaKeys 8 + +@interface SPMediaKeyTap : NSObject { + EventHandlerRef _app_switching_ref; + EventHandlerRef _app_terminating_ref; + CFMachPortRef _eventPort; + CFRunLoopSourceRef _eventPortSource; + CFRunLoopRef _tapThreadRL; + BOOL _shouldInterceptMediaKeyEvents; + id _delegate; + // The app that is frontmost in this list owns media keys + NSMutableArray *_mediaKeyAppList; +} ++ (NSArray*)defaultMediaKeyUserBundleIdentifiers; + +-(id)initWithDelegate:(id)delegate; + ++(BOOL)usesGlobalMediaKeyTap; +-(void)startWatchingMediaKeys; +-(void)stopWatchingMediaKeys; +-(void)handleAndReleaseMediaKeyEvent:(NSEvent *)event; +@end + +@interface NSObject (SPMediaKeyTapDelegate) +-(void)mediaKeyTap:(SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)event; +@end + +#ifdef __cplusplus +extern "C" { +#endif + +extern NSString *kMediaKeyUsingBundleIdentifiersDefaultsKey; +extern NSString *kIgnoreMediaKeysDefaultsKey; + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/external/SPMediaKeyTap/SPMediaKeyTap.m b/external/SPMediaKeyTap/SPMediaKeyTap.m new file mode 100644 index 0000000..54ae878 --- /dev/null +++ b/external/SPMediaKeyTap/SPMediaKeyTap.m @@ -0,0 +1,346 @@ +// Copyright (c) 2010 Spotify AB +#import "SPMediaKeyTap.h" +#import "SPInvocationGrabbing/NSObject+SPInvocationGrabbing.h" // https://gist.github.com/511181, in submodule + +@interface SPMediaKeyTap () +-(BOOL)shouldInterceptMediaKeyEvents; +-(void)setShouldInterceptMediaKeyEvents:(BOOL)newSetting; +-(void)startWatchingAppSwitching; +-(void)stopWatchingAppSwitching; +-(void)eventTapThread; +@end +static SPMediaKeyTap *singleton = nil; + +static pascal OSStatus appSwitched (EventHandlerCallRef nextHandler, EventRef evt, void* userData); +static pascal OSStatus appTerminated (EventHandlerCallRef nextHandler, EventRef evt, void* userData); +static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon); + + +// Inspired by http://gist.github.com/546311 + +@implementation SPMediaKeyTap + +#pragma mark - +#pragma mark Setup and teardown +-(id)initWithDelegate:(id)delegate; +{ + _delegate = delegate; + [self startWatchingAppSwitching]; + singleton = self; + _mediaKeyAppList = [NSMutableArray new]; + _tapThreadRL=nil; + _eventPort=nil; + _eventPortSource=nil; + return self; +} +-(void)dealloc; +{ + [self stopWatchingMediaKeys]; + [self stopWatchingAppSwitching]; + [_mediaKeyAppList release]; + [super dealloc]; +} + +-(void)startWatchingAppSwitching; +{ + // Listen to "app switched" event, so that we don't intercept media keys if we + // weren't the last "media key listening" app to be active + EventTypeSpec eventType = { kEventClassApplication, kEventAppFrontSwitched }; + OSStatus err = InstallApplicationEventHandler(NewEventHandlerUPP(appSwitched), 1, &eventType, self, &_app_switching_ref); + assert(err == noErr); + + eventType.eventKind = kEventAppTerminated; + err = InstallApplicationEventHandler(NewEventHandlerUPP(appTerminated), 1, &eventType, self, &_app_terminating_ref); + assert(err == noErr); +} +-(void)stopWatchingAppSwitching; +{ + if(!_app_switching_ref) return; + RemoveEventHandler(_app_switching_ref); + _app_switching_ref = NULL; +} + +-(void)startWatchingMediaKeys;{ + // Prevent having multiple mediaKeys threads + [self stopWatchingMediaKeys]; + + [self setShouldInterceptMediaKeyEvents:YES]; + + // Add an event tap to intercept the system defined media key events + _eventPort = CGEventTapCreate(kCGSessionEventTap, + kCGHeadInsertEventTap, + kCGEventTapOptionDefault, + CGEventMaskBit(NX_SYSDEFINED), + tapEventCallback, + self); + assert(_eventPort != NULL); + + _eventPortSource = CFMachPortCreateRunLoopSource(kCFAllocatorSystemDefault, _eventPort, 0); + assert(_eventPortSource != NULL); + + // Let's do this in a separate thread so that a slow app doesn't lag the event tap + [NSThread detachNewThreadSelector:@selector(eventTapThread) toTarget:self withObject:nil]; +} +-(void)stopWatchingMediaKeys; +{ + // TODO: Shut down thread, remove event tap port and source + + if(_tapThreadRL){ + CFRunLoopStop(_tapThreadRL); + _tapThreadRL=nil; + } + + if(_eventPort){ + CFMachPortInvalidate(_eventPort); + CFRelease(_eventPort); + _eventPort=nil; + } + + if(_eventPortSource){ + CFRelease(_eventPortSource); + _eventPortSource=nil; + } +} + +#pragma mark - +#pragma mark Accessors + ++(BOOL)usesGlobalMediaKeyTap +{ +#ifdef _DEBUG + // breaking in gdb with a key tap inserted sometimes locks up all mouse and keyboard input forever, forcing reboot + return NO; +#else + // XXX(nevyn): MediaKey event tap doesn't work on 10.4, feel free to figure out why if you have the energy. + return + ![[NSUserDefaults standardUserDefaults] boolForKey:kIgnoreMediaKeysDefaultsKey] + && floor(NSAppKitVersionNumber) >= 949/*NSAppKitVersionNumber10_5*/; +#endif +} + ++ (NSArray*)defaultMediaKeyUserBundleIdentifiers; +{ + return [NSArray arrayWithObjects: + [[NSBundle mainBundle] bundleIdentifier], // your app + @"com.spotify.client", + @"com.apple.iTunes", + @"com.apple.QuickTimePlayerX", + @"com.apple.quicktimeplayer", + @"com.apple.iWork.Keynote", + @"com.apple.iPhoto", + @"org.videolan.vlc", + @"com.apple.Aperture", + @"com.plexsquared.Plex", + @"com.soundcloud.desktop", + @"org.niltsh.MPlayerX", + @"com.ilabs.PandorasHelper", + @"com.mahasoftware.pandabar", + @"com.bitcartel.pandorajam", + @"org.clementine-player.clementine", + @"fm.last.Last.fm", + @"fm.last.Scrobbler", + @"com.beatport.BeatportPro", + @"com.Timenut.SongKey", + @"com.macromedia.fireworks", // the tap messes up their mouse input + @"at.justp.Theremin", + @"ru.ya.themblsha.YandexMusic", + @"com.jriver.MediaCenter18", + @"com.jriver.MediaCenter19", + @"com.jriver.MediaCenter20", + @"co.rackit.mate", + @"com.ttitt.b-music", + @"com.beardedspice.BeardedSpice", + @"com.plug.Plug", + @"com.plug.Plug2", + @"com.netease.163music", + nil + ]; +} + + +-(BOOL)shouldInterceptMediaKeyEvents; +{ + BOOL shouldIntercept = NO; + @synchronized(self) { + shouldIntercept = _shouldInterceptMediaKeyEvents; + } + return shouldIntercept; +} + +-(void)pauseTapOnTapThread:(BOOL)yeahno; +{ + CGEventTapEnable(self->_eventPort, yeahno); +} +-(void)setShouldInterceptMediaKeyEvents:(BOOL)newSetting; +{ + BOOL oldSetting; + @synchronized(self) { + oldSetting = _shouldInterceptMediaKeyEvents; + _shouldInterceptMediaKeyEvents = newSetting; + } + if(_tapThreadRL && oldSetting != newSetting) { + id grab = [self grab]; + [grab pauseTapOnTapThread:newSetting]; + NSTimer *timer = [NSTimer timerWithTimeInterval:0 invocation:[grab invocation] repeats:NO]; + CFRunLoopAddTimer(_tapThreadRL, (CFRunLoopTimerRef)timer, kCFRunLoopCommonModes); + } +} + +#pragma mark +#pragma mark - +#pragma mark Event tap callbacks + +// Note: method called on background thread + +static CGEventRef tapEventCallback2(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) +{ + SPMediaKeyTap *self = refcon; + + if(type == kCGEventTapDisabledByTimeout) { + NSLog(@"Media key event tap was disabled by timeout"); + CGEventTapEnable(self->_eventPort, TRUE); + return event; + } else if(type == kCGEventTapDisabledByUserInput) { + // Was disabled manually by -[pauseTapOnTapThread] + return event; + } + NSEvent *nsEvent = nil; + @try { + nsEvent = [NSEvent eventWithCGEvent:event]; + } + @catch (NSException * e) { + NSLog(@"Strange CGEventType: %d: %@", type, e); + assert(0); + return event; + } + + if (type != NX_SYSDEFINED || [nsEvent subtype] != SPSystemDefinedEventMediaKeys) + return event; + + int keyCode = (([nsEvent data1] & 0xFFFF0000) >> 16); + if (keyCode != NX_KEYTYPE_PLAY && keyCode != NX_KEYTYPE_FAST && keyCode != NX_KEYTYPE_REWIND && keyCode != NX_KEYTYPE_PREVIOUS && keyCode != NX_KEYTYPE_NEXT) + return event; + + if (![self shouldInterceptMediaKeyEvents]) + return event; + + [nsEvent retain]; // matched in handleAndReleaseMediaKeyEvent: + [self performSelectorOnMainThread:@selector(handleAndReleaseMediaKeyEvent:) withObject:nsEvent waitUntilDone:NO]; + + return NULL; +} + +static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) +{ + NSAutoreleasePool *pool = [NSAutoreleasePool new]; + CGEventRef ret = tapEventCallback2(proxy, type, event, refcon); + [pool drain]; + return ret; +} + + +// event will have been retained in the other thread +-(void)handleAndReleaseMediaKeyEvent:(NSEvent *)event { + [event autorelease]; + + [_delegate mediaKeyTap:self receivedMediaKeyEvent:event]; +} + + +-(void)eventTapThread; +{ + _tapThreadRL = CFRunLoopGetCurrent(); + CFRunLoopAddSource(_tapThreadRL, _eventPortSource, kCFRunLoopCommonModes); + CFRunLoopRun(); +} + +#pragma mark Task switching callbacks + +NSString *kMediaKeyUsingBundleIdentifiersDefaultsKey = @"SPApplicationsNeedingMediaKeys"; +NSString *kIgnoreMediaKeysDefaultsKey = @"SPIgnoreMediaKeys"; + + + +-(void)mediaKeyAppListChanged; +{ + if([_mediaKeyAppList count] == 0) return; + + /*NSLog(@"--"); + int i = 0; + for (NSValue *psnv in _mediaKeyAppList) { + ProcessSerialNumber psn; [psnv getValue:&psn]; + NSDictionary *processInfo = [(id)ProcessInformationCopyDictionary( + &psn, + kProcessDictionaryIncludeAllInformationMask + ) autorelease]; + NSString *bundleIdentifier = [processInfo objectForKey:(id)kCFBundleIdentifierKey]; + NSLog(@"%d: %@", i++, bundleIdentifier); + }*/ + + ProcessSerialNumber mySerial, topSerial; + GetCurrentProcess(&mySerial); + [[_mediaKeyAppList objectAtIndex:0] getValue:&topSerial]; + + Boolean same; + OSErr err = SameProcess(&mySerial, &topSerial, &same); + [self setShouldInterceptMediaKeyEvents:(err == noErr && same)]; + +} +-(void)appIsNowFrontmost:(ProcessSerialNumber)psn; +{ + NSValue *psnv = [NSValue valueWithBytes:&psn objCType:@encode(ProcessSerialNumber)]; + + NSDictionary *processInfo = [(id)ProcessInformationCopyDictionary( + &psn, + kProcessDictionaryIncludeAllInformationMask + ) autorelease]; + NSString *bundleIdentifier = [processInfo objectForKey:(id)kCFBundleIdentifierKey]; + + NSArray *whitelistIdentifiers = [[NSUserDefaults standardUserDefaults] arrayForKey:kMediaKeyUsingBundleIdentifiersDefaultsKey]; + if(![whitelistIdentifiers containsObject:bundleIdentifier]) return; + + [_mediaKeyAppList removeObject:psnv]; + [_mediaKeyAppList insertObject:psnv atIndex:0]; + [self mediaKeyAppListChanged]; +} +-(void)appTerminated:(ProcessSerialNumber)psn; +{ + NSValue *psnv = [NSValue valueWithBytes:&psn objCType:@encode(ProcessSerialNumber)]; + [_mediaKeyAppList removeObject:psnv]; + [self mediaKeyAppListChanged]; +} + +static pascal OSStatus appSwitched (EventHandlerCallRef nextHandler, EventRef evt, void* userData) +{ + SPMediaKeyTap *self = (id)userData; + + ProcessSerialNumber newSerial; + GetFrontProcess(&newSerial); + + [self appIsNowFrontmost:newSerial]; + + return CallNextEventHandler(nextHandler, evt); +} + +static pascal OSStatus appTerminated (EventHandlerCallRef nextHandler, EventRef evt, void* userData) +{ + SPMediaKeyTap *self = (id)userData; + + ProcessSerialNumber deadPSN; + + GetEventParameter( + evt, + kEventParamProcessID, + typeProcessSerialNumber, + NULL, + sizeof(deadPSN), + NULL, + &deadPSN + ); + + + [self appTerminated:deadPSN]; + return CallNextEventHandler(nextHandler, evt); +} + +@end diff --git a/external/plistparser/CMakeLists.txt b/external/plistparser/CMakeLists.txt new file mode 100644 index 0000000..f9e06e5 --- /dev/null +++ b/external/plistparser/CMakeLists.txt @@ -0,0 +1,8 @@ +include_directories(.) + +add_library(plistparser STATIC + plistparser.cpp + plistparser.h + plistserializer.cpp + plistserializer.h +) \ No newline at end of file diff --git a/external/plistparser/LICENSE b/external/plistparser/LICENSE new file mode 100644 index 0000000..2e90774 --- /dev/null +++ b/external/plistparser/LICENSE @@ -0,0 +1,22 @@ +Gist: this is an MIT license. Act accordingly (basically, do whatever you want). +It would be nice to get an email from you if you use this, but if not that's also cool. + +Copyright (c) 2010 Reilly Watson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/external/plistparser/plistparser.cpp b/external/plistparser/plistparser.cpp new file mode 100644 index 0000000..76e24c0 --- /dev/null +++ b/external/plistparser/plistparser.cpp @@ -0,0 +1,98 @@ +// Own includes +#include "plistparser.h" + +// Qt includes +#include +#include +#include +#include + +QVariant PListParser::parsePList(QIODevice *device) { + QVariantMap result; + QDomDocument doc; + QString errorMessage; + int errorLine; + int errorColumn; + bool success = doc.setContent(device, false, &errorMessage, &errorLine, &errorColumn); + if (!success) { + qDebug() << "PListParser Warning: Could not parse PList file!"; + qDebug() << "Error message: " << errorMessage; + qDebug() << "Error line: " << errorLine; + qDebug() << "Error column: " << errorColumn; + return result; + } + QDomElement root = doc.documentElement(); + if (root.attribute(QStringLiteral("version"), QStringLiteral("1.0")) != QLatin1String("1.0")) { + qDebug() << "PListParser Warning: plist is using an unknown format version, parsing might fail unexpectedly"; + } + return parseElement(root.firstChild().toElement()); +} + +QVariant PListParser::parseElement(const QDomElement &e) { + QString tagName = e.tagName(); + QVariant result; + if (tagName == QLatin1String("dict")) { + result = parseDictElement(e); + } + else if (tagName == QLatin1String("array")) { + result = parseArrayElement(e); + } + else if (tagName == QLatin1String("string")) { + result = e.text(); + } + else if (tagName == QLatin1String("data")) { + result = QByteArray::fromBase64(e.text().toUtf8()); + } + else if (tagName == QLatin1String("integer")) { + result = e.text().toInt(); + } + else if (tagName == QLatin1String("real")) { + result = e.text().toFloat(); + } + else if (tagName == QLatin1String("true")) { + result = true; + } + else if (tagName == QLatin1String("false")) { + result = false; + } + else if (tagName == QLatin1String("date")) { + result = QDateTime::fromString(e.text(), Qt::ISODate); + } + else { + qDebug() << "PListParser Warning: Invalid tag found: " << e.tagName() << e.text(); + } + return result; +} + +QVariantList PListParser::parseArrayElement(const QDomElement& element) { + QVariantList result; + QDomNodeList children = element.childNodes(); + for (int i = 0; i < children.count(); i++) { + QDomNode child = children.at(i); + QDomElement e = child.toElement(); + if (!e.isNull()) { + result.append(parseElement(e)); + } + } + return result; +} + +QVariantMap PListParser::parseDictElement(const QDomElement& element) { + QVariantMap result; + QDomNodeList children = element.childNodes(); + QString currentKey; + for (int i = 0; i < children.count(); i++) { + QDomNode child = children.at(i); + QDomElement e = child.toElement(); + if (!e.isNull()) { + QString tagName = e.tagName(); + if (tagName == QLatin1String("key")) { + currentKey = e.text(); + } + else if (!currentKey.isEmpty()) { + result[currentKey] = parseElement(e); + } + } + } + return result; +} diff --git a/external/plistparser/plistparser.h b/external/plistparser/plistparser.h new file mode 100644 index 0000000..a1d1982 --- /dev/null +++ b/external/plistparser/plistparser.h @@ -0,0 +1,18 @@ +#pragma once + +// Qt includes +#include +#include +#include +#include +#include + +class PListParser { +public: + static QVariant parsePList(QIODevice *device); +private: + static QVariant parseElement(const QDomElement &e); + static QVariantList parseArrayElement(const QDomElement& node); + static QVariantMap parseDictElement(const QDomElement& element); +}; + diff --git a/external/plistparser/plistserializer.cpp b/external/plistparser/plistserializer.cpp new file mode 100644 index 0000000..6aba024 --- /dev/null +++ b/external/plistparser/plistserializer.cpp @@ -0,0 +1,83 @@ +// Own includes +#include "plistserializer.h" + +// Qt includes +#include +#include +#include +#include + +static QDomElement textElement(QDomDocument& doc, const char *tagName, QString contents) { + QDomElement tag = doc.createElement(QString::fromLatin1(tagName)); + tag.appendChild(doc.createTextNode(contents)); + return tag; +} + +static QDomElement serializePrimitive(QDomDocument &doc, const QVariant &variant) { + QDomElement result; + if (variant.type() == QVariant::Bool) { + result = doc.createElement(variant.toBool() ? QStringLiteral("true") : QStringLiteral("false")); + } + else if (variant.type() == QVariant::Date) { + result = textElement(doc, "date", variant.toDate().toString(Qt::ISODate)); + } + else if (variant.type() == QVariant::DateTime) { + result = textElement(doc, "date", variant.toDateTime().toString(Qt::ISODate)); + } + else if (variant.type() == QVariant::ByteArray) { + result = textElement(doc, "data", QString::fromLatin1(variant.toByteArray().toBase64())); + } + else if (variant.type() == QVariant::String) { + result = textElement(doc, "string", variant.toString()); + } + else if (variant.type() == QVariant::Int) { + result = textElement(doc, "integer", QString::number(variant.toInt())); + } + else if (variant.canConvert(QVariant::Double)) { + QString num; + num.setNum(variant.toDouble()); + result = textElement(doc, "real", num); + } + return result; +} + +QDomElement PListSerializer::serializeElement(QDomDocument &doc, const QVariant &variant) { + if (variant.type() == QVariant::Map) { + return serializeMap(doc, variant.toMap()); + } + else if (variant.type() == QVariant::List) { + return serializeList(doc, variant.toList()); + } + else { + return serializePrimitive(doc, variant); + } +} + +QDomElement PListSerializer::serializeList(QDomDocument &doc, const QVariantList &list) { + QDomElement element = doc.createElement(QStringLiteral("array")); + foreach(QVariant item, list) { + element.appendChild(serializeElement(doc, item)); + } + return element; +} + +QDomElement PListSerializer::serializeMap(QDomDocument &doc, const QVariantMap &map) { + QDomElement element = doc.createElement(QStringLiteral("dict")); + QList keys = map.keys(); + foreach(QString key, keys) { + QDomElement keyElement = textElement(doc, "key", key); + element.appendChild(keyElement); + element.appendChild(serializeElement(doc, map[key])); + } + return element; +} + +QString PListSerializer::toPList(const QVariant &variant) { + QDomDocument document(QStringLiteral("plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"")); + document.appendChild(document.createProcessingInstruction(QStringLiteral("xml"), QStringLiteral("version=\"1.0\" encoding=\"UTF-8\""))); + QDomElement plist = document.createElement(QStringLiteral("plist")); + plist.setAttribute(QStringLiteral("version"), QStringLiteral("1.0")); + document.appendChild(plist); + plist.appendChild(serializeElement(document, variant)); + return document.toString(); +} diff --git a/external/plistparser/plistserializer.h b/external/plistparser/plistserializer.h new file mode 100644 index 0000000..92088b9 --- /dev/null +++ b/external/plistparser/plistserializer.h @@ -0,0 +1,20 @@ +#pragma once + +// Qt includes +#include +#include +#include +#include +#include +#include +#include + +class PListSerializer { +public: + static QString toPList(const QVariant &variant); +private: + static QDomElement serializeElement(QDomDocument &doc, const QVariant &variant); + static QDomElement serializeMap(QDomDocument &doc, const QVariantMap &map); + static QDomElement serializeList(QDomDocument &doc, const QVariantList &list); +}; + diff --git a/external/qhttpserver/.gitignore b/external/qhttpserver/.gitignore new file mode 100755 index 0000000..e7540b8 --- /dev/null +++ b/external/qhttpserver/.gitignore @@ -0,0 +1,30 @@ +# Folders +build +lib + +# Generated +Makefile +*.o +moc_* + +# Docs +docs/html + +# Build folders +*/debug +*/release +*/*/debug +*/*/release + +# Visual studio +*.suo +*.ncb +*.user +*.pdb +*.idb +*.vcproj +*.vcxproj +*.vcxproj.filters +*.lib +*.sln +*.rc diff --git a/external/qhttpserver/CMakeLists.txt b/external/qhttpserver/CMakeLists.txt new file mode 100644 index 0000000..8663bfb --- /dev/null +++ b/external/qhttpserver/CMakeLists.txt @@ -0,0 +1,8 @@ +aux_source_directory(src HTTP_SRC) + +include_directories(http-parser) +add_library(http-parser STATIC http-parser/http_parser.c) + +set(CMAKE_AUTOMOC ON) +add_library(qhttpserver STATIC ${HTTP_SRC} ${PARSER_SRC}) +target_link_libraries(qhttpserver http-parser) \ No newline at end of file diff --git a/external/qhttpserver/LICENSE b/external/qhttpserver/LICENSE new file mode 100755 index 0000000..4cac42a --- /dev/null +++ b/external/qhttpserver/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2011-2014 Nikhil Marathe + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/external/qhttpserver/README.md b/external/qhttpserver/README.md new file mode 100755 index 0000000..97e253c --- /dev/null +++ b/external/qhttpserver/README.md @@ -0,0 +1,72 @@ +QHttpServer +=========== + +A Qt HTTP Server - because hard-core programmers write web-apps in C++ :) + +It uses Joyent's [HTTP Parser](http://github.com/joyent/http-parser) and is asynchronous and does not require any inheritance. + +QHttpServer is available under the MIT License. + +**NOTE: QHttpServer is NOT fully HTTP compliant right now! DO NOT use it for +anything complex** + +Installation +------------ + +Requires Qt 4 or Qt 5. + + qmake && make && su -c 'make install' + +To link to your projects put this in your project's qmake project file + + LIBS += -lqhttpserver + +By default, the installation prefix is /usr/local. To change that to /usr, +for example, run: + + qmake -r PREFIX=/usr + +Usage +----- + +Include the headers + + #include + #include + #include + +Create a server, and connect to the signal for new requests + + QHttpServer *server = new QHttpServer; + connect(server, SIGNAL(newRequest(QHttpRequest*, QHttpResponse*)), + handler, SLOT(handle(QHttpRequest*, QHttpResponse*))); + + // let's go + server->listen(8080); + +In the handler, you may dispatch on routes or do whatever other things +you want. See the API documentation for what information +is provided about the request via the QHttpRequest object. + +To send data back to the browser and end the request: + + void Handler::handle(QHttpRequest *req, QHttpResponse *resp) + { + resp->setHeader("Content-Length", 11); + resp->writeHead(200); // everything is OK + resp->write("Hello World"); + resp->end(); + } + +The server and request/response objects emit various signals +and have guarantees about memory management. See the API documentation for +these. + +Contribute +---------- + +Feel free to file issues, branch and send pull requests. If you plan to work on a major feature (say WebSocket support), please run it by me first by filing an issue! qhttpserver has a narrow scope and API and I'd like to keep it that way, so a thousand line patch that implements the kitchen sink is unlikely to be accepted. + +- Nikhil Marathe (maintainer) + +Everybody who has ever contributed shows up in [Contributors](https://github.com/nikhilm/qhttpserver/graphs/contributors). diff --git a/external/qhttpserver/TODO b/external/qhttpserver/TODO new file mode 100755 index 0000000..d376a28 --- /dev/null +++ b/external/qhttpserver/TODO @@ -0,0 +1,7 @@ +* Expect & Continue stuff +* Chunked encoding support +* Only copy over public headers etc. +* connection object should connect to QHttpResponse::destroyed() +and stop pushing data into it or whatever if the object is destroyed. +* response object should keep track of emitting done() and not accept writes after that +* handle encoding in response write and end diff --git a/external/qhttpserver/docs/Doxyfile b/external/qhttpserver/docs/Doxyfile new file mode 100755 index 0000000..ebc46e7 --- /dev/null +++ b/external/qhttpserver/docs/Doxyfile @@ -0,0 +1,2314 @@ +# Doxyfile 1.8.5 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = QHttpServer + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = 0.1 + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "A Qt based asynchronous Http Server" + +# With the PROJECT_LOGO tag one can specify an logo or icon that is included in +# the documentation. The maximum height of the logo should not exceed 55 pixels +# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo +# to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = ./ + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese- +# Traditional, Croatian, Czech, Danish, Dutch, English, Esperanto, Farsi, +# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en, +# Korean, Korean-en, Latvian, Norwegian, Macedonian, Persian, Polish, +# Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, +# Turkish, Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = YES + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = YES + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a +# new page for each member. If set to NO, the documentation of a member will be +# part of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. +# +# Note For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by by putting a % sign in front of the word +# or globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO these classes will be included in the various overviews. This option has +# no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the +# todo list. This list is created by putting \todo commands in the +# documentation. +# The default value is: YES. + +GENERATE_TODOLIST = NO + +# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the +# test list. This list is created by putting \test commands in the +# documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES the list +# will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = NO + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. Do not use file names with spaces, bibtex cannot handle them. See +# also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO doxygen will only warn about wrong or incomplete parameter +# documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. +# Note: If this tag is empty the current directory is searched. + +INPUT = pages \ + ../src + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank the +# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, +# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, +# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, +# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, +# *.qsf, *.as and *.js. + +FILE_PATTERNS = *.cpp \ + *.inl \ + *.h \ + *.dox + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = ui_*.h \ + moc_* + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = ../examples + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER ) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES, then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +# If the CLANG_ASSISTED_PARSING tag is set to YES, then doxygen will use the +# clang parser (see: http://clang.llvm.org/) for more acurate parsing at the +# cost of reduced performance. This can be particularly helpful with template +# rich C++ code for which doxygen's built-in parser lacks the necessary type +# information. +# Note: The availability of this option depends on whether or not doxygen was +# compiled with the --with-libclang option. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- +# defined cascading style sheet that is included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefor more robust against future updates. +# Doxygen will copy the style sheet file to the output directory. For an example +# see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the stylesheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler ( hhc.exe). If non-empty +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated ( +# YES) or that it should be included in the master .chm file ( NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated ( +# YES) or a normal table of contents ( NO) in the .chm file. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using prerendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://www.mathjax.org/mathjax + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /conf.kde.in 2011. The slides +can be found on Github. + +%QHttpServer uses a signal-slots based mechanism +for all communication, so no inheritance is required. +It tries to be as asynchronous as possible, to the +extent that request body data is also delivered as and +when it is received over the socket via signals. This +kind of programming may take some getting used to. + +%QHttpServer is backed by Ryan +Dahl's secure and fast http parser which makes it streaming +till the lowest level. + +@section usage Usage + +Using %QHttpServer is very simple. Simply create a QHttpServer, +connect a slot to the newRequest() signal and use the request and +response objects. +See the QHttpServer class documentation for an example. + +@section memorymanagement Memory Management + +The QHttpRequest and QHttpResponse deletion policies +are such. + +QHttpRequest is never deleted by %QHttpServer. +Since it is not possible to determine till what point the application +may want access to its data, it is up to the application to delete it. +A recommended way to handle this is to create a new responder object for +every request and to delete the request in that object's destructor. The +object itself can be deleted by connecting to QHttpResponse's done() +slot as explained below. + +You should not delete the QHttpRequest object until it +has emitted an QHttpRequest::end() signal. + +QHttpResponse queues itself up for auto-deletion once the application +calls its end() method. Once the data has been flushed to the underlying +socket, the object will emit a QHttpResponse::done() signal before queueing itself up +for deletion. You should not interact with the response +object once it has emitted QHttpResponse::done() although actual deletion does not +happen until QHttpResponse::destroyed() is emitted. +QHttpResponse::done() serves as a useful way to handle memory management of the +application itself. For example: + +@code + +MyApp::MyApp() : QObject() +{ + QHttpServer *server = new QHttpServer(this); + connect(server, SIGNAL(newRequest(...)), this, SLOT(handle(...))); + s.listen(8080); +} + +void MyApp::handle(QHttpRequest *request, QHttpResponse *response) +{ + if (request->path() == x) // Match a route + new Responder(request, response); + else + new PageNotFound(request, response); +} + +... + +Responder::Responder(QHttpRequest *request, QHttpResponse *response) +{ + m_request = request; + + connect(request, SIGNAL(end()), response, SLOT(end())); + + // Once the request is complete, the response is sent. + // When the response ends, it deletes itself + // the Responder object connects to done() + // which will lead to it being deleted + // and this will delete the request. + // So all 3 are properly deleted. + connect(response, SIGNAL(done()), this, SLOT(deleteLater())); + + response->writeHead(200); + response->write("Quitting soon"); +} + +Responder::~Responder() +{ + delete m_request; + m_request = 0; +} + +@endcode + +*/ \ No newline at end of file diff --git a/external/qhttpserver/examples/bodydata/bodydata.cpp b/external/qhttpserver/examples/bodydata/bodydata.cpp new file mode 100755 index 0000000..1a2c7a1 --- /dev/null +++ b/external/qhttpserver/examples/bodydata/bodydata.cpp @@ -0,0 +1,76 @@ +#include "bodydata.h" + +#include +#include +#include +#include + +#include +#include +#include + +/// BodyData + +BodyData::BodyData() +{ + QHttpServer *server = new QHttpServer(this); + connect(server, SIGNAL(newRequest(QHttpRequest*, QHttpResponse*)), + this, SLOT(handleRequest(QHttpRequest*, QHttpResponse*))); + + server->listen(QHostAddress::Any, 8080); +} + +void BodyData::handleRequest(QHttpRequest *req, QHttpResponse *resp) +{ + new Responder(req, resp); +} + +/// Responder + +Responder::Responder(QHttpRequest *req, QHttpResponse *resp) + : m_req(req) + , m_resp(resp) +{ + QRegExp exp("^/user/([a-z]+$)"); + if (exp.indexIn(req->path()) == -1) + { + resp->writeHead(403); + resp->end(QByteArray("You aren't allowed here!")); + /// @todo There should be a way to tell request to stop streaming data + return; + } + + resp->setHeader("Content-Type", "text/html"); + resp->writeHead(200); + + QString name = exp.capturedTexts()[1]; + QString bodyStart = tr("BodyData App

    Hello %1!

    ").arg(name); + resp->write(bodyStart.toUtf8()); + + connect(req, SIGNAL(data(const QByteArray&)), this, SLOT(accumulate(const QByteArray&))); + connect(req, SIGNAL(end()), this, SLOT(reply())); + connect(m_resp, SIGNAL(done()), this, SLOT(deleteLater())); +} + +Responder::~Responder() +{ +} + +void Responder::accumulate(const QByteArray &data) +{ + m_resp->write(data); +} + +void Responder::reply() +{ + m_resp->end(QByteArray("

    ")); +} + +/// main + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + BodyData bodydata; + app.exec(); +} diff --git a/external/qhttpserver/examples/bodydata/bodydata.h b/external/qhttpserver/examples/bodydata/bodydata.h new file mode 100755 index 0000000..8fed37d --- /dev/null +++ b/external/qhttpserver/examples/bodydata/bodydata.h @@ -0,0 +1,39 @@ +#include "qhttpserverfwd.h" + +#include +#include + +/// BodyData + +class BodyData : public QObject +{ + Q_OBJECT + +public: + BodyData(); + +private slots: + void handleRequest(QHttpRequest *req, QHttpResponse *resp); +}; + +/// Responder + +class Responder : public QObject +{ + Q_OBJECT + +public: + Responder(QHttpRequest *req, QHttpResponse *resp); + ~Responder(); + +signals: + void done(); + +private slots: + void accumulate(const QByteArray &data); + void reply(); + +private: + QScopedPointer m_req; + QHttpResponse *m_resp; +}; diff --git a/external/qhttpserver/examples/bodydata/bodydata.pro b/external/qhttpserver/examples/bodydata/bodydata.pro new file mode 100755 index 0000000..46d15d9 --- /dev/null +++ b/external/qhttpserver/examples/bodydata/bodydata.pro @@ -0,0 +1,19 @@ +TARGET = bodydata + +QT += network +QT -= gui + +CONFIG += debug + +INCLUDEPATH += ../../src +LIBS += -L../../lib + +win32 { + debug: LIBS += -lqhttpserverd + else: LIBS += -lqhttpserver +} else { + LIBS += -lqhttpserver +} + +SOURCES = bodydata.cpp +HEADERS = bodydata.h diff --git a/external/qhttpserver/examples/examples.pro b/external/qhttpserver/examples/examples.pro new file mode 100755 index 0000000..b6e7a08 --- /dev/null +++ b/external/qhttpserver/examples/examples.pro @@ -0,0 +1,5 @@ +TEMPLATE = subdirs +SUBDIRS += \ +helloworld\ +greeting\ +bodydata\ diff --git a/external/qhttpserver/examples/greeting/greeting.cpp b/external/qhttpserver/examples/greeting/greeting.cpp new file mode 100755 index 0000000..4a2550e --- /dev/null +++ b/external/qhttpserver/examples/greeting/greeting.cpp @@ -0,0 +1,48 @@ +#include "greeting.h" + +#include +#include +#include + +#include +#include +#include + +/// Greeting + +Greeting::Greeting() +{ + QHttpServer *server = new QHttpServer(this); + connect(server, SIGNAL(newRequest(QHttpRequest*, QHttpResponse*)), + this, SLOT(handleRequest(QHttpRequest*, QHttpResponse*))); + + server->listen(QHostAddress::Any, 8080); +} + +void Greeting::handleRequest(QHttpRequest *req, QHttpResponse *resp) +{ + QRegExp exp("^/user/([a-z]+)$"); + if( exp.indexIn(req->path()) != -1 ) + { + resp->setHeader("Content-Type", "text/html"); + resp->writeHead(200); + + QString name = exp.capturedTexts()[1]; + QString body = tr("Greeting App

    Hello %1!

    "); + resp->end(body.arg(name).toUtf8()); + } + else + { + resp->writeHead(403); + resp->end(QByteArray("You aren't allowed here!")); + } +} + +/// main + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + Greeting greeting; + app.exec(); +} diff --git a/external/qhttpserver/examples/greeting/greeting.h b/external/qhttpserver/examples/greeting/greeting.h new file mode 100755 index 0000000..ed26fbc --- /dev/null +++ b/external/qhttpserver/examples/greeting/greeting.h @@ -0,0 +1,16 @@ +#include "qhttpserverfwd.h" + +#include + +/// Greeting + +class Greeting : public QObject +{ + Q_OBJECT + +public: + Greeting(); + +private slots: + void handleRequest(QHttpRequest *req, QHttpResponse *resp); +}; diff --git a/external/qhttpserver/examples/greeting/greeting.pro b/external/qhttpserver/examples/greeting/greeting.pro new file mode 100755 index 0000000..ff2089a --- /dev/null +++ b/external/qhttpserver/examples/greeting/greeting.pro @@ -0,0 +1,19 @@ +TARGET = greeting + +QT += network +QT -= gui + +CONFIG += debug + +INCLUDEPATH += ../../src +LIBS += -L../../lib + +win32 { + debug: LIBS += -lqhttpserverd + else: LIBS += -lqhttpserver +} else { + LIBS += -lqhttpserver +} + +SOURCES = greeting.cpp +HEADERS = greeting.h diff --git a/external/qhttpserver/examples/helloworld/helloworld.cpp b/external/qhttpserver/examples/helloworld/helloworld.cpp new file mode 100755 index 0000000..428eacd --- /dev/null +++ b/external/qhttpserver/examples/helloworld/helloworld.cpp @@ -0,0 +1,37 @@ +#include "helloworld.h" + +#include + +#include +#include +#include + +/// HelloWorld + +HelloWorld::HelloWorld() +{ + QHttpServer *server = new QHttpServer(this); + connect(server, SIGNAL(newRequest(QHttpRequest*, QHttpResponse*)), + this, SLOT(handleRequest(QHttpRequest*, QHttpResponse*))); + + server->listen(QHostAddress::Any, 8080); +} + +void HelloWorld::handleRequest(QHttpRequest *req, QHttpResponse *resp) +{ + Q_UNUSED(req); + + QByteArray body = "Hello World"; + resp->setHeader("Content-Length", QString::number(body.size())); + resp->writeHead(200); + resp->end(body); +} + +/// main + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + HelloWorld hello; + app.exec(); +} diff --git a/external/qhttpserver/examples/helloworld/helloworld.h b/external/qhttpserver/examples/helloworld/helloworld.h new file mode 100755 index 0000000..7e6239b --- /dev/null +++ b/external/qhttpserver/examples/helloworld/helloworld.h @@ -0,0 +1,16 @@ +#include "qhttpserverfwd.h" + +#include + +/// HelloWorld + +class HelloWorld : public QObject +{ + Q_OBJECT + +public: + HelloWorld(); + +private slots: + void handleRequest(QHttpRequest *req, QHttpResponse *resp); +}; diff --git a/external/qhttpserver/examples/helloworld/helloworld.pro b/external/qhttpserver/examples/helloworld/helloworld.pro new file mode 100755 index 0000000..1ca0cbc --- /dev/null +++ b/external/qhttpserver/examples/helloworld/helloworld.pro @@ -0,0 +1,19 @@ +TARGET = helloworld + +QT += network +QT -= gui + +CONFIG += debug + +INCLUDEPATH += ../../src +LIBS += -L../../lib + +win32 { + debug: LIBS += -lqhttpserverd + else: LIBS += -lqhttpserver +} else { + LIBS += -lqhttpserver +} + +SOURCES = helloworld.cpp +HEADERS = helloworld.h diff --git a/external/qhttpserver/http-parser/.gitignore b/external/qhttpserver/http-parser/.gitignore new file mode 100755 index 0000000..32cb51b --- /dev/null +++ b/external/qhttpserver/http-parser/.gitignore @@ -0,0 +1,28 @@ +/out/ +core +tags +*.o +test +test_g +test_fast +bench +url_parser +parsertrace +parsertrace_g +*.mk +*.Makefile +*.so.* +*.a + + +# Visual Studio uglies +*.suo +*.sln +*.vcxproj +*.vcxproj.filters +*.vcxproj.user +*.opensdf +*.ncrunchsolution* +*.sdf +*.vsp +*.psess diff --git a/external/qhttpserver/http-parser/.mailmap b/external/qhttpserver/http-parser/.mailmap new file mode 100644 index 0000000..278d141 --- /dev/null +++ b/external/qhttpserver/http-parser/.mailmap @@ -0,0 +1,8 @@ +# update AUTHORS with: +# git log --all --reverse --format='%aN <%aE>' | perl -ne 'BEGIN{print "# Authors ordered by first contribution.\n"} print unless $h{$_}; $h{$_} = 1' > AUTHORS +Ryan Dahl +Salman Haq +Simon Zimmermann +Thomas LE ROUX LE ROUX Thomas +Thomas LE ROUX Thomas LE ROUX +Fedor Indutny diff --git a/external/qhttpserver/http-parser/.travis.yml b/external/qhttpserver/http-parser/.travis.yml new file mode 100644 index 0000000..4b038e6 --- /dev/null +++ b/external/qhttpserver/http-parser/.travis.yml @@ -0,0 +1,13 @@ +language: c + +compiler: + - clang + - gcc + +script: + - "make" + +notifications: + email: false + irc: + - "irc.freenode.net#node-ci" diff --git a/external/qhttpserver/http-parser/AUTHORS b/external/qhttpserver/http-parser/AUTHORS new file mode 100755 index 0000000..8e2df1d --- /dev/null +++ b/external/qhttpserver/http-parser/AUTHORS @@ -0,0 +1,67 @@ +# Authors ordered by first contribution. +Ryan Dahl +Jeremy Hinegardner +Sergey Shepelev +Joe Damato +tomika +Phoenix Sol +Cliff Frey +Ewen Cheslack-Postava +Santiago Gala +Tim Becker +Jeff Terrace +Ben Noordhuis +Nathan Rajlich +Mark Nottingham +Aman Gupta +Tim Becker +Sean Cunningham +Peter Griess +Salman Haq +Cliff Frey +Jon Kolb +Fouad Mardini +Paul Querna +Felix Geisendörfer +koichik +Andre Caron +Ivo Raisr +James McLaughlin +David Gwynne +Thomas LE ROUX +Randy Rizun +Andre Louis Caron +Simon Zimmermann +Erik Dubbelboer +Martell Malone +Bertrand Paquet +BogDan Vatra +Peter Faiman +Corey Richardson +Tóth Tamás +Cam Swords +Chris Dickinson +Uli Köhler +Charlie Somerville +Patrik Stutz +Fedor Indutny +runner +Alexis Campailla +David Wragg +Vinnie Falco +Alex Butum +Rex Feng +Alex Kocharin +Mark Koopman +Helge Heß +Alexis La Goutte +George Miroshnykov +Maciej Małecki +Marc O'Morain +Jeff Pinner +Timothy J Fontaine +Akagi201 +Romain Giraud +Jay Satiro +Arne Steen +Kjell Schubert diff --git a/external/qhttpserver/http-parser/CONTRIBUTIONS b/external/qhttpserver/http-parser/CONTRIBUTIONS new file mode 100755 index 0000000..11ba31e --- /dev/null +++ b/external/qhttpserver/http-parser/CONTRIBUTIONS @@ -0,0 +1,4 @@ +Contributors must agree to the Contributor License Agreement before patches +can be accepted. + +http://spreadsheets2.google.com/viewform?hl=en&formkey=dDJXOGUwbzlYaWM4cHN1MERwQS1CSnc6MQ diff --git a/external/qhttpserver/http-parser/LICENSE-MIT b/external/qhttpserver/http-parser/LICENSE-MIT new file mode 100755 index 0000000..58010b3 --- /dev/null +++ b/external/qhttpserver/http-parser/LICENSE-MIT @@ -0,0 +1,23 @@ +http_parser.c is based on src/http/ngx_http_parse.c from NGINX copyright +Igor Sysoev. + +Additional changes are licensed under the same terms as NGINX and +copyright Joyent, Inc. and other Node contributors. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/external/qhttpserver/http-parser/README.md b/external/qhttpserver/http-parser/README.md new file mode 100755 index 0000000..7c54dd4 --- /dev/null +++ b/external/qhttpserver/http-parser/README.md @@ -0,0 +1,183 @@ +HTTP Parser +=========== + +[![Build Status](https://travis-ci.org/joyent/http-parser.png?branch=master)](https://travis-ci.org/joyent/http-parser) + +This is a parser for HTTP messages written in C. It parses both requests and +responses. The parser is designed to be used in performance HTTP +applications. It does not make any syscalls nor allocations, it does not +buffer data, it can be interrupted at anytime. Depending on your +architecture, it only requires about 40 bytes of data per message +stream (in a web server that is per connection). + +Features: + + * No dependencies + * Handles persistent streams (keep-alive). + * Decodes chunked encoding. + * Upgrade support + * Defends against buffer overflow attacks. + +The parser extracts the following information from HTTP messages: + + * Header fields and values + * Content-Length + * Request method + * Response status code + * Transfer-Encoding + * HTTP version + * Request URL + * Message body + + +Usage +----- + +One `http_parser` object is used per TCP connection. Initialize the struct +using `http_parser_init()` and set the callbacks. That might look something +like this for a request parser: +```c +http_parser_settings settings; +settings.on_url = my_url_callback; +settings.on_header_field = my_header_field_callback; +/* ... */ + +http_parser *parser = malloc(sizeof(http_parser)); +http_parser_init(parser, HTTP_REQUEST); +parser->data = my_socket; +``` + +When data is received on the socket execute the parser and check for errors. + +```c +size_t len = 80*1024, nparsed; +char buf[len]; +ssize_t recved; + +recved = recv(fd, buf, len, 0); + +if (recved < 0) { + /* Handle error. */ +} + +/* Start up / continue the parser. + * Note we pass recved==0 to signal that EOF has been received. + */ +nparsed = http_parser_execute(parser, &settings, buf, recved); + +if (parser->upgrade) { + /* handle new protocol */ +} else if (nparsed != recved) { + /* Handle error. Usually just close the connection. */ +} +``` + +HTTP needs to know where the end of the stream is. For example, sometimes +servers send responses without Content-Length and expect the client to +consume input (for the body) until EOF. To tell http_parser about EOF, give +`0` as the fourth parameter to `http_parser_execute()`. Callbacks and errors +can still be encountered during an EOF, so one must still be prepared +to receive them. + +Scalar valued message information such as `status_code`, `method`, and the +HTTP version are stored in the parser structure. This data is only +temporally stored in `http_parser` and gets reset on each new message. If +this information is needed later, copy it out of the structure during the +`headers_complete` callback. + +The parser decodes the transfer-encoding for both requests and responses +transparently. That is, a chunked encoding is decoded before being sent to +the on_body callback. + + +The Special Problem of Upgrade +------------------------------ + +HTTP supports upgrading the connection to a different protocol. An +increasingly common example of this is the Web Socket protocol which sends +a request like + + GET /demo HTTP/1.1 + Upgrade: WebSocket + Connection: Upgrade + Host: example.com + Origin: http://example.com + WebSocket-Protocol: sample + +followed by non-HTTP data. + +(See http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75 for more +information the Web Socket protocol.) + +To support this, the parser will treat this as a normal HTTP message without a +body, issuing both on_headers_complete and on_message_complete callbacks. However +http_parser_execute() will stop parsing at the end of the headers and return. + +The user is expected to check if `parser->upgrade` has been set to 1 after +`http_parser_execute()` returns. Non-HTTP data begins at the buffer supplied +offset by the return value of `http_parser_execute()`. + + +Callbacks +--------- + +During the `http_parser_execute()` call, the callbacks set in +`http_parser_settings` will be executed. The parser maintains state and +never looks behind, so buffering the data is not necessary. If you need to +save certain data for later usage, you can do that from the callbacks. + +There are two types of callbacks: + +* notification `typedef int (*http_cb) (http_parser*);` + Callbacks: on_message_begin, on_headers_complete, on_message_complete. +* data `typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);` + Callbacks: (requests only) on_url, + (common) on_header_field, on_header_value, on_body; + +Callbacks must return 0 on success. Returning a non-zero value indicates +error to the parser, making it exit immediately. + +In case you parse HTTP message in chunks (i.e. `read()` request line +from socket, parse, read half headers, parse, etc) your data callbacks +may be called more than once. Http-parser guarantees that data pointer is only +valid for the lifetime of callback. You can also `read()` into a heap allocated +buffer to avoid copying memory around if this fits your application. + +Reading headers may be a tricky task if you read/parse headers partially. +Basically, you need to remember whether last header callback was field or value +and apply the following logic: + + (on_header_field and on_header_value shortened to on_h_*) + ------------------------ ------------ -------------------------------------------- + | State (prev. callback) | Callback | Description/action | + ------------------------ ------------ -------------------------------------------- + | nothing (first call) | on_h_field | Allocate new buffer and copy callback data | + | | | into it | + ------------------------ ------------ -------------------------------------------- + | value | on_h_field | New header started. | + | | | Copy current name,value buffers to headers | + | | | list and allocate new buffer for new name | + ------------------------ ------------ -------------------------------------------- + | field | on_h_field | Previous name continues. Reallocate name | + | | | buffer and append callback data to it | + ------------------------ ------------ -------------------------------------------- + | field | on_h_value | Value for current header started. Allocate | + | | | new buffer and copy callback data to it | + ------------------------ ------------ -------------------------------------------- + | value | on_h_value | Value continues. Reallocate value buffer | + | | | and append callback data to it | + ------------------------ ------------ -------------------------------------------- + + +Parsing URLs +------------ + +A simplistic zero-copy URL parser is provided as `http_parser_parse_url()`. +Users of this library may wish to use it to parse URLs constructed from +consecutive `on_url` callbacks. + +See examples of reading in headers: + +* [partial example](http://gist.github.com/155877) in C +* [from http-parser tests](http://github.com/joyent/http-parser/blob/37a0ff8/test.c#L403) in C +* [from Node library](http://github.com/joyent/node/blob/842eaf4/src/http.js#L284) in Javascript diff --git a/external/qhttpserver/http-parser/bench.c b/external/qhttpserver/http-parser/bench.c new file mode 100644 index 0000000..5b452fa --- /dev/null +++ b/external/qhttpserver/http-parser/bench.c @@ -0,0 +1,111 @@ +/* Copyright Fedor Indutny. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include "http_parser.h" +#include +#include +#include +#include + +static const char data[] = + "POST /joyent/http-parser HTTP/1.1\r\n" + "Host: github.com\r\n" + "DNT: 1\r\n" + "Accept-Encoding: gzip, deflate, sdch\r\n" + "Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4\r\n" + "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) " + "AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/39.0.2171.65 Safari/537.36\r\n" + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9," + "image/webp,*/*;q=0.8\r\n" + "Referer: https://github.com/joyent/http-parser\r\n" + "Connection: keep-alive\r\n" + "Transfer-Encoding: chunked\r\n" + "Cache-Control: max-age=0\r\n\r\nb\r\nhello world\r\n0\r\n\r\n"; +static const size_t data_len = sizeof(data) - 1; + +static int on_info(http_parser* p) { + return 0; +} + + +static int on_data(http_parser* p, const char *at, size_t length) { + return 0; +} + +static http_parser_settings settings = { + .on_message_begin = on_info, + .on_headers_complete = on_info, + .on_message_complete = on_info, + .on_header_field = on_data, + .on_header_value = on_data, + .on_url = on_data, + .on_status = on_data, + .on_body = on_data +}; + +int bench(int iter_count, int silent) { + struct http_parser parser; + int i; + int err; + struct timeval start; + struct timeval end; + float rps; + + if (!silent) { + err = gettimeofday(&start, NULL); + assert(err == 0); + } + + for (i = 0; i < iter_count; i++) { + size_t parsed; + http_parser_init(&parser, HTTP_REQUEST); + + parsed = http_parser_execute(&parser, &settings, data, data_len); + assert(parsed == data_len); + } + + if (!silent) { + err = gettimeofday(&end, NULL); + assert(err == 0); + + fprintf(stdout, "Benchmark result:\n"); + + rps = (float) (end.tv_sec - start.tv_sec) + + (end.tv_usec - start.tv_usec) * 1e-6f; + fprintf(stdout, "Took %f seconds to run\n", rps); + + rps = (float) iter_count / rps; + fprintf(stdout, "%f req/sec\n", rps); + fflush(stdout); + } + + return 0; +} + +int main(int argc, char** argv) { + if (argc == 2 && strcmp(argv[1], "infinite") == 0) { + for (;;) + bench(5000000, 1); + return 0; + } else { + return bench(5000000, 0); + } +} diff --git a/external/qhttpserver/http-parser/contrib/parsertrace.c b/external/qhttpserver/http-parser/contrib/parsertrace.c new file mode 100644 index 0000000..e715368 --- /dev/null +++ b/external/qhttpserver/http-parser/contrib/parsertrace.c @@ -0,0 +1,160 @@ +/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev + * + * Additional changes are licensed under the same terms as NGINX and + * copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* Dump what the parser finds to stdout as it happen */ + +#include "http_parser.h" +#include +#include +#include + +int on_message_begin(http_parser* _) { + (void)_; + printf("\n***MESSAGE BEGIN***\n\n"); + return 0; +} + +int on_headers_complete(http_parser* _) { + (void)_; + printf("\n***HEADERS COMPLETE***\n\n"); + return 0; +} + +int on_message_complete(http_parser* _) { + (void)_; + printf("\n***MESSAGE COMPLETE***\n\n"); + return 0; +} + +int on_url(http_parser* _, const char* at, size_t length) { + (void)_; + printf("Url: %.*s\n", (int)length, at); + return 0; +} + +int on_header_field(http_parser* _, const char* at, size_t length) { + (void)_; + printf("Header field: %.*s\n", (int)length, at); + return 0; +} + +int on_header_value(http_parser* _, const char* at, size_t length) { + (void)_; + printf("Header value: %.*s\n", (int)length, at); + return 0; +} + +int on_body(http_parser* _, const char* at, size_t length) { + (void)_; + printf("Body: %.*s\n", (int)length, at); + return 0; +} + +void usage(const char* name) { + fprintf(stderr, + "Usage: %s $type $filename\n" + " type: -x, where x is one of {r,b,q}\n" + " parses file as a Response, reQuest, or Both\n", + name); + exit(EXIT_FAILURE); +} + +int main(int argc, char* argv[]) { + enum http_parser_type file_type; + + if (argc != 3) { + usage(argv[0]); + } + + char* type = argv[1]; + if (type[0] != '-') { + usage(argv[0]); + } + + switch (type[1]) { + /* in the case of "-", type[1] will be NUL */ + case 'r': + file_type = HTTP_RESPONSE; + break; + case 'q': + file_type = HTTP_REQUEST; + break; + case 'b': + file_type = HTTP_BOTH; + break; + default: + usage(argv[0]); + } + + char* filename = argv[2]; + FILE* file = fopen(filename, "r"); + if (file == NULL) { + perror("fopen"); + goto fail; + } + + fseek(file, 0, SEEK_END); + long file_length = ftell(file); + if (file_length == -1) { + perror("ftell"); + goto fail; + } + fseek(file, 0, SEEK_SET); + + char* data = malloc(file_length); + if (fread(data, 1, file_length, file) != (size_t)file_length) { + fprintf(stderr, "couldn't read entire file\n"); + free(data); + goto fail; + } + + http_parser_settings settings; + memset(&settings, 0, sizeof(settings)); + settings.on_message_begin = on_message_begin; + settings.on_url = on_url; + settings.on_header_field = on_header_field; + settings.on_header_value = on_header_value; + settings.on_headers_complete = on_headers_complete; + settings.on_body = on_body; + settings.on_message_complete = on_message_complete; + + http_parser parser; + http_parser_init(&parser, file_type); + size_t nparsed = http_parser_execute(&parser, &settings, data, file_length); + free(data); + + if (nparsed != (size_t)file_length) { + fprintf(stderr, + "Error: %s (%s)\n", + http_errno_description(HTTP_PARSER_ERRNO(&parser)), + http_errno_name(HTTP_PARSER_ERRNO(&parser))); + goto fail; + } + + return EXIT_SUCCESS; + +fail: + fclose(file); + return EXIT_FAILURE; +} diff --git a/external/qhttpserver/http-parser/contrib/url_parser.c b/external/qhttpserver/http-parser/contrib/url_parser.c new file mode 100644 index 0000000..6650b41 --- /dev/null +++ b/external/qhttpserver/http-parser/contrib/url_parser.c @@ -0,0 +1,46 @@ +#include "http_parser.h" +#include +#include + +void +dump_url (const char *url, const struct http_parser_url *u) +{ + unsigned int i; + + printf("\tfield_set: 0x%x, port: %u\n", u->field_set, u->port); + for (i = 0; i < UF_MAX; i++) { + if ((u->field_set & (1 << i)) == 0) { + printf("\tfield_data[%u]: unset\n", i); + continue; + } + + printf("\tfield_data[%u]: off: %u, len: %u, part: %.*s\n", + i, + u->field_data[i].off, + u->field_data[i].len, + u->field_data[i].len, + url + u->field_data[i].off); + } +} + +int main(int argc, char ** argv) { + struct http_parser_url u; + int len, connect, result; + + if (argc != 3) { + printf("Syntax : %s connect|get url\n", argv[0]); + return 1; + } + len = strlen(argv[2]); + connect = strcmp("connect", argv[1]) == 0 ? 1 : 0; + printf("Parsing %s, connect %d\n", argv[2], connect); + + result = http_parser_parse_url(argv[2], len, connect, &u); + if (result != 0) { + printf("Parse error : %d\n", result); + return result; + } + printf("Parse ok, result : \n"); + dump_url(argv[2], &u); + return 0; +} \ No newline at end of file diff --git a/external/qhttpserver/http-parser/http_parser.c b/external/qhttpserver/http-parser/http_parser.c new file mode 100755 index 0000000..0fa1c36 --- /dev/null +++ b/external/qhttpserver/http-parser/http_parser.c @@ -0,0 +1,2429 @@ +/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev + * + * Additional changes are licensed under the same terms as NGINX and + * copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include "http_parser.h" +#include +#include +#include +#include +#include +#include + +#ifndef ULLONG_MAX +# define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */ +#endif + +#ifndef MIN +# define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef ARRAY_SIZE +# define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#endif + +#ifndef BIT_AT +# define BIT_AT(a, i) \ + (!!((unsigned int) (a)[(unsigned int) (i) >> 3] & \ + (1 << ((unsigned int) (i) & 7)))) +#endif + +#ifndef ELEM_AT +# define ELEM_AT(a, i, v) ((unsigned int) (i) < ARRAY_SIZE(a) ? (a)[(i)] : (v)) +#endif + +#define SET_ERRNO(e) \ +do { \ + parser->http_errno = (e); \ +} while(0) + +#define CURRENT_STATE() p_state +#define UPDATE_STATE(V) p_state = (enum state) (V); +#define RETURN(V) \ +do { \ + parser->state = CURRENT_STATE(); \ + return (V); \ +} while (0); +#define REEXECUTE() \ + goto reexecute; \ + + +#ifdef __GNUC__ +# define LIKELY(X) __builtin_expect(!!(X), 1) +# define UNLIKELY(X) __builtin_expect(!!(X), 0) +#else +# define LIKELY(X) (X) +# define UNLIKELY(X) (X) +#endif + + +/* Run the notify callback FOR, returning ER if it fails */ +#define CALLBACK_NOTIFY_(FOR, ER) \ +do { \ + assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \ + \ + if (LIKELY(settings->on_##FOR)) { \ + parser->state = CURRENT_STATE(); \ + if (UNLIKELY(0 != settings->on_##FOR(parser))) { \ + SET_ERRNO(HPE_CB_##FOR); \ + } \ + UPDATE_STATE(parser->state); \ + \ + /* We either errored above or got paused; get out */ \ + if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \ + return (ER); \ + } \ + } \ +} while (0) + +/* Run the notify callback FOR and consume the current byte */ +#define CALLBACK_NOTIFY(FOR) CALLBACK_NOTIFY_(FOR, p - data + 1) + +/* Run the notify callback FOR and don't consume the current byte */ +#define CALLBACK_NOTIFY_NOADVANCE(FOR) CALLBACK_NOTIFY_(FOR, p - data) + +/* Run data callback FOR with LEN bytes, returning ER if it fails */ +#define CALLBACK_DATA_(FOR, LEN, ER) \ +do { \ + assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \ + \ + if (FOR##_mark) { \ + if (LIKELY(settings->on_##FOR)) { \ + parser->state = CURRENT_STATE(); \ + if (UNLIKELY(0 != \ + settings->on_##FOR(parser, FOR##_mark, (LEN)))) { \ + SET_ERRNO(HPE_CB_##FOR); \ + } \ + UPDATE_STATE(parser->state); \ + \ + /* We either errored above or got paused; get out */ \ + if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \ + return (ER); \ + } \ + } \ + FOR##_mark = NULL; \ + } \ +} while (0) + +/* Run the data callback FOR and consume the current byte */ +#define CALLBACK_DATA(FOR) \ + CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1) + +/* Run the data callback FOR and don't consume the current byte */ +#define CALLBACK_DATA_NOADVANCE(FOR) \ + CALLBACK_DATA_(FOR, p - FOR##_mark, p - data) + +/* Set the mark FOR; non-destructive if mark is already set */ +#define MARK(FOR) \ +do { \ + if (!FOR##_mark) { \ + FOR##_mark = p; \ + } \ +} while (0) + +/* Don't allow the total size of the HTTP headers (including the status + * line) to exceed HTTP_MAX_HEADER_SIZE. This check is here to protect + * embedders against denial-of-service attacks where the attacker feeds + * us a never-ending header that the embedder keeps buffering. + * + * This check is arguably the responsibility of embedders but we're doing + * it on the embedder's behalf because most won't bother and this way we + * make the web a little safer. HTTP_MAX_HEADER_SIZE is still far bigger + * than any reasonable request or response so this should never affect + * day-to-day operation. + */ +#define COUNT_HEADER_SIZE(V) \ +do { \ + parser->nread += (V); \ + if (UNLIKELY(parser->nread > (HTTP_MAX_HEADER_SIZE))) { \ + SET_ERRNO(HPE_HEADER_OVERFLOW); \ + goto error; \ + } \ +} while (0) + + +#define PROXY_CONNECTION "proxy-connection" +#define CONNECTION "connection" +#define CONTENT_LENGTH "content-length" +#define TRANSFER_ENCODING "transfer-encoding" +#define UPGRADE "upgrade" +#define CHUNKED "chunked" +#define KEEP_ALIVE "keep-alive" +#define CLOSE "close" + + +static const char *method_strings[] = + { +#define XX(num, name, string) #string, + HTTP_METHOD_MAP(XX) +#undef XX + }; + + +/* Tokens as defined by rfc 2616. Also lowercases them. + * token = 1* + * separators = "(" | ")" | "<" | ">" | "@" + * | "," | ";" | ":" | "\" | <"> + * | "/" | "[" | "]" | "?" | "=" + * | "{" | "}" | SP | HT + */ +static const char tokens[256] = { +/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ + 0, 0, 0, 0, 0, 0, 0, 0, +/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ + 0, 0, 0, 0, 0, 0, 0, 0, +/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ + 0, 0, 0, 0, 0, 0, 0, 0, +/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ + 0, 0, 0, 0, 0, 0, 0, 0, +/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ + 0, '!', 0, '#', '$', '%', '&', '\'', +/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ + 0, 0, '*', '+', 0, '-', '.', 0, +/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ + '0', '1', '2', '3', '4', '5', '6', '7', +/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ + '8', '9', 0, 0, 0, 0, 0, 0, +/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ + 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', +/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', +/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', +/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ + 'x', 'y', 'z', 0, 0, 0, '^', '_', +/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ + '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', +/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', +/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', +/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ + 'x', 'y', 'z', 0, '|', 0, '~', 0 }; + + +static const int8_t unhex[256] = + {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1 + ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1 + ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + }; + + +#if HTTP_PARSER_STRICT +# define T(v) 0 +#else +# define T(v) v +#endif + + +static const uint8_t normal_url_char[32] = { +/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ + 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, +/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ + 0 | T(2) | 0 | 0 | T(16) | 0 | 0 | 0, +/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ + 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, +/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ + 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, +/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ + 0 | 2 | 4 | 0 | 16 | 32 | 64 | 128, +/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, +/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, }; + +#undef T + +enum state + { s_dead = 1 /* important that this is > 0 */ + + , s_start_req_or_res + , s_res_or_resp_H + , s_start_res + , s_res_H + , s_res_HT + , s_res_HTT + , s_res_HTTP + , s_res_first_http_major + , s_res_http_major + , s_res_first_http_minor + , s_res_http_minor + , s_res_first_status_code + , s_res_status_code + , s_res_status_start + , s_res_status + , s_res_line_almost_done + + , s_start_req + + , s_req_method + , s_req_spaces_before_url + , s_req_schema + , s_req_schema_slash + , s_req_schema_slash_slash + , s_req_server_start + , s_req_server + , s_req_server_with_at + , s_req_path + , s_req_query_string_start + , s_req_query_string + , s_req_fragment_start + , s_req_fragment + , s_req_http_start + , s_req_http_H + , s_req_http_HT + , s_req_http_HTT + , s_req_http_HTTP + , s_req_first_http_major + , s_req_http_major + , s_req_first_http_minor + , s_req_http_minor + , s_req_line_almost_done + + , s_header_field_start + , s_header_field + , s_header_value_discard_ws + , s_header_value_discard_ws_almost_done + , s_header_value_discard_lws + , s_header_value_start + , s_header_value + , s_header_value_lws + + , s_header_almost_done + + , s_chunk_size_start + , s_chunk_size + , s_chunk_parameters + , s_chunk_size_almost_done + + , s_headers_almost_done + , s_headers_done + + /* Important: 's_headers_done' must be the last 'header' state. All + * states beyond this must be 'body' states. It is used for overflow + * checking. See the PARSING_HEADER() macro. + */ + + , s_chunk_data + , s_chunk_data_almost_done + , s_chunk_data_done + + , s_body_identity + , s_body_identity_eof + + , s_message_done + }; + + +#define PARSING_HEADER(state) (state <= s_headers_done) + + +enum header_states + { h_general = 0 + , h_C + , h_CO + , h_CON + + , h_matching_connection + , h_matching_proxy_connection + , h_matching_content_length + , h_matching_transfer_encoding + , h_matching_upgrade + + , h_connection + , h_content_length + , h_transfer_encoding + , h_upgrade + + , h_matching_transfer_encoding_chunked + , h_matching_connection_token_start + , h_matching_connection_keep_alive + , h_matching_connection_close + , h_matching_connection_upgrade + , h_matching_connection_token + + , h_transfer_encoding_chunked + , h_connection_keep_alive + , h_connection_close + , h_connection_upgrade + }; + +enum http_host_state + { + s_http_host_dead = 1 + , s_http_userinfo_start + , s_http_userinfo + , s_http_host_start + , s_http_host_v6_start + , s_http_host + , s_http_host_v6 + , s_http_host_v6_end + , s_http_host_port_start + , s_http_host_port +}; + +/* Macros for character classes; depends on strict-mode */ +#define CR '\r' +#define LF '\n' +#define LOWER(c) (unsigned char)(c | 0x20) +#define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z') +#define IS_NUM(c) ((c) >= '0' && (c) <= '9') +#define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c)) +#define IS_HEX(c) (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f')) +#define IS_MARK(c) ((c) == '-' || (c) == '_' || (c) == '.' || \ + (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \ + (c) == ')') +#define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \ + (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \ + (c) == '$' || (c) == ',') + +#define STRICT_TOKEN(c) (tokens[(unsigned char)c]) + +#if HTTP_PARSER_STRICT +#define TOKEN(c) (tokens[(unsigned char)c]) +#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c)) +#define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-') +#else +#define TOKEN(c) ((c == ' ') ? ' ' : tokens[(unsigned char)c]) +#define IS_URL_CHAR(c) \ + (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80)) +#define IS_HOST_CHAR(c) \ + (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_') +#endif + + +#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res) + + +#if HTTP_PARSER_STRICT +# define STRICT_CHECK(cond) \ +do { \ + if (cond) { \ + SET_ERRNO(HPE_STRICT); \ + goto error; \ + } \ +} while (0) +# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead) +#else +# define STRICT_CHECK(cond) +# define NEW_MESSAGE() start_state +#endif + + +/* Map errno values to strings for human-readable output */ +#define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s }, +static struct { + const char *name; + const char *description; +} http_strerror_tab[] = { + HTTP_ERRNO_MAP(HTTP_STRERROR_GEN) +}; +#undef HTTP_STRERROR_GEN + +int http_message_needs_eof(const http_parser *parser); + +/* Our URL parser. + * + * This is designed to be shared by http_parser_execute() for URL validation, + * hence it has a state transition + byte-for-byte interface. In addition, it + * is meant to be embedded in http_parser_parse_url(), which does the dirty + * work of turning state transitions URL components for its API. + * + * This function should only be invoked with non-space characters. It is + * assumed that the caller cares about (and can detect) the transition between + * URL and non-URL states by looking for these. + */ +static enum state +parse_url_char(enum state s, const char ch) +{ + if (ch == ' ' || ch == '\r' || ch == '\n') { + return s_dead; + } + +#if HTTP_PARSER_STRICT + if (ch == '\t' || ch == '\f') { + return s_dead; + } +#endif + + switch (s) { + case s_req_spaces_before_url: + /* Proxied requests are followed by scheme of an absolute URI (alpha). + * All methods except CONNECT are followed by '/' or '*'. + */ + + if (ch == '/' || ch == '*') { + return s_req_path; + } + + if (IS_ALPHA(ch)) { + return s_req_schema; + } + + break; + + case s_req_schema: + if (IS_ALPHA(ch)) { + return s; + } + + if (ch == ':') { + return s_req_schema_slash; + } + + break; + + case s_req_schema_slash: + if (ch == '/') { + return s_req_schema_slash_slash; + } + + break; + + case s_req_schema_slash_slash: + if (ch == '/') { + return s_req_server_start; + } + + break; + + case s_req_server_with_at: + if (ch == '@') { + return s_dead; + } + + /* FALLTHROUGH */ + case s_req_server_start: + case s_req_server: + if (ch == '/') { + return s_req_path; + } + + if (ch == '?') { + return s_req_query_string_start; + } + + if (ch == '@') { + return s_req_server_with_at; + } + + if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') { + return s_req_server; + } + + break; + + case s_req_path: + if (IS_URL_CHAR(ch)) { + return s; + } + + switch (ch) { + case '?': + return s_req_query_string_start; + + case '#': + return s_req_fragment_start; + } + + break; + + case s_req_query_string_start: + case s_req_query_string: + if (IS_URL_CHAR(ch)) { + return s_req_query_string; + } + + switch (ch) { + case '?': + /* allow extra '?' in query string */ + return s_req_query_string; + + case '#': + return s_req_fragment_start; + } + + break; + + case s_req_fragment_start: + if (IS_URL_CHAR(ch)) { + return s_req_fragment; + } + + switch (ch) { + case '?': + return s_req_fragment; + + case '#': + return s; + } + + break; + + case s_req_fragment: + if (IS_URL_CHAR(ch)) { + return s; + } + + switch (ch) { + case '?': + case '#': + return s; + } + + break; + + default: + break; + } + + /* We should never fall out of the switch above unless there's an error */ + return s_dead; +} + +size_t http_parser_execute (http_parser *parser, + const http_parser_settings *settings, + const char *data, + size_t len) +{ + char c, ch; + int8_t unhex_val; + const char *p = data; + const char *header_field_mark = 0; + const char *header_value_mark = 0; + const char *url_mark = 0; + const char *body_mark = 0; + const char *status_mark = 0; + enum state p_state = (enum state) parser->state; + + /* We're in an error state. Don't bother doing anything. */ + if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { + return 0; + } + + if (len == 0) { + switch (CURRENT_STATE()) { + case s_body_identity_eof: + /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if + * we got paused. + */ + CALLBACK_NOTIFY_NOADVANCE(message_complete); + return 0; + + case s_dead: + case s_start_req_or_res: + case s_start_res: + case s_start_req: + return 0; + + default: + SET_ERRNO(HPE_INVALID_EOF_STATE); + return 1; + } + } + + + if (CURRENT_STATE() == s_header_field) + header_field_mark = data; + if (CURRENT_STATE() == s_header_value) + header_value_mark = data; + switch (CURRENT_STATE()) { + case s_req_path: + case s_req_schema: + case s_req_schema_slash: + case s_req_schema_slash_slash: + case s_req_server_start: + case s_req_server: + case s_req_server_with_at: + case s_req_query_string_start: + case s_req_query_string: + case s_req_fragment_start: + case s_req_fragment: + url_mark = data; + break; + case s_res_status: + status_mark = data; + break; + default: + break; + } + + for (p=data; p != data + len; p++) { + ch = *p; + + if (PARSING_HEADER(CURRENT_STATE())) + COUNT_HEADER_SIZE(1); + +reexecute: + switch (CURRENT_STATE()) { + + case s_dead: + /* this state is used after a 'Connection: close' message + * the parser will error out if it reads another message + */ + if (LIKELY(ch == CR || ch == LF)) + break; + + SET_ERRNO(HPE_CLOSED_CONNECTION); + goto error; + + case s_start_req_or_res: + { + if (ch == CR || ch == LF) + break; + parser->flags = 0; + parser->content_length = ULLONG_MAX; + + if (ch == 'H') { + UPDATE_STATE(s_res_or_resp_H); + + CALLBACK_NOTIFY(message_begin); + } else { + parser->type = HTTP_REQUEST; + UPDATE_STATE(s_start_req); + REEXECUTE(); + } + + break; + } + + case s_res_or_resp_H: + if (ch == 'T') { + parser->type = HTTP_RESPONSE; + UPDATE_STATE(s_res_HT); + } else { + if (UNLIKELY(ch != 'E')) { + SET_ERRNO(HPE_INVALID_CONSTANT); + goto error; + } + + parser->type = HTTP_REQUEST; + parser->method = HTTP_HEAD; + parser->index = 2; + UPDATE_STATE(s_req_method); + } + break; + + case s_start_res: + { + parser->flags = 0; + parser->content_length = ULLONG_MAX; + + switch (ch) { + case 'H': + UPDATE_STATE(s_res_H); + break; + + case CR: + case LF: + break; + + default: + SET_ERRNO(HPE_INVALID_CONSTANT); + goto error; + } + + CALLBACK_NOTIFY(message_begin); + break; + } + + case s_res_H: + STRICT_CHECK(ch != 'T'); + UPDATE_STATE(s_res_HT); + break; + + case s_res_HT: + STRICT_CHECK(ch != 'T'); + UPDATE_STATE(s_res_HTT); + break; + + case s_res_HTT: + STRICT_CHECK(ch != 'P'); + UPDATE_STATE(s_res_HTTP); + break; + + case s_res_HTTP: + STRICT_CHECK(ch != '/'); + UPDATE_STATE(s_res_first_http_major); + break; + + case s_res_first_http_major: + if (UNLIKELY(ch < '0' || ch > '9')) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_major = ch - '0'; + UPDATE_STATE(s_res_http_major); + break; + + /* major HTTP version or dot */ + case s_res_http_major: + { + if (ch == '.') { + UPDATE_STATE(s_res_first_http_minor); + break; + } + + if (!IS_NUM(ch)) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_major *= 10; + parser->http_major += ch - '0'; + + if (UNLIKELY(parser->http_major > 999)) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + break; + } + + /* first digit of minor HTTP version */ + case s_res_first_http_minor: + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_minor = ch - '0'; + UPDATE_STATE(s_res_http_minor); + break; + + /* minor HTTP version or end of request line */ + case s_res_http_minor: + { + if (ch == ' ') { + UPDATE_STATE(s_res_first_status_code); + break; + } + + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_minor *= 10; + parser->http_minor += ch - '0'; + + if (UNLIKELY(parser->http_minor > 999)) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + break; + } + + case s_res_first_status_code: + { + if (!IS_NUM(ch)) { + if (ch == ' ') { + break; + } + + SET_ERRNO(HPE_INVALID_STATUS); + goto error; + } + parser->status_code = ch - '0'; + UPDATE_STATE(s_res_status_code); + break; + } + + case s_res_status_code: + { + if (!IS_NUM(ch)) { + switch (ch) { + case ' ': + UPDATE_STATE(s_res_status_start); + break; + case CR: + UPDATE_STATE(s_res_line_almost_done); + break; + case LF: + UPDATE_STATE(s_header_field_start); + break; + default: + SET_ERRNO(HPE_INVALID_STATUS); + goto error; + } + break; + } + + parser->status_code *= 10; + parser->status_code += ch - '0'; + + if (UNLIKELY(parser->status_code > 999)) { + SET_ERRNO(HPE_INVALID_STATUS); + goto error; + } + + break; + } + + case s_res_status_start: + { + if (ch == CR) { + UPDATE_STATE(s_res_line_almost_done); + break; + } + + if (ch == LF) { + UPDATE_STATE(s_header_field_start); + break; + } + + MARK(status); + UPDATE_STATE(s_res_status); + parser->index = 0; + break; + } + + case s_res_status: + if (ch == CR) { + UPDATE_STATE(s_res_line_almost_done); + CALLBACK_DATA(status); + break; + } + + if (ch == LF) { + UPDATE_STATE(s_header_field_start); + CALLBACK_DATA(status); + break; + } + + break; + + case s_res_line_almost_done: + STRICT_CHECK(ch != LF); + UPDATE_STATE(s_header_field_start); + break; + + case s_start_req: + { + if (ch == CR || ch == LF) + break; + parser->flags = 0; + parser->content_length = ULLONG_MAX; + + if (UNLIKELY(!IS_ALPHA(ch))) { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + + parser->method = (enum http_method) 0; + parser->index = 1; + switch (ch) { + case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break; + case 'D': parser->method = HTTP_DELETE; break; + case 'G': parser->method = HTTP_GET; break; + case 'H': parser->method = HTTP_HEAD; break; + case 'L': parser->method = HTTP_LOCK; break; + case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break; + case 'N': parser->method = HTTP_NOTIFY; break; + case 'O': parser->method = HTTP_OPTIONS; break; + case 'P': parser->method = HTTP_POST; + /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */ + break; + case 'R': parser->method = HTTP_REPORT; break; + case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break; + case 'T': parser->method = HTTP_TRACE; break; + case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE */ break; + default: + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + UPDATE_STATE(s_req_method); + + CALLBACK_NOTIFY(message_begin); + + break; + } + + case s_req_method: + { + const char *matcher; + if (UNLIKELY(ch == '\0')) { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + + matcher = method_strings[parser->method]; + if (ch == ' ' && matcher[parser->index] == '\0') { + UPDATE_STATE(s_req_spaces_before_url); + } else if (ch == matcher[parser->index]) { + ; /* nada */ + } else if (parser->method == HTTP_CONNECT) { + if (parser->index == 1 && ch == 'H') { + parser->method = HTTP_CHECKOUT; + } else if (parser->index == 2 && ch == 'P') { + parser->method = HTTP_COPY; + } else { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + } else if (parser->method == HTTP_MKCOL) { + if (parser->index == 1 && ch == 'O') { + parser->method = HTTP_MOVE; + } else if (parser->index == 1 && ch == 'E') { + parser->method = HTTP_MERGE; + } else if (parser->index == 1 && ch == '-') { + parser->method = HTTP_MSEARCH; + } else if (parser->index == 2 && ch == 'A') { + parser->method = HTTP_MKACTIVITY; + } else if (parser->index == 3 && ch == 'A') { + parser->method = HTTP_MKCALENDAR; + } else { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + } else if (parser->method == HTTP_SUBSCRIBE) { + if (parser->index == 1 && ch == 'E') { + parser->method = HTTP_SEARCH; + } else { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + } else if (parser->index == 1 && parser->method == HTTP_POST) { + if (ch == 'R') { + parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */ + } else if (ch == 'U') { + parser->method = HTTP_PUT; /* or HTTP_PURGE */ + } else if (ch == 'A') { + parser->method = HTTP_PATCH; + } else { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + } else if (parser->index == 2) { + if (parser->method == HTTP_PUT) { + if (ch == 'R') { + parser->method = HTTP_PURGE; + } else { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + } else if (parser->method == HTTP_UNLOCK) { + if (ch == 'S') { + parser->method = HTTP_UNSUBSCRIBE; + } else { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + } else { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + } else if (parser->index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') { + parser->method = HTTP_PROPPATCH; + } else { + SET_ERRNO(HPE_INVALID_METHOD); + goto error; + } + + ++parser->index; + break; + } + + case s_req_spaces_before_url: + { + if (ch == ' ') break; + + MARK(url); + if (parser->method == HTTP_CONNECT) { + UPDATE_STATE(s_req_server_start); + } + + UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); + if (UNLIKELY(CURRENT_STATE() == s_dead)) { + SET_ERRNO(HPE_INVALID_URL); + goto error; + } + + break; + } + + case s_req_schema: + case s_req_schema_slash: + case s_req_schema_slash_slash: + case s_req_server_start: + { + switch (ch) { + /* No whitespace allowed here */ + case ' ': + case CR: + case LF: + SET_ERRNO(HPE_INVALID_URL); + goto error; + default: + UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); + if (UNLIKELY(CURRENT_STATE() == s_dead)) { + SET_ERRNO(HPE_INVALID_URL); + goto error; + } + } + + break; + } + + case s_req_server: + case s_req_server_with_at: + case s_req_path: + case s_req_query_string_start: + case s_req_query_string: + case s_req_fragment_start: + case s_req_fragment: + { + switch (ch) { + case ' ': + UPDATE_STATE(s_req_http_start); + CALLBACK_DATA(url); + break; + case CR: + case LF: + parser->http_major = 0; + parser->http_minor = 9; + UPDATE_STATE((ch == CR) ? + s_req_line_almost_done : + s_header_field_start); + CALLBACK_DATA(url); + break; + default: + UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); + if (UNLIKELY(CURRENT_STATE() == s_dead)) { + SET_ERRNO(HPE_INVALID_URL); + goto error; + } + } + break; + } + + case s_req_http_start: + switch (ch) { + case 'H': + UPDATE_STATE(s_req_http_H); + break; + case ' ': + break; + default: + SET_ERRNO(HPE_INVALID_CONSTANT); + goto error; + } + break; + + case s_req_http_H: + STRICT_CHECK(ch != 'T'); + UPDATE_STATE(s_req_http_HT); + break; + + case s_req_http_HT: + STRICT_CHECK(ch != 'T'); + UPDATE_STATE(s_req_http_HTT); + break; + + case s_req_http_HTT: + STRICT_CHECK(ch != 'P'); + UPDATE_STATE(s_req_http_HTTP); + break; + + case s_req_http_HTTP: + STRICT_CHECK(ch != '/'); + UPDATE_STATE(s_req_first_http_major); + break; + + /* first digit of major HTTP version */ + case s_req_first_http_major: + if (UNLIKELY(ch < '1' || ch > '9')) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_major = ch - '0'; + UPDATE_STATE(s_req_http_major); + break; + + /* major HTTP version or dot */ + case s_req_http_major: + { + if (ch == '.') { + UPDATE_STATE(s_req_first_http_minor); + break; + } + + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_major *= 10; + parser->http_major += ch - '0'; + + if (UNLIKELY(parser->http_major > 999)) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + break; + } + + /* first digit of minor HTTP version */ + case s_req_first_http_minor: + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_minor = ch - '0'; + UPDATE_STATE(s_req_http_minor); + break; + + /* minor HTTP version or end of request line */ + case s_req_http_minor: + { + if (ch == CR) { + UPDATE_STATE(s_req_line_almost_done); + break; + } + + if (ch == LF) { + UPDATE_STATE(s_header_field_start); + break; + } + + /* XXX allow spaces after digit? */ + + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + parser->http_minor *= 10; + parser->http_minor += ch - '0'; + + if (UNLIKELY(parser->http_minor > 999)) { + SET_ERRNO(HPE_INVALID_VERSION); + goto error; + } + + break; + } + + /* end of request line */ + case s_req_line_almost_done: + { + if (UNLIKELY(ch != LF)) { + SET_ERRNO(HPE_LF_EXPECTED); + goto error; + } + + UPDATE_STATE(s_header_field_start); + break; + } + + case s_header_field_start: + { + if (ch == CR) { + UPDATE_STATE(s_headers_almost_done); + break; + } + + if (ch == LF) { + /* they might be just sending \n instead of \r\n so this would be + * the second \n to denote the end of headers*/ + UPDATE_STATE(s_headers_almost_done); + REEXECUTE(); + } + + c = TOKEN(ch); + + if (UNLIKELY(!c)) { + SET_ERRNO(HPE_INVALID_HEADER_TOKEN); + goto error; + } + + MARK(header_field); + + parser->index = 0; + UPDATE_STATE(s_header_field); + + switch (c) { + case 'c': + parser->header_state = h_C; + break; + + case 'p': + parser->header_state = h_matching_proxy_connection; + break; + + case 't': + parser->header_state = h_matching_transfer_encoding; + break; + + case 'u': + parser->header_state = h_matching_upgrade; + break; + + default: + parser->header_state = h_general; + break; + } + break; + } + + case s_header_field: + { + const char* start = p; + for (; p != data + len; p++) { + ch = *p; + c = TOKEN(ch); + + if (!c) + break; + + switch (parser->header_state) { + case h_general: + break; + + case h_C: + parser->index++; + parser->header_state = (c == 'o' ? h_CO : h_general); + break; + + case h_CO: + parser->index++; + parser->header_state = (c == 'n' ? h_CON : h_general); + break; + + case h_CON: + parser->index++; + switch (c) { + case 'n': + parser->header_state = h_matching_connection; + break; + case 't': + parser->header_state = h_matching_content_length; + break; + default: + parser->header_state = h_general; + break; + } + break; + + /* connection */ + + case h_matching_connection: + parser->index++; + if (parser->index > sizeof(CONNECTION)-1 + || c != CONNECTION[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(CONNECTION)-2) { + parser->header_state = h_connection; + } + break; + + /* proxy-connection */ + + case h_matching_proxy_connection: + parser->index++; + if (parser->index > sizeof(PROXY_CONNECTION)-1 + || c != PROXY_CONNECTION[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(PROXY_CONNECTION)-2) { + parser->header_state = h_connection; + } + break; + + /* content-length */ + + case h_matching_content_length: + parser->index++; + if (parser->index > sizeof(CONTENT_LENGTH)-1 + || c != CONTENT_LENGTH[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(CONTENT_LENGTH)-2) { + parser->header_state = h_content_length; + } + break; + + /* transfer-encoding */ + + case h_matching_transfer_encoding: + parser->index++; + if (parser->index > sizeof(TRANSFER_ENCODING)-1 + || c != TRANSFER_ENCODING[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) { + parser->header_state = h_transfer_encoding; + } + break; + + /* upgrade */ + + case h_matching_upgrade: + parser->index++; + if (parser->index > sizeof(UPGRADE)-1 + || c != UPGRADE[parser->index]) { + parser->header_state = h_general; + } else if (parser->index == sizeof(UPGRADE)-2) { + parser->header_state = h_upgrade; + } + break; + + case h_connection: + case h_content_length: + case h_transfer_encoding: + case h_upgrade: + if (ch != ' ') parser->header_state = h_general; + break; + + default: + assert(0 && "Unknown header_state"); + break; + } + } + + COUNT_HEADER_SIZE(p - start); + + if (p == data + len) { + --p; + break; + } + + if (ch == ':') { + UPDATE_STATE(s_header_value_discard_ws); + CALLBACK_DATA(header_field); + break; + } + + SET_ERRNO(HPE_INVALID_HEADER_TOKEN); + goto error; + } + + case s_header_value_discard_ws: + if (ch == ' ' || ch == '\t') break; + + if (ch == CR) { + UPDATE_STATE(s_header_value_discard_ws_almost_done); + break; + } + + if (ch == LF) { + UPDATE_STATE(s_header_value_discard_lws); + break; + } + + /* FALLTHROUGH */ + + case s_header_value_start: + { + MARK(header_value); + + UPDATE_STATE(s_header_value); + parser->index = 0; + + c = LOWER(ch); + + switch (parser->header_state) { + case h_upgrade: + parser->flags |= F_UPGRADE; + parser->header_state = h_general; + break; + + case h_transfer_encoding: + /* looking for 'Transfer-Encoding: chunked' */ + if ('c' == c) { + parser->header_state = h_matching_transfer_encoding_chunked; + } else { + parser->header_state = h_general; + } + break; + + case h_content_length: + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + goto error; + } + + parser->content_length = ch - '0'; + break; + + case h_connection: + /* looking for 'Connection: keep-alive' */ + if (c == 'k') { + parser->header_state = h_matching_connection_keep_alive; + /* looking for 'Connection: close' */ + } else if (c == 'c') { + parser->header_state = h_matching_connection_close; + } else if (c == 'u') { + parser->header_state = h_matching_connection_upgrade; + } else { + parser->header_state = h_matching_connection_token; + } + break; + + /* Multi-value `Connection` header */ + case h_matching_connection_token_start: + break; + + default: + parser->header_state = h_general; + break; + } + break; + } + + case s_header_value: + { + const char* start = p; + enum header_states h_state = (enum header_states) parser->header_state; + for (; p != data + len; p++) { + ch = *p; + if (ch == CR) { + UPDATE_STATE(s_header_almost_done); + parser->header_state = h_state; + CALLBACK_DATA(header_value); + break; + } + + if (ch == LF) { + UPDATE_STATE(s_header_almost_done); + COUNT_HEADER_SIZE(p - start); + parser->header_state = h_state; + CALLBACK_DATA_NOADVANCE(header_value); + REEXECUTE(); + } + + c = LOWER(ch); + + switch (h_state) { + case h_general: + { + const char* p_cr; + const char* p_lf; + size_t limit = data + len - p; + + limit = MIN(limit, HTTP_MAX_HEADER_SIZE); + + p_cr = (const char*) memchr(p, CR, limit); + p_lf = (const char*) memchr(p, LF, limit); + if (p_cr != NULL) { + if (p_lf != NULL && p_cr >= p_lf) + p = p_lf; + else + p = p_cr; + } else if (UNLIKELY(p_lf != NULL)) { + p = p_lf; + } else { + p = data + len; + } + --p; + + break; + } + + case h_connection: + case h_transfer_encoding: + assert(0 && "Shouldn't get here."); + break; + + case h_content_length: + { + uint64_t t; + + if (ch == ' ') break; + + if (UNLIKELY(!IS_NUM(ch))) { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + parser->header_state = h_state; + goto error; + } + + t = parser->content_length; + t *= 10; + t += ch - '0'; + + /* Overflow? Test against a conservative limit for simplicity. */ + if (UNLIKELY((ULLONG_MAX - 10) / 10 < parser->content_length)) { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + parser->header_state = h_state; + goto error; + } + + parser->content_length = t; + break; + } + + /* Transfer-Encoding: chunked */ + case h_matching_transfer_encoding_chunked: + parser->index++; + if (parser->index > sizeof(CHUNKED)-1 + || c != CHUNKED[parser->index]) { + h_state = h_general; + } else if (parser->index == sizeof(CHUNKED)-2) { + h_state = h_transfer_encoding_chunked; + } + break; + + case h_matching_connection_token_start: + /* looking for 'Connection: keep-alive' */ + if (c == 'k') { + h_state = h_matching_connection_keep_alive; + /* looking for 'Connection: close' */ + } else if (c == 'c') { + h_state = h_matching_connection_close; + } else if (c == 'u') { + h_state = h_matching_connection_upgrade; + } else if (STRICT_TOKEN(c)) { + h_state = h_matching_connection_token; + } else if (c == ' ' || c == '\t') { + /* Skip lws */ + } else { + h_state = h_general; + } + break; + + /* looking for 'Connection: keep-alive' */ + case h_matching_connection_keep_alive: + parser->index++; + if (parser->index > sizeof(KEEP_ALIVE)-1 + || c != KEEP_ALIVE[parser->index]) { + h_state = h_matching_connection_token; + } else if (parser->index == sizeof(KEEP_ALIVE)-2) { + h_state = h_connection_keep_alive; + } + break; + + /* looking for 'Connection: close' */ + case h_matching_connection_close: + parser->index++; + if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) { + h_state = h_matching_connection_token; + } else if (parser->index == sizeof(CLOSE)-2) { + h_state = h_connection_close; + } + break; + + /* looking for 'Connection: upgrade' */ + case h_matching_connection_upgrade: + parser->index++; + if (parser->index > sizeof(UPGRADE) - 1 || + c != UPGRADE[parser->index]) { + h_state = h_matching_connection_token; + } else if (parser->index == sizeof(UPGRADE)-2) { + h_state = h_connection_upgrade; + } + break; + + case h_matching_connection_token: + if (ch == ',') { + h_state = h_matching_connection_token_start; + parser->index = 0; + } + break; + + case h_transfer_encoding_chunked: + if (ch != ' ') h_state = h_general; + break; + + case h_connection_keep_alive: + case h_connection_close: + case h_connection_upgrade: + if (ch == ',') { + if (h_state == h_connection_keep_alive) { + parser->flags |= F_CONNECTION_KEEP_ALIVE; + } else if (h_state == h_connection_close) { + parser->flags |= F_CONNECTION_CLOSE; + } else if (h_state == h_connection_upgrade) { + parser->flags |= F_CONNECTION_UPGRADE; + } + h_state = h_matching_connection_token_start; + parser->index = 0; + } else if (ch != ' ') { + h_state = h_matching_connection_token; + } + break; + + default: + UPDATE_STATE(s_header_value); + h_state = h_general; + break; + } + } + parser->header_state = h_state; + + COUNT_HEADER_SIZE(p - start); + + if (p == data + len) + --p; + break; + } + + case s_header_almost_done: + { + STRICT_CHECK(ch != LF); + + UPDATE_STATE(s_header_value_lws); + break; + } + + case s_header_value_lws: + { + if (ch == ' ' || ch == '\t') { + UPDATE_STATE(s_header_value_start); + REEXECUTE(); + } + + /* finished the header */ + switch (parser->header_state) { + case h_connection_keep_alive: + parser->flags |= F_CONNECTION_KEEP_ALIVE; + break; + case h_connection_close: + parser->flags |= F_CONNECTION_CLOSE; + break; + case h_transfer_encoding_chunked: + parser->flags |= F_CHUNKED; + break; + case h_connection_upgrade: + parser->flags |= F_CONNECTION_UPGRADE; + break; + default: + break; + } + + UPDATE_STATE(s_header_field_start); + REEXECUTE(); + } + + case s_header_value_discard_ws_almost_done: + { + STRICT_CHECK(ch != LF); + UPDATE_STATE(s_header_value_discard_lws); + break; + } + + case s_header_value_discard_lws: + { + if (ch == ' ' || ch == '\t') { + UPDATE_STATE(s_header_value_discard_ws); + break; + } else { + switch (parser->header_state) { + case h_connection_keep_alive: + parser->flags |= F_CONNECTION_KEEP_ALIVE; + break; + case h_connection_close: + parser->flags |= F_CONNECTION_CLOSE; + break; + case h_connection_upgrade: + parser->flags |= F_CONNECTION_UPGRADE; + break; + case h_transfer_encoding_chunked: + parser->flags |= F_CHUNKED; + break; + default: + break; + } + + /* header value was empty */ + MARK(header_value); + UPDATE_STATE(s_header_field_start); + CALLBACK_DATA_NOADVANCE(header_value); + REEXECUTE(); + } + } + + case s_headers_almost_done: + { + STRICT_CHECK(ch != LF); + + if (parser->flags & F_TRAILING) { + /* End of a chunked request */ + UPDATE_STATE(s_message_done); + CALLBACK_NOTIFY_NOADVANCE(chunk_complete); + REEXECUTE(); + } + + UPDATE_STATE(s_headers_done); + + /* Set this here so that on_headers_complete() callbacks can see it */ + parser->upgrade = + ((parser->flags & (F_UPGRADE | F_CONNECTION_UPGRADE)) == + (F_UPGRADE | F_CONNECTION_UPGRADE) || + parser->method == HTTP_CONNECT); + + /* Here we call the headers_complete callback. This is somewhat + * different than other callbacks because if the user returns 1, we + * will interpret that as saying that this message has no body. This + * is needed for the annoying case of recieving a response to a HEAD + * request. + * + * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so + * we have to simulate it by handling a change in errno below. + */ + if (settings->on_headers_complete) { + switch (settings->on_headers_complete(parser)) { + case 0: + break; + + case 1: + parser->flags |= F_SKIPBODY; + break; + + default: + SET_ERRNO(HPE_CB_headers_complete); + RETURN(p - data); /* Error */ + } + } + + if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { + RETURN(p - data); + } + + REEXECUTE(); + } + + case s_headers_done: + { + STRICT_CHECK(ch != LF); + + parser->nread = 0; + + int hasBody = parser->flags & F_CHUNKED || + (parser->content_length > 0 && parser->content_length != ULLONG_MAX); + if (parser->upgrade && (parser->method == HTTP_CONNECT || + (parser->flags & F_SKIPBODY) || !hasBody)) { + /* Exit, the rest of the message is in a different protocol. */ + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + RETURN((p - data) + 1); + } + + if (parser->flags & F_SKIPBODY) { + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + } else if (parser->flags & F_CHUNKED) { + /* chunked encoding - ignore Content-Length header */ + UPDATE_STATE(s_chunk_size_start); + } else { + if (parser->content_length == 0) { + /* Content-Length header given but zero: Content-Length: 0\r\n */ + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + } else if (parser->content_length != ULLONG_MAX) { + /* Content-Length header given and non-zero */ + UPDATE_STATE(s_body_identity); + } else { + if (parser->type == HTTP_REQUEST || + !http_message_needs_eof(parser)) { + /* Assume content-length 0 - read the next */ + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + } else { + /* Read body until EOF */ + UPDATE_STATE(s_body_identity_eof); + } + } + } + + break; + } + + case s_body_identity: + { + uint64_t to_read = MIN(parser->content_length, + (uint64_t) ((data + len) - p)); + + assert(parser->content_length != 0 + && parser->content_length != ULLONG_MAX); + + /* The difference between advancing content_length and p is because + * the latter will automaticaly advance on the next loop iteration. + * Further, if content_length ends up at 0, we want to see the last + * byte again for our message complete callback. + */ + MARK(body); + parser->content_length -= to_read; + p += to_read - 1; + + if (parser->content_length == 0) { + UPDATE_STATE(s_message_done); + + /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte. + * + * The alternative to doing this is to wait for the next byte to + * trigger the data callback, just as in every other case. The + * problem with this is that this makes it difficult for the test + * harness to distinguish between complete-on-EOF and + * complete-on-length. It's not clear that this distinction is + * important for applications, but let's keep it for now. + */ + CALLBACK_DATA_(body, p - body_mark + 1, p - data); + REEXECUTE(); + } + + break; + } + + /* read until EOF */ + case s_body_identity_eof: + MARK(body); + p = data + len - 1; + + break; + + case s_message_done: + UPDATE_STATE(NEW_MESSAGE()); + CALLBACK_NOTIFY(message_complete); + if (parser->upgrade) { + /* Exit, the rest of the message is in a different protocol. */ + RETURN((p - data) + 1); + } + break; + + case s_chunk_size_start: + { + assert(parser->nread == 1); + assert(parser->flags & F_CHUNKED); + + unhex_val = unhex[(unsigned char)ch]; + if (UNLIKELY(unhex_val == -1)) { + SET_ERRNO(HPE_INVALID_CHUNK_SIZE); + goto error; + } + + parser->content_length = unhex_val; + UPDATE_STATE(s_chunk_size); + break; + } + + case s_chunk_size: + { + uint64_t t; + + assert(parser->flags & F_CHUNKED); + + if (ch == CR) { + UPDATE_STATE(s_chunk_size_almost_done); + break; + } + + unhex_val = unhex[(unsigned char)ch]; + + if (unhex_val == -1) { + if (ch == ';' || ch == ' ') { + UPDATE_STATE(s_chunk_parameters); + break; + } + + SET_ERRNO(HPE_INVALID_CHUNK_SIZE); + goto error; + } + + t = parser->content_length; + t *= 16; + t += unhex_val; + + /* Overflow? Test against a conservative limit for simplicity. */ + if (UNLIKELY((ULLONG_MAX - 16) / 16 < parser->content_length)) { + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); + goto error; + } + + parser->content_length = t; + break; + } + + case s_chunk_parameters: + { + assert(parser->flags & F_CHUNKED); + /* just ignore this shit. TODO check for overflow */ + if (ch == CR) { + UPDATE_STATE(s_chunk_size_almost_done); + break; + } + break; + } + + case s_chunk_size_almost_done: + { + assert(parser->flags & F_CHUNKED); + STRICT_CHECK(ch != LF); + + parser->nread = 0; + + if (parser->content_length == 0) { + parser->flags |= F_TRAILING; + UPDATE_STATE(s_header_field_start); + } else { + UPDATE_STATE(s_chunk_data); + } + CALLBACK_NOTIFY(chunk_header); + break; + } + + case s_chunk_data: + { + uint64_t to_read = MIN(parser->content_length, + (uint64_t) ((data + len) - p)); + + assert(parser->flags & F_CHUNKED); + assert(parser->content_length != 0 + && parser->content_length != ULLONG_MAX); + + /* See the explanation in s_body_identity for why the content + * length and data pointers are managed this way. + */ + MARK(body); + parser->content_length -= to_read; + p += to_read - 1; + + if (parser->content_length == 0) { + UPDATE_STATE(s_chunk_data_almost_done); + } + + break; + } + + case s_chunk_data_almost_done: + assert(parser->flags & F_CHUNKED); + assert(parser->content_length == 0); + STRICT_CHECK(ch != CR); + UPDATE_STATE(s_chunk_data_done); + CALLBACK_DATA(body); + break; + + case s_chunk_data_done: + assert(parser->flags & F_CHUNKED); + STRICT_CHECK(ch != LF); + parser->nread = 0; + UPDATE_STATE(s_chunk_size_start); + CALLBACK_NOTIFY(chunk_complete); + break; + + default: + assert(0 && "unhandled state"); + SET_ERRNO(HPE_INVALID_INTERNAL_STATE); + goto error; + } + } + + /* Run callbacks for any marks that we have leftover after we ran our of + * bytes. There should be at most one of these set, so it's OK to invoke + * them in series (unset marks will not result in callbacks). + * + * We use the NOADVANCE() variety of callbacks here because 'p' has already + * overflowed 'data' and this allows us to correct for the off-by-one that + * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p' + * value that's in-bounds). + */ + + assert(((header_field_mark ? 1 : 0) + + (header_value_mark ? 1 : 0) + + (url_mark ? 1 : 0) + + (body_mark ? 1 : 0) + + (status_mark ? 1 : 0)) <= 1); + + CALLBACK_DATA_NOADVANCE(header_field); + CALLBACK_DATA_NOADVANCE(header_value); + CALLBACK_DATA_NOADVANCE(url); + CALLBACK_DATA_NOADVANCE(body); + CALLBACK_DATA_NOADVANCE(status); + + RETURN(len); + +error: + if (HTTP_PARSER_ERRNO(parser) == HPE_OK) { + SET_ERRNO(HPE_UNKNOWN); + } + + RETURN(p - data); +} + + +/* Does the parser need to see an EOF to find the end of the message? */ +int +http_message_needs_eof (const http_parser *parser) +{ + if (parser->type == HTTP_REQUEST) { + return 0; + } + + /* See RFC 2616 section 4.4 */ + if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */ + parser->status_code == 204 || /* No Content */ + parser->status_code == 304 || /* Not Modified */ + parser->flags & F_SKIPBODY) { /* response to a HEAD request */ + return 0; + } + + if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) { + return 0; + } + + return 1; +} + + +int +http_should_keep_alive (const http_parser *parser) +{ + if (parser->http_major > 0 && parser->http_minor > 0) { + /* HTTP/1.1 */ + if (parser->flags & F_CONNECTION_CLOSE) { + return 0; + } + } else { + /* HTTP/1.0 or earlier */ + if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) { + return 0; + } + } + + return !http_message_needs_eof(parser); +} + + +const char * +http_method_str (enum http_method m) +{ + return ELEM_AT(method_strings, m, ""); +} + + +void +http_parser_init (http_parser *parser, enum http_parser_type t) +{ + void *data = parser->data; /* preserve application data */ + memset(parser, 0, sizeof(*parser)); + parser->data = data; + parser->type = t; + parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res)); + parser->http_errno = HPE_OK; +} + +void +http_parser_settings_init(http_parser_settings *settings) +{ + memset(settings, 0, sizeof(*settings)); +} + +const char * +http_errno_name(enum http_errno err) { + assert(((size_t) err) < + (sizeof(http_strerror_tab) / sizeof(http_strerror_tab[0]))); + return http_strerror_tab[err].name; +} + +const char * +http_errno_description(enum http_errno err) { + assert(((size_t) err) < + (sizeof(http_strerror_tab) / sizeof(http_strerror_tab[0]))); + return http_strerror_tab[err].description; +} + +static enum http_host_state +http_parse_host_char(enum http_host_state s, const char ch) { + switch(s) { + case s_http_userinfo: + case s_http_userinfo_start: + if (ch == '@') { + return s_http_host_start; + } + + if (IS_USERINFO_CHAR(ch)) { + return s_http_userinfo; + } + break; + + case s_http_host_start: + if (ch == '[') { + return s_http_host_v6_start; + } + + if (IS_HOST_CHAR(ch)) { + return s_http_host; + } + + break; + + case s_http_host: + if (IS_HOST_CHAR(ch)) { + return s_http_host; + } + + /* FALLTHROUGH */ + case s_http_host_v6_end: + if (ch == ':') { + return s_http_host_port_start; + } + + break; + + case s_http_host_v6: + if (ch == ']') { + return s_http_host_v6_end; + } + + /* FALLTHROUGH */ + case s_http_host_v6_start: + if (IS_HEX(ch) || ch == ':' || ch == '.') { + return s_http_host_v6; + } + + break; + + case s_http_host_port: + case s_http_host_port_start: + if (IS_NUM(ch)) { + return s_http_host_port; + } + + break; + + default: + break; + } + return s_http_host_dead; +} + +static int +http_parse_host(const char * buf, struct http_parser_url *u, int found_at) { + enum http_host_state s; + + const char *p; + size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len; + + u->field_data[UF_HOST].len = 0; + + s = found_at ? s_http_userinfo_start : s_http_host_start; + + for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) { + enum http_host_state new_s = http_parse_host_char(s, *p); + + if (new_s == s_http_host_dead) { + return 1; + } + + switch(new_s) { + case s_http_host: + if (s != s_http_host) { + u->field_data[UF_HOST].off = p - buf; + } + u->field_data[UF_HOST].len++; + break; + + case s_http_host_v6: + if (s != s_http_host_v6) { + u->field_data[UF_HOST].off = p - buf; + } + u->field_data[UF_HOST].len++; + break; + + case s_http_host_port: + if (s != s_http_host_port) { + u->field_data[UF_PORT].off = p - buf; + u->field_data[UF_PORT].len = 0; + u->field_set |= (1 << UF_PORT); + } + u->field_data[UF_PORT].len++; + break; + + case s_http_userinfo: + if (s != s_http_userinfo) { + u->field_data[UF_USERINFO].off = p - buf ; + u->field_data[UF_USERINFO].len = 0; + u->field_set |= (1 << UF_USERINFO); + } + u->field_data[UF_USERINFO].len++; + break; + + default: + break; + } + s = new_s; + } + + /* Make sure we don't end somewhere unexpected */ + switch (s) { + case s_http_host_start: + case s_http_host_v6_start: + case s_http_host_v6: + case s_http_host_port_start: + case s_http_userinfo: + case s_http_userinfo_start: + return 1; + default: + break; + } + + return 0; +} + +int +http_parser_parse_url(const char *buf, size_t buflen, int is_connect, + struct http_parser_url *u) +{ + enum state s; + const char *p; + enum http_parser_url_fields uf, old_uf; + int found_at = 0; + + u->port = u->field_set = 0; + s = is_connect ? s_req_server_start : s_req_spaces_before_url; + old_uf = UF_MAX; + + for (p = buf; p < buf + buflen; p++) { + s = parse_url_char(s, *p); + + /* Figure out the next field that we're operating on */ + switch (s) { + case s_dead: + return 1; + + /* Skip delimeters */ + case s_req_schema_slash: + case s_req_schema_slash_slash: + case s_req_server_start: + case s_req_query_string_start: + case s_req_fragment_start: + continue; + + case s_req_schema: + uf = UF_SCHEMA; + break; + + case s_req_server_with_at: + found_at = 1; + + /* FALLTROUGH */ + case s_req_server: + uf = UF_HOST; + break; + + case s_req_path: + uf = UF_PATH; + break; + + case s_req_query_string: + uf = UF_QUERY; + break; + + case s_req_fragment: + uf = UF_FRAGMENT; + break; + + default: + assert(!"Unexpected state"); + return 1; + } + + /* Nothing's changed; soldier on */ + if (uf == old_uf) { + u->field_data[uf].len++; + continue; + } + + u->field_data[uf].off = p - buf; + u->field_data[uf].len = 1; + + u->field_set |= (1 << uf); + old_uf = uf; + } + + /* host must be present if there is a schema */ + /* parsing http:///toto will fail */ + if ((u->field_set & ((1 << UF_SCHEMA) | (1 << UF_HOST))) != 0) { + if (http_parse_host(buf, u, found_at) != 0) { + return 1; + } + } + + /* CONNECT requests can only contain "hostname:port" */ + if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) { + return 1; + } + + if (u->field_set & (1 << UF_PORT)) { + /* Don't bother with endp; we've already validated the string */ + unsigned long v = strtoul(buf + u->field_data[UF_PORT].off, NULL, 10); + + /* Ports have a max value of 2^16 */ + if (v > 0xffff) { + return 1; + } + + u->port = (uint16_t) v; + } + + return 0; +} + +void +http_parser_pause(http_parser *parser, int paused) { + /* Users should only be pausing/unpausing a parser that is not in an error + * state. In non-debug builds, there's not much that we can do about this + * other than ignore it. + */ + if (HTTP_PARSER_ERRNO(parser) == HPE_OK || + HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) { + SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK); + } else { + assert(0 && "Attempting to pause parser in error state"); + } +} + +int +http_body_is_final(const struct http_parser *parser) { + return parser->state == s_message_done; +} + +unsigned long +http_parser_version(void) { + return HTTP_PARSER_VERSION_MAJOR * 0x10000 | + HTTP_PARSER_VERSION_MINOR * 0x00100 | + HTTP_PARSER_VERSION_PATCH * 0x00001; +} diff --git a/external/qhttpserver/http-parser/http_parser.gyp b/external/qhttpserver/http-parser/http_parser.gyp new file mode 100755 index 0000000..ef34eca --- /dev/null +++ b/external/qhttpserver/http-parser/http_parser.gyp @@ -0,0 +1,111 @@ +# This file is used with the GYP meta build system. +# http://code.google.com/p/gyp/ +# To build try this: +# svn co http://gyp.googlecode.com/svn/trunk gyp +# ./gyp/gyp -f make --depth=`pwd` http_parser.gyp +# ./out/Debug/test +{ + 'target_defaults': { + 'default_configuration': 'Debug', + 'configurations': { + # TODO: hoist these out and put them somewhere common, because + # RuntimeLibrary MUST MATCH across the entire project + 'Debug': { + 'defines': [ 'DEBUG', '_DEBUG' ], + 'cflags': [ '-Wall', '-Wextra', '-O0', '-g', '-ftrapv' ], + 'msvs_settings': { + 'VCCLCompilerTool': { + 'RuntimeLibrary': 1, # static debug + }, + }, + }, + 'Release': { + 'defines': [ 'NDEBUG' ], + 'cflags': [ '-Wall', '-Wextra', '-O3' ], + 'msvs_settings': { + 'VCCLCompilerTool': { + 'RuntimeLibrary': 0, # static release + }, + }, + } + }, + 'msvs_settings': { + 'VCCLCompilerTool': { + }, + 'VCLibrarianTool': { + }, + 'VCLinkerTool': { + 'GenerateDebugInformation': 'true', + }, + }, + 'conditions': [ + ['OS == "win"', { + 'defines': [ + 'WIN32' + ], + }] + ], + }, + + 'targets': [ + { + 'target_name': 'http_parser', + 'type': 'static_library', + 'include_dirs': [ '.' ], + 'direct_dependent_settings': { + 'defines': [ 'HTTP_PARSER_STRICT=0' ], + 'include_dirs': [ '.' ], + }, + 'defines': [ 'HTTP_PARSER_STRICT=0' ], + 'sources': [ './http_parser.c', ], + 'conditions': [ + ['OS=="win"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + # Compile as C++. http_parser.c is actually C99, but C++ is + # close enough in this case. + 'CompileAs': 2, + }, + }, + }] + ], + }, + + { + 'target_name': 'http_parser_strict', + 'type': 'static_library', + 'include_dirs': [ '.' ], + 'direct_dependent_settings': { + 'defines': [ 'HTTP_PARSER_STRICT=1' ], + 'include_dirs': [ '.' ], + }, + 'defines': [ 'HTTP_PARSER_STRICT=1' ], + 'sources': [ './http_parser.c', ], + 'conditions': [ + ['OS=="win"', { + 'msvs_settings': { + 'VCCLCompilerTool': { + # Compile as C++. http_parser.c is actually C99, but C++ is + # close enough in this case. + 'CompileAs': 2, + }, + }, + }] + ], + }, + + { + 'target_name': 'test-nonstrict', + 'type': 'executable', + 'dependencies': [ 'http_parser' ], + 'sources': [ 'test.c' ] + }, + + { + 'target_name': 'test-strict', + 'type': 'executable', + 'dependencies': [ 'http_parser_strict' ], + 'sources': [ 'test.c' ] + } + ] +} diff --git a/external/qhttpserver/http-parser/http_parser.h b/external/qhttpserver/http-parser/http_parser.h new file mode 100755 index 0000000..eb71bf9 --- /dev/null +++ b/external/qhttpserver/http-parser/http_parser.h @@ -0,0 +1,342 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#ifndef http_parser_h +#define http_parser_h +#ifdef __cplusplus +extern "C" { +#endif + +/* Also update SONAME in the Makefile whenever you change these. */ +#define HTTP_PARSER_VERSION_MAJOR 2 +#define HTTP_PARSER_VERSION_MINOR 5 +#define HTTP_PARSER_VERSION_PATCH 0 + +#include +#if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600) +#include +#include +typedef __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#else +#include +#endif + +/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run + * faster + */ +#ifndef HTTP_PARSER_STRICT +# define HTTP_PARSER_STRICT 1 +#endif + +/* Maximium header size allowed. If the macro is not defined + * before including this header then the default is used. To + * change the maximum header size, define the macro in the build + * environment (e.g. -DHTTP_MAX_HEADER_SIZE=). To remove + * the effective limit on the size of the header, define the macro + * to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff) + */ +#ifndef HTTP_MAX_HEADER_SIZE +# define HTTP_MAX_HEADER_SIZE (80*1024) +#endif + +typedef struct http_parser http_parser; +typedef struct http_parser_settings http_parser_settings; + + +/* Callbacks should return non-zero to indicate an error. The parser will + * then halt execution. + * + * The one exception is on_headers_complete. In a HTTP_RESPONSE parser + * returning '1' from on_headers_complete will tell the parser that it + * should not expect a body. This is used when receiving a response to a + * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding: + * chunked' headers that indicate the presence of a body. + * + * http_data_cb does not return data chunks. It will be called arbitrarily + * many times for each string. E.G. you might get 10 callbacks for "on_url" + * each providing just a few characters more data. + */ +typedef int (*http_data_cb) (http_parser*, const char *at, size_t length); +typedef int (*http_cb) (http_parser*); + + +/* Request Methods */ +#define HTTP_METHOD_MAP(XX) \ + XX(0, DELETE, DELETE) \ + XX(1, GET, GET) \ + XX(2, HEAD, HEAD) \ + XX(3, POST, POST) \ + XX(4, PUT, PUT) \ + /* pathological */ \ + XX(5, CONNECT, CONNECT) \ + XX(6, OPTIONS, OPTIONS) \ + XX(7, TRACE, TRACE) \ + /* webdav */ \ + XX(8, COPY, COPY) \ + XX(9, LOCK, LOCK) \ + XX(10, MKCOL, MKCOL) \ + XX(11, MOVE, MOVE) \ + XX(12, PROPFIND, PROPFIND) \ + XX(13, PROPPATCH, PROPPATCH) \ + XX(14, SEARCH, SEARCH) \ + XX(15, UNLOCK, UNLOCK) \ + /* subversion */ \ + XX(16, REPORT, REPORT) \ + XX(17, MKACTIVITY, MKACTIVITY) \ + XX(18, CHECKOUT, CHECKOUT) \ + XX(19, MERGE, MERGE) \ + /* upnp */ \ + XX(20, MSEARCH, M-SEARCH) \ + XX(21, NOTIFY, NOTIFY) \ + XX(22, SUBSCRIBE, SUBSCRIBE) \ + XX(23, UNSUBSCRIBE, UNSUBSCRIBE) \ + /* RFC-5789 */ \ + XX(24, PATCH, PATCH) \ + XX(25, PURGE, PURGE) \ + /* CalDAV */ \ + XX(26, MKCALENDAR, MKCALENDAR) \ + +enum http_method + { +#define XX(num, name, string) HTTP_##name = num, + HTTP_METHOD_MAP(XX) +#undef XX + }; + + +enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH }; + + +/* Flag values for http_parser.flags field */ +enum flags + { F_CHUNKED = 1 << 0 + , F_CONNECTION_KEEP_ALIVE = 1 << 1 + , F_CONNECTION_CLOSE = 1 << 2 + , F_CONNECTION_UPGRADE = 1 << 3 + , F_TRAILING = 1 << 4 + , F_UPGRADE = 1 << 5 + , F_SKIPBODY = 1 << 6 + }; + + +/* Map for errno-related constants + * + * The provided argument should be a macro that takes 2 arguments. + */ +#define HTTP_ERRNO_MAP(XX) \ + /* No error */ \ + XX(OK, "success") \ + \ + /* Callback-related errors */ \ + XX(CB_message_begin, "the on_message_begin callback failed") \ + XX(CB_url, "the on_url callback failed") \ + XX(CB_header_field, "the on_header_field callback failed") \ + XX(CB_header_value, "the on_header_value callback failed") \ + XX(CB_headers_complete, "the on_headers_complete callback failed") \ + XX(CB_body, "the on_body callback failed") \ + XX(CB_message_complete, "the on_message_complete callback failed") \ + XX(CB_status, "the on_status callback failed") \ + XX(CB_chunk_header, "the on_chunk_header callback failed") \ + XX(CB_chunk_complete, "the on_chunk_complete callback failed") \ + \ + /* Parsing-related errors */ \ + XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \ + XX(HEADER_OVERFLOW, \ + "too many header bytes seen; overflow detected") \ + XX(CLOSED_CONNECTION, \ + "data received after completed connection: close message") \ + XX(INVALID_VERSION, "invalid HTTP version") \ + XX(INVALID_STATUS, "invalid HTTP status code") \ + XX(INVALID_METHOD, "invalid HTTP method") \ + XX(INVALID_URL, "invalid URL") \ + XX(INVALID_HOST, "invalid host") \ + XX(INVALID_PORT, "invalid port") \ + XX(INVALID_PATH, "invalid path") \ + XX(INVALID_QUERY_STRING, "invalid query string") \ + XX(INVALID_FRAGMENT, "invalid fragment") \ + XX(LF_EXPECTED, "LF character expected") \ + XX(INVALID_HEADER_TOKEN, "invalid character in header") \ + XX(INVALID_CONTENT_LENGTH, \ + "invalid character in content-length header") \ + XX(INVALID_CHUNK_SIZE, \ + "invalid character in chunk size header") \ + XX(INVALID_CONSTANT, "invalid constant string") \ + XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\ + XX(STRICT, "strict mode assertion failed") \ + XX(PAUSED, "parser is paused") \ + XX(UNKNOWN, "an unknown error occurred") + + +/* Define HPE_* values for each errno value above */ +#define HTTP_ERRNO_GEN(n, s) HPE_##n, +enum http_errno { + HTTP_ERRNO_MAP(HTTP_ERRNO_GEN) +}; +#undef HTTP_ERRNO_GEN + + +/* Get an http_errno value from an http_parser */ +#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno) + + +struct http_parser { + /** PRIVATE **/ + unsigned int type : 2; /* enum http_parser_type */ + unsigned int flags : 7; /* F_* values from 'flags' enum; semi-public */ + unsigned int state : 7; /* enum state from http_parser.c */ + unsigned int header_state : 8; /* enum header_state from http_parser.c */ + unsigned int index : 8; /* index into current matcher */ + + uint32_t nread; /* # bytes read in various scenarios */ + uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */ + + /** READ-ONLY **/ + unsigned short http_major; + unsigned short http_minor; + unsigned int status_code : 16; /* responses only */ + unsigned int method : 8; /* requests only */ + unsigned int http_errno : 7; + + /* 1 = Upgrade header was present and the parser has exited because of that. + * 0 = No upgrade header present. + * Should be checked when http_parser_execute() returns in addition to + * error checking. + */ + unsigned int upgrade : 1; + + /** PUBLIC **/ + void *data; /* A pointer to get hook to the "connection" or "socket" object */ +}; + + +struct http_parser_settings { + http_cb on_message_begin; + http_data_cb on_url; + http_data_cb on_status; + http_data_cb on_header_field; + http_data_cb on_header_value; + http_cb on_headers_complete; + http_data_cb on_body; + http_cb on_message_complete; + /* When on_chunk_header is called, the current chunk length is stored + * in parser->content_length. + */ + http_cb on_chunk_header; + http_cb on_chunk_complete; +}; + + +enum http_parser_url_fields + { UF_SCHEMA = 0 + , UF_HOST = 1 + , UF_PORT = 2 + , UF_PATH = 3 + , UF_QUERY = 4 + , UF_FRAGMENT = 5 + , UF_USERINFO = 6 + , UF_MAX = 7 + }; + + +/* Result structure for http_parser_parse_url(). + * + * Callers should index into field_data[] with UF_* values iff field_set + * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and + * because we probably have padding left over), we convert any port to + * a uint16_t. + */ +struct http_parser_url { + uint16_t field_set; /* Bitmask of (1 << UF_*) values */ + uint16_t port; /* Converted UF_PORT string */ + + struct { + uint16_t off; /* Offset into buffer in which field starts */ + uint16_t len; /* Length of run in buffer */ + } field_data[UF_MAX]; +}; + + +/* Returns the library version. Bits 16-23 contain the major version number, + * bits 8-15 the minor version number and bits 0-7 the patch level. + * Usage example: + * + * unsigned long version = http_parser_version(); + * unsigned major = (version >> 16) & 255; + * unsigned minor = (version >> 8) & 255; + * unsigned patch = version & 255; + * printf("http_parser v%u.%u.%u\n", major, minor, patch); + */ +unsigned long http_parser_version(void); + +void http_parser_init(http_parser *parser, enum http_parser_type type); + + +/* Initialize http_parser_settings members to 0 + */ +void http_parser_settings_init(http_parser_settings *settings); + + +/* Executes the parser. Returns number of parsed bytes. Sets + * `parser->http_errno` on error. */ +size_t http_parser_execute(http_parser *parser, + const http_parser_settings *settings, + const char *data, + size_t len); + + +/* If http_should_keep_alive() in the on_headers_complete or + * on_message_complete callback returns 0, then this should be + * the last message on the connection. + * If you are the server, respond with the "Connection: close" header. + * If you are the client, close the connection. + */ +int http_should_keep_alive(const http_parser *parser); + +/* Returns a string version of the HTTP method. */ +const char *http_method_str(enum http_method m); + +/* Return a string name of the given error */ +const char *http_errno_name(enum http_errno err); + +/* Return a string description of the given error */ +const char *http_errno_description(enum http_errno err); + +/* Parse a URL; return nonzero on failure */ +int http_parser_parse_url(const char *buf, size_t buflen, + int is_connect, + struct http_parser_url *u); + +/* Pause or un-pause the parser; a nonzero value pauses */ +void http_parser_pause(http_parser *parser, int paused); + +/* Checks if this is the final chunk of the body. */ +int http_body_is_final(const http_parser *parser); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/external/qhttpserver/http-parser/test.c b/external/qhttpserver/http-parser/test.c new file mode 100755 index 0000000..4c00571 --- /dev/null +++ b/external/qhttpserver/http-parser/test.c @@ -0,0 +1,3852 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include "http_parser.h" +#include +#include +#include +#include /* rand */ +#include +#include + +#if defined(__APPLE__) +# undef strlcat +# undef strlncpy +# undef strlcpy +#endif /* defined(__APPLE__) */ + +#undef TRUE +#define TRUE 1 +#undef FALSE +#define FALSE 0 + +#define MAX_HEADERS 13 +#define MAX_ELEMENT_SIZE 2048 +#define MAX_CHUNKS 16 + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + +static http_parser *parser; + +struct message { + const char *name; // for debugging purposes + const char *raw; + enum http_parser_type type; + enum http_method method; + int status_code; + char response_status[MAX_ELEMENT_SIZE]; + char request_path[MAX_ELEMENT_SIZE]; + char request_url[MAX_ELEMENT_SIZE]; + char fragment[MAX_ELEMENT_SIZE]; + char query_string[MAX_ELEMENT_SIZE]; + char body[MAX_ELEMENT_SIZE]; + size_t body_size; + const char *host; + const char *userinfo; + uint16_t port; + int num_headers; + enum { NONE=0, FIELD, VALUE } last_header_element; + char headers [MAX_HEADERS][2][MAX_ELEMENT_SIZE]; + int should_keep_alive; + + int num_chunks; + int num_chunks_complete; + int chunk_lengths[MAX_CHUNKS]; + + const char *upgrade; // upgraded body + + unsigned short http_major; + unsigned short http_minor; + + int message_begin_cb_called; + int headers_complete_cb_called; + int message_complete_cb_called; + int message_complete_on_eof; + int body_is_final; +}; + +static int currently_parsing_eof; + +static struct message messages[5]; +static int num_messages; +static http_parser_settings *current_pause_parser; + +/* * R E Q U E S T S * */ +const struct message requests[] = +#define CURL_GET 0 +{ {.name= "curl get" + ,.type= HTTP_REQUEST + ,.raw= "GET /test HTTP/1.1\r\n" + "User-Agent: curl/7.18.0 (i486-pc-linux-gnu) libcurl/7.18.0 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.1\r\n" + "Host: 0.0.0.0=5000\r\n" + "Accept: */*\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/test" + ,.request_url= "/test" + ,.num_headers= 3 + ,.headers= + { { "User-Agent", "curl/7.18.0 (i486-pc-linux-gnu) libcurl/7.18.0 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.1" } + , { "Host", "0.0.0.0=5000" } + , { "Accept", "*/*" } + } + ,.body= "" + } + +#define FIREFOX_GET 1 +, {.name= "firefox get" + ,.type= HTTP_REQUEST + ,.raw= "GET /favicon.ico HTTP/1.1\r\n" + "Host: 0.0.0.0=5000\r\n" + "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0\r\n" + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" + "Accept-Language: en-us,en;q=0.5\r\n" + "Accept-Encoding: gzip,deflate\r\n" + "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n" + "Keep-Alive: 300\r\n" + "Connection: keep-alive\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/favicon.ico" + ,.request_url= "/favicon.ico" + ,.num_headers= 8 + ,.headers= + { { "Host", "0.0.0.0=5000" } + , { "User-Agent", "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0" } + , { "Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" } + , { "Accept-Language", "en-us,en;q=0.5" } + , { "Accept-Encoding", "gzip,deflate" } + , { "Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7" } + , { "Keep-Alive", "300" } + , { "Connection", "keep-alive" } + } + ,.body= "" + } + +#define DUMBFUCK 2 +, {.name= "dumbfuck" + ,.type= HTTP_REQUEST + ,.raw= "GET /dumbfuck HTTP/1.1\r\n" + "aaaaaaaaaaaaa:++++++++++\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/dumbfuck" + ,.request_url= "/dumbfuck" + ,.num_headers= 1 + ,.headers= + { { "aaaaaaaaaaaaa", "++++++++++" } + } + ,.body= "" + } + +#define FRAGMENT_IN_URI 3 +, {.name= "fragment in url" + ,.type= HTTP_REQUEST + ,.raw= "GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "page=1" + ,.fragment= "posts-17408" + ,.request_path= "/forums/1/topics/2375" + /* XXX request url does include fragment? */ + ,.request_url= "/forums/1/topics/2375?page=1#posts-17408" + ,.num_headers= 0 + ,.body= "" + } + +#define GET_NO_HEADERS_NO_BODY 4 +, {.name= "get no headers no body" + ,.type= HTTP_REQUEST + ,.raw= "GET /get_no_headers_no_body/world HTTP/1.1\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE /* would need Connection: close */ + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/get_no_headers_no_body/world" + ,.request_url= "/get_no_headers_no_body/world" + ,.num_headers= 0 + ,.body= "" + } + +#define GET_ONE_HEADER_NO_BODY 5 +, {.name= "get one header no body" + ,.type= HTTP_REQUEST + ,.raw= "GET /get_one_header_no_body HTTP/1.1\r\n" + "Accept: */*\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE /* would need Connection: close */ + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/get_one_header_no_body" + ,.request_url= "/get_one_header_no_body" + ,.num_headers= 1 + ,.headers= + { { "Accept" , "*/*" } + } + ,.body= "" + } + +#define GET_FUNKY_CONTENT_LENGTH 6 +, {.name= "get funky content length body hello" + ,.type= HTTP_REQUEST + ,.raw= "GET /get_funky_content_length_body_hello HTTP/1.0\r\n" + "conTENT-Length: 5\r\n" + "\r\n" + "HELLO" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 0 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/get_funky_content_length_body_hello" + ,.request_url= "/get_funky_content_length_body_hello" + ,.num_headers= 1 + ,.headers= + { { "conTENT-Length" , "5" } + } + ,.body= "HELLO" + } + +#define POST_IDENTITY_BODY_WORLD 7 +, {.name= "post identity body world" + ,.type= HTTP_REQUEST + ,.raw= "POST /post_identity_body_world?q=search#hey HTTP/1.1\r\n" + "Accept: */*\r\n" + "Transfer-Encoding: identity\r\n" + "Content-Length: 5\r\n" + "\r\n" + "World" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_POST + ,.query_string= "q=search" + ,.fragment= "hey" + ,.request_path= "/post_identity_body_world" + ,.request_url= "/post_identity_body_world?q=search#hey" + ,.num_headers= 3 + ,.headers= + { { "Accept", "*/*" } + , { "Transfer-Encoding", "identity" } + , { "Content-Length", "5" } + } + ,.body= "World" + } + +#define POST_CHUNKED_ALL_YOUR_BASE 8 +, {.name= "post - chunked body: all your base are belong to us" + ,.type= HTTP_REQUEST + ,.raw= "POST /post_chunked_all_your_base HTTP/1.1\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "1e\r\nall your base are belong to us\r\n" + "0\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_POST + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/post_chunked_all_your_base" + ,.request_url= "/post_chunked_all_your_base" + ,.num_headers= 1 + ,.headers= + { { "Transfer-Encoding" , "chunked" } + } + ,.body= "all your base are belong to us" + ,.num_chunks_complete= 2 + ,.chunk_lengths= { 0x1e } + } + +#define TWO_CHUNKS_MULT_ZERO_END 9 +, {.name= "two chunks ; triple zero ending" + ,.type= HTTP_REQUEST + ,.raw= "POST /two_chunks_mult_zero_end HTTP/1.1\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "5\r\nhello\r\n" + "6\r\n world\r\n" + "000\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_POST + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/two_chunks_mult_zero_end" + ,.request_url= "/two_chunks_mult_zero_end" + ,.num_headers= 1 + ,.headers= + { { "Transfer-Encoding", "chunked" } + } + ,.body= "hello world" + ,.num_chunks_complete= 3 + ,.chunk_lengths= { 5, 6 } + } + +#define CHUNKED_W_TRAILING_HEADERS 10 +, {.name= "chunked with trailing headers. blech." + ,.type= HTTP_REQUEST + ,.raw= "POST /chunked_w_trailing_headers HTTP/1.1\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "5\r\nhello\r\n" + "6\r\n world\r\n" + "0\r\n" + "Vary: *\r\n" + "Content-Type: text/plain\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_POST + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/chunked_w_trailing_headers" + ,.request_url= "/chunked_w_trailing_headers" + ,.num_headers= 3 + ,.headers= + { { "Transfer-Encoding", "chunked" } + , { "Vary", "*" } + , { "Content-Type", "text/plain" } + } + ,.body= "hello world" + ,.num_chunks_complete= 3 + ,.chunk_lengths= { 5, 6 } + } + +#define CHUNKED_W_BULLSHIT_AFTER_LENGTH 11 +, {.name= "with bullshit after the length" + ,.type= HTTP_REQUEST + ,.raw= "POST /chunked_w_bullshit_after_length HTTP/1.1\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "5; ihatew3;whatthefuck=aretheseparametersfor\r\nhello\r\n" + "6; blahblah; blah\r\n world\r\n" + "0\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_POST + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/chunked_w_bullshit_after_length" + ,.request_url= "/chunked_w_bullshit_after_length" + ,.num_headers= 1 + ,.headers= + { { "Transfer-Encoding", "chunked" } + } + ,.body= "hello world" + ,.num_chunks_complete= 3 + ,.chunk_lengths= { 5, 6 } + } + +#define WITH_QUOTES 12 +, {.name= "with quotes" + ,.type= HTTP_REQUEST + ,.raw= "GET /with_\"stupid\"_quotes?foo=\"bar\" HTTP/1.1\r\n\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "foo=\"bar\"" + ,.fragment= "" + ,.request_path= "/with_\"stupid\"_quotes" + ,.request_url= "/with_\"stupid\"_quotes?foo=\"bar\"" + ,.num_headers= 0 + ,.headers= { } + ,.body= "" + } + +#define APACHEBENCH_GET 13 +/* The server receiving this request SHOULD NOT wait for EOF + * to know that content-length == 0. + * How to represent this in a unit test? message_complete_on_eof + * Compare with NO_CONTENT_LENGTH_RESPONSE. + */ +, {.name = "apachebench get" + ,.type= HTTP_REQUEST + ,.raw= "GET /test HTTP/1.0\r\n" + "Host: 0.0.0.0:5000\r\n" + "User-Agent: ApacheBench/2.3\r\n" + "Accept: */*\r\n\r\n" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 0 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/test" + ,.request_url= "/test" + ,.num_headers= 3 + ,.headers= { { "Host", "0.0.0.0:5000" } + , { "User-Agent", "ApacheBench/2.3" } + , { "Accept", "*/*" } + } + ,.body= "" + } + +#define QUERY_URL_WITH_QUESTION_MARK_GET 14 +/* Some clients include '?' characters in query strings. + */ +, {.name = "query url with question mark" + ,.type= HTTP_REQUEST + ,.raw= "GET /test.cgi?foo=bar?baz HTTP/1.1\r\n\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "foo=bar?baz" + ,.fragment= "" + ,.request_path= "/test.cgi" + ,.request_url= "/test.cgi?foo=bar?baz" + ,.num_headers= 0 + ,.headers= {} + ,.body= "" + } + +#define PREFIX_NEWLINE_GET 15 +/* Some clients, especially after a POST in a keep-alive connection, + * will send an extra CRLF before the next request + */ +, {.name = "newline prefix get" + ,.type= HTTP_REQUEST + ,.raw= "\r\nGET /test HTTP/1.1\r\n\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/test" + ,.request_url= "/test" + ,.num_headers= 0 + ,.headers= { } + ,.body= "" + } + +#define UPGRADE_REQUEST 16 +, {.name = "upgrade request" + ,.type= HTTP_REQUEST + ,.raw= "GET /demo HTTP/1.1\r\n" + "Host: example.com\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n" + "Sec-WebSocket-Protocol: sample\r\n" + "Upgrade: WebSocket\r\n" + "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n" + "Origin: http://example.com\r\n" + "\r\n" + "Hot diggity dogg" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/demo" + ,.request_url= "/demo" + ,.num_headers= 7 + ,.upgrade="Hot diggity dogg" + ,.headers= { { "Host", "example.com" } + , { "Connection", "Upgrade" } + , { "Sec-WebSocket-Key2", "12998 5 Y3 1 .P00" } + , { "Sec-WebSocket-Protocol", "sample" } + , { "Upgrade", "WebSocket" } + , { "Sec-WebSocket-Key1", "4 @1 46546xW%0l 1 5" } + , { "Origin", "http://example.com" } + } + ,.body= "" + } + +#define CONNECT_REQUEST 17 +, {.name = "connect request" + ,.type= HTTP_REQUEST + ,.raw= "CONNECT 0-home0.netscape.com:443 HTTP/1.0\r\n" + "User-agent: Mozilla/1.1N\r\n" + "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n" + "\r\n" + "some data\r\n" + "and yet even more data" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 0 + ,.method= HTTP_CONNECT + ,.query_string= "" + ,.fragment= "" + ,.request_path= "" + ,.request_url= "0-home0.netscape.com:443" + ,.num_headers= 2 + ,.upgrade="some data\r\nand yet even more data" + ,.headers= { { "User-agent", "Mozilla/1.1N" } + , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" } + } + ,.body= "" + } + +#define REPORT_REQ 18 +, {.name= "report request" + ,.type= HTTP_REQUEST + ,.raw= "REPORT /test HTTP/1.1\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_REPORT + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/test" + ,.request_url= "/test" + ,.num_headers= 0 + ,.headers= {} + ,.body= "" + } + +#define NO_HTTP_VERSION 19 +, {.name= "request with no http version" + ,.type= HTTP_REQUEST + ,.raw= "GET /\r\n" + "\r\n" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= FALSE + ,.http_major= 0 + ,.http_minor= 9 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/" + ,.request_url= "/" + ,.num_headers= 0 + ,.headers= {} + ,.body= "" + } + +#define MSEARCH_REQ 20 +, {.name= "m-search request" + ,.type= HTTP_REQUEST + ,.raw= "M-SEARCH * HTTP/1.1\r\n" + "HOST: 239.255.255.250:1900\r\n" + "MAN: \"ssdp:discover\"\r\n" + "ST: \"ssdp:all\"\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_MSEARCH + ,.query_string= "" + ,.fragment= "" + ,.request_path= "*" + ,.request_url= "*" + ,.num_headers= 3 + ,.headers= { { "HOST", "239.255.255.250:1900" } + , { "MAN", "\"ssdp:discover\"" } + , { "ST", "\"ssdp:all\"" } + } + ,.body= "" + } + +#define LINE_FOLDING_IN_HEADER 21 +, {.name= "line folding in header value" + ,.type= HTTP_REQUEST + ,.raw= "GET / HTTP/1.1\r\n" + "Line1: abc\r\n" + "\tdef\r\n" + " ghi\r\n" + "\t\tjkl\r\n" + " mno \r\n" + "\t \tqrs\r\n" + "Line2: \t line2\t\r\n" + "Line3:\r\n" + " line3\r\n" + "Line4: \r\n" + " \r\n" + "Connection:\r\n" + " close\r\n" + "\r\n" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/" + ,.request_url= "/" + ,.num_headers= 5 + ,.headers= { { "Line1", "abc\tdef ghi\t\tjkl mno \t \tqrs" } + , { "Line2", "line2\t" } + , { "Line3", "line3" } + , { "Line4", "" } + , { "Connection", "close" }, + } + ,.body= "" + } + + +#define QUERY_TERMINATED_HOST 22 +, {.name= "host terminated by a query string" + ,.type= HTTP_REQUEST + ,.raw= "GET http://hypnotoad.org?hail=all HTTP/1.1\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "hail=all" + ,.fragment= "" + ,.request_path= "" + ,.request_url= "http://hypnotoad.org?hail=all" + ,.host= "hypnotoad.org" + ,.num_headers= 0 + ,.headers= { } + ,.body= "" + } + +#define QUERY_TERMINATED_HOSTPORT 23 +, {.name= "host:port terminated by a query string" + ,.type= HTTP_REQUEST + ,.raw= "GET http://hypnotoad.org:1234?hail=all HTTP/1.1\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "hail=all" + ,.fragment= "" + ,.request_path= "" + ,.request_url= "http://hypnotoad.org:1234?hail=all" + ,.host= "hypnotoad.org" + ,.port= 1234 + ,.num_headers= 0 + ,.headers= { } + ,.body= "" + } + +#define SPACE_TERMINATED_HOSTPORT 24 +, {.name= "host:port terminated by a space" + ,.type= HTTP_REQUEST + ,.raw= "GET http://hypnotoad.org:1234 HTTP/1.1\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "" + ,.request_url= "http://hypnotoad.org:1234" + ,.host= "hypnotoad.org" + ,.port= 1234 + ,.num_headers= 0 + ,.headers= { } + ,.body= "" + } + +#define PATCH_REQ 25 +, {.name = "PATCH request" + ,.type= HTTP_REQUEST + ,.raw= "PATCH /file.txt HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "Content-Type: application/example\r\n" + "If-Match: \"e0023aa4e\"\r\n" + "Content-Length: 10\r\n" + "\r\n" + "cccccccccc" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_PATCH + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/file.txt" + ,.request_url= "/file.txt" + ,.num_headers= 4 + ,.headers= { { "Host", "www.example.com" } + , { "Content-Type", "application/example" } + , { "If-Match", "\"e0023aa4e\"" } + , { "Content-Length", "10" } + } + ,.body= "cccccccccc" + } + +#define CONNECT_CAPS_REQUEST 26 +, {.name = "connect caps request" + ,.type= HTTP_REQUEST + ,.raw= "CONNECT HOME0.NETSCAPE.COM:443 HTTP/1.0\r\n" + "User-agent: Mozilla/1.1N\r\n" + "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n" + "\r\n" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 0 + ,.method= HTTP_CONNECT + ,.query_string= "" + ,.fragment= "" + ,.request_path= "" + ,.request_url= "HOME0.NETSCAPE.COM:443" + ,.num_headers= 2 + ,.upgrade="" + ,.headers= { { "User-agent", "Mozilla/1.1N" } + , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" } + } + ,.body= "" + } + +#if !HTTP_PARSER_STRICT +#define UTF8_PATH_REQ 27 +, {.name= "utf-8 path request" + ,.type= HTTP_REQUEST + ,.raw= "GET /δ¶/δt/pope?q=1#narf HTTP/1.1\r\n" + "Host: github.com\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "q=1" + ,.fragment= "narf" + ,.request_path= "/δ¶/δt/pope" + ,.request_url= "/δ¶/δt/pope?q=1#narf" + ,.num_headers= 1 + ,.headers= { {"Host", "github.com" } + } + ,.body= "" + } + +#define HOSTNAME_UNDERSCORE 28 +, {.name = "hostname underscore" + ,.type= HTTP_REQUEST + ,.raw= "CONNECT home_0.netscape.com:443 HTTP/1.0\r\n" + "User-agent: Mozilla/1.1N\r\n" + "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n" + "\r\n" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 0 + ,.method= HTTP_CONNECT + ,.query_string= "" + ,.fragment= "" + ,.request_path= "" + ,.request_url= "home_0.netscape.com:443" + ,.num_headers= 2 + ,.upgrade="" + ,.headers= { { "User-agent", "Mozilla/1.1N" } + , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" } + } + ,.body= "" + } +#endif /* !HTTP_PARSER_STRICT */ + +/* see https://github.com/ry/http-parser/issues/47 */ +#define EAT_TRAILING_CRLF_NO_CONNECTION_CLOSE 29 +, {.name = "eat CRLF between requests, no \"Connection: close\" header" + ,.raw= "POST / HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "Content-Length: 4\r\n" + "\r\n" + "q=42\r\n" /* note the trailing CRLF */ + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_POST + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/" + ,.request_url= "/" + ,.num_headers= 3 + ,.upgrade= 0 + ,.headers= { { "Host", "www.example.com" } + , { "Content-Type", "application/x-www-form-urlencoded" } + , { "Content-Length", "4" } + } + ,.body= "q=42" + } + +/* see https://github.com/ry/http-parser/issues/47 */ +#define EAT_TRAILING_CRLF_WITH_CONNECTION_CLOSE 30 +, {.name = "eat CRLF between requests even if \"Connection: close\" is set" + ,.raw= "POST / HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "Content-Length: 4\r\n" + "Connection: close\r\n" + "\r\n" + "q=42\r\n" /* note the trailing CRLF */ + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= FALSE /* input buffer isn't empty when on_message_complete is called */ + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_POST + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/" + ,.request_url= "/" + ,.num_headers= 4 + ,.upgrade= 0 + ,.headers= { { "Host", "www.example.com" } + , { "Content-Type", "application/x-www-form-urlencoded" } + , { "Content-Length", "4" } + , { "Connection", "close" } + } + ,.body= "q=42" + } + +#define PURGE_REQ 31 +, {.name = "PURGE request" + ,.type= HTTP_REQUEST + ,.raw= "PURGE /file.txt HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_PURGE + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/file.txt" + ,.request_url= "/file.txt" + ,.num_headers= 1 + ,.headers= { { "Host", "www.example.com" } } + ,.body= "" + } + +#define SEARCH_REQ 32 +, {.name = "SEARCH request" + ,.type= HTTP_REQUEST + ,.raw= "SEARCH / HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_SEARCH + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/" + ,.request_url= "/" + ,.num_headers= 1 + ,.headers= { { "Host", "www.example.com" } } + ,.body= "" + } + +#define PROXY_WITH_BASIC_AUTH 33 +, {.name= "host:port and basic_auth" + ,.type= HTTP_REQUEST + ,.raw= "GET http://a%12:b!&*$@hypnotoad.org:1234/toto HTTP/1.1\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.fragment= "" + ,.request_path= "/toto" + ,.request_url= "http://a%12:b!&*$@hypnotoad.org:1234/toto" + ,.host= "hypnotoad.org" + ,.userinfo= "a%12:b!&*$" + ,.port= 1234 + ,.num_headers= 0 + ,.headers= { } + ,.body= "" + } + +#define LINE_FOLDING_IN_HEADER_WITH_LF 34 +, {.name= "line folding in header value" + ,.type= HTTP_REQUEST + ,.raw= "GET / HTTP/1.1\n" + "Line1: abc\n" + "\tdef\n" + " ghi\n" + "\t\tjkl\n" + " mno \n" + "\t \tqrs\n" + "Line2: \t line2\t\n" + "Line3:\n" + " line3\n" + "Line4: \n" + " \n" + "Connection:\n" + " close\n" + "\n" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/" + ,.request_url= "/" + ,.num_headers= 5 + ,.headers= { { "Line1", "abc\tdef ghi\t\tjkl mno \t \tqrs" } + , { "Line2", "line2\t" } + , { "Line3", "line3" } + , { "Line4", "" } + , { "Connection", "close" }, + } + ,.body= "" + } + +#define CONNECTION_MULTI 35 +, {.name = "multiple connection header values with folding" + ,.type= HTTP_REQUEST + ,.raw= "GET /demo HTTP/1.1\r\n" + "Host: example.com\r\n" + "Connection: Something,\r\n" + " Upgrade, ,Keep-Alive\r\n" + "Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n" + "Sec-WebSocket-Protocol: sample\r\n" + "Upgrade: WebSocket\r\n" + "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n" + "Origin: http://example.com\r\n" + "\r\n" + "Hot diggity dogg" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/demo" + ,.request_url= "/demo" + ,.num_headers= 7 + ,.upgrade="Hot diggity dogg" + ,.headers= { { "Host", "example.com" } + , { "Connection", "Something, Upgrade, ,Keep-Alive" } + , { "Sec-WebSocket-Key2", "12998 5 Y3 1 .P00" } + , { "Sec-WebSocket-Protocol", "sample" } + , { "Upgrade", "WebSocket" } + , { "Sec-WebSocket-Key1", "4 @1 46546xW%0l 1 5" } + , { "Origin", "http://example.com" } + } + ,.body= "" + } + +#define CONNECTION_MULTI_LWS 36 +, {.name = "multiple connection header values with folding and lws" + ,.type= HTTP_REQUEST + ,.raw= "GET /demo HTTP/1.1\r\n" + "Connection: keep-alive, upgrade\r\n" + "Upgrade: WebSocket\r\n" + "\r\n" + "Hot diggity dogg" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/demo" + ,.request_url= "/demo" + ,.num_headers= 2 + ,.upgrade="Hot diggity dogg" + ,.headers= { { "Connection", "keep-alive, upgrade" } + , { "Upgrade", "WebSocket" } + } + ,.body= "" + } + +#define CONNECTION_MULTI_LWS_CRLF 37 +, {.name = "multiple connection header values with folding and lws" + ,.type= HTTP_REQUEST + ,.raw= "GET /demo HTTP/1.1\r\n" + "Connection: keep-alive, \r\n upgrade\r\n" + "Upgrade: WebSocket\r\n" + "\r\n" + "Hot diggity dogg" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_GET + ,.query_string= "" + ,.fragment= "" + ,.request_path= "/demo" + ,.request_url= "/demo" + ,.num_headers= 2 + ,.upgrade="Hot diggity dogg" + ,.headers= { { "Connection", "keep-alive, upgrade" } + , { "Upgrade", "WebSocket" } + } + ,.body= "" + } + +#define UPGRADE_POST_REQUEST 38 +, {.name = "upgrade post request" + ,.type= HTTP_REQUEST + ,.raw= "POST /demo HTTP/1.1\r\n" + "Host: example.com\r\n" + "Connection: Upgrade\r\n" + "Upgrade: HTTP/2.0\r\n" + "Content-Length: 15\r\n" + "\r\n" + "sweet post body" + "Hot diggity dogg" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.method= HTTP_POST + ,.request_path= "/demo" + ,.request_url= "/demo" + ,.num_headers= 4 + ,.upgrade="Hot diggity dogg" + ,.headers= { { "Host", "example.com" } + , { "Connection", "Upgrade" } + , { "Upgrade", "HTTP/2.0" } + , { "Content-Length", "15" } + } + ,.body= "sweet post body" + } + +#define CONNECT_WITH_BODY_REQUEST 39 +, {.name = "connect with body request" + ,.type= HTTP_REQUEST + ,.raw= "CONNECT foo.bar.com:443 HTTP/1.0\r\n" + "User-agent: Mozilla/1.1N\r\n" + "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n" + "Content-Length: 10\r\n" + "\r\n" + "blarfcicle" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 0 + ,.method= HTTP_CONNECT + ,.request_url= "foo.bar.com:443" + ,.num_headers= 3 + ,.upgrade="blarfcicle" + ,.headers= { { "User-agent", "Mozilla/1.1N" } + , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" } + , { "Content-Length", "10" } + } + ,.body= "" + } + +, {.name= NULL } /* sentinel */ +}; + +/* * R E S P O N S E S * */ +const struct message responses[] = +#define GOOGLE_301 0 +{ {.name= "google 301" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 301 Moved Permanently\r\n" + "Location: http://www.google.com/\r\n" + "Content-Type: text/html; charset=UTF-8\r\n" + "Date: Sun, 26 Apr 2009 11:11:49 GMT\r\n" + "Expires: Tue, 26 May 2009 11:11:49 GMT\r\n" + "X-$PrototypeBI-Version: 1.6.0.3\r\n" /* $ char in header field */ + "Cache-Control: public, max-age=2592000\r\n" + "Server: gws\r\n" + "Content-Length: 219 \r\n" + "\r\n" + "\n" + "301 Moved\n" + "

    301 Moved

    \n" + "The document has moved\n" + "here.\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 301 + ,.response_status= "Moved Permanently" + ,.num_headers= 8 + ,.headers= + { { "Location", "http://www.google.com/" } + , { "Content-Type", "text/html; charset=UTF-8" } + , { "Date", "Sun, 26 Apr 2009 11:11:49 GMT" } + , { "Expires", "Tue, 26 May 2009 11:11:49 GMT" } + , { "X-$PrototypeBI-Version", "1.6.0.3" } + , { "Cache-Control", "public, max-age=2592000" } + , { "Server", "gws" } + , { "Content-Length", "219 " } + } + ,.body= "\n" + "301 Moved\n" + "

    301 Moved

    \n" + "The document has moved\n" + "here.\r\n" + "\r\n" + } + +#define NO_CONTENT_LENGTH_RESPONSE 1 +/* The client should wait for the server's EOF. That is, when content-length + * is not specified, and "Connection: close", the end of body is specified + * by the EOF. + * Compare with APACHEBENCH_GET + */ +, {.name= "no content-length response" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 200 OK\r\n" + "Date: Tue, 04 Aug 2009 07:59:32 GMT\r\n" + "Server: Apache\r\n" + "X-Powered-By: Servlet/2.5 JSP/2.1\r\n" + "Content-Type: text/xml; charset=utf-8\r\n" + "Connection: close\r\n" + "\r\n" + "\n" + "\n" + " \n" + " \n" + " SOAP-ENV:Client\n" + " Client Error\n" + " \n" + " \n" + "" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= TRUE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 200 + ,.response_status= "OK" + ,.num_headers= 5 + ,.headers= + { { "Date", "Tue, 04 Aug 2009 07:59:32 GMT" } + , { "Server", "Apache" } + , { "X-Powered-By", "Servlet/2.5 JSP/2.1" } + , { "Content-Type", "text/xml; charset=utf-8" } + , { "Connection", "close" } + } + ,.body= "\n" + "\n" + " \n" + " \n" + " SOAP-ENV:Client\n" + " Client Error\n" + " \n" + " \n" + "" + } + +#define NO_HEADERS_NO_BODY_404 2 +, {.name= "404 no headers no body" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 404 Not Found\r\n\r\n" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= TRUE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 404 + ,.response_status= "Not Found" + ,.num_headers= 0 + ,.headers= {} + ,.body_size= 0 + ,.body= "" + } + +#define NO_REASON_PHRASE 3 +, {.name= "301 no response phrase" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 301\r\n\r\n" + ,.should_keep_alive = FALSE + ,.message_complete_on_eof= TRUE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 301 + ,.response_status= "" + ,.num_headers= 0 + ,.headers= {} + ,.body= "" + } + +#define TRAILING_SPACE_ON_CHUNKED_BODY 4 +, {.name="200 trailing space on chunked body" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 200 OK\r\n" + "Content-Type: text/plain\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "25 \r\n" + "This is the data in the first chunk\r\n" + "\r\n" + "1C\r\n" + "and this is the second one\r\n" + "\r\n" + "0 \r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 200 + ,.response_status= "OK" + ,.num_headers= 2 + ,.headers= + { {"Content-Type", "text/plain" } + , {"Transfer-Encoding", "chunked" } + } + ,.body_size = 37+28 + ,.body = + "This is the data in the first chunk\r\n" + "and this is the second one\r\n" + ,.num_chunks_complete= 3 + ,.chunk_lengths= { 0x25, 0x1c } + } + +#define NO_CARRIAGE_RET 5 +, {.name="no carriage ret" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 200 OK\n" + "Content-Type: text/html; charset=utf-8\n" + "Connection: close\n" + "\n" + "these headers are from http://news.ycombinator.com/" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= TRUE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 200 + ,.response_status= "OK" + ,.num_headers= 2 + ,.headers= + { {"Content-Type", "text/html; charset=utf-8" } + , {"Connection", "close" } + } + ,.body= "these headers are from http://news.ycombinator.com/" + } + +#define PROXY_CONNECTION 6 +, {.name="proxy connection" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 200 OK\r\n" + "Content-Type: text/html; charset=UTF-8\r\n" + "Content-Length: 11\r\n" + "Proxy-Connection: close\r\n" + "Date: Thu, 31 Dec 2009 20:55:48 +0000\r\n" + "\r\n" + "hello world" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 200 + ,.response_status= "OK" + ,.num_headers= 4 + ,.headers= + { {"Content-Type", "text/html; charset=UTF-8" } + , {"Content-Length", "11" } + , {"Proxy-Connection", "close" } + , {"Date", "Thu, 31 Dec 2009 20:55:48 +0000"} + } + ,.body= "hello world" + } + +#define UNDERSTORE_HEADER_KEY 7 + // shown by + // curl -o /dev/null -v "http://ad.doubleclick.net/pfadx/DARTSHELLCONFIGXML;dcmt=text/xml;" +, {.name="underscore header key" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 200 OK\r\n" + "Server: DCLK-AdSvr\r\n" + "Content-Type: text/xml\r\n" + "Content-Length: 0\r\n" + "DCLK_imp: v7;x;114750856;0-0;0;17820020;0/0;21603567/21621457/1;;~okv=;dcmt=text/xml;;~cs=o\r\n\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 200 + ,.response_status= "OK" + ,.num_headers= 4 + ,.headers= + { {"Server", "DCLK-AdSvr" } + , {"Content-Type", "text/xml" } + , {"Content-Length", "0" } + , {"DCLK_imp", "v7;x;114750856;0-0;0;17820020;0/0;21603567/21621457/1;;~okv=;dcmt=text/xml;;~cs=o" } + } + ,.body= "" + } + +#define BONJOUR_MADAME_FR 8 +/* The client should not merge two headers fields when the first one doesn't + * have a value. + */ +, {.name= "bonjourmadame.fr" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.0 301 Moved Permanently\r\n" + "Date: Thu, 03 Jun 2010 09:56:32 GMT\r\n" + "Server: Apache/2.2.3 (Red Hat)\r\n" + "Cache-Control: public\r\n" + "Pragma: \r\n" + "Location: http://www.bonjourmadame.fr/\r\n" + "Vary: Accept-Encoding\r\n" + "Content-Length: 0\r\n" + "Content-Type: text/html; charset=UTF-8\r\n" + "Connection: keep-alive\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 0 + ,.status_code= 301 + ,.response_status= "Moved Permanently" + ,.num_headers= 9 + ,.headers= + { { "Date", "Thu, 03 Jun 2010 09:56:32 GMT" } + , { "Server", "Apache/2.2.3 (Red Hat)" } + , { "Cache-Control", "public" } + , { "Pragma", "" } + , { "Location", "http://www.bonjourmadame.fr/" } + , { "Vary", "Accept-Encoding" } + , { "Content-Length", "0" } + , { "Content-Type", "text/html; charset=UTF-8" } + , { "Connection", "keep-alive" } + } + ,.body= "" + } + +#define RES_FIELD_UNDERSCORE 9 +/* Should handle spaces in header fields */ +, {.name= "field underscore" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 200 OK\r\n" + "Date: Tue, 28 Sep 2010 01:14:13 GMT\r\n" + "Server: Apache\r\n" + "Cache-Control: no-cache, must-revalidate\r\n" + "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n" + ".et-Cookie: PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com\r\n" + "Vary: Accept-Encoding\r\n" + "_eep-Alive: timeout=45\r\n" /* semantic value ignored */ + "_onnection: Keep-Alive\r\n" /* semantic value ignored */ + "Transfer-Encoding: chunked\r\n" + "Content-Type: text/html\r\n" + "Connection: close\r\n" + "\r\n" + "0\r\n\r\n" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 200 + ,.response_status= "OK" + ,.num_headers= 11 + ,.headers= + { { "Date", "Tue, 28 Sep 2010 01:14:13 GMT" } + , { "Server", "Apache" } + , { "Cache-Control", "no-cache, must-revalidate" } + , { "Expires", "Mon, 26 Jul 1997 05:00:00 GMT" } + , { ".et-Cookie", "PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com" } + , { "Vary", "Accept-Encoding" } + , { "_eep-Alive", "timeout=45" } + , { "_onnection", "Keep-Alive" } + , { "Transfer-Encoding", "chunked" } + , { "Content-Type", "text/html" } + , { "Connection", "close" } + } + ,.body= "" + ,.num_chunks_complete= 1 + ,.chunk_lengths= {} + } + +#define NON_ASCII_IN_STATUS_LINE 10 +/* Should handle non-ASCII in status line */ +, {.name= "non-ASCII in status line" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 500 Oriëntatieprobleem\r\n" + "Date: Fri, 5 Nov 2010 23:07:12 GMT+2\r\n" + "Content-Length: 0\r\n" + "Connection: close\r\n" + "\r\n" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 500 + ,.response_status= "Oriëntatieprobleem" + ,.num_headers= 3 + ,.headers= + { { "Date", "Fri, 5 Nov 2010 23:07:12 GMT+2" } + , { "Content-Length", "0" } + , { "Connection", "close" } + } + ,.body= "" + } + +#define HTTP_VERSION_0_9 11 +/* Should handle HTTP/0.9 */ +, {.name= "http version 0.9" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/0.9 200 OK\r\n" + "\r\n" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= TRUE + ,.http_major= 0 + ,.http_minor= 9 + ,.status_code= 200 + ,.response_status= "OK" + ,.num_headers= 0 + ,.headers= + {} + ,.body= "" + } + +#define NO_CONTENT_LENGTH_NO_TRANSFER_ENCODING_RESPONSE 12 +/* The client should wait for the server's EOF. That is, when neither + * content-length nor transfer-encoding is specified, the end of body + * is specified by the EOF. + */ +, {.name= "neither content-length nor transfer-encoding response" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 200 OK\r\n" + "Content-Type: text/plain\r\n" + "\r\n" + "hello world" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= TRUE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 200 + ,.response_status= "OK" + ,.num_headers= 1 + ,.headers= + { { "Content-Type", "text/plain" } + } + ,.body= "hello world" + } + +#define NO_BODY_HTTP10_KA_200 13 +, {.name= "HTTP/1.0 with keep-alive and EOF-terminated 200 status" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.0 200 OK\r\n" + "Connection: keep-alive\r\n" + "\r\n" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= TRUE + ,.http_major= 1 + ,.http_minor= 0 + ,.status_code= 200 + ,.response_status= "OK" + ,.num_headers= 1 + ,.headers= + { { "Connection", "keep-alive" } + } + ,.body_size= 0 + ,.body= "" + } + +#define NO_BODY_HTTP10_KA_204 14 +, {.name= "HTTP/1.0 with keep-alive and a 204 status" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.0 204 No content\r\n" + "Connection: keep-alive\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 0 + ,.status_code= 204 + ,.response_status= "No content" + ,.num_headers= 1 + ,.headers= + { { "Connection", "keep-alive" } + } + ,.body_size= 0 + ,.body= "" + } + +#define NO_BODY_HTTP11_KA_200 15 +, {.name= "HTTP/1.1 with an EOF-terminated 200 status" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 200 OK\r\n" + "\r\n" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= TRUE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 200 + ,.response_status= "OK" + ,.num_headers= 0 + ,.headers={} + ,.body_size= 0 + ,.body= "" + } + +#define NO_BODY_HTTP11_KA_204 16 +, {.name= "HTTP/1.1 with a 204 status" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 204 No content\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 204 + ,.response_status= "No content" + ,.num_headers= 0 + ,.headers={} + ,.body_size= 0 + ,.body= "" + } + +#define NO_BODY_HTTP11_NOKA_204 17 +, {.name= "HTTP/1.1 with a 204 status and keep-alive disabled" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 204 No content\r\n" + "Connection: close\r\n" + "\r\n" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 204 + ,.response_status= "No content" + ,.num_headers= 1 + ,.headers= + { { "Connection", "close" } + } + ,.body_size= 0 + ,.body= "" + } + +#define NO_BODY_HTTP11_KA_CHUNKED_200 18 +, {.name= "HTTP/1.1 with chunked endocing and a 200 response" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 200 OK\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "0\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 200 + ,.response_status= "OK" + ,.num_headers= 1 + ,.headers= + { { "Transfer-Encoding", "chunked" } + } + ,.body_size= 0 + ,.body= "" + ,.num_chunks_complete= 1 + } + +#if !HTTP_PARSER_STRICT +#define SPACE_IN_FIELD_RES 19 +/* Should handle spaces in header fields */ +, {.name= "field space" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 200 OK\r\n" + "Server: Microsoft-IIS/6.0\r\n" + "X-Powered-By: ASP.NET\r\n" + "en-US Content-Type: text/xml\r\n" /* this is the problem */ + "Content-Type: text/xml\r\n" + "Content-Length: 16\r\n" + "Date: Fri, 23 Jul 2010 18:45:38 GMT\r\n" + "Connection: keep-alive\r\n" + "\r\n" + "hello" /* fake body */ + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 200 + ,.response_status= "OK" + ,.num_headers= 7 + ,.headers= + { { "Server", "Microsoft-IIS/6.0" } + , { "X-Powered-By", "ASP.NET" } + , { "en-US Content-Type", "text/xml" } + , { "Content-Type", "text/xml" } + , { "Content-Length", "16" } + , { "Date", "Fri, 23 Jul 2010 18:45:38 GMT" } + , { "Connection", "keep-alive" } + } + ,.body= "hello" + } +#endif /* !HTTP_PARSER_STRICT */ + +#define AMAZON_COM 20 +, {.name= "amazon.com" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 301 MovedPermanently\r\n" + "Date: Wed, 15 May 2013 17:06:33 GMT\r\n" + "Server: Server\r\n" + "x-amz-id-1: 0GPHKXSJQ826RK7GZEB2\r\n" + "p3p: policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \"\r\n" + "x-amz-id-2: STN69VZxIFSz9YJLbz1GDbxpbjG6Qjmmq5E3DxRhOUw+Et0p4hr7c/Q8qNcx4oAD\r\n" + "Location: http://www.amazon.com/Dan-Brown/e/B000AP9DSU/ref=s9_pop_gw_al1?_encoding=UTF8&refinementId=618073011&pf_rd_m=ATVPDKIKX0DER&pf_rd_s=center-2&pf_rd_r=0SHYY5BZXN3KR20BNFAY&pf_rd_t=101&pf_rd_p=1263340922&pf_rd_i=507846\r\n" + "Vary: Accept-Encoding,User-Agent\r\n" + "Content-Type: text/html; charset=ISO-8859-1\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "1\r\n" + "\n\r\n" + "0\r\n" + "\r\n" + ,.should_keep_alive= TRUE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 301 + ,.response_status= "MovedPermanently" + ,.num_headers= 9 + ,.headers= { { "Date", "Wed, 15 May 2013 17:06:33 GMT" } + , { "Server", "Server" } + , { "x-amz-id-1", "0GPHKXSJQ826RK7GZEB2" } + , { "p3p", "policyref=\"http://www.amazon.com/w3c/p3p.xml\",CP=\"CAO DSP LAW CUR ADM IVAo IVDo CONo OTPo OUR DELi PUBi OTRi BUS PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA HEA PRE LOC GOV OTC \"" } + , { "x-amz-id-2", "STN69VZxIFSz9YJLbz1GDbxpbjG6Qjmmq5E3DxRhOUw+Et0p4hr7c/Q8qNcx4oAD" } + , { "Location", "http://www.amazon.com/Dan-Brown/e/B000AP9DSU/ref=s9_pop_gw_al1?_encoding=UTF8&refinementId=618073011&pf_rd_m=ATVPDKIKX0DER&pf_rd_s=center-2&pf_rd_r=0SHYY5BZXN3KR20BNFAY&pf_rd_t=101&pf_rd_p=1263340922&pf_rd_i=507846" } + , { "Vary", "Accept-Encoding,User-Agent" } + , { "Content-Type", "text/html; charset=ISO-8859-1" } + , { "Transfer-Encoding", "chunked" } + } + ,.body= "\n" + ,.num_chunks_complete= 2 + ,.chunk_lengths= { 1 } + } + +#define EMPTY_REASON_PHRASE_AFTER_SPACE 20 +, {.name= "empty reason phrase after space" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 200 \r\n" + "\r\n" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= TRUE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 200 + ,.response_status= "" + ,.num_headers= 0 + ,.headers= {} + ,.body= "" + } + +, {.name= NULL } /* sentinel */ +}; + +/* strnlen() is a POSIX.2008 addition. Can't rely on it being available so + * define it ourselves. + */ +size_t +strnlen(const char *s, size_t maxlen) +{ + const char *p; + + p = memchr(s, '\0', maxlen); + if (p == NULL) + return maxlen; + + return p - s; +} + +size_t +strlncat(char *dst, size_t len, const char *src, size_t n) +{ + size_t slen; + size_t dlen; + size_t rlen; + size_t ncpy; + + slen = strnlen(src, n); + dlen = strnlen(dst, len); + + if (dlen < len) { + rlen = len - dlen; + ncpy = slen < rlen ? slen : (rlen - 1); + memcpy(dst + dlen, src, ncpy); + dst[dlen + ncpy] = '\0'; + } + + assert(len > slen + dlen); + return slen + dlen; +} + +size_t +strlcat(char *dst, const char *src, size_t len) +{ + return strlncat(dst, len, src, (size_t) -1); +} + +size_t +strlncpy(char *dst, size_t len, const char *src, size_t n) +{ + size_t slen; + size_t ncpy; + + slen = strnlen(src, n); + + if (len > 0) { + ncpy = slen < len ? slen : (len - 1); + memcpy(dst, src, ncpy); + dst[ncpy] = '\0'; + } + + assert(len > slen); + return slen; +} + +size_t +strlcpy(char *dst, const char *src, size_t len) +{ + return strlncpy(dst, len, src, (size_t) -1); +} + +int +request_url_cb (http_parser *p, const char *buf, size_t len) +{ + assert(p == parser); + strlncat(messages[num_messages].request_url, + sizeof(messages[num_messages].request_url), + buf, + len); + return 0; +} + +int +header_field_cb (http_parser *p, const char *buf, size_t len) +{ + assert(p == parser); + struct message *m = &messages[num_messages]; + + if (m->last_header_element != FIELD) + m->num_headers++; + + strlncat(m->headers[m->num_headers-1][0], + sizeof(m->headers[m->num_headers-1][0]), + buf, + len); + + m->last_header_element = FIELD; + + return 0; +} + +int +header_value_cb (http_parser *p, const char *buf, size_t len) +{ + assert(p == parser); + struct message *m = &messages[num_messages]; + + strlncat(m->headers[m->num_headers-1][1], + sizeof(m->headers[m->num_headers-1][1]), + buf, + len); + + m->last_header_element = VALUE; + + return 0; +} + +void +check_body_is_final (const http_parser *p) +{ + if (messages[num_messages].body_is_final) { + fprintf(stderr, "\n\n *** Error http_body_is_final() should return 1 " + "on last on_body callback call " + "but it doesn't! ***\n\n"); + assert(0); + abort(); + } + messages[num_messages].body_is_final = http_body_is_final(p); +} + +int +body_cb (http_parser *p, const char *buf, size_t len) +{ + assert(p == parser); + strlncat(messages[num_messages].body, + sizeof(messages[num_messages].body), + buf, + len); + messages[num_messages].body_size += len; + check_body_is_final(p); + // printf("body_cb: '%s'\n", requests[num_messages].body); + return 0; +} + +int +count_body_cb (http_parser *p, const char *buf, size_t len) +{ + assert(p == parser); + assert(buf); + messages[num_messages].body_size += len; + check_body_is_final(p); + return 0; +} + +int +message_begin_cb (http_parser *p) +{ + assert(p == parser); + messages[num_messages].message_begin_cb_called = TRUE; + return 0; +} + +int +headers_complete_cb (http_parser *p) +{ + assert(p == parser); + messages[num_messages].method = parser->method; + messages[num_messages].status_code = parser->status_code; + messages[num_messages].http_major = parser->http_major; + messages[num_messages].http_minor = parser->http_minor; + messages[num_messages].headers_complete_cb_called = TRUE; + messages[num_messages].should_keep_alive = http_should_keep_alive(parser); + return 0; +} + +int +message_complete_cb (http_parser *p) +{ + assert(p == parser); + if (messages[num_messages].should_keep_alive != http_should_keep_alive(parser)) + { + fprintf(stderr, "\n\n *** Error http_should_keep_alive() should have same " + "value in both on_message_complete and on_headers_complete " + "but it doesn't! ***\n\n"); + assert(0); + abort(); + } + + if (messages[num_messages].body_size && + http_body_is_final(p) && + !messages[num_messages].body_is_final) + { + fprintf(stderr, "\n\n *** Error http_body_is_final() should return 1 " + "on last on_body callback call " + "but it doesn't! ***\n\n"); + assert(0); + abort(); + } + + messages[num_messages].message_complete_cb_called = TRUE; + + messages[num_messages].message_complete_on_eof = currently_parsing_eof; + + num_messages++; + return 0; +} + +int +response_status_cb (http_parser *p, const char *buf, size_t len) +{ + assert(p == parser); + strlncat(messages[num_messages].response_status, + sizeof(messages[num_messages].response_status), + buf, + len); + return 0; +} + +int +chunk_header_cb (http_parser *p) +{ + assert(p == parser); + int chunk_idx = messages[num_messages].num_chunks; + messages[num_messages].num_chunks++; + if (chunk_idx < MAX_CHUNKS) { + messages[num_messages].chunk_lengths[chunk_idx] = p->content_length; + } + + return 0; +} + +int +chunk_complete_cb (http_parser *p) +{ + assert(p == parser); + + /* Here we want to verify that each chunk_header_cb is matched by a + * chunk_complete_cb, so not only should the total number of calls to + * both callbacks be the same, but they also should be interleaved + * properly */ + assert(messages[num_messages].num_chunks == + messages[num_messages].num_chunks_complete + 1); + + messages[num_messages].num_chunks_complete++; + return 0; +} + +/* These dontcall_* callbacks exist so that we can verify that when we're + * paused, no additional callbacks are invoked */ +int +dontcall_message_begin_cb (http_parser *p) +{ + if (p) { } // gcc + fprintf(stderr, "\n\n*** on_message_begin() called on paused parser ***\n\n"); + abort(); +} + +int +dontcall_header_field_cb (http_parser *p, const char *buf, size_t len) +{ + if (p || buf || len) { } // gcc + fprintf(stderr, "\n\n*** on_header_field() called on paused parser ***\n\n"); + abort(); +} + +int +dontcall_header_value_cb (http_parser *p, const char *buf, size_t len) +{ + if (p || buf || len) { } // gcc + fprintf(stderr, "\n\n*** on_header_value() called on paused parser ***\n\n"); + abort(); +} + +int +dontcall_request_url_cb (http_parser *p, const char *buf, size_t len) +{ + if (p || buf || len) { } // gcc + fprintf(stderr, "\n\n*** on_request_url() called on paused parser ***\n\n"); + abort(); +} + +int +dontcall_body_cb (http_parser *p, const char *buf, size_t len) +{ + if (p || buf || len) { } // gcc + fprintf(stderr, "\n\n*** on_body_cb() called on paused parser ***\n\n"); + abort(); +} + +int +dontcall_headers_complete_cb (http_parser *p) +{ + if (p) { } // gcc + fprintf(stderr, "\n\n*** on_headers_complete() called on paused " + "parser ***\n\n"); + abort(); +} + +int +dontcall_message_complete_cb (http_parser *p) +{ + if (p) { } // gcc + fprintf(stderr, "\n\n*** on_message_complete() called on paused " + "parser ***\n\n"); + abort(); +} + +int +dontcall_response_status_cb (http_parser *p, const char *buf, size_t len) +{ + if (p || buf || len) { } // gcc + fprintf(stderr, "\n\n*** on_status() called on paused parser ***\n\n"); + abort(); +} + +int +dontcall_chunk_header_cb (http_parser *p) +{ + if (p) { } // gcc + fprintf(stderr, "\n\n*** on_chunk_header() called on paused parser ***\n\n"); + exit(1); +} + +int +dontcall_chunk_complete_cb (http_parser *p) +{ + if (p) { } // gcc + fprintf(stderr, "\n\n*** on_chunk_complete() " + "called on paused parser ***\n\n"); + exit(1); +} + +static http_parser_settings settings_dontcall = + {.on_message_begin = dontcall_message_begin_cb + ,.on_header_field = dontcall_header_field_cb + ,.on_header_value = dontcall_header_value_cb + ,.on_url = dontcall_request_url_cb + ,.on_status = dontcall_response_status_cb + ,.on_body = dontcall_body_cb + ,.on_headers_complete = dontcall_headers_complete_cb + ,.on_message_complete = dontcall_message_complete_cb + ,.on_chunk_header = dontcall_chunk_header_cb + ,.on_chunk_complete = dontcall_chunk_complete_cb + }; + +/* These pause_* callbacks always pause the parser and just invoke the regular + * callback that tracks content. Before returning, we overwrite the parser + * settings to point to the _dontcall variety so that we can verify that + * the pause actually did, you know, pause. */ +int +pause_message_begin_cb (http_parser *p) +{ + http_parser_pause(p, 1); + *current_pause_parser = settings_dontcall; + return message_begin_cb(p); +} + +int +pause_header_field_cb (http_parser *p, const char *buf, size_t len) +{ + http_parser_pause(p, 1); + *current_pause_parser = settings_dontcall; + return header_field_cb(p, buf, len); +} + +int +pause_header_value_cb (http_parser *p, const char *buf, size_t len) +{ + http_parser_pause(p, 1); + *current_pause_parser = settings_dontcall; + return header_value_cb(p, buf, len); +} + +int +pause_request_url_cb (http_parser *p, const char *buf, size_t len) +{ + http_parser_pause(p, 1); + *current_pause_parser = settings_dontcall; + return request_url_cb(p, buf, len); +} + +int +pause_body_cb (http_parser *p, const char *buf, size_t len) +{ + http_parser_pause(p, 1); + *current_pause_parser = settings_dontcall; + return body_cb(p, buf, len); +} + +int +pause_headers_complete_cb (http_parser *p) +{ + http_parser_pause(p, 1); + *current_pause_parser = settings_dontcall; + return headers_complete_cb(p); +} + +int +pause_message_complete_cb (http_parser *p) +{ + http_parser_pause(p, 1); + *current_pause_parser = settings_dontcall; + return message_complete_cb(p); +} + +int +pause_response_status_cb (http_parser *p, const char *buf, size_t len) +{ + http_parser_pause(p, 1); + *current_pause_parser = settings_dontcall; + return response_status_cb(p, buf, len); +} + +int +pause_chunk_header_cb (http_parser *p) +{ + http_parser_pause(p, 1); + *current_pause_parser = settings_dontcall; + return chunk_header_cb(p); +} + +int +pause_chunk_complete_cb (http_parser *p) +{ + http_parser_pause(p, 1); + *current_pause_parser = settings_dontcall; + return chunk_complete_cb(p); +} + +static http_parser_settings settings_pause = + {.on_message_begin = pause_message_begin_cb + ,.on_header_field = pause_header_field_cb + ,.on_header_value = pause_header_value_cb + ,.on_url = pause_request_url_cb + ,.on_status = pause_response_status_cb + ,.on_body = pause_body_cb + ,.on_headers_complete = pause_headers_complete_cb + ,.on_message_complete = pause_message_complete_cb + ,.on_chunk_header = pause_chunk_header_cb + ,.on_chunk_complete = pause_chunk_complete_cb + }; + +static http_parser_settings settings = + {.on_message_begin = message_begin_cb + ,.on_header_field = header_field_cb + ,.on_header_value = header_value_cb + ,.on_url = request_url_cb + ,.on_status = response_status_cb + ,.on_body = body_cb + ,.on_headers_complete = headers_complete_cb + ,.on_message_complete = message_complete_cb + ,.on_chunk_header = chunk_header_cb + ,.on_chunk_complete = chunk_complete_cb + }; + +static http_parser_settings settings_count_body = + {.on_message_begin = message_begin_cb + ,.on_header_field = header_field_cb + ,.on_header_value = header_value_cb + ,.on_url = request_url_cb + ,.on_status = response_status_cb + ,.on_body = count_body_cb + ,.on_headers_complete = headers_complete_cb + ,.on_message_complete = message_complete_cb + ,.on_chunk_header = chunk_header_cb + ,.on_chunk_complete = chunk_complete_cb + }; + +static http_parser_settings settings_null = + {.on_message_begin = 0 + ,.on_header_field = 0 + ,.on_header_value = 0 + ,.on_url = 0 + ,.on_status = 0 + ,.on_body = 0 + ,.on_headers_complete = 0 + ,.on_message_complete = 0 + ,.on_chunk_header = 0 + ,.on_chunk_complete = 0 + }; + +void +parser_init (enum http_parser_type type) +{ + num_messages = 0; + + assert(parser == NULL); + + parser = malloc(sizeof(http_parser)); + + http_parser_init(parser, type); + + memset(&messages, 0, sizeof messages); + +} + +void +parser_free () +{ + assert(parser); + free(parser); + parser = NULL; +} + +size_t parse (const char *buf, size_t len) +{ + size_t nparsed; + currently_parsing_eof = (len == 0); + nparsed = http_parser_execute(parser, &settings, buf, len); + return nparsed; +} + +size_t parse_count_body (const char *buf, size_t len) +{ + size_t nparsed; + currently_parsing_eof = (len == 0); + nparsed = http_parser_execute(parser, &settings_count_body, buf, len); + return nparsed; +} + +size_t parse_pause (const char *buf, size_t len) +{ + size_t nparsed; + http_parser_settings s = settings_pause; + + currently_parsing_eof = (len == 0); + current_pause_parser = &s; + nparsed = http_parser_execute(parser, current_pause_parser, buf, len); + return nparsed; +} + +static inline int +check_str_eq (const struct message *m, + const char *prop, + const char *expected, + const char *found) { + if ((expected == NULL) != (found == NULL)) { + printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name); + printf("expected %s\n", (expected == NULL) ? "NULL" : expected); + printf(" found %s\n", (found == NULL) ? "NULL" : found); + return 0; + } + if (expected != NULL && 0 != strcmp(expected, found)) { + printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name); + printf("expected '%s'\n", expected); + printf(" found '%s'\n", found); + return 0; + } + return 1; +} + +static inline int +check_num_eq (const struct message *m, + const char *prop, + int expected, + int found) { + if (expected != found) { + printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name); + printf("expected %d\n", expected); + printf(" found %d\n", found); + return 0; + } + return 1; +} + +#define MESSAGE_CHECK_STR_EQ(expected, found, prop) \ + if (!check_str_eq(expected, #prop, expected->prop, found->prop)) return 0 + +#define MESSAGE_CHECK_NUM_EQ(expected, found, prop) \ + if (!check_num_eq(expected, #prop, expected->prop, found->prop)) return 0 + +#define MESSAGE_CHECK_URL_EQ(u, expected, found, prop, fn) \ +do { \ + char ubuf[256]; \ + \ + if ((u)->field_set & (1 << (fn))) { \ + memcpy(ubuf, (found)->request_url + (u)->field_data[(fn)].off, \ + (u)->field_data[(fn)].len); \ + ubuf[(u)->field_data[(fn)].len] = '\0'; \ + } else { \ + ubuf[0] = '\0'; \ + } \ + \ + check_str_eq(expected, #prop, expected->prop, ubuf); \ +} while(0) + +int +message_eq (int index, const struct message *expected) +{ + int i; + struct message *m = &messages[index]; + + MESSAGE_CHECK_NUM_EQ(expected, m, http_major); + MESSAGE_CHECK_NUM_EQ(expected, m, http_minor); + + if (expected->type == HTTP_REQUEST) { + MESSAGE_CHECK_NUM_EQ(expected, m, method); + } else { + MESSAGE_CHECK_NUM_EQ(expected, m, status_code); + MESSAGE_CHECK_STR_EQ(expected, m, response_status); + } + + MESSAGE_CHECK_NUM_EQ(expected, m, should_keep_alive); + MESSAGE_CHECK_NUM_EQ(expected, m, message_complete_on_eof); + + assert(m->message_begin_cb_called); + assert(m->headers_complete_cb_called); + assert(m->message_complete_cb_called); + + + MESSAGE_CHECK_STR_EQ(expected, m, request_url); + + /* Check URL components; we can't do this w/ CONNECT since it doesn't + * send us a well-formed URL. + */ + if (*m->request_url && m->method != HTTP_CONNECT) { + struct http_parser_url u; + + if (http_parser_parse_url(m->request_url, strlen(m->request_url), 0, &u)) { + fprintf(stderr, "\n\n*** failed to parse URL %s ***\n\n", + m->request_url); + abort(); + } + + if (expected->host) { + MESSAGE_CHECK_URL_EQ(&u, expected, m, host, UF_HOST); + } + + if (expected->userinfo) { + MESSAGE_CHECK_URL_EQ(&u, expected, m, userinfo, UF_USERINFO); + } + + m->port = (u.field_set & (1 << UF_PORT)) ? + u.port : 0; + + MESSAGE_CHECK_URL_EQ(&u, expected, m, query_string, UF_QUERY); + MESSAGE_CHECK_URL_EQ(&u, expected, m, fragment, UF_FRAGMENT); + MESSAGE_CHECK_URL_EQ(&u, expected, m, request_path, UF_PATH); + MESSAGE_CHECK_NUM_EQ(expected, m, port); + } + + if (expected->body_size) { + MESSAGE_CHECK_NUM_EQ(expected, m, body_size); + } else { + MESSAGE_CHECK_STR_EQ(expected, m, body); + } + + assert(m->num_chunks == m->num_chunks_complete); + MESSAGE_CHECK_NUM_EQ(expected, m, num_chunks_complete); + for (i = 0; i < m->num_chunks && i < MAX_CHUNKS; i++) { + MESSAGE_CHECK_NUM_EQ(expected, m, chunk_lengths[i]); + } + + MESSAGE_CHECK_NUM_EQ(expected, m, num_headers); + + int r; + for (i = 0; i < m->num_headers; i++) { + r = check_str_eq(expected, "header field", expected->headers[i][0], m->headers[i][0]); + if (!r) return 0; + r = check_str_eq(expected, "header value", expected->headers[i][1], m->headers[i][1]); + if (!r) return 0; + } + + MESSAGE_CHECK_STR_EQ(expected, m, upgrade); + + return 1; +} + +/* Given a sequence of varargs messages, return the number of them that the + * parser should successfully parse, taking into account that upgraded + * messages prevent all subsequent messages from being parsed. + */ +size_t +count_parsed_messages(const size_t nmsgs, ...) { + size_t i; + va_list ap; + + va_start(ap, nmsgs); + + for (i = 0; i < nmsgs; i++) { + struct message *m = va_arg(ap, struct message *); + + if (m->upgrade) { + va_end(ap); + return i + 1; + } + } + + va_end(ap); + return nmsgs; +} + +/* Given a sequence of bytes and the number of these that we were able to + * parse, verify that upgrade bodies are correct. + */ +void +upgrade_message_fix(char *body, const size_t nread, const size_t nmsgs, ...) { + va_list ap; + size_t i; + size_t off = 0; + + va_start(ap, nmsgs); + + for (i = 0; i < nmsgs; i++) { + struct message *m = va_arg(ap, struct message *); + + off += strlen(m->raw); + + if (m->upgrade) { + off -= strlen(m->upgrade); + + /* Check the portion of the response after its specified upgrade */ + if (!check_str_eq(m, "upgrade", body + off, body + nread)) { + abort(); + } + + /* Fix up the response so that message_eq() will verify the beginning + * of the upgrade */ + *(body + nread + strlen(m->upgrade)) = '\0'; + messages[num_messages -1 ].upgrade = body + nread; + + va_end(ap); + return; + } + } + + va_end(ap); + printf("\n\n*** Error: expected a message with upgrade ***\n"); + + abort(); +} + +static void +print_error (const char *raw, size_t error_location) +{ + fprintf(stderr, "\n*** %s ***\n\n", + http_errno_description(HTTP_PARSER_ERRNO(parser))); + + int this_line = 0, char_len = 0; + size_t i, j, len = strlen(raw), error_location_line = 0; + for (i = 0; i < len; i++) { + if (i == error_location) this_line = 1; + switch (raw[i]) { + case '\r': + char_len = 2; + fprintf(stderr, "\\r"); + break; + + case '\n': + fprintf(stderr, "\\n\n"); + + if (this_line) goto print; + + error_location_line = 0; + continue; + + default: + char_len = 1; + fputc(raw[i], stderr); + break; + } + if (!this_line) error_location_line += char_len; + } + + fprintf(stderr, "[eof]\n"); + + print: + for (j = 0; j < error_location_line; j++) { + fputc(' ', stderr); + } + fprintf(stderr, "^\n\nerror location: %u\n", (unsigned int)error_location); +} + +void +test_preserve_data (void) +{ + char my_data[] = "application-specific data"; + http_parser parser; + parser.data = my_data; + http_parser_init(&parser, HTTP_REQUEST); + if (parser.data != my_data) { + printf("\n*** parser.data not preserved accross http_parser_init ***\n\n"); + abort(); + } +} + +struct url_test { + const char *name; + const char *url; + int is_connect; + struct http_parser_url u; + int rv; +}; + +const struct url_test url_tests[] = +{ {.name="proxy request" + ,.url="http://hostname/" + ,.is_connect=0 + ,.u= + {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH) + ,.port=0 + ,.field_data= + {{ 0, 4 } /* UF_SCHEMA */ + ,{ 7, 8 } /* UF_HOST */ + ,{ 0, 0 } /* UF_PORT */ + ,{ 15, 1 } /* UF_PATH */ + ,{ 0, 0 } /* UF_QUERY */ + ,{ 0, 0 } /* UF_FRAGMENT */ + ,{ 0, 0 } /* UF_USERINFO */ + } + } + ,.rv=0 + } + +, {.name="proxy request with port" + ,.url="http://hostname:444/" + ,.is_connect=0 + ,.u= + {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PORT) | (1 << UF_PATH) + ,.port=444 + ,.field_data= + {{ 0, 4 } /* UF_SCHEMA */ + ,{ 7, 8 } /* UF_HOST */ + ,{ 16, 3 } /* UF_PORT */ + ,{ 19, 1 } /* UF_PATH */ + ,{ 0, 0 } /* UF_QUERY */ + ,{ 0, 0 } /* UF_FRAGMENT */ + ,{ 0, 0 } /* UF_USERINFO */ + } + } + ,.rv=0 + } + +, {.name="CONNECT request" + ,.url="hostname:443" + ,.is_connect=1 + ,.u= + {.field_set=(1 << UF_HOST) | (1 << UF_PORT) + ,.port=443 + ,.field_data= + {{ 0, 0 } /* UF_SCHEMA */ + ,{ 0, 8 } /* UF_HOST */ + ,{ 9, 3 } /* UF_PORT */ + ,{ 0, 0 } /* UF_PATH */ + ,{ 0, 0 } /* UF_QUERY */ + ,{ 0, 0 } /* UF_FRAGMENT */ + ,{ 0, 0 } /* UF_USERINFO */ + } + } + ,.rv=0 + } + +, {.name="CONNECT request but not connect" + ,.url="hostname:443" + ,.is_connect=0 + ,.rv=1 + } + +, {.name="proxy ipv6 request" + ,.url="http://[1:2::3:4]/" + ,.is_connect=0 + ,.u= + {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH) + ,.port=0 + ,.field_data= + {{ 0, 4 } /* UF_SCHEMA */ + ,{ 8, 8 } /* UF_HOST */ + ,{ 0, 0 } /* UF_PORT */ + ,{ 17, 1 } /* UF_PATH */ + ,{ 0, 0 } /* UF_QUERY */ + ,{ 0, 0 } /* UF_FRAGMENT */ + ,{ 0, 0 } /* UF_USERINFO */ + } + } + ,.rv=0 + } + +, {.name="proxy ipv6 request with port" + ,.url="http://[1:2::3:4]:67/" + ,.is_connect=0 + ,.u= + {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PORT) | (1 << UF_PATH) + ,.port=67 + ,.field_data= + {{ 0, 4 } /* UF_SCHEMA */ + ,{ 8, 8 } /* UF_HOST */ + ,{ 18, 2 } /* UF_PORT */ + ,{ 20, 1 } /* UF_PATH */ + ,{ 0, 0 } /* UF_QUERY */ + ,{ 0, 0 } /* UF_FRAGMENT */ + ,{ 0, 0 } /* UF_USERINFO */ + } + } + ,.rv=0 + } + +, {.name="CONNECT ipv6 address" + ,.url="[1:2::3:4]:443" + ,.is_connect=1 + ,.u= + {.field_set=(1 << UF_HOST) | (1 << UF_PORT) + ,.port=443 + ,.field_data= + {{ 0, 0 } /* UF_SCHEMA */ + ,{ 1, 8 } /* UF_HOST */ + ,{ 11, 3 } /* UF_PORT */ + ,{ 0, 0 } /* UF_PATH */ + ,{ 0, 0 } /* UF_QUERY */ + ,{ 0, 0 } /* UF_FRAGMENT */ + ,{ 0, 0 } /* UF_USERINFO */ + } + } + ,.rv=0 + } + +, {.name="ipv4 in ipv6 address" + ,.url="http://[2001:0000:0000:0000:0000:0000:1.9.1.1]/" + ,.is_connect=0 + ,.u= + {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH) + ,.port=0 + ,.field_data= + {{ 0, 4 } /* UF_SCHEMA */ + ,{ 8, 37 } /* UF_HOST */ + ,{ 0, 0 } /* UF_PORT */ + ,{ 46, 1 } /* UF_PATH */ + ,{ 0, 0 } /* UF_QUERY */ + ,{ 0, 0 } /* UF_FRAGMENT */ + ,{ 0, 0 } /* UF_USERINFO */ + } + } + ,.rv=0 + } + +, {.name="extra ? in query string" + ,.url="http://a.tbcdn.cn/p/fp/2010c/??fp-header-min.css,fp-base-min.css," + "fp-channel-min.css,fp-product-min.css,fp-mall-min.css,fp-category-min.css," + "fp-sub-min.css,fp-gdp4p-min.css,fp-css3-min.css,fp-misc-min.css?t=20101022.css" + ,.is_connect=0 + ,.u= + {.field_set=(1<field_set, u->port); + for (i = 0; i < UF_MAX; i++) { + if ((u->field_set & (1 << i)) == 0) { + printf("\tfield_data[%u]: unset\n", i); + continue; + } + + printf("\tfield_data[%u]: off: %u len: %u part: \"%.*s\n\"", + i, + u->field_data[i].off, + u->field_data[i].len, + u->field_data[i].len, + url + u->field_data[i].off); + } +} + +void +test_parse_url (void) +{ + struct http_parser_url u; + const struct url_test *test; + unsigned int i; + int rv; + + for (i = 0; i < (sizeof(url_tests) / sizeof(url_tests[0])); i++) { + test = &url_tests[i]; + memset(&u, 0, sizeof(u)); + + rv = http_parser_parse_url(test->url, + strlen(test->url), + test->is_connect, + &u); + + if (test->rv == 0) { + if (rv != 0) { + printf("\n*** http_parser_parse_url(\"%s\") \"%s\" test failed, " + "unexpected rv %d ***\n\n", test->url, test->name, rv); + abort(); + } + + if (memcmp(&u, &test->u, sizeof(u)) != 0) { + printf("\n*** http_parser_parse_url(\"%s\") \"%s\" failed ***\n", + test->url, test->name); + + printf("target http_parser_url:\n"); + dump_url(test->url, &test->u); + printf("result http_parser_url:\n"); + dump_url(test->url, &u); + + abort(); + } + } else { + /* test->rv != 0 */ + if (rv == 0) { + printf("\n*** http_parser_parse_url(\"%s\") \"%s\" test failed, " + "unexpected rv %d ***\n\n", test->url, test->name, rv); + abort(); + } + } + } +} + +void +test_method_str (void) +{ + assert(0 == strcmp("GET", http_method_str(HTTP_GET))); + assert(0 == strcmp("", http_method_str(1337))); +} + +void +test_message (const struct message *message) +{ + size_t raw_len = strlen(message->raw); + size_t msg1len; + for (msg1len = 0; msg1len < raw_len; msg1len++) { + parser_init(message->type); + + size_t read; + const char *msg1 = message->raw; + const char *msg2 = msg1 + msg1len; + size_t msg2len = raw_len - msg1len; + + if (msg1len) { + read = parse(msg1, msg1len); + + if (message->upgrade && parser->upgrade && num_messages > 0) { + messages[num_messages - 1].upgrade = msg1 + read; + goto test; + } + + if (read != msg1len) { + print_error(msg1, read); + abort(); + } + } + + + read = parse(msg2, msg2len); + + if (message->upgrade && parser->upgrade) { + messages[num_messages - 1].upgrade = msg2 + read; + goto test; + } + + if (read != msg2len) { + print_error(msg2, read); + abort(); + } + + read = parse(NULL, 0); + + if (read != 0) { + print_error(message->raw, read); + abort(); + } + + test: + + if (num_messages != 1) { + printf("\n*** num_messages != 1 after testing '%s' ***\n\n", message->name); + abort(); + } + + if(!message_eq(0, message)) abort(); + + parser_free(); + } +} + +void +test_message_count_body (const struct message *message) +{ + parser_init(message->type); + + size_t read; + size_t l = strlen(message->raw); + size_t i, toread; + size_t chunk = 4024; + + for (i = 0; i < l; i+= chunk) { + toread = MIN(l-i, chunk); + read = parse_count_body(message->raw + i, toread); + if (read != toread) { + print_error(message->raw, read); + abort(); + } + } + + + read = parse_count_body(NULL, 0); + if (read != 0) { + print_error(message->raw, read); + abort(); + } + + if (num_messages != 1) { + printf("\n*** num_messages != 1 after testing '%s' ***\n\n", message->name); + abort(); + } + + if(!message_eq(0, message)) abort(); + + parser_free(); +} + +void +test_simple (const char *buf, enum http_errno err_expected) +{ + parser_init(HTTP_REQUEST); + + enum http_errno err; + + parse(buf, strlen(buf)); + err = HTTP_PARSER_ERRNO(parser); + parse(NULL, 0); + + parser_free(); + + /* In strict mode, allow us to pass with an unexpected HPE_STRICT as + * long as the caller isn't expecting success. + */ +#if HTTP_PARSER_STRICT + if (err_expected != err && err_expected != HPE_OK && err != HPE_STRICT) { +#else + if (err_expected != err) { +#endif + fprintf(stderr, "\n*** test_simple expected %s, but saw %s ***\n\n%s\n", + http_errno_name(err_expected), http_errno_name(err), buf); + abort(); + } +} + +void +test_header_overflow_error (int req) +{ + http_parser parser; + http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE); + size_t parsed; + const char *buf; + buf = req ? "GET / HTTP/1.1\r\n" : "HTTP/1.0 200 OK\r\n"; + parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf)); + assert(parsed == strlen(buf)); + + buf = "header-key: header-value\r\n"; + size_t buflen = strlen(buf); + + int i; + for (i = 0; i < 10000; i++) { + parsed = http_parser_execute(&parser, &settings_null, buf, buflen); + if (parsed != buflen) { + //fprintf(stderr, "error found on iter %d\n", i); + assert(HTTP_PARSER_ERRNO(&parser) == HPE_HEADER_OVERFLOW); + return; + } + } + + fprintf(stderr, "\n*** Error expected but none in header overflow test ***\n"); + abort(); +} + + +void +test_header_nread_value () +{ + http_parser parser; + http_parser_init(&parser, HTTP_REQUEST); + size_t parsed; + const char *buf; + buf = "GET / HTTP/1.1\r\nheader: value\nhdr: value\r\n"; + parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf)); + assert(parsed == strlen(buf)); + + assert(parser.nread == strlen(buf)); +} + + +static void +test_content_length_overflow (const char *buf, size_t buflen, int expect_ok) +{ + http_parser parser; + http_parser_init(&parser, HTTP_RESPONSE); + http_parser_execute(&parser, &settings_null, buf, buflen); + + if (expect_ok) + assert(HTTP_PARSER_ERRNO(&parser) == HPE_OK); + else + assert(HTTP_PARSER_ERRNO(&parser) == HPE_INVALID_CONTENT_LENGTH); +} + +void +test_header_content_length_overflow_error (void) +{ +#define X(size) \ + "HTTP/1.1 200 OK\r\n" \ + "Content-Length: " #size "\r\n" \ + "\r\n" + const char a[] = X(1844674407370955160); /* 2^64 / 10 - 1 */ + const char b[] = X(18446744073709551615); /* 2^64-1 */ + const char c[] = X(18446744073709551616); /* 2^64 */ +#undef X + test_content_length_overflow(a, sizeof(a) - 1, 1); /* expect ok */ + test_content_length_overflow(b, sizeof(b) - 1, 0); /* expect failure */ + test_content_length_overflow(c, sizeof(c) - 1, 0); /* expect failure */ +} + +void +test_chunk_content_length_overflow_error (void) +{ +#define X(size) \ + "HTTP/1.1 200 OK\r\n" \ + "Transfer-Encoding: chunked\r\n" \ + "\r\n" \ + #size "\r\n" \ + "..." + const char a[] = X(FFFFFFFFFFFFFFE); /* 2^64 / 16 - 1 */ + const char b[] = X(FFFFFFFFFFFFFFFF); /* 2^64-1 */ + const char c[] = X(10000000000000000); /* 2^64 */ +#undef X + test_content_length_overflow(a, sizeof(a) - 1, 1); /* expect ok */ + test_content_length_overflow(b, sizeof(b) - 1, 0); /* expect failure */ + test_content_length_overflow(c, sizeof(c) - 1, 0); /* expect failure */ +} + +void +test_no_overflow_long_body (int req, size_t length) +{ + http_parser parser; + http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE); + size_t parsed; + size_t i; + char buf1[3000]; + size_t buf1len = sprintf(buf1, "%s\r\nConnection: Keep-Alive\r\nContent-Length: %lu\r\n\r\n", + req ? "POST / HTTP/1.0" : "HTTP/1.0 200 OK", (unsigned long)length); + parsed = http_parser_execute(&parser, &settings_null, buf1, buf1len); + if (parsed != buf1len) + goto err; + + for (i = 0; i < length; i++) { + char foo = 'a'; + parsed = http_parser_execute(&parser, &settings_null, &foo, 1); + if (parsed != 1) + goto err; + } + + parsed = http_parser_execute(&parser, &settings_null, buf1, buf1len); + if (parsed != buf1len) goto err; + return; + + err: + fprintf(stderr, + "\n*** error in test_no_overflow_long_body %s of length %lu ***\n", + req ? "REQUEST" : "RESPONSE", + (unsigned long)length); + abort(); +} + +void +test_multiple3 (const struct message *r1, const struct message *r2, const struct message *r3) +{ + int message_count = count_parsed_messages(3, r1, r2, r3); + + char total[ strlen(r1->raw) + + strlen(r2->raw) + + strlen(r3->raw) + + 1 + ]; + total[0] = '\0'; + + strcat(total, r1->raw); + strcat(total, r2->raw); + strcat(total, r3->raw); + + parser_init(r1->type); + + size_t read; + + read = parse(total, strlen(total)); + + if (parser->upgrade) { + upgrade_message_fix(total, read, 3, r1, r2, r3); + goto test; + } + + if (read != strlen(total)) { + print_error(total, read); + abort(); + } + + read = parse(NULL, 0); + + if (read != 0) { + print_error(total, read); + abort(); + } + +test: + + if (message_count != num_messages) { + fprintf(stderr, "\n\n*** Parser didn't see 3 messages only %d *** \n", num_messages); + abort(); + } + + if (!message_eq(0, r1)) abort(); + if (message_count > 1 && !message_eq(1, r2)) abort(); + if (message_count > 2 && !message_eq(2, r3)) abort(); + + parser_free(); +} + +/* SCAN through every possible breaking to make sure the + * parser can handle getting the content in any chunks that + * might come from the socket + */ +void +test_scan (const struct message *r1, const struct message *r2, const struct message *r3) +{ + char total[80*1024] = "\0"; + char buf1[80*1024] = "\0"; + char buf2[80*1024] = "\0"; + char buf3[80*1024] = "\0"; + + strcat(total, r1->raw); + strcat(total, r2->raw); + strcat(total, r3->raw); + + size_t read; + + int total_len = strlen(total); + + int total_ops = 2 * (total_len - 1) * (total_len - 2) / 2; + int ops = 0 ; + + size_t buf1_len, buf2_len, buf3_len; + int message_count = count_parsed_messages(3, r1, r2, r3); + + int i,j,type_both; + for (type_both = 0; type_both < 2; type_both ++ ) { + for (j = 2; j < total_len; j ++ ) { + for (i = 1; i < j; i ++ ) { + + if (ops % 1000 == 0) { + printf("\b\b\b\b%3.0f%%", 100 * (float)ops /(float)total_ops); + fflush(stdout); + } + ops += 1; + + parser_init(type_both ? HTTP_BOTH : r1->type); + + buf1_len = i; + strlncpy(buf1, sizeof(buf1), total, buf1_len); + buf1[buf1_len] = 0; + + buf2_len = j - i; + strlncpy(buf2, sizeof(buf1), total+i, buf2_len); + buf2[buf2_len] = 0; + + buf3_len = total_len - j; + strlncpy(buf3, sizeof(buf1), total+j, buf3_len); + buf3[buf3_len] = 0; + + read = parse(buf1, buf1_len); + + if (parser->upgrade) goto test; + + if (read != buf1_len) { + print_error(buf1, read); + goto error; + } + + read += parse(buf2, buf2_len); + + if (parser->upgrade) goto test; + + if (read != buf1_len + buf2_len) { + print_error(buf2, read); + goto error; + } + + read += parse(buf3, buf3_len); + + if (parser->upgrade) goto test; + + if (read != buf1_len + buf2_len + buf3_len) { + print_error(buf3, read); + goto error; + } + + parse(NULL, 0); + +test: + if (parser->upgrade) { + upgrade_message_fix(total, read, 3, r1, r2, r3); + } + + if (message_count != num_messages) { + fprintf(stderr, "\n\nParser didn't see %d messages only %d\n", + message_count, num_messages); + goto error; + } + + if (!message_eq(0, r1)) { + fprintf(stderr, "\n\nError matching messages[0] in test_scan.\n"); + goto error; + } + + if (message_count > 1 && !message_eq(1, r2)) { + fprintf(stderr, "\n\nError matching messages[1] in test_scan.\n"); + goto error; + } + + if (message_count > 2 && !message_eq(2, r3)) { + fprintf(stderr, "\n\nError matching messages[2] in test_scan.\n"); + goto error; + } + + parser_free(); + } + } + } + puts("\b\b\b\b100%"); + return; + + error: + fprintf(stderr, "i=%d j=%d\n", i, j); + fprintf(stderr, "buf1 (%u) %s\n\n", (unsigned int)buf1_len, buf1); + fprintf(stderr, "buf2 (%u) %s\n\n", (unsigned int)buf2_len , buf2); + fprintf(stderr, "buf3 (%u) %s\n", (unsigned int)buf3_len, buf3); + abort(); +} + +// user required to free the result +// string terminated by \0 +char * +create_large_chunked_message (int body_size_in_kb, const char* headers) +{ + int i; + size_t wrote = 0; + size_t headers_len = strlen(headers); + size_t bufsize = headers_len + (5+1024+2)*body_size_in_kb + 6; + char * buf = malloc(bufsize); + + memcpy(buf, headers, headers_len); + wrote += headers_len; + + for (i = 0; i < body_size_in_kb; i++) { + // write 1kb chunk into the body. + memcpy(buf + wrote, "400\r\n", 5); + wrote += 5; + memset(buf + wrote, 'C', 1024); + wrote += 1024; + strcpy(buf + wrote, "\r\n"); + wrote += 2; + } + + memcpy(buf + wrote, "0\r\n\r\n", 6); + wrote += 6; + assert(wrote == bufsize); + + return buf; +} + +/* Verify that we can pause parsing at any of the bytes in the + * message and still get the result that we're expecting. */ +void +test_message_pause (const struct message *msg) +{ + char *buf = (char*) msg->raw; + size_t buflen = strlen(msg->raw); + size_t nread; + + parser_init(msg->type); + + do { + nread = parse_pause(buf, buflen); + + // We can only set the upgrade buffer once we've gotten our message + // completion callback. + if (messages[0].message_complete_cb_called && + msg->upgrade && + parser->upgrade) { + messages[0].upgrade = buf + nread; + goto test; + } + + if (nread < buflen) { + + // Not much do to if we failed a strict-mode check + if (HTTP_PARSER_ERRNO(parser) == HPE_STRICT) { + parser_free(); + return; + } + + assert (HTTP_PARSER_ERRNO(parser) == HPE_PAUSED); + } + + buf += nread; + buflen -= nread; + http_parser_pause(parser, 0); + } while (buflen > 0); + + nread = parse_pause(NULL, 0); + assert (nread == 0); + +test: + if (num_messages != 1) { + printf("\n*** num_messages != 1 after testing '%s' ***\n\n", msg->name); + abort(); + } + + if(!message_eq(0, msg)) abort(); + + parser_free(); +} + +int +main (void) +{ + parser = NULL; + int i, j, k; + int request_count; + int response_count; + unsigned long version; + unsigned major; + unsigned minor; + unsigned patch; + + version = http_parser_version(); + major = (version >> 16) & 255; + minor = (version >> 8) & 255; + patch = version & 255; + printf("http_parser v%u.%u.%u (0x%06lx)\n", major, minor, patch, version); + + printf("sizeof(http_parser) = %u\n", (unsigned int)sizeof(http_parser)); + + for (request_count = 0; requests[request_count].name; request_count++); + for (response_count = 0; responses[response_count].name; response_count++); + + //// API + test_preserve_data(); + test_parse_url(); + test_method_str(); + + //// NREAD + test_header_nread_value(); + + //// OVERFLOW CONDITIONS + + test_header_overflow_error(HTTP_REQUEST); + test_no_overflow_long_body(HTTP_REQUEST, 1000); + test_no_overflow_long_body(HTTP_REQUEST, 100000); + + test_header_overflow_error(HTTP_RESPONSE); + test_no_overflow_long_body(HTTP_RESPONSE, 1000); + test_no_overflow_long_body(HTTP_RESPONSE, 100000); + + test_header_content_length_overflow_error(); + test_chunk_content_length_overflow_error(); + + //// RESPONSES + + for (i = 0; i < response_count; i++) { + test_message(&responses[i]); + } + + for (i = 0; i < response_count; i++) { + test_message_pause(&responses[i]); + } + + for (i = 0; i < response_count; i++) { + if (!responses[i].should_keep_alive) continue; + for (j = 0; j < response_count; j++) { + if (!responses[j].should_keep_alive) continue; + for (k = 0; k < response_count; k++) { + test_multiple3(&responses[i], &responses[j], &responses[k]); + } + } + } + + test_message_count_body(&responses[NO_HEADERS_NO_BODY_404]); + test_message_count_body(&responses[TRAILING_SPACE_ON_CHUNKED_BODY]); + + // test very large chunked response + { + char * msg = create_large_chunked_message(31337, + "HTTP/1.0 200 OK\r\n" + "Transfer-Encoding: chunked\r\n" + "Content-Type: text/plain\r\n" + "\r\n"); + struct message large_chunked = + {.name= "large chunked" + ,.type= HTTP_RESPONSE + ,.raw= msg + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= FALSE + ,.http_major= 1 + ,.http_minor= 0 + ,.status_code= 200 + ,.response_status= "OK" + ,.num_headers= 2 + ,.headers= + { { "Transfer-Encoding", "chunked" } + , { "Content-Type", "text/plain" } + } + ,.body_size= 31337*1024 + ,.num_chunks_complete= 31338 + }; + for (i = 0; i < MAX_CHUNKS; i++) { + large_chunked.chunk_lengths[i] = 1024; + } + test_message_count_body(&large_chunked); + free(msg); + } + + + + printf("response scan 1/2 "); + test_scan( &responses[TRAILING_SPACE_ON_CHUNKED_BODY] + , &responses[NO_BODY_HTTP10_KA_204] + , &responses[NO_REASON_PHRASE] + ); + + printf("response scan 2/2 "); + test_scan( &responses[BONJOUR_MADAME_FR] + , &responses[UNDERSTORE_HEADER_KEY] + , &responses[NO_CARRIAGE_RET] + ); + + puts("responses okay"); + + + /// REQUESTS + + test_simple("GET / HTP/1.1\r\n\r\n", HPE_INVALID_VERSION); + + // Well-formed but incomplete + test_simple("GET / HTTP/1.1\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: 6\r\n" + "\r\n" + "fooba", + HPE_OK); + + static const char *all_methods[] = { + "DELETE", + "GET", + "HEAD", + "POST", + "PUT", + //"CONNECT", //CONNECT can't be tested like other methods, it's a tunnel + "OPTIONS", + "TRACE", + "COPY", + "LOCK", + "MKCOL", + "MOVE", + "PROPFIND", + "PROPPATCH", + "UNLOCK", + "REPORT", + "MKACTIVITY", + "CHECKOUT", + "MERGE", + "M-SEARCH", + "NOTIFY", + "SUBSCRIBE", + "UNSUBSCRIBE", + "PATCH", + 0 }; + const char **this_method; + for (this_method = all_methods; *this_method; this_method++) { + char buf[200]; + sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method); + test_simple(buf, HPE_OK); + } + + static const char *bad_methods[] = { + "ASDF", + "C******", + "COLA", + "GEM", + "GETA", + "M****", + "MKCOLA", + "PROPPATCHA", + "PUN", + "PX", + "SA", + "hello world", + 0 }; + for (this_method = bad_methods; *this_method; this_method++) { + char buf[200]; + sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method); + test_simple(buf, HPE_INVALID_METHOD); + } + + // illegal header field name line folding + test_simple("GET / HTTP/1.1\r\n" + "name\r\n" + " : value\r\n" + "\r\n", + HPE_INVALID_HEADER_TOKEN); + + const char *dumbfuck2 = + "GET / HTTP/1.1\r\n" + "X-SSL-Bullshit: -----BEGIN CERTIFICATE-----\r\n" + "\tMIIFbTCCBFWgAwIBAgICH4cwDQYJKoZIhvcNAQEFBQAwcDELMAkGA1UEBhMCVUsx\r\n" + "\tETAPBgNVBAoTCGVTY2llbmNlMRIwEAYDVQQLEwlBdXRob3JpdHkxCzAJBgNVBAMT\r\n" + "\tAkNBMS0wKwYJKoZIhvcNAQkBFh5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMu\r\n" + "\tdWswHhcNMDYwNzI3MTQxMzI4WhcNMDcwNzI3MTQxMzI4WjBbMQswCQYDVQQGEwJV\r\n" + "\tSzERMA8GA1UEChMIZVNjaWVuY2UxEzARBgNVBAsTCk1hbmNoZXN0ZXIxCzAJBgNV\r\n" + "\tBAcTmrsogriqMWLAk1DMRcwFQYDVQQDEw5taWNoYWVsIHBhcmQYJKoZIhvcNAQEB\r\n" + "\tBQADggEPADCCAQoCggEBANPEQBgl1IaKdSS1TbhF3hEXSl72G9J+WC/1R64fAcEF\r\n" + "\tW51rEyFYiIeZGx/BVzwXbeBoNUK41OK65sxGuflMo5gLflbwJtHBRIEKAfVVp3YR\r\n" + "\tgW7cMA/s/XKgL1GEC7rQw8lIZT8RApukCGqOVHSi/F1SiFlPDxuDfmdiNzL31+sL\r\n" + "\t0iwHDdNkGjy5pyBSB8Y79dsSJtCW/iaLB0/n8Sj7HgvvZJ7x0fr+RQjYOUUfrePP\r\n" + "\tu2MSpFyf+9BbC/aXgaZuiCvSR+8Snv3xApQY+fULK/xY8h8Ua51iXoQ5jrgu2SqR\r\n" + "\twgA7BUi3G8LFzMBl8FRCDYGUDy7M6QaHXx1ZWIPWNKsCAwEAAaOCAiQwggIgMAwG\r\n" + "\tA1UdEwEB/wQCMAAwEQYJYIZIAYb4QgHTTPAQDAgWgMA4GA1UdDwEB/wQEAwID6DAs\r\n" + "\tBglghkgBhvhCAQ0EHxYdVUsgZS1TY2llbmNlIFVzZXIgQ2VydGlmaWNhdGUwHQYD\r\n" + "\tVR0OBBYEFDTt/sf9PeMaZDHkUIldrDYMNTBZMIGaBgNVHSMEgZIwgY+AFAI4qxGj\r\n" + "\tloCLDdMVKwiljjDastqooXSkcjBwMQswCQYDVQQGEwJVSzERMA8GA1UEChMIZVNj\r\n" + "\taWVuY2UxEjAQBgNVBAsTCUF1dGhvcml0eTELMAkGA1UEAxMCQ0ExLTArBgkqhkiG\r\n" + "\t9w0BCQEWHmNhLW9wZXJhdG9yQGdyaWQtc3VwcG9ydC5hYy51a4IBADApBgNVHRIE\r\n" + "\tIjAggR5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMudWswGQYDVR0gBBIwEDAO\r\n" + "\tBgwrBgEEAdkvAQEBAQYwPQYJYIZIAYb4QgEEBDAWLmh0dHA6Ly9jYS5ncmlkLXN1\r\n" + "\tcHBvcnQuYWMudmT4sopwqlBWsvcHViL2NybC9jYWNybC5jcmwwPQYJYIZIAYb4QgEDBDAWLmh0\r\n" + "\tdHA6Ly9jYS5ncmlkLXN1cHBvcnQuYWMudWsvcHViL2NybC9jYWNybC5jcmwwPwYD\r\n" + "\tVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NhLmdyaWQt5hYy51ay9wdWIv\r\n" + "\tY3JsL2NhY3JsLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAS/U4iiooBENGW/Hwmmd3\r\n" + "\tXCy6Zrt08YjKCzGNjorT98g8uGsqYjSxv/hmi0qlnlHs+k/3Iobc3LjS5AMYr5L8\r\n" + "\tUO7OSkgFFlLHQyC9JzPfmLCAugvzEbyv4Olnsr8hbxF1MbKZoQxUZtMVu29wjfXk\r\n" + "\thTeApBv7eaKCWpSp7MCbvgzm74izKhu3vlDk9w6qVrxePfGgpKPqfHiOoGhFnbTK\r\n" + "\twTC6o2xq5y0qZ03JonF7OJspEd3I5zKY3E+ov7/ZhW6DqT8UFvsAdjvQbXyhV8Eu\r\n" + "\tYhixw1aKEPzNjNowuIseVogKOLXxWI5vAi5HgXdS0/ES5gDGsABo4fqovUKlgop3\r\n" + "\tRA==\r\n" + "\t-----END CERTIFICATE-----\r\n" + "\r\n"; + test_simple(dumbfuck2, HPE_OK); + + const char *corrupted_connection = + "GET / HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "Connection\r\033\065\325eep-Alive\r\n" + "Accept-Encoding: gzip\r\n" + "\r\n"; + test_simple(corrupted_connection, HPE_INVALID_HEADER_TOKEN); + + const char *corrupted_header_name = + "GET / HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "X-Some-Header\r\033\065\325eep-Alive\r\n" + "Accept-Encoding: gzip\r\n" + "\r\n"; + test_simple(corrupted_header_name, HPE_INVALID_HEADER_TOKEN); + +#if 0 + // NOTE(Wed Nov 18 11:57:27 CET 2009) this seems okay. we just read body + // until EOF. + // + // no content-length + // error if there is a body without content length + const char *bad_get_no_headers_no_body = "GET /bad_get_no_headers_no_body/world HTTP/1.1\r\n" + "Accept: */*\r\n" + "\r\n" + "HELLO"; + test_simple(bad_get_no_headers_no_body, 0); +#endif + /* TODO sending junk and large headers gets rejected */ + + + /* check to make sure our predefined requests are okay */ + for (i = 0; requests[i].name; i++) { + test_message(&requests[i]); + } + + for (i = 0; i < request_count; i++) { + test_message_pause(&requests[i]); + } + + for (i = 0; i < request_count; i++) { + if (!requests[i].should_keep_alive) continue; + for (j = 0; j < request_count; j++) { + if (!requests[j].should_keep_alive) continue; + for (k = 0; k < request_count; k++) { + test_multiple3(&requests[i], &requests[j], &requests[k]); + } + } + } + + printf("request scan 1/4 "); + test_scan( &requests[GET_NO_HEADERS_NO_BODY] + , &requests[GET_ONE_HEADER_NO_BODY] + , &requests[GET_NO_HEADERS_NO_BODY] + ); + + printf("request scan 2/4 "); + test_scan( &requests[POST_CHUNKED_ALL_YOUR_BASE] + , &requests[POST_IDENTITY_BODY_WORLD] + , &requests[GET_FUNKY_CONTENT_LENGTH] + ); + + printf("request scan 3/4 "); + test_scan( &requests[TWO_CHUNKS_MULT_ZERO_END] + , &requests[CHUNKED_W_TRAILING_HEADERS] + , &requests[CHUNKED_W_BULLSHIT_AFTER_LENGTH] + ); + + printf("request scan 4/4 "); + test_scan( &requests[QUERY_URL_WITH_QUESTION_MARK_GET] + , &requests[PREFIX_NEWLINE_GET ] + , &requests[CONNECT_REQUEST] + ); + + puts("requests okay"); + + return 0; +} diff --git a/external/qhttpserver/http-parser/url_parser.c b/external/qhttpserver/http-parser/url_parser.c new file mode 100755 index 0000000..b1f9c97 --- /dev/null +++ b/external/qhttpserver/http-parser/url_parser.c @@ -0,0 +1,44 @@ +#include "http_parser.h" +#include +#include + +void +dump_url (const char *url, const struct http_parser_url *u) +{ + unsigned int i; + + printf("\tfield_set: 0x%x, port: %u\n", u->field_set, u->port); + for (i = 0; i < UF_MAX; i++) { + if ((u->field_set & (1 << i)) == 0) { + printf("\tfield_data[%u]: unset\n", i); + continue; + } + + printf("\tfield_data[%u]: off: %u len: %u part: \"%.*s\n", + i, + u->field_data[i].off, + u->field_data[i].len, + u->field_data[i].len, + url + u->field_data[i].off); + } +} + +int main(int argc, char ** argv) { + if (argc != 3) { + printf("Syntax : %s connect|get url\n", argv[0]); + return 1; + } + struct http_parser_url u; + int len = strlen(argv[2]); + int connect = strcmp("connect", argv[1]) == 0 ? 1 : 0; + printf("Parsing %s, connect %d\n", argv[2], connect); + + int result = http_parser_parse_url(argv[2], len, connect, &u); + if (result != 0) { + printf("Parse error : %d\n", result); + return result; + } + printf("Parse ok, result : \n"); + dump_url(argv[2], &u); + return 0; +} \ No newline at end of file diff --git a/external/qhttpserver/konvergo-error.patch b/external/qhttpserver/konvergo-error.patch new file mode 100644 index 0000000..17a9eed --- /dev/null +++ b/external/qhttpserver/konvergo-error.patch @@ -0,0 +1,46 @@ +diff --git a/external/qhttpserver/src/qhttpserver.cpp b/external/qhttpserver/src/qhttpserver.cpp +index 07e4a85..507645d 100755 +--- a/external/qhttpserver/src/qhttpserver.cpp ++++ b/external/qhttpserver/src/qhttpserver.cpp +@@ -115,6 +115,7 @@ bool QHttpServer::listen(const QHostAddress &address, quint16 port) + if (couldBindToPort) { + connect(m_tcpServer, SIGNAL(newConnection()), this, SLOT(newConnection())); + } else { ++ m_errorString = m_tcpServer->errorString(); + delete m_tcpServer; + m_tcpServer = NULL; + } +@@ -126,6 +127,11 @@ bool QHttpServer::listen(quint16 port) + return listen(QHostAddress::Any, port); + } + ++QString QHttpServer::errorString() const ++{ ++ return m_errorString; ++} ++ + void QHttpServer::close() + { + if (m_tcpServer) +diff --git a/external/qhttpserver/src/qhttpserver.h b/external/qhttpserver/src/qhttpserver.h +index a3cb74b..4adb391 100755 +--- a/external/qhttpserver/src/qhttpserver.h ++++ b/external/qhttpserver/src/qhttpserver.h +@@ -79,6 +79,9 @@ public: + @sa listen(const QHostAddress&, quint16) */ + bool listen(quint16 port); + ++ /// Return the last error encountered. ++ QString errorString() const; ++ + /// Stop the server and listening for new connections. + void close(); + signals: +@@ -94,6 +97,7 @@ private slots: + + private: + QTcpServer *m_tcpServer; ++ QString m_errorString; + }; + + #endif diff --git a/external/qhttpserver/qhttpserver.pri b/external/qhttpserver/qhttpserver.pri new file mode 100755 index 0000000..28e801c --- /dev/null +++ b/external/qhttpserver/qhttpserver.pri @@ -0,0 +1,3 @@ +isEmpty(PREFIX):PREFIX = /usr/local +isEmpty(LIBDIR):LIBDIR = $${PREFIX}/lib +isEmpty(INCLUDEDIR):INCLUDEDIR = $${PREFIX}/include diff --git a/external/qhttpserver/qhttpserver.pro b/external/qhttpserver/qhttpserver.pro new file mode 100755 index 0000000..7fa2161 --- /dev/null +++ b/external/qhttpserver/qhttpserver.pro @@ -0,0 +1,8 @@ +CONFIG += ordered + +TEMPLATE = subdirs + +SUBDIRS += src \ + examples + +examples.depends = src diff --git a/external/qhttpserver/src/qhttpconnection.cpp b/external/qhttpserver/src/qhttpconnection.cpp new file mode 100755 index 0000000..bfae140 --- /dev/null +++ b/external/qhttpserver/src/qhttpconnection.cpp @@ -0,0 +1,293 @@ +/* + * Copyright 2011-2014 Nikhil Marathe + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "qhttpconnection.h" + +#include +#include + +#include "http_parser.h" +#include "qhttprequest.h" +#include "qhttpresponse.h" + +/// @cond nodoc + +QHttpConnection::QHttpConnection(QTcpSocket *socket, QObject *parent) + : QObject(parent), + m_socket(socket), + m_parser(0), + m_parserSettings(0), + m_request(0), + m_transmitLen(0), + m_transmitPos(0) +{ + m_parser = (http_parser *)malloc(sizeof(http_parser)); + http_parser_init(m_parser, HTTP_REQUEST); + + m_parserSettings = new http_parser_settings(); + m_parserSettings->on_message_begin = MessageBegin; + m_parserSettings->on_url = Url; + m_parserSettings->on_header_field = HeaderField; + m_parserSettings->on_header_value = HeaderValue; + m_parserSettings->on_headers_complete = HeadersComplete; + m_parserSettings->on_body = Body; + m_parserSettings->on_message_complete = MessageComplete; + + m_parser->data = this; + + connect(socket, SIGNAL(readyRead()), this, SLOT(parseRequest())); + connect(socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected())); + connect(socket, SIGNAL(bytesWritten(qint64)), this, SLOT(updateWriteCount(qint64))); +} + +QHttpConnection::~QHttpConnection() +{ + m_socket = 0; + + free(m_parser); + m_parser = 0; + + delete m_parserSettings; + m_parserSettings = 0; +} + +void QHttpConnection::socketDisconnected() +{ + deleteLater(); + + if (m_request) { + if (m_request->successful()) + return; + + m_request->setSuccessful(false); + Q_EMIT m_request->end(); + } +} + +void QHttpConnection::updateWriteCount(qint64 count) +{ + Q_ASSERT(m_transmitPos + count <= m_transmitLen); + + m_transmitPos += count; + + if (m_transmitPos == m_transmitLen) + { + m_transmitLen = 0; + m_transmitPos = 0; + Q_EMIT allBytesWritten(); + } +} + +void QHttpConnection::parseRequest() +{ + Q_ASSERT(m_parser); + + while (m_socket->bytesAvailable()) { + QByteArray arr = m_socket->readAll(); + http_parser_execute(m_parser, m_parserSettings, arr.constData(), arr.size()); + } +} + +void QHttpConnection::write(const QByteArray &data) +{ + m_socket->write(data); + m_transmitLen += data.size(); +} + +void QHttpConnection::flush() +{ + m_socket->flush(); +} + +void QHttpConnection::waitForBytesWritten() +{ + m_socket->waitForBytesWritten(); +} + +void QHttpConnection::responseDone() +{ + QHttpResponse *response = qobject_cast(QObject::sender()); + if (response->m_last) + m_socket->disconnectFromHost(); +} + +/* URL Utilities */ +#define HAS_URL_FIELD(info, field) (info.field_set &(1 << (field))) + +#define GET_FIELD(data, info, field) \ + QString::fromLatin1(data + info.field_data[field].off, info.field_data[field].len) + +#define CHECK_AND_GET_FIELD(data, info, field) \ + (HAS_URL_FIELD(info, field) ? GET_FIELD(data, info, field) : QString()) + +QUrl createUrl(const char *urlData, const http_parser_url &urlInfo) +{ + QUrl url; + url.setScheme(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_SCHEMA)); + url.setHost(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_HOST)); + // Port is dealt with separately since it is available as an integer. + url.setPath(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_PATH)); +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + url.setQuery(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_QUERY)); +#else + if (HAS_URL_FIELD(urlInfo, UF_QUERY)) { + url.setEncodedQuery(QByteArray(urlData + urlInfo.field_data[UF_QUERY].off, + urlInfo.field_data[UF_QUERY].len)); + } +#endif + url.setFragment(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_FRAGMENT)); + url.setUserInfo(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_USERINFO)); + + if (HAS_URL_FIELD(urlInfo, UF_PORT)) + url.setPort(urlInfo.port); + + return url; +} + +#undef CHECK_AND_SET_FIELD +#undef GET_FIELD +#undef HAS_URL_FIELD + +/******************** + * Static Callbacks * + *******************/ + +int QHttpConnection::MessageBegin(http_parser *parser) +{ + QHttpConnection *theConnection = static_cast(parser->data); + theConnection->m_currentHeaders.clear(); + theConnection->m_currentUrl.clear(); + theConnection->m_currentUrl.reserve(128); + + // The QHttpRequest should not be parented to this, since it's memory + // management is the responsibility of the user of the library. + theConnection->m_request = new QHttpRequest(theConnection); + return 0; +} + +int QHttpConnection::HeadersComplete(http_parser *parser) +{ + QHttpConnection *theConnection = static_cast(parser->data); + Q_ASSERT(theConnection->m_request); + + /** set method **/ + theConnection->m_request->setMethod(static_cast(parser->method)); + + /** set version **/ + theConnection->m_request->setVersion( + QString("%1.%2").arg(parser->http_major).arg(parser->http_minor)); + + /** get parsed url **/ + struct http_parser_url urlInfo; + int r = http_parser_parse_url(theConnection->m_currentUrl.constData(), + theConnection->m_currentUrl.size(), + parser->method == HTTP_CONNECT, &urlInfo); + Q_ASSERT(r == 0); + Q_UNUSED(r); + + theConnection->m_request->setUrl(createUrl(theConnection->m_currentUrl.constData(), urlInfo)); + + // Insert last remaining header + theConnection->m_currentHeaders[theConnection->m_currentHeaderField.toLower()] = + theConnection->m_currentHeaderValue; + theConnection->m_request->setHeaders(theConnection->m_currentHeaders); + + /** set client information **/ + theConnection->m_request->m_remoteAddress = theConnection->m_socket->peerAddress().toString(); + theConnection->m_request->m_remotePort = theConnection->m_socket->peerPort(); + + QHttpResponse *response = new QHttpResponse(theConnection); + if (parser->http_major < 1 || parser->http_minor < 1) + response->m_keepAlive = false; + + connect(theConnection, SIGNAL(destroyed()), response, SLOT(connectionClosed())); + connect(response, SIGNAL(done()), theConnection, SLOT(responseDone())); + + // we are good to go! + Q_EMIT theConnection->newRequest(theConnection->m_request, response); + return 0; +} + +int QHttpConnection::MessageComplete(http_parser *parser) +{ + // TODO: do cleanup and prepare for next request + QHttpConnection *theConnection = static_cast(parser->data); + Q_ASSERT(theConnection->m_request); + + theConnection->m_request->setSuccessful(true); + Q_EMIT theConnection->m_request->end(); + return 0; +} + +int QHttpConnection::Url(http_parser *parser, const char *at, size_t length) +{ + QHttpConnection *theConnection = static_cast(parser->data); + Q_ASSERT(theConnection->m_request); + + theConnection->m_currentUrl.append(at, length); + return 0; +} + +int QHttpConnection::HeaderField(http_parser *parser, const char *at, size_t length) +{ + QHttpConnection *theConnection = static_cast(parser->data); + Q_ASSERT(theConnection->m_request); + + // insert the header we parsed previously + // into the header map + if (!theConnection->m_currentHeaderField.isEmpty() && + !theConnection->m_currentHeaderValue.isEmpty()) { + // header names are always lower-cased + theConnection->m_currentHeaders[theConnection->m_currentHeaderField.toLower()] = + theConnection->m_currentHeaderValue; + // clear header value. this sets up a nice + // feedback loop where the next time + // HeaderValue is called, it can simply append + theConnection->m_currentHeaderField = QString(); + theConnection->m_currentHeaderValue = QString(); + } + + QString fieldSuffix = QString::fromLatin1(at, length); + theConnection->m_currentHeaderField += fieldSuffix; + return 0; +} + +int QHttpConnection::HeaderValue(http_parser *parser, const char *at, size_t length) +{ + QHttpConnection *theConnection = static_cast(parser->data); + Q_ASSERT(theConnection->m_request); + + QString valueSuffix = QString::fromLatin1(at, length); + theConnection->m_currentHeaderValue += valueSuffix; + return 0; +} + +int QHttpConnection::Body(http_parser *parser, const char *at, size_t length) +{ + QHttpConnection *theConnection = static_cast(parser->data); + Q_ASSERT(theConnection->m_request); + + Q_EMIT theConnection->m_request->data(QByteArray(at, length)); + return 0; +} + +/// @endcond diff --git a/external/qhttpserver/src/qhttpconnection.h b/external/qhttpserver/src/qhttpconnection.h new file mode 100755 index 0000000..6a9f5eb --- /dev/null +++ b/external/qhttpserver/src/qhttpconnection.h @@ -0,0 +1,85 @@ +/* + * Copyright 2011-2014 Nikhil Marathe + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef Q_HTTP_CONNECTION +#define Q_HTTP_CONNECTION + +#include "qhttpserverapi.h" +#include "qhttpserverfwd.h" + +#include + +/// @cond nodoc + +class QHTTPSERVER_API QHttpConnection : public QObject +{ + Q_OBJECT + +public: + QHttpConnection(QTcpSocket *socket, QObject *parent = 0); + virtual ~QHttpConnection(); + + void write(const QByteArray &data); + void flush(); + void waitForBytesWritten(); + +Q_SIGNALS: + void newRequest(QHttpRequest *, QHttpResponse *); + void allBytesWritten(); + +private Q_SLOTS: + void parseRequest(); + void responseDone(); + void socketDisconnected(); + void updateWriteCount(qint64); + +private: + static int MessageBegin(http_parser *parser); + static int Url(http_parser *parser, const char *at, size_t length); + static int HeaderField(http_parser *parser, const char *at, size_t length); + static int HeaderValue(http_parser *parser, const char *at, size_t length); + static int HeadersComplete(http_parser *parser); + static int Body(http_parser *parser, const char *at, size_t length); + static int MessageComplete(http_parser *parser); + +private: + QTcpSocket *m_socket; + http_parser *m_parser; + http_parser_settings *m_parserSettings; + + // Since there can only be one request at any time even with pipelining. + QHttpRequest *m_request; + + QByteArray m_currentUrl; + // The ones we are reading in from the parser + HeaderHash m_currentHeaders; + QString m_currentHeaderField; + QString m_currentHeaderValue; + + // Keep track of transmit buffer status + qint64 m_transmitLen; + qint64 m_transmitPos; +}; + +/// @endcond + +#endif diff --git a/external/qhttpserver/src/qhttprequest.cpp b/external/qhttpserver/src/qhttprequest.cpp new file mode 100755 index 0000000..ea0765a --- /dev/null +++ b/external/qhttpserver/src/qhttprequest.cpp @@ -0,0 +1,96 @@ +/* + * Copyright 2011-2014 Nikhil Marathe + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "qhttprequest.h" + +#include "qhttpconnection.h" + +QHttpRequest::QHttpRequest(QHttpConnection *connection, QObject *parent) + : QObject(parent), m_connection(connection), m_url("http://localhost/"), m_success(false) +{ +} + +QHttpRequest::~QHttpRequest() +{ +} + +QString QHttpRequest::header(const QString &field) +{ + return m_headers.value(field.toLower(), ""); +} + +const HeaderHash &QHttpRequest::headers() const +{ + return m_headers; +} + +const QString &QHttpRequest::httpVersion() const +{ + return m_version; +} + +const QUrl &QHttpRequest::url() const +{ + return m_url; +} + +const QString QHttpRequest::path() const +{ + return m_url.path(); +} + +const QString QHttpRequest::methodString() const +{ + return MethodToString(method()); +} + +QHttpRequest::HttpMethod QHttpRequest::method() const +{ + return m_method; +} + +const QString &QHttpRequest::remoteAddress() const +{ + return m_remoteAddress; +} + +quint16 QHttpRequest::remotePort() const +{ + return m_remotePort; +} + +void QHttpRequest::storeBody() +{ + connect(this, SIGNAL(data(const QByteArray &)), this, SLOT(appendBody(const QByteArray &)), + Qt::UniqueConnection); +} + +QString QHttpRequest::MethodToString(HttpMethod method) +{ + int index = staticMetaObject.indexOfEnumerator("HttpMethod"); + return staticMetaObject.enumerator(index).valueToKey(method); +} + +void QHttpRequest::appendBody(const QByteArray &body) +{ + m_body.append(body); +} diff --git a/external/qhttpserver/src/qhttprequest.h b/external/qhttpserver/src/qhttprequest.h new file mode 100755 index 0000000..4f9cc3b --- /dev/null +++ b/external/qhttpserver/src/qhttprequest.h @@ -0,0 +1,202 @@ +/* + * Copyright 2011-2014 Nikhil Marathe + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef Q_HTTP_REQUEST +#define Q_HTTP_REQUEST + +#include "qhttpserverapi.h" +#include "qhttpserverfwd.h" + +#include +#include +#include +#include + +/// The QHttpRequest class represents the header and body data sent by the client. +/** The requests header data is available immediately. Body data is streamed as + it comes in via the data() signal. As a consequence the application's request + callback should ensure that it connects to the data() signal before control + returns back to the event loop. Otherwise there is a risk of some data never + being received by the application. + + The class is read-only. */ +class QHTTPSERVER_API QHttpRequest : public QObject +{ + Q_OBJECT + + Q_PROPERTY(HeaderHash headers READ headers) + Q_PROPERTY(QString remoteAddress READ remoteAddress) + Q_PROPERTY(quint16 remotePort READ remotePort) + Q_PROPERTY(QString method READ method) + Q_PROPERTY(QUrl url READ url) + Q_PROPERTY(QString path READ path) + Q_PROPERTY(QString httpVersion READ httpVersion) + + Q_ENUMS(HttpMethod) + + /// @cond nodoc + friend class QHttpConnection; + /// @endcond + +public: + virtual ~QHttpRequest(); + + /// Request method enumeration. + /** @note Taken from http_parser.h -- make sure to keep synced */ + enum HttpMethod { + HTTP_DELETE = 0, + HTTP_GET, + HTTP_HEAD, + HTTP_POST, + HTTP_PUT, + // pathological + HTTP_CONNECT, + HTTP_OPTIONS, + HTTP_TRACE, + // webdav + HTTP_COPY, + HTTP_LOCK, + HTTP_MKCOL, + HTTP_MOVE, + HTTP_PROPFIND, + HTTP_PROPPATCH, + HTTP_SEARCH, + HTTP_UNLOCK, + // subversion + HTTP_REPORT, + HTTP_MKACTIVITY, + HTTP_CHECKOUT, + HTTP_MERGE, + // upnp + HTTP_MSEARCH, + HTTP_NOTIFY, + HTTP_SUBSCRIBE, + HTTP_UNSUBSCRIBE, + // RFC-5789 + HTTP_PATCH, + HTTP_PURGE + }; + + /// The method used for the request. + HttpMethod method() const; + + /// Returns the method string for the request. + /** @note This will plainly transform the enum into a string HTTP_GET -> "HTTP_GET". */ + const QString methodString() const; + + /// The complete URL for the request. + /** This includes the path and query string. + @sa path() */ + const QUrl &url() const; + + /// The path portion of the query URL. + /** @sa url() */ + const QString path() const; + + /// The HTTP version of the request. + /** @return A string in the form of "x.x" */ + const QString &httpVersion() const; + + /// Return all the headers sent by the client. + /** This returns a reference. If you want to store headers + somewhere else, where the request may be deleted, + make sure you store them as a copy. + @note All header names are lowercase + so that Content-Length becomes content-length etc. */ + const HeaderHash &headers() const; + + /// Get the value of a header. + /** Headers are stored as lowercase so the input @c field will be lowercased. + @param field Name of the header field + @return Value of the header or empty string if not found. */ + QString header(const QString &field); + + /// IP Address of the client in dotted decimal format. + const QString &remoteAddress() const; + + /// Outbound connection port for the client. + quint16 remotePort() const; + + /// Request body data, empty for non POST/PUT requests. + /** @sa storeBody() */ + const QByteArray &body() const + { + return m_body; + } + + /// If this request was successfully received. + /** Set before end() has been emitted, stating whether + the message was properly received. This is false + until the receiving the full request has completed. */ + bool successful() const + { + return m_success; + } + + /// Utility function to make this request store all body data internally. + /** If you call this when the request is received via QHttpServer::newRequest() + the request will take care of storing the body data for you. + Once the end() signal is emitted you can access the body data with + the body() function. + + If you wish to handle incoming data yourself don't call this function + and see the data() signal. + @sa data() body() */ + void storeBody(); + +Q_SIGNALS: + /// Emitted when new body data has been received. + /** @note This may be emitted zero or more times + depending on the request type. + @param data Received data. */ + void data(const QByteArray &data); + + /// Emitted when the request has been fully received. + /** @note The no more data() signals will be emitted after this. */ + void end(); + +private Q_SLOTS: + void appendBody(const QByteArray &body); + +private: + QHttpRequest(QHttpConnection *connection, QObject *parent = 0); + + static QString MethodToString(HttpMethod method); + + void setMethod(HttpMethod method) { m_method = method; } + void setVersion(const QString &version) { m_version = version; } + void setUrl(const QUrl &url) { m_url = url; } + void setHeaders(const HeaderHash headers) { m_headers = headers; } + void setSuccessful(bool success) { m_success = success; } + + QHttpConnection *m_connection; + HeaderHash m_headers; + HttpMethod m_method; + QUrl m_url; + QString m_version; + QString m_remoteAddress; + quint16 m_remotePort; + QByteArray m_body; + bool m_success; +}; + +#endif diff --git a/external/qhttpserver/src/qhttpresponse.cpp b/external/qhttpserver/src/qhttpresponse.cpp new file mode 100755 index 0000000..39367a4 --- /dev/null +++ b/external/qhttpserver/src/qhttpresponse.cpp @@ -0,0 +1,196 @@ +/* + * Copyright 2011-2014 Nikhil Marathe + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "qhttpresponse.h" + +#include +#include + +#include "qhttpserver.h" +#include "qhttpconnection.h" + +QHttpResponse::QHttpResponse(QHttpConnection *connection) + // TODO: parent child relation + : QObject(0), + m_connection(connection), + m_headerWritten(false), + m_sentConnectionHeader(false), + m_sentContentLengthHeader(false), + m_sentTransferEncodingHeader(false), + m_sentDate(false), + m_keepAlive(true), + m_last(false), + m_useChunkedEncoding(false), + m_finished(false) +{ + connect(m_connection, SIGNAL(allBytesWritten()), this, SIGNAL(allBytesWritten())); +} + +QHttpResponse::~QHttpResponse() +{ +} + +void QHttpResponse::setHeader(const QString &field, const QString &value) +{ + if (!m_finished) + m_headers[field] = value; + else + qWarning() << "QHttpResponse::setHeader() Cannot set headers after response has finished."; +} + +void QHttpResponse::writeHeader(const char *field, const QString &value) +{ + if (!m_finished) { + m_connection->write(field); + m_connection->write(": "); + m_connection->write(value.toUtf8()); + m_connection->write("\r\n"); + } else + qWarning() + << "QHttpResponse::writeHeader() Cannot write headers after response has finished."; +} + +void QHttpResponse::writeHeaders() +{ + if (m_finished) + return; + + foreach(const QString & name, m_headers.keys()) { + QString value = m_headers[name]; + if (name.compare("connection", Qt::CaseInsensitive) == 0) { + m_sentConnectionHeader = true; + if (value.compare("close", Qt::CaseInsensitive) == 0) + m_last = true; + else + m_keepAlive = true; + } else if (name.compare("transfer-encoding", Qt::CaseInsensitive) == 0) { + m_sentTransferEncodingHeader = true; + if (value.compare("chunked", Qt::CaseInsensitive) == 0) + m_useChunkedEncoding = true; + } else if (name.compare("content-length", Qt::CaseInsensitive) == 0) + m_sentContentLengthHeader = true; + else if (name.compare("date", Qt::CaseInsensitive) == 0) + m_sentDate = true; + + /// @todo Expect case (??) + + writeHeader(name.toLatin1(), value.toLatin1()); + } + + if (!m_sentConnectionHeader) { + if (m_keepAlive && (m_sentContentLengthHeader || m_useChunkedEncoding)) { + writeHeader("Connection", "keep-alive"); + } else { + m_last = true; + writeHeader("Connection", "close"); + } + } + + if (!m_sentContentLengthHeader && !m_sentTransferEncodingHeader) { + if (m_useChunkedEncoding) + writeHeader("Transfer-Encoding", "chunked"); + else + m_last = true; + } + + // Sun, 06 Nov 1994 08:49:37 GMT - RFC 822. Use QLocale::c() so english is used for month and + // day. + if (!m_sentDate) + writeHeader("Date", + QLocale::c().toString(QDateTime::currentDateTimeUtc(), + "ddd, dd MMM yyyy hh:mm:ss") + " GMT"); +} + +void QHttpResponse::writeHead(int status) +{ + if (m_finished) { + qWarning() + << "QHttpResponse::writeHead() Cannot write headers after response has finished."; + return; + } + + if (m_headerWritten) { + qWarning() << "QHttpResponse::writeHead() Already called once for this response."; + return; + } + + m_connection->write( + QString("HTTP/1.1 %1 %2\r\n").arg(status).arg(STATUS_CODES[status]).toLatin1()); + writeHeaders(); + m_connection->write("\r\n"); + + m_headerWritten = true; +} + +void QHttpResponse::writeHead(StatusCode statusCode) +{ + writeHead(static_cast(statusCode)); +} + +void QHttpResponse::write(const QByteArray &data) +{ + if (m_finished) { + qWarning() << "QHttpResponse::write() Cannot write body after response has finished."; + return; + } + + if (!m_headerWritten) { + qWarning() << "QHttpResponse::write() You must call writeHead() before writing body data."; + return; + } + + m_connection->write(data); +} + +void QHttpResponse::flush() +{ + m_connection->flush(); +} + +void QHttpResponse::waitForBytesWritten() +{ + m_connection->waitForBytesWritten(); +} + +void QHttpResponse::end(const QByteArray &data) +{ + if (m_finished) { + qWarning() << "QHttpResponse::end() Cannot write end after response has finished."; + return; + } + + if (data.size() > 0) + write(data); + m_finished = true; + + Q_EMIT done(); + + /// @todo End connection and delete ourselves. Is this a still valid note? + deleteLater(); +} + +void QHttpResponse::connectionClosed() +{ + m_finished = true; + Q_EMIT done(); + deleteLater(); +} diff --git a/external/qhttpserver/src/qhttpresponse.h b/external/qhttpserver/src/qhttpresponse.h new file mode 100755 index 0000000..fcf5897 --- /dev/null +++ b/external/qhttpserver/src/qhttpresponse.h @@ -0,0 +1,172 @@ +/* + * Copyright 2011-2014 Nikhil Marathe + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef Q_HTTP_RESPONSE +#define Q_HTTP_RESPONSE + +#include "qhttpserverapi.h" +#include "qhttpserverfwd.h" + +#include + +/// The QHttpResponse class handles sending data back to the client as a response to a request. +/** The steps to respond correctly are +
      +
    1. Call setHeader() to set headers [optional]
    2. +
    3. Call writeHead() with the HTTP status code
    4. +
    5. Call write() zero or more times for body data.
    6. +
    7. Call end() when the resonse can be sent back
    8. +
    */ +class QHTTPSERVER_API QHttpResponse : public QObject +{ + Q_OBJECT + +public: + /// HTTP status code. + enum StatusCode { + STATUS_CONTINUE = 100, + STATUS_SWITCH_PROTOCOLS = 101, + STATUS_OK = 200, + STATUS_CREATED = 201, + STATUS_ACCEPTED = 202, + STATUS_NON_AUTHORITATIVE_INFORMATION = 203, + STATUS_NO_CONTENT = 204, + STATUS_RESET_CONTENT = 205, + STATUS_PARTIAL_CONTENT = 206, + STATUS_MULTIPLE_CHOICES = 300, + STATUS_MOVED_PERMANENTLY = 301, + STATUS_FOUND = 302, + STATUS_SEE_OTHER = 303, + STATUS_NOT_MODIFIED = 304, + STATUS_USE_PROXY = 305, + STATUS_TEMPORARY_REDIRECT = 307, + STATUS_BAD_REQUEST = 400, + STATUS_UNAUTHORIZED = 401, + STATUS_PAYMENT_REQUIRED = 402, + STATUS_FORBIDDEN = 403, + STATUS_NOT_FOUND = 404, + STATUS_METHOD_NOT_ALLOWED = 405, + STATUS_NOT_ACCEPTABLE = 406, + STATUS_PROXY_AUTHENTICATION_REQUIRED = 407, + STATUS_REQUEST_TIMEOUT = 408, + STATUS_CONFLICT = 409, + STATUS_GONE = 410, + STATUS_LENGTH_REQUIRED = 411, + STATUS_PRECONDITION_FAILED = 412, + STATUS_REQUEST_ENTITY_TOO_LARGE = 413, + STATUS_REQUEST_URI_TOO_LONG = 414, + STATUS_REQUEST_UNSUPPORTED_MEDIA_TYPE = 415, + STATUS_REQUESTED_RANGE_NOT_SATISFIABLE = 416, + STATUS_EXPECTATION_FAILED = 417, + STATUS_INTERNAL_SERVER_ERROR = 500, + STATUS_NOT_IMPLEMENTED = 501, + STATUS_BAD_GATEWAY = 502, + STATUS_SERVICE_UNAVAILABLE = 503, + STATUS_GATEWAY_TIMEOUT = 504, + STATUS_HTTP_VERSION_NOT_SUPPORTED = 505 + }; + + virtual ~QHttpResponse(); + + /// @cond nodoc + friend class QHttpConnection; + /// @endcond + +public Q_SLOTS: + /// Sets a response header @c field to @c value. + /** @note You must call this with all your custom headers + before calling writeHead(), write() or end(). + @param field Header field to be set. + @param value Header value to be set. */ + void setHeader(const QString &field, const QString &value); + + /// Writes the header section of the response + /// using @c status as the response status code. + /** @param statusCode Status code for the response. + @note Any headers should be set before + invoking this function with setHeader(). */ + void writeHead(int statusCode); + + /** @overload */ + void writeHead(StatusCode statusCode); + + /// Writes a block of @c data to the client. + /** @note writeHead() must be called before this function. */ + void write(const QByteArray &data); + + /// Flushes the written data to the client. + /** @note writeHead() must be called before this function. */ + void flush(); + + /// Waiting for bytes to be written. See QAbstractSocket::waitForBytesWritten in the Qt documentation + /** @note writeHead() must be called before this function. */ + void waitForBytesWritten(); + + /// End/finish the response. + /** Data will be flushed to the underlying socket + and the connection itself will be closed if + this is the last response. + + This will emit done() and queue this object + for deletion. For details see \ref memorymanagement. + @param data Optional data to be written before finishing. */ + void end(const QByteArray &data = ""); + +Q_SIGNALS: + /// Emitted when all the data has been sent + /** This signal indicates that the underlaying socket has transmitted all + of it's buffered data. It is possible to implement memory-efficient + file transfers by calling \ref write() for a block of data only after + receiving this signal. */ + void allBytesWritten(); + + /// Emitted when the response is finished. + /** You should not interact with this object + after done() has been emitted as the object + has already been scheduled for deletion. */ + void done(); + +private: + QHttpResponse(QHttpConnection *connection); + + void writeHeaders(); + void writeHeader(const char *field, const QString &value); + + QHttpConnection *m_connection; + + HeaderHash m_headers; + + bool m_headerWritten; + bool m_sentConnectionHeader; + bool m_sentContentLengthHeader; + bool m_sentTransferEncodingHeader; + bool m_sentDate; + bool m_keepAlive; + bool m_last; + bool m_useChunkedEncoding; + bool m_finished; + +private Q_SLOTS: + void connectionClosed(); +}; + +#endif diff --git a/external/qhttpserver/src/qhttpserver.cpp b/external/qhttpserver/src/qhttpserver.cpp new file mode 100755 index 0000000..507645d --- /dev/null +++ b/external/qhttpserver/src/qhttpserver.cpp @@ -0,0 +1,139 @@ +/* + * Copyright 2011-2014 Nikhil Marathe + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "qhttpserver.h" + +#include +#include +#include +#include + +#include "qhttpconnection.h" + +QHash STATUS_CODES; + +QHttpServer::QHttpServer(QObject *parent) : QObject(parent), m_tcpServer(0) +{ +#define STATUS_CODE(num, reason) STATUS_CODES.insert(num, reason); + // {{{ + STATUS_CODE(100, "Continue") + STATUS_CODE(101, "Switching Protocols") + STATUS_CODE(102, "Processing") // RFC 2518) obsoleted by RFC 4918 + STATUS_CODE(200, "OK") + STATUS_CODE(201, "Created") + STATUS_CODE(202, "Accepted") + STATUS_CODE(203, "Non-Authoritative Information") + STATUS_CODE(204, "No Content") + STATUS_CODE(205, "Reset Content") + STATUS_CODE(206, "Partial Content") + STATUS_CODE(207, "Multi-Status") // RFC 4918 + STATUS_CODE(300, "Multiple Choices") + STATUS_CODE(301, "Moved Permanently") + STATUS_CODE(302, "Moved Temporarily") + STATUS_CODE(303, "See Other") + STATUS_CODE(304, "Not Modified") + STATUS_CODE(305, "Use Proxy") + STATUS_CODE(307, "Temporary Redirect") + STATUS_CODE(400, "Bad Request") + STATUS_CODE(401, "Unauthorized") + STATUS_CODE(402, "Payment Required") + STATUS_CODE(403, "Forbidden") + STATUS_CODE(404, "Not Found") + STATUS_CODE(405, "Method Not Allowed") + STATUS_CODE(406, "Not Acceptable") + STATUS_CODE(407, "Proxy Authentication Required") + STATUS_CODE(408, "Request Time-out") + STATUS_CODE(409, "Conflict") + STATUS_CODE(410, "Gone") + STATUS_CODE(411, "Length Required") + STATUS_CODE(412, "Precondition Failed") + STATUS_CODE(413, "Request Entity Too Large") + STATUS_CODE(414, "Request-URI Too Large") + STATUS_CODE(415, "Unsupported Media Type") + STATUS_CODE(416, "Requested Range Not Satisfiable") + STATUS_CODE(417, "Expectation Failed") + STATUS_CODE(418, "I\"m a teapot") // RFC 2324 + STATUS_CODE(422, "Unprocessable Entity") // RFC 4918 + STATUS_CODE(423, "Locked") // RFC 4918 + STATUS_CODE(424, "Failed Dependency") // RFC 4918 + STATUS_CODE(425, "Unordered Collection") // RFC 4918 + STATUS_CODE(426, "Upgrade Required") // RFC 2817 + STATUS_CODE(500, "Internal Server Error") + STATUS_CODE(501, "Not Implemented") + STATUS_CODE(502, "Bad Gateway") + STATUS_CODE(503, "Service Unavailable") + STATUS_CODE(504, "Gateway Time-out") + STATUS_CODE(505, "HTTP Version not supported") + STATUS_CODE(506, "Variant Also Negotiates") // RFC 2295 + STATUS_CODE(507, "Insufficient Storage") // RFC 4918 + STATUS_CODE(509, "Bandwidth Limit Exceeded") + STATUS_CODE(510, "Not Extended") // RFC 2774 + // }}} +} + +QHttpServer::~QHttpServer() +{ +} + +void QHttpServer::newConnection() +{ + Q_ASSERT(m_tcpServer); + + while (m_tcpServer->hasPendingConnections()) { + QHttpConnection *connection = + new QHttpConnection(m_tcpServer->nextPendingConnection(), this); + connect(connection, SIGNAL(newRequest(QHttpRequest *, QHttpResponse *)), this, + SIGNAL(newRequest(QHttpRequest *, QHttpResponse *))); + } +} + +bool QHttpServer::listen(const QHostAddress &address, quint16 port) +{ + Q_ASSERT(!m_tcpServer); + m_tcpServer = new QTcpServer(this); + + bool couldBindToPort = m_tcpServer->listen(address, port); + if (couldBindToPort) { + connect(m_tcpServer, SIGNAL(newConnection()), this, SLOT(newConnection())); + } else { + m_errorString = m_tcpServer->errorString(); + delete m_tcpServer; + m_tcpServer = NULL; + } + return couldBindToPort; +} + +bool QHttpServer::listen(quint16 port) +{ + return listen(QHostAddress::Any, port); +} + +QString QHttpServer::errorString() const +{ + return m_errorString; +} + +void QHttpServer::close() +{ + if (m_tcpServer) + m_tcpServer->close(); +} diff --git a/external/qhttpserver/src/qhttpserver.h b/external/qhttpserver/src/qhttpserver.h new file mode 100755 index 0000000..c7ed557 --- /dev/null +++ b/external/qhttpserver/src/qhttpserver.h @@ -0,0 +1,103 @@ +/* + * Copyright 2011-2014 Nikhil Marathe + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef Q_HTTP_SERVER +#define Q_HTTP_SERVER + +#define QHTTPSERVER_VERSION_MAJOR 0 +#define QHTTPSERVER_VERSION_MINOR 1 +#define QHTTPSERVER_VERSION_PATCH 0 + +#include "qhttpserverapi.h" +#include "qhttpserverfwd.h" + +#include +#include + +/// Maps status codes to string reason phrases +extern QHash STATUS_CODES; + +/// The QHttpServer class forms the basis of the %QHttpServer +/// project. It is a fast, non-blocking HTTP server. +/** These are the steps to create a server, handle and respond to requests: +
      +
    1. Create an instance of QHttpServer.
    2. +
    3. Connect a slot to the newRequest() signal.
    4. +
    5. Create a QCoreApplication to drive the server event loop.
    6. +
    7. Respond to clients by writing out to the QHttpResponse object.
    8. +
    + + Here is a simple sample application on how to use this library + + helloworld.cpp + @include helloworld/helloworld.cpp + + helloworld.h + @include helloworld/helloworld.h */ +class QHTTPSERVER_API QHttpServer : public QObject +{ + Q_OBJECT + +public: + /// Construct a new HTTP Server. + /** @param parent Parent QObject for the server. */ + QHttpServer(QObject *parent = 0); + + virtual ~QHttpServer(); + + /// Start the server by bounding to the given @c address and @c port. + /** @note This function returns immediately, it does not block. + @param address Address on which to listen to. Default is to listen on + all interfaces which means the server can be accessed from anywhere. + @param port Port number on which the server should run. + @return True if the server was started successfully, false otherwise. + @sa listen(quint16) */ + bool listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 80); + + /// Starts the server on @c port listening on all interfaces. + /** @param port Port number on which the server should run. + @return True if the server was started successfully, false otherwise. + @sa listen(const QHostAddress&, quint16) */ + bool listen(quint16 port); + + /// Return the last error encountered. + QString errorString() const; + + /// Stop the server and listening for new connections. + void close(); +Q_SIGNALS: + /// Emitted when a client makes a new request to the server. + /** The slot should use the given @c request and @c response + objects to communicate with the client. + @param request New incoming request. + @param response Response object to the request. */ + void newRequest(QHttpRequest *request, QHttpResponse *response); + +private Q_SLOTS: + void newConnection(); + +private: + QTcpServer *m_tcpServer; + QString m_errorString; +}; + +#endif diff --git a/external/qhttpserver/src/qhttpserver.h.orig b/external/qhttpserver/src/qhttpserver.h.orig new file mode 100755 index 0000000..46c5b4b --- /dev/null +++ b/external/qhttpserver/src/qhttpserver.h.orig @@ -0,0 +1,99 @@ +/* + * Copyright 2011-2014 Nikhil Marathe + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef Q_HTTP_SERVER +#define Q_HTTP_SERVER + +#define QHTTPSERVER_VERSION_MAJOR 0 +#define QHTTPSERVER_VERSION_MINOR 1 +#define QHTTPSERVER_VERSION_PATCH 0 + +#include "qhttpserverapi.h" +#include "qhttpserverfwd.h" + +#include +#include + +/// Maps status codes to string reason phrases +extern QHash STATUS_CODES; + +/// The QHttpServer class forms the basis of the %QHttpServer +/// project. It is a fast, non-blocking HTTP server. +/** These are the steps to create a server, handle and respond to requests: +
      +
    1. Create an instance of QHttpServer.
    2. +
    3. Connect a slot to the newRequest() signal.
    4. +
    5. Create a QCoreApplication to drive the server event loop.
    6. +
    7. Respond to clients by writing out to the QHttpResponse object.
    8. +
    + + Here is a simple sample application on how to use this library + + helloworld.cpp + @include helloworld/helloworld.cpp + + helloworld.h + @include helloworld/helloworld.h */ +class QHTTPSERVER_API QHttpServer : public QObject +{ + Q_OBJECT + +public: + /// Construct a new HTTP Server. + /** @param parent Parent QObject for the server. */ + QHttpServer(QObject *parent = 0); + + virtual ~QHttpServer(); + + /// Start the server by bounding to the given @c address and @c port. + /** @note This function returns immediately, it does not block. + @param address Address on which to listen to. Default is to listen on + all interfaces which means the server can be accessed from anywhere. + @param port Port number on which the server should run. + @return True if the server was started successfully, false otherwise. + @sa listen(quint16) */ + bool listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 80); + + /// Starts the server on @c port listening on all interfaces. + /** @param port Port number on which the server should run. + @return True if the server was started successfully, false otherwise. + @sa listen(const QHostAddress&, quint16) */ + bool listen(quint16 port); + + /// Stop the server and listening for new connections. + void close(); +Q_SIGNALS: + /// Emitted when a client makes a new request to the server. + /** The slot should use the given @c request and @c response + objects to communicate with the client. + @param request New incoming request. + @param response Response object to the request. */ + void newRequest(QHttpRequest *request, QHttpResponse *response); + +private Q_SLOTS: + void newConnection(); + +private: + QTcpServer *m_tcpServer; +}; + +#endif diff --git a/external/qhttpserver/src/qhttpserverapi.h b/external/qhttpserver/src/qhttpserverapi.h new file mode 100755 index 0000000..38e4625 --- /dev/null +++ b/external/qhttpserver/src/qhttpserverapi.h @@ -0,0 +1,56 @@ +/* + * Copyright 2011-2014 Nikhil Marathe + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef Q_HTTP_SERVER_API +#define Q_HTTP_SERVER_API + +#include + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) +#ifdef Q_OS_WIN +// Define to export or import depending if we are building or using the library. +// QHTTPSERVER_EXPORT should only be defined when building. +#if defined(QHTTPSERVER_EXPORT) +#define QHTTPSERVER_API __declspec(dllexport) +#else +#define QHTTPSERVER_API __declspec(dllimport) +#endif +#else +// Define empty for other platforms +#define QHTTPSERVER_API +#endif +#else +#ifdef Q_WS_WIN +// Define to export or import depending if we are building or using the library. +// QHTTPSERVER_EXPORT should only be defined when building. +#if defined(QHTTPSERVER_EXPORT) +#define QHTTPSERVER_API __declspec(dllexport) +#else +#define QHTTPSERVER_API __declspec(dllimport) +#endif +#else +// Define empty for other platforms +#define QHTTPSERVER_API +#endif +#endif + +#endif diff --git a/external/qhttpserver/src/qhttpserverfwd.h b/external/qhttpserver/src/qhttpserverfwd.h new file mode 100755 index 0000000..ca7c266 --- /dev/null +++ b/external/qhttpserver/src/qhttpserverfwd.h @@ -0,0 +1,48 @@ +/* + * Copyright 2011-2014 Nikhil Marathe + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef Q_HTTP_SERVER_FWD +#define Q_HTTP_SERVER_FWD + +#include +#include + +/*! + * A map of request or response headers + */ +typedef QHash HeaderHash; + +// QHttpServer +class QHttpServer; +class QHttpConnection; +class QHttpRequest; +class QHttpResponse; + +// Qt +class QTcpServer; +class QTcpSocket; + +// http_parser +struct http_parser_settings; +struct http_parser; + +#endif diff --git a/external/qhttpserver/src/src.pro b/external/qhttpserver/src/src.pro new file mode 100755 index 0000000..dfe8789 --- /dev/null +++ b/external/qhttpserver/src/src.pro @@ -0,0 +1,37 @@ +include(../qhttpserver.pri) + +QHTTPSERVER_BASE = .. +TEMPLATE = lib + +TARGET = qhttpserver + +!win32:VERSION = 0.1.0 + +QT += network +QT -= gui + +CONFIG += dll debug_and_release + +CONFIG(debug, debug|release) { + win32: TARGET = $$join(TARGET,,,d) +} + +DEFINES += QHTTPSERVER_EXPORT + +INCLUDEPATH += $$QHTTPSERVER_BASE/http-parser + +PRIVATE_HEADERS += $$QHTTPSERVER_BASE/http-parser/http_parser.h qhttpconnection.h + +PUBLIC_HEADERS += qhttpserver.h qhttprequest.h qhttpresponse.h qhttpserverapi.h qhttpserverfwd.h + +HEADERS = $$PRIVATE_HEADERS $$PUBLIC_HEADERS +SOURCES = *.cpp $$QHTTPSERVER_BASE/http-parser/http_parser.c + +OBJECTS_DIR = $$QHTTPSERVER_BASE/build +MOC_DIR = $$QHTTPSERVER_BASE/build +DESTDIR = $$QHTTPSERVER_BASE/lib + +target.path = $$LIBDIR +headers.path = $$INCLUDEDIR +headers.files = $$PUBLIC_HEADERS +INSTALLS += target headers diff --git a/external/qslog/CMakeLists.txt b/external/qslog/CMakeLists.txt new file mode 100644 index 0000000..87b66b7 --- /dev/null +++ b/external/qslog/CMakeLists.txt @@ -0,0 +1,3 @@ +set(CMAKE_AUTOMOC ON) +aux_source_directory(. QSLOG_SRC) +add_library(qslog STATIC ${QSLOG_SRC}) diff --git a/external/qslog/LICENSE.txt b/external/qslog/LICENSE.txt new file mode 100644 index 0000000..af90342 --- /dev/null +++ b/external/qslog/LICENSE.txt @@ -0,0 +1,24 @@ +Copyright (c) 2014, Razvan Petru +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or other + materials provided with the distribution. +* The name of the contributors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/external/qslog/QsLog.cpp b/external/qslog/QsLog.cpp new file mode 100644 index 0000000..7004529 --- /dev/null +++ b/external/qslog/QsLog.cpp @@ -0,0 +1,258 @@ +// Copyright (c) 2013, Razvan Petru +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: + +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// * The name of the contributors may not be used to endorse or promote products +// derived from this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "QsLog.h" +#include "QsLogDest.h" +#ifdef QS_LOG_SEPARATE_THREAD +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +namespace QsLogging +{ +typedef QVector DestinationList; + +static const char TraceString[] = "TRACE"; +static const char DebugString[] = "DEBUG"; +static const char InfoString[] = "INFO "; +static const char WarnString[] = "WARN "; +static const char ErrorString[] = "ERROR"; +static const char FatalString[] = "FATAL"; + +// not using Qt::ISODate because we need the milliseconds too +static const QString fmtDateTime("yyyy-MM-dd hh:mm:ss"); + +static Logger* sInstance = 0; + +static const char* LevelToText(Level theLevel) +{ + switch (theLevel) { + case TraceLevel: + return TraceString; + case DebugLevel: + return DebugString; + case InfoLevel: + return InfoString; + case WarnLevel: + return WarnString; + case ErrorLevel: + return ErrorString; + case FatalLevel: + return FatalString; + case OffLevel: + return ""; + default: { + assert(!"bad log level"); + return InfoString; + } + } +} + +#ifdef QS_LOG_SEPARATE_THREAD +class LogWriterRunnable : public QRunnable +{ +public: + LogWriterRunnable(QString message, Level level); + virtual void run(); + +private: + QString mMessage; + Level mLevel; +}; +#endif + +class LoggerImpl +{ +public: + LoggerImpl(); + +#ifdef QS_LOG_SEPARATE_THREAD + QThreadPool threadPool; +#endif + QMutex logMutex; + Level level; + DestinationList destList; + ProcessingCallback process; +}; + +#ifdef QS_LOG_SEPARATE_THREAD +LogWriterRunnable::LogWriterRunnable(QString message, Level level) + : QRunnable() + , mMessage(message) + , mLevel(level) +{ +} + +void LogWriterRunnable::run() +{ + Logger::instance().write(mMessage, mLevel); +} +#endif + + +LoggerImpl::LoggerImpl() + : level(InfoLevel) + , process(0) +{ + // assume at least file + console + destList.reserve(2); +#ifdef QS_LOG_SEPARATE_THREAD + threadPool.setMaxThreadCount(1); + threadPool.setExpiryTimeout(-1); +#endif +} + + +Logger::Logger() + : d(new LoggerImpl) +{ +} + +Logger& Logger::instance() +{ + if (!sInstance) + sInstance = new Logger; + + return *sInstance; +} + +void Logger::destroyInstance() +{ + delete sInstance; + sInstance = 0; +} + +// tries to extract the level from a string log message. If available, conversionSucceeded will +// contain the conversion result. +Level Logger::levelFromLogMessage(const QString& logMessage, bool* conversionSucceeded) +{ + if (conversionSucceeded) + *conversionSucceeded = true; + + if (logMessage.startsWith(QLatin1String(TraceString))) + return TraceLevel; + if (logMessage.startsWith(QLatin1String(DebugString))) + return DebugLevel; + if (logMessage.startsWith(QLatin1String(InfoString))) + return InfoLevel; + if (logMessage.startsWith(QLatin1String(WarnString))) + return WarnLevel; + if (logMessage.startsWith(QLatin1String(ErrorString))) + return ErrorLevel; + if (logMessage.startsWith(QLatin1String(FatalString))) + return FatalLevel; + + if (conversionSucceeded) + *conversionSucceeded = false; + return OffLevel; +} + +Logger::~Logger() +{ +#ifdef QS_LOG_SEPARATE_THREAD + d->threadPool.waitForDone(); +#endif + delete d; + d = 0; +} + +void Logger::addDestination(DestinationPtr destination) +{ + assert(destination.data()); + d->destList.push_back(destination); +} + +void Logger::setLoggingLevel(Level newLevel) +{ + d->level = newLevel; +} + +Level Logger::loggingLevel() const +{ + return d->level; +} + +void Logger::setProcessingCallback(ProcessingCallback cb) +{ + d->process = cb; +} + +//! creates the complete log message and passes it to the logger +void Logger::Helper::writeToLog() +{ + const char* const levelName = LevelToText(level); + const QString completeMessage(QString("%1 [ %2 ] %3") + .arg(QDateTime::currentDateTime().toString(fmtDateTime)) + .arg(levelName) + .arg(buffer) + ); + + Logger::instance().enqueueWrite(completeMessage, level); +} + +Logger::Helper::~Helper() +{ + try { + writeToLog(); + } + catch(std::exception&) { + // you shouldn't throw exceptions from a sink + assert(!"exception in logger helper destructor"); + throw; + } +} + +//! directs the message to the task queue or writes it directly +void Logger::enqueueWrite(QString message, Level level) +{ + if (d->process) + d->process(message); +#ifdef QS_LOG_SEPARATE_THREAD + LogWriterRunnable *r = new LogWriterRunnable(message, level); + d->threadPool.start(r); +#else + write(message, level); +#endif +} + +//! Sends the message to all the destinations. The level for this message is passed in case +//! it's useful for processing in the destination. +void Logger::write(const QString& message, Level level) +{ + QMutexLocker lock(&d->logMutex); + for (DestinationList::iterator it = d->destList.begin(), + endIt = d->destList.end();it != endIt;++it) { + (*it)->write(message, level); + } +} + +} // end namespace diff --git a/external/qslog/QsLog.h b/external/qslog/QsLog.h new file mode 100644 index 0000000..2cda2ae --- /dev/null +++ b/external/qslog/QsLog.h @@ -0,0 +1,142 @@ +// Copyright (c) 2013, Razvan Petru +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: + +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// * The name of the contributors may not be used to endorse or promote products +// derived from this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef QSLOG_H +#define QSLOG_H + +#include "QsLogLevel.h" +#include "QsLogDest.h" +#include +#include +#include + +#define QS_LOG_VERSION "2.0b3" + +namespace QsLogging +{ +class Destination; +class LoggerImpl; // d pointer + +typedef void (*ProcessingCallback)(QString& message); + +class QSLOG_SHARED_OBJECT Logger +{ +public: + static Logger& instance(); + static void destroyInstance(); + static Level levelFromLogMessage(const QString& logMessage, bool* conversionSucceeded = 0); + + ~Logger(); + + //! Adds a log message destination. Don't add null destinations. + void addDestination(DestinationPtr destination); + //! Logging at a level < 'newLevel' will be ignored + void setLoggingLevel(Level newLevel); + //! The default level is INFO + Level loggingLevel() const; + //! Set callback for changing message contents before writing to output + void setProcessingCallback(ProcessingCallback cb); + + //! The helper forwards the streaming to QDebug and builds the final + //! log message. + class QSLOG_SHARED_OBJECT Helper + { + public: + explicit Helper(Level logLevel) : + level(logLevel), + qtDebug(&buffer) {} + ~Helper(); + QDebug& stream(){ return qtDebug; } + + private: + void writeToLog(); + + Level level; + QString buffer; + QDebug qtDebug; + }; + +private: + Logger(); + Logger(const Logger&); // not available + Logger& operator=(const Logger&); // not available + + void enqueueWrite(QString message, Level level); + void write(const QString& message, Level level); + + LoggerImpl* d; + + friend class LogWriterRunnable; +}; + +} // end namespace + +//! Logging macros: define QS_LOG_LINE_NUMBERS to get the file and line number +//! in the log output. +#ifndef QS_LOG_LINE_NUMBERS +#define QLOG_TRACE() \ + if (QsLogging::Logger::instance().loggingLevel() > QsLogging::TraceLevel) {} \ + else QsLogging::Logger::Helper(QsLogging::TraceLevel).stream() +#define QLOG_DEBUG() \ + if (QsLogging::Logger::instance().loggingLevel() > QsLogging::DebugLevel) {} \ + else QsLogging::Logger::Helper(QsLogging::DebugLevel).stream() +#define QLOG_INFO() \ + if (QsLogging::Logger::instance().loggingLevel() > QsLogging::InfoLevel) {} \ + else QsLogging::Logger::Helper(QsLogging::InfoLevel).stream() +#define QLOG_WARN() \ + if (QsLogging::Logger::instance().loggingLevel() > QsLogging::WarnLevel) {} \ + else QsLogging::Logger::Helper(QsLogging::WarnLevel).stream() +#define QLOG_ERROR() \ + if (QsLogging::Logger::instance().loggingLevel() > QsLogging::ErrorLevel) {} \ + else QsLogging::Logger::Helper(QsLogging::ErrorLevel).stream() +#define QLOG_FATAL() \ + if (QsLogging::Logger::instance().loggingLevel() > QsLogging::FatalLevel) {} \ + else QsLogging::Logger::Helper(QsLogging::FatalLevel).stream() +#else +#define QLOG_TRACE() \ + if (QsLogging::Logger::instance().loggingLevel() > QsLogging::TraceLevel) {} \ + else QsLogging::Logger::Helper(QsLogging::TraceLevel).stream() << qPrintable(QFileInfo(__FILE__).fileName()) << '@' << __LINE__ << "-" +#define QLOG_DEBUG() \ + if (QsLogging::Logger::instance().loggingLevel() > QsLogging::DebugLevel) {} \ + else QsLogging::Logger::Helper(QsLogging::DebugLevel).stream() << qPrintable(QFileInfo(__FILE__).fileName()) << '@' << __LINE__ << "-" +#define QLOG_INFO() \ + if (QsLogging::Logger::instance().loggingLevel() > QsLogging::InfoLevel) {} \ + else QsLogging::Logger::Helper(QsLogging::InfoLevel).stream() << qPrintable(QFileInfo(__FILE__).fileName()) << '@' << __LINE__ << "-" +#define QLOG_WARN() \ + if (QsLogging::Logger::instance().loggingLevel() > QsLogging::WarnLevel) {} \ + else QsLogging::Logger::Helper(QsLogging::WarnLevel).stream() << qPrintable(QFileInfo(__FILE__).fileName()) << '@' << __LINE__ << "-" +#define QLOG_ERROR() \ + if (QsLogging::Logger::instance().loggingLevel() > QsLogging::ErrorLevel) {} \ + else QsLogging::Logger::Helper(QsLogging::ErrorLevel).stream() << qPrintable(QFileInfo(__FILE__).fileName()) << '@' << __LINE__ << "-" +#define QLOG_FATAL() \ + if (QsLogging::Logger::instance().loggingLevel() > QsLogging::FatalLevel) {} \ + else QsLogging::Logger::Helper(QsLogging::FatalLevel).stream() << qPrintable(QFileInfo(__FILE__).fileName()) << '@' << __LINE__ << "-" +#endif + +#ifdef QS_LOG_DISABLE +#include "QsLogDisableForThisFile.h" +#endif + +#endif // QSLOG_H diff --git a/external/qslog/QsLogDest.cpp b/external/qslog/QsLogDest.cpp new file mode 100644 index 0000000..cddd7d0 --- /dev/null +++ b/external/qslog/QsLogDest.cpp @@ -0,0 +1,73 @@ +// Copyright (c) 2013, Razvan Petru +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: + +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// * The name of the contributors may not be used to endorse or promote products +// derived from this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "QsLogDest.h" +#include "QsLogDestConsole.h" +#include "QsLogDestFile.h" +#include "QsLogDestFunctor.h" +#include + +namespace QsLogging +{ + +Destination::~Destination() +{ +} + +//! destination factory +DestinationPtr DestinationFactory::MakeFileDestination(const QString& filePath, + LogRotationOption rotation, const MaxSizeBytes &sizeInBytesToRotateAfter, + const MaxOldLogCount &oldLogsToKeep) +{ + if (EnableLogRotation == rotation || EnableLogRotationOnOpen == rotation) { + QScopedPointer logRotation(new SizeRotationStrategy); + logRotation->setMaximumSizeInBytes(sizeInBytesToRotateAfter.size); + logRotation->setBackupCount(oldLogsToKeep.count); + + FileDestination *dest = new FileDestination(filePath, RotationStrategyPtr(logRotation.take())); + if (EnableLogRotationOnOpen == rotation && dest->rotationStrategy()->currentSizeInBytes() > 0) + dest->rotate(); + return DestinationPtr(dest); + } + + return DestinationPtr(new FileDestination(filePath, RotationStrategyPtr(new NullRotationStrategy))); +} + +DestinationPtr DestinationFactory::MakeDebugOutputDestination() +{ + return DestinationPtr(new DebugOutputDestination); +} + +DestinationPtr DestinationFactory::MakeFunctorDestination(QsLogging::Destination::LogFunction f) +{ + return DestinationPtr(new FunctorDestination(f)); +} + +DestinationPtr DestinationFactory::MakeFunctorDestination(QObject *receiver, const char *member) +{ + return DestinationPtr(new FunctorDestination(receiver, member)); +} + +} // end namespace diff --git a/external/qslog/QsLogDest.h b/external/qslog/QsLogDest.h new file mode 100644 index 0000000..31c9c56 --- /dev/null +++ b/external/qslog/QsLogDest.h @@ -0,0 +1,101 @@ +// Copyright (c) 2013, Razvan Petru +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: + +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// * The name of the contributors may not be used to endorse or promote products +// derived from this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef QSLOGDEST_H +#define QSLOGDEST_H + +#include "QsLogLevel.h" +#include +#include +class QString; +class QObject; + +#ifdef QSLOG_IS_SHARED_LIBRARY +#define QSLOG_SHARED_OBJECT Q_DECL_EXPORT +#elif QSLOG_IS_SHARED_LIBRARY_IMPORT +#define QSLOG_SHARED_OBJECT Q_DECL_IMPORT +#else +#define QSLOG_SHARED_OBJECT +#endif + +namespace QsLogging +{ + +class QSLOG_SHARED_OBJECT Destination +{ +public: + typedef void (*LogFunction)(const QString &message, Level level); + virtual void rotate() = 0; + +public: + virtual ~Destination(); + virtual void write(const QString& message, Level level) = 0; + virtual bool isValid() = 0; // returns whether the destination was created correctly +}; +typedef QSharedPointer DestinationPtr; + + +// a series of "named" paramaters, to make the file destination creation more readable +enum LogRotationOption +{ + DisableLogRotation = 0, + EnableLogRotation = 1, + EnableLogRotationOnOpen = 2, +}; + +struct QSLOG_SHARED_OBJECT MaxSizeBytes +{ + MaxSizeBytes() : size(0) {} + explicit MaxSizeBytes(qint64 size_) : size(size_) {} + qint64 size; +}; + +struct QSLOG_SHARED_OBJECT MaxOldLogCount +{ + MaxOldLogCount() : count(0) {} + explicit MaxOldLogCount(int count_) : count(count_) {} + int count; +}; + + +//! Creates logging destinations/sinks. The caller shares ownership of the destinations with the logger. +//! After being added to a logger, the caller can discard the pointers. +class QSLOG_SHARED_OBJECT DestinationFactory +{ +public: + static DestinationPtr MakeFileDestination(const QString& filePath, + LogRotationOption rotation = DisableLogRotation, + const MaxSizeBytes &sizeInBytesToRotateAfter = MaxSizeBytes(), + const MaxOldLogCount &oldLogsToKeep = MaxOldLogCount()); + static DestinationPtr MakeDebugOutputDestination(); + // takes a pointer to a function + static DestinationPtr MakeFunctorDestination(Destination::LogFunction f); + // takes a QObject + signal/slot + static DestinationPtr MakeFunctorDestination(QObject *receiver, const char *member); +}; + +} // end namespace + +#endif // QSLOGDEST_H diff --git a/external/qslog/QsLogDestConsole.cpp b/external/qslog/QsLogDestConsole.cpp new file mode 100644 index 0000000..9efe0e3 --- /dev/null +++ b/external/qslog/QsLogDestConsole.cpp @@ -0,0 +1,52 @@ +// Copyright (c) 2013, Razvan Petru +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: + +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// * The name of the contributors may not be used to endorse or promote products +// derived from this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "QsLogDestConsole.h" +#include +#include + +#if defined(Q_OS_WIN) +#define WIN32_LEAN_AND_MEAN +void QsDebugOutput::output( const QString& message ) +{ +} +#elif defined(Q_OS_UNIX) +#include +void QsDebugOutput::output( const QString& message ) +{ + fprintf(stderr, "%s\n", qPrintable(message)); + fflush(stderr); +} +#endif + +void QsLogging::DebugOutputDestination::write(const QString& message, Level) +{ + QsDebugOutput::output(message); +} + +bool QsLogging::DebugOutputDestination::isValid() +{ + return true; +} diff --git a/external/qslog/QsLogDestConsole.h b/external/qslog/QsLogDestConsole.h new file mode 100644 index 0000000..04bd868 --- /dev/null +++ b/external/qslog/QsLogDestConsole.h @@ -0,0 +1,53 @@ +// Copyright (c) 2013, Razvan Petru +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: + +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// * The name of the contributors may not be used to endorse or promote products +// derived from this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef QSLOGDESTCONSOLE_H +#define QSLOGDESTCONSOLE_H + +#include "QsLogDest.h" + +class QString; + +class QsDebugOutput +{ +public: + static void output(const QString& a_message); +}; + +namespace QsLogging +{ + +// debugger sink +class DebugOutputDestination : public Destination +{ +public: + virtual void write(const QString& message, Level level); + virtual bool isValid(); + virtual void rotate() { } +}; + +} + +#endif // QSLOGDESTCONSOLE_H diff --git a/external/qslog/QsLogDestFile.cpp b/external/qslog/QsLogDestFile.cpp new file mode 100644 index 0000000..307b0af --- /dev/null +++ b/external/qslog/QsLogDestFile.cpp @@ -0,0 +1,163 @@ +// Copyright (c) 2013, Razvan Petru +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: + +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// * The name of the contributors may not be used to endorse or promote products +// derived from this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "QsLogDestFile.h" +#include +#include +#include +#include + +const int QsLogging::SizeRotationStrategy::MaxBackupCount = 10; + +QsLogging::RotationStrategy::~RotationStrategy() +{ +} + +QsLogging::SizeRotationStrategy::SizeRotationStrategy() : mCurrentSizeInBytes(0), mMaxSizeInBytes(0), mBackupsCount(0) +{ +} + +void QsLogging::SizeRotationStrategy::setInitialInfo(const QFile& file) +{ + mFileName = file.fileName(); + mCurrentSizeInBytes = file.size(); +} + +void QsLogging::SizeRotationStrategy::includeMessageInCalculation(const QString& message) +{ + mCurrentSizeInBytes += message.toUtf8().size(); +} + +bool QsLogging::SizeRotationStrategy::shouldRotate() +{ + return mCurrentSizeInBytes > mMaxSizeInBytes; +} + +// Algorithm assumes backups will be named filename.X, where 1 <= X <= mBackupsCount. +// All X's will be shifted up. +void QsLogging::SizeRotationStrategy::rotate() +{ + if (!mBackupsCount) + { + if (!QFile::remove(mFileName)) + std::cerr << "QsLog: backup delete failed " << qPrintable(mFileName); + return; + } + + // 1. find the last existing backup than can be shifted up + const QString logNamePattern = mFileName + QString::fromUtf8(".%1"); + int lastExistingBackupIndex = 0; + for (int i = 1; i <= mBackupsCount; ++i) + { + const QString backupFileName = logNamePattern.arg(i); + if (QFile::exists(backupFileName)) + lastExistingBackupIndex = qMin(i, mBackupsCount - 1); + else + break; + } + + // 2. shift up + for (int i = lastExistingBackupIndex; i >= 1; --i) + { + const QString oldName = logNamePattern.arg(i); + const QString newName = logNamePattern.arg(i + 1); + QFile::remove(newName); + const bool renamed = QFile::rename(oldName, newName); + if (!renamed) + { + std::cerr << "QsLog: could not rename backup " << qPrintable(oldName) << " to " << qPrintable(newName); + } + } + + // 3. rename current log file + const QString newName = logNamePattern.arg(1); + if (QFile::exists(newName)) + QFile::remove(newName); + if (!QFile::rename(mFileName, newName)) + { + std::cerr << "QsLog: could not rename log " << qPrintable(mFileName) << " to " << qPrintable(newName); + } +} + +QIODevice::OpenMode QsLogging::SizeRotationStrategy::recommendedOpenModeFlag() +{ + return QIODevice::Append; +} + +void QsLogging::SizeRotationStrategy::setMaximumSizeInBytes(qint64 size) +{ + Q_ASSERT(size >= 0); + mMaxSizeInBytes = size; +} + +void QsLogging::SizeRotationStrategy::setBackupCount(int backups) +{ + Q_ASSERT(backups >= 0); + mBackupsCount = qMin(backups, SizeRotationStrategy::MaxBackupCount); +} + +qint64 QsLogging::SizeRotationStrategy::currentSizeInBytes() +{ + return mCurrentSizeInBytes; +} + +QsLogging::FileDestination::FileDestination(const QString& filePath, RotationStrategyPtr rotationStrategy) + : mRotationStrategy(rotationStrategy) +{ + mFile.setFileName(filePath); + if (!mFile.open(QFile::WriteOnly | QFile::Text | mRotationStrategy->recommendedOpenModeFlag())) + std::cerr << "QsLog: could not open log file " << qPrintable(filePath); + mOutputStream.setDevice(&mFile); + mOutputStream.setCodec("UTF-8"); + + mRotationStrategy->setInitialInfo(mFile); +} + +void QsLogging::FileDestination::write(const QString& message, Level) +{ + mRotationStrategy->includeMessageInCalculation(message); + if (mRotationStrategy->shouldRotate()) + rotate(); + + mOutputStream << message << endl; + mOutputStream.flush(); +} + +bool QsLogging::FileDestination::isValid() +{ + return mFile.isOpen(); +} + +void QsLogging::FileDestination::rotate() +{ + mOutputStream.setDevice(NULL); + mFile.close(); + mRotationStrategy->rotate(); + if (!mFile.open(QFile::WriteOnly | QFile::Text | mRotationStrategy->recommendedOpenModeFlag())) + std::cerr << "QsLog: could not reopen log file " << qPrintable(mFile.fileName()); + mRotationStrategy->setInitialInfo(mFile); + mOutputStream.setDevice(&mFile); + mOutputStream.setCodec("UTF-8"); +} diff --git a/external/qslog/QsLogDestFile.h b/external/qslog/QsLogDestFile.h new file mode 100644 index 0000000..6b5a688 --- /dev/null +++ b/external/qslog/QsLogDestFile.h @@ -0,0 +1,106 @@ +// Copyright (c) 2013, Razvan Petru +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: + +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// * The name of the contributors may not be used to endorse or promote products +// derived from this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef QSLOGDESTFILE_H +#define QSLOGDESTFILE_H + +#include "QsLogDest.h" +#include +#include +#include +#include + +namespace QsLogging +{ +class RotationStrategy +{ +public: + virtual ~RotationStrategy(); + + virtual void setInitialInfo(const QFile &file) = 0; + virtual void includeMessageInCalculation(const QString &message) = 0; + virtual bool shouldRotate() = 0; + virtual void rotate() = 0; + virtual QIODevice::OpenMode recommendedOpenModeFlag() = 0; + virtual qint64 currentSizeInBytes() {return 0;} +}; + +// Never rotates file, overwrites existing file. +class NullRotationStrategy : public RotationStrategy +{ +public: + virtual void setInitialInfo(const QFile &) {} + virtual void includeMessageInCalculation(const QString &) {} + virtual bool shouldRotate() { return false; } + virtual void rotate() {} + virtual QIODevice::OpenMode recommendedOpenModeFlag() { return QIODevice::Truncate; } +}; + +// Rotates after a size is reached, keeps a number of <= 10 backups, appends to existing file. +class SizeRotationStrategy : public RotationStrategy +{ +public: + SizeRotationStrategy(); + static const int MaxBackupCount; + + virtual void setInitialInfo(const QFile &file); + virtual void includeMessageInCalculation(const QString &message); + virtual bool shouldRotate(); + virtual void rotate(); + virtual QIODevice::OpenMode recommendedOpenModeFlag(); + virtual qint64 currentSizeInBytes(); + + void setMaximumSizeInBytes(qint64 size); + void setBackupCount(int backups); + +private: + QString mFileName; + qint64 mCurrentSizeInBytes; + qint64 mMaxSizeInBytes; + int mBackupsCount; +}; + +typedef QSharedPointer RotationStrategyPtr; + +// file message sink +class FileDestination : public Destination +{ +public: + FileDestination(const QString& filePath, RotationStrategyPtr rotationStrategy); + virtual void write(const QString& message, Level level); + virtual bool isValid(); + virtual void rotate(); + + QSharedPointer rotationStrategy() { return mRotationStrategy; } + +private: + QFile mFile; + QTextStream mOutputStream; + QSharedPointer mRotationStrategy; +}; + +} + +#endif // QSLOGDESTFILE_H diff --git a/external/qslog/QsLogDestFunctor.cpp b/external/qslog/QsLogDestFunctor.cpp new file mode 100644 index 0000000..601139d --- /dev/null +++ b/external/qslog/QsLogDestFunctor.cpp @@ -0,0 +1,57 @@ +// Copyright (c) 2014, Razvan Petru +// Copyright (c) 2014, Omar Carey +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: + +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// * The name of the contributors may not be used to endorse or promote products +// derived from this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "QsLogDestFunctor.h" +#include +#include + +QsLogging::FunctorDestination::FunctorDestination(LogFunction f) + : QObject(NULL) + , mLogFunction(f) +{ +} + +QsLogging::FunctorDestination::FunctorDestination(QObject *receiver, const char *member) + : QObject(NULL) + , mLogFunction(NULL) +{ + connect(this, SIGNAL(logMessageReady(QString,int)), receiver, member, Qt::QueuedConnection); +} + + +void QsLogging::FunctorDestination::write(const QString &message, QsLogging::Level level) +{ + if (mLogFunction) + mLogFunction(message, level); + + if (level > QsLogging::TraceLevel) + emit logMessageReady(message, static_cast(level)); +} + +bool QsLogging::FunctorDestination::isValid() +{ + return true; +} diff --git a/external/qslog/QsLogDestFunctor.h b/external/qslog/QsLogDestFunctor.h new file mode 100644 index 0000000..9e6ee1e --- /dev/null +++ b/external/qslog/QsLogDestFunctor.h @@ -0,0 +1,60 @@ +// Copyright (c) 2014, Razvan Petru +// Copyright (c) 2014, Omar Carey +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: + +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// * The name of the contributors may not be used to endorse or promote products +// derived from this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef QSLOGDESTFUNCTOR_H +#define QSLOGDESTFUNCTOR_H + +#include "QsLogDest.h" +#include + +namespace QsLogging +{ +// Offers various types of function-like sinks. +// This is an advanced destination type. Depending on your configuration, LogFunction might be +// called from a different thread or even a different binary. You should not access QsLog from +// inside LogFunction and should not perform any time-consuming operations. +// logMessageReady is connected through a queued connection and trace messages are not included +class FunctorDestination : public QObject, public Destination +{ + Q_OBJECT +public: + explicit FunctorDestination(LogFunction f); + FunctorDestination(QObject *receiver, const char *member); + + virtual void write(const QString &message, Level level); + virtual bool isValid(); + virtual void rotate() { } + +protected: + // int used to avoid registering a new enum type + Q_SIGNAL void logMessageReady(const QString &message, int level); + +private: + LogFunction mLogFunction; +}; +} + +#endif // QSLOGDESTFUNCTOR_H diff --git a/external/qslog/QsLogDisableForThisFile.h b/external/qslog/QsLogDisableForThisFile.h new file mode 100644 index 0000000..c70af10 --- /dev/null +++ b/external/qslog/QsLogDisableForThisFile.h @@ -0,0 +1,22 @@ +#ifndef QSLOGDISABLEFORTHISFILE_H +#define QSLOGDISABLEFORTHISFILE_H + +#include +// When included AFTER QsLog.h, this file will disable logging in that C++ file. When included +// before, it will lead to compiler warnings or errors about macro redefinitions. + +#undef QLOG_TRACE +#undef QLOG_DEBUG +#undef QLOG_INFO +#undef QLOG_WARN +#undef QLOG_ERROR +#undef QLOG_FATAL + +#define QLOG_TRACE() if (1) {} else qDebug() +#define QLOG_DEBUG() if (1) {} else qDebug() +#define QLOG_INFO() if (1) {} else qDebug() +#define QLOG_WARN() if (1) {} else qDebug() +#define QLOG_ERROR() if (1) {} else qDebug() +#define QLOG_FATAL() if (1) {} else qDebug() + +#endif // QSLOGDISABLEFORTHISFILE_H diff --git a/external/qslog/QsLogLevel.h b/external/qslog/QsLogLevel.h new file mode 100644 index 0000000..6298473 --- /dev/null +++ b/external/qslog/QsLogLevel.h @@ -0,0 +1,45 @@ +// Copyright (c) 2013, Razvan Petru +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: + +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// * The name of the contributors may not be used to endorse or promote products +// derived from this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef QSLOGLEVEL_H +#define QSLOGLEVEL_H + +namespace QsLogging +{ + +enum Level +{ + TraceLevel = 0, + DebugLevel, + InfoLevel, + WarnLevel, + ErrorLevel, + FatalLevel, + OffLevel +}; + +} + +#endif // QSLOGLEVEL_H diff --git a/external/qslog/QsLogReadme.txt b/external/qslog/QsLogReadme.txt new file mode 100644 index 0000000..a7a50e0 --- /dev/null +++ b/external/qslog/QsLogReadme.txt @@ -0,0 +1,65 @@ +QsLog - the simple Qt logger +------------------------------------------------------------------------------- +QsLog is an easy to use logger that is based on Qt's QDebug class. + +Features +------------------------------------------------------------------------------- + * Six logging levels (from trace to fatal) + * Logging level threshold configurable at runtime. + * Minimum overhead when logging is turned off. + * Multiple destinations, comes with file and debug destinations. + * Thread-safe + * Logging of common Qt types out of the box. + * Immediate logging or queueing messages in a separate thread. + * Small dependency: just drop it in your project directly. + +Usage +------------------------------------------------------------------------------- +By directly including QsLog in your project: + 1. Include QsLog.pri in your pro file + 2. Include QsLog.h in your C++ files. Include QsLogDest.h only where you create/add destinations. + 3. Get the instance of the logger by calling QsLogging::Logger::instance(); + 4. Optionally set the logging level. Info is default. + 5. Create as many destinations as you want by using the QsLogging::DestinationFactory. + 6. Add the destinations to the logger instance by calling addDestination. + 7. Start logging! + Note: when you want to use QsLog both from an executable and a shared library you have to + link dynamically with QsLog due to a limitation with static variables. + +By linking to QsLog dynamically: + 1. Build QsLog using the QsLogSharedLibrary.pro. + 2. Add the QsLog shared library to your LIBS project dependencies. + 3. Follow the steps in "directly including QsLog in your project" starting with step 2. + +Configuration +------------------------------------------------------------------------------- +QsLog has several configurable parameters: + * defining QS_LOG_LINE_NUMBERS in the .pri file enables writing the file and line number + automatically for each logging call + * defining QS_LOG_SEPARATE_THREAD will route all log messages to a separate thread. + +Sometimes it's necessary to turn off logging. This can be done in several ways: + * globally, at compile time, by enabling the QS_LOG_DISABLE macro in the .pri file. + * globally, at run time, by setting the log level to "OffLevel". + * per file, at compile time, by including QsLogDisableForThisFile.h in the target file. + +Thread safety +------------------------------------------------------------------------------- +The Qt docs say: A thread-safe function can be called simultaneously from multiple threads, +even when the invocations use shared data, because all references to the shared data are serialized. +A reentrant function can also be called simultaneously from multiple threads, but only if each +invocation uses its own data. + +Since sending the log message to the destinations is protected by a mutex, the logging macros are +thread-safe provided that the log has been initialized - i.e: instance() has been called. +The instance function and the setup functions (e.g: setLoggingLevel, addDestination) are NOT +thread-safe and are NOT reentrant. + +IMPORTANT: when using a separate thread for logging, your program might crash at exit time on some + operating systems if you won't call Logger::destroyInstance() before your program exits. + This function can be called either before returning from main in a console app or + inside QCoreApplication::aboutToQuit in a Qt GUI app. + The reason is that the logging thread is still running as some objects are destroyed by + the OS. Calling destroyInstance will wait for the thread to finish. + Nothing will happen if you forget to call the function when not using a separate thread + for logging. diff --git a/qt-patches/0001-qtwebengine-Add-a-backgroundColor-property.patch b/qt-patches/0001-qtwebengine-Add-a-backgroundColor-property.patch new file mode 100644 index 0000000..7503114 --- /dev/null +++ b/qt-patches/0001-qtwebengine-Add-a-backgroundColor-property.patch @@ -0,0 +1,532 @@ +From 3adadda878f63fcca6892c49c4ddc82143d59b66 Mon Sep 17 00:00:00 2001 +From: Jocelyn Turcotte +Date: Thu, 9 Jul 2015 21:52:43 +0200 +Subject: [PATCH] Add a backgroundColor property + +This also allows setting a transparent color to see through +the web view's body if its background color isn't specified. + +The color is initially held by the top API view and is pulled +by the WebContentsAdapter in order to set it on the RenderView. +Since both blink and our local compositors (in the QOpenGLWidget +case) need to know about this color, RWHVQt takes care of pushing +it to both. The former through an IPC message and the latter +directly on the RWHVQtDelegate. + +Task-number: QTBUG-41960 +Change-Id: Ie13317b2d087f5612ad9c5fb0e05ca3e91aec9af +Reviewed-by: Allan Sandfeld Jensen +Reviewed-by: Andras Becsi +--- + qtwebengine/src/core/common/qt_messages.h | 3 ++ + qtwebengine/src/core/render_widget_host_view_qt.cpp | 9 ++++++ + qtwebengine/src/core/render_widget_host_view_qt.h | 1 + + qtwebengine/src/core/render_widget_host_view_qt_delegate.h | 1 + + qtwebengine/src/core/renderer/qt_render_view_observer.cpp | 6 ++++ + qtwebengine/src/core/renderer/qt_render_view_observer.h | 1 + + qtwebengine/src/core/type_conversion.h | 5 ++++ + qtwebengine/src/core/web_contents_adapter.cpp | 7 +++++ + qtwebengine/src/core/web_contents_adapter.h | 1 + + qtwebengine/src/core/web_contents_adapter_client.h | 1 + + qtwebengine/src/core/web_contents_view_qt.cpp | 7 +++++ + qtwebengine/src/core/web_contents_view_qt.h | 2 +- + qtwebengine/src/webengine/api/qquickwebengineview.cpp | 34 ++++++++++++++++++++++ + qtwebengine/src/webengine/api/qquickwebengineview_p.h | 5 +++- + qtwebengine/src/webengine/api/qquickwebengineview_p_p.h | 2 ++ + .../render_widget_host_view_qt_delegate_quick.h | 2 ++ + ...nder_widget_host_view_qt_delegate_quickwindow.h | 1 + + qtwebengine/src/webenginewidgets/api/qwebenginepage.cpp | 33 +++++++++++++++++++++ + qtwebengine/src/webenginewidgets/api/qwebenginepage.h | 3 ++ + qtwebengine/src/webenginewidgets/api/qwebenginepage_p.h | 2 ++ + .../render_widget_host_view_qt_delegate_widget.cpp | 16 +++++++++- + .../render_widget_host_view_qt_delegate_widget.h | 2 ++ + 22 files changed, 141 insertions(+), 3 deletions(-) + +diff --git a/qtwebengine/src/core/common/qt_messages.h b/src/core/common/qt_messages.h +index c692ee5..25a995b 100644 +--- a/qtwebengine/src/core/common/qt_messages.h ++++ b/qtwebengine/src/core/common/qt_messages.h +@@ -31,6 +31,9 @@ IPC_MESSAGE_ROUTED1(QtRenderViewObserver_FetchDocumentMarkup, + IPC_MESSAGE_ROUTED1(QtRenderViewObserver_FetchDocumentInnerText, + uint64 /* requestId */) + ++IPC_MESSAGE_ROUTED1(QtRenderViewObserver_SetBackgroundColor, ++ uint32 /* color */) ++ + IPC_MESSAGE_ROUTED1(WebChannelIPCTransport_Message, std::vector /*binaryJSON*/) + + // User scripts messages +diff --git a/qtwebengine/src/core/render_widget_host_view_qt.cpp b/src/core/render_widget_host_view_qt.cpp +index 572bc34..20b41bc 100644 +--- a/qtwebengine/src/core/render_widget_host_view_qt.cpp ++++ b/qtwebengine/src/core/render_widget_host_view_qt.cpp +@@ -36,6 +36,7 @@ + + #include "render_widget_host_view_qt.h" + ++#include "common/qt_messages.h" + #include "browser_accessibility_manager_qt.h" + #include "browser_accessibility_qt.h" + #include "chromium_overrides.h" +@@ -408,6 +409,14 @@ gfx::Rect RenderWidgetHostViewQt::GetViewBounds() const + return gfx::BoundingRect(p1, p2); + } + ++void RenderWidgetHostViewQt::SetBackgroundColor(SkColor color) { ++ RenderWidgetHostViewBase::SetBackgroundColor(color); ++ // Set the background of the compositor if necessary ++ m_delegate->setClearColor(toQt(color)); ++ // Set the background of the blink::FrameView ++ m_host->Send(new QtRenderViewObserver_SetBackgroundColor(m_host->GetRoutingID(), color)); ++} ++ + // Return value indicates whether the mouse is locked successfully or not. + bool RenderWidgetHostViewQt::LockMouse() + { +diff --git a/qtwebengine/src/core/render_widget_host_view_qt.h b/src/core/render_widget_host_view_qt.h +index 248c52f..2724292 100644 +--- a/qtwebengine/src/core/render_widget_host_view_qt.h ++++ b/qtwebengine/src/core/render_widget_host_view_qt.h +@@ -125,6 +125,7 @@ public: + virtual void Hide() Q_DECL_OVERRIDE; + virtual bool IsShowing() Q_DECL_OVERRIDE; + virtual gfx::Rect GetViewBounds() const Q_DECL_OVERRIDE; ++ virtual void SetBackgroundColor(SkColor color) Q_DECL_OVERRIDE; + virtual bool LockMouse() Q_DECL_OVERRIDE; + virtual void UnlockMouse() Q_DECL_OVERRIDE; + virtual void WasShown() Q_DECL_OVERRIDE; +diff --git a/qtwebengine/src/core/render_widget_host_view_qt_delegate.h b/src/core/render_widget_host_view_qt_delegate.h +index da595b9..f4aa9b2 100644 +--- a/qtwebengine/src/core/render_widget_host_view_qt_delegate.h ++++ b/qtwebengine/src/core/render_widget_host_view_qt_delegate.h +@@ -96,6 +96,7 @@ public: + virtual void move(const QPoint &) = 0; + virtual void inputMethodStateChanged(bool editorVisible) = 0; + virtual void setTooltip(const QString &) = 0; ++ virtual void setClearColor(const QColor &color) = 0; + }; + + } // namespace QtWebEngineCore +diff --git a/qtwebengine/src/core/renderer/qt_render_view_observer.cpp b/src/core/renderer/qt_render_view_observer.cpp +index 83534da..ba91e54 100644 +--- a/qtwebengine/src/core/renderer/qt_render_view_observer.cpp ++++ b/qtwebengine/src/core/renderer/qt_render_view_observer.cpp +@@ -65,6 +65,11 @@ void QtRenderViewObserver::onFetchDocumentInnerText(quint64 requestId) + render_view()->GetWebView()->mainFrame()->contentAsText(std::numeric_limits::max()))); + } + ++void QtRenderViewObserver::onSetBackgroundColor(quint32 color) ++{ ++ render_view()->GetWebView()->setBaseBackgroundColor(color); ++} ++ + void QtRenderViewObserver::OnFirstVisuallyNonEmptyLayout() + { + Send(new QtRenderViewObserverHost_DidFirstVisuallyNonEmptyLayout(routing_id())); +@@ -76,6 +81,7 @@ bool QtRenderViewObserver::OnMessageReceived(const IPC::Message& message) + IPC_BEGIN_MESSAGE_MAP(QtRenderViewObserver, message) + IPC_MESSAGE_HANDLER(QtRenderViewObserver_FetchDocumentMarkup, onFetchDocumentMarkup) + IPC_MESSAGE_HANDLER(QtRenderViewObserver_FetchDocumentInnerText, onFetchDocumentInnerText) ++ IPC_MESSAGE_HANDLER(QtRenderViewObserver_SetBackgroundColor, onSetBackgroundColor) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +diff --git a/qtwebengine/src/core/renderer/qt_render_view_observer.h b/src/core/renderer/qt_render_view_observer.h +index cb77cd0..3f7829a 100644 +--- a/qtwebengine/src/core/renderer/qt_render_view_observer.h ++++ b/qtwebengine/src/core/renderer/qt_render_view_observer.h +@@ -47,6 +47,7 @@ public: + private: + void onFetchDocumentMarkup(quint64 requestId); + void onFetchDocumentInnerText(quint64 requestId); ++ void onSetBackgroundColor(quint32 color); + + void OnFirstVisuallyNonEmptyLayout() Q_DECL_OVERRIDE; + +diff --git a/qtwebengine/src/core/type_conversion.h b/src/core/type_conversion.h +index 66fcd4d..f1bf54b 100644 +--- a/qtwebengine/src/core/type_conversion.h ++++ b/qtwebengine/src/core/type_conversion.h +@@ -122,6 +122,11 @@ inline QColor toQt(const SkColor &c) + return QColor(SkColorGetR(c), SkColorGetG(c), SkColorGetB(c), SkColorGetA(c)); + } + ++inline SkColor toSk(const QColor &c) ++{ ++ return c.rgba(); ++} ++ + inline QMatrix4x4 toQt(const SkMatrix44 &m) + { + QMatrix4x4 qtMatrix( +diff --git a/qtwebengine/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp +index 8c13035..0c5b6ca 100644 +--- a/qtwebengine/src/core/web_contents_adapter.cpp ++++ b/qtwebengine/src/core/web_contents_adapter.cpp +@@ -837,6 +837,13 @@ void WebContentsAdapter::filesSelectedInChooser(const QStringList &fileList, Web + rvh->FilesSelectedInChooser(toVector(files), static_cast(mode)); + } + ++void WebContentsAdapter::backgroundColorChanged() ++{ ++ Q_D(WebContentsAdapter); ++ if (content::RenderWidgetHostView *rwhv = d->webContents->GetRenderWidgetHostView()) ++ rwhv->SetBackgroundColor(toSk(d->adapterClient->backgroundColor())); ++} ++ + content::WebContents *WebContentsAdapter::webContents() const + { + Q_D(const WebContentsAdapter); +diff --git a/qtwebengine/src/core/web_contents_adapter.h b/src/core/web_contents_adapter.h +index 5ea55c1..929967a 100644 +--- a/qtwebengine/src/core/web_contents_adapter.h ++++ b/qtwebengine/src/core/web_contents_adapter.h +@@ -120,6 +120,7 @@ public: + void grantMouseLockPermission(bool granted); + + void dpiScaleChanged(); ++ void backgroundColorChanged(); + QAccessibleInterface *browserAccessible(); + BrowserContextQt* browserContext(); + BrowserContextAdapter* browserContextAdapter(); +diff --git a/qtwebengine/src/core/web_contents_adapter_client.h b/src/core/web_contents_adapter_client.h +index 3ed3ab9..acf3095 100644 +--- a/qtwebengine/src/core/web_contents_adapter_client.h ++++ b/qtwebengine/src/core/web_contents_adapter_client.h +@@ -152,6 +152,7 @@ public: + virtual void selectionChanged() = 0; + virtual QRectF viewportRect() const = 0; + virtual qreal dpiScale() const = 0; ++ virtual QColor backgroundColor() const = 0; + virtual void loadStarted(const QUrl &provisionalUrl, bool isErrorPage = false) = 0; + virtual void loadCommitted() = 0; + virtual void loadVisuallyCommitted() = 0; +diff --git a/qtwebengine/src/core/web_contents_view_qt.cpp b/src/core/web_contents_view_qt.cpp +index 9321706..f40bf8f 100644 +--- a/qtwebengine/src/core/web_contents_view_qt.cpp ++++ b/qtwebengine/src/core/web_contents_view_qt.cpp +@@ -81,6 +81,13 @@ content::RenderWidgetHostViewBase* WebContentsViewQt::CreateViewForPopupWidget(c + return view; + } + ++void WebContentsViewQt::RenderViewCreated(content::RenderViewHost* host) ++{ ++ // The render process is done creating the RenderView and it's ready to be routed ++ // messages at this point. ++ host->GetView()->SetBackgroundColor(toSk(m_client->backgroundColor())); ++} ++ + void WebContentsViewQt::CreateView(const gfx::Size& initial_size, gfx::NativeView context) + { + // This is passed through content::WebContents::CreateParams::context either as the native view's client +diff --git a/qtwebengine/src/core/web_contents_view_qt.h b/src/core/web_contents_view_qt.h +index 896955f..3ede530 100644 +--- a/qtwebengine/src/core/web_contents_view_qt.h ++++ b/qtwebengine/src/core/web_contents_view_qt.h +@@ -75,7 +75,7 @@ public: + + virtual void SetPageTitle(const base::string16& title) Q_DECL_OVERRIDE { } + +- virtual void RenderViewCreated(content::RenderViewHost* host) Q_DECL_OVERRIDE { QT_NOT_YET_IMPLEMENTED } ++ virtual void RenderViewCreated(content::RenderViewHost* host) Q_DECL_OVERRIDE; + + virtual void RenderViewSwappedIn(content::RenderViewHost* host) Q_DECL_OVERRIDE { QT_NOT_YET_IMPLEMENTED } + +diff --git a/qtwebengine/src/webengine/api/qquickwebengineview.cpp b/src/webengine/api/qquickwebengineview.cpp +index 7405cae..c57d8d1 100644 +--- a/qtwebengine/src/webengine/api/qquickwebengineview.cpp ++++ b/qtwebengine/src/webengine/api/qquickwebengineview.cpp +@@ -104,6 +104,7 @@ QQuickWebEngineViewPrivate::QQuickWebEngineViewPrivate() + , isLoading(false) + , devicePixelRatio(QGuiApplication::primaryScreen()->devicePixelRatio()) + , m_dpiScale(1.0) ++ , m_backgroundColor(Qt::white) + { + // The gold standard for mobile web content is 160 dpi, and the devicePixelRatio expected + // is the (possibly quantized) ratio of device dpi to 160 dpi. +@@ -310,6 +311,11 @@ qreal QQuickWebEngineViewPrivate::dpiScale() const + return m_dpiScale; + } + ++QColor QQuickWebEngineViewPrivate::backgroundColor() const ++{ ++ return m_backgroundColor; ++} ++ + void QQuickWebEngineViewPrivate::loadStarted(const QUrl &provisionalUrl, bool isErrorPage) + { + Q_Q(QQuickWebEngineView); +@@ -858,6 +864,34 @@ qreal QQuickWebEngineView::zoomFactor() const + return d->adapter->currentZoomFactor(); + } + ++/*! ++ \qmlproperty bool WebEngineView::backgroundColor ++ \since QtWebEngine 1.3 ++ ++ Sets this property to change the color of the WebEngineView's background, ++ behing the document's body. You can set it to "transparent" or to a translucent ++ color to see through the document, or you can set this color to match your ++ web content in an hybrid app to prevent the white flashes that may appear ++ during loading. ++ ++ The default value is white. ++*/ ++QColor QQuickWebEngineView::backgroundColor() const ++{ ++ Q_D(const QQuickWebEngineView); ++ return d->m_backgroundColor; ++} ++ ++void QQuickWebEngineView::setBackgroundColor(const QColor &color) ++{ ++ Q_D(QQuickWebEngineView); ++ if (color == d->m_backgroundColor) ++ return; ++ d->m_backgroundColor = color; ++ d->ensureContentsAdapter(); ++ d->adapter->backgroundColorChanged(); ++ emit backgroundColorChanged(); ++} + + bool QQuickWebEngineView::isFullScreen() const + { +diff --git a/qtwebengine/src/webengine/api/qquickwebengineview_p.h b/src/webengine/api/qquickwebengineview_p.h +index 40299c1..362c3a2 100644 +--- a/qtwebengine/src/webengine/api/qquickwebengineview_p.h ++++ b/qtwebengine/src/webengine/api/qquickwebengineview_p.h +@@ -89,6 +89,7 @@ class Q_WEBENGINE_PRIVATE_EXPORT QQuickWebEngineView : public QQuickItem { + Q_PROPERTY(QQuickWebEngineHistory *navigationHistory READ navigationHistory CONSTANT FINAL REVISION 1) + Q_PROPERTY(QQmlWebChannel *webChannel READ webChannel WRITE setWebChannel NOTIFY webChannelChanged REVISION 1) + Q_PROPERTY(QQmlListProperty userScripts READ userScripts FINAL) ++ Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor NOTIFY backgroundColorChanged REVISION 1) + + #ifdef ENABLE_QML_TESTSUPPORT_API + Q_PROPERTY(QQuickWebEngineTestSupport *testSupport READ testSupport WRITE setTestSupport FINAL) +@@ -118,6 +119,8 @@ public: + bool isFullScreen() const; + qreal zoomFactor() const; + void setZoomFactor(qreal arg); ++ QColor backgroundColor() const; ++ void setBackgroundColor(const QColor &color); + + QQuickWebEngineViewExperimental *experimental() const; + +@@ -230,7 +233,7 @@ Q_SIGNALS: + Q_REVISION(1) void zoomFactorChanged(qreal arg); + Q_REVISION(1) void profileChanged(); + Q_REVISION(1) void webChannelChanged(); +- ++ Q_REVISION(1) void backgroundColorChanged(); + + protected: + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); +diff --git a/qtwebengine/src/webengine/api/qquickwebengineview_p_p.h b/src/webengine/api/qquickwebengineview_p_p.h +index edc8c1a..3bfed2f 100644 +--- a/qtwebengine/src/webengine/api/qquickwebengineview_p_p.h ++++ b/qtwebengine/src/webengine/api/qquickwebengineview_p_p.h +@@ -123,6 +123,7 @@ public: + virtual void selectionChanged() Q_DECL_OVERRIDE { } + virtual QRectF viewportRect() const Q_DECL_OVERRIDE; + virtual qreal dpiScale() const Q_DECL_OVERRIDE; ++ virtual QColor backgroundColor() const Q_DECL_OVERRIDE; + virtual void loadStarted(const QUrl &provisionalUrl, bool isErrorPage = false) Q_DECL_OVERRIDE; + virtual void loadCommitted() Q_DECL_OVERRIDE; + virtual void loadVisuallyCommitted() Q_DECL_OVERRIDE; +@@ -192,6 +193,7 @@ private: + QScopedPointer m_uIDelegatesManager; + QList m_userScripts; + qreal m_dpiScale; ++ QColor m_backgroundColor; + }; + + #ifndef QT_NO_ACCESSIBILITY +diff --git a/qtwebengine/src/webengine/render_widget_host_view_qt_delegate_quick.h b/src/webengine/render_widget_host_view_qt_delegate_quick.h +index ddd0e4d..eb2860b 100644 +--- a/qtwebengine/src/webengine/render_widget_host_view_qt_delegate_quick.h ++++ b/qtwebengine/src/webengine/render_widget_host_view_qt_delegate_quick.h +@@ -70,6 +70,8 @@ public: + virtual void move(const QPoint&) Q_DECL_OVERRIDE { } + virtual void inputMethodStateChanged(bool editorVisible) Q_DECL_OVERRIDE; + virtual void setTooltip(const QString&) Q_DECL_OVERRIDE { } ++ // The QtQuick view doesn't have a backbuffer of its own and doesn't need this ++ virtual void setClearColor(const QColor &) Q_DECL_OVERRIDE { } + + protected: + virtual void focusInEvent(QFocusEvent *event) Q_DECL_OVERRIDE; +diff --git a/qtwebengine/src/webengine/render_widget_host_view_qt_delegate_quickwindow.h b/src/webengine/render_widget_host_view_qt_delegate_quickwindow.h +index cda51a1..a4b0848 100644 +--- a/qtwebengine/src/webengine/render_widget_host_view_qt_delegate_quickwindow.h ++++ b/qtwebengine/src/webengine/render_widget_host_view_qt_delegate_quickwindow.h +@@ -73,6 +73,7 @@ public: + virtual void move(const QPoint &screenPos) Q_DECL_OVERRIDE; + virtual void inputMethodStateChanged(bool) Q_DECL_OVERRIDE {} + virtual void setTooltip(const QString &tooltip) Q_DECL_OVERRIDE; ++ virtual void setClearColor(const QColor &) Q_DECL_OVERRIDE { } + + private: + QScopedPointer m_realDelegate; +diff --git a/qtwebengine/src/webenginewidgets/api/qwebenginepage.cpp b/src/webenginewidgets/api/qwebenginepage.cpp +index 1b5a243..1cd7d6a 100644 +--- a/qtwebengine/src/webenginewidgets/api/qwebenginepage.cpp ++++ b/qtwebengine/src/webenginewidgets/api/qwebenginepage.cpp +@@ -185,6 +185,7 @@ QWebEnginePagePrivate::QWebEnginePagePrivate(QWebEngineProfile *_profile) + , view(0) + , isLoading(false) + , scriptCollection(new QWebEngineScriptCollectionPrivate(browserContextAdapter()->userScriptController(), adapter.data())) ++ , m_backgroundColor(Qt::white) + { + memset(actions, 0, sizeof(actions)); + } +@@ -247,6 +248,11 @@ qreal QWebEnginePagePrivate::dpiScale() const + return 1.0; + } + ++QColor QWebEnginePagePrivate::backgroundColor() const ++{ ++ return m_backgroundColor; ++} ++ + void QWebEnginePagePrivate::loadStarted(const QUrl &provisionalUrl, bool isErrorPage) + { + Q_UNUSED(provisionalUrl); +@@ -531,6 +537,33 @@ void QWebEnginePage::setWebChannel(QWebChannel *channel) + d->adapter->setWebChannel(channel); + } + ++/*! ++ \property QWebEnginePage::backgroundColor ++ \brief the page's background color, behing the document's body. ++ \since 5.6 ++ ++ You can set it to Qt::transparent or to a translucent ++ color to see through the document, or you can set this color to match your ++ web content in an hybrid app to prevent the white flashes that may appear ++ during loading. ++ ++ The default value is white. ++*/ ++QColor QWebEnginePage::backgroundColor() const ++{ ++ Q_D(const QWebEnginePage); ++ return d->m_backgroundColor; ++} ++ ++void QWebEnginePage::setBackgroundColor(const QColor &color) ++{ ++ Q_D(QWebEnginePage); ++ if (d->m_backgroundColor == color) ++ return; ++ d->m_backgroundColor = color; ++ d->adapter->backgroundColorChanged(); ++} ++ + void QWebEnginePage::setView(QWidget *view) + { + QWebEngineViewPrivate::bind(qobject_cast(view), this); +diff --git a/qtwebengine/src/webenginewidgets/api/qwebenginepage.h b/src/webenginewidgets/api/qwebenginepage.h +index b4cf522..57ed071 100644 +--- a/qtwebengine/src/webenginewidgets/api/qwebenginepage.h ++++ b/qtwebengine/src/webenginewidgets/api/qwebenginepage.h +@@ -98,6 +98,7 @@ class QWEBENGINEWIDGETS_EXPORT QWebEnginePage : public QObject { + Q_PROPERTY(QString title READ title) + Q_PROPERTY(QUrl url READ url WRITE setUrl) + Q_PROPERTY(QUrl iconUrl READ iconUrl) ++ Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor) + + public: + enum WebAction { +@@ -236,6 +237,8 @@ public: + + QWebChannel *webChannel() const; + void setWebChannel(QWebChannel *); ++ QColor backgroundColor() const; ++ void setBackgroundColor(const QColor &color); + + Q_SIGNALS: + void loadStarted(); +diff --git a/qtwebengine/src/webenginewidgets/api/qwebenginepage_p.h b/src/webenginewidgets/api/qwebenginepage_p.h +index 8f45ecd..7d74d3f 100644 +--- a/qtwebengine/src/webenginewidgets/api/qwebenginepage_p.h ++++ b/qtwebengine/src/webenginewidgets/api/qwebenginepage_p.h +@@ -118,6 +118,7 @@ public: + virtual void selectionChanged() Q_DECL_OVERRIDE; + virtual QRectF viewportRect() const Q_DECL_OVERRIDE; + virtual qreal dpiScale() const Q_DECL_OVERRIDE; ++ virtual QColor backgroundColor() const Q_DECL_OVERRIDE; + virtual void loadStarted(const QUrl &provisionalUrl, bool isErrorPage = false) Q_DECL_OVERRIDE; + virtual void loadCommitted() Q_DECL_OVERRIDE; + virtual void loadVisuallyCommitted() Q_DECL_OVERRIDE { } +@@ -170,6 +171,7 @@ public: + QtWebEngineCore::WebEngineContextMenuData m_menuData; + bool isLoading; + QWebEngineScriptCollection scriptCollection; ++ QColor m_backgroundColor; + + mutable CallbackDirectory m_callbacks; + mutable QAction *actions[QWebEnginePage::WebActionCount]; +diff --git a/qtwebengine/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp +index dba37ce..76ca8d3 100644 +--- a/qtwebengine/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp ++++ b/qtwebengine/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp +@@ -59,6 +59,7 @@ RenderWidgetHostViewQtDelegateWidget::RenderWidgetHostViewQtDelegateWidget(Rende + , m_rootNode(new QSGRootNode) + , m_sgEngine(new QSGEngine) + , m_isPopup(false) ++ , m_clearColor(Qt::white) + { + setFocusPolicy(Qt::StrongFocus); + +@@ -218,6 +219,19 @@ void RenderWidgetHostViewQtDelegateWidget::setTooltip(const QString &tooltip) + setToolTip(wrappedTip); + } + ++void RenderWidgetHostViewQtDelegateWidget::setClearColor(const QColor &color) ++{ ++ m_clearColor = color; ++ // QOpenGLWidget is usually blended by punching holes into widgets ++ // above it to simulate the visual stacking order. If we want it to be ++ // transparent we have to throw away the proper stacking order and always ++ // blend the complete normal widgets backing store under it. ++ bool isTranslucent = color.alpha() < 255; ++ setAttribute(Qt::WA_AlwaysStackOnTop, isTranslucent); ++ setAttribute(Qt::WA_OpaquePaintEvent, !isTranslucent); ++ update(); ++} ++ + QVariant RenderWidgetHostViewQtDelegateWidget::inputMethodQuery(Qt::InputMethodQuery query) const + { + return m_client->inputMethodQuery(query); +@@ -270,7 +284,7 @@ void RenderWidgetHostViewQtDelegateWidget::initializeGL() + m_sgEngine->initialize(QOpenGLContext::currentContext()); + m_sgRenderer.reset(m_sgEngine->createRenderer()); + m_sgRenderer->setRootNode(m_rootNode.data()); +- m_sgRenderer->setClearColor(Qt::white); ++ m_sgRenderer->setClearColor(m_clearColor); + } + + void RenderWidgetHostViewQtDelegateWidget::paintGL() +diff --git a/qtwebengine/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.h b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.h +index d0dfdc6..d228bd4 100644 +--- a/qtwebengine/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.h ++++ b/qtwebengine/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.h +@@ -78,6 +78,7 @@ public: + virtual void move(const QPoint &screenPos) Q_DECL_OVERRIDE; + virtual void inputMethodStateChanged(bool editorVisible) Q_DECL_OVERRIDE; + virtual void setTooltip(const QString &tooltip) Q_DECL_OVERRIDE; ++ virtual void setClearColor(const QColor &color) Q_DECL_OVERRIDE; + + protected: + bool event(QEvent *event) Q_DECL_OVERRIDE; +@@ -98,6 +99,7 @@ private: + QScopedPointer m_sgEngine; + QScopedPointer m_sgRenderer; + bool m_isPopup; ++ QColor m_clearColor; + QList m_windowConnections; + }; + +-- +2.3.3 + + diff --git a/qt-patches/0002-qtbase-Don-t-show-the-menu-bar-at-all-in-lion-style-fullscr.patch b/qt-patches/0002-qtbase-Don-t-show-the-menu-bar-at-all-in-lion-style-fullscr.patch new file mode 100644 index 0000000..d361b42 --- /dev/null +++ b/qt-patches/0002-qtbase-Don-t-show-the-menu-bar-at-all-in-lion-style-fullscr.patch @@ -0,0 +1,27 @@ +From d04cee1adfd944d724c4b8777638c9319f53f443 Mon Sep 17 00:00:00 2001 +From: Jocelyn Turcotte +Date: Thu, 23 Jul 2015 11:56:37 +0200 +Subject: [PATCH] Don't show the menu bar at all in lion-style fullscreen + +This works around a bug in OSX where the menubar could +appear and not go away when losing the connection to a +single main display, which triggers the menu bar to be resized. +--- + qtbase/src/plugins/platforms/cocoa/qnswindowdelegate.mm | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/qtbase/src/plugins/platforms/cocoa/qnswindowdelegate.mm b/qtbase/src/plugins/platforms/cocoa/qnswindowdelegate.mm +index 015274c..37a481a 100644 +--- a/qtbase/src/plugins/platforms/cocoa/qnswindowdelegate.mm ++++ b/qtbase/src/plugins/platforms/cocoa/qnswindowdelegate.mm +@@ -109,4 +109,7 @@ + return YES; + } + ++- (NSApplicationPresentationOptions)window:(NSWindow *)window willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions { ++ return NSApplicationPresentationFullScreen | NSApplicationPresentationHideMenuBar | NSApplicationPresentationHideDock; ++} + @end +-- +2.3.3 + diff --git a/qt-patches/0003-Always-enable-viewport-stuff.patch b/qt-patches/0003-Always-enable-viewport-stuff.patch new file mode 100644 index 0000000..432fce8 --- /dev/null +++ b/qt-patches/0003-Always-enable-viewport-stuff.patch @@ -0,0 +1,29 @@ +From 6ca3c5f3430043074f7299cb1c3ba7f453ff7b5f Mon Sep 17 00:00:00 2001 +From: Vincent Lang +Date: Wed, 29 Jul 2015 21:27:34 +0200 +Subject: [PATCH] Always enable viewport stuff + +Normally, applications can configure this by changing the command +line passed to QApplication/QtWebEngine, but this doesn't work on +Windows. So we change these directly. +--- + src/core/web_engine_context.cpp | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/qtwebengine/src/core/web_engine_context.cpp b/qtwebengine/src/core/web_engine_context.cpp +index 8bc0473..88b6245 100644 +--- a/qtwebengine/src/core/web_engine_context.cpp ++++ b/qtwebengine/src/core/web_engine_context.cpp +@@ -231,6 +231,9 @@ WebEngineContext::WebEngineContext() + parsedCommandLine->AppendSwitchASCII(switches::kProfilerTiming, switches::kProfilerTimingDisabledValue); + #endif + ++ parsedCommandLine->AppendSwitch(switches::kEnableViewport); ++ parsedCommandLine->AppendSwitch(switches::kEnableViewportMeta); ++ + GLContextHelper::initialize(); + + if (usingANGLE() || usingSoftwareDynamicGL() || usingQtQuick2DRenderer()) { +-- +2.4.6 + diff --git a/qt-patches/0004-qtwebengine-transparency-window-creation.patch b/qt-patches/0004-qtwebengine-transparency-window-creation.patch new file mode 100644 index 0000000..3c13209 --- /dev/null +++ b/qt-patches/0004-qtwebengine-transparency-window-creation.patch @@ -0,0 +1,44 @@ +rom e62ab19d897155bb10f4c7a2a99b901b458361b5 Mon Sep 17 00:00:00 2001 +From: Jocelyn Turcotte +Date: Mon, 20 Jul 2015 12:06:59 +0200 +Subject: [PATCH] Fix a crash on new windows + +Fixes a regression of 99e98f7bf6aec78fe0d647fb898e65d13ff522e4 +by delaying applying the background color if the client hasn't +been set on the WebContentsViewQt yet. + +Task-number: QTBUG-41960 +Change-Id: I1c138777f616541179976570d8c29f8f4d49ecbf +--- + qtwebengine/src/core/web_contents_view_qt.cpp | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/qtwebengine/src/core/web_contents_view_qt.cpp b/src/core/web_contents_view_qt.cpp +index aad44c1..c327534 100644 +--- a/qtwebengine/src/core/web_contents_view_qt.cpp ++++ b/qtwebengine/src/core/web_contents_view_qt.cpp +@@ -53,8 +53,10 @@ void WebContentsViewQt::initialize(WebContentsAdapterClient* client) + m_factoryClient = client; + + // Check if a RWHV was created before the initialization. +- if (m_webContents->GetRenderWidgetHostView()) +- static_cast(m_webContents->GetRenderWidgetHostView())->setAdapterClient(client); ++ if (auto rwhv = static_cast(m_webContents->GetRenderWidgetHostView())) { ++ rwhv->setAdapterClient(client); ++ rwhv->SetBackgroundColor(toSk(client->backgroundColor())); ++ } + } + + content::RenderWidgetHostViewBase* WebContentsViewQt::CreateViewForWidget(content::RenderWidgetHost* render_widget_host, bool is_guest_view_hack) +@@ -86,7 +88,8 @@ void WebContentsViewQt::RenderViewCreated(content::RenderViewHost* host) + { + // The render process is done creating the RenderView and it's ready to be routed + // messages at this point. +- host->GetView()->SetBackgroundColor(toSk(m_client->backgroundColor())); ++ if (m_client) ++ host->GetView()->SetBackgroundColor(toSk(m_client->backgroundColor())); + } + + void WebContentsViewQt::CreateView(const gfx::Size& initial_size, gfx::NativeView context) +-- +2.3.3 diff --git a/qt-patches/README.md b/qt-patches/README.md new file mode 100644 index 0000000..14441a7 --- /dev/null +++ b/qt-patches/README.md @@ -0,0 +1,14 @@ +In this directory we store all patches against Qt that is required by konvergo. The patches are: + +* 0001-qtwebengine-Add-a-backgroundColor-property.patch - Which adds the backgroundColor property to + the webengineview. Without this patch playback will not be rendered correctly. Has been submitted upstream + and will most likely be included in Qt 5.6 +* 0002-qtbase-Don-t-show-the-menu-bar-at-all-in-lion-style-fullscr.patch - This patch hids the menu bar + when we go into fullscreen mode on OS X. Without this patch the menu bar can show up when it's not wanted + (for example switching input on your TV). Has not been submitted upstream since it doesn't really fit + for other applications. +* 0003-Always-enable-viewport-stuff.patch - This patch makes sure that the viewport is scalable on windows. + Without this one the UI will be rendered in the upper left corner way to small. Has not been submitted + upstream since it enables a feature that was intentionally disabled in desktop versions of Qt. +* 0004-qtwebengine-transparency-window-creation.patch - Fixes a bug in patch 0001. Will be included at the same + time as patch 0001. \ No newline at end of file diff --git a/qt-patches/qt-5.6-alpha/0001-Windows-Add-checks-to-usages-of-QWindow-screen.patch b/qt-patches/qt-5.6-alpha/0001-Windows-Add-checks-to-usages-of-QWindow-screen.patch new file mode 100644 index 0000000..073962c --- /dev/null +++ b/qt-patches/qt-5.6-alpha/0001-Windows-Add-checks-to-usages-of-QWindow-screen.patch @@ -0,0 +1,120 @@ +From 78b2719b04f1177b5af0f52fce98316505f9000b Mon Sep 17 00:00:00 2001 +From: Friedemann Kleint +Date: Tue, 22 Sep 2015 12:21:29 +0200 +Subject: [PATCH] Windows: Add checks to usages of QWindow::screen(). + +Default to primary screen and handle situations where the screen +is null consistently. Remove unused QWindowsScreen::screenOf() method. + +Task-number: QTBUG-48288 +Change-Id: I91b3c2331521d9d3be8ac77606ee820cd35ebb0f +Reviewed-by: Joerg Bornemann +--- + src/plugins/platforms/windows/qwindowsmousehandler.cpp | 14 ++++++++++++-- + src/plugins/platforms/windows/qwindowsscreen.cpp | 12 ------------ + src/plugins/platforms/windows/qwindowsscreen.h | 2 -- + src/plugins/platforms/windows/qwindowswindow.cpp | 12 +++++++++--- + 4 files changed, 21 insertions(+), 19 deletions(-) + +diff --git a/src/plugins/platforms/windows/qwindowsmousehandler.cpp b/src/plugins/platforms/windows/qwindowsmousehandler.cpp +index 4ab861a..90cb6fe 100644 +--- a/src/plugins/platforms/windows/qwindowsmousehandler.cpp ++++ b/src/plugins/platforms/windows/qwindowsmousehandler.cpp +@@ -477,7 +477,12 @@ bool QWindowsMouseHandler::translateTouchEvent(QWindow *window, HWND, + typedef QList QTouchPointList; + + Q_ASSERT(m_touchDevice); +- const QRect screenGeometry = window->screen()->geometry(); ++ const QScreen *screen = window->screen(); ++ if (!screen) ++ screen = QGuiApplication::primaryScreen(); ++ if (!screen) ++ return true; ++ const QRect screenGeometry = screen->geometry(); + + const int winTouchPointCount = msg.wParam; + QScopedArrayPointer winTouchInputs(new TOUCHINPUT[winTouchPointCount]); +@@ -569,7 +574,12 @@ bool QWindowsMouseHandler::translateGestureEvent(QWindow *window, HWND hwnd, + if (gi.dwID != GID_DIRECTMANIPULATION) + return true; + static QPoint lastTouchPos; +- const QRect screenGeometry = window->screen()->geometry(); ++ const QScreen *screen = window->screen(); ++ if (!screen) ++ screen = QGuiApplication::primaryScreen(); ++ if (!screen) ++ return true; ++ const QRect screenGeometry = screen->geometry(); + QWindowSystemInterface::TouchPoint touchPoint; + static QWindowSystemInterface::TouchPoint touchPoint2; + touchPoint.id = 0;//gi.dwInstanceID; +diff --git a/src/plugins/platforms/windows/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp +index 391735a..e6abfb2 100644 +--- a/src/plugins/platforms/windows/qwindowsscreen.cpp ++++ b/src/plugins/platforms/windows/qwindowsscreen.cpp +@@ -276,18 +276,6 @@ QWindow *QWindowsScreen::windowAt(const QPoint &screenPoint, unsigned flags) + return result; + } + +-QWindowsScreen *QWindowsScreen::screenOf(const QWindow *w) +-{ +- if (w) +- if (const QScreen *s = w->screen()) +- if (QPlatformScreen *pscr = s->handle()) +- return static_cast(pscr); +- if (const QScreen *ps = QGuiApplication::primaryScreen()) +- if (QPlatformScreen *ppscr = ps->handle()) +- return static_cast(ppscr); +- return 0; +-} +- + qreal QWindowsScreen::pixelDensity() const + { + const qreal physicalDpi = m_data.geometry.width() / m_data.physicalSizeMM.width() * qreal(25.4); +diff --git a/src/plugins/platforms/windows/qwindowsscreen.h b/src/plugins/platforms/windows/qwindowsscreen.h +index 67e7ff6..bc8fbf5 100644 +--- a/src/plugins/platforms/windows/qwindowsscreen.h ++++ b/src/plugins/platforms/windows/qwindowsscreen.h +@@ -79,8 +79,6 @@ public: + + explicit QWindowsScreen(const QWindowsScreenData &data); + +- static QWindowsScreen *screenOf(const QWindow *w = 0); +- + QRect geometry() const Q_DECL_OVERRIDE { return m_data.geometry; } + QRect availableGeometry() const Q_DECL_OVERRIDE { return m_data.availableGeometry; } + int depth() const Q_DECL_OVERRIDE { return m_data.depth; } +diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp +index 5318360..abfddcf 100644 +--- a/src/plugins/platforms/windows/qwindowswindow.cpp ++++ b/src/plugins/platforms/windows/qwindowswindow.cpp +@@ -1637,8 +1637,12 @@ void QWindowsWindow::setWindowState(Qt::WindowState state) + bool QWindowsWindow::isFullScreen_sys() const + { + const QWindow *w = window(); +- return w->isTopLevel() +- && geometry_sys() == QHighDpi::toNativePixels(w->screen()->geometry(), w); ++ if (!w->isTopLevel()) ++ return false; ++ const QScreen *screen = w->screen(); ++ if (!screen) ++ screen = QGuiApplication::primaryScreen(); ++ return screen && geometry_sys() == QHighDpi::toNativePixels(screen->geometry(), w); + } + + /*! +@@ -1708,7 +1712,9 @@ void QWindowsWindow::setWindowState_sys(Qt::WindowState newState) + // Use geometry of QWindow::screen() within creation or the virtual screen the + // window is in (QTBUG-31166, QTBUG-30724). + const QScreen *screen = window()->screen(); +- const QRect r = QHighDpi::toNativePixels(screen->geometry(), window()); ++ if (!screen) ++ screen = QGuiApplication::primaryScreen(); ++ const QRect r = screen ? QHighDpi::toNativePixels(screen->geometry(), window()) : m_savedFrameGeometry; + const UINT swpf = SWP_FRAMECHANGED | SWP_NOACTIVATE; + const bool wasSync = testFlag(SynchronousGeometryChangeEvent); + setFlag(SynchronousGeometryChangeEvent); +-- +2.5.1 + + diff --git a/qt-patches/qt-5.6-alpha/0002-windows-Improve-error-handling-with-EGL.patch b/qt-patches/qt-5.6-alpha/0002-windows-Improve-error-handling-with-EGL.patch new file mode 100644 index 0000000..f4e734c --- /dev/null +++ b/qt-patches/qt-5.6-alpha/0002-windows-Improve-error-handling-with-EGL.patch @@ -0,0 +1,151 @@ +From 867fc30c7c9be51b4b3813d2cc558b5a9df0256d Mon Sep 17 00:00:00 2001 +From: Laszlo Agocs +Date: Wed, 23 Sep 2015 14:38:48 +0200 +Subject: [PATCH] windows: Improve error handling with EGL + +When disabling the graphics adapter, things start failing +with context lost and then bad_alloc and bad_access failures +when creating contexts/window surfaces. + +Swap buffers now handles context loss. This makes it possible +for Qt Quick to act when the graphics adapter goes away. + +Similarly, the window surface creation failure EGL_BAD_ACCESS +is treated the same way as context loss. + +Note that this will not really help the main issue, because +rendering is not possible without a GPU (reinit attempts will +fail either at context creation or window surface creation), +but proper logging and context loss reporting improves the +situation somewhat. + +Also unify and prettyify the warning and debug prints. +This makes it easier to understand what is going on. + +Change-Id: Iec3a9b54f1134e78e87eefcf938525283ec9412a +Task-number: QTBUG-48095 +Reviewed-by: Friedemann Kleint +--- + .../platforms/windows/qwindowseglcontext.cpp | 43 ++++++++++++++++------ + 1 file changed, 31 insertions(+), 12 deletions(-) + +diff --git a/src/plugins/platforms/windows/qwindowseglcontext.cpp b/src/plugins/platforms/windows/qwindowseglcontext.cpp +index 06c9985..e4ec3f3 100644 +--- a/src/plugins/platforms/windows/qwindowseglcontext.cpp ++++ b/src/plugins/platforms/windows/qwindowseglcontext.cpp +@@ -350,16 +350,16 @@ QWindowsEGLStaticContext *QWindowsEGLStaticContext::create(QWindowsOpenGLTester: + { + const HDC dc = QWindowsContext::instance()->displayContext(); + if (!dc){ +- qWarning("%s: No Display", Q_FUNC_INFO); ++ qWarning("%s: No Display", __FUNCTION__); + return 0; + } + + if (!libEGL.init()) { +- qWarning("%s: Failed to load and resolve libEGL functions", Q_FUNC_INFO); ++ qWarning("%s: Failed to load and resolve libEGL functions", __FUNCTION__); + return 0; + } + if (!libGLESv2.init()) { +- qWarning("%s: Failed to load and resolve libGLESv2 functions", Q_FUNC_INFO); ++ qWarning("%s: Failed to load and resolve libGLESv2 functions", __FUNCTION__); + return 0; + } + +@@ -396,15 +396,15 @@ QWindowsEGLStaticContext *QWindowsEGLStaticContext::create(QWindowsOpenGLTester: + if (display == EGL_NO_DISPLAY) + display = libEGL.eglGetDisplay((EGLNativeDisplayType)dc); + if (!display) { +- qWarning("%s: Could not obtain EGL display", Q_FUNC_INFO); ++ qWarning("%s: Could not obtain EGL display", __FUNCTION__); + return 0; + } + + if (!major && !libEGL.eglInitialize(display, &major, &minor)) { + int err = libEGL.eglGetError(); +- qWarning("%s: Could not initialize EGL display: error 0x%x\n", Q_FUNC_INFO, err); ++ qWarning("%s: Could not initialize EGL display: error 0x%x", __FUNCTION__, err); + if (err == 0x3001) +- qWarning("%s: When using ANGLE, check if d3dcompiler_4x.dll is available", Q_FUNC_INFO); ++ qWarning("%s: When using ANGLE, check if d3dcompiler_4x.dll is available", __FUNCTION__); + return 0; + } + +@@ -430,7 +430,7 @@ void *QWindowsEGLStaticContext::createWindowSurface(void *nativeWindow, void *na + (EGLNativeWindowType) nativeWindow, 0); + if (surface == EGL_NO_SURFACE) { + *err = libEGL.eglGetError(); +- qWarning("%s: Could not create the EGL window surface: 0x%x\n", Q_FUNC_INFO, *err); ++ qWarning("%s: Could not create the EGL window surface: 0x%x", __FUNCTION__, *err); + } + + return surface; +@@ -533,7 +533,12 @@ QWindowsEGLContext::QWindowsEGLContext(QWindowsEGLStaticContext *staticContext, + } + + if (m_eglContext == EGL_NO_CONTEXT) { +- qWarning("QWindowsEGLContext: eglError: %x, this: %p \n", QWindowsEGLStaticContext::libEGL.eglGetError(), this); ++ int err = QWindowsEGLStaticContext::libEGL.eglGetError(); ++ qWarning("QWindowsEGLContext: Failed to create context, eglError: %x, this: %p", err, this); ++ // ANGLE gives bad alloc when it fails to reset a previously lost D3D device. ++ // A common cause for this is disabling the graphics adapter used by the app. ++ if (err == EGL_BAD_ALLOC) ++ qWarning("QWindowsEGLContext: Graphics device lost. (Did the adapter get disabled?)"); + return; + } + +@@ -594,6 +599,12 @@ bool QWindowsEGLContext::makeCurrent(QPlatformSurface *surface) + if (err == EGL_CONTEXT_LOST) { + m_eglContext = EGL_NO_CONTEXT; + qCDebug(lcQpaGl) << "Got EGL context lost in createWindowSurface() for context" << this; ++ } else if (err == EGL_BAD_ACCESS) { ++ // With ANGLE this means no (D3D) device and can happen when disabling/changing graphics adapters. ++ qCDebug(lcQpaGl) << "Bad access (missing device?) in createWindowSurface() for context" << this; ++ // Simulate context loss as the context is useless. ++ QWindowsEGLStaticContext::libEGL.eglDestroyContext(m_eglDisplay, m_eglContext); ++ m_eglContext = EGL_NO_CONTEXT; + } + return false; + } +@@ -623,7 +634,7 @@ bool QWindowsEGLContext::makeCurrent(QPlatformSurface *surface) + // Drop the surface. Will recreate on the next makeCurrent. + window->invalidateSurface(); + } else { +- qWarning("QWindowsEGLContext::makeCurrent: eglError: %x, this: %p \n", err, this); ++ qWarning("%s: Failed to make surface current. eglError: %x, this: %p", __FUNCTION__, err, this); + } + } + +@@ -635,7 +646,8 @@ void QWindowsEGLContext::doneCurrent() + QWindowsEGLStaticContext::libEGL.eglBindAPI(m_api); + bool ok = QWindowsEGLStaticContext::libEGL.eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (!ok) +- qWarning("QWindowsEGLContext::doneCurrent: eglError: %d, this: %p \n", QWindowsEGLStaticContext::libEGL.eglGetError(), this); ++ qWarning("%s: Failed to make no context/surface current. eglError: %d, this: %p", __FUNCTION__, ++ QWindowsEGLStaticContext::libEGL.eglGetError(), this); + } + + void QWindowsEGLContext::swapBuffers(QPlatformSurface *surface) +@@ -653,8 +665,15 @@ void QWindowsEGLContext::swapBuffers(QPlatformSurface *surface) + } + + bool ok = QWindowsEGLStaticContext::libEGL.eglSwapBuffers(m_eglDisplay, eglSurface); +- if (!ok) +- qWarning("QWindowsEGLContext::swapBuffers: eglError: %d, this: %p \n", QWindowsEGLStaticContext::libEGL.eglGetError(), this); ++ if (!ok) { ++ err = QWindowsEGLStaticContext::libEGL.eglGetError(); ++ if (err == EGL_CONTEXT_LOST) { ++ m_eglContext = EGL_NO_CONTEXT; ++ qCDebug(lcQpaGl) << "Got EGL context lost in eglSwapBuffers()"; ++ } else { ++ qWarning("%s: Failed to swap buffers. eglError: %d, this: %p", __FUNCTION__, err, this); ++ } ++ } + } + + QFunctionPointer QWindowsEGLContext::getProcAddress(const QByteArray &procName) +-- +2.5.1 + + diff --git a/qt-patches/qt-5.6-alpha/0003-QPlatformWindow-Extract-static-method-for-closestAcc.patch b/qt-patches/qt-5.6-alpha/0003-QPlatformWindow-Extract-static-method-for-closestAcc.patch new file mode 100644 index 0000000..659ec8c --- /dev/null +++ b/qt-patches/qt-5.6-alpha/0003-QPlatformWindow-Extract-static-method-for-closestAcc.patch @@ -0,0 +1,85 @@ +From 42c6ea4f6c29845d68e865b19a73a09cc6d0bdbe Mon Sep 17 00:00:00 2001 +From: Friedemann Kleint +Date: Tue, 15 Sep 2015 12:12:51 +0200 +Subject: [PATCH] QPlatformWindow: Extract static method for + closestAcceptableGeometry(). + +On Windows, some messages occur before a QPlatformWindow is actually +created, for example WM_WINDOWPOSCHANGING, which is handled in +QWindowsWindow::handleGeometryChangingMessage(). + +Extract a static function QPlatformWindow::closestAcceptableGeometry() +from QPlatformWindow::windowClosestAcceptableGeometry() and use +that in QWindowsWindow::handleGeometryChangingMessage(). + +This fixes a regression crash occurring in Qt 5.6 when running +the example from QTBUG-48201. + +Task-number: QTBUG-36220 +Task-number: QTBUG-48201 +Task-number: QTBUG-46615 +Change-Id: I86b8f923447c8e447382427cf5795628ef1c9717 +Reviewed-by: Joerg Bornemann +--- + src/gui/kernel/qplatformwindow.cpp | 11 ++++++++--- + src/gui/kernel/qplatformwindow.h | 1 + + src/plugins/platforms/windows/qwindowswindow.cpp | 2 +- + 3 files changed, 10 insertions(+), 4 deletions(-) + +diff --git a/src/gui/kernel/qplatformwindow.cpp b/src/gui/kernel/qplatformwindow.cpp +index 0430d5a..ea3b75c 100644 +--- a/src/gui/kernel/qplatformwindow.cpp ++++ b/src/gui/kernel/qplatformwindow.cpp +@@ -702,15 +702,20 @@ QRect QPlatformWindow::windowFrameGeometry() const + a resize/move event for platforms that support it, for example to + implement heightForWidth(). + */ +-QRectF QPlatformWindow::windowClosestAcceptableGeometry(const QRectF &nativeRect) const ++ ++QRectF QPlatformWindow::closestAcceptableGeometry(const QWindow *qWindow, const QRectF &nativeRect) + { +- QWindow *qWindow = window(); + const QRectF rectF = QHighDpi::fromNativePixels(nativeRect, qWindow); +- const QRectF correctedGeometryF = qt_window_private(qWindow)->closestAcceptableGeometry(rectF); ++ const QRectF correctedGeometryF = qt_window_private(const_cast(qWindow))->closestAcceptableGeometry(rectF); + return !correctedGeometryF.isEmpty() && rectF != correctedGeometryF + ? QHighDpi::toNativePixels(correctedGeometryF, qWindow) : nativeRect; + } + ++QRectF QPlatformWindow::windowClosestAcceptableGeometry(const QRectF &nativeRect) const ++{ ++ return QPlatformWindow::closestAcceptableGeometry(window(), nativeRect); ++} ++ + /*! + \class QPlatformWindow + \since 4.8 +diff --git a/src/gui/kernel/qplatformwindow.h b/src/gui/kernel/qplatformwindow.h +index 1b283db..9c28179 100644 +--- a/src/gui/kernel/qplatformwindow.h ++++ b/src/gui/kernel/qplatformwindow.h +@@ -140,6 +140,7 @@ public: + QRect windowGeometry() const; + QRect windowFrameGeometry() const; + QRectF windowClosestAcceptableGeometry(const QRectF &nativeRect) const; ++ static QRectF closestAcceptableGeometry(const QWindow *w, const QRectF &nativeRect); + + protected: + static QString formatWindowTitle(const QString &title, const QString &separator); +diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp +index f9b3433..5318360 100644 +--- a/src/plugins/platforms/windows/qwindowswindow.cpp ++++ b/src/plugins/platforms/windows/qwindowswindow.cpp +@@ -1825,7 +1825,7 @@ bool QWindowsWindow::handleGeometryChangingMessage(MSG *message, const QWindow * + const QRect suggestedFrameGeometry(windowPos->x, windowPos->y, + windowPos->cx, windowPos->cy); + const QRect suggestedGeometry = suggestedFrameGeometry - margins; +- const QRectF correctedGeometryF = qWindow->handle()->windowClosestAcceptableGeometry(suggestedGeometry); ++ const QRectF correctedGeometryF = QPlatformWindow::closestAcceptableGeometry(qWindow, suggestedGeometry); + if (!correctedGeometryF.isValid()) + return false; + const QRect correctedFrameGeometry = correctedGeometryF.toRect() + margins; +-- +2.5.1 + + diff --git a/qt-patches/qt-5.6-alpha/0004-chromium-create-wakeup-pipe-with-O_CLOEXEC.patch b/qt-patches/qt-5.6-alpha/0004-chromium-create-wakeup-pipe-with-O_CLOEXEC.patch new file mode 100644 index 0000000..2ababbc --- /dev/null +++ b/qt-patches/qt-5.6-alpha/0004-chromium-create-wakeup-pipe-with-O_CLOEXEC.patch @@ -0,0 +1,40 @@ +From af4993f6ad442816be12ad609544ac56e2ed4cee Mon Sep 17 00:00:00 2001 +From: Vincent Lang +Date: Mon, 12 Oct 2015 19:24:54 +0200 +Subject: [PATCH] chromium: create wakeup pipe with O_CLOEXEC + +Since the sandbox thread is terminated by closing the pipe, forked +processes could keep the pipe alive for a longer time than intended, +and freeze RenderSandboxHostLinux::~RenderSandboxHostLinux(). + +This is all Linux-specific code, so use the glibc-specific pipe2() +function. +--- + .../content/browser/renderer_host/render_sandbox_host_linux.cc | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/src/3rdparty/chromium/content/browser/renderer_host/render_sandbox_host_linux.cc b/src/3rdparty/chromium/content/browser/renderer_host/render_sandbox_host_linux.cc +index 5b26054..c7b3cdd 100644 +--- a/src/3rdparty/chromium/content/browser/renderer_host/render_sandbox_host_linux.cc ++++ b/src/3rdparty/chromium/content/browser/renderer_host/render_sandbox_host_linux.cc +@@ -5,6 +5,8 @@ + #include "content/browser/renderer_host/render_sandbox_host_linux.h" + + #include ++#include ++#include + + #include "base/memory/singleton.h" + #include "base/posix/eintr_wrapper.h" +@@ -45,7 +47,7 @@ void RenderSandboxHostLinux::Init() { + PCHECK(0 == shutdown(browser_socket, SHUT_WR)) << "shutdown"; + + int pipefds[2]; +- CHECK(0 == pipe(pipefds)); ++ CHECK(0 == pipe2(pipefds, O_CLOEXEC)); + const int child_lifeline_fd = pipefds[0]; + childs_lifeline_fd_ = pipefds[1]; + +-- +2.5.1 + diff --git a/resources/images/icon.png b/resources/images/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..2ebea44afb8f42d95a4decdc3b4d77181758d878 GIT binary patch literal 40769 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 VuF*rGwLt&?002ovPDHLkV1j*HXNLd) literal 0 HcmV?d00001 diff --git a/resources/images/splash.png b/resources/images/splash.png new file mode 100644 index 0000000000000000000000000000000000000000..8054585da20ac0c6f58e3a07239a2ad68a708fed GIT binary patch literal 6789 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 iscfGyyNaE + + + plex-logo-light + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/inputmaps/LIRC.json b/resources/inputmaps/LIRC.json new file mode 100644 index 0000000..68dd8c3 --- /dev/null +++ b/resources/inputmaps/LIRC.json @@ -0,0 +1,33 @@ +{ + "name": "LIRC IR", + "idmatcher": "LIRC*", + "mapping": + { + "KEY_LEFT": "left", + "KEY_RIGHT": "right", + "KEY_UP": "up", + "KEY_DOWN": "down", + "KEY_MENU": "unknown", + "KEY_SELECT": "enter", + "KEY_OK": "enter", + "KEY_BACK": "back", + "KEY_EXIT": "back", + "KEY_RED": "red", + "KEY_GREEN": "green", + "KEY_YELLOW": "yellow", + "KEY_BLUE": "blue", + "KEY_PLAY": "play", + "KEY_STOP": "stop", + "KEY_PAUSE": "pause", + "KEY_SEEKFWD": "seek_forward", + "KEY_FASTFORWARD" : "seek_forward", + "KEY_SEEKBCK": "seek_backward", + "KEY_REWIND": "seek_backward", + "KEY_SUBTITLES": "cycle_subtitles", + "KEY_INFO": "unknown", + "KEY_NEXT": "step_forward", + "KEY_PREV": "step_backward", + "KEY_HOME": "home", + "KEY_NUMERIC_([0-9])": "%1" + } +} diff --git a/resources/inputmaps/apple-media-keys.json b/resources/inputmaps/apple-media-keys.json new file mode 100644 index 0000000..192a4c7 --- /dev/null +++ b/resources/inputmaps/apple-media-keys.json @@ -0,0 +1,12 @@ +{ + "name": "Apple Media Keys", + "idmatcher": "AppleMediaKeys.*", + "mapping": + { + "KEY_PLAY": "play_pause", + "KEY_NEXT": "step_forward", + "KEY_PREV": "step_backward", + "KEY_FAST": "step_forward", + "KEY_REWIND": "step_backward" + } +} diff --git a/resources/inputmaps/apple-remote.json b/resources/inputmaps/apple-remote.json new file mode 100644 index 0000000..259d0a6 --- /dev/null +++ b/resources/inputmaps/apple-remote.json @@ -0,0 +1,19 @@ +{ + "name": "Apple Remote", + "idmatcher": "AppleRemote.*", + "mapping": + { + "KEY_LEFT": "left", + "KEY_LEFT_LONG": "cycle_subtitles_back", + "KEY_RIGHT": "right", + "KEY_RIGHT_LONG": "cycle_subtitles", + "KEY_UP": "up", + "KEY_UP_LONG": "cycle_audio", + "KEY_DOWN": "down", + "KEY_DOWN_LONG": "cycle_audio_back", + "KEY_SELECT": "enter", + "KEY_SELECT_LONG": "host:toggleDebug", + "KEY_MENU": "back", + "KEY_MENU_LONG": "home" + } +} \ No newline at end of file diff --git a/resources/inputmaps/cec.json b/resources/inputmaps/cec.json new file mode 100644 index 0000000..02bc875 --- /dev/null +++ b/resources/inputmaps/cec.json @@ -0,0 +1,30 @@ +{ + "name": "HDMI CEC", + "idmatcher": "CEC.*", + "mapping": + { + "KEY_LEFT": "left", + "KEY_RIGHT": "right", + "KEY_UP": "up", + "KEY_DOWN": "down", + "KEY_MENU": "unknown", + "KEY_SELECT": "enter", + "KEY_BACK": "back", + "KEY_RED": "red", + "KEY_GREEN": "green", + "KEY_YELLOW": "yellow", + "KEY_BLUE": "blue", + "KEY_PLAY": "play", + "KEY_STOP": "stop", + "KEY_PAUSE": "pause", + "KEY_SEEKFWD": "seek_forward", + "KEY_SEEKBCK": "seek_backward", + "KEY_SUBTITLES": "cycle_subtitles", + "KEY_INFO": "unknown", + "KEY_NEXT": "step_forward", + "KEY_PREV": "step_backward", + "KEY_HOME": "home", + "KEY_NUMERIC_([0-9])": "%1", + "KEY_GUIDE": "guide" + } +} \ No newline at end of file diff --git a/resources/inputmaps/keyboard.json b/resources/inputmaps/keyboard.json new file mode 100644 index 0000000..a747c8c --- /dev/null +++ b/resources/inputmaps/keyboard.json @@ -0,0 +1,44 @@ +{ + "name": "Keyboard Generic", + "idmatcher": "Keyboard.*", + "mapping": + { + // standard navigation, we allow num and shift modifiers + "(Num\\+|Shift\\+)?Left": "left", + "(Num\\+|Shift\\+)?Right": "right", + "(Num\\+|Shift\\+)?Up": "up", + "(Num\\+|Shift\\+)?Down": "down", + "(Num\\+)?(Return|Enter|Space)": "enter", + "(Esc|Backspace)": "back", + + // map Num+X and Shift+X to X. This allows normal number input + "(?:Num\\+|Shift\\+)?([0-9])": "%1", + + // map Shift+Letter to action jump+letter + "Shift\\+([A-Z])": "jump+%1", + + "P": "play_pause", + "Ctrl\\+P": "pause", + "X": "stop", + "B": "back", + "H": "home", + "A": "cycle_audio", + "Ctrl\\+A": "cycle_audio_back", + "L": "cycle_subtitles", + "Ctrl\\+L": "cycle_subtitles_back", + "S": "toggle_subtitles", + "Alt\\+A": "increase_audio_delay", + "Alt+Shift\\+A": "decrease_audio_delay", + "Alt\\+S": "increase_subtitles_delay", + "Alt\\+Shift\\+S": "decrease_subtitles_delay", + + // application shortcuts + "Ctrl\\+F": "host:fullscreen", + "Ctrl\\+Q": "host:close", + "Ctrl\\+Shift\\+R": "host:reload", + "Ctrl\\+Shift\\+D": "host:toggleDebug", + + // debug command to crash the host. for testing + "Ctrl\\+Alt\\+Shift\\+F1": "host:crash!" + } +} diff --git a/resources/inputmaps/wiimote.json b/resources/inputmaps/wiimote.json new file mode 100644 index 0000000..e381369 --- /dev/null +++ b/resources/inputmaps/wiimote.json @@ -0,0 +1,13 @@ +{ + "name": "Wiimote", + "idmatcher": "Wiimote.*", + "mapping": + { + "KEY_BUTTON_1": "up", + "KEY_BUTTON_3": "right", + "KEY_BUTTON_0": "down", + "KEY_BUTTON_2": "left", + "KEY_BUTTON_10": "enter", + "KEY_BUTTON_9": "back" + } +} diff --git a/resources/inputmaps/xbox-controller.json b/resources/inputmaps/xbox-controller.json new file mode 100644 index 0000000..1ac9e88 --- /dev/null +++ b/resources/inputmaps/xbox-controller.json @@ -0,0 +1,14 @@ +{ + "name": "Xbox Controller", + "idmatcher": "Xbox.*", + "mapping": + { + "KEY_BUTTON_13": "left", + "KEY_BUTTON_14": "right", + "KEY_BUTTON_11": "up", + "KEY_BUTTON_12": "down", + "KEY_BUTTON_0": "enter", + "KEY_BUTTON_1": "back", + "KEY_BUTTON_3": "info" + } +} \ No newline at end of file diff --git a/resources/settings/settings_description.json b/resources/settings/settings_description.json new file mode 100644 index 0000000..4b07822 --- /dev/null +++ b/resources/settings/settings_description.json @@ -0,0 +1,279 @@ +[ + { + "section": "__meta__", + "version": 3 + }, + { + "section": "state", + "hidden": true, + "storage": true, + "values":[ + { + "value": "geometry", + "default": "", + "hidden": true + } + ] + }, + { + "section": "main", + "values": [ + { + "value": "remoteInspector", + "default": false, + "hidden": true + }, + { + "value": "fullscreen", + "default": false, + "platforms_excluded": "oe" + }, + { + "value": "webserverport", + "default": 32433, + "hidden": true + }, + { + "value": "updateChannel", + "default": 4, + "possible_values": [ + [ 0, "Stable" ], + [ 8, "PlexPass" ], + [ 4, "Ninja" ], + [ 2, "Employee" ] + ] + } + ] + }, + { + "section": "audio", + "values": [ + { + "value": "devicetype", + "default": "basic", + "possible_values": [ + [ "basic", "Basic" ], + [ "spdif", "Optical (S/PDIF)" ], + [ "hdmi", "HDMI" ] + ] + }, + { + "value": "channels", + "default": "2.0", + "possible_values": [ + [ "auto", "Auto Select Channels" ], + [ "1.0", "Mono" ], + [ "2.0", "Stereo" ], + [ "2.1", "2.1" ], + [ "3.0", "3.0" ], + [ "3.1", "3.1" ], + [ "5.1", "5.1" ], + [ "7.1", "7.1" ] + ] + }, + { + "value": "device", + "default": "auto" + }, + { + "value": "normalize", + "default": true + }, + { + "value": "advanced", + "default": false + }, + { + "value": "exclusive", + "default": false, + "hidden": true, + "platforms": [ "osx", "windows" ] + }, + { + "value": "passthrough.ac3", + "default": false + }, + { + "value": "passthrough.dts", + "default": false + }, + { + "value": "passthrough.eac3", + "default": false, + "platforms_excluded": [ "osx", "oe_rpi" ] + }, + { + "value": "passthrough.dts-hd", + "default": false, + "platforms_excluded": [ "osx", "oe_rpi" ] + }, + { + "value": "passthrough.truehd", + "default": false, + "platforms_excluded": [ "osx", "oe_rpi" ] + } + ] + }, + { + "section": "video", + "values": [ + { + "value": "debug.force_vo", + "default": "", + "hidden": true + }, + { + "value": "refreshrate.auto_switch", + "default": false + }, + { + "value": "refreshrate.delay", + "default": 3, + "hidden": true + }, + { + "value": "hardware_decoding", + "default": true, + "platforms_excluded": "oe_rpi" + }, + { + "value": "deinterlace", + "default": false, + "platforms_excluded": "oe_rpi" + }, + { + "value": "directPlay", + "default": true + }, + { + "value": "directStream", + "default": true + }, + { + "value": "sync_mode", + "default": "audio", + "possible_values": [ + [ "audio", "Audio" ], + [ "display-resample", "Display" ] + ] + }, + { + "value": "audio_delay.normal", + "default": 0, + "hidden": true, + "possible_range": { + "min": -300, + "max": 300, + "step": 50 + } + }, + { + "value": "audio_delay.24hz", + "default": 0, + "hidden": true, + "possible_range": { + "min": -300, + "max": 300, + "step": 50 + } + }, + { + "value": "cache", + "default": 75, + "possible_values": [ + [ 10, "Tiny (10 MB)" ], + [ 75, "Default (75 MB)" ], + [ 150, "Large (150 MB)", { "platforms_excluded": "oe_rpi" } ], + [ 500, "Very Large (500 MB)", { "platforms_excluded": "oe_rpi" } ] + ] + } + ] + }, + { + "section": "subtitles", + "values": [ + { + "value": "placement", + "default": "center,bottom", + "possible_values": [ + [ "left,bottom", "Lower left" ], + [ "right,bottom", "Lower right" ], + [ "center,bottom", "Lower middle" ], + [ "left,top", "Upper left" ], + [ "right,top", "Upper right" ], + [ "center,top", "Upper middle" ] + ] + }, + { + "value": "color", + "default": "#EEEEEE,#000000", + "possible_values": [ + [ "#EEEEEE,#000000", "Light grey" ], + [ "#CCCCCC,#000000", "Blended light grey" ], + [ "#FBF93E,#000000", "Yellow" ], + [ "#FFFFCC,#000000", "Light yellow" ], + [ "#EEEDB8,#000000", "Blended light yellow" ] + ] + }, + { + "value": "size", + "default": 32, + "possible_values": [ + [ 18, "Tiny" ], + [ 24, "Small" ], + [ 32, "Normal" ], + [ 42, "Large" ], + [ 60, "Huge" ] + ] + } + ] + }, + { + "section": "path", + "hidden": true, + "values": [ + { + "value": "startupurl", + "default": "qrc:/konvergo/index.html" + }, + { + "value": "helperprogram", + "default": "" + } + ] + }, + { + "section": "cec", + "hidden": true, + "values": [ + { + "value": "hdmiport", + "default": 0 + }, + { + "value": "verbose_logging", + "default": false + }, + { + "value": "enable", + "default": true + } + ] + }, + { + "section": "webclient", + "hidden": true, + "storage": true, + "values": [] + }, + { + "section": "openelec", + "hidden": true, + "values": [ + { + "value": "systemname", + "default": "PlexMediaPlayer" + } + ] + } +] diff --git a/resources/sounds/Back.wav b/resources/sounds/Back.wav new file mode 100644 index 0000000000000000000000000000000000000000..eb1b193a34a11b8d4fe6a97007bfb0630f6246a4 GIT binary patch literal 12396 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 literal 0 HcmV?d00001 diff --git a/resources/sounds/Click.wav b/resources/sounds/Click.wav new file mode 100644 index 0000000000000000000000000000000000000000..13ac3a8acee08336d4225e2f702f0690d6b71130 GIT binary patch literal 24748 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 literal 0 HcmV?d00001 diff --git a/resources/sounds/Notify.wav b/resources/sounds/Notify.wav new file mode 100644 index 0000000000000000000000000000000000000000..b8670b0b2edd7d5a94a8906ab3df6723b1418d99 GIT binary patch literal 43822 zcmeFZb#xU?*Du(8+&%8D#9c@Nfh4#~a0~7Z4?MVgaCZU(2n3Q4Pl&q^_ng?tare{H zyzlqjckayGyVm?Of6RK$UfrjAwHymumBUV1ndB3zzyb%56q_kAQX z3G9IRau+tLN&z;20PVm@;A-$F{7i4bOt26v2J^vm@Hu!9+yTx8CxAq77{~+e0K0(M zfInO-7EnP#hzs!`F{FY3SQ?DsZ|n|Viv;4~FSHO?3M_|dF`S+PgaRIL84{p{#-T2# z4*CWCguX%_p?A<5=q>af`V4)Ca-mYF7WxhKKtpi7B1jI`tAVtT4&F478d5?s_+CQz z&A1Q;rZIRMhtmQ$uLRDmgmdX21N}kN?x(|M~s7`+rjZ^RxUX_y2tUe|7ZVdg8x(|34b* zzkH$p4@Lgm)$p(FKl^__$-lMJzpwu1%zvKdKdJwFwf~*}zw`V#{-f*u^Z!SO{?W&( z|I(v!SSw3lz5Hhr{TB&foy>zZ^B;6D4(sW0_@57JZV8zL< zaG)EEL_FZV8{h(?5=+1u-n#-pFrwTD+yIJU3>XJK1tD-9jLKq=g~&;$64VTIGkON* z7bY0{06T{D!fnKz!Ck?fz%9dB;hL}qu^@IgrVee2PDH&x4j~wbFz^6S2H8W`4Rk|_ zey*OS*Xv|Dwoa(?*Pqij>q8A+4Mxxk=n`}r+6|dO7YsG}9^E_bBu#2vJu?*7n~+!@jFy=_%1v*k>)pz(48qTasNqdKxO zpd4S?TAW$5J_yR+x!-7cI`va147ThW-_ z+S4^*;PeQOyF)yxn5BCT{>C&BuhEQ*_Lv>Fink*=J$7yNpn1>p^A4;Dz7*yZxhCpR z%=XFFQ@&2Mjt@!DCG45@VA|ek0|^81g>h%63S&u=HPN3ZhDYRtng!1enC&Z`u*JRK zS#5vI+TFavXe#|G3B>J178?RJ!}6zMbuyY6n~t&(E}i?btsxTH<{ z`1|eOFC(8`c)07{&D+#l2{*9U?XQ8?(ymospL6qMa_rrc59U63`eNSOTOa3rO~^cw z$1LGgWi>u%|I$}8(#luLymj8le*8w-sPRs#Vuu@U4&Lm5Lt*Emj>PUw;LPlwo4YV^ z>9OSztDda6yUugNl%$~~$Bmsy>WQ2!k7a&#>C%Yx+9mFbm#@!{g>T943XG5EBa>9DMleuS(-hTjjFuZBobP zFWd5VP+nN6HPbcvr@WIx;As)U? zG#iPjj1fq_f^7#E>MWH4aWl_i>^a-G?@i~?)^$xmb=FlrWr@Xi^L5z|GUtB>zh-_a z{xBgW?M>9H*cXCls;85lYM(?uHGOvC`SO?9ufM#bek}RyknWf@IqyR8!ivLn_kNFc zJ|0*7ROxYPt3qKwB#Jj-F%5kOjR?}~c zS>#%*IpU-4p7Ms|I=^ttZ0J(o#IC?Lzh>)tpX%G?VI|7KjJ!+PmowA9i@(nL(w=(u z!@`s;ZzZpXU*TWPeo1+GmQ_b(&?QpI0Y7ZC>6Gy+Ds+is}XWGK8OXsYpUHxgDY(v||wVP*d zIl5)imRFl6Z;DT1tlzK(SQ(Kxa}jIatC^$(r&#^O&d{2GX76tIY^PJUhb*oezol1^ zhOm7IoqmS;imX;Ro!d9EZP30K)yZ!WHagXvszQ`M{pDSlpL_P_g^Y*k8EJ}7vp$xm z+<#m0n)T|{i<;+GpMQSt`@-(!^;d;&KBRPhoc-lMdQ{e|yzJuCijlgxEhgROY`gKv z;+rZlbO0MjtultJ9GoV4YW;SHwnf!V37&p_&bfupm%6XGx_ZO9XB)0<`9%vf|}Uhho*ga@&wqj;fd0k6E3Zmy1P)@#kKGD(yi90j>c zAEwevQuvR@+SwEOUUbH`+BHq9`&<=MZeOynC?h{GSM;+bGwa9W^vtigw9TKDsl^|! zec1KBD&kD=C+uP%&F8E;$I;8?w3(i{pQ6RxM_Lb&FKsKQ$wom zUv|7YyMf>Gw5xt#V04i0EW4>8fzPpC zloS@(Vv=2=%Z3S#{%1m7L~fkaG1Yi__blYR!3D<_A6*J1&Rp)k;?RoqD|(hUF58@V zcS+cyYi4t6!``~-H6~To6^F|tCFg!kD9$NdQV^8S$ZO5X%>MSXFpHXXHq#+9 zF!NcaS(YyAcJ|-7srlQBo|TX*gw^2ZVpsN}d=TmJ+D*3HMy(a@78&)qT@X%nk~bum?>9-|D8StYy|BAKuon@}bay zk)Ha_<~FcpYm=_Nu=Ypwr^?)N)3V1U^k3(S@P*IwSLV6p_GOp;#Q)roRhD@;^F(Gw z=KQSFKW%eN@~#%_C{8YQu8gWpZp60oJ16(Q9ya23iRu)ubbll6;EE`0mdbqG_OtVJ z&otl8ppNis(G{_GIlpLO#Nvo0l}mU_-Yn597A-os@c4YAx$|auP5%_v zKe;+;M|f8d)6Z%G%N60!X_aG|$$UvUhCh!gfRvi)ib3%c{(jD>;nIQVUR~!<8~?X| zQ+oZ}TC-|qrC0gx(ve^5i=~A{1zGvIdHUQ#xx+adawg@>&Y7CCB?p5&^&(G?fRWb`d%X#MJ8NSm!5?ZHjj!ljEGVxG&cQ7s> z+Gma@(>2FowoSQtqA|!gPS)b`P`80qx+SVxvN6#Pe(d=4(XB&i1FXL5-4i=q+TB{i ze#$m|AOHA zVf!LQQO=XV*o#xTr;fy3i0_CWjei<1i5r-DWJ>wu!kE=jg%K^GCxe83tG#nQT3req zuGoZFbQ_;wm{1Dwuh1{RoBB)YFLJEpiolgSGAbU58GPO6*@Nq1c38JfYPktxtHcIw z-GkcYHBr^!Rl${U6{pGv%I24~m+mgLFIANwN~21@maZwAUw)~ARyA5pt9#Ut+}zQ+ zs&i8BnL*Xab?#cxX89@2L+Bmq1^yxBCG(EyRI6Wh9?qNGmw9P?rw6VK2@B7SY>3K; zIX!tawsXpdsZ-;U;_Tv{O|6{rGIr+V$M9PeMU0024!Y=1@LAz`&o$NYq;06B$mAx| zn<^v}U@jrn7^Z1fDe5H0g+4qq$8}^YJEuRmx3}v@M^Rf#OHZ?H)B6UG`i$C#H5Ju{ zs;N~Km3u3_EAf@6N<<}CX;~RwIi>PS<+7^H)qmAA*50g7ZFKpqXytX#dM@=Z7}_+J z!!s41lsjsA3|o;x>@HG1&A^&zw#Z6gyWNrPLiJEjDEC?KUloW7;fEdx?~bsU=n`d! zN{eR3Sj0%8pGHff`X(NX%nDBp-5x9pu=dkQYrk)nHXZHKvAGJFBj!esQBm^V$|}+k?(MJt_T@hoI34ZmCcv4OHFJ zIe>4_B*G3#E`wu?wUAk#x7RxDa(m#p#CyPZV!*5*qmU<|{4hiK=Ll3}U}Q+-NW|=j zL*WTw;*e9pj|1cV(Z0D}Bu@{w8P2W_B{n-P4W|A^bLi*DIrtGY4KdMhTVttAm+lZH z@pq2z9(_K97~IfT(H+^D-4@rPYRYU#s$BK+nfc$h4KU1L=caCPa-myCOV5w+Y?Sa zH!m-HzgK}@LjuDcBA-tD92F2982u$`=fu+y{$Uq`PX#3U#(K^6c;sr~e9vKm9oa@{ znQnf>bd&LN))K~H+7F6?w3LV;knn48QfxZ*EOsMyJ$5s8E;axgj$Mm=iIrg;aU|S# ztPWFzwm=aOC!wFZ?`pbYMEr_Bfirt3zAvmZwB=dDj+&Vjmr70)rRR%t+J5fJnv(e@ zgOxcu%ObltcY5KalFUj{gLB)>-nfy;f*3`V;V9-eMQ0psGvKnuXLSfJ+AD7BOu@YD z#R<#4t~j~s=IX<%=dLnd@nUK4LbEwDrdh`JMIH@i`T`#APOEI1%^tCCQ1b{=F}8>( z!yS#S5+~au-XO#X*79fZ3;5*%Q_){yBWbJbtYW?DrKVE%&Ts*UM=V9=B5RR%kQ_t} zI1@N#h|syKS@Mmdb6o$Cxc=Fl>Aw@|DOGW$`a-`vx9ri(*o;*_CjWT#V_(K-=J3y1 zxzPn}#UbT)YD${6be!tPj^zvADaFuX9GU*eOmD|w1M?9)Bocicjlo1?zM#WVE{J3(Q5UP) zAoUWok8NPndim{D&A;o`RLv?oReY%6L|$TUc}`l6IOkaIqPz|HzX~dfZkHe{JZt14~e%7oL??PugoKQb*Wo;>AH zOj_jD(8_>FpHz?E&O*Bws{+#-EDr4p`3HfA4M*=qoPjRr%G8^c^W~?c!IA@Fme@>u zSv*g2NUE2;QEXEk*WA>_7(PJ+&22G)-tt>h$c39Qw}56FMl; zb%&9w2wwC-lXceH9mn1DUJw1B1?$6JMV^a#A1#kw5G|QVkE{+02-zEW&+m=*J;lT#zuEDO-s0Hzo#5{vQ8 zL}013`{~T^pm-cZ}O`5M$_3laU$2uZNIB8`(93w85}}ll}dD zPJKIiiM`^U!5(Dq{!DM^f5#@XgytQXo> zIT75tJY2mFS1ex?}s*VyiKMevLSSUJFEOwBXSkjME3u!}SnQVUINv7AOwOcHjBq}Pt-1omVsRvI?&!|FGI#9ru*Zcy!@+%NNx1c+t`9`QDD?~H3X#T-57;rI)#8$VRoAnupV zRXORl0VH%I{te|C^9+pR*VxCnGU4cUr_MRu)iV;qT*s2V)n;GM%zsc4?i9};`e<5!nMq9rbRBRf&2t> z0lKO>EZ#N#g&o`D*0!e6y5>c>@D~U6aZbuh&b^ttI*(Rxs_1pe#R|{bsHU@REsnLnwIiSw~YWwRXkYJI z>=hlL4naiDj{Y`jYOKc;$0_Eq;gilpHHFU$_VZiqIp&11sWpkF{f$cj>QqSaVouIL zd}l!O^O|pEpNbymZO@L(a?ey`?9Z&t!sGz?RYfbywpX(oIIYWjfT5=G67fS-Aao9$ zL`U-Jupg(c&?a=N~&NxE&Oj@CuZ(yMu2s>!sj1`uC4qDeKUNilRf}_J( z6IVrljbTg@#(2jZiRz8mA38m7uTQDFy`$LjluRNf@jEYL=;xli`XbZv)*40A*N&9_i=&?f7zIh6jSM18rd1kMnd5El z*cUm?a}~L<-IsbGJc`}&T*e*e+F4u8G37Jnkjt@C5kGW+N}br0e|&7?kmUbd~UYEM&TIhKI8@XzYe+@nj9{O$c$`= z^o#5cZw++~PVx8h7Q5Lvp0ctv-bT5KIcB(~7#GYKt>`Onk8AR%sVv)6Y{F-LD2+I5&kD#cQ1s(Skcf$z;Zx##orIoc{u(-$dUb}xeoMV+{j0n-_4%cL^JSi{3%T*42A8A3XOOapR# zE_p6;nQiyn+?F{+ScrJ2&Jj_@O8W}i%o}VgtBTF@lCp}vbH0+&4t?Q$vH3bH{aD7V z?9zPW5=7PP2Di4uy+gzM_<`~&9Rj5$T&73YyENHbSo@m!u;x);5*#rckgE^UAe9?sYbB7#SyU~w5xI(=NOsA-DlF8u zwKEMKpaNNlIgM8kttlq7WcnFK2=f@TgK>_opi0T!#4fBi$`3&6@|BiS4u9=f!C+hW z^;Yi&O{MggTYhDhBt7FxY--i}gp~Amt|^W0Po?U{=)iAxiGmNFKr=0mf^(=uYuMV<=$ z>^avV)vSl=hZ)edNW;0?*;~8aS{oYX)!eD*ES*tO_{*i_b?L;4rs^3Dms>V=6%Sk- zdm(I9jO)u$>xqx)^GvQ-POPmrI+58=PtFVNS{JKXb*tF>d7wchkA z!5c3+9-KPVHu8|OlxHIx5}%UMm52r3RwF=zRi``YrHl01I+6bEh!yF1Zxk~DTv z94_H~!h;0g1V+67R7~ua7*%9Ns3c&McYvFZ{WSBhbRS%?K31w9OX&U5>|3?1$mpm3 zD>?P{Tbq}xX9bV#9#uSC`zZaf!?Q;(Id4~{Hhe4ld7?;Ld97(y*Q23vewU&Q+KNk{ zHJHTPsGXWT&ie9$_J&W3dNv6?<=E8tIJ3C=DZP^eVh%4mf%wB@wfv_#{%P(DP;&gcm<6 zy;KHa4YBdXFyOjlpvxPDVS%PKX!_NJhN;>~QzoW_82MGZ58JOaze+F1*#TwBQQ`Hm?tzi6o2~rD$huY4 z^vd()qB25xW_e)cgX&LpE1SNyUg-MX?=d3c3d90szCHrE66Zm_P2X!&Vwz&%V(n{N zY}aZ(-@(UWhrP!3mo?AgzG)HbIE_hqi48%TLnpO~s&jHPsjc{iaD(6lKMtlN{1N^~ z!8_qwQMEWo`a|ZYC{j*TU)PLlC+U|OLf}~FX`le;0y=?rzy>((*$>AZ*FbJip26CX zs9&O^YE#sHs&|S2IYDM4JtNsKVN0G!Q)RY_7G;H6r`=%C0_DgrnD_W@QY`Ho)57GA zxrOx&yC%n0mk;i9CuDm!`tI^S8NdpR3e*M63<&mr?7Q8&#~Cvd-S~Ia!jh)^k)OAI^nUI8T$H;1W9tXxN7q!h&w{iQ-^;Vo@)CX> ztHd=lwf6QH4{zXQOC=fuD8c=pni^YKw%f(IO!NHhGZJtxWO4Y`$X^p@MR6vUMY=`c z!gd6|325{k_hNb`xwSa2b|l&7+pM>uTKqCSX*`cLkr7ARM7dAuBKYFZV$0A9Bp!hR zwEAA{6ZJfJAX&0xf=IwG<$mB4j8aDK4XtEH584el_6PLodMC3GCU7$1x=7S=XW z`}@uuw=U27-X?x-0Ud#{!K*_iggyw}9QrC`Mew}9seU`W#@%i@p0Q?`ZltFYTv46+ z8s!9umghSrXM6WQ?cUol)SCZW*VNuPuW?gjLQ`{dOUu^w>s|WZ(n0QsB{y1lQktX8 z(k4UGk(n3+eg*j}Er)r@IKb?@MV!@sYfBrd%>?Vmmb=aKOww4V>4}t0#7f*&%rewR zga`NoT3|rxQ?+w7GSyXOumY5KOGhN(l0xxnv5iFtScBRc49jb7e!Bz5VSUn<@3k>Xh=qlH0n`I*0zpO^ zrP?t4ja|&5Eq!cm+P!jk?BwARXpWw)fWwu8};)&8NK+M(%4>{ zt2l$Uf^v-Xjv&Qt#EQ{pQFh22FamgD7}uF=&DC^eko>fiCq64$DLf?j&0ofs@$U1Y zcq7~^+)3O}?l~@+SH@!qT7|V@PDNLCa5LfpG7061u0_wmbYjk6XW^#e zR}sz;n@O>hN7P=L7h?{S#Jb0_F}h(iVia!-_dO;ZCKpT>n@P+9%qN*s%-5OCG!+{! zHoDEsq?c1W$arI1UCU^~ElggQv=jp*!arT_lYm|uUOuD13> z?WI~wU1wc?y?x`;rpdqETX(jbcDeMv7&th5izDEll-MY5Yx^NvR5*@C%BSvShM15o zMy=EBPdg{Lr%t%+L-IEX+#lo-&L$F0v=Y&T^Z z{Y5V)bMbBH0(wE+Y>^{&*>`=7Dw<%gJ+S1xy zv~xN(bq#ed>87$7e^tk zByXc`rXOJ*Fj{W1$Sl!ffmM)AtF5zru*0we-BIt5=Fn&V-R?Qu4j03%aL8hZd9|sT z3CZXzqmtT5x{haJZXmRV&04YYfQ&9C3f6Op9E;K9;blXI*n@+A4^A759$Yy1V9=T^ zWQPuqj^vKDjj!ReM8%TZ@+~U8W|jUEQ~_onucBvRP4HSm8|gJ=4XvB5V)QW2vPxL* zS#wyKOc8^^5YmoQTgfca48lcRGsXdZ0x1HQ!!nhntJnNieNgO`?UDQt`3l8+fIppA z%00^6$vw=y$oIyOD>nz&O$5j>#L7{U#KX+r|#Y^+p$srW%ors#r5vcbO%O z9C{QjfkGpF!{e}Mlpb&}EYUtunJd0ZW5k_;eLOAa?&$JiSGJ&kuy;bw*DkNl%=R~J zK-;y}1Fe`gQhR*Ia3`l5)#owr7rSS8^4REj2Y*lm$~G#o+H}Kl#0rcSznGjw6Ejnd z?aaYxlzbo`Z+ucE_`ha~)qhoU%vRIoXU_E;D~_ z(#>*X+@Rvg4+*ny49qX&M$j0_(#2}}lo#Yg>E9v?K@Il|=kL+R;cY_)*d&-Xu&sv7 zhi8stj0SLu#&7dd1;0fI=@j`BrMc#d&I#H9K0=;Hn_;)${>H}<1_)b;)kGvInskL! zP12H_$rH(*WC7_qi2_GG_7m3Pjd1rdDD)EKT~GuaGEntd+B=$O>K`h$GEn(i5ujkp zAIan82>F1lUp6A+$dK}h@+0!+@?Y|Pd6&FK&XzkU{#MYHLZzwtjK)dVraupRvQWrR zs6fmoECoN25Kr_bjgU@|%_%7qN9uQK63vL7Nng*fW;QZ6uS74iLV{N_Ka;3Sk z={2Kah7WBec{kxO_5|t-_`tx{9#qFFqvd<0zs1u;ZGuyLUtY`jF;37Ja#S*m9`+jA z!LAsL7;GPSFtBxC*1*z%I|JCkuY*_E4~N9VM@FqUqHzt+SQsJRFMTJ^QD&<@YH#XK zL4SjPBafl?VVC0*h;ihF)IIdu%zH+sOjekI7K<#8S?#gL*~HjH+MsQ2TaQ^`tdN!+ zFh00%^3Lc4(}Mn(!XoX(H(+L<`oR;Bk6xx}R<$XlG7IT!@oC{tK8yEkd=|%REPnLX zh{XtTWZj7IsNYxt=PkF4j}c9k{3ZLL5UTK6sV>W~3g`u+kWWwov=ufKHwjNB+#>J^ zc0@}ekMNjaPdJSq#4W{*!Xp7m*bkV=m@>2<`ZOvX`3q43W&@9)b5vr_GZdF;RJJhc=$F;k4TlGr} z5s*D#0=ghpBHy9L(Sg`oI5r+d94CGx%_Fyw!zdRh1(Zfg4W*tkNYPXLs5ht>+BVp7 zYNM6Wl4vzl992txPVyjT;RA3FF*4LNWHOBPQuKSZMD-p;vvjifhhPG)igR=H%5XdT z#NdX3!~IQtoBFK#G`-k9|GvY0n0|J@V8EYUHQ6N0A<2NsZ``5S^M><+D@@2Se z$aLsAdnJ1rJB1xRBpiA^ylTW@bZoS6EQON{`%s5?v4V2pG4UL!k33MhK)p`8R6hd> z2hEV(s7n|(+!wqp@d`;vUQVs2&1cjzV~x^`RVHvhX|}}tEKG^!BC~a7O{T%7*GvYC z&5Rw4)XXc4TG}tl2@->F1uH`>MASfQ^dxPQs#ZafAC(Hk2Sk&F(SphR6}+ch?)da^ z9;b%W#|aQMklGrjzF>*Qf_IQ*_t#KMY-vHMk#9j&wv{z_ei%xHkM& zLN=j{aF-BGXu(g%zsF%<^i+xQ$Gk#Yq2Hl`QTfO~$_0{}TSt{qqw@I&xzX)6S*1VhJR-E6X zEhFY5PlvY-ZyUZiTs<5*QarL^ls48q*33b0m+-pzsls$|v(yfbON43%^{;`YNFI7W zZj|6nUQInrU&RbEax@{BRh#d#;?gh+Q9AG)6s|APB&isRpVIx}S;86oYh1+mg|Wa<<_Kn(J~W;E zX>ih@3yf3>2e-51hJuEpM;45(AG^RQ87J`$^0C6(B3B7pnk9dw{I0Im*66FC8t^T0 zHkyrDf%}0+5a*Gukl#?gQqyUV=_?s5W&`sW3k~<^jYbG#l~IM!aU+(|W0ni+3)7qV zkzvW$M!!niLe-HMl1>s%;&x&ZQ47tZuf;{8VIc#K z*%=Eu_&@kXd@y4|&&B15jO$b}yJ}Zk*N=PH@h5N1_cys(p+(qnNOfvd1>JV}{Vip(!*h75% zN8K8&xn@k&tt2XUDwOgk^7Znmu;yDNzbD5i-YX6(SE}Z!S8EPw_vu#X7aCSVTY)p+ zTSPTdjP+>ZT2TRNDJ6*_fTijI=_j$8P>cVC z%g5%zQoaRcjVuG#08+yl{RACGOVC)WLsZL@mlWUS)v_jOkAw$Xp$Jj1@T8!LKb0@w zedpceUE{su{pO+h{`}4SFMJt44z^2I3n?O*h%E7uZjp7$w5geNFQ_n zHURHSbRt_)o#=i{XCt+7x@oBSUW@IPepWeFOzT+dIBRR`CacL-2P_|0yfWWtrZG8U zT*c~O6wv;rFiH1d-_vQ73Oo#nb=x&^>pkI|*H#WdI_LOnwPDaXm( zBrDP!;wnN6-Wo^5cA`(9e2@mP9Jm0jGi=fy*8QbDrb$+(s@^M~DqhN;$&#hFC2z&u zqQxSds8sk&_(_-#Q@_w(bW?;7zl28}5T%{cN?D=2USXuVpr&a*=q4G;ph)mJq7O+# z2V)jt*W-5JlL(Os*o~NGzfP{|z?{`y0Iz<%(2-wZKbgkHJsh z1wX|EwOT1v*eG_&IkHExgR%p%k1|VnntZ)tqS95xRAV&=EvTdDy$wmw55OF83ORzB zh53QC#h)S+!`5CY`6k7ddXic|t)YIT?x%WFYbf(6Wn??@Qc@Bzo`Au>#9Cu+pzz33 z@N|ir25)_n_Jk%>jaG3LLvpT+AzLZUlT46gis!@A6DmY`uy3PVq!&erpNr{|s}iyl zwoj!Avb(Z!8Cxcm8Dtc>hdfDMCts)#E50gksy3>JFXkon?pwc4R`_Rh1OyC zxGq9I=>z34Z8w9(I%dX|o^RjCVAe<{NiT3MsCUNSDGh+{-ogj~TK z!B4&q|0Bs?e-QW)k){FG8^k0`RQrf76n%BCCh7M>aXh7USg<(43 zHb5d?B=wL@sJ=8~dKo>6v567I7@#kp-=`H(2Pg_Mj%-Y#6Xp0O+-I%&|os~fd^@2Uf7IYr= zCO)3nM6#fGQEh2sw7=;fV=Ln^Bbl*0|*>hi7A7qE{lL!D-MTeZTgU zCR)u@p_OEXncP*DAiXRZ6i*Tti;jvmiVle0h@_$=;yQ7Zt?mP_qm*|(8@lP^{P%1_D#su9(8wMe~LqtO&VIm!Gz?16DHbu?);TEi-5sA!JV-Q+By z2p@(!he<;fAku(!h5_wl^-jfJ=~>ZBe)c$eEN8fwO&-kYFYeR#9_Wqi_3sVq-O$_E zd!TQ2|IUGq!HYw8M$nuL?h`?QSSGVl&C?z;Yyizs1(;y`e&QYSEou^d0<(qXYaD0d zW}0uRF>N(nV*1eJuCcGtQ>HJYn|7G$NgT?ZZsMJVk3z zyHN<#ZKNNP2hXUUfOrccK_6fe6loyof5E7Er8-_UOL<7qF5e`l%8O+uWPY-Cn2TmG zuOaa@aiAD0HWtqjmx%AedT>-0rg*6&s8?!kYG3J|=}#J_z%zz%T$#P^^`3m`<{H|iF@}R0j9jis?l?Dx9Kv2+3tO%D% zh$LMmzo)#S?xT6qf72H;J}|l&1B^EeYepR1g;qqtlkvnpTr$Q5l>=^sJoQ%EdFm47 zW<`R08QedQOO{Bw#fQYU;%3n+QL^Zzs99t$J}<^gUP(fvSyB(#by=%SARC2kQ;rNL zpD%BfAAxNMCslxYuI8}zHjFfP8YV#)pbR(xkF{z*e1L6;Z!l_^hAYC`5I2(EkgF-# z)EzVhZ6W;#eJ_0tJ(0eievp2No=m?%KSf_lH>2O74Ny_A4N*_JN8CtQjNgPikNt`n zM5AH;Xc6B)GT>$K*FowVN|yYg#6i@;&*heJkYo2p{u;hNBxE0Dd$S2_BlZGzJ^R|w zt>K}Ot7E6fvv}^XfA73(x{|3W(1kW{HOZP5ji>gOR;696djrqpGSNrsSLq+=HTo@val;wN2KWqwfZ3oY zyaK=*L@FW+k%#z#NJBItP{;)2TO^2zL!CsuMBPHoK(!)$kV_FO!34kp>e4^g?a;<( zywuZG7nCE4V~QwPCweJ%D{2%`%0Xp@s#s0eKG7u_VgP@H5p02`VPo(wU_TWCM(F*N z57bpOH2oo+0;7R_j5Q1-V;1Z;aG~asDI{+~ATAU$85NFj0bC3Lx@nrzst$#}{Ht_} zBueZlN)+Y_{9)hgSN<7;X{ z3$W)UmxLiFlE0IYlu0o5TR=fk4w6$zg~T=j53j*l;8tSOFc{1hbSVl%Ss`5!RIm_= zG2GW>Xwp>KiVm4Z5-Ls+x(UAX_Hj4DW3(QRnT(Z;CXYTD<&EwdbLV)DALm-}(ZUJh zn^IH7eU(zRaDsslX_`x9qPctM;%zDh}_exhxKTOyxcOHZPw&{C;aD05+c z^%E-apK;HyFE9_#2T<`y8-xOAfU*pQdXX+hm#JN<_0@)Hk7||LWSy_RL?3IoYxoSW zzHrH~)(`?m(ku+&h8ys_z8^4mjNvsNoDkdL^&K`L>=3`;bsyS+Wq<&>3$Mr!0r^3e zuq>7sjvH(Z-}RIAwYs%Bf-YZsTDwhqOq->()xFnE(U0p-7}SO(aIEe<^Z|MX9f9V+ zC9UE4f+5gO=mpdauT5Zpx}h5o6WR{Tb+=BXwbCxsyj8Q*cT_>JJ&09lm3pNcY#XMj z0QGh?pvl!d*QV`^2Br#GOnNBt$=PIbGh3ZO8l8$J20>HU`|NI&&Au}?eWq0qxeDmTEcIF z6LBN)A@L<~JCR7dMZgob<8$C~%)Z#m7$JHtx(MZlT8-R-m=EK?eTHf{+Br#mOj#;7 zm0gwuit$`Nw_n}y@yjfs;;TgY1}zSLr>5sgOsNL5qNln&D0L=xdCE($w_PC-3I{zPR(Tb}B4YprOpL0jh#nz$SQ24H|d= z#3PCk*N`hvHs}&`6y_Y}DrPZe9Gw8$YPCoL(jT!K?10zZXok)}t0CCO0hvOrh7$%G z!z*~sX_)RZED`OR$C`VZQB9(@UwcMpslTSz!q(b0!v0j!1>uvNYIt$p6Z`K%T=BRI}`jyj_97Q2aJjF64URABS0zYw(7SQGCj_N}U7>EQc z0!tCIQ8>&fwhI3g?!Egd{(6S z9q@gaYp5v*9MG!I*4C>{RmT-b`48z4xR1h%*n(1i2CtZ_9N#wXFzz^hV%&{u;1cy+4`q(tioSq;jc!Dbqa|nt zCLVJF(~a4P)nYH=oba{yC4?`8K7xTjBI*cjgdAALUK3st>Ir7VS;UpZ4aE6G17RB> z6GjMa*iv}(dp_z5jDTN*&w(>PnW_lzZE z5*&yJh>wZSiRXw@i0yo_GM3G9Zp8jSRV+7XSJW}g~R|5V*jT~fVO zwW>(!N$S7Vjp~V-pPHFky3U}39drf?U=Nxjq=;PPUKAbXMKC5GF?^_>F>kRJ(M@QLt(?edMl>95aw&bjwK_x^sVCq<%c3d8oCy(is)G()&o!hdEH~I#xotjCB^>_1SNlQ(6 zm8>LnNurWIpc-INQtRY)(Eff%`xS^!g!)qJU>q>NSec#DR4KYBlLt*|?y$?b#{5Tq zu<%MK0&IGa*hMTWD#B)=jBti8!!O{Tu^HIvq#;w6u1=M4GT5o+pT=`NP7Br|)ihq{Q{oKV-7O}oDKsB!Se;D#ZK zLau~-39&+)kgp*ZLdJk)oE7{w$Q=|KNCjN*gu9c zY5^{#bOa97De*u;)r3q5B@_B5yhxaWC@M!Xm+~NGXsRo1e_B&tvhR0)A7C0~)QVbD zy|*#MoNL{;DN3c@(r1~z#3t+50InG~iQCLA!mA)ySO`tJJGcccyXdMSMo?J#O#>GaeQ{ZDL0W_1FU!& zJ&x*t`}^2jg#H4ux*wTEHGe^0?zBp&gHtXh_f8&>d@4Ch%Egousjt$?_|MAiR7E>r zbhnc2-qdOO4Re>wWixS8x%+6Fl%KE1cj0^SJ@}4%13m}h?loLZ?joCo?Mb4U)AVKP zrgPaoY;7@D8FTeH+BP*#sjXaoukuEjC`)i(N&-2lYP2_}SbOce&RgmweTo@J zO0X~41sJs+;jyqq91fo!=bGXU@htRw@#OFp^3vWtp2D87?yZOb){1?EjQmlyGC4w5 zpdQ-OtafI&F(1vKc5CCbq1tloz7~M^{*2BWwT!XG24kHu$!G_C{9Ap8K3s3CSI`UT z0eXTKtIg8tYn*mU?W9`DcBP*3S{^Is#aLSFk3^zxUfl~FGKQ%R1Yo@ zil!QzOYa@ESwPb3d@Ba zLV&Q3ugLG_JltUR3`u5kq8_L})z9f*x3+qiBXD<5YnobJovHjGR|Ov2*iZbjudsiC zKeK#K{#{w3F4A`B_mF?ru*W$EFu!QxVkM5_RsO5+R(v2Gb4mR>%fo2BVNnlT(y>(zZ*RG;Kq7!y(oT4AZKi=Pt-T* z$MqZfEB%>%QeUM1s^`-mYxA{cnx!sP^Qm`~@k(~(xEwAM`K*76zZ;IWg1?r35U}$L zey==6elK@YQk3OteJz`w*{Eyov+6nub&om7PUM@4g zNo$wtPkxs4Jkgc7C?Nt$o9F~SadKi{($%D$;11TLu1H(wTi_okcTno9nY0A$Fa4Df zYword+C!ZPstO&(_`y+)M&z)W%fZj)U+}>~J)y4;|E$8KX4whJP97rahOzAxXKug_=VA8>uR6m|y7v619CQv|X3JnFks z+Zk&gx01{dvoh*fVv)y;Rg;zB3Zwikw~-n7f&aNbM4k?GqmRM~k2` z&S9%6X1%Hv&}OSS)%VI3u)#6PABv`gsm0Y=@NBKs8fuvORXL_~R|MsP94Tj!zu<`O z0%^)Dw}QX>AXiXEDVLNCY8Q2rnxZz;qBULXs$bCa8ncb>Mo&|;URh6Uff@@-b%^c9 zmlJu{E%#`zD{w^6?%?Ag8$+AGM!gM7410)CBM~9kr{`v5Jp~=ebSIVzTXf_b;jM|wGY}!eZDc=jKJ(H?^LB)(!-h6$p7Fl*_e z^h0VS`U_om1~|Gs-cGVQTbD4Zx*41Gmzt;*Rck99X!NWdqU6SUBrGztF33e{?l^X0g7Wx+$>+~mD744GCQ(MRfc5uLR(MjA^I zp?x(%%qHe>vx#L{-|Vc^5c(Z6jcvqd5kE;g+)cfA1M&sc3oa6JGo*Ovub~m4Lg-B3 z_1S}~1l0&EA5h8L)YH=4z|~l4iM%)qpTb@t!x<60Y&m+sKm-iAz)_I6eE@KQT_z~L;0XGS*@dK+E%@ZVF2f?V;=wv zV4TaDqvSUGjC;dB6E=zEq;1ky$(G(oa{#ms6Hf@=_NkLMJj3F;b z4R$4>_xjvO_|6BMik?rSITtsHJ%>0f4VZKQRnX~V@3%67MO50yC}rq+tiD9= zp!513SpNidg<1{y@+M`PG7OL36&J33J*|*l#TaQ`w{klZ5bflKU2F)S`-1O=YU^jh z6=5zat#3j(asv6pZnhYEfkcp0W(`xDc}e%DQ-B8CKxW^~UJk~8r!h|-gzTWP(pnzr zzvQd!Gcm4m_-6Xb_;bTHMyeOJVn(z{?4Hh1>MPBWP}YlRU^NiBt3nxZsrXr}A#Iif z*Kk+7Yk>QqyNGA3=Kv78&7St2ICow5a95<%7@qJV*B5!oPP!A7$I&gH8H!wYw_Zwr zsx8twYGt*onp;azZ>X!)&dBT5D*2Tiaz1&ZKf8Y~u<=YjkFSs~+_%k_)4$(eLw+OA zgtc0!W`LiJ*J|r4^uP2n#sWiwRglePRuj9hQ;OJvV8+oIsFgNv4KXeQ zPnaOT_q9lSo3bH!9JHqU6S5~B>}_DUP-o9$RE_^vVAtYNwAR68D?IV01EOks}#G5a7~6Q@d9Tr*u4U5{OtTytGzTo)vaGciz% z1V&JrKhD+TzOcvGeQYfImCepYaI1koP<&QCfPcYF;R3j6K!HV)lPQVps|oJwOe@yR zVlFj`0CSn6*VhI8uC_vJrP116T-#vvnleRcqU1z-Qe6&HYAR!uCrTGJhZd|CHM*Jy ztSrt<>I+>N`TbIEHs1`mS-7|ojK+3xxR_6j6`Bf{_$vHKt{AtO&567~Ad$>fTA&+% z89oYx^OAYRxQG$FR^6&RkPCtvuHaAdz4tl3>i(1969y}j)PrE@N}1!VE4J!nr>ip! zNI~`s8_m_{AM=$EJ3fHMV6~_q3w$hvyJB3y?(RV4_PN))2O+weP#SzQ8W}hAfqHiRnYKwAiHKa(uBqeIJnAK-m%=KWz=!kcU{1jKv%F4d?BPw=+@8^q1!?`g`Nrd9$Y-Qdr*(St^xhLEj$_B-;u$N z7jp2I*ly$lx&>^ulgv8CN-a_8EI;P9^W^9L%b_t4HO84 zl8T~gzf5W^M8Tu{bL17uI(3CMN$+MqI7fSH?oM`&s_rgvr=pfJo8uA zG1o_z=qBznZ;w zFTp6Tr!@dJIlvqYE~S^#f+|QS(fgU6L}JtPm>XPwo)`8D9Ys@IEfoh^7vc_gk8_W4 zcW~!+KXmngQmPvA-aEoyydT#>gS9?KucmrAmBIO!H`^GibzK{vC8~4PBJlN-m6A%F zyhZLNXOQ3fW1-@l+f&cm8fb7_;IN=Sg9-(=5AGA(BseVi zcF?auj{~a&E($p9z34gNUgc^lr3%x4Q;i|d>0;DW`?XoqIG`0#_sLcKZ_`$%_DX4% z+#YrJR};!7+>T!nzb^h^d}%0uMkZEBvXUMr$E2J|J(qUb_u9`QzUr@?*2B!<)?aon z>KR>?tYP1CE+LaBU@nex5%+NSBX>SeC(lG=(=$9>Jq0{Z+`Zj@xjMsgWt4`B_k^Ot z5}xB{Ap2U+7G!sjJY)?cF;R3JdTifBWPaHW2iL#WY6`ypPw*3?%{r!G>_kpY8C&(* z`k%b*$tiJOsE~7E7=i)h}tTxl5QinxMUu)PT4WeMd}>AlbJ!f zvt_vyXq5U2*~MLApwv^^DjkznO9Q0JQatR}3n8D-obS%{WBZT}$O#)!4V?yd3#*Gc z+*qaG(=ut}z;mpIear;=NR-@4TVjS7)n#s5#*=XKTB( z>)Lyb(<|#k^*y=|2;&+<#@PRexmFaO<_`G&9rQ|O8JW&TfCHHTlsW>48jJh41Y_%i zcT9jMa4yDI#-PGMo}deXjevU=3yAVA@GN#ub~TjZg{eHxO(q6C2@Oc6TOM;0R4@0H zK60S{LE84z)hWl5lai_>?M|$k_*cS?goVfl?*U~Pm8b$!?~+_2rB-U+v^_pnj#6xO zgVCxYHx*DPtEyeO_Me68%vBP zhN>^t^XM^JH{{b>5VunB151?Nz>2c5(}EkzE+Yrgh2|ZV z<}eO}2C6JHZyvq4)>AzS?dE6sh`dd{Df3EwWts9(DGDa<3>u_X(Z*{RG*v6DN9jip zMFkq=ji%ryhZ$>)Ylhz_ZVolCn8mFi2wDXLFwd&)k=5gG+ z+|AsnuEnk#u5D6IX$E?(GD3E~0GEReK@3%oo_cp&hA?tD~zTy!3i0pEO_mAQTh&f-5L20MLk)3e+6Sz7vozQ^9^R;uo}!(E7jC45zpP>rAg&gXL7OMBsf7hP_ zU2?B7vq*XN z3EP9az!Cme{sCVOzB)q8DaA`yT}$2dJ#Rg&y^FnDypz3Iz0-lSy>q>j?jgG$iC8^9 z`jQCi2C=O0*Flqdx2aa-Ej01+LSs zd#;DBE3O@`39iPj0M|_^N)n|dVvsln5sZoGxH&tNtbmnDpqe1#>f;oJo$}l9c7pxM zeq^7smx1ZOYPGSRnYGOA@Bk}-Bb?N_Y98$}7`Pt5ZnCPW%1h-byk3HmM~#HFXbe8C zr%oB0jFQNnYgi|(eD*rK09Ip&q8if`nX04>I~bOA94u=tWUHE#&1Fd2p|D;dz84Ck zx9}Ox0d_l&+-3rp_K0mBIbQI{%d9VETiDAUh6}OtV%)`gdJ#QHH?(hBoc2lM^#=Mj zd zQ;CNxWP+KMv_wZzPr)y*w!cC9vdsL9F>+q7g?N1-W>h<^3vALMEd}Fu2GlfpFn(ql z+l*bt7L1}nMqT32h{|T-%h3<$;w{D zh)TmX(SWi&6>5sd#G<$|d0lJ4)OK+na(_ZS59_(_?&7}c5?z@kBEIA|b3NJ8L}cDj zXPiy;Y-CA z&TIc$SyQrtr^a4*;&N!kzKKK%l0qdx zN)ulo-x@BK7C#Bogb-l|@~v2|E<9f=_9etw9 zfkmFST3S!cX67*?lQBbAw9&8)XJ8R3sQJ}URY64RC_z93iviQQs^&okE$Fimlb!{i zlm*P}Ecm_8&MK-7Rwp^bi~^VP37KGV{vvY03qm&WS8=YmNQ@No!j@+j#`0G(!`>+SW)P>y(vfv^UR%o}EV%V({| z9^X2%sp9l~AbA=V4L%D*VxIZ14$&u$rMt9SXe!#FB8NZ!+J33#q zb|zBI>5R-%)ZG5U-eSXnwpQS0;)qkg(q$I1qTcBne*}D>0aPQEZOLvTADOJ+lm}BA zotIEIML{7j*Q{@PVK44uwC;sXK-*~ie-m1|O6DR{Mcx+&8}QK{j*jmOs4VmrIybYH z2_R5>ld9}o_C9N}nYkidHt3J9v9qA&xrki%K2r;4ou6J#1yIpWW#_ZK1$veew%
    Ie)E#K4rl+H+1u=D_Am#U1I)e{A5rFbbDp`{++-d!V=K|32( z@=2r~LXF>+$YcRJXGJ1o&I*op9>1MGfjf1Y-+l59 zFr~>hLf9_sSvH94z^%g3eZqWr%N@t@W#f*rRoIiH1lhu5W|q++J%uuzQBI0I%9iXs zRwpYv;+m`G9&-b<0()_<1l*^QR*WUsP3`qq38|Vh+xg(sr?yjGdIlnuQA`3bgc#b*of{uNU(3s?F09z$yaW-+So#ZX#>g3&qAxHY$V>$3zz5ToV6u9`u;QQi~VQPSOP8$dY@&PPVXRDY^J1?AF)HvKBj_fAY*i&pM z7Y=r1KlEqMx$m5gXd#%-20l0|qJ~t&568F(z@Y-Ur|d>{Fk2P65}mvuF=Q)da9iN( zUzr0;PsRh4a0IPWYp@DblCvGQpK7MAh)Uw@0X~?sfnsv zD~;%CjrK*Wg1F)jy*i?qyyhXOBje!nia3{?_EbC-MJLc*z-ot*&SVYlaT*DRm8i_t zU@NeBS(bf*ZlCqZJ0=R13u~|**F~&xB~UTWRHucL-?8i$_Ax|uL+loIWq857$V$uD zjqHKYcV4l5c4=oUVl|p-KrKcGmBLsF>>8bgiG(hL#a1WbDqRKB`JCJ!yT}MqlDuJN zfg8F=cK~j`7^|8+bEZ4xoVWIDsHeVK%dMK0idcHQ+1xA!9gK$g;TU0H6k1|TO*J=~ z$IRPUDIyFutg$r!5z<=dst#Mn@pAwwtS#0`YcBGdu^3B>@cVNs2j;{(jHj0lx`$Gu zX_i?Ge;iGUAjT^LwO24d7M&7)5rzpn5xJzHm+N=otq?1$LYLQK!Yh6P@8#!1g&)E0 z1b0?}8AL}@e_*~>u_LX+W|~pc*rn&yH*00IS3n2LsNa;+KwHKD&FiB~MD^-!RI;*a zU3Hn72L9~6)(oss3*!~4!osXG$jF18Gr-C;Y91YmBU71hvXA&lX|^Y9`8GBN_Wd#Y z2m1j?c`|E()nkF;Iq=o->~q+L4VbNUSi;^W%SdaI5!UGjR$YsR*DJ;l<{9Q}4fGhf zO-;wjZtrnNGdRD)3mmXYTc^zi=3}F$;nU~ph4ok3PAEObXfw1u+AA$g@2;QKa~RW% z6r(?^d}qwd+E|q%FVwV_Gk|(Rm8Tcc@1W-%#B66?G7>2QJx)VXn-nHCbBP(sgdt04 zk2|~yYo@7KrR+DSI+VGVooFXv6}AA3tIFV+`#W>6j@ebLdXg36z8jXVUP`S;qGJvzbz>G&l#20!4_{{fEvuDO?aD$*8 zzG_djYumPU){3%nS})C8@MQ~rD5g@4)5csQ(&&cANMn@|W27PP7z7)c2z}{q7%{Ib z2Iz1-yOrI#uFqUTT@4yI%=?MFB*9?~Ue| zGX<>QQ4e;;ZsNRmMp86AotEhcsO^)OT%;cP6*1-#vW{#ZTgdNZJK0H&k?Z6w(TJDL z2DYy#TL}M)Y!bQmW2TmamgOM2&W18i=xvzQ?Xc!wVRWpa=~U`F^$BzR3blzEj8$X4 zI_sd?ehv%aSc{>KJp@GIxe;aLH9lh$ZGgV^fPPi~qUSRD8pjMbqKqeIUBn@oz&by% zbHjV?!PTsQRrPKHn`(%a0ngJ4?s!LLB-9c!n9fXg9K#fNpF>#T@R2jn5n&s9;4ax# zf|X{u?Nac)`(TTzIty^_WvN-zd&K%H=$BZFusv!HHsVZ9L7#Y^dCtT^>B)dy3LrU9 zQ`3-yldhyAX#)I>BG;HnOi|`GeEVnAMVQVM>}d-;rU?b`OsfaHT^>t-J-%yhgO6)! z<_E_6)HrG^H^vzQkZE>?R&NwW)n%}*mCZ5mczLWT);p__eaL2=I?$-^ajrYhkQu!D z@$=646R*BQr=Alk<;Bz+=qQ)dDRg_xp<-kOqTrtFc{TtH*-9|%PmwizEd)RRMp?XQJ1wwnCFbF#%$fvrXUjeQ$41xS68Wf z)F(iH+X4TzwO)uqieOHB2J^BAIB-d8IP4OGala6mXK_RoH&9(a33I+6R%hHtzXf`j zAG59`bSaq_hyF@G#=3_aVQFT-ChVdw!!Cp{jWJ(;XOa=gS0UAKb(@fE zenzEl;qRN#Vf3HWL24m10zH7Ew@3G_(U`@vsTtI7Kp3)8e>%%xC;qS_p{U(yHADWj z6LYl|vYTvXcKC;e_-&d zbOpK-JqRl^cEc+lwGAsSen91mgu7hR{%9?;T3Gp^g5WV1)2+cdc#mavs1xn{LS3gi z(}{Ex#!oGH(Km#{46ep@V6lV@qO^`|Yql{SP4TG1{=(*Fz3iW4DQwjrOdlqR?n&># zT1-o@_T((glI2!bXvjO78O>#vx-rX2~hzAYPw9PH+~v+at_y z#mI`hZX(81ZfhEFfe8DiT?|p&8;qR>)UVWVY7|zc?142g>tSuridY4-J2izmO8Ka& z^kPH+b(!_>$OFh_l8J4>_J)e`GWdKJY^#S;;a_fH6h*Pk5fP-qE^LLhXaO7hfSC+i zz5uIjwx@PFU+e;QH}L!SV5Pe0x3repH>hhy0R!%d2=}xqX??XPsH=LZ4*@DR%VeyH zP#(0hciEQR*jeb@cf2^(iPT~0Bb5UYz*71qZP0m{YD^<=`Gpvoc}TCLf5obq23FTx z3SPejR^6E_6vMfyAL z_6t}6mC8U@!rdN=^-1T_lkutq{S{j-PF;1voi~VL97M78EgRQ$1ESbr$b+Jg0qua# zP|XsEF!sa3R6$I7-qxVZ8RBff8l3MjV}qfdDUE9#jw73iTB>Lq-z=&x)=xFCGG{~Q zJ>m|pz1ymX`k3S9GIKg+*AXD5O`sRg3IsMkR_?5i89$2lLdTkuEP^HO%9?nH9_j@CLPv z5F-(Y*-pRSQb>lNN<-97n!1!Flc! z$85hr5qRG^SfRBgR(bt}&H^0dBlVnmh`wcau4R8bY!bY+ZB@3S%!7zU zT4Pl81ZVYGeXPDxO=w|2cUsc$VyEMfV#+tL? z@Kytv-BmGcbzx~7d^!(4C5V0u+Y(B}I^(hW>jPZ>sfajN+Xvx+9J>U3&?e^( zCmTlG5sa~Xh{dO4G##NYU_I9>^mY0Mo-fl^k(<57TymiLE6P;C9vWcm=3^{;Iuh6D z2=2!fr-5?=(ZZj|ax$QHX_Ywy+U+LD@>?Q~--N6=mo>zS1O z_A~;%Fw!>dRZcPLB8BV}Yv}2jESCsF^FXLD6lYv7Q;GZH68Nn5WX0;>k z&LrTNOM#xwq&1A-n@(1zCsfLU)fwKSCMskmKo1b8ftR37z~hu zJE(6MV_mW&pjGSb+qNHesgJYVIfWy4V>P*kSUYY3wV%3+ag#>5>D+X7nuqm$4LdLy zpR%y?e_>5J1-9A)wODDYD}2HQ%Ap#;LcF1Wfd_wt(KP`1_g#`g1U4g^l?{NMxp-;^QtfeY2 zjrmqNjHeA?)>Udf_O+kMPR3(pyYCpsF7R?U$Q)7;D$L&)mWjrC#ji2T%OmEAcA7ex zy~A#1CtK@~)f%YwS&0mIJaqa;&A-gN@T0e_0`?s6mK{*_mI>qU0aXAi1>eAoZ^_I@ z<&pyAtqF9-BgrTdNqXXSAwt7r9Af6;jLI`QeVQIdXQmHeEL?QzJGX#_ezq3EUU{t# zuuxab2bjY-psL$~yI%{qx!-Q(Y{OcCjj-bI6Rg)<8UAhvw$Y6)Pdn5pj4Kn-T4m=I z#(#0tHm!okbIg5ady_LygCVPE1R6F};mDFvSx^~!=woHEpWADka4G~?<{YLn`2dA- zam2Y5`Mtast6)sRx_|5N+(c0L>Ac2u13E#oEy*^_zFx>ZDp0$eY|eD}*9OqCgqRai zt1t^{i+Jst7Ngw*Q&$BV;qR!(Ibq~8m!c|XkoAYv&^~HsamFCuE>F#+o>94Q4hyhu z^d+pZydNH|Kde(O;Kgycs@JGO%&e+n(rY@2`z@Y}BF0CQ1eGY25;{r=X z>`7304L~JGSJ>CZdR^U%b9jS_^rHGSu+`I%NqjPQScB|x7|AhEmKFszIF1CetJpy3 z=FS7Zaq~HW4t#`m&d&~FPm?$%3)7!Ij+MVh0i)}V3^>Y?ftzGRCeXl00YlLXEJ7(T z;{Ekwx_~>BW{k#aiQ_Hao{N}r66Q@LI!N}V-_xC#ySO{+NdhUu4g{{ej$I0mUY`}v z7bg<7_Z1X#ZJ~j=OplK_5cYhV?%iX+DDVXa2YS_a#HV;4oH zbJNL!X!s_T8Q1J6okr(k8Zuq55_e%nqxaBd>AmoT3sJEd0i3>=bqqZ94Wk!Wmpytj zpsKI6}?GrZ`QhFj{0N@(5KP-8qrp3Vv)SGzZPZwc=Uv zgcvRMguTVza{6^^}SGv`5LF$J2ijamyWv*t&hol_eG zuTfS%s@F2^AUAqwcC$X?zW9K8+s}3PR(S5P@cSd9_ zw|-zPt6@c6kuT&mIRmCBH`#B7AgoaK%M$= zYuF$?YHeHSgT{uAb+Q)p}J-v;eiKUW5c*c&{Q?#3SsOXMaJR6o;nhZSp`q{hMs^L z&WluUtZBdB>4sI-Z(=p}{&s7-F(QjG$bVG30lb*cX@`hkpu1q?B+OLT1(7N;5YW@+4QwqF;q59R_m#G)htjZw}A>N4OL7$ zY7AyUB{|d@XU_%8vytA;Y$UV6krjf5bdIB~KrPv=Fc}}4nKb~6w zEzb#P48CD4)Dr0KI@`Kzy3O8T46Er^weH};Z=!N=jk;I;05!#GRD12knEDfyqQ$I- z))?d&hcSx(#Qgh2cV@0a<1hq>*E5m?|8b9OLPV5}JYuFW1);efh`4kcRgHRqJTR|w z-5vo8oB+mnE;5aNKxfwhWyuU{^&CAJPC7-Y%~U9|-Y>w@H!|_eFUV8Jk=bN2=?iAV zK+ISiy&y!oD|OW2oCs7(XM=h>898=5_^51P@)u$jbkM@JVcK5k{(2%Rk3{YCSM#cM z!am|0qwdqmOd`1p)#g&Zns8C5BVGW*+C`cKo%u+h^GV_)F;S=}^x(%pkvR!^Akhy$xEK7{5mV!UKHc}xlbRT~AgIf`uu>#LCqsN`x+0?8ftzXrfY4#4JnQE532=#YTE z%-5VlUhI4=M>SsEs{N)%Ks!3rT4Y~x zWYp!<27}j?Yl+IxRH!j>NQsOHP;a=Y$G!zCV_7VZizp(J02>%jJfmS-|>!sEEh zpONdeg?9Cw-UuVAnf4KS;J#{OwKm4@RNyuc`44Ra$R$xKZX4+JJfI<8!P6+r2XJH)nX*7wdLSEbO&xS>pu5Y#p4T&X z7#X0KFOK6LtmQ{cehwJaarG&1=XTmrtvKq(IzjdR(R^q-&9fuAi1^sgFu!qQeW-#4>s_T@n^ICb#qDC!@ zsCB4{tD&5hn?nh?*FV=k-M_~F*k1+=PkqIw99G9eaaazyOuTssoMD6$NS#J*o{Wxd z_kfy=CVP;--XXETx1yn3ZwIU}Bl!+obPq;VPh^Xk83m}_31pfJkrU3uV+rz@yXe^P z3w-`wMrP_GBDjh8uM9H!HpuL20=0h)l)Ngr!Bl3}(<)UL{(6&r1N@x})i}TDyPz3u zt)5q^D=$!iQ%lY&yJU}CR33n;@G__s9f^J;9^KJdGpkhxnxpd+!H62gZsSg&2j(oO zFutO@-F242AbM|&P2bYwjd9w2TU~)8A>d&)!ld;#~gf%m8UCGW07UJ=+5*( zV0C4Iam)q2u$ftoD_9Cl{t|eDEy(VsfWt0okF#PBU$!+i>d9Jr9C3R!McJzKMKxhf zrMVKNJW$%AI=hob1B+U2bO9gM(jM>Zrk>DV%(_)jK)n;v#4pliRHzk)4&Ud=?=9+e zquOs9(_@K*yV387;^Ar}<0!{pbht zC)5n3!5V%~Cw#mJZ-DiZK%qk3Yv;5eWly+d;k8 zdyKRr%re9ay?@{j)sY2OX6hii8U)007uG09WwMc`ux2sDN6Ny+9bx}M3|JPGtl?aJ z@QSI(=c}^U5b>X9a$pV|q&{QrR=2xay-=w+P(O^Gh2_+#(73gdZ=thK4*w_LYoCT{ z@acZdKNEE*pOh==X)RWNiHg4J_AKWvz(25^ACk9;wq`8>yE3cdzyO8VC0jHm_9W2z`Kg%BMe*a2Oi*E}$1tdU=DG_n?Wch&XWqvVuXtP$~l_eFYZ3Gvb|7%oFAdERMw(ObRfRJ78}% zGYc`YhM+gf5MU*XV7Kn$)1t@^x026bh9j4UOT&RzKcRiJo5={>MHpiPt6mM1K%tgn&HKGr<6#5p z-j-Pbh{HYRT!S{2LaY4=HS`?10Jc%TKv&uy3dnG!pArqlT7>#q9jO)66;%AApNv() z`ITCO8t?*aFK!ioPPiqWMDPCQZom6CPmCwkBY2ZNXFa1mVNf6YUCmsxq&?y#Xz31c z3sDiCmw5)HqNbf}?m@@DK3aEmq_P{GB>VXt-!b0|-&o%=-&J2m|5U&2Uo4jeAH7!X zt`!ERw-2l7d98g`1N))f9JaXxP@`n3BYl?kU{oCg&RY+0;vvk(7l;~O0X4ZwP9Z8< z0>3s3xyWQ(!O3JUJlYOa3%(~RYM|16T`zVFI~3It4S?A_hV{xy<}f^PmB#dbWT+#Y z7j_L)c!gS{5R-RDKZG_q_)P5{YH(E8^R7x^g(!*~2roDc^%8mEiJAZ}x}rxLvtU;@ z*@v8)R2-sCo4CM~-{NNqMWG7IfVh04bV@pkT8>Vr(%&VP61NGw&<)+sEKp}{hRhHs>xnbmHHPx5Q)wIcdutzrFrO*xD@*AKg@YRaCmeqItV{KnCVTz*=ndL_VcHO z0iq>rgw~1+RVx8mCqEYN(J;7|(Q% z-bp*JmO_Ny4qcQV%bVqynCmMs4^v?w*Z!yj2-3Er-s~fCsN6uRn_3@G3GfwtcK&j@ zQ1_`i$oaFu#=l`IKvQ)VGcgOaC(Y5*BZ{5Fu4NAbn|{fDWYbuOHP~iYvey~ncaw1Ycd>jkDdd*^A!-z9Moy2o$~=XkZ7+(Pm~xl!b~*g7{yR;+!sfE z2|9s716&RM z9CYpDgwx>hhYUj& zs{@6J1oyLp9Dxq)0NB(WKrYu{wyhwGVb8_@!EcTVl6XW4tr?kK4`%fbAfY7?0~~Oo z(7#FqK5!aL^Fk;goc*)#|x{^j_0aGwvfhAf9#bG^Y5lh1pmO+L;5n4aa z>;Nu1$eLlvR)6RoI)HDijEp-!9StsYD^rl{MLcqbt;wB3AC?U~gW9)=!gh2SI3O%V zmw}AJd8nhlp?c~C6p&L9?^<+8=+*WCohpgz^bJVi6L9CLsFy79Xt2|_fXaPi1~8wH^Ql1l%0MNy2suw?@Hjib z5mo?Ra1F>z7obooINE*Sae9Cg$!Mk;w{UF^|4>4nFdhPd%4F6B3vv*>d8%8p(cvkB z-N}wdRFli;3H|%o788ZR@s5J)kC+m-e6)UfY(@w9A*Q( z3C~NwS42WBQ36WjIN<6N!5?Iz5^(o7I+LNZXy-J70;3!{q!n>;1C0#@qRBZ9v>1>( zB15!of_FmjNg<~^^j)oS9fmp6&|Pf@V&NOGO7TEWdGHaL5!L6xBNzV9jYm$r%7+eu zrH~WU0Gi(lxMDb1$TncK>jCX353Vgg{w5O@M2W!DR42{(24>(Tc<`srBjoZ=fBgT* zd4m5RI}gy`>pHTGSe$>X6NBd~&N;l3>=1k#Bt4!vO2WhS;~|1Spz)po{=kPTmW-cN z>?55|P!VzABYejNe8&SVWFU?rkn-ZS8%HAmtrD;$9^2sX11R#xPx>>;gYaz)TakZk zH+`!qc=)hY4eXPJEfai;$9CM)3{dZKZz?mmH7uW1{hw z=?@04(qE_VpT#!P&no>$z1V8{Ss>@Zo$?aW?6{%Ri3I#PQlta-|;!_v1)}A6w>s9BulU{d{mg zjy3&w)31B_*&09gkiPx&NBY%CKlk*9gLB2xk26mHTjt00P2WC}+8=vL-|o+AkbWIN z<^9-u`aaUHZ2DRL{7dp5drRM5`j&qF`}v*ptNHU-(m%D+claOsPd}sd$KT)i@zMYP z+uvLH`TyU4^YgdAzw^&G{`K?pzxsRc>95j9%zr+PAHV*`(|`Zw&kuec^*`UGsPwUs ze$Re>#@aKmY6B{`3C-{3`vp z)9+mRzrUYb{rB_VfAL?>`d^RdzaQ^^-QU0d{`0>4v(0~A{d33u>o@*azyEy~e}4X-xAedI z#{bRxKd;o^&;MMF^!@yw_w}Dg(*JI+|K~0J@4o%-&;ES{{(S}heFgq~1^#^n{(S}h LeFgqsufYEYMoFH` literal 0 HcmV?d00001 diff --git a/resources/sounds/Out.wav b/resources/sounds/Out.wav new file mode 100644 index 0000000000000000000000000000000000000000..af089a7751594fdbe5abc469e964a03d34790ebe GIT binary patch literal 18478 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 literal 0 HcmV?d00001 diff --git a/resources/sounds/Shutter.wav b/resources/sounds/Shutter.wav new file mode 100644 index 0000000000000000000000000000000000000000..d5ac1e2d8a5c6fbcb57ed0d4ed0a9692f06d2898 GIT binary patch literal 20780 zcmeIahnG~v(>LB#)puqCyGzbd1(75{kRYf;6)=Gail87#f+A5-K@XAEHe^Y`+WSVoI2DT7;I3kgW7hrwMh%?4w3UqiUrII3-|-0@BAz`{`Tn>7U|_;hypr z0uF6e$;1Gq)M>lYpfbtBuQ=VxPqct~#TFD?G2or^{_A~%Ta}ke$^(TrB&MV06i^YD zbO5?S7$Yv-5We*$xgpaS$) zIcdB8ic3s;s2+V6MTMfH zfK}UcH2qYa=vX0>5BgPB7IO3puRDx`bg7QxHjxNe1|F56N=|J=fM0*d ztsq>`D~!8pCsM$N?{YDg%1?D2=RqYD!7t@jaVsXZ6y;wf9JfTZxoq51Zex(N_Es)b z?r|wAq;eR+2&y}^^tc@tDxk^~@Eb+D{#o4hajiPb0|vcLG1pvSK!po1MZv4`thf?D zPZIj%;&%?T!zQtyM|}M2fKiM}xALC~96Gj2RW+hCg$ksp(ojoM4)TFjZ8C)MeGnVD`{oEg=G~<1=0Hc(||nRV!Ed5@ZwurntQ);#zGr0@;vBf|cpqh+DFb zAD_jFBLV&69zpp?E$A;M8LjGbuHmgVmW6S2?kHW^z!L?IT+pXHYwtu*n1~nQt-J=1 zWO6|dor7V(M%+bkt9R7C5^*mP*z)l!4_NfCFz%_{sHao9mA|-OxQt&qA1uaB!MzY* zJlqT8I}cd3hu#gF1W=$@EHLK+X9Di&oX#o06#+lW7egX9G#Uc+YD>z;HPB4Jp>xNW z47BG%1KOrmt&l#byb8)Ku&94hnW^>p1=b%1WEw`(86Uy8YSB9XEHJA+!hlagYXA(1 z7$JgrV=!Ju0gbA~WZX|cADwj(Xd`}o4ZLJyH0`VMSBd6>677)*DkH$G8dj@j#QiFP zMD*7=rgF%Hl+?FHz>h#u>U|ivwZG1dYkn%Y^il)+p%k(Ni@W!`K-G zT1}JJDK1l z0^Hiav^j_N>#hvAzJR+Y&_Bx8jMjy3JLH`VJ5Cgofa+%OoaZs#Y{t8-3f=)6=F|uMV z1x-{gfL9tX15+XMr>ko&6=*I2R-pdm3Px5xo(1fwkYg24mX0yXn8WTcuoMHw)y+Td z5a_D_sV8H+kf{VIr$g??-8o<_3@Nt+ePzudcewy(EpyUsL+=vM{7KkNrt1ytDe#H8 z;5Y`JqV6mtP|VzEZZefkd9w$UwKc0;eZWLrEx`T^nmqHbJLYb|$Yo4(^B{Q30JamL z<}P6Q#Z@!+K{k`j?`{q(I@b+^Zj9N8vEMRBKye1%i`>o7%xKroj4`v^o4~aeBXj^4 z7u;=t-V6L|0C&kPGk2Lk+#=vFX&RfUu7i8Td}B7d1ghw&yN^vh^Ox&y#+$|N7sxil z6*p<_s@vh7F$>(6v;@4jG#%YUU~gcWy38PO_n8AO-Ap#0xxq9R8d&67frg{Ov0x2k z@QV4s%y3uDQ*<@h82l5Iq9N`zbBiIrtZ7FBf_AQ#>1rC9`IP3)1hK$(v&`vWm#a#5 znn|vX8SLf*1?-@vO7x0 zf`@}Ij7Pv@1_syt6k1Jpn3cgs>IJoIcVgj2Iu_4{Gyq|Oy9X%Tu;6iavDH$-FCmd`O4KZ$9$$f{t@>w{p*hfx8J#n zrVO#$;HKK*K|B66z&r@P2%5P>F3Z31J!YOuabKAFrX2O9>28?m>c8xE(&IGMl@2=4 zD)T4xq>kLzU%`hd<{t`R8#hHp2K8ded?~iWPNyD`@usBY1ZT}F zRNPcC&CG|f!hQ|&EVqHI_L!$!CqCwuW2Sd7tDZWjm6Ymv<%5zm z$ZX`w}Av*LXbTi_qazs2ttoc=j6>5^oA51orl^kbnxUOR8RJjO@O&QMMMoaef-@+r%)UAuT8dJ zP@X%;ZD??;hS$|w9jop?8(o?=Ch1mb;NA-g1>gE*LN%!oeI>Qsh@{7)GCyVgd_U-# zaNIwau!Z+UmnUW=pUR!*xyTcdPqN>x@JdF0+UxXgBq|G29xF75-^o2D_qrFtm-#^% z5V@B2k?9jWP2WVy*}_S0Mu)j?QyYZCdFRs31?!@Rq*!d})n*Cvyjk+awa@GZ^C$P@ zL7_}qliVsaBDTUjOobCZr*@JN*<840vEi|4DLvfo=;%ox8~9_ zC3Mn5LhDzsZ(&OgYTVpHs=q3lRee`&BQ{F@&QxANDKB=5*e zmHUE>$eCErq@AvGsGrn}Z3!j@cX}(_zF?xQ%ccEt-X=56JYT4}dn)+H{%${z4XIy7 z9`m~;eCUoOEeK7ccU<$()Yy4DAhsu{XOHl630=ZxXn61@{}u7&v4lhJrBH2CT^h($ zu1JN=X5L_5NWPn9x~`s|HPMdt?hZFfct=W^C!(8@R=bWoJ{rmI6-uRt`Onysu|McU zP?v}{MBetwCsy+J21m^K%-z&BTq-ol6R&OTCCatm@nik~H#X9U$Ix z=7h7jqkG8?i#{Ek@H_jz&?fH**)1Qyf4wP2zjgXc0Ur@4Ug3=ype`UUGuQH8d^f%$9B6*<_+3zqu#Dalh94!3-Sv* z?rK`=U!ym;mOJU)%F}32WGd%|937>St`9Y#5ndyj863t2U;`g_YbeE*VXSFV-xZN@ z;a#q$+0K*rnzxq%zRRm++nd|W`=&?G(6+Saf@$2?9=5yXsCSSab1U6?itrMF*Wh=# zJ>TbbaG&#+T$WaNH8|#?T;F|8|4=W!kB>^%P-DpnF_q@sW|L_m*Gv=MLeH4ClEv4! z5_P0m=2vsUyCN5CrX9N($*)|KhT3#qD?_}x-ca5n z>xj6U`O>uET{K0i*%KyH(&TelE5*1fY_2XhmSnS3guBWTdX#6-KeANT^BS(_Wts@J zGRNq3xnwU%vOGYy$tqq(`}mS9lO6KCykyBtwT);6=g4vz$M;L7scj$OpLw^uE2FqB z^`i{lPIvHGaQdLsm9O~-58!X*9y&{1xrX$zv!tYKmaX&&@8)VURi2eQDa_Nc+SIUp zq?at=NB9NVMMro#IDb_($UnSVe&lppl{=a$GEsc)XrH1Ip=3Kaxd9*Lr%j6dF1@)7 zca|{!Bu~m8rY2<3lWWjp++BX)N9~6^pWf#pk|+;RGcHcU_%Y0flG23-N(ouSX;N5D z(I5N`uf?5ia+n)X9=%Q5XgqJD*7hwvz$a)wG&otB$Z!0dIn19>Rr!vO(nMOuL-=X? ztW=dXTuiF*z5F9D1il?ypI?!I+|rh(dnk>M*unM`xUL7P=SUA(%6Sx(t$a`-{JXT4 z1DwcPQ&GtFE*Zqd z>@fPv%!m9w;v_!F5AzbnnujF50Ly2;f_wcXO)T;yO zFQjGkJNJMt=W-jF&FOqt{(vPO<53bZokL&qdOk>lU@@&>=^LdoM(!s2q%HTB6uH7L z%R*Zgt9uWAQrb~DQ1lL6M+{twtJ?GI$@gYGwKTtS4c)T}Px3~|fHvw$efuoWg|%IhDSVu#*=zEc?3CG50UOF`GD7;<;_@ed%0p=+m9RHS zS$M8T%}(k7eRY#LcC~%Pe&RhWbGWwW+&WlnTj*j5eeC3&cD? zZl)&u55Fgm^Y`)~ucO9%*wnDAsg2}PDS3teFwe=Ih!2-Rf*av+9m9>F#~apVQ88SISQ~Lox3b zGg_v5{mpFooKI0V-e<;!oJo|`R0mRjklwMMnj^g1o;M|Ei+m&h@)s7~W|iuaeUIt!ZE{z|QKJbUWLADtDPNJe94~RiA8nOJM8TB6Ljh z?Ju&`wYT5XEj$ah-icFeZ{F%1;KTO5gjX={UUVzWX-Bfq(seD}0gI~RlcCvhAMu*FDZ}U672;0kX z^SHD3lq+nPn+R-Twzrc;1j9pL*(9%*-!Jwof97R|)7?;S1N9Y;&X~>g5q%GS^4-kv zIjlaNyuZu<+aXlRRfNxZE9mY&9^P!rAePxq4X7#IZi~}pdC!*j?%;G63OxsJJv{C8Its=cfkD+9mvP* z34dN}aQKzP2qNv`3G4V7=`Xt@mvZwH@Ab~{75`)Elz4<@@{q($Sh>!{f?x@Z0lF=djLb$RFiuL#Vq&}W=B6zLBllH0T=dNt{o7fxncD@k4Ihf3knVpeI=0eIJ zMSl0Ma#?RSv8{!SLpj-$xW}JD8^dLzA0_8vh1`*MOrG&>Hx(1#x07;nA~~UAiR;6s zrL^0H+{eD~D$^(Qtb81M(sVKRhI82Wng^CWZz@M;TKC*PwH^#_r!;j8brVItIB{--%y`$uAIMiChflD{rtVTgdMMw zr)9JEK77W+;KKFe@>$ z{0iw@;&92;e#+H(a#Ii#YFu<}$uXJbPR-~z_Hc=zc;cfEEla1tG8g;#MiN2nMzsk8L)i3!e-4egeT5>Hv zt$XNPAuoJ)WEZ?#HEEboJn3oszBe@1*RPzq&2%lbT}tut)R%MWUp?lg*@LcoTF=m> zU`pjmR5Ecug|o54T)jxQ{Ld0bChZP=7I{h@Dz++NVI(v0?xa!XUhm!TsPGTrpAw6B zr7};3+Lul8r{u2igA0WdC#CdHtR{W^H9=DTar?D*##IT@lG2Kvk8~*9EB0{Cn&Q(7 z?MRwx!-dkk{DdxMdBXIZ54n73UigE|$;Db`Q)0)Y+T4P=*|F3%_b4}ViTTebr*kN2 zWq4H5yONNV6X}!iW>!k#{nRPHh?hmJc~oeBP@h^zVs;C!U!k++eL3h(<;Top(GR?n ziIby2(j&&>#nRRbC;yfINYcY5C3rvD!FokE#MUN~U6y}G$_ut<9UEOn`VUclpql>KeF6DkE*0tvKmJv;d&1>|YDU@H;oDDBeZW}Ce z`y2(6?0tTNV0CDsYm@Y5Y>HcHKBM8`Bq>ivrAK}m62v{^T`x0qCc4aPoZNucy508U zNb7`p;Z2yO#du%xhtxgqthYF^o;jGbFLB1@Hxon2mm|&XR&JT|O(-!I@;WA_2h(UB zEz92|H`z~Pw;<>AgK1~B@!t})&HXxlHh+@1AdH-XshC?Ud^Ke*ccHtbs_)VFazEmj za`q0yy>$suS5U{4<3b4~Qf@X(O?g11t=A^`Ykt@J*-iIbxZOhDsf2Aj+}>>8%KMf= zN!^n6*p*bojBtyRZ%t?(J?BrLCrxI!gSn57QF>%5vRp&#{!nFmI{Gg6;^CZ}RE9>l zAH8F-X+di{gsbv?n{7vO3$G8o@7|!L*wbwD`h>mwU{b z?8hz*5zj>P1)brA;ScB=uRNCxg@XoOP4}m~6Y{x5!W8#G>~n9m*~*2J9{_a~OndvI zd?JHwW%`TXH?PQge>u$xALgC>so5!eBsFw5t#{wk^D+xDMPIIEO4tT;oJ)JtZ8Lk2 z{*>~F8Yi2tWwBZ6U9=nc75;)c+ShrEB=T`P%Pf?Z+)7%7XW79tm7@HmbPlaV)bx*e zQaYMl_60f5&ApXwk-RSN$vUiZ*X4HmlYDL-!XDrYdxi5&89UrIu$Rqb`Jtr%v3vJ`yZ9nfAWg@D&-^Rk{g8`vW{57GX{3=K6TXvOn zrWN#y8DzVf53mwG?X8fG(u}W4Dcf3(njIX$KBl@ipYD}bHpEL2%hs0#^0=&Y*QAuZ zCZ`eSwy_PRr4*w(Ip4b`CAga%U_IYQwrMR*><=_px=L-XgBY~C8H3oNoE+z|7=N@KLYue{KZpJ5uZS6+;N~<{n)8p+ z!s~>XwgNrQNpe5E<_)GC?CSbquhtQfLVx*5ZsB2w!%9kHX=hVyU&K$xYz`0PkGx0S zSufz3Jk^Heyj(+cHkBKDsrDU2Sv{C#F*me_XfGo5K{Adj*jv2{{E0N9&4|u^;5&F0 zo#Rxy9+C5#TvoOr;{OdCJY$B!4u;52yns*AgNQ{Nde0-$Y)@~PYPOlogJv3HPyT{D zZo9}yxkw+$xAY-p^V`TuJt}kLdAo|f<_Xdqd(nM}B*OHxJj6}y8{7)o?vLoIh8-?5 zxr4lgxtUA5c@%#@QTu>>85Y`@Psk6nUfS_t^v>Y>5chVG2c)mv!WXcYv$DlJi1tNt z1v$D!Hbg8lWOVRcLKSC_uQ-9|a|fU2m>fZ*SrhT?NXT#uwX{EDhnQ-cP-6)pjXDo| zri$2gESHAJWvrLWrl;4LKDSM=D`Mw&G@Tgz_<;OUWMIKv!fk$7~0FRNl8G zDNDXJYq*_M;gR+b-z&B4Tx!ReoPikdR_TZx+LN>cyEbCq*1$v85%2@8#gn%X(U*}4 zlFXN}AFk$2r&K!upLf}L?oFy@r^^9Z&V6_l4MbeHi$@~jy$)K3Kp%HtUscJTqcOHM zUvX`1KUoNjw_)crRHCr>%kngRAFdb-_S*0;xr*J$5PJ~&$RDvUn~xC|+Yy{Ylk7j{ zO|PCz!^fmE~TU%L6f!7gA@$zX87p zO+U|3U>qpJ`B^!_&m$_Y3yYtLoy<;K1^qi?|MnszJIQtgFQ>8ZX~3^xC%P5)=76(m zvc#6-zKFa(r+Jui{Y7Uo}f^txYCkpZc| z-*I0%3_H^6)E=3D{)oDl!?qSkYhXRg-_rB;BVI%gV@7<(Vb3Se3v&}WK|}Zhn;--2 z9J*aT#t!T!dkXvJo;WT!QGStc_&uITOM!6?e+Exc-$qOt_GUL@UL58TRF5C=#>ofR zMIVtfQi)!GPsrvW*r`^udDKg4%06Dksoq&yLk~zN?6?=n8}MuIz-oKhnS2`7d5r(! z6WG;fBJ%uNrt=ZGD6@DxXWCooZr+4^#|k-Y&+#1I%!APWiLGS6qA%nfDu!&!Td=?` zoNU+fOnWOghqYYAtV)&r*uk#Bp1dWj?g3;gCdpR$mWSFO5%q_-E%r`ruutucJ>@WW z-aBCa2{kfN%)qS}J%exMT6~4W(%9=qO^_v+PQ~R`yOdL~r`(TOWo%e(hYzo1r(iZT z!_2=(E9_(NaR#&OBUuZ2{zN`+GXD!I+1m_0%E(!A$ZDrUn~%zaGLM_WKh~2Kn75tf zPDph%=H>fROnSiQwZ>lgNofI#-ow4Qu6>OP+bW(jTM)9_oL!V5jiXXr`0kk3m?n}n=PqOAcx z{+PUg+*Mc1q9nPGeumGowl((DFUdA!%RWN(st>iIyX|J0E~})mnZYkw%j50uSlLtI zO)hID9<#EF9D?o6mgc}WlE=Ve-{vXUZQq0)VmIW28Y9yd<$Jx|@a2aonHzE~WYMza zF7(gmT=>yl_G#WFz05?}Nc|z<=H`su6!;0H=!o|XvN_jbfhXb7JKIWp#U6#NPmq=H zoCEC?NHvE>0%Id@24~PqSnWOrB{y+||KwD7yIZjvyUwS$4Dy{jV3#A{-w%T4Zd|({ z!|*Ho#u3?Rup5zXERu^H$9_6MYk=(+WK&AXAM_A>_5t{u<5U9P?REH(N}Oq)2E|X< zN4cJTADM~A?3AHwR-8FO?td7}Fm)nLo@~*-hVK9qVCEL$6zdq2Zy(c%GMCW;7S&Tj3vj*$wV{ z-b6psC&&Z;fPCZxxkH`-_v7J5reO^c&Z4j7I$edfa(Nnm#UvY*4{ zb>3mdBOaKF*W{pQ+VTt;Km+V(SoDuFjrY?wL`Z{SMR!pR z-Xi17F=ScaLayxsUq=+w#x~|E^n{GSJg<+uTM=a0|AuGZ2>mpN=TD^^(Dxv6R+SLv zt)iRkPxLGzo+rr4M8h^&9;N%`pjkzQVNoq$odNHpL&&}T4GP|sqi!{Hb%lQAN5EMY zmG$0ppGzf3dmiS`_gIUZnIK=nuYLyayO(-!Y2%g}D!WvwfYr~%vg1pka zW~PjH-lZZ(`4X}VLtru1mO{+27S{TRWYVMXqvQCrIH?7z&!*Y(0nfm^`aQn%WKg5{;5xn8Up|(NvP{Qc+r9 z1xbdScS>ult1bBj?nuwk8@3mxA=WKMO%RI=miCeY`&vNrj1kX{vQL{Kw8DI6zeP;F z%=)H+^fyOnC9Sg;_-4%9h^#P&xR>;!RamKCMqYcl?2<`b0ok}_$c&ak{`Oxg8FJTRg5jXO~=5B94*4&QvP0ZtY<{7M;zhSnQv-QmVQo*M3 zog$F(JXq5|<`mYk2$iv2_#@aX%NSaWxb`;eKueid>~GNFQreD~xGH4#Ap2ZcHqd)q z!Ca+(unw)~el(lcBCGt2?MeS~Z^~s$!{~3BLcNju+lTD^So;w6OOIpjtl(89#Gd^K zJBU@-sl0?prX|*+u{;ke^(u44bg}iZmd)lnu%FpN{k=)SAX|%~F2$BWjIl_1b6>gu zJG++&8R2>G$8C_S{ueu~2!BU)%(HSAR`9l59RB%LT1zR22hMSE`y;G$4Sj=|ehhK! z53~;Q-6s1X&$7sqKg5NQ4Xp<6_aJ;~C)r0|Q4MbZ6@@4G%bdoVmCRpb4PHxMVJEkZ zwn5T;Xs*16=plf1hhfEND|h3n5O&TDWEp*q{QM%!fx4y?tm&wE84<}g`iZAYcj}M$ zpbh5Hb$T36vwr0F$mhvand;e6$jYvl($MbB+=;8(8pvEir+9`mM9LxaosIRVgYEAI zVin)RU(rCkzd;=E8~FVgxzw$ocNpUTXSp5t8zHCR6MjG(Ta@mUW#FwSWYGnyy>E-r zui)h`7Cs4zf20ob1$HlY5y9`g3QaFVq;(ofTt-B!z1mWr{pI2!XN@g z{9gl^@EOz;QORYjbra15&cR;bP5u|pLq@s^_7=WL=3@_XFIBPC5c6$EgfbOJV^^71 zu}ZE(hX1MhyZt*Wq7_?S0MM(I0;qLdmAfe9eK+%HFt(~ zn8B0?AImgH-lh4xLyAx`@(9Zzh1xQ~6z0)b8)i{W8Dc8a7+OV)8QC3C!5i?neN8`V zBhSzhJCO&|ub2ysDW4voE%4^WVb5ta-S)tWQXMv3CQL=gWmJG9GcTkOR3GmlmD&eeMN3WGh zv~|IA7ikl;>`YkS zh2T9N1AX-e^&62LD@G^qtTY2wOo*Ab%VKGTXlt=sD`PnY8I4MKCi56}R3FPobBc}` zX9n|&t}|k^L#}_QwQY%f%y)=gUXpg^q`XWGTzd{o7?$w~&JmTDmt_m~Wf@cszWHTy z4_`M~c#eOOrkkS3556p&^~@)BmMvwsX@$sO94~NpAqMP66}hilY}?@(*?p20ObETm zk4ru1v6tBu$`6`(-@!|rq1SL+e41Rfv#DC}5vcn?~W&jMa^8C>nuXI$+r@+E5r9o`%qE2 z;6Lh*i6oi@(d9hA8;IDdGAyXBch-F&6-{0+M@EHicT3G%cBtur{LVU1bCs%jv&}u; zeG;Y-W}!QWjAt**&#%4opbC#hwzFGkhS%OJ!fjC6a4M)1^bIx#Wx_q^nzz-oHsmFT zhL}1*O?f5qEKbE{xEkSfyV4tK*P7Pep~xjSGJ4%#;pMomZHw@~GSgf#OA)WTa5?Gk zPvxFoCp*wqG*@Ds%mTlWYw8kgUwe;z5}DuD^p|VvTG9`;c|xm%SIk|pa^}_i-R6Xw zYZCcF!mq(!$a8k|=fjq&_;-gM4Q1oG%_ov%>-yhdk5Df9xLrnzj32(oYwspeooK^Y zAL%RKAj`T-YRGTi6LdLx#Qnpk>|fMB`arCje=6KR^aYhnn4K^w)E6_gbZ&`QhDnTU zqUXHPUaEAo6GK%!FR@Z=VPvme$K%4|xn%59KH>Vvn{H{M$Dez}uK50#;B3;P#Uh0# zC(keb0OjU|t{shK`j2`ga%bm$X1{e4%%1#*XleL`ViUrZ@~cAEHOwrNl!xc$rhM#d ztY~bJS1x?fEkK4kOQs#aE5FXEh2F+0TPilJzNo|4Dt`5-|8M%t3%>`M*ZwiHxMK10 zZfo#)&aO-MhX;rHn|)qp!i&to+|W%tF?kiOi#(hAo8gi>qt~L3bMySi)7nUS^p?o2 z{vYnK{13fxG`7$#f2ec|FG+l;a8}UBFM%hj^-Wo~*ZZ6or2fGfxwjTNX&y-480+92 z3nwQ(7wQzLkaN+$C7fUhR`^M1i`Uhx3g3}Lp?OIS{ms#F{_t26I+y!&!tsOyw8~p( zTPA#Ezbn+(olUBoRNH%kzt1}n&2sJhJpXdES@I^g(YuWE2(xf_yn&1l?hKbsZjD$Z z!PO7dl&9TT?|ST}U{Yv~H{Z=j{y5<Rnge>mj*r;%eq_@3VUW*ViN(lqKQSy3ZX!H>p70g6z zGTH0stqQjZ9W}mu1V56;TVj{FpWT`Cs9a81fHRBh5nrv3WCf*>nVjZjd1sA<1>a83 zMKb&k-GOka)29B)v0SxH+CZSo3l6> zGRf|9t8vclWY97CGPe$uv>g+hgqPEquml{vm=}s3i!Arsx}&ix3DXi=rwmBBJ#>ku zNBaf8=WUC=ixb#wz1m(i+tVD!sn@1-$+Ys)>@PAr^a`DIZw2H0+fA|9vs8r|g-3^O zml5_6cTDD)7XHZCb$^iAiddyKa>;juuF_yz6cJ7i)sSUDZSzua$e$dW>F#oPf@$`8 z$K*jf8v9CEp1EjzKQH=_X@F?@ei@EEQnt5<4*A{OLbn?E(>Bh-)4NghL{K(1oxXOt zGR3QjeL^z7K)YfqgPN4-UkYYptw}SLuO|Wg60-$geb~o6I272|Li8Zb`5<*z1qSIoqFIj(^g$ zpmJoev)^TUyN)j8>c>_EMciPVn?FQz={7_=$K01eI2eS}`U6q0_Ja8^c;7u3bP3ku zGV%cgi5)o2-qF-C)7^NSe9y*N z`zokX2%DK^B-*q><8^%QbTx3I`d(B`Y(_QB3efPCoA2&LEk`j_Dm2Dvf?cQw+2gjj zdr&8kV7i;PQF*Z5EpbcTMx2KK31`=3J{jR22hjaYDf`*8D7$>!hncq+q zvDs}$HN_EBUz9erQB$!4wHarjuL$ZY8k$n5WcVAk5~YFv6evI7{z3JIU@b@h*3F>1 z2&+KWt#@h2b;qpcJwts+>PQc=GUb?3pC){ba>Nh&H;Qn3#2Lfu3T z&U#qXPGq4%qzWo3^gkV(L_Nl3KxJZt9CsRhucD49UYVL^s-d!@Bzm7h{l+Pb6T&#B z!Cw-pdYn5B*d#!dLWM~NDqOTK;~ZKpVO*_ZvVhX+L9JRV0m*0$Ngi+*v}S@ot#Z;j zvWsZd>Y%Hrr^!bB$1cd^AM_^Bs1-C0l^w@0dK6>iKq^}Kq_stV;T>~-gEp?~1rz3{YCDrPU~%O!`PH{#s2edXVUJ)EB(3M)6*8k-c(4qB(S|`bnnAXE;ji}atYHgOKZKn0<8HYxJ-vVW#LW^KDBmBWp)KMRo7u@SKU#_?sz;?z2{-cx6A3VId-PR6HJ|7zWtRt9Ueo!0tl&0!vB*9z4T zG!+G3S`DoBpcS5~8J)-JfQT0Gc@^+lZ+9KCJP(K*ysqLq3oTiY(^beI1HRz`tTF;w zs`S;%T)?$@9hK)r$l@&8&)~Pt|9{b{^>X>Zn2z_qc%Q@FbhK-2p;oJ%1Kz6zI8;X& zcx9vI5}+=k{Ti;aarX+K&ja@vTph;P*8!IY|ECsm6?pXCMT~O=<7UHV{slGXah*{> zLw*7Ll>#Uqca*ma7&{-I5%f<-+ZABSfV6V)c@;R6ZyiPJ4%Ir+@m5Ks7xc~oq*g0x zt$8*iuT_`Xph&A^<5JB7MhF?dRo2%5qt)hG|9J^gVq9rWs#-+6qFZYuwQgSfYZa!} z7w2PUY2CE8sprx9&q4(=NGqRJ$65!g)v{{gVOWFK8LM~K8daK_o~I@ zKyE5W^+1{Ul?lD6@6y`RZ2Y>8QFOkk-V}$<(DV4t1+IL&uK`EifAvrsQA^AKq}o$l z3#w7ANL1bH7H>rWIpc3?K%LgR>j6`T`5u9x{g-Wo?5?F?P^7> zdS#VUoX-eoQWUbTts(P&8ihDgw|1OoxIW#|I<2b^}AZf9ghRFB3hx;uj;2tPHj(D zF|9IJ9cmw)m--!tRLfPlE2V19`qqANYuDepRw@-B2cxS7bxgfd3bk%tDTr5|Ya12N z9iK1P!E>Kzz2wduyA{xcH~yQpmVu^iy9dJH6Js z3aiv8yyA&N>V5r-|DTLF&GDA_T#omTOI6$BBgVh=x88~4xWP&M_YJNTK2C+oBo43r z;(u`{?V}pV$9iy#M?4f5XSeyMZ?j{l9(QfcxL> U{ip3e68Mh<{v(0^w +rem + +setlocal + +set TargetFile=%~1 +if not exist "%SigningCert%" ( + set SigningCert=%~dp0PlexTestSPC.pfx +) + +set PasswordArg= +if "%SigningCertPassword%" NEQ "" ( + set PasswordArg=/p %SigningCertPassword% +) + +signtool.exe sign /f "%SigningCert%" %PasswordArg% /t http://timestamp.verisign.com/scripts/timstamp.dll "%TargetFile%" +if "%errorlevel%" == 1 ( + echo FATAL ERROR - could sign %TargetFile% +) + +signtool.exe verify /pa "%TargetFile%" +if "%errorlevel%" == 1 ( + echo FATAL ERROR - could not verify signature for %TargetFile% +) + +endlocal \ No newline at end of file diff --git a/scripts/build-konvergo-qt.sh b/scripts/build-konvergo-qt.sh new file mode 100644 index 0000000..1e832ea --- /dev/null +++ b/scripts/build-konvergo-qt.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +./configure \ + -prefix $HOME/Qt/Qt5.6.0-alpha-konvergo \ + -opensource \ + -confirm-license \ + -nomake tests \ + -nomake examples \ + -optimized-qmake \ + -opengl desktop \ + -no-feature-bearermanagement \ + -no-gif \ + -qt-libpng \ + -qt-libjpeg \ + -no-openssl \ + -securetransport \ + -qt-pcre \ + -no-glib \ + -no-cups \ + -pch \ + -no-eglfs \ + -framework \ + -release \ + -force-debug-info \ + -no-dbus \ + -skip qtconnectivity \ + -skip qtdoc \ + -skip qtgraphicaleffects \ + -skip qtmultimedia \ + -skip qtsensors \ + -skip qtserialport \ + -skip qt3d diff --git a/scripts/build-qt-resources.py b/scripts/build-qt-resources.py new file mode 100755 index 0000000..94016bc --- /dev/null +++ b/scripts/build-qt-resources.py @@ -0,0 +1,57 @@ +#!/usr/bin/python -u +# +# This creates a Qt resources XML file. +# +# Usage: +# +# build-qt-resources.py output.qrc virtualpath1=realpath1 virtualpath2=realpath2 ... virtualpathN=realpathN +# +# "output.qrc" will be overwritten! Each argument after the output file +# consists of a virtual path, which defines the resource prefix, and a real +# path (i.e. an actual file system path), which sets the source file or +# directory. If the real path is a file, the virtual path sets the full +# virtual file name. If the real path is a directory, the directory is +# scanned recursively, and all files are added, using the virtual path as +# prefix. + +import sys +import os +import stat +import io + +if len(sys.argv) < 2: + sys.exit(1) + +result = "\n" + +def add_files(virtualpath, filepath): + global result + + fstat = os.stat(filepath) + if stat.S_ISDIR(fstat.st_mode): + for item in os.listdir(filepath): + add_files(os.path.join(virtualpath, item), os.path.join(filepath, item)) + else: + dirname, fname = os.path.split(virtualpath) + result += (('\n' + + ' %s\n' + + '\n') % (dirname, fname, filepath)) + +for item in sys.argv[2:]: + virtualpath, filepath = item.split("=", 1) + add_files(virtualpath, filepath) + +result += "" + +result = result.encode("utf8") + +try: + with open(sys.argv[1], "rb") as infile: + if infile.read() == result: + # They're the same -> prevent cmake from rebuilding resources. + sys.exit(0) +except IOError: + pass + +outfile = open(sys.argv[1], "wb") +outfile.write(result) diff --git a/scripts/build-windows.bat b/scripts/build-windows.bat new file mode 100644 index 0000000..684b839 --- /dev/null +++ b/scripts/build-windows.bat @@ -0,0 +1,41 @@ +REM This script assumes the environment is already setup. For example: +REM +REM set PATH=C:\Python27;C:\Tools;C:\msys32\mingw32\bin;c:\msys32\usr\bin;C:\MinGW\msys\1.0\bin\;c:\windows\system32 +REM set CMAKE_DIR="c:\Program Files (x86)\CMake 3.2.3\bin" +REM set QTROOT=c:\qt\qt5\build +REM set EXTRADEPS=c:\whateveryouhave (see below) +REM call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Tools\vsvars32.bat" +REM set BUILD_DIR=build +REM (BUILD_DIR should be the name of the working dir - it must be a direct child directory of the repo dir) + +REM EXTRADEPS directory contents: +REM - download libsdl.org/release/SDL2-devel-2.0.3-VC.zip +REM - copy the include dir contents to EXTRADEPS/include +REM - copy lib/x64/SDL2.{dll,lib} to EXTRADEPS/lib + +scripts\fetch-binaries.py -p windows-mingw32-x86_64 -n latest -d plexmediaplayer-windows-dependencies --nochecksha || exit /b +scripts\fetch-binaries.py -p msvc-windows-i386 -n latest -d plexmediaplayer-dependencies-msvc --nochecksha || exit /b + +REM put all dependencies into a single dir +rmdir /S /Q dependencies\all-deps +mkdir dependencies\all-deps || exit /b +robocopy dependencies\konvergo-depends-windows-mingw32-x86_64-release dependencies\all-deps * /e +robocopy dependencies\konvergo-depends-msvc-windows-i386-release dependencies\all-deps * /e +robocopy %EXTRADEPS% dependencies\all-deps * /e +set DEPS=%cd%\dependencies\all-deps + +LIB /def:%DEPS%\bin\mpv-1.def /out:%DEPS%\lib\mpv.lib /MACHINE:X64 || exit /b + +cd %BUILD_DIR% || exit /b + +%CMAKE_DIR%\cmake -DQTROOT=%QTROOT% -DCMAKE_INSTALL_PREFIX=output -DDEPENDENCY_ROOT=%DEPS% -DBREAKPAD_LIBRARY=%DEPS%\lib\breakpad.lib -DMPV_INCLUDE_DIR=%DEPS%\include -DMPV_LIBRARY=%DEPS%\lib\mpv.lib -DSDL2_LIBRARY=%DEPS%\lib\sdl2.lib -DSDL2_INCLUDE_DIR=%DEPS%\include -DCEC_LIBRARY=%DEPS%\lib\libcec.lib -DCEC_INCLUDE_DIR=%DEPS%\include .. -G "Visual Studio 12 2013 Win64" -DENABLE_DUMP_SYMBOLS=off -DCMAKE_CONFIGURATION_TYPES=RelWithDebInfo -DCODE_SIGN=ON || exit /b + +msbuild PlexMediaPlayer.sln /p:configuration=RelWithDebInfo || exit /b + +mkdir output + +%CMAKE_DIR%\cmake -P cmake_install.cmake || exit /b + +%CMAKE_DIR%\cpack || exit /b + +..\scripts\WindowsSign.cmd PlexMediaPlayer-*.exe diff --git a/scripts/dump-syms.sh b/scripts/dump-syms.sh new file mode 100755 index 0000000..07784cc --- /dev/null +++ b/scripts/dump-syms.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +DUMP_SYMS=$1 +COMPRESS=$2 +SOURCE=$3 +TARGET=$4 + +echo $SOURCE + +DSYM_ARG="" +DSYM_FILE="" +BASESOURCE=$(basename "$SOURCE") +if [ -e "$BASESOURCE.dSYM" ]; then + DSYM_ARG="-g" + DSYM_FILE="$BASESOURCE.dSYM" +fi + +echo $DUMP_SYMS $DSYM_ARG $DSYM_FILE $SOURCE | $COMPRESS > $TARGET +$DUMP_SYMS $DSYM_ARG "$DSYM_FILE" "$SOURCE" | $COMPRESS > "$TARGET" diff --git a/scripts/dump_qt_symbols_osx.sh b/scripts/dump_qt_symbols_osx.sh new file mode 100755 index 0000000..a64ca2b --- /dev/null +++ b/scripts/dump_qt_symbols_osx.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +set -e + +rm -rf symbols +mkdir symbols + +dylibs=$(find lib plugins -type f -name *.dylib) +frameworks=$(find lib -type d -name *.framework) + +echo "Dumping symbols for dylibs..." +for lib in $dylibs; do + libname=$(basename $lib) + echo $libname + dsymutil -o symbols/$libname.dSYM $lib &>/dev/null + ~/dump_syms -g symbols/$libname.dSYM $lib 2> /dev/null | xz -9 -e - 1> symbols/$libname.symbols.xz +done + +for f in $frameworks; do + frameworkname=$(basename $f) + frameworkfname="${frameworkname%.*}" + ffname="$f/$frameworkfname" + if [ -e $ffname ]; then + echo $frameworkname + dsymutil -o symbols/$frameworkname.dSYM $ffname + ~/dump_syms -g symbols/$frameworkname.dSYM $ffname 2>/dev/null | xz -9 -e - > symbols/$frameworkname.symbols.xz + fi +done diff --git a/scripts/fetch-binaries.py b/scripts/fetch-binaries.py new file mode 100755 index 0000000..6937d87 --- /dev/null +++ b/scripts/fetch-binaries.py @@ -0,0 +1,278 @@ +#!/usr/bin/env python +# Sample CI usage: scripts\common\fetch-binaries.py -p windows -b release -t abc123 -n 33 +# Sample desktop usage: scripts\common\fetch-binaries.py -i ..\plex-dependency-builder\output\Packages\pms-depends-windows-i386-debug-dev.bz2 + +import hashlib +import optparse +import ConfigParser +import os, re +import platform +import subprocess +import sys +import shutil +import urllib2 +import base64 +import glob + +# Edit these to set a new default dependencies build +default_tag = "auto" +default_release_build_number = "96" +default_release_dir = "plexmediaplayer-dependencies" +default_branch = "master" + +def sha1_for_file(path): + hash=hashlib.sha1() + fp=file(path, "rb") + while True: + data=fp.read(4096) + if not data: + break + hash.update(data) + return hash.hexdigest() + +def exec_cmd(args, env={}, supress_output=False): + """ main exec_cmd function """ + + # forward SSH_AUTH_SOCK, so that ssh-agent works + if os.name != "nt" and "SSH_AUTH_SOCK" in os.environ: + env = os.environ + extra_env={"SSH_AUTH_SOCK":os.environ["SSH_AUTH_SOCK"]} + env.update(extra_env) + else: + env = os.environ + + cmd = subprocess.Popen(args, stdout = subprocess.PIPE, stderr = subprocess.STDOUT, env = env) + output = '' + while True: + out = cmd.stdout.read(1) + if out == '' and cmd.poll() != None: + break + if out != '': + if not supress_output: + sys.stdout.write(out) + output += out + if cmd.wait() != 0: + raise Exception("Command failed: \"%s\"" % " ".join(args), output) + return output + + +platform_map={"linux-synology-i386":"synology-i686", + "linux-readynas-arm":"ubuntu-arm", + "linux-debian-4-i386":"debian-i686", + "linux-control4-arm":"control4-arm", + "linux-apm-ppc":"apm-ppc", + "linux-armada-arm7":"armada-arm7", + "linux-synology-arm":"synology-arm"} + +def platform_str(): + if "BUILD_TAG" in os.environ: + for (k, v) in platform_map.iteritems(): + if k in os.environ["BUILD_TAG"]: + return "linux-"+v + + return "linux-%s-%s"%(platform.linux_distribution()[0].strip().lower(), platform.machine()) + +def merge_directories(src, dest, move = False): + for src_dir, dirs, files in os.walk(src): + dst_dir = src_dir.replace(src, dest) + if not os.path.exists(dst_dir): + os.mkdir(dst_dir) + for file_ in files: + src_file = os.path.join(src_dir, file_) + dst_file = os.path.join(dst_dir, file_) + if os.path.exists(dst_file): + os.remove(dst_file) + if move: + shutil.move(src_file, dst_dir) + else: + shutil.copy(src_file, dst_dir) + if move and os.path.exists(src): + shutil.rmtree(src) + +def unpack_and_install(download, inputfile, installed_filepath): + # Make paths absolute before changing directories + inputfile = os.path.abspath(inputfile) + if not os.path.exists(inputfile): + print "Input file %s does not exist" % inputfile + sys.exit(1) + shafile = "%s.sha.txt" % inputfile + + installed_filepath = os.path.abspath(installed_filepath) + + # Go to directory. + old_cwd = os.getcwd() + os.chdir(options.output) + + # Check the SHA + if options.nochecksha: + print "-- Skipping SHA verification" + elif os.path.exists(shafile): + f = open(shafile, "r") + sha = f.readline().strip() + computed = sha1_for_file(inputfile) + if not computed == sha: + print "-- SHA didn't match: %s != %s" % (sha, computed) + sys.exit(1) + else: + print "-- SHA %s matches" % sha + + f.close() + else: + print "-- ERROR - No SHA file is available to verify the file's integrity" + sys.exit(1) + + # Untar the package file + inputfile_tarfriendly = inputfile + if os.name == "nt": + pattern = re.compile(r'([a-z]):\\', re.IGNORECASE) + inputfile_tarfriendly = pattern.sub('/\\1/', inputfile).replace('\\','/') + + # The final destination directory is the filename without a version number + # The version number is the last element in the filename (by convention) + packagename = os.path.splitext(os.path.basename(inputfile))[0] + packagename_elements = packagename.split("-") + del packagename_elements[-1] + packagename_noversion = "-".join(packagename_elements) + + if os.path.exists(packagename_noversion): + shutil.rmtree(packagename_noversion, ignore_errors=True) + + os.makedirs(packagename_noversion) + + print "-- Unpacking %s... to %s" % (os.path.basename(inputfile), packagename_noversion) + if os.name == "nt": + # Touch files from the package, which often arrive from the future when freshly built on our Windows build machine + exec_cmd(["tar", "xjf", inputfile_tarfriendly, "-C", packagename_noversion, "--touch", "--strip-components", "1", "--no-same-owner"]) + else: + exec_cmd(["tar", "xjf", inputfile_tarfriendly, "-C", packagename_noversion, "--strip-components", "1", "--no-same-owner"]) + + if download and installed_filepath: + # Create the installed stamp file to note our success + open(installed_filepath, "wb") + + # Restore directory. + os.chdir(old_cwd) + + return packagename + +if __name__=='__main__': + parser=optparse.OptionParser() + + parser.add_option("-p", "--platform", action="store", type="string", + dest="platform", help="Platform identifier (e.g. windows-i386)", default=None) + + parser.add_option("-b", "--buildconfig", action="store", type="string", + dest="buildconfig", help="Build configuration (release or debug). Default is release", default="release") + + parser.add_option("-t", "--tag", action="store", type="string", + dest="tag", help="Build tag. Default is %s" % default_tag, default=default_tag) + + parser.add_option("-n", "--buildnumber", action="store", type="string", + dest="buildnumber", help="Build number. Default is %s for release builds" % (default_release_build_number), default=None) + + parser.add_option("-d", "--dir", action="store", type="string", + dest="dir", help="CI build dir. Default is %s for release builds" % (default_release_dir), default=None) + + parser.add_option("-i", "--inputfile", action="store", type="string", + dest="inputfile", help="Dependencies package filename, can be supplied instead of platform, buildconfig, tag and buildnumber", default=None) + + parser.add_option("-o", "--output", action="store", type="string", + dest="output", help="Output directory. Default is Dependencies", + default="Dependencies") + + parser.add_option("-x", "--nochecksha", action="store_true", + dest="nochecksha", help="Don't check the SHA. Default is false", + default=False) + + parser.add_option("-r", "--branch", action="store", type="string", + dest="branch", help="Git branch", default=None) + + (options, args)=parser.parse_args(sys.argv) + + if not os.path.exists(options.output): + os.makedirs(options.output) + + # Fail early if platform is not known + download = not options.inputfile + if download and not options.platform: + print "ERROR - A platform must be specified" + sys.exit(1) + + installed_filepath = None + if download: + + if not options.buildnumber: + if options.buildconfig == "release": + options.buildnumber = default_release_build_number + else: + options.buildnumber = default_debug_build_number + + if not options.dir: + if options.buildconfig == "release": + options.dir = default_release_dir + else: + options.dir = default_debug_dir + + installed_filepath = os.path.join(options.output, "konvergo-depends-%s-%s-%s.installed" % (options.platform, options.buildconfig, options.buildnumber)) + if os.path.exists(installed_filepath) and options.buildnumber != "latest": + print "The required deps bundle was already downloaded and installed." + print "You can delete %s to force a reinstall." % installed_filepath + sys.exit(0) + + # Delete previous installed stamps + cont = ["-c"] + for path in glob.iglob(os.path.join(options.output, "konvergo-depends-%s-%s-*.installed") % (options.platform, options.buildconfig)): + match = re.search("-(\d+)\.installed", path, re.DOTALL) + if match: + if not match.group(1) == options.buildnumber: + cont = [] + os.remove(path) + + if options.tag == "auto": + req = urllib2.Request("https://nightlies.plex.tv/directdl/plex-dependencies/%s/%s/hash.txt" % (options.dir, options.buildnumber)) + try: + match = urllib2.urlopen(req).read().rstrip() + except urllib2.URLError, err: + print err + print "ERROR - Download failed" + sys.exit(1) + + options.tag = match + + base_filename = "konvergo-depends-%s-%s-%s" % (options.platform, options.buildconfig, options.tag) + filename = "%s.tbz2" % base_filename + + installed_filepath = os.path.join(options.output, "konvergo-depends-%s-%s-%s.installed" % (options.platform, options.buildconfig, options.buildnumber)) + if os.path.exists(installed_filepath): + print "%s was already downloaded and installed." % filename + print "You can delete %s to force a reinstall." % installed_filepath + sys.exit(0) + + url = "https://nightlies.plex.tv/directdl/plex-dependencies/%s/%s/%s" % (options.dir, options.buildnumber, filename) + inputfile = os.path.join(options.output, filename) + + print "-- Downloading %s ..." % url + exec_cmd(["wget", "--no-check-certificate"] + cont + ["-O", inputfile, url]) + + shaurl = "%s.sha.txt" % url + shafile = "%s.sha.txt" % inputfile + + print "-- Downloading %s ..." % shaurl + exec_cmd(["wget", "--no-check-certificate"] + ["-O", shafile, shaurl]) + + else: + inputfile = options.inputfile + + # Unpack and install + packagename = unpack_and_install(download, inputfile, installed_filepath) + + # On OS X, we need to postprocess the dependencies. + if platform.system() == 'Darwin': + root = os.path.realpath(os.path.join(os.getcwd())) + script = os.path.join(root, "scripts", "fix-install-names.py") + for p in ("lib", "bin", "update_installer"): + path = os.path.join(root, "Dependencies", "konvergo-depends-" + options.platform + "-" + options.buildconfig, p) + exec_cmd([script, path]) + + # Done! + print "-- Done with %s" % packagename diff --git a/scripts/fix-install-names.py b/scripts/fix-install-names.py new file mode 100755 index 0000000..c63665f --- /dev/null +++ b/scripts/fix-install-names.py @@ -0,0 +1,74 @@ +#!/usr/bin/python + +import hashlib +import optparse +import ConfigParser +import os +import platform +import subprocess +import sys +import shutil + +def exec_cmd(args, env={}, supress_output=False): + cmd = subprocess.Popen(args, stdout = subprocess.PIPE, stderr = subprocess.STDOUT, env = env) + output = '' + while True: + out = cmd.stdout.read(1) + if out == '' and cmd.poll() != None: + break + if out != '': + if not supress_output: + sys.stdout.write(out) + output += out + if cmd.wait() != 0: + raise Exception("Command failed: \"%s\"" % " ".join(args), output) + return output + +def fix_install_name(path): + for root, dirs, files in os.walk(path): + for f in files: + fpath = os.path.join(root, f) + if (f.endswith(".dylib") or f in ['fc-cache'] or f.endswith(".so")) and not os.path.islink(fpath) and os.path.exists(fpath): + + # Fix permissions + if not os.access(fpath, os.W_OK) or not os.access(fpath, os.R_OK) or not os.access(fpath, os.X_OK): + os.chmod(fpath, 0o644) + + try: + basename = os.path.basename(fpath) + otoolout = exec_cmd(["otool", "-L", fpath], supress_output=True) + + for l in otoolout.split("\n")[1:]: + l = l.rstrip().strip() + + # See if we need to fix it up. + if len(l) > 0 and (l.startswith("/Users/admin/") or l[0] != '/'): + current_lib = l.split(" (compat")[0] + current_basename = os.path.basename(current_lib) + correct_lib = os.path.join(root, current_basename) + + if not os.path.exists(correct_lib): + # look for it further up, like in the root path: + if os.path.exists(os.path.join(path, "lib", current_basename)): + correct_lib = os.path.join(path, "lib", current_basename) + else: + print "Can't link %s" % current_lib + continue + + # print current_lib, correct_lib + if current_lib != correct_lib: + if current_basename.split('.')[0] == basename.split('.')[0]: + print "-- Fixing ID for", basename + exec_cmd(["install_name_tool", "-id", correct_lib, fpath], supress_output=True) + else: + print "-- Fixing library link for %s (%s)" % (basename, current_basename) + exec_cmd(["install_name_tool", "-change", current_lib, correct_lib, fpath], supress_output=True) + + except: + print "** Fail when running installname on %s" % f + raise + continue + +if __name__=='__main__': + if os.path.isdir(sys.argv[1]): + fix_install_name(sys.argv[1]) diff --git a/scripts/install-dylibs.py b/scripts/install-dylibs.py new file mode 100755 index 0000000..577f62e --- /dev/null +++ b/scripts/install-dylibs.py @@ -0,0 +1,61 @@ +#!/usr/bin/python +import subprocess, os, sys, shutil, Queue, fnmatch + +def exec_cmd(args, env={}, supress_output=False): + cmd = subprocess.Popen(args, stdout = subprocess.PIPE, stderr = subprocess.STDOUT, env = env) + output = '' + while True: + out = cmd.stdout.read(1) + if out == '' and cmd.poll() != None: + break + if out != '': + if not supress_output: + sys.stdout.write(out) + output += out + if cmd.wait() != 0: + raise Exception("Command failed: \"%s\"" % " ".join(args), output) + return output + +binaries = {} + +def install_libraries(binary): + if binaries.has_key(binary): return [] + binaries[binary] = True + other_binaries = [] + + out = exec_cmd(["otool", "-L", binary], supress_output=True) + for line in out.split('\n'): + line = line.strip() + if line.find("dylib") != -1 and (line.startswith('@executable_path') or line.find("libQt5") != -1): + file = line.split('/')[-1].split(' ')[0] + if line.startswith("@executable_path"): + src = os.path.join(sys.argv[1], "dependencies/darwin-x86_64/lib", file) + else: + src = line.split(" (")[0] + + dst = os.path.join(sys.argv[2], "Konvergo.app", "Contents", "Frameworks", file) + if os.path.exists(src): + if (not os.path.exists(dst) or os.path.getsize(dst) != os.path.getsize(src)): + print "-- Installing %s" % file + shutil.copyfile(src, dst) + + other_binaries.append(src) + + return other_binaries + +queue = Queue.Queue() + +# Add the top-level binaries. +for b in ['Contents/MacOS/Konvergo']: + queue.put(os.path.join(sys.argv[2], "Konvergo.app", b)) + +for root, dirs, files in os.walk(os.path.join(sys.argv[2], "Konvergo.app", "Contents", "Frameworks", "vlc")): + for f in files: + path = os.path.join(root, f) + queue.put(path) + +while queue.empty() == False: + binary = queue.get() + other_binaries = install_libraries(binary) + for b in other_binaries: + queue.put(b) \ No newline at end of file diff --git a/src/.clang-format b/src/.clang-format new file mode 100644 index 0000000..236ef64 --- /dev/null +++ b/src/.clang-format @@ -0,0 +1,47 @@ +--- +# BasedOnStyle: LLVM +AccessModifierOffset: -2 +ConstructorInitializerIndentWidth: 2 +AlignEscapedNewlinesLeft: false +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakTemplateDeclarations: false +AlwaysBreakBeforeMultilineStrings: false +BreakBeforeBinaryOperators: false +BreakBeforeTernaryOperators: false +BreakConstructorInitializersBeforeComma: false +BinPackParameters: true +ColumnLimit: 100 +ConstructorInitializerAllOnOneLineOrOnePerLine: true +DerivePointerBinding: false +ExperimentalAutoDetectBinPacking: false +IndentCaseLabels: true +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: All +ObjCSpaceBeforeProtocolList: true +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 60 +PenaltyBreakString: 1000 +PenaltyBreakFirstLessLess: 120 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 1000 +PointerBindsToType: true +SpacesBeforeTrailingComments: 1 +Cpp11BracedListStyle: false +Standard: Cpp03 +IndentWidth: 2 +TabWidth: 2 +UseTab: Never +BreakBeforeBraces: Allman +IndentFunctionDeclarationAfterType: false +SpacesInParentheses: false +SpacesInAngles: false +SpaceInEmptyParentheses: false +SpacesInCStyleCastParentheses: false +SpaceAfterControlStatementKeyword: true +SpaceBeforeAssignmentOperators: true +ContinuationIndentWidth: 0 +... + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..01fd841 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,168 @@ +include_directories( + ${CMAKE_SOURCE_DIR}/external/qhttpserver/src + ${CMAKE_SOURCE_DIR}/external/qslog + ${CMAKE_SOURCE_DIR}/external/SPMediaKeyTap + ${CMAKE_SOURCE_DIR}/external/plistparser + ${CMAKE_SOURCE_DIR}/src/player + shared +) + +set(CMAKE_AUTOMOC ON) + +add_definitions(-DPREFIX="${CMAKE_INSTALL_PREFIX}") + +find_package(Breakpad) + +if(BREAKPAD_FOUND) + include_directories(${BREAKPAD_INCLUDE_DIR}) + set(BREAKPAD_LIBRARIES ${BREAKPAD_LIBRARY}) + if(UNIX) + # cmake issue: breakpad-client.pc adds this, but cmake discards it + set(BREAKPAD_LIBRARIES ${BREAKPAD_LIBRARIES} -lpthread) + endif() +endif() + +add_subdirectory(shared) +add_subdirectory(display) +add_subdirectory(player) +add_subdirectory(utils) +add_subdirectory(server) +add_subdirectory(ui) +add_subdirectory(input) +add_subdirectory(system) +add_subdirectory(breakpad) +add_subdirectory(settings) +add_subdirectory(power) +add_subdirectory(remote) +add_subdirectory(tools) + +get_property(ALL_SRCS GLOBAL PROPERTY SRCS_LIST) +set(MAIN_SRCS + Version.h + ComponentManager.cpp + ComponentManager.h + main.cpp +) + +# add unix signal management classes +if (UNIX) + set(MAIN_SRCS ${MAIN_SRCS} + SignalManager.h + SignalManager.cpp + ) +endif() + +source_group("Source Files" FILES ${MAIN_SRCS}) +set(SOURCES ${MAIN_SRCS} ${ALL_SRCS}) + +# Set some Objective-C flags. +# We need to force the Language to C instead of C++ +# and also make sure that we use ARC +# +foreach(S ${SOURCES}) + string(REGEX MATCH ".*\\.m$" MATCH_OBJC ${S}) + if(MATCH_OBJC) + set_property(SOURCE ${S} PROPERTY COMPILE_FLAGS "-fobjc-arc") + set_property(SOURCE ${S} PROPERTY LANGUAGE C) + endif() +endforeach(S SOURCE) + +foreach(sfile in ${ALL_SRCS}) + get_filename_component(REALNAME ${sfile} REALPATH) + get_filename_component(DIRNAME ${REALNAME} DIRECTORY) + string(REPLACE "${CMAKE_SOURCE_DIR}/src/" "" SUBDIR ${DIRNAME}) + string(TOLOWER ${SUBDIR} SUBDIR) + string(REPLACE "/" "\\\\" GNAME ${SUBDIR}) + source_group("Source Files\\\\${GNAME}" FILES ${sfile}) +endforeach(sfile in ${ALL_SRCS}) + +find_package(PythonInterp REQUIRED) +file(GLOB_RECURSE RESOURCE_FILES ${CMAKE_SOURCE_DIR}/resources/*) + +# generate resources.qrc from directory tree listings +add_custom_command(OUTPUT resources.qrc + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_SOURCE_DIR}/scripts/build-qt-resources.py + ${CMAKE_CURRENT_BINARY_DIR}/resources.qrc + /=${CMAKE_SOURCE_DIR}/resources/ + ui/webview.qml=${CMAKE_SOURCE_DIR}/src/ui/webview.qml + ui/errormessage.qml=${CMAKE_SOURCE_DIR}/src/ui/errormessage.qml + COMMENT "Creating resources.qrc" + DEPENDS ${CMAKE_SOURCE_DIR}/scripts/build-qt-resources.py + ${RESOURCE_FILES} + ${CMAKE_SOURCE_DIR}/src/ui/webview.qml + ${CMAKE_SOURCE_DIR}/src/ui/errormessage.qml +) +set_source_files_properties(resources.qrc PROPERTIES GENERATED TRUE) +add_custom_command(OUTPUT qrc_resources.cpp + COMMAND ${Qt5Core_RCC_EXECUTABLE} -name resources -o qrc_resources.cpp resources.qrc + DEPENDS resources.qrc ${CMAKE_SOURCE_DIR}/src/ui/webview.qml ${RESOURCE_FILES} +) +set_source_files_properties(qrc_resources.cpp PROPERTIES GENERATED TRUE) + +# handle Web-Client resource +include(WebClientResources) + +set_source_files_properties(${PROJECT_SOURCE_DIR}/bundle/osx/Plex.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources) +list(APPEND RESOURCE_FILES qrc_resources.cpp ${WEB_CLIENT_CPP}) + +if(APPLE) + list(APPEND RESOURCE_FILES ${PROJECT_SOURCE_DIR}/bundle/osx/Plex.icns) + if(HAVE_UPDATER) + list(APPEND RESOURCE_FILES ${UPDATER_PATH}) + set_source_files_properties(${UPDATER_PATH} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) + endif(HAVE_UPDATER) + + set(PLISTPARSER plistparser) +endif() + +set(MACOSX_BUNDLE_ICON_FILE Plex.icns) +set(MACOSX_BUNDLE_SHORT_VERSION_STRING ${VERSION_STRING}) +set(MACOSX_BUNDLE_BUNDLE_NAME "Plex Media Player") +set(MACOSX_BUNDLE_BUNDLE_VERSION ${VERSION_STRING}) +set(MACOSX_BUNDLE_GUI_IDENTIFIER tv.plex.player) + +if(WIN32) + list(APPEND RESOURCE_FILES ${CMAKE_SOURCE_DIR}/bundle/win/iconres.rc) + install(PROGRAMS ${CMAKE_BINARY_DIR}/updater.exe DESTINATION ${INSTALL_BIN_DIR}) +endif() + +get_property(BUNDLED_FILES GLOBAL PROPERTY CONFIG_BUNDLED_FILES) +add_executable(${MAIN_TARGET} WIN32 MACOSX_BUNDLE ${SOURCES} ${BUNDLED_FILES} ${RESOURCE_FILES}) + +set_target_properties(${MAIN_TARGET} PROPERTIES + MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/bundle/osx/Info.plist.in + INSTALL_RPATH "${QTROOT}/lib" + OUTPUT_NAME ${MAIN_NAME} + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED TRUE +) + +target_link_libraries(${MAIN_TARGET} + shared + qhttpserver + qslog + ${PLISTPARSER} + ${MPV_LIBRARY} + ${OPENGL_LIBS} + ${QT5_LIBRARIES} + ${OS_LIBS} + ${EXTRA_LIBS} + ${X11_LIBRARIES} + ${X11_Xrandr_LIB} + ${BREAKPAD_LIBRARIES} + ${ICU_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} + ${RPI_LIBS} +) + +install(TARGETS ${MAIN_TARGET} DESTINATION ${INSTALL_BIN_DIR}) + +set(EXE "${MAIN_NAME}.app") +set(LIBPATH ${CMAKE_FIND_ROOT_PATH}) +set(SOURCE_ROOT ${CMAKE_SOURCE_DIR}) + +if(ENABLE_DUMP_SYMBOLS) + dumpsyms(${MAIN_TARGET} ${CMAKE_CURRENT_BINARY_DIR}/${MAIN_TARGET}.symbols) +endif() + +include(CompleteBundle) diff --git a/src/ComponentManager.cpp b/src/ComponentManager.cpp new file mode 100644 index 0000000..ea962e1 --- /dev/null +++ b/src/ComponentManager.cpp @@ -0,0 +1,86 @@ +#include +#include +#include + +#include "ComponentManager.h" + +#include "power/PowerComponent.h" +#include "server/HTTPServer.h" +#include "input/InputComponent.h" +#include "player/PlayerComponent.h" +#include "display/DisplayComponent.h" +#include "system/SystemComponent.h" +#include "system/UpdaterComponent.h" +#include "settings/SettingsComponent.h" +#include "remote/RemoteComponent.h" + +#if KONVERGO_OPENELEC +#include "system/openelec/OESystemComponent.h" +#endif + +#include "QsLog.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +ComponentManager::ComponentManager() : QObject(0) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void ComponentManager::registerComponent(ComponentBase* comp) +{ + if (m_components.contains(comp->componentName())) + { + QLOG_ERROR() << "Component" << comp->componentName() << "already registered!"; + return; + } + + if (comp->componentInitialize()) + { + QLOG_INFO() << "Component:" << comp->componentName() << "inited"; + m_components[comp->componentName()] = comp; + + // define component as property for qml + m_qmlProperyMap.insert(comp->componentName(), QVariant::fromValue(comp)); + } + else + { + QLOG_ERROR() << "Failed to init component:" << comp->componentName(); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void ComponentManager::initialize() +{ + // then settings, since all other components + // might have some settings + // + registerComponent(&SettingsComponent::Get()); + + m_server = new HttpServer(this); + m_server->start(); + + registerComponent(&SystemComponent::Get()); + registerComponent(&InputComponent::Get()); + registerComponent(&DisplayComponent::Get()); + registerComponent(&UpdaterComponent::Get()); + registerComponent(&RemoteComponent::Get()); + registerComponent(&PlayerComponent::Get()); + registerComponent(&PowerComponent::Get()); + +#if KONVERGO_OPENELEC + registerComponent(&OESystemComponent::Get()); +#endif +} + +///////////////////////////////////////////////////////////////////////////////////////// +void ComponentManager::setWebChannel(QWebChannel* webChannel) +{ + foreach(ComponentBase* comp, m_components.values()) + { + if (comp->componentExport()) + { + QLOG_DEBUG() << "Adding component:" << comp->componentName() << "to webchannel"; + webChannel->registerObject(comp->componentName(), comp); + } + } +} diff --git a/src/ComponentManager.h b/src/ComponentManager.h new file mode 100644 index 0000000..91a1a60 --- /dev/null +++ b/src/ComponentManager.h @@ -0,0 +1,42 @@ +#ifndef __COMPONENT_MANAGER_H__ +#define __COMPONENT_MANAGER_H__ + +#include +#include +#include +#include +#include + +#include "utils/Utils.h" +#include "server/HTTPServer.h" + +class ComponentBase : public QObject +{ +public: + ComponentBase(QObject* parent = 0) : QObject(parent) { } + + virtual bool componentInitialize() = 0; + virtual const char* componentName() = 0; + virtual bool componentExport() = 0; +}; + +class ComponentManager : public QObject +{ + Q_OBJECT + DEFINE_SINGLETON(ComponentManager); +public: + void initialize(); + inline QQmlPropertyMap &getQmlPropertyMap() { return m_qmlProperyMap; } + void setWebChannel(QWebChannel* webChannel); + +private: + ComponentManager(); + void registerComponent(ComponentBase* comp); + + HttpServer* m_server; + + QMap m_components; + QQmlPropertyMap m_qmlProperyMap; +}; + +#endif diff --git a/src/KonvergoPCH.h b/src/KonvergoPCH.h new file mode 100644 index 0000000..2f0e319 --- /dev/null +++ b/src/KonvergoPCH.h @@ -0,0 +1,9 @@ + +#if defined(__cplusplus) +#include +#include +#include +#include +#include +#include +#endif diff --git a/src/SignalManager.cpp b/src/SignalManager.cpp new file mode 100644 index 0000000..6c49686 --- /dev/null +++ b/src/SignalManager.cpp @@ -0,0 +1,86 @@ +#include "sys/socket.h" +#include "unistd.h" +#include "signal.h" + +#include "QsLog.h" +#include "SignalManager.h" +#include "settings/SettingsComponent.h" + +int SignalManager::sigtermFd[2]; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +SignalManager::SignalManager(QGuiApplication* app) : QObject(NULL), m_app(app) +{ + if (setupHandlers()) + { + QLOG_ERROR() << "Failed to install SignalDaemon handlers."; + } + + QLOG_DEBUG() << "Signal handlers installed successfully."; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, SignalManager::sigtermFd)) + { + QLOG_ERROR() << "Couldn't create TERM socketpair"; + } + + snTerm = new QSocketNotifier(SignalManager::sigtermFd[1], QSocketNotifier::Read, this); + connect(snTerm, SIGNAL(activated(int)), this, SLOT(handleSignal())); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +int SignalManager::setupHandlers() +{ + struct sigaction term; + + term.sa_handler = SignalManager::signalHandler; + sigemptyset(&term.sa_mask); + term.sa_flags = SA_RESTART | SA_RESETHAND; + + if (sigaction(SIGHUP, &term, 0) < 0) + return -1; + + if (sigaction(SIGTERM, &term, 0) < 0) + return -2; + + term.sa_flags = SA_RESTART; + if (sigaction(SIGUSR1, &term, 0) < 0) + return -3; + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void SignalManager::signalHandler(int signal_num) +{ + unsigned char a = signal_num < 255 ? signal_num : 0; + write(sigtermFd[0], &a, sizeof(a)); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void SignalManager::handleSignal() +{ + snTerm->setEnabled(false); + unsigned char signal_number = 0; + read(sigtermFd[1], &signal_number, sizeof(signal_number)); + + // do Qt stuff + if (signal_number == SIGUSR1) + { + QLOG_DEBUG() << "Received SIGUSR1, reloading config file"; + SettingsComponent::Get().load(); + } + else + { + QLOG_DEBUG() << "Received signal, closing application"; + closeApplication(); + } + + snTerm->setEnabled(true); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void SignalManager::closeApplication() +{ + if (m_app) + m_app->quit(); +} diff --git a/src/SignalManager.h b/src/SignalManager.h new file mode 100644 index 0000000..da53530 --- /dev/null +++ b/src/SignalManager.h @@ -0,0 +1,33 @@ +#ifndef SIGNALMANAGER_H +#define SIGNALMANAGER_H + +#include +#include +#include + +class SignalManager : public QObject +{ + Q_OBJECT + +public: + SignalManager(QGuiApplication* app); + ~SignalManager() {} + + // Unix signal handlers. + static void signalHandler(int signal_num); + + int setupHandlers(); + void closeApplication(); + +public slots: + // Qt signal handlers. + void handleSignal(); + +private: + static int sigtermFd[2]; + + QSocketNotifier* snTerm; + QGuiApplication* m_app; +}; + +#endif // SIGNALMANAGER_H diff --git a/src/Version.cpp.in b/src/Version.cpp.in new file mode 100644 index 0000000..fd089b6 --- /dev/null +++ b/src/Version.cpp.in @@ -0,0 +1,24 @@ +// Autogenerated CPP file + +#include +#include "Version.h" + +QString Version::GetVersionString() +{ + return QString("@VERSION_STRING@"); +} + +QString Version::GetCanonicalVersionString() +{ + return QString("@CANONICAL_VERSION_STRING@"); +} + +QString Version::GetBuildDate() +{ + return QString("@CURRENT_DATE@"); +} + +QString Version::GetWebVersion() +{ + return QString("@WEB_CLIENT_VERSION@"); +} \ No newline at end of file diff --git a/src/Version.h b/src/Version.h new file mode 100644 index 0000000..9da17ff --- /dev/null +++ b/src/Version.h @@ -0,0 +1,13 @@ +#ifndef VERSION_H +#define VERSION_H + +namespace Version +{ + QString GetVersionString(); + QString GetCanonicalVersionString(); + QString GetBuildDate(); + QString GetWebVersion(); +}; + +#endif // VERSION_H + diff --git a/src/breakpad/BreakPad.h b/src/breakpad/BreakPad.h new file mode 100644 index 0000000..ba73964 --- /dev/null +++ b/src/breakpad/BreakPad.h @@ -0,0 +1,8 @@ +#ifndef BREAKPAD_H +#define BREAKPAD_H + +#include + +void installBreakPadHandler(const QString& name, const QString& destPath); + +#endif diff --git a/src/breakpad/BreakPadDummy.cpp b/src/breakpad/BreakPadDummy.cpp new file mode 100644 index 0000000..efab66f --- /dev/null +++ b/src/breakpad/BreakPadDummy.cpp @@ -0,0 +1,7 @@ +#include "QsLog.h" +#include "BreakPad.h" + +void installBreakPadHandler(const QString& name, const QString& destPath) +{ + QLOG_WARN() << "No crash reporting compiled."; +} diff --git a/src/breakpad/BreakPadLinux.cpp b/src/breakpad/BreakPadLinux.cpp new file mode 100644 index 0000000..6822337 --- /dev/null +++ b/src/breakpad/BreakPadLinux.cpp @@ -0,0 +1,9 @@ +#include + +#include "BreakPad.h" + +void installBreakPadHandler(const QString& name, const QString& destPath) +{ + google_breakpad::MinidumpDescriptor desc(destPath.toStdString()); + new google_breakpad::ExceptionHandler(desc, 0, 0, 0, true, -1); +} diff --git a/src/breakpad/BreakPadOSX.cpp b/src/breakpad/BreakPadOSX.cpp new file mode 100644 index 0000000..5f04ba0 --- /dev/null +++ b/src/breakpad/BreakPadOSX.cpp @@ -0,0 +1,17 @@ +#include + +#include "BreakPad.h" + +///////////////////////////////////////////////////////////////////////////////////////// +static inline bool BreakPad_MinidumpCallback(const char *dump_dir, const char *minidump_id, void *context, bool succeeded) +{ + fprintf(stderr, "****** Plex Media Player CRASHED, CRASH REPORT WRITTEN: %s\n", dump_dir); + return succeeded; +} + +///////////////////////////////////////////////////////////////////////////////////////// +void installBreakPadHandler(const QString& name, const QString& destPath) +{ + Q_UNUSED(name); + new google_breakpad::ExceptionHandler(destPath.toStdString(), NULL, BreakPad_MinidumpCallback, NULL, true, NULL); +} diff --git a/src/breakpad/BreakPadWin32.cpp b/src/breakpad/BreakPadWin32.cpp new file mode 100644 index 0000000..5671506 --- /dev/null +++ b/src/breakpad/BreakPadWin32.cpp @@ -0,0 +1,22 @@ +#include + +#include "BreakPad.h" + +///////////////////////////////////////////////////////////////////////////////////////// +bool BreakPad_MinidumpCallback(const wchar_t* dump_path, + const wchar_t* minidump_id, + void* context, + EXCEPTION_POINTERS* exinfo, + MDRawAssertionInfo* assertion, + bool succeeded) +{ + return succeeded; +} + +///////////////////////////////////////////////////////////////////////////////////////// +void installBreakPadHandler(const QString& name, const QString& destPath) +{ + new google_breakpad::ExceptionHandler(destPath.toStdWString().c_str(), NULL, BreakPad_MinidumpCallback, NULL, + google_breakpad::ExceptionHandler::HANDLER_EXCEPTION | + google_breakpad::ExceptionHandler::HANDLER_PURECALL); +} diff --git a/src/breakpad/CMakeLists.txt b/src/breakpad/CMakeLists.txt new file mode 100644 index 0000000..03deec0 --- /dev/null +++ b/src/breakpad/CMakeLists.txt @@ -0,0 +1,16 @@ +add_sources(BreakPad.h) + +if(BREAKPAD_FOUND) + if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") + # requires Linux kernel and glibc + add_sources(BreakPadLinux.cpp) + elseif(APPLE) + add_sources(BreakPadOSX.cpp) + elseif(WIN32) + add_sources(BreakPadWin32.cpp) + else() + add_sources(BreakPadDummy.cpp) + endif() +else() + add_sources(BreakPadDummy.cpp) +endif() diff --git a/src/breakpad/CrashDumps.h b/src/breakpad/CrashDumps.h new file mode 100644 index 0000000..5966454 --- /dev/null +++ b/src/breakpad/CrashDumps.h @@ -0,0 +1,24 @@ +// +// Created by Tobias Hieta on 26/08/15. +// + +#ifndef KONVERGO_CRASHDUMPS_H +#define KONVERGO_CRASHDUMPS_H + +#include "BreakPad.h" +#include "Paths.h" +#include "Version.h" + +static void setupCrashDumper() +{ + QDir dir(Paths::cacheDir("crashdumps/incoming/" + Version::GetCanonicalVersionString())); + dir.mkpath(dir.absolutePath()); + +#ifdef NDEBUG + installBreakPadHandler("Plex Media Player", dir.path()); +#else + QLOG_INFO() << "This is a debug build. Not installing crash handler."; +#endif +} + +#endif //KONVERGO_CRASHDUMPS_H diff --git a/src/display/CMakeLists.txt b/src/display/CMakeLists.txt new file mode 100644 index 0000000..fb284f4 --- /dev/null +++ b/src/display/CMakeLists.txt @@ -0,0 +1,14 @@ +if(APPLE) + add_subdirectory(osx) +elseif(BUILD_TARGET STREQUAL "RPI") + add_subdirectory(rpi) +elseif(X11XRANDR_FOUND) + add_subdirectory(x11) +elseif(WIN32) + add_subdirectory(win) +endif() + +add_subdirectory(dummy) + +find_all_sources(. SRC) +add_sources(${SRC}) \ No newline at end of file diff --git a/src/display/DisplayComponent.cpp b/src/display/DisplayComponent.cpp new file mode 100644 index 0000000..bff8f78 --- /dev/null +++ b/src/display/DisplayComponent.cpp @@ -0,0 +1,225 @@ + +#include "QsLog.h" +#include "DisplayComponent.h" +#include "DisplayManager.h" +#include +#include + +#ifdef Q_OS_MAC +#include "osx/DisplayManagerOSX.h" +#elif defined(TARGET_RPI) +#include "rpi/DisplayManagerRPI.h" +#elif defined(USE_X11XRANDR) +#include "x11/DisplayManagerX11.h" +#elif defined(Q_OS_WIN) +#include "win/DisplayManagerWin.h" +#endif + +#include "dummy/DisplayManagerDummy.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +DisplayComponent::DisplayComponent(QObject* parent) : ComponentBase(parent), m_initTimer(this) +{ + m_displayManager = NULL; + m_lastVideoMode = -1; + m_lastDisplay = -1; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +DisplayComponent::~DisplayComponent() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool DisplayComponent::initializeDisplayManager() +{ + m_initTimer.setSingleShot(false); + + bool res = false; + if (m_displayManager) + res = m_displayManager->initialize(); + + emit refreshRateChanged(); + + return res; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool DisplayComponent::componentInitialize() +{ +#if 0 + m_displayManager = new DisplayManagerDummy(this); +#elif defined(Q_OS_MAC) + m_displayManager = new DisplayManagerOSX(this); +#elif defined(TARGET_RPI) + m_displayManager = new DisplayManagerRPI(this); +#elif defined(USE_X11XRANDR) + m_displayManager = new DisplayManagerX11(this); +#elif defined(Q_OS_WIN) + m_displayManager = new DisplayManagerWin(this); +#endif + + if (initializeDisplayManager()) + { + QGuiApplication* app = (QGuiApplication*)QGuiApplication::instance(); + + connect(app, SIGNAL(screenAdded(QScreen*)), this, SLOT(monitorChange())); + connect(app, SIGNAL(screenRemoved(QScreen*)), this, SLOT(monitorChange())); + + foreach (QScreen *screen, app->screens()) + { + connect(screen, SIGNAL(refreshRateChanged(qreal)), this, SLOT(monitorChange())); + connect(screen, SIGNAL(geometryChanged(QRect)), this, SLOT(monitorChange())); + } + +#ifdef TARGET_RPI + // The firmware doesn't always make the best decision. Hope we do better. + QLOG_INFO() << "Trying to switch to best display mode."; + switchToBestOverallVideoMode(0); +#endif + + return true; + } + + return false; +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +void DisplayComponent::monitorChange() +{ + QLOG_INFO() << "Monitor change detected."; + + if (!m_initTimer.isSingleShot()) + { + m_initTimer.setSingleShot(true); + m_initTimer.singleShot(1000, this, SLOT(initializeDisplayManager())); + } +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +bool DisplayComponent::switchToBestVideoMode(float frameRate) +{ + if (!m_displayManager) + return false; + + int currentDisplay = getApplicationDisplay(); + if (currentDisplay < 0) + return false; + + int currentMode = m_displayManager->getCurrentDisplayMode(currentDisplay); + if (m_lastVideoMode < 0) + { + m_lastVideoMode = currentMode; + m_lastDisplay = currentDisplay; + } + + DMMatchMediaInfo matchInfo(frameRate, false); + int bestmode = m_displayManager->findBestMatch(currentDisplay, matchInfo); + if (bestmode >= 0) + { + if (bestmode != currentMode) + { + QLOG_DEBUG() + << "Best video matching mode is " + << m_displayManager->displays[currentDisplay]->videoModes[bestmode]->getPrettyName() + << "on display" << currentDisplay; + + m_displayManager->setDisplayMode(currentDisplay, bestmode); + return true; + } + QLOG_INFO() << "No better video mode than the currently active one found."; + } + else + { + QLOG_DEBUG() << "No video mode found as better match."; + } + + return false; +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +bool DisplayComponent::switchToBestOverallVideoMode(int display) +{ + if (!m_displayManager || !m_displayManager->isValidDisplay(display)) + return false; + + int bestmode = m_displayManager->findBestMode(display); + if (bestmode < 0) + return false; + + QLOG_INFO() << "We think mode" << bestmode << "is the best mode."; + + if (bestmode == m_displayManager->getCurrentDisplayMode(display)) + { + QLOG_INFO() << "This mode is the currently active one. Not switching."; + return false; + } + + if (!m_displayManager->setDisplayMode(display, bestmode)) + { + QLOG_INFO() << "Switching mode failed."; + return false; + } + + QLOG_INFO() << "Switching mode successful."; + return true; +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +double DisplayComponent::currentRefreshRate() +{ + if (!m_displayManager) + return 0; + + int currentDisplay = getApplicationDisplay(); + if (currentDisplay < 0) + return 0; + int mode = m_displayManager->getCurrentDisplayMode(currentDisplay); + if (mode < 0) + return 0; + return m_displayManager->displays[currentDisplay]->videoModes[mode]->refreshRate; +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +bool DisplayComponent::restorePreviousVideoMode() +{ + if (!m_displayManager) + return false; + + if (m_lastVideoMode < 0 || m_lastDisplay < 0) + return false; + + bool ret = true; + + if (m_displayManager->getCurrentDisplayMode(m_lastDisplay) != m_lastVideoMode) + { + QLOG_DEBUG() + << "Restoring VideoMode to" + << m_displayManager->displays[m_lastDisplay]->videoModes[m_lastVideoMode]->getPrettyName() + << "on display" << m_lastDisplay; + + ret = m_displayManager->setDisplayMode(m_lastDisplay, m_lastVideoMode); + } + + m_lastVideoMode = -1; + m_lastDisplay = -1; + + return ret; +} + + +////////////////////////////////////////////////////////////////////////////////////////////////// +int DisplayComponent::getApplicationDisplay() +{ + QWindow* activeWindow = QGuiApplication::focusWindow(); + + int display = -1; + if (activeWindow && m_displayManager) + display = m_displayManager->getDisplayFromPoint(activeWindow->geometry().center()); + + if (display < 0) + { + QLOG_WARN() << "Unable to locate current display."; + } + return display; +} diff --git a/src/display/DisplayComponent.h b/src/display/DisplayComponent.h new file mode 100644 index 0000000..119be75 --- /dev/null +++ b/src/display/DisplayComponent.h @@ -0,0 +1,52 @@ +#ifndef DISPLAYCOMPONENT_H +#define DISPLAYCOMPONENT_H + +#include "DisplayManager.h" +#include "ComponentManager.h" +#include +#include + +class DisplayComponent : public ComponentBase +{ + Q_OBJECT + DEFINE_SINGLETON(DisplayComponent); + +public: + ~DisplayComponent(); + + virtual const char* componentName() { return "display"; } + virtual bool componentExport() { return true; } + virtual bool componentInitialize(); + + inline DisplayManager* getDisplayManager() { return m_displayManager; } + int getApplicationDisplay(); + + // Switch to the best video mode for the given video framerate. Return true only if the actual + // mode was switched. If a good match was found, but the current video mode didn't have to be + // changed, return false. Return false on failure too. + bool switchToBestVideoMode(float frameRate); + + // Switch to best overall video mode. This will also switch the resolution. + bool switchToBestOverallVideoMode(int display); + + double currentRefreshRate(); + +private: + DisplayComponent(QObject *parent = 0); + + DisplayManager *m_displayManager; + int m_lastVideoMode; + int m_lastDisplay; + QTimer m_initTimer; + +public Q_SLOTS: + void monitorChange(); + bool initializeDisplayManager(); + bool restorePreviousVideoMode(); + +Q_SIGNALS: + void refreshRateChanged(); + +}; + +#endif // DISPLAYCOMPONENT_H diff --git a/src/display/DisplayManager.cpp b/src/display/DisplayManager.cpp new file mode 100755 index 0000000..f2e4c31 --- /dev/null +++ b/src/display/DisplayManager.cpp @@ -0,0 +1,226 @@ +// +// DisplayManager.cpp +// konvergo +// +// Created by Lionel CHAZALLON on 28/09/2014. +// +// + +#include "QsLog.h" +#include "DisplayManager.h" +#include "math.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +DisplayManager::DisplayManager(QObject* parent) : QObject(parent) {} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool DisplayManager::initialize() +{ + QLOG_INFO() << QString("DisplayManager found %1 Display(s).").arg(displays.size()); + + // list video modes + for (int displayid = 0; displayid < displays.size(); displayid++) + { + DMDisplayPtr display = displays[displayid]; + QLOG_INFO() << QString("Available modes for Display #%1 (%2)").arg(displayid).arg(display->name); + for (int modeid = 0; modeid < display->videoModes.size(); modeid++) + { + DMVideoModePtr mode = display->videoModes[modeid]; + QLOG_INFO() << QString("Mode %1: %2").arg(modeid, 2).arg(mode->getPrettyName()); + } + } + + // Log current display mode + int mainDisplay = getMainDisplay(); + if (mainDisplay >= 0) + { + int currentMode = getCurrentDisplayMode(mainDisplay); + if (currentMode >= 0) + QLOG_INFO() << QString("DisplayManager : Current Display Mode on Display #%1 is %2") + .arg(mainDisplay) + .arg(displays[mainDisplay]->videoModes[currentMode]->getPrettyName()); + else + QLOG_ERROR() << "DisplayManager : unable to retrieve current video mode"; + } + else + QLOG_ERROR() << "DisplayManager : unable to retrieve main display"; + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +DMVideoModePtr DisplayManager::getCurrentVideoMode(int display) +{ + int currentMode = getCurrentDisplayMode(display); + DMVideoModePtr currentVideoMode; + + if (currentMode >= 0) + currentVideoMode = displays[display]->videoModes[currentMode]; + + return currentVideoMode; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool DisplayManager::isValidDisplay(int display) { return display < displays.size(); } + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool DisplayManager::isValidDisplayMode(int display, int mode) +{ + if (display < displays.size()) + if (displays[display]->videoModes.size() > mode) + return true; + + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool DisplayManager::isRateMultipleOf(float refresh, float multiple, bool exact) +{ + if (((int)refresh == 0) || ((int)multiple == 0)) + return false; + + if (((int)multiple % (int)refresh) == 0) + return true; + + int roundedRefresh = (int)round(refresh); + int roundedMultiple = (int)round(multiple); + if (((roundedMultiple % roundedRefresh) == 0) && (!exact)) + return true; + + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +int DisplayManager::findBestMatch(int display, DMMatchMediaInfo& matchInfo) +{ + + // Grab current videomode information + DMVideoModePtr currentVideoMode = getCurrentVideoMode(display); + if (!currentVideoMode) + return -1; + + // now we try to find the exact match in current resolution + // then fill a list + DMVideoModeWeightMap weights; + + DMVideoModeMap::const_iterator modeit = displays[display]->videoModes.constBegin(); + + while (modeit != displays[display]->videoModes.constEnd()) + { + DMVideoModePtr candidate = modeit.value(); + + weights[candidate->id] = DMVideoModeWeightPtr(new DMVideoModeWeight); + weights[candidate->id]->mode = candidate; + weights[candidate->id]->weight = 0; + + // Weight Resolution match + if ((candidate->width == currentVideoMode->width) && + (candidate->height == currentVideoMode->height) && + (candidate->bitsPerPixel == currentVideoMode->bitsPerPixel)) + { + weights[candidate->id]->weight += MATCH_WEIGHT_RES; + } + + // weight refresh rate + // exact Match + if (candidate->refreshRate == matchInfo.refreshRate) + weights[candidate->id]->weight += MATCH_WEIGHT_REFRESH_RATE_EXACT; + + // exact multiple refresh rate + if (isRateMultipleOf(matchInfo.refreshRate, candidate->refreshRate, true)) + weights[candidate->id]->weight += MATCH_WEIGHT_REFRESH_RATE_MULTIPLE; + + // close refresh match (less than 1 hz diff to match all 23.xxx modes to 24p) + if (fabs(candidate->refreshRate - matchInfo.refreshRate) <= 1) + { + weights[candidate->id]->weight += MATCH_WEIGHT_REFRESH_RATE_CLOSE; + } + + // approx multiple refresh rate + if (isRateMultipleOf(matchInfo.refreshRate, candidate->refreshRate, false)) + weights[candidate->id]->weight += MATCH_WEIGHT_REFRESH_RATE_MULTIPLE_CLOSE; + + // weight interlacing + if (candidate->interlaced == matchInfo.interlaced) + weights[candidate->id]->weight += MATCH_WEIGHT_INTERLACE; + + if (candidate->id == currentVideoMode->id) + weights[candidate->id]->weight += MATCH_WEIGHT_CURRENT; + + modeit++; + } + + // now grab the mode with the highest weight + DMVideoModeWeightPtr chosen; + float maxWeight = 0; + + DMVideoModeWeightMap::const_iterator weightit = weights.constBegin(); + while (weightit != weights.constEnd()) + { + QLOG_DEBUG() << "Mode " << weightit.value()->mode->id << "(" + << weightit.value()->mode->getPrettyName() << ") has weight " + << weightit.value()->weight; + if (weightit.value()->weight > maxWeight) + { + chosen = weightit.value(); + maxWeight = chosen->weight; + } + + weightit++; + } + + if ((chosen) && (chosen->weight > MATCH_WEIGHT_RES)) + { + QLOG_INFO() << "DisplayManager RefreshMatch : found a suitable mode : " + << chosen->mode->getPrettyName(); + return chosen->mode->id; + } + + QLOG_INFO() << "DisplayManager RefreshMatch : found no suitable videomode"; + return -1; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +int DisplayManager::findBestMode(int display) +{ + int best_mode = -1; + + foreach (auto mode, displays[display]->videoModes) + { + if (best_mode < 0) + { + best_mode = mode->id; + } + else + { + DMVideoModePtr best = displays[display]->videoModes[best_mode]; + DMVideoModePtr candidate = mode; + + // Highest priority: prefer non-interlaced modes. + if (!best->interlaced && candidate->interlaced) + continue; + + if (best->bitsPerPixel > candidate->bitsPerPixel) + continue; + + if (best->width > candidate->width) + continue; + + if (best->height > candidate->height) + continue; + + if (best->refreshRate > candidate->refreshRate) + continue; + + best_mode = candidate->id; + } + } + + return best_mode; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +int DisplayManager::getDisplayFromPoint(const QPoint& pt) +{ + return getDisplayFromPoint(pt.x(), pt.y()); +} diff --git a/src/display/DisplayManager.h b/src/display/DisplayManager.h new file mode 100644 index 0000000..2691868 --- /dev/null +++ b/src/display/DisplayManager.h @@ -0,0 +1,134 @@ +// +// DisplayManager.h +// konvergo +// +// Created by Lionel CHAZALLON on 28/09/2014. +// +// + +#ifndef _DISPLAYMANAGER_H_ +#define _DISPLAYMANAGER_H_ + +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Video Modes +class DMVideoMode +{ +public: + int id; + int width; + int height; + int bitsPerPixel; + float refreshRate; + bool interlaced; + + int priv_id; + + inline QString getPrettyName() + { + QString name; + + name = QString("%1 x%2%3").arg(width, 5).arg(height, 5).arg((interlaced ? "i" : " ")); + name += QString("x %1bpp @%2Hz").arg(bitsPerPixel, 2).arg(refreshRate); + return name; + } +}; + +typedef QSharedPointer DMVideoModePtr; +typedef QMap DMVideoModeMap; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Displays +class DMDisplay +{ +public: + int id; + QString name; + + int priv_id; + + DMVideoModeMap videoModes; +}; + +typedef QSharedPointer DMDisplayPtr; +typedef QMap DMDisplayMap; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// MatchMediaInfo +class DMMatchMediaInfo +{ +public: + DMMatchMediaInfo() : refreshRate(0), interlaced(false) {}; + DMMatchMediaInfo(float refreshRate, bool interlaced) + : refreshRate(refreshRate), interlaced(interlaced) {}; + + float refreshRate; + bool interlaced; +}; + +// Matching weights +#define MATCH_WEIGHT_RES 1000 + +#define MATCH_WEIGHT_REFRESH_RATE_EXACT 100 +#define MATCH_WEIGHT_REFRESH_RATE_MULTIPLE 75 +#define MATCH_WEIGHT_REFRESH_RATE_CLOSE 50 +#define MATCH_WEIGHT_REFRESH_RATE_MULTIPLE_CLOSE 25 + +#define MATCH_WEIGHT_INTERLACE 10 + +#define MATCH_WEIGHT_CURRENT 5 + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// VideoMode Weight +class DMVideoModeWeight +{ +public: + float weight; + DMVideoModePtr mode; +}; + +typedef QSharedPointer DMVideoModeWeightPtr; +typedef QMap DMVideoModeWeightMap; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// DisplayManager +class DisplayManager : public QObject +{ + Q_OBJECT +public: + DisplayManager(QObject* parent); + virtual ~DisplayManager() {} + + DMDisplayMap displays; + + // functions that should be implemented on each platform + virtual bool initialize(); + virtual bool setDisplayMode(int display, int mode) = 0; + virtual int getCurrentDisplayMode(int display) = 0; + virtual int getMainDisplay() = 0; + virtual int getDisplayFromPoint(int x, int y) = 0; + + // extra functions that can be implemented + virtual void resetRendering() {} + + // other classes functions + int findBestMatch(int display, DMMatchMediaInfo& matchInfo); + DMVideoModePtr getCurrentVideoMode(int display); + + int findBestMode(int display); + + bool isValidDisplay(int display); + bool isValidDisplayMode(int display, int mode); + int getDisplayFromPoint(const QPoint& pt); + +private: + bool isRateMultipleOf(float refresh, float multiple, bool exact = true); +}; + +typedef QSharedPointer DisplayManagerPtr; + +#endif /* _DISPLAYMANAGER_H_ */ diff --git a/src/display/dummy/CMakeLists.txt b/src/display/dummy/CMakeLists.txt new file mode 100644 index 0000000..b0e41b9 --- /dev/null +++ b/src/display/dummy/CMakeLists.txt @@ -0,0 +1 @@ +add_sources(DisplayManagerDummy.cpp DisplayManagerDummy.h) \ No newline at end of file diff --git a/src/display/dummy/DisplayManagerDummy.cpp b/src/display/dummy/DisplayManagerDummy.cpp new file mode 100644 index 0000000..025b1a3 --- /dev/null +++ b/src/display/dummy/DisplayManagerDummy.cpp @@ -0,0 +1,75 @@ +#include "QsLog.h" +#include "DisplayManagerDummy.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void DisplayManagerDummy::addMode(float rate) +{ + if (!displays.size()) + return; + + DMVideoModePtr mode = DMVideoModePtr(new DMVideoMode()); + mode->id = displays[0]->videoModes.size(); + mode->interlaced = false; + mode->refreshRate = rate; + mode->width = 1280; + mode->height = 720; + mode->bitsPerPixel = 0; + displays[0]->videoModes[mode->id] = mode; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool DisplayManagerDummy::initialize() +{ + displays.clear(); + + DMDisplayPtr display = DMDisplayPtr(new DMDisplay()); + display->id = displays.size(); + display->name = "Dummy display"; + displays[display->id] = display; + + addMode(60); + + return DisplayManager::initialize(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool DisplayManagerDummy::setDisplayMode(int display, int mode) +{ + if (!isValidDisplayMode(display, mode)) + return false; + + DMDisplayPtr displayptr = displays[display]; + DMVideoModePtr videomode = displayptr->videoModes[mode]; + + QLOG_INFO() << "Switching to" << videomode->width << "x" << videomode->height << "@" << videomode->refreshRate; + + m_currentMode = videomode->id; + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +int DisplayManagerDummy::getCurrentDisplayMode(int display) +{ + if (!isValidDisplay(display)) + return -1; + + return m_currentMode; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +int DisplayManagerDummy::getMainDisplay() +{ + return 0; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +int DisplayManagerDummy::getDisplayFromPoint(int x, int y) +{ + if (displays.size()) + { + if (x >= 0 && y >= 0 && x < 1280 && y < 720) + return 0; + } + return -1; +} diff --git a/src/display/dummy/DisplayManagerDummy.h b/src/display/dummy/DisplayManagerDummy.h new file mode 100644 index 0000000..cc473a7 --- /dev/null +++ b/src/display/dummy/DisplayManagerDummy.h @@ -0,0 +1,25 @@ +#ifndef DISPLAYMANAGERDUMMY_H_ +#define DISPLAYMANAGERDUMMY_H_ + +#include "display/DisplayManager.h" + +class DisplayManagerDummy : public DisplayManager +{ + Q_OBJECT +private: + + void addMode(float rate); + + int m_currentMode; + +public: + DisplayManagerDummy(QObject* parent) : DisplayManager(parent), m_currentMode(0) {}; + + virtual bool initialize(); + virtual bool setDisplayMode(int display, int mode); + virtual int getCurrentDisplayMode(int display); + virtual int getMainDisplay(); + virtual int getDisplayFromPoint(int x, int y); +}; + +#endif /* DISPLAYMANAGERX11_H_ */ diff --git a/src/display/osx/CMakeLists.txt b/src/display/osx/CMakeLists.txt new file mode 100644 index 0000000..d88261a --- /dev/null +++ b/src/display/osx/CMakeLists.txt @@ -0,0 +1 @@ +add_sources(DisplayManagerOSX.cpp DisplayManagerOSX.h) diff --git a/src/display/osx/DisplayManagerOSX.cpp b/src/display/osx/DisplayManagerOSX.cpp new file mode 100644 index 0000000..47498cb --- /dev/null +++ b/src/display/osx/DisplayManagerOSX.cpp @@ -0,0 +1,155 @@ +// +// DisplayManagerOSX.cpp +// konvergo +// +// Created by Lionel CHAZALLON on 28/09/2014. +// +// + +#include +#include "utils/osx/OSXUtils.h" +#include "DisplayManagerOSX.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool DisplayManagerOSX::initialize() +{ + int totalModes = 0; + + CGGetActiveDisplayList(MAX_DISPLAYS, m_osxDisplays, &m_osxnumDisplays); + displays.clear(); + + for (int displayid = 0; displayid < m_osxnumDisplays; displayid++) + { + // add the display to the list + DMDisplayPtr display = DMDisplayPtr(new DMDisplay); + display->id = displayid; + display->name = QString("Display %1").arg(displayid); + displays[display->id] = display; + + m_osxDisplayModes[displayid] = CGDisplayCopyAllDisplayModes(m_osxDisplays[displayid], NULL); + if (!m_osxDisplayModes[displayid]) + continue; + + int numModes = (int)CFArrayGetCount(m_osxDisplayModes[displayid]); + + for (int modeid = 0; modeid < numModes; modeid++) + { + totalModes++; + + // add the videomode to the display + DMVideoModePtr mode = DMVideoModePtr(new DMVideoMode); + mode->id = modeid; + display->videoModes[modeid] = mode; + + // grab videomode info + CGDisplayModeRef displayMode = + (CGDisplayModeRef)CFArrayGetValueAtIndex(m_osxDisplayModes[displayid], modeid); + + mode->height = CGDisplayModeGetHeight(displayMode); + mode->width = CGDisplayModeGetWidth(displayMode); + mode->refreshRate = CGDisplayModeGetRefreshRate(displayMode); + + CFStringRef pixEnc = CGDisplayModeCopyPixelEncoding(displayMode); + + if (CFStringCompare(pixEnc, CFSTR(IO32BitDirectPixels), kCFCompareCaseInsensitive) == + kCFCompareEqualTo) + mode->bitsPerPixel = 32; + else if (CFStringCompare(pixEnc, CFSTR(IO16BitDirectPixels), kCFCompareCaseInsensitive) == + kCFCompareEqualTo) + mode->bitsPerPixel = 16; + else if (CFStringCompare(pixEnc, CFSTR(IO8BitIndexedPixels), kCFCompareCaseInsensitive) == + kCFCompareEqualTo) + mode->bitsPerPixel = 8; + + mode->interlaced = (CGDisplayModeGetIOFlags(displayMode) & kDisplayModeInterlacedFlag) > 0; + + if (mode->refreshRate == 0) + mode->refreshRate = 60; + } + } + + if (totalModes == 0) + return false; + else + return DisplayManager::initialize(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool DisplayManagerOSX::setDisplayMode(int display, int mode) +{ + if (!isValidDisplayMode(display, mode) || !m_osxDisplayModes[display]) + return false; + + CGDisplayModeRef displayMode = + (CGDisplayModeRef)CFArrayGetValueAtIndex(m_osxDisplayModes[display], mode); + + CGDisplaySetDisplayMode(m_osxDisplays[display], displayMode, NULL); + + // HACK : on OSX, switching display mode can leave dock in a state where mouse cursor + // will not hide on top of hidden dock, so we reset it state to fix this + OSXUtils::SetMenuBarVisible(true); + OSXUtils::SetMenuBarVisible(false); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +int DisplayManagerOSX::getCurrentDisplayMode(int display) +{ + if (!isValidDisplay(display) || !m_osxDisplayModes[display]) + return -1; + + CGDisplayModeRef currentMode = CGDisplayCopyDisplayMode(m_osxDisplays[display]); + + for (int mode = 0; mode < CFArrayGetCount(m_osxDisplayModes[display]); mode++) + { + if (currentMode == (CGDisplayModeRef)CFArrayGetValueAtIndex(m_osxDisplayModes[display], mode)) + { + return mode; + } + } + + return -1; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +int DisplayManagerOSX::getMainDisplay() +{ + CGDirectDisplayID mainID = CGMainDisplayID(); + + for (int i = 0; i < m_osxnumDisplays; i++) + { + if (m_osxDisplays[i] == mainID) + return i; + } + + return -1; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +DisplayManagerOSX::~DisplayManagerOSX() +{ + for (int i = 0; i < m_osxDisplayModes.size(); i++) + { + if (m_osxDisplayModes[i]) + CFRelease(m_osxDisplayModes[i]); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +int DisplayManagerOSX::getDisplayFromPoint(int x, int y) +{ + CGPoint point = { (double)x, (double)y }; + CGDirectDisplayID foundDisplay; + uint32_t numFound; + + CGGetDisplaysWithPoint(point, 1, &foundDisplay, &numFound); + + for (int i=0; i +#include + +#include "display/DisplayManager.h" + +#define MAX_DISPLAYS 32 + +typedef std::map OSXDisplayModeMap; + +class DisplayManagerOSX : public DisplayManager +{ + Q_OBJECT +private: + // OSX Display/ VideoMode handling structs + uint32_t m_osxnumDisplays; + CGDirectDisplayID m_osxDisplays[MAX_DISPLAYS]; + OSXDisplayModeMap m_osxDisplayModes; + +public: + DisplayManagerOSX(QObject* parent) : DisplayManager(parent) {}; + virtual ~DisplayManagerOSX(); + + virtual bool initialize(); + virtual bool setDisplayMode(int display, int mode); + virtual int getCurrentDisplayMode(int display); + virtual int getMainDisplay(); + virtual int getDisplayFromPoint(int x, int y); +}; + +#endif /* _DISPLAYMANAGEROSX_H_ */ diff --git a/src/display/rpi/CMakeLists.txt b/src/display/rpi/CMakeLists.txt new file mode 100644 index 0000000..f46472c --- /dev/null +++ b/src/display/rpi/CMakeLists.txt @@ -0,0 +1 @@ +add_sources(DisplayManagerRPI.cpp DisplayManagerRPI.h) diff --git a/src/display/rpi/DisplayManagerRPI.cpp b/src/display/rpi/DisplayManagerRPI.cpp new file mode 100644 index 0000000..6a5cbd6 --- /dev/null +++ b/src/display/rpi/DisplayManagerRPI.cpp @@ -0,0 +1,182 @@ +#include +#include +#include +#include +#include + +#include "QsLog.h" +#include "DisplayManagerRPI.h" +#include "display/DisplayComponent.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void DisplayManagerRPI::tv_callback(void *callback_data, uint32_t reason, uint32_t param1, uint32_t param2) +{ + DisplayManagerRPI* obj = (DisplayManagerRPI *)callback_data; + + emit obj->onTvChange(reason); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +DisplayManagerRPI::DisplayManagerRPI(QObject* parent) : DisplayManager(parent) +{ + connect(this, &DisplayManagerRPI::onTvChange, + this, &DisplayManagerRPI::handleTvChange, + Qt::QueuedConnection); + + vc_tv_register_callback(&tv_callback, (void *)this); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +DisplayManagerRPI::~DisplayManagerRPI() +{ + vc_tv_unregister_callback_full(&tv_callback, (void *)this); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void DisplayManagerRPI::handleTvChange(uint32_t reason) +{ + QLOG_INFO() << "tv_service notification:" << reason; + + if (reason & (VC_HDMI_DVI | VC_HDMI_HDMI)) + { + // Looks like a mode change - Qt's dispmanx state is probably trashed, recreate it. + resetRendering(); + } + else if (reason & VC_HDMI_ATTACHED) + { + // Plugged in, but is in standby mode. May happen when reconnecting a monitor via HDMI. + QLOG_INFO() << "Powering on screen."; + DisplayComponent::Get().switchToBestOverallVideoMode(0); + } + else if (reason & VC_HDMI_UNPLUGGED) + { + QLOG_INFO() << "Screen was unplugged."; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +static void get_modes(std::vector& modes, HDMI_RES_GROUP_T group) +{ + const int max_modes = 256; + + size_t count = modes.size(); + modes.resize(count + max_modes); + + HDMI_RES_GROUP_T preferred_group; + uint32_t preferred_mode; + int got = vc_tv_hdmi_get_supported_modes_new(group, &modes[count], max_modes, &preferred_group, &preferred_mode); + modes.resize(count + got); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool DisplayManagerRPI::initialize() +{ + bcm_host_init(); + + displays.clear(); + + // create the main display + DMDisplayPtr display = DMDisplayPtr(new DMDisplay); + display->id = 0; + display->name = "Display"; + displays[display->id] = display; + + // fills mode array with both CEA and DMT + m_modes.resize(0); + get_modes(m_modes, HDMI_RES_GROUP_CEA); // TV + get_modes(m_modes, HDMI_RES_GROUP_DMT); // PC + + for (size_t n = 0; n < m_modes.size(); n++) + { + TV_SUPPORTED_MODE_NEW_T* tvmode = &m_modes[n]; + DMVideoModePtr mode = DMVideoModePtr(new DMVideoMode); + mode->id = n; + display->videoModes[mode->id] = mode; + + mode->height = tvmode->height; + mode->width = tvmode->width; + mode->refreshRate = tvmode->frame_rate; + mode->interlaced = (tvmode->scan_mode == 1); + mode->bitsPerPixel = 32; + } + + if (m_modes.size() == 0) + return false; + else + return DisplayManager::initialize(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool DisplayManagerRPI::setDisplayMode(int display, int mode) +{ + if (!isValidDisplayMode(display, mode)) + return false; + + TV_SUPPORTED_MODE_NEW_T* tvmode = &m_modes[mode]; + bool ret = vc_tv_hdmi_power_on_explicit_new(HDMI_MODE_HDMI, (HDMI_RES_GROUP_T)tvmode->group, tvmode->code) == 0; + if (!ret) + { + QLOG_ERROR() << "Failed to switch display mode" << ret; + } + + return ret; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +int DisplayManagerRPI::getCurrentDisplayMode(int display) +{ + if (!isValidDisplay(display)) + return -1; + + TV_GET_STATE_RESP_T tvstate; + if (vc_tv_get_state(&tvstate)) + return -1; + + for (int mode = 0; mode < displays[display]->videoModes.size(); mode++) + { + TV_SUPPORTED_MODE_NEW_T* tvmode = &m_modes[mode]; + if (tvmode->width == tvstate.width && + tvmode->height == tvstate.height && + tvmode->frame_rate == tvstate.frame_rate && + tvmode->scan_mode == tvstate.scan_mode) + return mode; + } + + return -1; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void DisplayManagerRPI::resetRendering() +{ + QGuiApplication *guiApp = (QGuiApplication*)QGuiApplication::instance(); + QQuickWindow *window = (QQuickWindow*)guiApp->focusWindow(); + if (window) + { + QLOG_INFO() << "Recreating Qt UI renderer"; + + // destroy the window to reset OpenGL context + window->setPersistentOpenGLContext(false); + window->setPersistentSceneGraph(false); + window->destroy(); + + // Grab the Platform integration private object and recreate it + // this allows to clean / recreate the dispmanx objects + QGuiApplicationPrivate *privateApp = (QGuiApplicationPrivate *)QGuiApplicationPrivate::get(guiApp); + QPlatformIntegration *integration = privateApp->platformIntegration(); + + if (integration) + { + integration->destroy(); + QThread::msleep(500); + integration->initialize(); + } + else + { + QLOG_ERROR() << "Failed to retrieve platform integration"; + } + + // now recreate the window OpenGL context + window->setScreen(QGuiApplication::primaryScreen()); + window->create(); + } +} diff --git a/src/display/rpi/DisplayManagerRPI.h b/src/display/rpi/DisplayManagerRPI.h new file mode 100644 index 0000000..e259165 --- /dev/null +++ b/src/display/rpi/DisplayManagerRPI.h @@ -0,0 +1,36 @@ +#ifndef DISPLAYMANAGERRPI_H +#define DISPLAYMANAGERRPI_H + +#include +#include +#include + +#include "display/DisplayManager.h" + +class DisplayManagerRPI : public DisplayManager +{ + Q_OBJECT +private: + std::vector m_modes; + +private Q_SLOTS: + void handleTvChange(uint32_t reason); + + static void tv_callback(void *callback_data, uint32_t reason, uint32_t param1, uint32_t param2); + +Q_SIGNALS: + void onTvChange(uint32_t reason); + +public: + DisplayManagerRPI(QObject* parent); + virtual ~DisplayManagerRPI(); + + virtual bool initialize(); + virtual void resetRendering(); + virtual bool setDisplayMode(int display, int mode); + virtual int getCurrentDisplayMode(int display); + virtual int getMainDisplay() { return 0; } + virtual int getDisplayFromPoint(int x, int y) { return 0; } +}; + +#endif diff --git a/src/display/win/CMakeLists.txt b/src/display/win/CMakeLists.txt new file mode 100644 index 0000000..a8b7e3d --- /dev/null +++ b/src/display/win/CMakeLists.txt @@ -0,0 +1 @@ +add_sources(DisplayManagerWin.cpp DisplayManagerWin.h) diff --git a/src/display/win/DisplayManagerWin.cpp b/src/display/win/DisplayManagerWin.cpp new file mode 100644 index 0000000..b61d730 --- /dev/null +++ b/src/display/win/DisplayManagerWin.cpp @@ -0,0 +1,212 @@ +// +// DisplayManagerWin.cpp +// konvergo +// +// Created by Lionel CHAZALLON on 18/06/2015. +// +// + +#include + +#include "QsLog.h" +#include "DisplayManagerWin.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool DisplayManagerWin::initialize() +{ + DISPLAY_DEVICEW displayInfo; + int displayId = 0; + + while (getDisplayInfo(displayId, displayInfo)) + { + if (displayInfo.StateFlags & (DISPLAY_DEVICE_ACTIVE | DISPLAY_DEVICE_ATTACHED)) + { + DEVMODEW modeInfo; + int modeId = 0; + + // add the display + DMDisplayPtr display = DMDisplayPtr(new DMDisplay); + display->id = displayId; + display->name = QString::fromWCharArray(displayInfo.DeviceString); + displays[display->id] = DMDisplayPtr(display); + m_displayAdapters[display->id] = QString::fromWCharArray(displayInfo.DeviceName); + + while (getModeInfo(displayId, modeId, modeInfo)) + { + // add the videomode to the display + DMVideoModePtr videoMode = DMVideoModePtr(new DMVideoMode); + videoMode->id = modeId; + display->videoModes[videoMode->id] = videoMode; + + // setup mode information + videoMode->height = modeInfo.dmPelsHeight; + videoMode->width = modeInfo.dmPelsWidth; + videoMode->refreshRate = modeInfo.dmDisplayFrequency; + videoMode->bitsPerPixel = modeInfo.dmBitsPerPel; + videoMode->interlaced = (modeInfo.dmDisplayFlags & DM_INTERLACED) ? true : false; + + // Windows just returns interger refresh rate so + // let's fudge it + if (videoMode->refreshRate == 59 || + videoMode->refreshRate == 29 || + videoMode->refreshRate == 23) + videoMode->refreshRate = (float)(videoMode->refreshRate + 1) / 1.001f; + + modeId++; + } + } + + displayId++; + } + + if (displays.size() == 0) + { + QLOG_DEBUG() << "No display found."; + return false; + } + else + return DisplayManager::initialize(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool DisplayManagerWin::setDisplayMode(int display, int mode) +{ + DEVMODEW modeInfo; + + if (!isValidDisplayMode(display, mode)) + return false; + + if (getModeInfo(display, mode, modeInfo)) + { + modeInfo.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS; + + LONG rc = ChangeDisplaySettingsExW((LPCWSTR)m_displayAdapters[display].utf16(), &modeInfo, NULL, + CDS_FULLSCREEN, NULL); + + if (rc != DISP_CHANGE_SUCCESSFUL) + { + QLOG_ERROR() << "Failed to changed DisplayMode, error" << rc; + return false; + } + else + { + return true; + } + } + + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +int DisplayManagerWin::getCurrentDisplayMode(int display) +{ + if (!isValidDisplay(display)) + return -1; + + // grab current mode + DEVMODEW modeInfo; + ZeroMemory(&modeInfo, sizeof(modeInfo)); + modeInfo.dmSize = sizeof(modeInfo); + + // grab current mode info + if (!EnumDisplaySettingsW((LPCWSTR)m_displayAdapters[display].utf16(), ENUM_CURRENT_SETTINGS, + &modeInfo)) + { + QLOG_ERROR() << "Failed to retrieve current mode"; + return -1; + } + + // check if current mode info matches on of our modes + for (int modeId = 0; modeId < displays[display]->videoModes.size(); modeId++) + { + if (isModeMatching(modeInfo, displays[display]->videoModes[modeId])) + return modeId; + } + + return -1; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +int DisplayManagerWin::getMainDisplay() +{ + DISPLAY_DEVICEW displayInfo; + int displayId = 0; + + while (getDisplayInfo(displayId, displayInfo)) + { + if (displayInfo.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) + return displayId; + + displayId++; + } + return -1; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +DisplayManagerWin::~DisplayManagerWin() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +int DisplayManagerWin::getDisplayFromPoint(int x, int y) +{ + for (int displayId = 0; displayId < displays.size(); displayId++) + { + int currentMode = getCurrentDisplayMode(displayId); + if (currentMode > 0) + { + DEVMODEW modeInfo; + if (getModeInfo(displayId, currentMode, modeInfo)) + { + QRect displayRect(modeInfo.dmPosition.x, modeInfo.dmPosition.y, modeInfo.dmPelsWidth, + modeInfo.dmPelsHeight); + + if (displayRect.contains(x, y)) + return displayId; + } + } + } + + return -1; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool DisplayManagerWin::getDisplayInfo(int display, DISPLAY_DEVICEW& info) +{ + ZeroMemory(&info, sizeof(info)); + info.cb = sizeof(info); + return EnumDisplayDevicesW(NULL, display, &info, 0); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool DisplayManagerWin::getModeInfo(int display, int mode, DEVMODEW& info) +{ + DISPLAY_DEVICEW displayInfo; + + if (m_displayAdapters.contains(display)) + { + ZeroMemory(&info, sizeof(info)); + info.dmSize = sizeof(info); + + return EnumDisplaySettingsExW((LPCWSTR)m_displayAdapters[display].utf16(), mode, &info, 0); + } + + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool DisplayManagerWin::isModeMatching(DEVMODEW& modeInfo, DMVideoModePtr videoMode) +{ + if (videoMode->height != modeInfo.dmPelsHeight) + return false; + if (videoMode->width != modeInfo.dmPelsWidth) + return false; + if (videoMode->refreshRate != modeInfo.dmDisplayFrequency) + return false; + if (videoMode->bitsPerPixel != modeInfo.dmBitsPerPel) + return false; + if (videoMode->interlaced != (modeInfo.dmDisplayFlags & DM_INTERLACED) ? true : false) + return false; + + return true; +} diff --git a/src/display/win/DisplayManagerWin.h b/src/display/win/DisplayManagerWin.h new file mode 100644 index 0000000..f8bd511 --- /dev/null +++ b/src/display/win/DisplayManagerWin.h @@ -0,0 +1,30 @@ +#ifndef DISPLAYMANAGERWIN_H +#define DISPLAYMANAGERWIN_H + +#include +#include "display/DisplayManager.h" +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Displays + +class DisplayManagerWin : public DisplayManager +{ + Q_OBJECT +private: + bool getDisplayInfo(int display, DISPLAY_DEVICEW& info); + bool getModeInfo(int display, int mode, DEVMODEW& info); + bool isModeMatching(DEVMODEW& modeInfo, DMVideoModePtr videoMode); + + QMap m_displayAdapters; + +public: + DisplayManagerWin(QObject* parent) : DisplayManager(parent) {} + virtual ~DisplayManagerWin(); + + virtual bool initialize(); + virtual bool setDisplayMode(int display, int mode); + virtual int getCurrentDisplayMode(int display); + virtual int getMainDisplay(); + virtual int getDisplayFromPoint(int x, int y); +}; + +#endif // DISPLAYMANAGERWIN_H diff --git a/src/display/x11/CMakeLists.txt b/src/display/x11/CMakeLists.txt new file mode 100644 index 0000000..55ac163 --- /dev/null +++ b/src/display/x11/CMakeLists.txt @@ -0,0 +1 @@ +add_sources(DisplayManagerX11.cpp DisplayManagerX11.h) diff --git a/src/display/x11/DisplayManagerX11.cpp b/src/display/x11/DisplayManagerX11.cpp new file mode 100755 index 0000000..567aeb6 --- /dev/null +++ b/src/display/x11/DisplayManagerX11.cpp @@ -0,0 +1,215 @@ +#include "DisplayManagerX11.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool DisplayManagerX11::initialize() +{ + displays.clear(); + + if (!xdisplay) + xdisplay = XOpenDisplay(NULL); + if (!xdisplay) + return false; + + int event_base, error_base; + if (!XRRQueryExtension(xdisplay, &event_base, &error_base)) + return false; + + if (resources) + XRRFreeScreenResources(resources); + + resources = XRRGetScreenResources(xdisplay, RootWindow(xdisplay, DefaultScreen(xdisplay))); + if (!resources) + return false; + + for (int o = 0; o < resources->noutput; o++) { + RROutput output = resources->outputs[o]; + XRRCrtcInfo *crtc = NULL; + XRROutputInfo *out = XRRGetOutputInfo(xdisplay, resources, output); + if (!out || !out->crtc) + goto next; + crtc = XRRGetCrtcInfo(xdisplay, resources, out->crtc); + if (!crtc) + goto next; + + { + DMDisplayPtr display = DMDisplayPtr(new DMDisplay()); + display->id = displays.size(); + display->name = out->name; + display->priv_id = o; + displays[display->id] = display; + + for (int om = 0; om < out->nmode; om++) { + RRMode xm = out->modes[om]; + for (int n = 0; n < resources->nmode; n++) { + XRRModeInfo m = resources->modes[n]; + if (m.id != xm) + continue; + + DMVideoModePtr mode = DMVideoModePtr(new DMVideoMode()); + mode->priv_id = n; + mode->id = display->videoModes.size(); + display->videoModes[mode->id] = mode; + + mode->interlaced = m.modeFlags & RR_Interlace; + + double vTotal = m.vTotal; + if (m.modeFlags & RR_DoubleScan) + vTotal *= 2; + if (m.modeFlags & RR_Interlace) + vTotal /= 2; + mode->refreshRate = m.dotClock / (m.hTotal * vTotal); + + mode->width = m.width; + mode->height = m.height; + mode->bitsPerPixel = 0; // we can't know; depth is not managed by xrandr + } + } + } + next: + if (crtc) + XRRFreeCrtcInfo(crtc); + if (out) + XRRFreeOutputInfo(out); + } + + if (displays.empty()) + return false; + else + return DisplayManager::initialize(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool DisplayManagerX11::setDisplayMode(int display, int mode) +{ + if (!isValidDisplayMode(display, mode) || !resources) + return false; + + DMDisplayPtr displayptr = displays[display]; + DMVideoModePtr videomode = displayptr->videoModes[mode]; + + RROutput output = resources->outputs[displayptr->priv_id]; + RRMode xrmode = resources->modes[videomode->priv_id].id; + + bool success = false; + XRRCrtcInfo *crtc = NULL; + XRROutputInfo *out = XRRGetOutputInfo(xdisplay, resources, output); + if (!out || !out->crtc) + goto done; + crtc = XRRGetCrtcInfo(xdisplay, resources, out->crtc); + if (!crtc) + goto done; + + // Keep all information, except the mode. + success = XRRSetCrtcConfig(xdisplay, resources, out->crtc, crtc->timestamp, + crtc->x, crtc->y, xrmode, crtc->rotation, + crtc->outputs, crtc->noutput); + +done: + if (crtc) + XRRFreeCrtcInfo(crtc); + if (out) + XRRFreeOutputInfo(out); + + return success; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +int DisplayManagerX11::getCurrentDisplayMode(int display) +{ + if (!isValidDisplay(display) || !resources) + return -1; + + DMDisplayPtr displayptr = displays[display]; + RROutput output = resources->outputs[displayptr->priv_id]; + + int videomode_id = -1; + XRRCrtcInfo *crtc = NULL; + XRROutputInfo *out = XRRGetOutputInfo(xdisplay, resources, output); + if (!out || !out->crtc) + goto done; + crtc = XRRGetCrtcInfo(xdisplay, resources, out->crtc); + if (!crtc) + goto done; + + foreach (DMVideoModePtr mode, displayptr->videoModes) + { + XRRModeInfo m = resources->modes[mode->priv_id]; + if (crtc->mode == m.id) + { + videomode_id = mode->id; + break; + } + } + +done: + if (crtc) + XRRFreeCrtcInfo(crtc); + if (out) + XRRFreeOutputInfo(out); + + return videomode_id; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +int DisplayManagerX11::getMainDisplay() +{ + // This is probably not what DisplayManager means. + RROutput main = XRRGetOutputPrimary(xdisplay, RootWindow(xdisplay, DefaultScreen(xdisplay))); + if (!main || !resources) + return -1; + + for (int o = 0; o < resources->noutput; o++) + { + if (main == resources->outputs[o]) + { + for (int displayid = 0; displayid < displays.size(); displayid++) + { + if (displays[displayid]->priv_id == o) + return displayid; + } + break; + } + } + return -1; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +int DisplayManagerX11::getDisplayFromPoint(int x, int y) +{ + for (int displayid = 0; displayid < displays.size(); displayid++) + { + RROutput output = resources->outputs[displays[displayid]->priv_id]; + XRRCrtcInfo *crtc = NULL; + XRROutputInfo *out = XRRGetOutputInfo(xdisplay, resources, output); + bool matches = false; + if (!out || !out->crtc) + goto done; + crtc = XRRGetCrtcInfo(xdisplay, resources, out->crtc); + if (!crtc) + goto done; + + matches = x >= crtc->x && y >= crtc->y && + x < crtc->x + crtc->width && + y < crtc->y + crtc->height; + + done: + if (crtc) + XRRFreeCrtcInfo(crtc); + if (out) + XRRFreeOutputInfo(out); + + if (matches) + return displayid; + } + + return -1; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +DisplayManagerX11::~DisplayManagerX11() +{ + if (resources) + XRRFreeScreenResources(resources); + if (xdisplay) + XCloseDisplay(xdisplay); +} diff --git a/src/display/x11/DisplayManagerX11.h b/src/display/x11/DisplayManagerX11.h new file mode 100644 index 0000000..df59096 --- /dev/null +++ b/src/display/x11/DisplayManagerX11.h @@ -0,0 +1,33 @@ +#ifndef DISPLAYMANAGERX11_H_ +#define DISPLAYMANAGERX11_H_ + +#include + +#include + +// X11 headers are messing up things for us +#undef CursorShape +#undef Bool +#undef Status + +#include "display/DisplayManager.h" + +class DisplayManagerX11 : public DisplayManager +{ + Q_OBJECT +private: + Display* xdisplay; + XRRScreenResources* resources; + +public: + DisplayManagerX11(QObject* parent) : DisplayManager(parent), xdisplay(0), resources(0) {}; + virtual ~DisplayManagerX11(); + + virtual bool initialize(); + virtual bool setDisplayMode(int display, int mode); + virtual int getCurrentDisplayMode(int display); + virtual int getMainDisplay(); + virtual int getDisplayFromPoint(int x, int y); +}; + +#endif /* DISPLAYMANAGERX11_H_ */ diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt new file mode 100644 index 0000000..3143be5 --- /dev/null +++ b/src/input/CMakeLists.txt @@ -0,0 +1,28 @@ +set(INPUT_SRCS + InputComponent.cpp + InputComponent.h + InputMapping.cpp + InputMapping.h + InputKeyboard.h + InputSocket.h + InputSocket.cpp +) + +if(APPLE) + add_subdirectory(apple) +endif(APPLE) + +if(SDL2_FOUND) + list(APPEND INPUT_SRCS InputSDL.cpp InputSDL.h) +endif(SDL2_FOUND) + +if(ENABLE_LIRC) + list(APPEND INPUT_SRCS InputLIRC.cpp InputLIRC.h) +endif(ENABLE_LIRC) + +if(CEC_FOUND) + list(APPEND INPUT_SRCS InputCEC.cpp InputCEC.h) +endif(CEC_FOUND) + +add_sources(${INPUT_SRCS}) + diff --git a/src/input/InputCEC.cpp b/src/input/InputCEC.cpp new file mode 100644 index 0000000..7dd5758 --- /dev/null +++ b/src/input/InputCEC.cpp @@ -0,0 +1,361 @@ + +#include "QsLog.h" +#include "InputCEC.h" +#include "settings/SettingsComponent.h" + +static QMap cecKeyMap { \ + { CEC_USER_CONTROL_CODE_SELECT , INPUT_KEY_SELECT } , \ + { CEC_USER_CONTROL_CODE_UP , INPUT_KEY_UP } , \ + { CEC_USER_CONTROL_CODE_DOWN , INPUT_KEY_DOWN } , \ + { CEC_USER_CONTROL_CODE_LEFT , INPUT_KEY_LEFT } , \ + { CEC_USER_CONTROL_CODE_RIGHT , INPUT_KEY_RIGHT } , \ + { CEC_USER_CONTROL_CODE_SETUP_MENU , INPUT_KEY_MENU } , \ + { CEC_USER_CONTROL_CODE_PLAY , INPUT_KEY_PLAY } , \ + { CEC_USER_CONTROL_CODE_PAUSE , INPUT_KEY_PAUSE } , \ + { CEC_USER_CONTROL_CODE_STOP , INPUT_KEY_STOP } , \ + { CEC_USER_CONTROL_CODE_EXIT , INPUT_KEY_BACK } , \ + { CEC_USER_CONTROL_CODE_FAST_FORWARD , INPUT_KEY_SEEKFWD } , \ + { CEC_USER_CONTROL_CODE_REWIND , INPUT_KEY_SEEKBCK } , \ + { CEC_USER_CONTROL_CODE_DISPLAY_INFORMATION , INPUT_KEY_INFO } , \ + { CEC_USER_CONTROL_CODE_FORWARD , INPUT_KEY_NEXT } , \ + { CEC_USER_CONTROL_CODE_BACKWARD , INPUT_KEY_PREV } , \ + { CEC_USER_CONTROL_CODE_F1_BLUE , INPUT_KEY_BLUE } , \ + { CEC_USER_CONTROL_CODE_F2_RED , INPUT_KEY_RED } , \ + { CEC_USER_CONTROL_CODE_F3_GREEN , INPUT_KEY_GREEN } , \ + { CEC_USER_CONTROL_CODE_F4_YELLOW , INPUT_KEY_YELLOW } , \ + { CEC_USER_CONTROL_CODE_SUB_PICTURE, INPUT_KEY_SUBTITLES } , \ + { CEC_USER_CONTROL_CODE_ROOT_MENU, INPUT_KEY_HOME }, \ + { CEC_USER_CONTROL_CODE_NUMBER0, INPUT_KEY_0 } , \ + { CEC_USER_CONTROL_CODE_NUMBER1, INPUT_KEY_1 } , \ + { CEC_USER_CONTROL_CODE_NUMBER2, INPUT_KEY_2 } , \ + { CEC_USER_CONTROL_CODE_NUMBER3, INPUT_KEY_3 } , \ + { CEC_USER_CONTROL_CODE_NUMBER4, INPUT_KEY_4 } , \ + { CEC_USER_CONTROL_CODE_NUMBER5, INPUT_KEY_5 } , \ + { CEC_USER_CONTROL_CODE_NUMBER6, INPUT_KEY_6 } , \ + { CEC_USER_CONTROL_CODE_NUMBER7, INPUT_KEY_7 } , \ + { CEC_USER_CONTROL_CODE_NUMBER8, INPUT_KEY_8 } , \ + { CEC_USER_CONTROL_CODE_NUMBER9, INPUT_KEY_9 } , \ + { CEC_USER_CONTROL_CODE_ELECTRONIC_PROGRAM_GUIDE, INPUT_KEY_GUIDE } , \ + }; + +////////////////////////////////////////////////////////////////////////////////////////////////// +InputCEC::InputCEC(QObject *parent) : InputBase(parent) +{ + m_configuration.Clear(); + m_callbacks.Clear(); + m_adapter = NULL; + m_verboseLogging = false; + + // setup adapter check timer + m_timer.setInterval(1000); + m_timer.setSingleShot(false); + connect(&m_timer, &QTimer::timeout, this, &InputCEC::checkAdapter); +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +bool InputCEC::initInput() +{ + m_configuration.Clear(); + m_callbacks.Clear(); + + m_verboseLogging = SettingsComponent::Get().value(SETTINGS_SECTION_CEC, "verbose_logging").toBool(); + + m_configuration.clientVersion = LIBCEC_VERSION_CURRENT; + strcpy(m_configuration.strDeviceName, "PlexMPlayer"); + m_configuration.bActivateSource = 0; + m_callbacks.CBCecLogMessage = &CecLogMessage; + m_callbacks.CBCecKeyPress = &CecKeyPress; + m_callbacks.CBCecCommand = &CecCommand; + m_callbacks.CBCecAlert = &CecAlert; + m_configuration.callbackParam = this; + m_configuration.callbacks = &m_callbacks; + m_configuration.deviceTypes.Add(CEC_DEVICE_TYPE_RECORDING_DEVICE); + m_configuration.bAutodetectAddress = CEC_DEFAULT_SETTING_AUTODETECT_ADDRESS; + m_configuration.iPhysicalAddress = CEC_PHYSICAL_ADDRESS_TV; + m_configuration.baseDevice = CECDEVICE_AUDIOSYSTEM; + m_configuration.bActivateSource = 1; + + m_configuration.iHDMIPort = (quint8)SettingsComponent::Get().value(SETTINGS_SECTION_CEC, "hdmiport").toInt(); + + // open libcec + m_adapter = (ICECAdapter*)CECInitialise(&m_configuration); + if (!m_adapter) + { + QLOG_ERROR() << "Unable to initialize libCEC."; + return false; + } + + QLOG_INFO() << "libCEC was successfully initialized, found version" + << m_configuration.serverVersion; + + // init video on targets that need this + m_adapter->InitVideoStandalone(); + + // start the adapter check timer + m_timer.start(); + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +InputCEC::~InputCEC() +{ + m_timer.stop(); + close(); +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +void InputCEC::close() +{ + if (m_adapter) + { + QLOG_DEBUG() << "Closing libCEC."; + closeAdapter(); + CECDestroy(m_adapter); + } +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +bool InputCEC::openAdapter() +{ + bool ret = false; + m_lock.lock(); + + // try to find devices + cec_adapter devices[10]; + int devicesCount = m_adapter->FindAdapters(devices, 10, NULL); + if (devicesCount > 0) + { + // list devices + QLOG_INFO() << "libCEC found" << devicesCount << "CEC adapters."; + + // open first adapter + m_adapterPort = devices[0].comm; + if (m_adapter->Open(m_adapterPort.toStdString().c_str())) + { + QLOG_INFO() << "Device " << devices[0].path << "was successfully openned"; + ret = true; + } + else + { + QLOG_ERROR() << "Opening device" << devices[0].path << "failed"; + ret = false; + } + } + + m_lock.unlock(); + return ret; +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +void InputCEC::closeAdapter() +{ + m_lock.lock(); + + if (m_adapter) + m_adapter->Close(); + + m_adapterPort = ""; + m_lock.unlock(); +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +void InputCEC::reopenAdapter() +{ + closeAdapter(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void InputCEC::checkAdapter() +{ + if (m_adapterPort.isEmpty()) + { + openAdapter(); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void InputCEC::sendReceivedInput(const QString &source, const QString &keycode, float amount) +{ + emit receivedInput(source, keycode, amount); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +QString InputCEC::getCommandString(cec_user_control_code code, unsigned int duration) +{ + QString key = cecKeyMap[code]; + + if (!key.isEmpty() && (duration > CEC_LONGPRESS_DURATION)) + { + key += "_LONG"; + } + + return key; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +int InputCEC::CecLogMessage(void* cbParam, const cec_log_message message) +{ + InputCEC *cec = (InputCEC*)cbParam; + switch (message.level) + { + case CEC_LOG_ERROR: + QLOG_ERROR() << "libCEC ERROR:" << message.message; + break; + + case CEC_LOG_WARNING: + QLOG_WARN() << "libCEC WARNING:" << message.message; + break; + + case CEC_LOG_NOTICE: + QLOG_INFO() << "libCEC NOTICE:" << message.message; + break; + + case CEC_LOG_DEBUG: + if (cec->m_verboseLogging) + { + QLOG_DEBUG() << "libCEC DEBUG:" << message.message; + } + break; + + case CEC_LOG_TRAFFIC: + break; + + default: + break; + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +int InputCEC::CecKeyPress(void *cbParam, const cec_keypress key) +{ + InputCEC *cec = (InputCEC*)cbParam; + if (cec && key.duration == 0) + { + QString cmdString = cec->getCommandString(key.keycode, key.duration); + + if (!cmdString.isEmpty()) + cec->sendReceivedInput(CEC_INPUT_NAME, cmdString); + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +int InputCEC::CecCommand(void *cbParam, const cec_command command) +{ + InputCEC *cec = (InputCEC*)cbParam; + + switch(command.opcode) + { + case CEC_OPCODE_PLAY: + cec->sendReceivedInput(CEC_INPUT_NAME, INPUT_KEY_PLAY); + break; + + case CEC_OPCODE_DECK_CONTROL: + if (command.parameters.size) + { + switch(command.parameters[0]) + { + case CEC_DECK_CONTROL_MODE_SKIP_FORWARD_WIND: + cec->sendReceivedInput(CEC_INPUT_NAME, INPUT_KEY_SEEKFWD); + break; + + case CEC_DECK_CONTROL_MODE_SKIP_REVERSE_REWIND: + cec->sendReceivedInput(CEC_INPUT_NAME, INPUT_KEY_SEEKBCK); + break; + + case CEC_DECK_CONTROL_MODE_STOP: + cec->sendReceivedInput(CEC_INPUT_NAME, INPUT_KEY_STOP); + break; + + default: + break; + } + } + break; + + case CEC_OPCODE_VENDOR_REMOTE_BUTTON_DOWN: + if (command.parameters.size) + { + switch(command.parameters[0]) + { + // samsung Return key + case CEC_USER_CONTROL_CODE_AN_RETURN: + cec->sendReceivedInput(CEC_INPUT_NAME, INPUT_KEY_BACK); + break; + + default: + break; + } + } + break; + + case CEC_OPCODE_VENDOR_REMOTE_BUTTON_UP: + case CEC_OPCODE_USER_CONTROL_PRESSED: + // ignore those commands as they are handled in CecKeypress + break; + + case CEC_OPCODE_GIVE_OSD_NAME: + case CEC_OPCODE_GIVE_PHYSICAL_ADDRESS: + // ignore those known commands (only pollng from TV) + break; + + default: + QLOG_DEBUG() << "Unhandled CEC command " << command.opcode; + if (command.parameters.size) + { + for (int i=0; i [%1]= %2").arg(i).arg(command.parameters[i]); + } + break; + } + + return 1; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +int InputCEC::CecAlert(void *cbParam, const libcec_alert type, const libcec_parameter param) +{ + bool reopen = false; + + switch (type) + { + case CEC_ALERT_SERVICE_DEVICE: + QLOG_ERROR() << "libCEC : Alert CEC_ALERT_SERVICE_DEVICE"; + break; + + case CEC_ALERT_CONNECTION_LOST: + QLOG_ERROR() << "libCEC : Alert CEC_ALERT_CONNECTION_LOST"; + break; + + case CEC_ALERT_PERMISSION_ERROR: + QLOG_ERROR() << "libCEC : Alert CEC_ALERT_PERMISSION_ERROR"; + reopen = true; + break; + + case CEC_ALERT_PORT_BUSY: + QLOG_ERROR() << "libCEC : Alert CEC_ALERT_PORT_BUSY"; + reopen = true; + break; + + default: + break; + } + + if (reopen) + { + QLOG_DEBUG() << "libCEC : Reopenning adapter"; + InputCEC *cec = (InputCEC*)cbParam; + if (cec) + cec->reopenAdapter(); + } + + return 0; +} + + + + + diff --git a/src/input/InputCEC.h b/src/input/InputCEC.h new file mode 100644 index 0000000..f608962 --- /dev/null +++ b/src/input/InputCEC.h @@ -0,0 +1,55 @@ +#ifndef INPUTCEC_H +#define INPUTCEC_H + +#include +#include +#include "input/InputComponent.h" +#include + +using namespace CEC; + +#define CEC_LONGPRESS_DURATION 1000 // duration after keypress is considerered as long + +#define CEC_INPUT_NAME "CEC" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +class InputCEC : public InputBase +{ +public: + InputCEC(QObject* parent); + ~InputCEC(); + + virtual const char* inputName() { return CEC_INPUT_NAME; } + virtual bool initInput(); + void close(); + +private: + libcec_configuration m_configuration; + ICECCallbacks m_callbacks; + ICECAdapter* m_adapter; + QString m_adapterPort; + QMutex m_lock; + QTimer m_timer; + bool m_verboseLogging; + + bool openAdapter(); + void closeAdapter(); + QString getCommandString(cec_user_control_code code, unsigned int duration); + void sendReceivedInput(const QString& source, const QString& keycode, float amount = 1.0); + + + // libcec callbacks + static int CecLogMessage(void *cbParam, const cec_log_message message); + static int CecKeyPress(void *cbParam, const cec_keypress key); + static int CecCommand(void *cbParam, const cec_command command); + static int CecAlert(void *cbParam, const libcec_alert type, const libcec_parameter param); + +public: + void reopenAdapter(); + +public slots: + void checkAdapter(); + +}; + +#endif // INPUTCEC_H diff --git a/src/input/InputComponent.cpp b/src/input/InputComponent.cpp new file mode 100644 index 0000000..4eddcc1 --- /dev/null +++ b/src/input/InputComponent.cpp @@ -0,0 +1,104 @@ + +#include "QsLog.h" +#include "InputComponent.h" +#include "settings/SettingsComponent.h" +#include "system/SystemComponent.h" +#include "power/PowerComponent.h" +#include "InputKeyboard.h" +#include "InputSocket.h" + +#ifdef Q_OS_MAC +#include "apple/InputAppleRemote.h" +#include "apple/InputAppleMediaKeys.h" +#endif + +#ifdef HAVE_SDL +#include "InputSDL.h" +#endif + +#ifdef HAVE_LIRC +#include "InputLIRC.h" +#endif + +#ifdef HAVE_CEC +#include "InputCEC.h" +#endif + +/////////////////////////////////////////////////////////////////////////////////////////////////// +InputComponent::InputComponent(QObject* parent) : ComponentBase(parent) +{ + m_mappings = new InputMapping(this); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool InputComponent::addInput(InputBase* base) +{ + if (!base->initInput()) + { + QLOG_WARN() << "Failed to init input:" << base->inputName(); + return false; + } + + QLOG_INFO() << "Successfully inited input:" << base->inputName(); + m_inputs.push_back(base); + + // we connect to the provider receivedInput signal, then we check if the name + // needs to be remaped in remapInput and then finally send it out to JS land. + // + connect(base, &InputBase::receivedInput, this, &InputComponent::remapInput); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool InputComponent::componentInitialize() +{ + // load our input mappings + m_mappings->loadMappings(); + + addInput(&InputKeyboard::Get()); + addInput(new InputSocket(this)); + +#ifdef Q_OS_MAC + addInput(new InputAppleRemote(this)); + addInput(new InputAppleMediaKeys(this)); +#endif +#ifdef HAVE_SDL + addInput(new InputSDL(this)); +#endif +#ifdef HAVE_LIRC + addInput(new InputLIRC(this)); +#endif +#ifdef HAVE_CEC + if (SettingsComponent::Get().value(SETTINGS_SECTION_CEC, "enable").toBool()) + addInput(new InputCEC(this)); +#endif + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void InputComponent::remapInput(const QString &source, const QString &keycode, float amount) +{ + // hide mouse if it's visible. + SystemComponent::Get().setCursorVisibility(false); + + QString action = m_mappings->mapToAction(source, keycode); + if (!action.isEmpty()) + { + if (action.startsWith("host:")) + { + QLOG_DEBUG() << "Handling host command:" << action.mid(5); + emit receivedHostCommand(action.mid(5)); + } + else + { + QLOG_DEBUG() << "Sending action:" << action; + emit receivedAction(action); + } + } + else + { + QLOG_WARN() << "Could not map:" << source << keycode << "to any useful action"; + } +} diff --git a/src/input/InputComponent.h b/src/input/InputComponent.h new file mode 100644 index 0000000..e2869a0 --- /dev/null +++ b/src/input/InputComponent.h @@ -0,0 +1,94 @@ +#ifndef INPUTADAPTER_H +#define INPUTADAPTER_H + +#include +#include +#include "ComponentManager.h" +#include "InputMapping.h" + +class InputBase : public QObject +{ + Q_OBJECT +public: + InputBase(QObject* parent = 0) : QObject(parent) { } + virtual bool initInput() = 0; + virtual const char* inputName() = 0; + +signals: + void receivedInput(const QString& source, const QString& keycode, float amount = 1.0); +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// well known keys +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#define INPUT_KEY_LEFT "KEY_LEFT" +#define INPUT_KEY_RIGHT "KEY_RIGHT" +#define INPUT_KEY_UP "KEY_UP" +#define INPUT_KEY_DOWN "KEY_DOWN" +#define INPUT_KEY_SELECT "KEY_SELECT" +#define INPUT_KEY_MENU "KEY_MENU" +#define INPUT_KEY_PLAY "KEY_PLAY" +#define INPUT_KEY_PAUSE "KEY_PAUSE" +#define INPUT_KEY_STOP "KEY_STOP" +#define INPUT_KEY_DOWN "KEY_DOWN" +#define INPUT_KEY_BACK "KEY_BACK" +#define INPUT_KEY_SEEKFWD "KEY_SEEKFWD" +#define INPUT_KEY_SEEKBCK "KEY_SEEKBCK" +#define INPUT_KEY_SUBTITLES "KEY_SUBTITLES" +#define INPUT_KEY_INFO "KEY_INFO" +#define INPUT_KEY_NEXT "KEY_NEXT" +#define INPUT_KEY_PREV "KEY_PREV" +#define INPUT_KEY_RED "KEY_RED" +#define INPUT_KEY_GREEN "KEY_GREEN" +#define INPUT_KEY_BLUE "KEY_BLUE" +#define INPUT_KEY_YELLOW "KEY_YELLOW" +#define INPUT_KEY_HOME "KEY_HOME" +#define INPUT_KEY_0 "KEY_NUMERIC_0" +#define INPUT_KEY_1 "KEY_NUMERIC_1" +#define INPUT_KEY_2 "KEY_NUMERIC_2" +#define INPUT_KEY_3 "KEY_NUMERIC_3" +#define INPUT_KEY_4 "KEY_NUMERIC_4" +#define INPUT_KEY_5 "KEY_NUMERIC_5" +#define INPUT_KEY_6 "KEY_NUMERIC_6" +#define INPUT_KEY_7 "KEY_NUMERIC_7" +#define INPUT_KEY_8 "KEY_NUMERIC_8" +#define INPUT_KEY_9 "KEY_NUMERIC_9" +#define INPUT_KEY_GUIDE "KEY_GUIDE" + +#define INPUT_KEY_LEFT_LONG "KEY_LEFT_LONG" +#define INPUT_KEY_RIGHT_LONG "KEY_RIGHT_LONG" +#define INPUT_KEY_UP_LONG "KEY_UP_LONG" +#define INPUT_KEY_DOWN_LONG "KEY_DOWN_LONG" +#define INPUT_KEY_SELECT_LONG "KEY_SELECT_LONG" +#define INPUT_KEY_MENU_LONG "KEY_MENU_LONG" +#define INPUT_KEY_PLAY_LONG "KEY_PLAY_LONG" +#define INPUT_KEY_DOWN_LONG "KEY_DOWN_LONG" + + +class InputComponent : public ComponentBase +{ + Q_OBJECT + DEFINE_SINGLETON(InputComponent); + +public: + virtual const char* componentName() { return "input"; } + virtual bool componentExport() { return true; } + virtual bool componentInitialize(); + +signals: + void receivedAction(const QString& action); + void receivedHostCommand(const QString& hostCommand); + +private Q_SLOTS: + void remapInput(const QString& source, const QString& keycode, float amount = 1.0); + +private: + InputComponent(QObject *parent = 0); + bool addInput(InputBase* input); + + QList m_inputs; + InputMapping* m_mappings; +}; + +#endif // INPUTADAPTER_H diff --git a/src/input/InputKeyboard.h b/src/input/InputKeyboard.h new file mode 100644 index 0000000..b39764e --- /dev/null +++ b/src/input/InputKeyboard.h @@ -0,0 +1,31 @@ +// +// Created by Tobias Hieta on 09/06/15. +// + +#ifndef KONVERGO_INPUTKEYBOARD_H +#define KONVERGO_INPUTKEYBOARD_H + +#include +#include "InputComponent.h" +#include "QsLog.h" + +class InputKeyboard : public InputBase +{ + Q_OBJECT + DEFINE_SINGLETON(InputKeyboard); + +public: + virtual bool initInput() { return true; } + virtual const char* inputName() { return "Keyboard"; } + + void keyPress(const QKeySequence& sequence) + { + QLOG_DEBUG() << "Input:" << sequence.toString(); + emit receivedInput("Keyboard", sequence.toString()); + } + +private: + explicit InputKeyboard(QObject* parent = 0) : InputBase(parent) {} +}; + +#endif //KONVERGO_INPUTKEYBOARD_H diff --git a/src/input/InputLIRC.cpp b/src/input/InputLIRC.cpp new file mode 100644 index 0000000..1fb9b52 --- /dev/null +++ b/src/input/InputLIRC.cpp @@ -0,0 +1,116 @@ + +#include +#include "QsLog.h" +#include "InputLIRC.h" + +#define DEFAULT_LIRC_ADDRESS "/run/lirc/lircd" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +InputLIRC::InputLIRC(QObject* parent) : InputBase(parent) +{ + socket = new QLocalSocket(this); + socketNotifier = NULL; + + connect(socket, SIGNAL(error(QLocalSocket::LocalSocketError)), this, + SLOT(socketerror(QLocalSocket::LocalSocketError))); + connect(socket, SIGNAL(connected()), this, SLOT(connected())); + connect(socket, SIGNAL(disconnected()), this, SLOT(disconnected())); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +InputLIRC::~InputLIRC() +{ + if (socket) + disconnect(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool InputLIRC::initInput() +{ + return connectToLIRC(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool InputLIRC::connectToLIRC() +{ + disconnect(); + + socket->connectToServer(DEFAULT_LIRC_ADDRESS, QIODevice::ReadWrite); + if (isConnected()) + { + socketNotifier = new QSocketNotifier(socket->socketDescriptor(), QSocketNotifier::Read, this); + connect(socketNotifier, SIGNAL(activated(int)), this, SLOT(read(int))); + return true; + } + else + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void InputLIRC::disconnectFromLIRC() +{ + if (socket) + socket->disconnectFromServer(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool InputLIRC::isConnected() +{ + while (socket->state() == QLocalSocket::ConnectingState) + { + QGuiApplication::processEvents(); + } + + return (socket->state() == QLocalSocket::ConnectedState); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void InputLIRC::connected() +{ + QLOG_INFO() << "LIRC socket connected "; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void InputLIRC::disconnected() +{ + QLOG_INFO() << "LIRC socket disconnected "; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void InputLIRC::socketerror(QLocalSocket::LocalSocketError socketError) +{ + QLOG_ERROR() << "LIRC Socket Error : " << socketError; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void InputLIRC::read(int handle) +{ + QString input; + + while ((input = socket->readLine()) != "") + { + QStringList cmdList = input.split(' ', QString::KeepEmptyParts); + if (cmdList.size() == 4) + { + // grab the input data + QString code = cmdList.at(0); + QString repeat = cmdList.at(1); + QString command = cmdList.at(2); + QString remote = cmdList.at(3); + + int repeatCount = repeat.toInt(); + + QLOG_INFO() << "LIRC Got Key : " << command << ", repeat count:" << repeatCount + << ", from remote " << remote; + + // we dont want to have all the IR Bursts when we press a key + // it makes GUI unusable + if ((repeatCount % 3) == 0) + { + emit receivedInput("LIRC", command); + } + } + else + QLOG_ERROR() << "Unknown LIRC input: " << input; + } +} diff --git a/src/input/InputLIRC.h b/src/input/InputLIRC.h new file mode 100644 index 0000000..5e6af4a --- /dev/null +++ b/src/input/InputLIRC.h @@ -0,0 +1,33 @@ +#ifndef INPUTLIRC_H +#define INPUTLIRC_H + +#include +#include +#include "input/InputComponent.h" + +class InputLIRC : public InputBase +{ + Q_OBJECT +private: + QLocalSocket* socket; + QSocketNotifier* socketNotifier; + + bool connectToLIRC(); + void disconnectFromLIRC(); + bool isConnected(); + +public: + InputLIRC(QObject* parent); + ~InputLIRC(); + + virtual const char* inputName() { return "LIRC"; } + virtual bool initInput(); + +private Q_SLOTS: + void connected(); + void disconnected(); + void socketerror(QLocalSocket::LocalSocketError socketError); + void read(int handle); +}; + +#endif // INPUTLIRC_H diff --git a/src/input/InputMapping.cpp b/src/input/InputMapping.cpp new file mode 100644 index 0000000..a4058bf --- /dev/null +++ b/src/input/InputMapping.cpp @@ -0,0 +1,161 @@ +#include "InputMapping.h" +#include +#include +#include +#include +#include +#include +#include + +#include "QsLog.h" +#include "Paths.h" +#include "utils/Utils.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +InputMapping::InputMapping(QObject *parent) : QObject(parent) +{ + m_watcher = new QFileSystemWatcher(this); + connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, &InputMapping::dirChange); + connect(m_watcher, &QFileSystemWatcher::fileChanged, this, &InputMapping::dirChange); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void InputMapping::dirChange() +{ + QLOG_INFO() << "Change to user input path, reloading mappings."; + loadMappings(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool InputMapping::loadMappings() +{ + m_inputMatcher.clear(); + m_sourceMatcher.clear(); + + // don't watch the path while we potentially copy files to the directory + if (m_watcher->directories().size() > 0) + m_watcher->removePath(Paths::dataDir("inputmaps")); + + // first we load the bundled mappings + loadMappingDirectory(":/inputmaps", true); + + // now we load the user ones, if there are any + // they will now overload the built-in ones. + // + loadMappingDirectory(Paths::dataDir("inputmaps"), false); + + // we want to watch this dir for new files and changed files + m_watcher->addPath(Paths::dataDir("inputmaps")); + + emit mappingChanged(); + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////// +QString InputMapping::mapToAction(const QString& source, const QString& keycode) +{ + // if the source is direct we will just use the keycode as the action + if (source == "direct") + return keycode; + + // first we need to match the source + QVariant sourceName = m_sourceMatcher.match(source); + if (sourceName.isValid()) + { + QVariant action = m_inputMatcher.value(sourceName.toString())->match(keycode); + if (action.isValid()) + { + QLOG_DEBUG() << "Mapped:" << keycode << "to action:" << action.toString(); + return action.toString(); + } + } + return QString(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool InputMapping::loadMappingFile(const QString& path, QPair &mappingPair) +{ + QJsonParseError err; + auto doc = Utils::OpenJsonDocument(path, &err); + if (doc.isNull()) + { + QLOG_WARN() << "Failed to parse input mapping file:" << path << "," << err.errorString(); + return false; + } + + if (doc.isObject()) + { + auto obj = doc.object(); + if (!obj.contains("name")) + { + QLOG_WARN() << "Missing elements 'name' from mapping file:" << path; + return false; + } + + if (!obj.contains("idmatcher")) + { + QLOG_WARN() << "Missing element 'idmatcher' from mapping file:" << path; + return false; + } + + if (!obj.contains("mapping")) + { + QLOG_WARN() << "Missing element 'mapping' from mapping file:" << path; + return false; + } + + mappingPair = qMakePair(obj["name"].toString(), obj.toVariantMap()); + return true; + } + + QLOG_WARN() << "Wrong format for file:" << path; + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool InputMapping::loadMappingDirectory(const QString& path, bool copy) +{ + QLOG_INFO() << "Loading inputmaps from:" << path; + QDirIterator it(path); + while (it.hasNext()) + { + QFileInfo finfo = QFileInfo(it.next()); + if (finfo.isFile() && finfo.isReadable() && finfo.fileName().endsWith(".json")) + { + // make a copy of the original file to the example directory + if (copy) + { + QDir userdir(Paths::dataDir()); + userdir.mkpath("inputmaps/examples/"); + QString examplePath(userdir.filePath("inputmaps/examples/" + finfo.fileName())); + + // make sure we really overwrite the file. copy will not do this. + if (QFile(examplePath).exists()) + QFile::remove(examplePath); + + QFile::copy(finfo.absoluteFilePath(), examplePath); + QFile(examplePath).setPermissions(QFileDevice::ReadOwner | QFileDevice::ReadGroup | QFileDevice::WriteOwner | + QFileDevice::WriteGroup | QFileDevice::ReadOther); + } + + + QPair mapping; + if (loadMappingFile(finfo.absoluteFilePath(), mapping)) + { + // add the source regexp to the matcher + if (m_sourceMatcher.addMatcher(mapping.second.value("idmatcher").toString(), mapping.first)) + { + // get the input map and add it to a new CachedMatcher + QVariantMap inputMap = mapping.second.value("mapping").toMap(); + CachedRegexMatcher* inputMatcher = new CachedRegexMatcher(this); + foreach(const QString& pattern, inputMap.keys()) + inputMatcher->addMatcher("^" + pattern + "$", inputMap.value(pattern)); + + m_inputMatcher.insert(mapping.first, inputMatcher); + } + } + } + } + + return true; +} diff --git a/src/input/InputMapping.h b/src/input/InputMapping.h new file mode 100644 index 0000000..1bc3caa --- /dev/null +++ b/src/input/InputMapping.h @@ -0,0 +1,37 @@ +#ifndef INPUTMAPPING_H +#define INPUTMAPPING_H + +#include +#include +#include +#include +#include +#include +#include + +class InputMapping : public QObject +{ + Q_OBJECT + +public: + explicit InputMapping(QObject *parent = 0); + bool loadMappings(); + QString mapToAction(const QString& source, const QString& keycode); + +private Q_SLOTS: + void dirChange(); + +signals: + void mappingChanged(); + +private: + bool loadMappingFile(const QString &path, QPair &mappingPair); + bool loadMappingDirectory(const QString& path, bool copy); + + QFileSystemWatcher* m_watcher; + + QHash m_inputMatcher; + CachedRegexMatcher m_sourceMatcher; +}; + +#endif // INPUTMAPPING_H diff --git a/src/input/InputSDL.cpp b/src/input/InputSDL.cpp new file mode 100644 index 0000000..204535a --- /dev/null +++ b/src/input/InputSDL.cpp @@ -0,0 +1,275 @@ +// +// InputSDL.cpp +// konvergo +// +// Created by Lionel CHAZALLON on 16/10/2014. +// +// + +#include +#include "InputSDL.h" +#include "QsLog.h" + +#include +#include + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool InputSDLWorker::initialize() +{ + // close if it was previously inited + close(); + + // init SDL + if (SDL_Init(SDL_INIT_JOYSTICK) < 0) + { + QLOG_ERROR() << "SDL failed to initialize : " << SDL_GetError(); + return false; + } + + SDL_JoystickEventState(SDL_ENABLE); + + refreshJoystickList(); + + return true; +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +void InputSDLWorker::close() +{ + if (SDL_WasInit(SDL_INIT_JOYSTICK)) + { + QLOG_INFO() << "SDL is closing."; + + // we need to close all the openned joysticks here and then exit the thread + for (int joyid = 0; joyid < m_joysticks.size(); joyid++) + { + SDL_JoystickClose(m_joysticks[joyid]); + } + + SDL_Quit(); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +QString InputSDLWorker::nameForId(SDL_JoystickID id) +{ + if (m_joysticks.contains(id)) + return SDL_JoystickName(m_joysticks[id]); + + return "unknown joystick"; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void InputSDLWorker::run() +{ + QElapsedTimer polltimer; + + while (true) + { + SDL_Event event; + + // startpoint for polling loop + polltimer.restart(); + + while (SDL_PollEvent(&event)) + { + switch (event.type) + { + case SDL_QUIT: + return; + break; + + case SDL_JOYBUTTONDOWN: + { + QLOG_DEBUG() << "SDL Got button down for button #" << event.jbutton.button + << " on Joystick #" << event.jbutton.which; + QElapsedTimer* timer = new QElapsedTimer(); + m_buttonTimestamps[event.jbutton.button] = timer; + timer->start(); + break; + } + + case SDL_JOYBUTTONUP: + { + if (m_buttonTimestamps[event.jbutton.button]) + { + emit receivedInput(nameForId(event.jbutton.which), QString("KEY_BUTTON_%1").arg(event.jbutton.button)); + delete m_buttonTimestamps[event.jbutton.button]; + m_buttonTimestamps.remove(event.jbutton.button); + } + + break; + } + + case SDL_JOYDEVICEADDED: + { + QLOG_INFO() << "SDL detected device was added."; + refreshJoystickList(); + break; + } + + case SDL_JOYDEVICEREMOVED: + { + QLOG_INFO() << "SDL detected device was removed."; + refreshJoystickList(); + break; + } + + case SDL_JOYAXISMOTION: + { + int deadband = 32768 *0.05; + + // handle the analog value push + // keep a dead band of 5% + if (std::abs(event.jaxis.value) > deadband) + { + // normalize the value + float normalizedvalue; + QString keyname; + if (event.jaxis.value > 0) + { + normalizedvalue = (float)(event.jaxis.value - deadband) / (float)(32767 - deadband); + keyname = QString("KEY_AXIS_%1_VAL_UP").arg(event.jaxis.axis); + } + else + { + normalizedvalue = (float)(std::abs(event.jaxis.value) - deadband) / (float)(32768 - deadband); + keyname = QString("KEY_AXIS_%1_VAL_DOWN").arg(event.jaxis.axis); + } + + emit receivedInput(nameForId(event.jaxis.which), keyname, normalizedvalue); + } + + // handle the Digital conversion of the analog axis + if (std::abs(event.jaxis.value) > 32768 / 2) + { + if (!m_axisState[event.jaxis.axis]) + { + m_axisState[event.jaxis.axis] = 1; + + if (event.jaxis.value > 0) + emit receivedInput(nameForId(event.jaxis.which), QString("KEY_AXIS_%1_UP").arg(event.jaxis.axis)); + else + emit receivedInput(nameForId(event.jaxis.which), QString("KEY_AXIS_%1_DOWN").arg(event.jaxis.axis)); + + break; + } + } + + m_axisState[event.jaxis.axis] = 0; + break; + } + } + } + + // check for longpresses + SDLTimeStampMapIterator it = m_buttonTimestamps.constBegin(); + while (it != m_buttonTimestamps.constEnd()) + { + if (it.value()) + { + if (m_buttonTimestamps[it.key()]->elapsed() > SDL_BUTTON_LONGPRESS_DELAY) + { + QLOG_DEBUG() << "SDL Got button longpress for button #" << event.jbutton.button + << " on Joystick #" << event.jbutton.which; + emit receivedInput(nameForId(event.jbutton.which), QString("KEY_BUTTON_%1_LONG").arg(it.key())); + delete it.value(); + m_buttonTimestamps.remove(it.key()); + it = m_buttonTimestamps.constBegin(); + continue; + } + } + else + { + m_buttonTimestamps.remove(it.key()); + it = m_buttonTimestamps.constBegin(); + continue; + } + + it++; + } + + // make the poll time fixed to SDL_POLL_TIME + if (polltimer.elapsed() < SDL_POLL_TIME) + QThread::msleep(SDL_POLL_TIME - polltimer.elapsed()); + } +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +void InputSDLWorker::refreshJoystickList() +{ + // close all openned joysticks + SDLJoystickMapIterator it = m_joysticks.constBegin(); + while (it != m_joysticks.constEnd()) + { + if (SDL_JoystickGetAttached(m_joysticks[it.key()])) + SDL_JoystickClose(m_joysticks[it.key()]); + it++; + } + + m_joysticks.clear(); + m_buttonTimestamps.clear(); + + // list all the joysticks and open them + int numJoysticks = SDL_NumJoysticks(); + QLOG_INFO() << "SDL found " << numJoysticks << " joysticks"; + + for (int joyid = 0; joyid < numJoysticks; joyid++) + { + SDL_Joystick* joystick = SDL_JoystickOpen(joyid); + + if (joystick) + { + int instanceid = SDL_JoystickInstanceID(joystick); + QLOG_INFO() << "JoyStick #" << instanceid << " is " << SDL_JoystickName(joystick) << " with " + << SDL_JoystickNumButtons(joystick) << " buttons and " << SDL_JoystickNumAxes(joystick) + << "axes"; + m_joysticks[instanceid] = joystick; + m_axisState.clear(); + m_axisState.resize(SDL_JoystickNumAxes(joystick)); + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +InputSDL::InputSDL(QObject* parent) : InputBase(parent) +{ + m_thread = new QThread(this); + m_sdlworker = new InputSDLWorker(NULL); + m_sdlworker->moveToThread(m_thread); + + connect(this, &InputSDL::run, m_sdlworker, &InputSDLWorker::run); + connect(m_sdlworker, &InputSDLWorker::receivedInput, this, &InputBase::receivedInput); + m_thread->start(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +InputSDL::~InputSDL() +{ + close(); + + if (m_thread->isRunning()) + { + m_thread->terminate(); + m_thread->wait(); + } +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +bool InputSDL::initInput() +{ + bool retValue; + QMetaObject::invokeMethod(m_sdlworker, "initialize", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(bool, retValue)); + + if (retValue) + emit run(); + + return retValue; +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +void InputSDL::close() +{ + QMetaObject::invokeMethod(m_sdlworker, "close"); +} diff --git a/src/input/InputSDL.h b/src/input/InputSDL.h new file mode 100644 index 0000000..9e5eae2 --- /dev/null +++ b/src/input/InputSDL.h @@ -0,0 +1,73 @@ +// +// InputSDL.h +// konvergo +// +// Created by Lionel CHAZALLON on 16/10/2014. +// +// + +#ifndef _INPUT_SDL_ +#define _INPUT_SDL_ + +#include +#include +#include +#include + +#include "input/InputComponent.h" + +typedef QMap SDLJoystickMap; +typedef SDLJoystickMap::const_iterator SDLJoystickMapIterator; + +typedef QMap SDLTimeStampMap; +typedef QMap::const_iterator SDLTimeStampMapIterator; + +#define SDL_POLL_TIME 50 +#define SDL_BUTTON_LONGPRESS_DELAY 500 + +/////////////////////////////////////////////////////////////////////////////////////////////////// +class InputSDLWorker : public QObject +{ + Q_OBJECT + +public: + InputSDLWorker(QObject* parent) : QObject(parent) {} + +public slots: + void run(); + bool initialize(); + void close(); + +signals: + void receivedInput(const QString& source, const QString& keycode, float amount = 1.0); + +private: + void refreshJoystickList(); + QString nameForId(SDL_JoystickID id); + + SDLTimeStampMap m_buttonTimestamps; + SDLJoystickMap m_joysticks; + QByteArray m_axisState; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +class InputSDL : public InputBase +{ + Q_OBJECT +public: + InputSDL(QObject* parent); + ~InputSDL(); + + virtual const char* inputName() { return "SDL"; } + virtual bool initInput(); + + void close(); +private: + InputSDLWorker* m_sdlworker; + QThread* m_thread; + +signals: + void run(); +}; + +#endif /* _INPUT_SDL_ */ diff --git a/src/input/InputSocket.cpp b/src/input/InputSocket.cpp new file mode 100644 index 0000000..073ed51 --- /dev/null +++ b/src/input/InputSocket.cpp @@ -0,0 +1,42 @@ +// +// Created by Tobias Hieta on 24/08/15. +// + +#include "InputSocket.h" +#include "QsLog.h" +#include "Version.h" + +#include + +///////////////////////////////////////////////////////////////////////////////////////// +bool InputSocket::initInput() +{ + return m_server->listen(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void InputSocket::clientConnected(QLocalSocket* socket) +{ + QVariantMap welcome; + welcome.insert("version", Version::GetVersionString()); + welcome.insert("builddate", Version::GetBuildDate()); + + m_server->sendMessage(welcome, socket); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void InputSocket::messageReceived(const QVariant& message) +{ + QVariantMap map = message.toMap(); + + if (!map.contains("client") || !map.contains("source") || !map.contains("keycode")) + { + QLOG_WARN() << "Got packet from client but it was missing the important fields"; + return; + } + + QLOG_DEBUG() << "Input from client:" << map.value("client").toString() << " - " << + map.value("source").toString() << map.value("keycode").toString(); + + emit receivedInput(map.value("source").toString(), map.value("keycode").toString(), 1); +} diff --git a/src/input/InputSocket.h b/src/input/InputSocket.h new file mode 100644 index 0000000..f05ebd9 --- /dev/null +++ b/src/input/InputSocket.h @@ -0,0 +1,33 @@ +// +// Created by Tobias Hieta on 24/08/15. +// + +#ifndef KONVERGO_INPUTSOCKET_H +#define KONVERGO_INPUTSOCKET_H + +#include "LocalJsonServer.h" +#include "InputComponent.h" + +class InputSocket : public InputBase +{ + Q_OBJECT +public: + explicit InputSocket(QObject* parent = 0) : InputBase(parent) + { + m_server = new LocalJsonServer("inputSocket"); + connect(m_server, &LocalJsonServer::clientConnected, this, &InputSocket::clientConnected); + connect(m_server, &LocalJsonServer::messageReceived, this, &InputSocket::messageReceived); + } + + virtual bool initInput() override; + virtual const char* inputName() override { return "socket"; }; + +private Q_SLOTS: + void clientConnected(QLocalSocket* socket); + void messageReceived(const QVariant& message); + +private: + LocalJsonServer* m_server; +}; + +#endif //KONVERGO_INPUTSOCKET_H diff --git a/src/input/apple/AppleRemoteDelegate.h b/src/input/apple/AppleRemoteDelegate.h new file mode 100644 index 0000000..71c5699 --- /dev/null +++ b/src/input/apple/AppleRemoteDelegate.h @@ -0,0 +1,29 @@ +#ifndef __APPLE_REMOTE_DELEGATE_H__ +#define __APPLE_REMOTE_DELEGATE_H__ + +#import +#import "HIDRemote/HIDRemote.h" + +class InputAppleRemote; + +@interface AppleRemoteDelegate : NSObject +{ + InputAppleRemote* m_remoteHandler; +} + +- (instancetype)initWithRemoteHandler:(InputAppleRemote*)remoteHandler; + +- (void)hidRemote:(HIDRemote *)hidRemote + eventWithButton:(HIDRemoteButtonCode)buttonCode + isPressed:(BOOL)isPressed +fromHardwareWithAttributes:(NSMutableDictionary *)attributes; + +- (void)hidRemote:(HIDRemote *)hidRemote failedNewHardwareWithError:(NSError *)error; +- (void)hidRemote:(HIDRemote *)hidRemote foundNewHardwareWithAttributes:(NSMutableDictionary *)attributes; +- (void)hidRemote:(HIDRemote *)hidRemote releasedHardwareWithAttributes:(NSMutableDictionary *)attributes; + +- (bool)setupRemote; + +@end + +#endif \ No newline at end of file diff --git a/src/input/apple/AppleRemoteDelegate.mm b/src/input/apple/AppleRemoteDelegate.mm new file mode 100644 index 0000000..9afbcea --- /dev/null +++ b/src/input/apple/AppleRemoteDelegate.mm @@ -0,0 +1,68 @@ +#import "AppleRemoteDelegate.h" +#import "InputAppleRemote.h" + +#include "QsLog.h" + +@implementation AppleRemoteDelegate + +/////////////////////////////////////////////////////////////////////////////////////////////////// +- (instancetype)initWithRemoteHandler: (InputAppleRemote*)remoteHandler +{ + self = [super init]; + if (self) { + m_remoteHandler = remoteHandler; + } + return self; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +- (bool)setupRemote +{ + [[HIDRemote sharedHIDRemote] setDelegate:self]; + [[HIDRemote sharedHIDRemote] setSimulateHoldEvents:false]; + if (![[HIDRemote sharedHIDRemote] startRemoteControl:kHIDRemoteModeExclusiveAuto]) + { + QLOG_ERROR() << "Failed to init AppleRemote"; + return false; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +- (QString)remoteNameFromAttributes:(NSMutableDictionary*)attributes +{ + NSString* product = [attributes objectForKey:kHIDRemoteProduct]; + NSString* manufacturer = [attributes objectForKey:kHIDRemoteManufacturer]; + + return QString("%1-%2").arg([manufacturer UTF8String]).arg([product UTF8String]); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +- (void)hidRemote:(HIDRemote *)hidRemote + eventWithButton:(HIDRemoteButtonCode)buttonCode + isPressed:(BOOL)isPressed +fromHardwareWithAttributes:(NSMutableDictionary *)attributes +{ + m_remoteHandler->remoteButtonEvent(buttonCode, isPressed, [self remoteNameFromAttributes:attributes]); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +- (void)hidRemote:(HIDRemote *)hidRemote foundNewHardwareWithAttributes:(NSMutableDictionary *)attributes +{ + m_remoteHandler->addRemote([self remoteNameFromAttributes:attributes]); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +- (void) hidRemote:(HIDRemote *)hidRemote releasedHardwareWithAttributes:(NSMutableDictionary *)attributes +{ + m_remoteHandler->removeRemote([self remoteNameFromAttributes:attributes]); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +- (void) hidRemote:(HIDRemote *)hidRemote failedNewHardwareWithError:(NSError *)error +{ + m_remoteHandler->addRemoteFailed(QString([[error localizedDescription] UTF8String])); +} + +@end diff --git a/src/input/apple/CMakeLists.txt b/src/input/apple/CMakeLists.txt new file mode 100644 index 0000000..778409b --- /dev/null +++ b/src/input/apple/CMakeLists.txt @@ -0,0 +1,7 @@ +set(APPLE_SRC + HIDRemote/HIDRemote.h HIDRemote/HIDRemote.m + InputAppleRemote.h InputAppleRemote.mm + AppleRemoteDelegate.mm AppleRemoteDelegate.h + ${HID_SRC} InputAppleMediaKeys.h InputAppleMediaKeys.mm +) +add_sources(${APPLE_SRC}) \ No newline at end of file diff --git a/src/input/apple/HIDRemote/HIDRemote.h b/src/input/apple/HIDRemote/HIDRemote.h new file mode 100644 index 0000000..0ccf39f --- /dev/null +++ b/src/input/apple/HIDRemote/HIDRemote.h @@ -0,0 +1,378 @@ +// +// HIDRemote.h +// HIDRemote V1.2 +// +// Created by Felix Schwarz on 06.04.07. +// Copyright 2007-2011 IOSPIRIT GmbH. All rights reserved. +// +// The latest version of this class is available at +// http://www.iospirit.com/developers/hidremote/ +// +// ** LICENSE ************************************************************************* +// +// Copyright (c) 2007-2011 IOSPIRIT GmbH (http://www.iospirit.com/) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this list +// of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// * Neither the name of IOSPIRIT GmbH nor the names of its contributors may be used to +// endorse or promote products derived from this software without specific prior +// written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +// SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +// DAMAGE. +// +// ************************************************************************************ + + +// ************************************************************************************ +// ********************************** DOCUMENTATION *********************************** +// ************************************************************************************ +// +// - a reference is available at http://www.iospirit.com/developers/hidremote/reference/ +// - for a guide, please see http://www.iospirit.com/developers/hidremote/guide/ +// +// ************************************************************************************ + + +#import + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma mark -- Enums / Codes -- + +typedef enum +{ + kHIDRemoteModeNone = 0L, + kHIDRemoteModeShared, // Share the remote with others - let's you listen to the remote control events as long as noone has an exclusive lock on it + // (RECOMMENDED ONLY FOR SPECIAL PURPOSES) + + kHIDRemoteModeExclusive, // Try to acquire an exclusive lock on the remote (NOT RECOMMENDED) + + kHIDRemoteModeExclusiveAuto // Try to acquire an exclusive lock on the remote whenever the application has focus. Temporarily release control over the + // remote when another application has focus (RECOMMENDED) +} HIDRemoteMode; + +typedef enum +{ + /* A code reserved for "no button" (needed for tracking) */ + kHIDRemoteButtonCodeNone = 0L, + + /* Standard codes - available for white plastic and aluminum remote */ + kHIDRemoteButtonCodeUp, + kHIDRemoteButtonCodeDown, + kHIDRemoteButtonCodeLeft, + kHIDRemoteButtonCodeRight, + kHIDRemoteButtonCodeCenter, + kHIDRemoteButtonCodeMenu, + + /* Extra codes - Only available for the new aluminum version of the remote */ + kHIDRemoteButtonCodePlay, + + /* Masks */ + kHIDRemoteButtonCodeCodeMask = 0xFFL, + kHIDRemoteButtonCodeHoldMask = (1L << 16L), + kHIDRemoteButtonCodeSpecialMask = (1L << 17L), + kHIDRemoteButtonCodeAluminumMask = (1L << 21L), // PRIVATE - only used internally + + /* Hold button standard codes - available for white plastic and aluminum remote */ + kHIDRemoteButtonCodeUpHold = (kHIDRemoteButtonCodeHoldMask|kHIDRemoteButtonCodeUp), + kHIDRemoteButtonCodeDownHold = (kHIDRemoteButtonCodeHoldMask|kHIDRemoteButtonCodeDown), + kHIDRemoteButtonCodeLeftHold = (kHIDRemoteButtonCodeHoldMask|kHIDRemoteButtonCodeLeft), + kHIDRemoteButtonCodeRightHold = (kHIDRemoteButtonCodeHoldMask|kHIDRemoteButtonCodeRight), + kHIDRemoteButtonCodeCenterHold = (kHIDRemoteButtonCodeHoldMask|kHIDRemoteButtonCodeCenter), + kHIDRemoteButtonCodeMenuHold = (kHIDRemoteButtonCodeHoldMask|kHIDRemoteButtonCodeMenu), + + /* Hold button extra codes - Only available for aluminum version of the remote */ + kHIDRemoteButtonCodePlayHold = (kHIDRemoteButtonCodeHoldMask|kHIDRemoteButtonCodePlay), + + /* DEPRECATED codes - compatibility with HIDRemote 1.0 */ + kHIDRemoteButtonCodePlus = kHIDRemoteButtonCodeUp, + kHIDRemoteButtonCodePlusHold = kHIDRemoteButtonCodeUpHold, + kHIDRemoteButtonCodeMinus = kHIDRemoteButtonCodeDown, + kHIDRemoteButtonCodeMinusHold = kHIDRemoteButtonCodeDownHold, + kHIDRemoteButtonCodePlayPause = kHIDRemoteButtonCodeCenter, + kHIDRemoteButtonCodePlayPauseHold = kHIDRemoteButtonCodeCenterHold, + + /* Special purpose codes */ + kHIDRemoteButtonCodeIDChanged = (kHIDRemoteButtonCodeSpecialMask|(1L << 18L)), // (the ID of the connected remote has changed, you can safely ignore this) +#ifdef _HIDREMOTE_EXTENSIONS +#define _HIDREMOTE_EXTENSIONS_SECTION 1 +#include "HIDRemoteAdditions.h" +#undef _HIDREMOTE_EXTENSIONS_SECTION +#endif /* _HIDREMOTE_EXTENSIONS */ +} HIDRemoteButtonCode; + +typedef enum +{ + kHIDRemoteModelUndetermined = 0L, // Assume a white plastic remote + kHIDRemoteModelWhitePlastic, // Signal *likely* to be coming from a white plastic remote + kHIDRemoteModelAluminum // Signal *definitely* coming from an aluminum remote +} HIDRemoteModel; + +typedef enum +{ + kHIDRemoteAluminumRemoteSupportLevelNone = 0L, // This system has no support for the Aluminum Remote at all + kHIDRemoteAluminumRemoteSupportLevelEmulation, // This system possibly has support for the Aluminum Remote (via emulation) + kHIDRemoteAluminumRemoteSupportLevelNative // This system has native support for the Aluminum Remote +} HIDRemoteAluminumRemoteSupportLevel; + +@class HIDRemote; + +#pragma mark -- Delegate protocol (mandatory) -- +@protocol HIDRemoteDelegate + +// Notification of button events +- (void)hidRemote:(HIDRemote *)hidRemote // The instance of HIDRemote sending this + eventWithButton:(HIDRemoteButtonCode)buttonCode // Event for the button specified by code + isPressed:(BOOL)isPressed // The button was pressed (YES) / released (NO) +fromHardwareWithAttributes:(NSMutableDictionary *)attributes; // Information on the device this event comes from + +@optional + +// Notification of ID changes +- (void)hidRemote:(HIDRemote *)hidRemote // Invoked when the user switched to a remote control with a different ID +remoteIDChangedOldID:(SInt32)old + newID:(SInt32)newID +forHardwareWithAttributes:(NSMutableDictionary *)attributes; + +// Notification about hardware additions/removals +- (void)hidRemote:(HIDRemote *)hidRemote // Invoked when new hardware was found / added to HIDRemote's pool +foundNewHardwareWithAttributes:(NSMutableDictionary *)attributes; + +- (void)hidRemote:(HIDRemote *)hidRemote // Invoked when initialization of new hardware as requested failed +failedNewHardwareWithError:(NSError *)error; + +- (void)hidRemote:(HIDRemote *)hidRemote // Invoked when hardware was removed from HIDRemote's pool +releasedHardwareWithAttributes:(NSMutableDictionary *)attributes; + +// ### WARNING: Unless you know VERY PRECISELY what you are doing, do not implement any of the delegate methods below. ### + +// Matching of newly found receiver hardware +- (BOOL)hidRemote:(HIDRemote *)hidRemote // Invoked when new hardware is inspected +inspectNewHardwareWithService:(io_service_t)service // + prematchResult:(BOOL)prematchResult; // Return YES if HIDRemote should go on with this hardware and try + // to use it, or NO if it should not be persued further. + +// Exlusive lock lending +- (BOOL)hidRemote:(HIDRemote *)hidRemote +lendExclusiveLockToApplicationWithInfo:(NSDictionary *)applicationInfo; + +- (void)hidRemote:(HIDRemote *)hidRemote +exclusiveLockReleasedByApplicationWithInfo:(NSDictionary *)applicationInfo; + +- (BOOL)hidRemote:(HIDRemote *)hidRemote +shouldRetryExclusiveLockWithInfo:(NSDictionary *)applicationInfo; + +@end + + +#pragma mark -- Actual header file for class -- + +@interface HIDRemote : NSObject +{ + // IOMasterPort + mach_port_t _masterPort; + + // Notification ports + IONotificationPortRef _notifyPort; + CFRunLoopSourceRef _notifyRLSource; + + // Matching iterator + io_iterator_t _matchingServicesIterator; + + // SecureInput notification + io_object_t _secureInputNotification; + + // Service attributes + NSMutableDictionary *_serviceAttribMap; + + // Mode + HIDRemoteMode _mode; + BOOL _autoRecover; + NSTimer *_autoRecoveryTimer; + + // Delegate + NSObject *_delegate; + + // Last seen ID and remote model + SInt32 _lastSeenRemoteID; + HIDRemoteModel _lastSeenModel; + SInt32 _lastSeenModelRemoteID; + + // Unused button codes + NSArray *_unusedButtonCodes; + + // Simulate Plus/Minus Hold + BOOL _simulateHoldEvents; + + // SecureEventInput workaround + BOOL _secureEventInputWorkAround; + UInt64 _lastSecureEventInputPIDSum; + uid_t _lastFrontUserSession; + + // Exclusive lock lending + BOOL _exclusiveLockLending; + BOOL _sendExclusiveResourceReuseNotification; + NSNumber *_waitForReturnByPID; + NSNumber *_returnToPID; + BOOL _isRestarting; + + // Status notifications + BOOL _sendStatusNotifications; + NSString *_pidString; + + // Status + BOOL _applicationIsTerminating; + BOOL _isStopping; + + // Thread safety +#ifdef HIDREMOTE_THREADSAFETY_HARDENED_NOTIFICATION_HANDLING /* #define HIDREMOTE_THREADSAFETY_HARDENED_NOTIFICATION_HANDLING if you're running your HIDRemote instance on a background thread (requires OS X 10.5 or later) */ + NSThread *_runOnThread; +#endif +} + +#pragma mark -- PUBLIC: Shared HID Remote -- ++ (HIDRemote *)sharedHIDRemote; + +#pragma mark -- PUBLIC: System Information -- ++ (BOOL)isCandelairInstalled; ++ (BOOL)isCandelairInstallationRequiredForRemoteMode:(HIDRemoteMode)remoteMode; +- (HIDRemoteAluminumRemoteSupportLevel)aluminiumRemoteSystemSupportLevel; + +#pragma mark -- PUBLIC: Interface / API -- +- (BOOL)startRemoteControl:(HIDRemoteMode)hidRemoteMode; +- (void)stopRemoteControl; + +- (BOOL)isStarted; +- (HIDRemoteMode)startedInMode; + +- (unsigned)activeRemoteControlCount; + +- (SInt32)lastSeenRemoteControlID; + +- (void)setLastSeenModel:(HIDRemoteModel)aModel; +- (HIDRemoteModel)lastSeenModel; + +- (void)setDelegate:(NSObject *)newDelegate; +- (NSObject *)delegate; + +- (void)setSimulateHoldEvents:(BOOL)newSimulateHoldEvents; +- (BOOL)simulateHoldEvents; + +- (void)setUnusedButtonCodes:(NSArray *)newArrayWithUnusedButtonCodesAsNSNumbers; +- (NSArray *)unusedButtonCodes; + +#pragma mark -- PUBLIC: Expert APIs -- +- (void)setEnableSecureEventInputWorkaround:(BOOL)newEnableSecureEventInputWorkaround; +- (BOOL)enableSecureEventInputWorkaround; + +- (void)setExclusiveLockLendingEnabled:(BOOL)newExclusiveLockLendingEnabled; +- (BOOL)exclusiveLockLendingEnabled; + +- (BOOL)isApplicationTerminating; +- (BOOL)isStopping; + +#pragma mark -- PRIVATE: HID Event handling -- +- (void)_handleButtonCode:(HIDRemoteButtonCode)buttonCode isPressed:(BOOL)isPressed hidAttribsDict:(NSMutableDictionary *)hidAttribsDict; +- (void)_sendButtonCode:(HIDRemoteButtonCode)buttonCode isPressed:(BOOL)isPressed hidAttribsDict:(NSMutableDictionary *)hidAttribsDict; +- (void)_hidEventFor:(io_service_t)hidDevice from:(IOHIDQueueInterface **)interface withResult:(IOReturn)result; + +#pragma mark -- PRIVATE: Service setup and destruction -- +- (BOOL)_prematchService:(io_object_t)service; +- (HIDRemoteButtonCode)buttonCodeForUsage:(unsigned int)usage usagePage:(unsigned int)usagePage; +- (BOOL)_setupService:(io_object_t)service; +- (void)_destructService:(io_object_t)service; + +#pragma mark -- PRIVATE: Distributed notifiations handling -- +- (void)_postStatusWithAction:(NSString *)action; +- (void)_handleNotifications:(NSNotification *)notification; +- (void)_setSendStatusNotifications:(BOOL)doSend; +- (BOOL)_sendStatusNotifications; + +#pragma mark -- PRIVATE: Application becomes active / inactive handling for kHIDRemoteModeExclusiveAuto -- +- (void)_appStatusChanged:(NSNotification *)notification; +- (void)_delayedAutoRecovery:(NSTimer *)aTimer; + +#pragma mark -- PRIVATE: Notification handling -- +- (void)_serviceMatching:(io_iterator_t)iterator; +- (void)_serviceNotificationFor:(io_service_t)service messageType:(natural_t)messageType messageArgument:(void *)messageArgument; +- (void)_updateSessionInformation; +- (void)_secureInputNotificationFor:(io_service_t)service messageType:(natural_t)messageType messageArgument:(void *)messageArgument; + +@end + +#pragma mark -- Information attribute keys -- +extern NSString *kHIDRemoteManufacturer; +extern NSString *kHIDRemoteProduct; +extern NSString *kHIDRemoteTransport; + +#pragma mark -- Internal/Expert attribute keys (AKA: don't touch these unless you really, really, REALLY know what you do) -- +extern NSString *kHIDRemoteCFPluginInterface; +extern NSString *kHIDRemoteHIDDeviceInterface; +extern NSString *kHIDRemoteCookieButtonCodeLUT; +extern NSString *kHIDRemoteHIDQueueInterface; +extern NSString *kHIDRemoteServiceNotification; +extern NSString *kHIDRemoteCFRunLoopSource; +extern NSString *kHIDRemoteLastButtonPressed; +extern NSString *kHIDRemoteService; +extern NSString *kHIDRemoteSimulateHoldEventsTimer; +extern NSString *kHIDRemoteSimulateHoldEventsOriginButtonCode; +extern NSString *kHIDRemoteAluminumRemoteSupportLevel; +extern NSString *kHIDRemoteAluminumRemoteSupportOnDemand; + +#pragma mark -- Distributed notifications -- +extern NSString *kHIDRemoteDNHIDRemotePing; +extern NSString *kHIDRemoteDNHIDRemoteRetry; +extern NSString *kHIDRemoteDNHIDRemoteStatus; + +extern NSString *kHIDRemoteDNHIDRemoteRetryGlobalObject; + +#pragma mark -- Distributed notifications userInfo keys and values -- +extern NSString *kHIDRemoteDNStatusHIDRemoteVersionKey; +extern NSString *kHIDRemoteDNStatusPIDKey; +extern NSString *kHIDRemoteDNStatusModeKey; +extern NSString *kHIDRemoteDNStatusUnusedButtonCodesKey; +extern NSString *kHIDRemoteDNStatusRemoteControlCountKey; +extern NSString *kHIDRemoteDNStatusReturnToPIDKey; +extern NSString *kHIDRemoteDNStatusActionKey; +extern NSString *kHIDRemoteDNStatusActionStart; +extern NSString *kHIDRemoteDNStatusActionStop; +extern NSString *kHIDRemoteDNStatusActionUpdate; +extern NSString *kHIDRemoteDNStatusActionNoNeed; + +#pragma mark -- Driver compatibility flags -- +typedef enum +{ + kHIDRemoteCompatibilityFlagsStandardHIDRemoteDevice = 1L, +} HIDRemoteCompatibilityFlags; diff --git a/src/input/apple/HIDRemote/HIDRemote.m b/src/input/apple/HIDRemote/HIDRemote.m new file mode 100644 index 0000000..47bce32 --- /dev/null +++ b/src/input/apple/HIDRemote/HIDRemote.m @@ -0,0 +1,1953 @@ +// +// HIDRemote.m +// HIDRemote V1.2 (27th May 2011) +// +// Created by Felix Schwarz on 06.04.07. +// Copyright 2007-2011 IOSPIRIT GmbH. All rights reserved. +// +// The latest version of this class is available at +// http://www.iospirit.com/developers/hidremote/ +// +// ** LICENSE ************************************************************************* +// +// Copyright (c) 2007-2011 IOSPIRIT GmbH (http://www.iospirit.com/) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this list +// of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// * Neither the name of IOSPIRIT GmbH nor the names of its contributors may be used to +// endorse or promote products derived from this software without specific prior +// written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +// SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +// DAMAGE. +// +// ************************************************************************************ + +// ************************************************************************************ +// ********************************** DOCUMENTATION *********************************** +// ************************************************************************************ +// +// - a reference is available at http://www.iospirit.com/developers/hidremote/reference/ +// - for a guide, please see http://www.iospirit.com/developers/hidremote/guide/ +// +// ************************************************************************************ + +#import "HIDRemote.h" + +// Callback Prototypes +static void HIDEventCallback(void * target, + IOReturn result, + void * refcon, + void * sender); + +static void ServiceMatchingCallback(void *refCon, + io_iterator_t iterator); + +static void ServiceNotificationCallback(void * refCon, + io_service_t service, + natural_t messageType, + void * messageArgument); + +static void SecureInputNotificationCallback(void * refCon, + io_service_t service, + natural_t messageType, + void * messageArgument); + +// Shared HIDRemote instance +static HIDRemote *sHIDRemote = nil; + +@implementation HIDRemote + +#pragma mark -- Init, dealloc & shared instance -- + ++ (HIDRemote *)sharedHIDRemote +{ + if (sHIDRemote==nil) + { + sHIDRemote = [[HIDRemote alloc] init]; + } + + return (sHIDRemote); +} + +- (id)init +{ + if ((self = [super init]) != nil) + { +#ifdef HIDREMOTE_THREADSAFETY_HARDENED_NOTIFICATION_HANDLING + _runOnThread = [NSThread currentThread]; +#endif + + // Detect application becoming active/inactive + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_appStatusChanged:) name:NSApplicationDidBecomeActiveNotification object:[NSApplication sharedApplication]]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_appStatusChanged:) name:NSApplicationWillResignActiveNotification object:[NSApplication sharedApplication]]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_appStatusChanged:) name:NSApplicationWillTerminateNotification object:[NSApplication sharedApplication]]; + + // Handle distributed notifications + _pidString = [[NSString alloc] initWithFormat:@"%d", getpid()]; + + [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleNotifications:) name:kHIDRemoteDNHIDRemotePing object:nil]; + [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleNotifications:) name:kHIDRemoteDNHIDRemoteRetry object:kHIDRemoteDNHIDRemoteRetryGlobalObject]; + [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleNotifications:) name:kHIDRemoteDNHIDRemoteRetry object:_pidString]; + + // Enabled by default: simulate hold events for plus/minus + _simulateHoldEvents = YES; + + // Enabled by default: work around for a locking issue introduced with Security Update 2008-004 / 10.4.9 and beyond (credit for finding this workaround goes to Martin Kahr) + _secureEventInputWorkAround = YES; + _secureInputNotification = 0; + + // Initialize instance variables + _lastSeenRemoteID = -1; + _lastSeenModel = kHIDRemoteModelUndetermined; + _unusedButtonCodes = [[NSMutableArray alloc] init]; + _exclusiveLockLending = NO; + _sendExclusiveResourceReuseNotification = YES; + _applicationIsTerminating = NO; + + // Send status notifications + _sendStatusNotifications = YES; + } + + return (self); +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self name:NSApplicationWillTerminateNotification object:[NSApplication sharedApplication]]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:NSApplicationWillResignActiveNotification object:[NSApplication sharedApplication]]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:NSApplicationDidBecomeActiveNotification object:[NSApplication sharedApplication]]; + + [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:kHIDRemoteDNHIDRemotePing object:nil]; + [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:kHIDRemoteDNHIDRemoteRetry object:kHIDRemoteDNHIDRemoteRetryGlobalObject]; + [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:kHIDRemoteDNHIDRemoteRetry object:_pidString]; + [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:nil object:nil]; /* As demanded by the documentation for -[NSDistributedNotificationCenter removeObserver:name:object:] */ + + [self stopRemoteControl]; + + [self setExclusiveLockLendingEnabled:NO]; + + [self setDelegate:nil]; + + _unusedButtonCodes = nil; + +#ifdef HIDREMOTE_THREADSAFETY_HARDENED_NOTIFICATION_HANDLING + _runOnThread = nil; +#endif + + _pidString = nil; +} + +#pragma mark -- PUBLIC: System Information -- ++ (BOOL)isCandelairInstalled +{ + mach_port_t masterPort = 0; + kern_return_t kernResult; + io_service_t matchingService = 0; + BOOL isInstalled = NO; + + kernResult = IOMasterPort(MACH_PORT_NULL, &masterPort); + if ((kernResult!=kIOReturnSuccess) || (masterPort==0)) { return(NO); } + + if ((matchingService = IOServiceGetMatchingService(masterPort, IOServiceMatching("IOSPIRITIRController"))) != 0) + { + isInstalled = YES; + IOObjectRelease((io_object_t) matchingService); + } + + mach_port_deallocate(mach_task_self(), masterPort); + + return (isInstalled); +} + ++ (BOOL)isCandelairInstallationRequiredForRemoteMode:(HIDRemoteMode)remoteMode +{ + return (NO); +} + +- (HIDRemoteAluminumRemoteSupportLevel)aluminiumRemoteSystemSupportLevel +{ + HIDRemoteAluminumRemoteSupportLevel supportLevel = kHIDRemoteAluminumRemoteSupportLevelNone; + + for (NSDictionary *hidAttribsDict in [_serviceAttribMap objectEnumerator]) + { + NSNumber *deviceSupportLevel; + + if ((deviceSupportLevel = hidAttribsDict[kHIDRemoteAluminumRemoteSupportLevel]) != nil) + { + if ([deviceSupportLevel intValue] > (int)supportLevel) + { + supportLevel = [deviceSupportLevel intValue]; + } + } + } + + return (supportLevel); +} + +#pragma mark -- PUBLIC: Interface / API -- +- (BOOL)startRemoteControl:(HIDRemoteMode)hidRemoteMode +{ + if ((_mode == kHIDRemoteModeNone) && (hidRemoteMode != kHIDRemoteModeNone)) + { + kern_return_t kernReturn; + CFMutableDictionaryRef matchDict=NULL; + io_service_t rootService; + + do + { + // Get IOKit master port + kernReturn = IOMasterPort(bootstrap_port, &_masterPort); + if ((kernReturn!=kIOReturnSuccess) || (_masterPort==0)) { break; } + + // Setup notification port + _notifyPort = IONotificationPortCreate(_masterPort); + + if ((_notifyRLSource = IONotificationPortGetRunLoopSource(_notifyPort)) != NULL) + { + CFRunLoopAddSource( CFRunLoopGetCurrent(), + _notifyRLSource, + kCFRunLoopCommonModes); + } + else + { + break; + } + + // Setup SecureInput notification + if ((hidRemoteMode == kHIDRemoteModeExclusive) || (hidRemoteMode == kHIDRemoteModeExclusiveAuto)) + { + if ((rootService = IORegistryEntryFromPath(_masterPort, kIOServicePlane ":/")) != 0) + { + kernReturn = IOServiceAddInterestNotification( _notifyPort, + rootService, + kIOBusyInterest, + SecureInputNotificationCallback, + (__bridge void *)self, + &_secureInputNotification); + if (kernReturn != kIOReturnSuccess) { break; } + + [self _updateSessionInformation]; + } + else + { + break; + } + } + + // Setup notification matching dict + matchDict = IOServiceMatching(kIOHIDDeviceKey); + CFRetain(matchDict); + + // Actually add notification + kernReturn = IOServiceAddMatchingNotification( _notifyPort, + kIOFirstMatchNotification, + matchDict, // one reference count consumed by this call + ServiceMatchingCallback, + (__bridge void *) self, + &_matchingServicesIterator); + if (kernReturn != kIOReturnSuccess) { break; } + + // Setup serviceAttribMap + _serviceAttribMap = [[NSMutableDictionary alloc] init]; + if (_serviceAttribMap==nil) { break; } + + // Phew .. everything went well! + _mode = hidRemoteMode; + CFRelease(matchDict); + + [self _serviceMatching:_matchingServicesIterator]; + + [self _postStatusWithAction:kHIDRemoteDNStatusActionStart]; + + return (YES); + + }while(0); + + // An error occured. Do necessary clean up. + if (matchDict!=NULL) + { + CFRelease(matchDict); + matchDict = NULL; + } + + [self stopRemoteControl]; + } + + return (NO); +} + +- (void)stopRemoteControl +{ + UInt32 serviceCount = 0; + + _autoRecover = NO; + _isStopping = YES; + + if (_autoRecoveryTimer!=nil) + { + [_autoRecoveryTimer invalidate]; + _autoRecoveryTimer = nil; + } + + if (_serviceAttribMap!=nil) + { + NSDictionary *cloneDict = [[NSDictionary alloc] initWithDictionary:_serviceAttribMap]; + + for (NSNumber *serviceValue in [cloneDict keyEnumerator]) + { + [self _destructService:(io_object_t)[serviceValue unsignedIntValue]]; + serviceCount++; + }; + + _serviceAttribMap = nil; + } + + if (_matchingServicesIterator!=0) + { + IOObjectRelease((io_object_t) _matchingServicesIterator); + _matchingServicesIterator = 0; + } + + if (_secureInputNotification!=0) + { + IOObjectRelease((io_object_t) _secureInputNotification); + _secureInputNotification = 0; + } + + if (_notifyRLSource!=NULL) + { + CFRunLoopSourceInvalidate(_notifyRLSource); + _notifyRLSource = NULL; + } + + if (_notifyPort!=NULL) + { + IONotificationPortDestroy(_notifyPort); + _notifyPort = NULL; + } + + if (_masterPort!=0) + { + mach_port_deallocate(mach_task_self(), _masterPort); + _masterPort = 0; + } + + _returnToPID = nil; + + if (_mode!=kHIDRemoteModeNone) + { + // Post status + [self _postStatusWithAction:kHIDRemoteDNStatusActionStop]; + + if (_sendStatusNotifications) + { + // In case we were not ready to lend it earlier, tell other HIDRemote apps that the resources (if any were used) are now again available for use by other applications + if (((_mode==kHIDRemoteModeExclusive) || (_mode==kHIDRemoteModeExclusiveAuto)) && (_sendExclusiveResourceReuseNotification==YES) && (_exclusiveLockLending==NO) && (serviceCount>0)) + { + _mode = kHIDRemoteModeNone; + + if (!_isRestarting) + { + [[NSDistributedNotificationCenter defaultCenter] postNotificationName:kHIDRemoteDNHIDRemoteRetry + object:kHIDRemoteDNHIDRemoteRetryGlobalObject + userInfo:@{kHIDRemoteDNStatusPIDKey: @((unsigned int)getpid()), + (NSString *)kCFBundleIdentifierKey: [[NSBundle mainBundle] bundleIdentifier]} + deliverImmediately:YES]; + } + } + } + } + + _mode = kHIDRemoteModeNone; + _isStopping = NO; +} + +- (BOOL)isStarted +{ + return (_mode != kHIDRemoteModeNone); +} + +- (HIDRemoteMode)startedInMode +{ + return (_mode); +} + +- (unsigned)activeRemoteControlCount +{ + return (int)[_serviceAttribMap count]; +} + +- (SInt32)lastSeenRemoteControlID +{ + return (_lastSeenRemoteID); +} + +- (HIDRemoteModel)lastSeenModel +{ + return (_lastSeenModel); +} + +- (void)setLastSeenModel:(HIDRemoteModel)aModel +{ + _lastSeenModel = aModel; +} + +- (void)setSimulateHoldEvents:(BOOL)newSimulateHoldEvents +{ + _simulateHoldEvents = newSimulateHoldEvents; +} + +- (BOOL)simulateHoldEvents +{ + return (_simulateHoldEvents); +} + +- (NSArray *)unusedButtonCodes +{ + return (_unusedButtonCodes); +} + +- (void)setUnusedButtonCodes:(NSArray *)newArrayWithUnusedButtonCodesAsNSNumbers +{ + _unusedButtonCodes = newArrayWithUnusedButtonCodesAsNSNumbers; + + [self _postStatusWithAction:kHIDRemoteDNStatusActionUpdate]; +} + +- (void)setDelegate:(NSObject *)newDelegate +{ + _delegate = newDelegate; +} + +- (NSObject *)delegate +{ + return (_delegate); +} + +#pragma mark -- PUBLIC: Expert APIs -- +- (void)setEnableSecureEventInputWorkaround:(BOOL)newEnableSecureEventInputWorkaround +{ + _secureEventInputWorkAround = newEnableSecureEventInputWorkaround; +} + +- (BOOL)enableSecureEventInputWorkaround +{ + return (_secureEventInputWorkAround); +} + +- (void)setExclusiveLockLendingEnabled:(BOOL)newExclusiveLockLendingEnabled +{ + if (newExclusiveLockLendingEnabled != _exclusiveLockLending) + { + _exclusiveLockLending = newExclusiveLockLendingEnabled; + + if (_exclusiveLockLending) + { + [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(_handleNotifications:) name:kHIDRemoteDNHIDRemoteStatus object:nil]; + } + else + { + [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:kHIDRemoteDNHIDRemoteStatus object:nil]; + + _waitForReturnByPID = nil; + } + } +} + +- (BOOL)exclusiveLockLendingEnabled +{ + return (_exclusiveLockLending); +} + +- (void)setSendExclusiveResourceReuseNotification:(BOOL)newSendExclusiveResourceReuseNotification +{ + _sendExclusiveResourceReuseNotification = newSendExclusiveResourceReuseNotification; +} + +- (BOOL)sendExclusiveResourceReuseNotification +{ + return (_sendExclusiveResourceReuseNotification); +} + +- (BOOL)isApplicationTerminating +{ + return (_applicationIsTerminating); +} + +- (BOOL)isStopping +{ + return (_isStopping); +} + +#pragma mark -- PRIVATE: Application becomes active / inactive handling for kHIDRemoteModeExclusiveAuto -- +- (void)_appStatusChanged:(NSNotification *)notification +{ +#ifdef HIDREMOTE_THREADSAFETY_HARDENED_NOTIFICATION_HANDLING + if ([self respondsToSelector:@selector(performSelector:onThread:withObject:waitUntilDone:)]) // OS X 10.5+ only + { + if ([NSThread currentThread] != _runOnThread) + { + if ([[notification name] isEqual:NSApplicationDidBecomeActiveNotification]) + { + if (!_autoRecover) + { + return; + } + } + + if ([[notification name] isEqual:NSApplicationWillResignActiveNotification]) + { + if (_mode != kHIDRemoteModeExclusiveAuto) + { + return; + } + } + + [self performSelector:@selector(_appStatusChanged:) onThread:_runOnThread withObject:notification waitUntilDone:[[notification name] isEqual:NSApplicationWillTerminateNotification]]; + return; + } + } +#endif + + if (notification!=nil) + { + if (_autoRecoveryTimer!=nil) + { + [_autoRecoveryTimer invalidate]; + _autoRecoveryTimer = nil; + } + + if ([[notification name] isEqual:NSApplicationDidBecomeActiveNotification]) + { + if (_autoRecover) + { + // Delay autorecover by 0.1 to avoid race conditions + if ((_autoRecoveryTimer = [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:0.1] interval:0.1 target:self selector:@selector(_delayedAutoRecovery:) userInfo:nil repeats:NO]) != nil) + { + [[NSRunLoop currentRunLoop] addTimer:_autoRecoveryTimer forMode:NSRunLoopCommonModes]; + } + } + } + + if ([[notification name] isEqual:NSApplicationWillResignActiveNotification]) + { + if (_mode == kHIDRemoteModeExclusiveAuto) + { + [self stopRemoteControl]; + _autoRecover = YES; + } + } + + if ([[notification name] isEqual:NSApplicationWillTerminateNotification]) + { + _applicationIsTerminating = YES; + + if ([self isStarted]) + { + [self stopRemoteControl]; + } + } + } +} + +- (void)_delayedAutoRecovery:(NSTimer *)aTimer +{ + [_autoRecoveryTimer invalidate]; + _autoRecoveryTimer = nil; + + if (_autoRecover) + { + [self startRemoteControl:kHIDRemoteModeExclusiveAuto]; + _autoRecover = NO; + } +} + + +#pragma mark -- PRIVATE: Distributed notifiations handling -- +- (void)_postStatusWithAction:(NSString *)action +{ + if (_sendStatusNotifications) + { + NSDictionary* d = @{kHIDRemoteDNStatusHIDRemoteVersionKey: @1, + kHIDRemoteDNStatusPIDKey: @((unsigned int)getpid()), + kHIDRemoteDNStatusModeKey: @((int)_mode), + kHIDRemoteDNStatusRemoteControlCountKey: @((unsigned int)[self activeRemoteControlCount]), + kHIDRemoteDNStatusUnusedButtonCodesKey: ((_unusedButtonCodes!=nil) ? _unusedButtonCodes : @[]), + kHIDRemoteDNStatusActionKey: action, + (NSString *)kCFBundleIdentifierKey: [[NSBundle mainBundle] bundleIdentifier]}; + + NSMutableDictionary* md = [NSMutableDictionary dictionaryWithDictionary:d]; + + if (_returnToPID) { + md[kHIDRemoteDNStatusReturnToPIDKey] = _returnToPID; + } + + [[NSDistributedNotificationCenter defaultCenter] postNotificationName:kHIDRemoteDNHIDRemoteStatus + object:((_pidString!=nil) ? _pidString : [NSString stringWithFormat:@"%d",getpid()]) + userInfo:[md copy] + deliverImmediately:YES + ]; + } +} + +- (void)_handleNotifications:(NSNotification *)notification +{ + NSString *notificationName; + +#ifdef HIDREMOTE_THREADSAFETY_HARDENED_NOTIFICATION_HANDLING + if ([self respondsToSelector:@selector(performSelector:onThread:withObject:waitUntilDone:)]) // OS X 10.5+ only + { + if ([NSThread currentThread] != _runOnThread) + { + [self performSelector:@selector(_handleNotifications:) onThread:_runOnThread withObject:notification waitUntilDone:NO]; + return; + } + } +#endif + + if ((notification!=nil) && ((notificationName = [notification name]) != nil)) + { + if ([notificationName isEqual:kHIDRemoteDNHIDRemotePing]) + { + [self _postStatusWithAction:kHIDRemoteDNStatusActionUpdate]; + } + + if ([notificationName isEqual:kHIDRemoteDNHIDRemoteRetry]) + { + if ([self isStarted]) + { + BOOL retry = YES; + + // Ignore our own global retry broadcasts + if ([[notification object] isEqual:kHIDRemoteDNHIDRemoteRetryGlobalObject]) + { + NSNumber *fromPID; + + if ((fromPID = [notification userInfo][kHIDRemoteDNStatusPIDKey]) != nil) + { + if (getpid() == (int)[fromPID unsignedIntValue]) + { + retry = NO; + } + } + } + + if (retry) + { + if (([self delegate] != nil) && + ([[self delegate] respondsToSelector:@selector(hidRemote:shouldRetryExclusiveLockWithInfo:)])) + { + retry = [[self delegate] hidRemote:self shouldRetryExclusiveLockWithInfo:[notification userInfo]]; + } + } + + if (retry) + { + HIDRemoteMode restartInMode = _mode; + + if (restartInMode != kHIDRemoteModeNone) + { + _isRestarting = YES; + [self stopRemoteControl]; + + _returnToPID = nil; + + [self startRemoteControl:restartInMode]; + _isRestarting = NO; + + if (restartInMode != kHIDRemoteModeShared) + { + _returnToPID = [notification userInfo][kHIDRemoteDNStatusPIDKey]; + } + } + } + else + { + NSNumber *cacheReturnPID = _returnToPID; + + _returnToPID = [notification userInfo][kHIDRemoteDNStatusPIDKey]; + [self _postStatusWithAction:kHIDRemoteDNStatusActionNoNeed]; + + _returnToPID = cacheReturnPID; + } + } + } + + if (_exclusiveLockLending) + { + if ([notificationName isEqual:kHIDRemoteDNHIDRemoteStatus]) + { + NSString *action; + + if ((action = [notification userInfo][kHIDRemoteDNStatusActionKey]) != nil) + { + if ((_mode == kHIDRemoteModeNone) && (_waitForReturnByPID!=nil)) + { + NSNumber *pidNumber, *returnToPIDNumber; + + if ((pidNumber = [notification userInfo][kHIDRemoteDNStatusPIDKey]) != nil) + { + returnToPIDNumber = [notification userInfo][kHIDRemoteDNStatusReturnToPIDKey]; + if (returnToPIDNumber == (id)[NSNull null]) { + returnToPIDNumber = nil; + } + + if ([action isEqual:kHIDRemoteDNStatusActionStart]) + { + if ([pidNumber isEqual:_waitForReturnByPID]) + { + NSNumber *startMode; + + if ((startMode = [notification userInfo][kHIDRemoteDNStatusModeKey]) != nil) + { + if ([startMode intValue] == kHIDRemoteModeShared) + { + returnToPIDNumber = @(getpid()); + action = kHIDRemoteDNStatusActionNoNeed; + } + } + } + } + + if (returnToPIDNumber != nil) + { + if ([action isEqual:kHIDRemoteDNStatusActionStop] || [action isEqual:kHIDRemoteDNStatusActionNoNeed]) + { + if ([pidNumber isEqual:_waitForReturnByPID] && ([returnToPIDNumber intValue] == getpid())) + { + _waitForReturnByPID = nil; + + if (([self delegate] != nil) && + ([[self delegate] respondsToSelector:@selector(hidRemote:exclusiveLockReleasedByApplicationWithInfo:)])) + { + [[self delegate] hidRemote:self exclusiveLockReleasedByApplicationWithInfo:[notification userInfo]]; + } + else + { + [self startRemoteControl:kHIDRemoteModeExclusive]; + } + } + } + } + } + } + + if (_mode==kHIDRemoteModeExclusive) + { + if ([action isEqual:kHIDRemoteDNStatusActionStart]) + { + NSNumber *originPID = [notification userInfo][kHIDRemoteDNStatusPIDKey]; + BOOL lendLock = YES; + + if ([originPID intValue] != getpid()) + { + if (([self delegate] != nil) && + ([[self delegate] respondsToSelector:@selector(hidRemote:lendExclusiveLockToApplicationWithInfo:)])) + { + lendLock = [[self delegate] hidRemote:self lendExclusiveLockToApplicationWithInfo:[notification userInfo]]; + } + + if (lendLock) + { + _waitForReturnByPID = originPID; + + if (_waitForReturnByPID != nil) + { + [self stopRemoteControl]; + + [[NSDistributedNotificationCenter defaultCenter] postNotificationName:kHIDRemoteDNHIDRemoteRetry + object:[NSString stringWithFormat:@"%d", [_waitForReturnByPID intValue]] + userInfo:@{kHIDRemoteDNStatusPIDKey: @((unsigned int)getpid()), + (NSString *)kCFBundleIdentifierKey: [[NSBundle mainBundle] bundleIdentifier]} + deliverImmediately:YES]; + } + } + } + } + } + } + } + } + } +} + +- (void)_setSendStatusNotifications:(BOOL)doSend +{ + _sendStatusNotifications = doSend; +} + +- (BOOL)_sendStatusNotifications +{ + return (_sendStatusNotifications); +} + +#pragma mark -- PRIVATE: Service setup and destruction -- +- (BOOL)_prematchService:(io_object_t)service +{ + BOOL serviceMatches = NO; + NSString *ioClass; + NSNumber *candelairHIDRemoteCompatibilityMask; + + if (service != 0) + { + // IOClass matching + if ((ioClass = (__bridge_transfer NSString *)IORegistryEntryCreateCFProperty((io_registry_entry_t)service, + CFSTR(kIOClassKey), + kCFAllocatorDefault, + 0)) != nil) + { + // Match on apple's AppleIRController and old versions of the Remote Buddy IR Controller + if ([ioClass isEqual:@"AppleIRController"] || [ioClass isEqual:@"RBIOKitAIREmu"]) + { + CFTypeRef candelairHIDRemoteCompatibilityDevice; + + serviceMatches = YES; + + if ((candelairHIDRemoteCompatibilityDevice = IORegistryEntryCreateCFProperty((io_registry_entry_t)service, CFSTR("CandelairHIDRemoteCompatibilityDevice"), kCFAllocatorDefault, 0)) != NULL) + { + if (CFEqual(kCFBooleanTrue, candelairHIDRemoteCompatibilityDevice)) + { + serviceMatches = NO; + } + + CFRelease (candelairHIDRemoteCompatibilityDevice); + } + } + + // Match on the virtual IOSPIRIT IR Controller + if ([ioClass isEqual:@"IOSPIRITIRController"]) + { + serviceMatches = YES; + } + } + + // Match on services that claim compatibility with the HID Remote class (Candelair or third-party) by having a property of CandelairHIDRemoteCompatibilityMask = 1 + if ((candelairHIDRemoteCompatibilityMask = (__bridge_transfer NSNumber *)IORegistryEntryCreateCFProperty((io_registry_entry_t)service, CFSTR("CandelairHIDRemoteCompatibilityMask"), kCFAllocatorDefault, 0)) != nil) + { + if ([candelairHIDRemoteCompatibilityMask isKindOfClass:[NSNumber class]]) + { + if ([candelairHIDRemoteCompatibilityMask unsignedIntValue] & kHIDRemoteCompatibilityFlagsStandardHIDRemoteDevice) + { + serviceMatches = YES; + } + else + { + serviceMatches = NO; + } + } + } + } + + if (([self delegate]!=nil) && + ([[self delegate] respondsToSelector:@selector(hidRemote:inspectNewHardwareWithService:prematchResult:)])) + { + serviceMatches = [((NSObject *)[self delegate]) hidRemote:self inspectNewHardwareWithService:service prematchResult:serviceMatches]; + } + + return (serviceMatches); +} + +- (HIDRemoteButtonCode)buttonCodeForUsage:(unsigned int)usage usagePage:(unsigned int)usagePage +{ + HIDRemoteButtonCode buttonCode = kHIDRemoteButtonCodeNone; + + switch (usagePage) + { + case kHIDPage_Consumer: + switch (usage) + { + case kHIDUsage_Csmr_MenuPick: + // Aluminum Remote: Center + buttonCode = (kHIDRemoteButtonCodeCenter|kHIDRemoteButtonCodeAluminumMask); + break; + + case kHIDUsage_Csmr_ModeStep: + // Aluminium Remote: Center Hold + buttonCode = (kHIDRemoteButtonCodeCenterHold|kHIDRemoteButtonCodeAluminumMask); + break; + + case kHIDUsage_Csmr_PlayOrPause: + // Aluminum Remote: Play/Pause + buttonCode = (kHIDRemoteButtonCodePlay|kHIDRemoteButtonCodeAluminumMask); + break; + + case kHIDUsage_Csmr_Rewind: + buttonCode = kHIDRemoteButtonCodeLeftHold; + break; + + case kHIDUsage_Csmr_FastForward: + buttonCode = kHIDRemoteButtonCodeRightHold; + break; + + case kHIDUsage_Csmr_Menu: + buttonCode = kHIDRemoteButtonCodeMenuHold; + break; + } + break; + + case kHIDPage_GenericDesktop: + switch (usage) + { + case kHIDUsage_GD_SystemAppMenu: + buttonCode = kHIDRemoteButtonCodeMenu; + break; + + case kHIDUsage_GD_SystemMenu: + buttonCode = kHIDRemoteButtonCodeCenter; + break; + + case kHIDUsage_GD_SystemMenuRight: + buttonCode = kHIDRemoteButtonCodeRight; + break; + + case kHIDUsage_GD_SystemMenuLeft: + buttonCode = kHIDRemoteButtonCodeLeft; + break; + + case kHIDUsage_GD_SystemMenuUp: + buttonCode = kHIDRemoteButtonCodeUp; + break; + + case kHIDUsage_GD_SystemMenuDown: + buttonCode = kHIDRemoteButtonCodeDown; + break; + } + break; + + case 0x06: /* Reserved */ + switch (usage) + { + case 0x22: + buttonCode = kHIDRemoteButtonCodeIDChanged; + break; + } + break; + + case 0xFF01: /* Vendor specific */ + switch (usage) + { + case 0x23: + buttonCode = kHIDRemoteButtonCodeCenterHold; + break; + +#ifdef _HIDREMOTE_EXTENSIONS +#define _HIDREMOTE_EXTENSIONS_SECTION 2 +#include "HIDRemoteAdditions.h" +#undef _HIDREMOTE_EXTENSIONS_SECTION +#endif /* _HIDREMOTE_EXTENSIONS */ + } + break; + } + + return (buttonCode); +} + +- (BOOL)_setupService:(io_object_t)service +{ + kern_return_t kernResult; + IOReturn returnCode; + HRESULT hResult; + SInt32 score; + BOOL opened = NO, queueStarted = NO; + IOHIDDeviceInterface122 **hidDeviceInterface = NULL; + IOCFPlugInInterface **cfPluginInterface = NULL; + IOHIDQueueInterface **hidQueueInterface = NULL; + io_object_t serviceNotification = 0; + CFRunLoopSourceRef queueEventSource = NULL; + NSMutableDictionary *hidAttribsDict = nil; + CFArrayRef hidElements = NULL; + NSError *error = nil; + UInt32 errorCode = 0; + + if (![self _prematchService:service]) + { + return (NO); + } + + do + { + // Create a plugin interface .. + kernResult = IOCreatePlugInInterfaceForService( service, + kIOHIDDeviceUserClientTypeID, + kIOCFPlugInInterfaceID, + &cfPluginInterface, + &score); + + if (kernResult != kIOReturnSuccess) + { + error = [NSError errorWithDomain:NSMachErrorDomain code:kernResult userInfo:nil]; + errorCode = 1; + break; + } + + + // .. use it to get the HID interface .. + hResult = (*cfPluginInterface)->QueryInterface( cfPluginInterface, + CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID122), + (LPVOID)&hidDeviceInterface); + + if ((hResult!=S_OK) || (hidDeviceInterface==NULL)) + { + error = [NSError errorWithDomain:NSMachErrorDomain code:hResult userInfo:nil]; + errorCode = 2; + break; + } + + + // .. then open it .. + switch (_mode) + { + case kHIDRemoteModeShared: + hResult = (*hidDeviceInterface)->open(hidDeviceInterface, kIOHIDOptionsTypeNone); + break; + + case kHIDRemoteModeExclusive: + case kHIDRemoteModeExclusiveAuto: + hResult = (*hidDeviceInterface)->open(hidDeviceInterface, kIOHIDOptionsTypeSeizeDevice); + break; + + default: + goto cleanUp; // Ugh! But there are no "double breaks" available in C AFAIK .. + break; + } + + if (hResult!=S_OK) + { + error = [NSError errorWithDomain:NSMachErrorDomain code:hResult userInfo:nil]; + errorCode = 3; + break; + } + + opened = YES; + + // .. query the HID elements .. + returnCode = (*hidDeviceInterface)->copyMatchingElements(hidDeviceInterface, + NULL, + &hidElements); + if ((returnCode != kIOReturnSuccess) || (hidElements==NULL)) + { + error = [NSError errorWithDomain:NSMachErrorDomain code:returnCode userInfo:nil]; + errorCode = 4; + + break; + } + + // Setup an event queue for HID events! + hidQueueInterface = (*hidDeviceInterface)->allocQueue(hidDeviceInterface); + if (hidQueueInterface == NULL) + { + error = [NSError errorWithDomain:NSMachErrorDomain code:kIOReturnError userInfo:nil]; + errorCode = 5; + + break; + } + + returnCode = (*hidQueueInterface)->create(hidQueueInterface, 0, 32); + if (returnCode != kIOReturnSuccess) + { + error = [NSError errorWithDomain:NSMachErrorDomain code:returnCode userInfo:nil]; + errorCode = 6; + + break; + } + + + // Setup of attributes stored for this HID device + hidAttribsDict = [[NSMutableDictionary alloc] initWithObjectsAndKeys: + [NSValue valueWithPointer:(const void *)cfPluginInterface], kHIDRemoteCFPluginInterface, + [NSValue valueWithPointer:(const void *)hidDeviceInterface], kHIDRemoteHIDDeviceInterface, + [NSValue valueWithPointer:(const void *)hidQueueInterface], kHIDRemoteHIDQueueInterface, + nil]; + + { + UInt32 i, hidElementCnt = (unsigned)CFArrayGetCount(hidElements); + NSMutableDictionary *cookieButtonCodeLUT = [[NSMutableDictionary alloc] init]; + NSMutableDictionary *cookieCount = [[NSMutableDictionary alloc] init]; + + if ((cookieButtonCodeLUT==nil) || (cookieCount==nil)) + { + cookieButtonCodeLUT = nil; + cookieCount = nil; + + error = [NSError errorWithDomain:NSMachErrorDomain code:kIOReturnError userInfo:nil]; + errorCode = 7; + + break; + } + + // Analyze the HID elements and find matching elements + for (i=0;iaddElement(hidQueueInterface, + (IOHIDElementCookie) [cookie unsignedIntValue], + 0); + +#ifdef _HIDREMOTE_EXTENSIONS + // Get current apple Remote ID value +#define _HIDREMOTE_EXTENSIONS_SECTION 7 +#include "HIDRemoteAdditions.h" +#undef _HIDREMOTE_EXTENSIONS_SECTION +#endif /* _HIDREMOTE_EXTENSIONS */ + } + } + } + + // Compare number of *unique* matches (thus the cookieCount dictionary) with required minimum + if ([cookieCount count] < 10) + { + cookieButtonCodeLUT = nil; + cookieCount = nil; + + error = [NSError errorWithDomain:NSMachErrorDomain code:kIOReturnError userInfo:nil]; + errorCode = 8; + + break; + } + + hidAttribsDict[kHIDRemoteCookieButtonCodeLUT] = cookieButtonCodeLUT; + + cookieButtonCodeLUT = nil; + cookieCount = nil; + } + + // Finish setup of IOHIDQueueInterface with CFRunLoop + returnCode = (*hidQueueInterface)->createAsyncEventSource(hidQueueInterface, &queueEventSource); + if ((returnCode != kIOReturnSuccess) || (queueEventSource == NULL)) + { + error = [NSError errorWithDomain:NSMachErrorDomain code:returnCode userInfo:nil]; + errorCode = 9; + break; + } + + returnCode = (*hidQueueInterface)->setEventCallout(hidQueueInterface, HIDEventCallback, (void *)((intptr_t)service), (__bridge void *)self); + if (returnCode != kIOReturnSuccess) + { + error = [NSError errorWithDomain:NSMachErrorDomain code:returnCode userInfo:nil]; + errorCode = 10; + break; + } + + CFRunLoopAddSource( CFRunLoopGetCurrent(), + queueEventSource, + kCFRunLoopCommonModes); + hidAttribsDict[kHIDRemoteCFRunLoopSource] = [NSValue valueWithPointer:(const void *)queueEventSource]; + + returnCode = (*hidQueueInterface)->start(hidQueueInterface); + if (returnCode != kIOReturnSuccess) + { + error = [NSError errorWithDomain:NSMachErrorDomain code:returnCode userInfo:nil]; + errorCode = 11; + break; + } + + queueStarted = YES; + + // Setup device notifications + returnCode = IOServiceAddInterestNotification(_notifyPort, + service, + kIOGeneralInterest, + ServiceNotificationCallback, + (__bridge void *)self, + &serviceNotification); + if ((returnCode != kIOReturnSuccess) || (serviceNotification==0)) + { + error = [NSError errorWithDomain:NSMachErrorDomain code:returnCode userInfo:nil]; + errorCode = 12; + break; + } + + hidAttribsDict[kHIDRemoteServiceNotification] = @((unsigned int)serviceNotification); + + // Retain service + if (IOObjectRetain(service) != kIOReturnSuccess) + { + error = [NSError errorWithDomain:NSMachErrorDomain code:kIOReturnError userInfo:nil]; + errorCode = 13; + break; + } + + hidAttribsDict[kHIDRemoteService] = @((unsigned int)service); + + // Get some (somewhat optional) infos on the device + { + CFStringRef product, manufacturer, transport; + + if ((product = IORegistryEntryCreateCFProperty( (io_registry_entry_t)service, + (CFStringRef) @"Product", + kCFAllocatorDefault, + 0)) != NULL) + { + if (CFGetTypeID(product) == CFStringGetTypeID()) + { + hidAttribsDict[kHIDRemoteProduct] = (__bridge_transfer NSString *)product; + } + else + { + CFRelease(product); + } + } + + if ((manufacturer = IORegistryEntryCreateCFProperty( (io_registry_entry_t)service, + (CFStringRef) @"Manufacturer", + kCFAllocatorDefault, + 0)) != NULL) + { + if (CFGetTypeID(manufacturer) == CFStringGetTypeID()) + { + hidAttribsDict[kHIDRemoteManufacturer] = (__bridge_transfer NSString *)manufacturer; + } + else + { + CFRelease(manufacturer); + } + } + + if ((transport = IORegistryEntryCreateCFProperty( (io_registry_entry_t)service, + (CFStringRef) @"Transport", + kCFAllocatorDefault, + 0)) != NULL) + { + if (CFGetTypeID(transport) == CFStringGetTypeID()) + { + hidAttribsDict[kHIDRemoteTransport] = (__bridge_transfer NSString *)transport; + } + else + { + CFRelease(transport); + } + } + } + + // Determine Aluminum Remote support + { + CFNumberRef aluSupport; + HIDRemoteAluminumRemoteSupportLevel supportLevel = kHIDRemoteAluminumRemoteSupportLevelNone; + + if ((_mode == kHIDRemoteModeExclusive) || (_mode == kHIDRemoteModeExclusiveAuto)) + { + // Determine if this driver offers on-demand support for the Aluminum Remote (only relevant under OS versions < 10.6.2) + if ((aluSupport = IORegistryEntryCreateCFProperty((io_registry_entry_t)service, + (CFStringRef) @"AluminumRemoteSupportLevelOnDemand", + kCFAllocatorDefault, + 0)) != nil) + { + // There is => request the driver to enable it for us + if (IORegistryEntrySetCFProperty((io_registry_entry_t)service, + CFSTR("EnableAluminumRemoteSupportForMe"), + (__bridge void *)@{@"pid": @((long long)getpid()), + @"uid": @((long long)getuid())}) == kIOReturnSuccess) + { + if (CFGetTypeID(aluSupport) == CFNumberGetTypeID()) + { + supportLevel = (HIDRemoteAluminumRemoteSupportLevel) [(__bridge_transfer NSNumber *)aluSupport intValue]; + } + else + { + CFRelease(aluSupport); + } + + hidAttribsDict[kHIDRemoteAluminumRemoteSupportOnDemand] = @YES; + } + else + { + CFRelease(aluSupport); + } + } + } + + if (supportLevel == kHIDRemoteAluminumRemoteSupportLevelNone) + { + if ((aluSupport = IORegistryEntryCreateCFProperty((io_registry_entry_t)service, + (CFStringRef) @"AluminumRemoteSupportLevel", + kCFAllocatorDefault, + 0)) != nil) + { + if (CFGetTypeID(aluSupport) == CFNumberGetTypeID()) + { + supportLevel = (HIDRemoteAluminumRemoteSupportLevel) [(__bridge_transfer NSNumber *)aluSupport intValue]; + } + else + { + CFRelease(aluSupport); + } + } + else + { + CFStringRef ioKitClassName; + + if ((ioKitClassName = IORegistryEntryCreateCFProperty( (io_registry_entry_t)service, + CFSTR(kIOClassKey), + kCFAllocatorDefault, + 0)) != nil) + { + if ([(__bridge_transfer NSString *)ioKitClassName isEqual:@"AppleIRController"]) + { + supportLevel = kHIDRemoteAluminumRemoteSupportLevelNative; + } + } + } + } + + hidAttribsDict[kHIDRemoteAluminumRemoteSupportLevel] = (NSNumber *)@((int)supportLevel); + } + + // Add it to the serviceAttribMap + _serviceAttribMap[@((unsigned int)service)] = hidAttribsDict; + + // And we're done with setup .. + if (([self delegate]!=nil) && + ([[self delegate] respondsToSelector:@selector(hidRemote:foundNewHardwareWithAttributes:)])) + { + [((NSObject *)[self delegate]) hidRemote:self foundNewHardwareWithAttributes:hidAttribsDict]; + } + + hidAttribsDict = nil; + + return(YES); + + }while(0); + +cleanUp: + + if (([self delegate]!=nil) && + ([[self delegate] respondsToSelector:@selector(hidRemote:failedNewHardwareWithError:)])) + { + if (error!=nil) + { + error = [NSError errorWithDomain:[error domain] + code:[error code] + userInfo:@{@"InternalErrorCode": [NSNumber numberWithInt:errorCode]} + ]; + } + + [((NSObject *)[self delegate]) hidRemote:self failedNewHardwareWithError:error]; + } + + // An error occured or this device is not of interest .. cleanup .. + if (serviceNotification!=0) + { + IOObjectRelease(serviceNotification); + serviceNotification = 0; + } + + if (queueEventSource!=NULL) + { + CFRunLoopSourceInvalidate(queueEventSource); + queueEventSource=NULL; + } + + if (hidQueueInterface!=NULL) + { + if (queueStarted) + { + (*hidQueueInterface)->stop(hidQueueInterface); + } + (*hidQueueInterface)->dispose(hidQueueInterface); + (*hidQueueInterface)->Release(hidQueueInterface); + hidQueueInterface = NULL; + } + + hidAttribsDict = nil; + + if (hidElements!=NULL) + { + CFRelease(hidElements); + hidElements = NULL; + } + + if (hidDeviceInterface!=NULL) + { + if (opened) + { + (*hidDeviceInterface)->close(hidDeviceInterface); + } + (*hidDeviceInterface)->Release(hidDeviceInterface); + // opened = NO; + hidDeviceInterface = NULL; + } + + if (cfPluginInterface!=NULL) + { + IODestroyPlugInInterface(cfPluginInterface); + cfPluginInterface = NULL; + } + + return (NO); +} + +- (void)_destructService:(io_object_t)service +{ + NSNumber *serviceValue; + NSMutableDictionary *serviceDict = NULL; + + if ((serviceValue = @((unsigned int)service)) == nil) + { + return; + } + + serviceDict = _serviceAttribMap[serviceValue]; + + if (serviceDict!=nil) + { + IOHIDDeviceInterface122 **hidDeviceInterface = NULL; + IOCFPlugInInterface **cfPluginInterface = NULL; + IOHIDQueueInterface **hidQueueInterface = NULL; + io_object_t serviceNotification = 0; + CFRunLoopSourceRef queueEventSource = NULL; + io_object_t theService = 0; + NSMutableDictionary *cookieButtonMap = nil; + NSTimer *simulateHoldTimer = nil; + + serviceNotification = (io_object_t) (serviceDict[kHIDRemoteServiceNotification] ? [serviceDict[kHIDRemoteServiceNotification] unsignedIntValue] : 0); + theService = (io_object_t) (serviceDict[kHIDRemoteService] ? [serviceDict[kHIDRemoteService] unsignedIntValue] : 0); + queueEventSource = (CFRunLoopSourceRef) (serviceDict[kHIDRemoteCFRunLoopSource] ? [serviceDict[kHIDRemoteCFRunLoopSource] pointerValue] : NULL); + hidQueueInterface = (IOHIDQueueInterface **) (serviceDict[kHIDRemoteHIDQueueInterface] ? [serviceDict[kHIDRemoteHIDQueueInterface] pointerValue] : NULL); + hidDeviceInterface = (IOHIDDeviceInterface122 **) (serviceDict[kHIDRemoteHIDDeviceInterface] ? [serviceDict[kHIDRemoteHIDDeviceInterface] pointerValue] : NULL); + cfPluginInterface = (IOCFPlugInInterface **) (serviceDict[kHIDRemoteCFPluginInterface] ? [serviceDict[kHIDRemoteCFPluginInterface] pointerValue] : NULL); + cookieButtonMap = (NSMutableDictionary *) serviceDict[kHIDRemoteCookieButtonCodeLUT]; + simulateHoldTimer = (NSTimer *) serviceDict[kHIDRemoteSimulateHoldEventsTimer]; + + [_serviceAttribMap removeObjectForKey:serviceValue]; + + if ((serviceDict[kHIDRemoteAluminumRemoteSupportOnDemand]!=nil) && [serviceDict[kHIDRemoteAluminumRemoteSupportOnDemand] boolValue] && (theService != 0)) + { + // We previously requested the driver to enable Aluminum Remote support for us. Tell it to turn it off again - now that we no longer need it + IORegistryEntrySetCFProperty((io_registry_entry_t)theService, + CFSTR("DisableAluminumRemoteSupportForMe"), + (__bridge void *)@{@"pid": @((long long)getpid()), + @"uid": @((long long)getuid())}); + } + + if (([self delegate]!=nil) && + ([[self delegate] respondsToSelector:@selector(hidRemote:releasedHardwareWithAttributes:)])) + { + [((NSObject *)[self delegate]) hidRemote:self releasedHardwareWithAttributes:serviceDict]; + } + + if (simulateHoldTimer!=nil) + { + [simulateHoldTimer invalidate]; + } + + if (serviceNotification!=0) + { + IOObjectRelease(serviceNotification); + } + + if (queueEventSource!=NULL) + { + CFRunLoopRemoveSource( CFRunLoopGetCurrent(), + queueEventSource, + kCFRunLoopCommonModes); + } + + if ((hidQueueInterface!=NULL) && (cookieButtonMap!=nil)) + { + for (NSNumber *cookie in [cookieButtonMap keyEnumerator]) + { + if ((*hidQueueInterface)->hasElement(hidQueueInterface, (IOHIDElementCookie) [cookie unsignedIntValue])) + { + (*hidQueueInterface)->removeElement(hidQueueInterface, + (IOHIDElementCookie) [cookie unsignedIntValue]); + } + }; + } + + if (hidQueueInterface!=NULL) + { + (*hidQueueInterface)->stop(hidQueueInterface); + (*hidQueueInterface)->dispose(hidQueueInterface); + (*hidQueueInterface)->Release(hidQueueInterface); + } + + if (hidDeviceInterface!=NULL) + { + (*hidDeviceInterface)->close(hidDeviceInterface); + (*hidDeviceInterface)->Release(hidDeviceInterface); + } + + if (cfPluginInterface!=NULL) + { + IODestroyPlugInInterface(cfPluginInterface); + } + + if (theService!=0) + { + IOObjectRelease(theService); + } + } +} + + +#pragma mark -- PRIVATE: HID Event handling -- +- (void)_simulateHoldEvent:(NSTimer *)aTimer +{ + NSMutableDictionary *hidAttribsDict; + NSTimer *shTimer; + NSNumber *shButtonCode; + + if ((hidAttribsDict = (NSMutableDictionary *)[aTimer userInfo]) != nil) + { + if (((shTimer = hidAttribsDict[kHIDRemoteSimulateHoldEventsTimer]) != nil) && + ((shButtonCode = hidAttribsDict[kHIDRemoteSimulateHoldEventsOriginButtonCode]) != nil)) + { + [shTimer invalidate]; + [hidAttribsDict removeObjectForKey:kHIDRemoteSimulateHoldEventsTimer]; + + [self _sendButtonCode:(((HIDRemoteButtonCode)[shButtonCode unsignedIntValue])|kHIDRemoteButtonCodeHoldMask) isPressed:YES hidAttribsDict:hidAttribsDict]; + } + } +} + +- (void)_handleButtonCode:(HIDRemoteButtonCode)buttonCode isPressed:(BOOL)isPressed hidAttribsDict:(NSMutableDictionary *)hidAttribsDict +{ + switch (buttonCode) + { + case kHIDRemoteButtonCodeIDChanged: + // Do nothing, this is handled seperately + break; + + case kHIDRemoteButtonCodeUp: + case kHIDRemoteButtonCodeDown: + if (_simulateHoldEvents) + { + NSTimer *shTimer = nil; + NSNumber *shButtonCode = nil; + + [hidAttribsDict[kHIDRemoteSimulateHoldEventsTimer] invalidate]; + + if (isPressed) + { + hidAttribsDict[kHIDRemoteSimulateHoldEventsOriginButtonCode] = [NSNumber numberWithUnsignedInt:buttonCode]; + + if ((shTimer = [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:0.7] interval:0.1 target:self selector:@selector(_simulateHoldEvent:) userInfo:hidAttribsDict repeats:NO]) != nil) + { + hidAttribsDict[kHIDRemoteSimulateHoldEventsTimer] = shTimer; + + [[NSRunLoop currentRunLoop] addTimer:shTimer forMode:NSRunLoopCommonModes]; + + break; + } + } + else + { + shTimer = hidAttribsDict[kHIDRemoteSimulateHoldEventsTimer]; + shButtonCode = hidAttribsDict[kHIDRemoteSimulateHoldEventsOriginButtonCode]; + + if ((shTimer!=nil) && (shButtonCode!=nil)) + { + [self _sendButtonCode:(HIDRemoteButtonCode)[shButtonCode unsignedIntValue] isPressed:YES hidAttribsDict:hidAttribsDict]; + [self _sendButtonCode:(HIDRemoteButtonCode)[shButtonCode unsignedIntValue] isPressed:NO hidAttribsDict:hidAttribsDict]; + } + else + { + if (shButtonCode!=nil) + { + [self _sendButtonCode:(((HIDRemoteButtonCode)[shButtonCode unsignedIntValue])|kHIDRemoteButtonCodeHoldMask) isPressed:NO hidAttribsDict:hidAttribsDict]; + } + } + } + + [hidAttribsDict removeObjectForKey:kHIDRemoteSimulateHoldEventsTimer]; + [hidAttribsDict removeObjectForKey:kHIDRemoteSimulateHoldEventsOriginButtonCode]; + + break; + } + + default: + [self _sendButtonCode:buttonCode isPressed:isPressed hidAttribsDict:hidAttribsDict]; + break; + } +} + +- (void)_sendButtonCode:(HIDRemoteButtonCode)buttonCode isPressed:(BOOL)isPressed hidAttribsDict:(NSMutableDictionary *)hidAttribsDict +{ + if (([self delegate]!=nil) && + ([[self delegate] respondsToSelector:@selector(hidRemote:eventWithButton:isPressed:fromHardwareWithAttributes:)])) + { + switch (buttonCode & (~kHIDRemoteButtonCodeAluminumMask)) + { + case kHIDRemoteButtonCodePlay: + case kHIDRemoteButtonCodeCenter: + if (buttonCode & kHIDRemoteButtonCodeAluminumMask) + { + _lastSeenModel = kHIDRemoteModelAluminum; + _lastSeenModelRemoteID = _lastSeenRemoteID; + } + else + { + switch ((HIDRemoteAluminumRemoteSupportLevel)[hidAttribsDict[kHIDRemoteAluminumRemoteSupportLevel] intValue]) + { + case kHIDRemoteAluminumRemoteSupportLevelNone: + case kHIDRemoteAluminumRemoteSupportLevelEmulation: + // Remote type can't be determined by just the Center button press + break; + + case kHIDRemoteAluminumRemoteSupportLevelNative: + // Remote type can be safely determined by just the Center button press + if (((_lastSeenModel == kHIDRemoteModelAluminum) && (_lastSeenModelRemoteID != _lastSeenRemoteID)) || + (_lastSeenModel == kHIDRemoteModelUndetermined)) + { + _lastSeenModel = kHIDRemoteModelWhitePlastic; + } + break; + } + } + break; + } + + // As soon as we have received a code that's unique to the Aluminum Remote, we can tell kHIDRemoteButtonCodePlayHold and kHIDRemoteButtonCodeCenterHold apart. + // Prior to that, a long press of the new "Play" button will be submitted as a "kHIDRemoteButtonCodeCenterHold", not a "kHIDRemoteButtonCodePlayHold" code. + if ((buttonCode == kHIDRemoteButtonCodeCenterHold) && (_lastSeenModel == kHIDRemoteModelAluminum)) + { + buttonCode = kHIDRemoteButtonCodePlayHold; + } + + [((NSObject *)[self delegate]) hidRemote:self eventWithButton:(buttonCode & (~kHIDRemoteButtonCodeAluminumMask)) isPressed:isPressed fromHardwareWithAttributes:hidAttribsDict]; + } +} + +- (void)_hidEventFor:(io_service_t)hidDevice from:(IOHIDQueueInterface **)interface withResult:(IOReturn)result +{ + NSMutableDictionary *hidAttribsDict = _serviceAttribMap[@((unsigned int)hidDevice)]; + + if (hidAttribsDict!=nil) + { + IOHIDQueueInterface **queueInterface = NULL; + + queueInterface = [hidAttribsDict[kHIDRemoteHIDQueueInterface] pointerValue]; + + if (interface == queueInterface) + { + NSNumber *lastButtonPressedNumber = nil; + HIDRemoteButtonCode lastButtonPressed = kHIDRemoteButtonCodeNone; + NSMutableDictionary *cookieButtonMap = nil; + + cookieButtonMap = hidAttribsDict[kHIDRemoteCookieButtonCodeLUT]; + + if ((lastButtonPressedNumber = hidAttribsDict[kHIDRemoteLastButtonPressed]) != nil) + { + lastButtonPressed = [lastButtonPressedNumber unsignedIntValue]; + } + + while (result == kIOReturnSuccess) + { + IOHIDEventStruct hidEvent; + AbsoluteTime supportedTime = { 0,0 }; + + result = (*queueInterface)->getNextEvent( queueInterface, + &hidEvent, + supportedTime, + 0); + + if (result == kIOReturnSuccess) + { + NSNumber *buttonCodeNumber = cookieButtonMap[@((unsigned int) hidEvent.elementCookie)]; + +#ifdef _HIDREMOTE_EXTENSIONS + // Debug logging code +#define _HIDREMOTE_EXTENSIONS_SECTION 5 +#include "HIDRemoteAdditions.h" +#undef _HIDREMOTE_EXTENSIONS_SECTION +#endif /* _HIDREMOTE_EXTENSIONS */ + + if (buttonCodeNumber!=nil) + { + HIDRemoteButtonCode buttonCode = [buttonCodeNumber unsignedIntValue]; + + if (hidEvent.value == 0) + { + if (buttonCode == lastButtonPressed) + { + [self _handleButtonCode:lastButtonPressed isPressed:NO hidAttribsDict:hidAttribsDict]; + lastButtonPressed = kHIDRemoteButtonCodeNone; + } + } + + if (hidEvent.value != 0) + { + if (lastButtonPressed != kHIDRemoteButtonCodeNone) + { + [self _handleButtonCode:lastButtonPressed isPressed:NO hidAttribsDict:hidAttribsDict]; + // lastButtonPressed = kHIDRemoteButtonCodeNone; + } + + if (buttonCode == kHIDRemoteButtonCodeIDChanged) + { + if (([self delegate]!=nil) && + ([[self delegate] respondsToSelector:@selector(hidRemote:remoteIDChangedOldID:newID:forHardwareWithAttributes:)])) + { + [((NSObject *)[self delegate]) hidRemote:self remoteIDChangedOldID:_lastSeenRemoteID newID:hidEvent.value forHardwareWithAttributes:hidAttribsDict]; + } + + _lastSeenRemoteID = hidEvent.value; + _lastSeenModel = kHIDRemoteModelUndetermined; + } + + [self _handleButtonCode:buttonCode isPressed:YES hidAttribsDict:hidAttribsDict]; + lastButtonPressed = buttonCode; + } + } + } + }; + + hidAttribsDict[kHIDRemoteLastButtonPressed] = [NSNumber numberWithUnsignedInt:lastButtonPressed]; + } + +#ifdef _HIDREMOTE_EXTENSIONS + // Debug logging code +#define _HIDREMOTE_EXTENSIONS_SECTION 6 +#include "HIDRemoteAdditions.h" +#undef _HIDREMOTE_EXTENSIONS_SECTION +#endif /* _HIDREMOTE_EXTENSIONS */ + } +} + +#pragma mark -- PRIVATE: Notification handling -- +- (void)_serviceMatching:(io_iterator_t)iterator +{ + io_object_t matchingService = 0; + + while ((matchingService = IOIteratorNext(iterator)) != 0) + { + [self _setupService:matchingService]; + + IOObjectRelease(matchingService); + }; +} + +- (void)_serviceNotificationFor:(io_service_t)service messageType:(natural_t)messageType messageArgument:(void *)messageArgument +{ + if (messageType == kIOMessageServiceIsTerminated) + { + [self _destructService:service]; + } +} + +- (void)_updateSessionInformation +{ + NSArray *consoleUsersArray; + io_service_t rootService; + + if (_masterPort==0) { return; } + + if ((rootService = IORegistryGetRootEntry(_masterPort)) != 0) + { + if ((consoleUsersArray = (__bridge_transfer NSArray *)IORegistryEntryCreateCFProperty((io_registry_entry_t)rootService, CFSTR("IOConsoleUsers"), kCFAllocatorDefault, 0)) != nil) + { + if ([consoleUsersArray isKindOfClass:[NSArray class]]) // Be careful - ensure this really is an array + { + UInt64 secureEventInputPIDSum = 0; + uid_t frontUserSession = 0; + + for (NSDictionary *consoleUserDict in consoleUsersArray) + { + if ([consoleUserDict isKindOfClass:[NSDictionary class]]) // Be careful - ensure this really is a dictionary + { + NSNumber *secureInputPID; + NSNumber *onConsole; + NSNumber *userID; + + if ((secureInputPID = consoleUserDict[@"kCGSSessionSecureInputPID"]) != nil) + { + if ([secureInputPID isKindOfClass:[NSNumber class]]) + { + secureEventInputPIDSum += ((UInt64) [secureInputPID intValue]); + } + } + + if (((onConsole = consoleUserDict[@"kCGSSessionOnConsoleKey"]) != nil) && + ((userID = consoleUserDict[@"kCGSSessionUserIDKey"]) != nil)) + { + if ([onConsole isKindOfClass:[NSNumber class]] && [userID isKindOfClass:[NSNumber class]]) + { + if ([onConsole boolValue]) + { + frontUserSession = (uid_t) [userID intValue]; + } + } + } + } + } + + _lastSecureEventInputPIDSum = secureEventInputPIDSum; + _lastFrontUserSession = frontUserSession; + } + } + + IOObjectRelease((io_object_t) rootService); + } +} + +- (void)_secureInputNotificationFor:(io_service_t)service messageType:(natural_t)messageType messageArgument:(void *)messageArgument +{ + if (messageType == kIOMessageServiceBusyStateChange) + { + UInt64 old_lastSecureEventInputPIDSum = _lastSecureEventInputPIDSum; + uid_t old_lastFrontUserSession = _lastFrontUserSession; + + [self _updateSessionInformation]; + + if (((old_lastSecureEventInputPIDSum != _lastSecureEventInputPIDSum) || (old_lastFrontUserSession != _lastFrontUserSession)) && _secureEventInputWorkAround) + { + if ((_mode == kHIDRemoteModeExclusive) || (_mode == kHIDRemoteModeExclusiveAuto)) + { + HIDRemoteMode restartInMode = _mode; + + _isRestarting = YES; + [self stopRemoteControl]; + [self startRemoteControl:restartInMode]; + _isRestarting = NO; + } + } + } +} + +@end + +#pragma mark -- PRIVATE: IOKitLib Callbacks -- + +static void HIDEventCallback( void * target, + IOReturn result, + void * refCon, + void * sender) +{ + HIDRemote *hidRemote = (__bridge HIDRemote *)refCon; + + @autoreleasepool { + [hidRemote _hidEventFor:(io_service_t)((intptr_t)target) from:(IOHIDQueueInterface**)sender withResult:(IOReturn)result]; + } +} + + +static void ServiceMatchingCallback( void *refCon, + io_iterator_t iterator) +{ + HIDRemote *hidRemote = (__bridge HIDRemote *)refCon; + + @autoreleasepool { + [hidRemote _serviceMatching:iterator]; + } +} + +static void ServiceNotificationCallback(void * refCon, + io_service_t service, + natural_t messageType, + void * messageArgument) +{ + HIDRemote *hidRemote = (__bridge HIDRemote *)refCon; + + @autoreleasepool { + [hidRemote _serviceNotificationFor:service + messageType:messageType + messageArgument:messageArgument]; + } +} + +static void SecureInputNotificationCallback( void * refCon, + io_service_t service, + natural_t messageType, + void * messageArgument) +{ + HIDRemote *hidRemote = (__bridge HIDRemote *)refCon; + + @autoreleasepool { + [hidRemote _secureInputNotificationFor:service + messageType:messageType + messageArgument:messageArgument]; + } +} + +// Attribute dictionary keys +NSString *kHIDRemoteCFPluginInterface = @"CFPluginInterface"; +NSString *kHIDRemoteHIDDeviceInterface = @"HIDDeviceInterface"; +NSString *kHIDRemoteCookieButtonCodeLUT = @"CookieButtonCodeLUT"; +NSString *kHIDRemoteHIDQueueInterface = @"HIDQueueInterface"; +NSString *kHIDRemoteServiceNotification = @"ServiceNotification"; +NSString *kHIDRemoteCFRunLoopSource = @"CFRunLoopSource"; +NSString *kHIDRemoteLastButtonPressed = @"LastButtonPressed"; +NSString *kHIDRemoteService = @"Service"; +NSString *kHIDRemoteSimulateHoldEventsTimer = @"SimulateHoldEventsTimer"; +NSString *kHIDRemoteSimulateHoldEventsOriginButtonCode = @"SimulateHoldEventsOriginButtonCode"; +NSString *kHIDRemoteAluminumRemoteSupportLevel = @"AluminumRemoteSupportLevel"; +NSString *kHIDRemoteAluminumRemoteSupportOnDemand = @"AluminumRemoteSupportLevelOnDemand"; + +NSString *kHIDRemoteManufacturer = @"Manufacturer"; +NSString *kHIDRemoteProduct = @"Product"; +NSString *kHIDRemoteTransport = @"Transport"; + +// Distributed notifications +NSString *kHIDRemoteDNHIDRemotePing = @"com.candelair.ping"; +NSString *kHIDRemoteDNHIDRemoteRetry = @"com.candelair.retry"; +NSString *kHIDRemoteDNHIDRemoteStatus = @"com.candelair.status"; + +NSString *kHIDRemoteDNHIDRemoteRetryGlobalObject = @"global"; + +// Distributed notifications userInfo keys and values +NSString *kHIDRemoteDNStatusHIDRemoteVersionKey = @"HIDRemoteVersion"; +NSString *kHIDRemoteDNStatusPIDKey = @"PID"; +NSString *kHIDRemoteDNStatusModeKey = @"Mode"; +NSString *kHIDRemoteDNStatusUnusedButtonCodesKey = @"UnusedButtonCodes"; +NSString *kHIDRemoteDNStatusActionKey = @"Action"; +NSString *kHIDRemoteDNStatusRemoteControlCountKey = @"RemoteControlCount"; +NSString *kHIDRemoteDNStatusReturnToPIDKey = @"ReturnToPID"; +NSString *kHIDRemoteDNStatusActionStart = @"start"; +NSString *kHIDRemoteDNStatusActionStop = @"stop"; +NSString *kHIDRemoteDNStatusActionUpdate = @"update"; +NSString *kHIDRemoteDNStatusActionNoNeed = @"noneed"; diff --git a/src/input/apple/HIDRemote/LICENSE.txt b/src/input/apple/HIDRemote/LICENSE.txt new file mode 100644 index 0000000..0a6695c --- /dev/null +++ b/src/input/apple/HIDRemote/LICENSE.txt @@ -0,0 +1,27 @@ +Copyright (c) 2007-2011 IOSPIRIT GmbH (http://www.iospirit.com/) +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list + of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of IOSPIRIT GmbH nor the names of its contributors may be used to + endorse or promote products derived from this software without specific prior + written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. \ No newline at end of file diff --git a/src/input/apple/HIDRemote/README.txt b/src/input/apple/HIDRemote/README.txt new file mode 100644 index 0000000..36ddafb --- /dev/null +++ b/src/input/apple/HIDRemote/README.txt @@ -0,0 +1,18 @@ +README FOR HIDREMOTE SDK +======================== + +HIDRemote +--------- +Contains the HIDRemote class. For the latest version of this class and +documentation, please visit http://www.iospirit.com/developers/hidremote/ + + +Example +------- +Contains a sample application showing the use of HIDRemote class. + + +Documentation +------------- +Please visit http://www.iospirit.com/developers/hidremote/ for a guide +and documentation. diff --git a/src/input/apple/InputAppleMediaKeys.h b/src/input/apple/InputAppleMediaKeys.h new file mode 100644 index 0000000..5b6d442 --- /dev/null +++ b/src/input/apple/InputAppleMediaKeys.h @@ -0,0 +1,22 @@ +// +// Created by Tobias Hieta on 21/08/15. +// + +#ifndef KONVERGO_INPUTAPPLEMEDIAKEYS_H +#define KONVERGO_INPUTAPPLEMEDIAKEYS_H + +#include "input/InputComponent.h" + +class InputAppleMediaKeys : public InputBase +{ + Q_OBJECT +public: + InputAppleMediaKeys(QObject* parent = 0) : InputBase(parent) { } + bool initInput(); + const char* inputName() { return "AppleMediaKeys"; } + +private: + void* m_delegate; +}; + +#endif //KONVERGO_INPUTAPPLEMEDIAKEYS_H diff --git a/src/input/apple/InputAppleMediaKeys.mm b/src/input/apple/InputAppleMediaKeys.mm new file mode 100644 index 0000000..917dacc --- /dev/null +++ b/src/input/apple/InputAppleMediaKeys.mm @@ -0,0 +1,74 @@ +// +// Created by Tobias Hieta on 21/08/15. +// + +#include "InputAppleMediaKeys.h" +#include "SPMediaKeyTap.h" +#include "QsLog.h" + +@interface MediaKeysDelegate : NSObject +{ + SPMediaKeyTap* keyTap; + InputAppleMediaKeys* m_input; +} +-(instancetype)initWithInput:(InputAppleMediaKeys*)input; +@end + +@implementation MediaKeysDelegate + +- (instancetype)initWithInput:(InputAppleMediaKeys*)input +{ + self = [super init]; + if (self) { + m_input = input; + keyTap = [[SPMediaKeyTap alloc] initWithDelegate:self]; + if ([SPMediaKeyTap usesGlobalMediaKeyTap]) + [keyTap startWatchingMediaKeys]; + else + QLOG_WARN() << "Could not grab global media keys"; + } + return self; +} + +-(void)mediaKeyTap:(SPMediaKeyTap *)keyTap receivedMediaKeyEvent:(NSEvent *)event +{ + int keyCode = (([event data1] & 0xFFFF0000) >> 16); + int keyFlags = ([event data1] & 0x0000FFFF); + BOOL keyIsPressed = (((keyFlags & 0xFF00) >> 8)) == 0xA; + + QString keyPressed; + + if (keyIsPressed) { + switch (keyCode) { + case NX_KEYTYPE_PLAY: + keyPressed = INPUT_KEY_PLAY; + break; + case NX_KEYTYPE_FAST: + keyPressed = "KEY_FAST"; + break; + case NX_KEYTYPE_REWIND: + keyPressed = "KEY_REWIND"; + break; + case NX_KEYTYPE_NEXT: + keyPressed = INPUT_KEY_NEXT; + break; + case NX_KEYTYPE_PREVIOUS: + keyPressed = INPUT_KEY_PREV; + break; + default: + break; + // More cases defined in hidsystem/ev_keymap.h + } + + emit m_input->receivedInput("AppleMediaKeys", keyPressed); + } +} + +@end + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool InputAppleMediaKeys::initInput() +{ + m_delegate = [[MediaKeysDelegate alloc] initWithInput:this]; + return true; +} \ No newline at end of file diff --git a/src/input/apple/InputAppleRemote.h b/src/input/apple/InputAppleRemote.h new file mode 100644 index 0000000..b094b7e --- /dev/null +++ b/src/input/apple/InputAppleRemote.h @@ -0,0 +1,35 @@ +#ifndef __INPUT_APPLE_REMOTE_H__ +#define __INPUT_APPLE_REMOTE_H__ + +#include +#include +#include "input/InputComponent.h" + + +#ifdef __OBJC__ +@class AppleRemoteDelegate; +typedef AppleRemoteDelegate delegate; +#else +typedef void delegate; +#endif + +class InputAppleRemote : public InputBase +{ +public: + InputAppleRemote(QObject* parent = 0) : InputBase(parent) { } + virtual const char* inputName() { return "AppleRemote"; } + virtual bool initInput(); + + void remoteButtonEvent(quint8 code, bool pressed, const QString& name); + + void addRemote(const QString& name); + void removeRemote(const QString& name); + void addRemoteFailed(const QString& error); + +private: + delegate* m_delegate; + QStringList m_remotes; + +}; + +#endif \ No newline at end of file diff --git a/src/input/apple/InputAppleRemote.mm b/src/input/apple/InputAppleRemote.mm new file mode 100644 index 0000000..93033fb --- /dev/null +++ b/src/input/apple/InputAppleRemote.mm @@ -0,0 +1,85 @@ +#include "InputAppleRemote.h" +#include "QsLog.h" + +#include "HIDRemote/HIDRemote.h" +#include "AppleRemoteDelegate.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool InputAppleRemote::initInput() +{ + m_delegate = [[AppleRemoteDelegate alloc] initWithRemoteHandler:this]; + return [m_delegate setupRemote]; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void InputAppleRemote::addRemoteFailed(const QString &error) +{ + QLOG_ERROR() << error; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void InputAppleRemote::addRemote(const QString &name) +{ + m_remotes << name; + QLOG_DEBUG() << "Added remote:" << name; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void InputAppleRemote::removeRemote(const QString &name) +{ + m_remotes.removeOne(name); + QLOG_DEBUG() << "Remove remote:" << name; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +static QString codeToString(HIDRemoteButtonCode code) +{ + switch (code) + { + case kHIDRemoteButtonCodeCenter: + return INPUT_KEY_SELECT; + case kHIDRemoteButtonCodeCenterHold: + return INPUT_KEY_SELECT_LONG; + case kHIDRemoteButtonCodeDown: + return INPUT_KEY_DOWN; + case kHIDRemoteButtonCodeDownHold: + return INPUT_KEY_DOWN_LONG; + case kHIDRemoteButtonCodeLeft: + return INPUT_KEY_LEFT; + case kHIDRemoteButtonCodeLeftHold: + return INPUT_KEY_LEFT_LONG; + case kHIDRemoteButtonCodeRight: + return INPUT_KEY_RIGHT; + case kHIDRemoteButtonCodeRightHold: + return INPUT_KEY_RIGHT_LONG; + case kHIDRemoteButtonCodeMenu: + return INPUT_KEY_MENU; + case kHIDRemoteButtonCodeMenuHold: + return INPUT_KEY_MENU_LONG; + case kHIDRemoteButtonCodePlay: + return INPUT_KEY_PLAY; + case kHIDRemoteButtonCodePlayHold: + return INPUT_KEY_PLAY_LONG; + case kHIDRemoteButtonCodeUp: + return INPUT_KEY_UP; + case kHIDRemoteButtonCodeUpHold: + return INPUT_KEY_UP_LONG; + default: + QLOG_WARN() << "could not handle key code:" << code; + } + return ""; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void InputAppleRemote::remoteButtonEvent(quint8 code, bool pressed, const QString &name) +{ + if (pressed) + { + QString codeStr = codeToString((HIDRemoteButtonCode)code); + if (!codeStr.isEmpty()) + { + QLOG_DEBUG() << name << "pressed button:" << codeStr; + emit receivedInput("AppleRemote", codeStr); + } + } +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..cc49c2d --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,276 @@ +#include + +#include +#include +#include +#include +#include +#include + +#include "system/SystemComponent.h" +#include "system/UpdateManager.h" +#include "QsLog.h" +#include "Paths.h" +#include "player/PlayerComponent.h" +#include "breakpad/CrashDumps.h" +#include "Version.h" +#include "settings/SettingsComponent.h" +#include "ui/KonvergoWindow.h" +#include "ui/KonvergoEngine.h" +#include "UniqueApplication.h" +#include "utils/HelperLauncher.h" + +#if defined(Q_OS_MAC) || defined(Q_OS_LINUX) +#include "SignalManager.h" +#endif + +using namespace QsLogging; + +///////////////////////////////////////////////////////////////////////////////////////// +void initQt(QGuiApplication* app) +{ + QCoreApplication::setApplicationName(Names::MainName()); + QCoreApplication::setApplicationVersion(Version::GetVersionString()); + QCoreApplication::setOrganizationDomain("plex.tv"); + + app->setWindowIcon(QIcon(":/images/icon.png")); +} + +///////////////////////////////////////////////////////////////////////////////////////// +static void qtMessageOutput(QtMsgType type, const QMessageLogContext& context, const QString& msg) +{ + QByteArray localMsg = msg.toLocal8Bit(); + QString prefix; + if (context.line) + prefix = QString("%1:%2:%3: ").arg(context.file).arg(context.line).arg(context.function); + QString text = prefix + msg; + switch (type) + { + case QtDebugMsg: + QLOG_DEBUG() << text; + break; + case QtInfoMsg: + QLOG_INFO() << text; + break; + case QtWarningMsg: + QLOG_WARN() << text; + break; + case QtCriticalMsg: + QLOG_ERROR() << text; + break; + case QtFatalMsg: + QLOG_FATAL() << text; + break; + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +static void elidePattern(QString& msg, const QString& substring, int chars) +{ + int start = 0; + while (true) + { + start = msg.indexOf(substring, start); + if (start < 0 || start + substring.length() + chars > msg.length()) + break; + start += substring.length(); + for (int n = 0; n < chars; n++) + msg[start + n] = QChar('x'); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +static void processLog(QString& msg) +{ + elidePattern(msg, "X-Plex-Token=", 20); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void initLogger() +{ + // Note where the logfile is going to be + qDebug("Logging to %s", qPrintable(Paths::logDir(Names::MainName() + ".log"))); + + // init logging. + DestinationPtr dest = DestinationFactory::MakeFileDestination( + Paths::logDir(Names::MainName() + ".log"), + EnableLogRotationOnOpen, + MaxSizeBytes(1024 * 1024), + MaxOldLogCount(9)); + + Logger::instance().addDestination(dest); + Logger::instance().setLoggingLevel(DebugLevel); + Logger::instance().setProcessingCallback(processLog); + + qInstallMessageHandler(qtMessageOutput); +} + +///////////////////////////////////////////////////////////////////////////////////////// +char** appendCommandLineArguments(int *argc, char **argv) +{ + static char *newArgs[16]; + QList argList; + + // Copy argv list to our StringList + for (int i=0; i < *argc; i++) + { + argList << QString(argv[i]); + } + + // add any required additionnal commandline argument +#if KONVERGO_OPENELEC + // on RPI with webengine, OpenGL contexts are shared statically with webengine + // which avoids proper reset when switching display mode + // On OE we also need that because there is a crash with OZONE otherwise + argList << "--disable-gpu"; +#endif + + // with webengine we need those to have a proper scaling of the webview in the window + argList << "--enable-viewport"; + argList << "--enable-viewport-meta"; + + // Now rebuild our argc, argv list + *argc = argList.size(); + + for(int iarg=0; iarg < argList.size(); iarg++) + { + newArgs[iarg] = (char*)malloc(256); + strcpy(newArgs[iarg], argList.value(iarg).toStdString().c_str()); + } + + return (char**)newArgs; +} + +///////////////////////////////////////////////////////////////////////////////////////// +int main(int argc, char *argv[]) +{ + try + { + int newArgc = argc; + char **newArgv = appendCommandLineArguments(&newArgc, argv); + + // Supress SSL related warnings on OSX + // See https://bugreports.qt.io/browse/QTBUG-43173 for more info + // +#ifdef Q_OS_MAC + qputenv("QT_LOGGING_RULES", "qt.network.ssl.warning=false"); + + // Request OpenGL 4.1 if possible on OSX, otherwise it defaults to 2.0 + // This needs to be done before we create the QGuiApplication + // + QSurfaceFormat format = QSurfaceFormat::defaultFormat(); + format.setMajorVersion(3); + format.setMinorVersion(2); + format.setProfile(QSurfaceFormat::CoreProfile); + QSurfaceFormat::setDefaultFormat(format); +#endif + + QGuiApplication app(newArgc, newArgv); + initQt(&app); + + // init breakpad. + setupCrashDumper(); + + UniqueApplication* uniqueApp = new UniqueApplication(); + if (!uniqueApp->ensureUnique()) + return EXIT_SUCCESS; + +#ifdef Q_OS_UNIX + // install signals handlers for proper app closing. + SignalManager signalManager(&app); + Q_UNUSED(signalManager); +#endif + + initLogger(); + QLOG_INFO() << "Starting Plex Media Player version:" << Version::GetVersionString() << "build date:" << Version::GetBuildDate(); + + // Quit app and apply update if we find one. + if (UpdateManager::CheckForUpdates()) + { + app.quit(); + return 0; + } + +#ifdef Q_OS_WIN32 + initD3DDevice(); +#endif + +#ifdef Q_OS_UNIX + setlocale(LC_NUMERIC, "C"); +#endif + + // Initialize all the components. This needs to be done + // early since most everything else relies on it + // + ComponentManager::Get().initialize(); + + // enable remote inspection if we have the correct setting for it. + if (SettingsComponent::Get().value(SETTINGS_SECTION_MAIN, "remoteInspector").toBool()) + qputenv("QTWEBENGINE_REMOTE_DEBUGGING", "0.0.0.0:9992"); + + QtWebEngine::initialize(); + + // Qt and QWebEngineProfile set the locale, which breaks parsing and + // formatting float numbers in a few countries. +#ifdef Q_OS_UNIX + setlocale(LC_NUMERIC, "C"); +#endif + + // start our helper + HelperLauncher::Get().connectToHelper(); + + // load QtWebChannel so that we can register our components with it. + QQmlApplicationEngine *engine = KonvergoEngine::Get(); + KonvergoWindow::RegisterClass(); + engine->rootContext()->setContextProperty("components", &ComponentManager::Get().getQmlPropertyMap()); + + // the only way to detect if QML parsing fails is to hook to this signal and then see + // if we get a valid object passed to it. Any error messages will be reported on stderr + // but since no normal user should ever see this it should be fine + // + QObject::connect(engine, &QQmlApplicationEngine::objectCreated, [=](QObject* object, const QUrl& url) + { + Q_UNUSED(url); + + if (object == 0) + throw FatalException(QObject::tr("Failed to parse application engine script.")); + + QObject* rootObject = engine->rootObjects().first(); + + QObject* webChannel = qvariant_cast(rootObject->property("webChannel")); + Q_ASSERT(webChannel); + ComponentManager::Get().setWebChannel(qobject_cast(webChannel)); + + KonvergoWindow* window = qobject_cast(rootObject); + Q_ASSERT(window); + QObject::connect(uniqueApp, &UniqueApplication::otherApplicationStarted, window, &KonvergoWindow::otherAppFocus); + + }); + engine->load(QUrl(QStringLiteral("qrc:/ui/webview.qml"))); + + // run our application + int ret = app.exec(); + + delete KonvergoEngine::Get(); + delete uniqueApp; + + return ret; + } + catch (FatalException& e) + { + + QLOG_FATAL() << "Unhandled FatalException:" << qPrintable(e.message()); + + QGuiApplication app(argc, argv); + QString text = e.message() + "
    " + QObject::tr("Please visit Plex support forums for support."); + + QQmlApplicationEngine* engine = new QQmlApplicationEngine(NULL); + engine->rootContext()->setContextProperty("errorTitle", QObject::tr("A critical error happened!")); + engine->rootContext()->setContextProperty("errorText", text); + engine->load(QUrl(QStringLiteral("qrc:/ui/errormessage.qml"))); + + app.exec(); + return 1; + + } +} diff --git a/src/player/CMakeLists.txt b/src/player/CMakeLists.txt new file mode 100644 index 0000000..5c549d7 --- /dev/null +++ b/src/player/CMakeLists.txt @@ -0,0 +1,2 @@ +add_sources(PlayerComponent.cpp PlayerComponent.h) +add_sources(PlayerQuickItem.cpp PlayerQuickItem.h) diff --git a/src/player/PlayerComponent.cpp b/src/player/PlayerComponent.cpp new file mode 100644 index 0000000..9096122 --- /dev/null +++ b/src/player/PlayerComponent.cpp @@ -0,0 +1,842 @@ +#include "PlayerComponent.h" +#include +#include +#include +#include +#include "display/DisplayComponent.h" +#include "settings/SettingsComponent.h" +#include "system/SystemComponent.h" +#include "utils/Utils.h" +#include "ComponentManager.h" +#include "settings/SettingsSection.h" + +#include "PlayerQuickItem.h" + +#include "QsLog.h" + +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////////////////////////////// +static void wakeup_cb(void *context) +{ + PlayerComponent *player = (PlayerComponent *)context; + + emit player->onMpvEvents(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +PlayerComponent::PlayerComponent(QObject* parent) + : ComponentBase(parent), m_lastPositionUpdate(0.0), m_playbackAudioDelay(0), m_playbackStartSent(false), m_window(0), m_mediaFrameRate(0), + m_restoreDisplayTimer(this), m_reloadAudioTimer(this) +{ + qmlRegisterType("Konvergo", 1, 0, "MpvVideo"); // deprecated name + qmlRegisterType("Konvergo", 1, 0, "KonvergoVideo"); + + m_restoreDisplayTimer.setSingleShot(true); + connect(&m_restoreDisplayTimer, &QTimer::timeout, this, &PlayerComponent::onRestoreDisplay); + + connect(&DisplayComponent::Get(), &DisplayComponent::refreshRateChanged, this, &PlayerComponent::onRefreshRateChange); + + m_reloadAudioTimer.setSingleShot(true); + connect(&m_reloadAudioTimer, &QTimer::timeout, this, &PlayerComponent::onReloadAudio); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +PlayerComponent::~PlayerComponent() +{ + if (m_mpv) + mpv_set_wakeup_callback(m_mpv, NULL, NULL); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool PlayerComponent::componentInitialize() +{ + m_mpv = mpv::qt::Handle::FromRawHandle(mpv_create()); + if (!m_mpv) + throw FatalException(tr("Failed to load mpv.")); + + mpv_request_log_messages(m_mpv, "terminal-default"); + mpv_set_option_string(m_mpv, "msg-level", "all=v"); + + // No mouse events + mpv_set_option_string(m_mpv, "input-cursor", "no"); + mpv_set_option_string(m_mpv, "cursor-autohide", "no"); + + mpv_set_option_string(m_mpv, "config", "yes"); + mpv_set_option_string(m_mpv, "config-dir", Paths::dataDir().toUtf8().data()); + + // We don't need this, so avoid initializing fontconfig. + mpv_set_option_string(m_mpv, "use-text-osd", "no"); + + // Always use the system mixer. + mpv_set_option_string(m_mpv, "softvol", "no"); + + // Just discard audio output if no audio device could be opened. This gives + // us better flexibility how to react to such errors (instead of just + // aborting playback immediately). + mpv_set_option_string(m_mpv, "audio-fallback-to-null", "yes"); + + // Make it load the hwdec interop, so hwdec can be enabled at runtime. + mpv::qt::set_option_variant(m_mpv, "hwdec-preload", "auto"); + + // User-visible application name used by some audio APIs (at least PulseAudio). + mpv_set_option_string(m_mpv, "audio-client-name", QCoreApplication::applicationName().toUtf8().data()); + // User-visible stream title used by some audio APIs (at least PulseAudio and wasapi). + mpv_set_option_string(m_mpv, "title", QCoreApplication::applicationName().toUtf8().data()); + + // Apply some low-memory settings on RPI, which is relatively memory-constrained. +#ifdef TARGET_RPI + // The backbuffer makes seeking back faster (without having to do a HTTP-level seek) + mpv::qt::set_option_variant(m_mpv, "cache-backbuffer", 10 * 1024); // KB + // The demuxer queue is used for the readahead, and also for dealing with badly + // interlaved audio/video. Setting it too low increases sensitivity to network + // issues, and could cause playback failure with "bad" files. + mpv::qt::set_option_variant(m_mpv, "demuxer-max-bytes", 50 * 1024 * 1024); // bytes +#endif + + mpv_observe_property(m_mpv, 0, "pause", MPV_FORMAT_FLAG); + mpv_observe_property(m_mpv, 0, "cache-buffering-state", MPV_FORMAT_INT64); + mpv_observe_property(m_mpv, 0, "playback-time", MPV_FORMAT_DOUBLE); + mpv_observe_property(m_mpv, 0, "vo-configured", MPV_FORMAT_FLAG); + mpv_observe_property(m_mpv, 0, "duration", MPV_FORMAT_DOUBLE); + mpv_observe_property(m_mpv, 0, "audio-device-list", MPV_FORMAT_NODE); + + connect(this, &PlayerComponent::onMpvEvents, this, &PlayerComponent::handleMpvEvents, Qt::QueuedConnection); + + mpv_set_wakeup_callback(m_mpv, wakeup_cb, this); + + if (mpv_initialize(m_mpv) < 0) + throw FatalException(tr("Failed to initialize mpv.")); + + // Setup a hook with the ID 1, which is run during the file is loaded. + // Used to delay playback start for display framerate switching. + // (See handler in handleMpvEvent() for details.) + mpv::qt::command_variant(m_mpv, QStringList() << "hook-add" << "on_load" << "1" << "0"); + + updateAudioDeviceList(); + setAudioConfiguration(); + updateSubtitleSettings(); + updateVideoSettings(); + + connect(SettingsComponent::Get().getSection(SETTINGS_SECTION_VIDEO), &SettingsSection::valuesUpdated, + this, &PlayerComponent::updateVideoSettings); + + connect(SettingsComponent::Get().getSection(SETTINGS_SECTION_SUBTITLES), &SettingsSection::valuesUpdated, + this, &PlayerComponent::updateSubtitleSettings); + + connect(SettingsComponent::Get().getSection(SETTINGS_SECTION_AUDIO), &SettingsSection::valuesUpdated, + this, &PlayerComponent::setAudioConfiguration); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PlayerComponent::setQtQuickWindow(QQuickWindow* window) +{ + PlayerQuickItem* video = window->findChild("video"); + if (!video) + throw FatalException(tr("Failed to load video element.")); + + mpv_set_option_string(m_mpv, "vo", "opengl-cb"); + + video->initMpv(this); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PlayerComponent::setRpiWindow(QQuickWindow* window) +{ + window->setFlags(Qt::FramelessWindowHint); + + mpv_set_option_string(m_mpv, "vo", "rpi"); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PlayerComponent::setWindow(QQuickWindow* window) +{ + bool use_rpi = false; +#ifdef TARGET_RPI + use_rpi = true; +#endif + + m_window = window; + if (!window) + return; + + QString force_vo = SettingsComponent::Get().value(SETTINGS_SECTION_VIDEO, "debug.force_vo").toString(); + if (force_vo.size()) + mpv::qt::set_option_variant(m_mpv, "vo", force_vo); + else if (use_rpi) + setRpiWindow(window); + else + setQtQuickWindow(window); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool PlayerComponent::load(const QString& url, const QVariantMap& options, const QVariantMap &metadata, const QString& audioStream , const QString& subtitleStream) +{ + stop(); + queueMedia(url, options, metadata, audioStream, subtitleStream); + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PlayerComponent::queueMedia(const QString& url, const QVariantMap& options, const QVariantMap &metadata, const QString& audioStream, const QString& subtitleStream) +{ + m_mediaFrameRate = metadata["frameRate"].toFloat(); // returns 0 on failure + + updateVideoSettings(); + + QVariantList command; + command << "loadfile" << url; + command << "append-play"; // if nothing is playing, play it now, otherwise just enqueue it + + QVariantMap extraArgs; + + quint64 startMilliseconds = options["startMilliseconds"].toLongLong(); + if (startMilliseconds != 0) + extraArgs.insert("start", "+" + QString::number(startMilliseconds / 1000.0)); + + // detect subtitles + if (!subtitleStream.isEmpty()) + { + // If the stream title starts with a #, then it's an index + if (subtitleStream.startsWith("#")) + extraArgs.insert("ff-sid", subtitleStream.mid(1)); + else + extraArgs.insert("sub-file", subtitleStream); + + // try guessing the codepage. We could pass a country code to enca to help + // it detect the correct one, but we don't get that from the server right now + // + extraArgs.insert("sub-codepage", "enca"); + } + else + { + // no subtitles, tell mpv to ignore them. + extraArgs.insert("sid", "no"); + } + + if (metadata["type"] == "music") + extraArgs.insert("vid", "no"); + + // and then the audio stream + if (!audioStream.isEmpty()) + extraArgs.insert("ff-aid", audioStream); + + extraArgs.insert("pause", options["autoplay"].toBool() ? "no" : "yes"); + + command << extraArgs; + + QLOG_DEBUG() << command; + mpv::qt::command_variant(m_mpv, command); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool PlayerComponent::switchDisplayFrameRate() +{ + QLOG_DEBUG() << "Video framerate:" << m_mediaFrameRate << "fps"; + + if (!SettingsComponent::Get().value(SETTINGS_SECTION_VIDEO, "refreshrate.auto_switch").toBool()) + { + QLOG_DEBUG() << "Not switching refresh-rate (disabled by settings)."; + return false; + } + + bool fs = SettingsComponent::Get().value(SETTINGS_SECTION_MAIN, "fullscreen").toBool(); +#if KONVERGO_OPENELEC + fs = true; +#endif + if (!fs) + { + QLOG_DEBUG() << "Not switching refresh-rate (not in fullscreen mode)."; + return false; + } + + if (m_mediaFrameRate < 1) + { + QLOG_DEBUG() << "Not switching refresh-rate (no known video framerate)."; + return false; + } + + // Make sure a timer started by the previous file ending isn't accidentally + // still in-flight. It could switch the display back after we've switched. + m_restoreDisplayTimer.stop(); + + DisplayComponent* display = &DisplayComponent::Get(); + if (!display->switchToBestVideoMode(m_mediaFrameRate)) + { + QLOG_DEBUG() << "Switching refresh-rate failed or unnecessary."; + return false; + } + + // Make sure settings dependent on the display refresh rate are updated properly. + updateVideoSettings(); + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PlayerComponent::onRestoreDisplay() +{ + // If the player will in fact start another file (or is playing one), don't restore. + if (mpv::qt::get_property_variant(m_mpv, "idle").toBool()) + DisplayComponent::Get().restorePreviousVideoMode(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PlayerComponent::onRefreshRateChange() +{ + // Make sure settings dependent on the display refresh rate are updated properly. + updateVideoSettings(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PlayerComponent::handleMpvEvent(mpv_event *event) +{ + switch (event->event_id) + { + case MPV_EVENT_START_FILE: + { + m_CurrentUrl = mpv::qt::get_property_variant(m_mpv, "path").toString(); + m_playbackStartSent = false; + break; + } + case MPV_EVENT_FILE_LOADED: + { + emit playing(m_CurrentUrl); + break; + } + case MPV_EVENT_END_FILE: + { + mpv_event_end_file *end_file = (mpv_event_end_file *)event->data; + switch (end_file->reason) + { + case MPV_END_FILE_REASON_EOF: + emit finished(m_CurrentUrl); + break; + case MPV_END_FILE_REASON_ERROR: + emit error(end_file->error, mpv_error_string(end_file->error)); + break; + default: + emit stopped(m_CurrentUrl); + break; + } + + emit playbackEnded(m_CurrentUrl); + m_CurrentUrl = ""; + + m_restoreDisplayTimer.start(0); + break; + } + case MPV_EVENT_IDLE: + { + emit playbackAllDone(); + break; + } + case MPV_EVENT_PLAYBACK_RESTART: + { + // it's also sent after seeks are completed + if (!m_playbackStartSent) + emit playbackStarting(); + m_playbackStartSent = true; + break; + } + case MPV_EVENT_PROPERTY_CHANGE: + { + mpv_event_property *prop = (mpv_event_property *)event->data; + if (strcmp(prop->name, "pause") == 0 && prop->format == MPV_FORMAT_FLAG) + { + int state = *(int *)prop->data; + emit paused(state); + } + else if (strcmp(prop->name, "cache-buffering-state") == 0 && prop->format == MPV_FORMAT_INT64) + { + int64_t percentage = *(int64_t *)prop->data; + emit buffering(percentage); + } + else if (strcmp(prop->name, "playback-time") == 0 && prop->format == MPV_FORMAT_DOUBLE) + { + double pos = *(double*)prop->data; + if (fabs(pos - m_lastPositionUpdate) > 0.25) + { + quint64 ms = (quint64)(qMax(pos * 1000.0, 0.0)); + emit positionUpdate(ms); + m_lastPositionUpdate = pos; + } + } + else if (strcmp(prop->name, "vo-configured") == 0) + { + int state = prop->format == MPV_FORMAT_FLAG ? *(int *)prop->data : 0; + emit windowVisible(state); + } + else if (strcmp(prop->name, "duration") == 0) + { + if (prop->format == MPV_FORMAT_DOUBLE) + emit updateDuration(*(double *)prop->data * 1000.0); + } + else if (strcmp(prop->name, "audio-device-list") == 0) + { + updateAudioDeviceList(); + } + break; + } + case MPV_EVENT_LOG_MESSAGE: + { + mpv_event_log_message *msg = (mpv_event_log_message *)event->data; + // Strip the trailing '\n' + size_t len = strlen(msg->text); + if (len > 0 && msg->text[len - 1] == '\n') + len -= 1; + QString logline = QString::fromUtf8(msg->prefix) + ": " + QString::fromUtf8(msg->text, len); + if (msg->log_level >= MPV_LOG_LEVEL_V) + QLOG_DEBUG() << qPrintable(logline); + else if (msg->log_level >= MPV_LOG_LEVEL_INFO) + QLOG_INFO() << qPrintable(logline); + else if (msg->log_level >= MPV_LOG_LEVEL_WARN) + QLOG_WARN() << qPrintable(logline); + else + QLOG_ERROR() << qPrintable(logline); + break; + } + case MPV_EVENT_CLIENT_MESSAGE: + { + mpv_event_client_message *msg = (mpv_event_client_message *)event->data; + // This happens when the player is about to load the file, but no actual loading has taken part yet. + // We use this to block loading until we explicitly tell it to continue. + if (msg->num_args >= 3 && !strcmp(msg->args[0], "hook_run") && !strcmp(msg->args[1], "1")) + { + QString resume_id = QString::fromUtf8(msg->args[2]); + // Calling this lambda will instruct mpv to continue loading the file. + auto resume = [=] { + mpv::qt::command_variant(m_mpv, QStringList() << "hook-ack" << resume_id); + }; + if (switchDisplayFrameRate()) + { + // Now wait for some time for mode change - this is needed because mode changing can take some + // time, during which the screen is black, and initializing hardware decoding could fail due + // to various strange OS-related reasons. + // (Better hope the user doesn't try to exit Konvergo during mode change.) + int pause = SettingsComponent::Get().value(SETTINGS_SECTION_VIDEO, "refreshrate.delay").toInt() * 1000; + QTimer::singleShot(pause, resume); + } + else + { + resume(); + } + break; + } + } + default:; /* ignore */ + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PlayerComponent::handleMpvEvents() +{ + // Process all events, until the event queue is empty. + while (1) + { + mpv_event *event = mpv_wait_event(m_mpv, 0); + if (event->event_id == MPV_EVENT_NONE) + break; + handleMpvEvent(event); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PlayerComponent::setVideoOnlyMode(bool enable) +{ + if (m_window) + { + QQuickItem *web = m_window->findChild("web"); + if (web) + web->setVisible(!enable); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PlayerComponent::play() +{ + QStringList args = (QStringList() << "set" << "pause" << "no"); + mpv::qt::command_variant(m_mpv, args); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PlayerComponent::stop() +{ + QStringList args("stop"); + mpv::qt::command_variant(m_mpv, args); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PlayerComponent::clearQueue() +{ + QStringList args("playlist_clear"); + mpv::qt::command_variant(m_mpv, args); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PlayerComponent::pause() +{ + QStringList args = (QStringList() << "set" << "pause" << "yes"); + mpv::qt::command_variant(m_mpv, args); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PlayerComponent::seekTo(qint64 ms) +{ + double start = mpv::qt::get_property_variant(m_mpv, "time-start").toDouble(); + QString timeStr = QString::number(ms / 1000.0 + start); + QStringList args = (QStringList() << "seek" << timeStr << "absolute"); + mpv::qt::command_variant(m_mpv, args); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +QVariant PlayerComponent::getAudioDeviceList() +{ + return mpv::qt::get_property_variant(m_mpv, "audio-device-list"); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PlayerComponent::setAudioDevice(const QString& name) +{ + mpv::qt::set_property_variant(m_mpv, "audio-device", name); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PlayerComponent::setVolume(quint8 volume) +{ + // Will fail if no audio output opened (i.e. no file playing) + mpv::qt::set_property_variant(m_mpv, "volume", volume); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +quint8 PlayerComponent::volume() +{ + QVariant volume = mpv::qt::get_property_variant(m_mpv, "volume"); + if (volume.isValid()) + return volume.toInt(); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PlayerComponent::setSubtitleStream(const QString &subtitleStream) +{ + if (subtitleStream.isEmpty()) + { + mpv::qt::set_property_variant(m_mpv, "ff-sid", "no"); + } + else if (subtitleStream.startsWith("#")) + { + mpv::qt::set_property_variant(m_mpv, "ff-sid", subtitleStream.mid(1)); + } + else + { + QStringList args = (QStringList() << "sub_add" << subtitleStream << "cached"); + mpv::qt::command_variant(m_mpv, args); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PlayerComponent::setAudioStream(const QString &audioStream) +{ + if (audioStream.isEmpty()) + mpv::qt::set_property_variant(m_mpv, "ff-aid", "no"); + else + mpv::qt::set_property_variant(m_mpv, "ff-aid", audioStream); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void PlayerComponent::setAudioDelay(qint64 milliseconds) +{ + m_playbackAudioDelay = milliseconds; + + double display_fps = DisplayComponent::Get().currentRefreshRate(); + const char *audio_delay_setting = "audio_delay.normal"; + if (fabs(display_fps - 24) < 1) // cover 24Hz, 23.976Hz, and values very close + audio_delay_setting = "audio_delay.24hz"; + + double fixed_delay = SettingsComponent::Get().value(SETTINGS_SECTION_VIDEO, audio_delay_setting).toFloat(); + mpv::qt::set_option_variant(m_mpv, "audio-delay", (fixed_delay + m_playbackAudioDelay) / 1000.0); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void PlayerComponent::setSubtitleDelay(qint64 milliseconds) +{ + mpv::qt::set_option_variant(m_mpv, "sub-delay", milliseconds / 1000.0); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PlayerComponent::onReloadAudio() +{ + mpv::qt::command_variant(m_mpv, QStringList() << "ao_reload"); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// This is called with the set of previous audio devices that were detected, and the set of current +// audio devices. From this we guess whether we should reopen the audio device. If the user-selected +// device went away previously, and now comes back, reinitializing the player's audio output will +// force the player and/or the OS to move audio output back to the user-selected device. +void PlayerComponent::checkCurrentAudioDevice(const QSet& old_devs, const QSet& new_devs) +{ + QString user_device = SettingsComponent::Get().value(SETTINGS_SECTION_AUDIO, "device").toString(); + + QSet removed = old_devs - new_devs; + QSet added = new_devs - old_devs; + + QLOG_DEBUG() << "Audio devices removed:" << removed; + QLOG_DEBUG() << "Audio devices added:" << added; + QLOG_DEBUG() << "Audio device selected:" << user_device; + + if (!mpv::qt::get_property_variant(m_mpv, "idle").toBool() && user_device.length()) + { + if (added.contains(user_device)) + { + // The timer is for debouncing the reload. Several change notifications could + // come in quick succession. Also, it's possible that trying to open the + // reappeared audio device immediately can fail. + m_reloadAudioTimer.start(500); + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PlayerComponent::updateAudioDeviceList() +{ + QVariantList settingList; + QVariant list = getAudioDeviceList(); + QSet devices; + foreach (const QVariant& d, list.toList()) + { + Q_ASSERT(d.type() == QVariant::Map); + QVariantMap dmap = d.toMap(); + + devices.insert(dmap["name"].toString()); + + QVariantMap entry; + entry["value"] = dmap["name"]; + entry["title"] = dmap["description"]; + + settingList << entry; + } + + SettingsComponent::Get().updatePossibleValues(SETTINGS_SECTION_AUDIO, "device", settingList); + + checkCurrentAudioDevice(m_audioDevices, devices); + m_audioDevices = devices; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PlayerComponent::setAudioConfiguration() +{ + QStringList ao_defaults; + // On OSX, ask mpv to change the audio format system-wide. This is needed + // at least to get multichannel PCM running, if the user didn't already + // select multichannel output in "Audio MIDI Setup". + ao_defaults << "coreaudio:change-physical-format=yes"; + + QString deviceType = SettingsComponent::Get().value(SETTINGS_SECTION_AUDIO, "devicetype").toString(); + + if (SettingsComponent::Get().value(SETTINGS_SECTION_AUDIO, "exclusive").toBool()) + { + ao_defaults << "wasapi:exclusive=yes"; + ao_defaults << "coreaudio:exclusive=yes"; + } + + mpv::qt::set_option_variant(m_mpv, "ao-defaults", ao_defaults.join(',')); + + // set the audio device + QVariant device = SettingsComponent::Get().value(SETTINGS_SECTION_AUDIO, "device"); + mpv::qt::set_property_variant(m_mpv, "audio-device", device); + + // set the channel layout + QVariant layout = SettingsComponent::Get().value(SETTINGS_SECTION_AUDIO, "channels"); + mpv::qt::set_option_variant(m_mpv, "audio-channels", layout); + + QString resampleOpts = ""; + bool normalize = SettingsComponent::Get().value(SETTINGS_SECTION_AUDIO, "normalize").toBool(); + resampleOpts += QString(":normalize=") + (normalize ? "yes" : "no"); + +#ifdef TARGET_RPI + // Low-quality resampling options on RPI for better display-sync behavior. + resampleOpts += ":o=[filter_type=cubic,filter_size=8,phase_shift=6]"; +#endif + + mpv::qt::set_option_variant(m_mpv, "af-defaults", "lavrresample" + resampleOpts); + + QString passthroughCodecs; + // passthrough doesn't make sense with basic type + if (deviceType != AUDIO_DEVICE_TYPE_BASIC) + { + QStringList enabledCodecs; + SettingsSection* audioSection = SettingsComponent::Get().getSection(SETTINGS_SECTION_AUDIO); + + QStringList codecs; + if (deviceType == AUDIO_DEVICE_TYPE_SPDIF) + codecs = AudioCodecsSPDIF(); + else if (deviceType == AUDIO_DEVICE_TYPE_HDMI && audioSection->value("advanced").toBool()) + codecs = AudioCodecsAll(); + + foreach (const QString& key, codecs) + { + if (audioSection->value("passthrough." + key).toBool()) + enabledCodecs << key; + } + + // dts-hd includes dts, but listing dts before dts-hd may disable dts-hd. + if (enabledCodecs.indexOf("dts-hd") != -1) + enabledCodecs.removeAll("dts"); + + passthroughCodecs = enabledCodecs.join(","); + } + + mpv::qt::set_option_variant(m_mpv, "audio-spdif", passthroughCodecs); + + // if the user has indicated that we have a optical spdif connection + // we need to set this extra option that allows us to transcode + // 5.1 audio into a usable format, note that we only support AC3 + // here for now. We might need to add support for DTS transcoding + // if we see user requests for it. + // + bool doAc3Transcoding = false; + if (deviceType == AUDIO_DEVICE_TYPE_SPDIF && + SettingsComponent::Get().value(SETTINGS_SECTION_AUDIO, "passthrough.ac3").toBool()) + { + QLOG_INFO() << "Enabling audio AC3 transcoding (if needed)"; + mpv::qt::set_option_variant(m_mpv, "af", "lavcac3enc"); + doAc3Transcoding = true; + } + + // Make a informational log message. + QString audioConfig = QString(QString("Audio Config - device: %1, ") + + "channel layout: %2, " + + "passthrough codecs: %3, " + + "ac3 transcoding: %4").arg(device.toString(), + layout.toString(), + passthroughCodecs.isEmpty() ? "none" : passthroughCodecs, + doAc3Transcoding ? "yes" : "no"); + QLOG_INFO() << qPrintable(audioConfig); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PlayerComponent::updateSubtitleSettings() +{ + QVariant size = SettingsComponent::Get().value(SETTINGS_SECTION_SUBTITLES, "size"); + mpv::qt::set_option_variant(m_mpv, "sub-text-font-size", size); + + QVariant colors_string = SettingsComponent::Get().value(SETTINGS_SECTION_SUBTITLES, "color"); + auto colors = colors_string.toString().split(","); + if (colors.length() == 2) + { + mpv::qt::set_option_variant(m_mpv, "sub-text-color", colors[0]); + mpv::qt::set_option_variant(m_mpv, "sub-text-border-color", colors[1]); + } + + QVariant subpos_string = SettingsComponent::Get().value(SETTINGS_SECTION_SUBTITLES, "placement"); + auto subpos = subpos_string.toString().split(","); + if (subpos.length() == 2) + { + mpv::qt::set_option_variant(m_mpv, "sub-text-align-x", subpos[0]); + mpv::qt::set_option_variant(m_mpv, "sub-text-align-y", subpos[1]); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PlayerComponent::updateVideoSettings() +{ + QVariant sync_mode = SettingsComponent::Get().value(SETTINGS_SECTION_VIDEO, "sync_mode"); + mpv::qt::set_option_variant(m_mpv, "video-sync", sync_mode); + QVariant hardware_decoding = SettingsComponent::Get().value(SETTINGS_SECTION_VIDEO, "hardware_decoding"); + mpv::qt::set_property_variant(m_mpv, "hwdec", hardware_decoding.toBool() ? "auto" : "no"); + QVariant deinterlace = SettingsComponent::Get().value(SETTINGS_SECTION_VIDEO, "deinterlace"); + mpv::qt::set_option_variant(m_mpv, "deinterlace", deinterlace.toBool() ? "yes" : "no"); + +#ifndef TARGET_RPI + double display_fps = DisplayComponent::Get().currentRefreshRate(); + mpv::qt::set_option_variant(m_mpv, "display-fps", display_fps); +#endif + + setAudioDelay(m_playbackAudioDelay); + + QVariant cache = SettingsComponent::Get().value(SETTINGS_SECTION_VIDEO, "cache"); + mpv::qt::set_option_variant(m_mpv, "cache", cache.toInt() * 1024); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void PlayerComponent::userCommand(const QString& command) +{ + QByteArray cmd_utf8 = command.toUtf8(); + mpv_command_string(m_mpv, cmd_utf8.data()); +} + +///////////////////////////////////////////////////////////////////////////////////////// +static QString get_mpv_osd(mpv_handle *ctx, const char *property) +{ + char *s = mpv_get_property_osd_string(ctx, property); + if (!s) + return "-"; + QString r = QString::fromUtf8(s); + mpv_free(s); + return r; +} + +#define MPV_PROPERTY(p) get_mpv_osd(m_mpv, p) + +///////////////////////////////////////////////////////////////////////////////////////// +QString PlayerComponent::videoInformation() const +{ + QString infoStr; + QTextStream info(&infoStr); + + // check if video is playing + if (mpv::qt::get_property_variant(m_mpv, "idle").toBool()) + return ""; + + info << "File:" << endl; + info << "URL: " << MPV_PROPERTY("path") << endl; + info << "Container: " << MPV_PROPERTY("file-format") << endl; + info << endl; + info << "Video:" << endl; + info << "Codec: " << MPV_PROPERTY("video-codec") << endl; + info << "Size: " << MPV_PROPERTY("video-params/dw") << "x" + << MPV_PROPERTY("video-params/dh") << endl; + info << "FPS: " << MPV_PROPERTY("fps") << endl; + info << "Aspect: " << MPV_PROPERTY("video-aspect") << endl; + info << "Bitrate: " << MPV_PROPERTY("video-bitrate") << endl; + info << "Display Sync: " << MPV_PROPERTY("display-sync-active") << endl; + info << "Hardware Decoding: " << MPV_PROPERTY("hwdec-active") + << " (" << MPV_PROPERTY("hwdec-detected") << ")" << endl; + info << endl; + info << "Audio: " << endl; + info << "Codec: " << MPV_PROPERTY("audio-codec") << endl; + info << "Bitrate: " << MPV_PROPERTY("audio-bitrate") << endl; + info << "Channels (input): " << MPV_PROPERTY("audio-params/channels") << endl; + info << "Channels (output): " << MPV_PROPERTY("audio-out-params/hr-channels") + << " (" << MPV_PROPERTY("audio-out-params/channels") << ")" << endl; + info << endl; + info << "Performance: " << endl; + info << "A/V: " << MPV_PROPERTY("avsync") << endl; + info << "Change: " << MPV_PROPERTY("total-avsync-change") << endl; + info << "Dropped frames: " << MPV_PROPERTY("vo-drop-frame-count") << endl; + info << "Missed frames: " << MPV_PROPERTY("vo-missed-frame-count") << endl; + info << endl; + info << "Cache:" << endl; + info << "Seconds: " << MPV_PROPERTY("demuxer-cache-duration") << endl; + info << "Extra readahead: " << MPV_PROPERTY("cache-used") << endl; + info << "Buffering: " << MPV_PROPERTY("cache-buffering-state") << endl; + info << endl; + info << "Misc: " << endl; + info << "Time: " << MPV_PROPERTY("playback-time") << " / " + << MPV_PROPERTY("duration") + << " (" << MPV_PROPERTY("percent-pos") << "%)" << endl; + info << "Pause state: " << MPV_PROPERTY("pause") << " / " + << MPV_PROPERTY("paused-for-cache") << " / " + << MPV_PROPERTY("core-idle") << endl; + info << "Seeking: " << MPV_PROPERTY("seeking") << endl; + info << "Seekable: " << MPV_PROPERTY("seekable") << " / " + << MPV_PROPERTY("partially-seekable") << endl; + + info << flush; + return infoStr; +} diff --git a/src/player/PlayerComponent.h b/src/player/PlayerComponent.h new file mode 100644 index 0000000..5a30da7 --- /dev/null +++ b/src/player/PlayerComponent.h @@ -0,0 +1,166 @@ +#ifndef PLAYERCOMPONENT_H +#define PLAYERCOMPONENT_H + +#include +#include +#include +#include +#include +#include + +#include "ComponentManager.h" + +#include +#include + +void initD3DDevice(void); + +/////////////////////////////////////////////////////////////////////////////////////////////////// +class PlayerComponent : public ComponentBase +{ + Q_OBJECT + DEFINE_SINGLETON(PlayerComponent); + +public: + virtual const char* componentName() { return "player"; } + virtual bool componentExport() { return true; } + virtual bool componentInitialize(); + + explicit PlayerComponent(QObject* parent = 0); + virtual ~PlayerComponent(); + + // Deprecated. Corresponds to stop() + queueMedia(). + Q_INVOKABLE bool load(const QString& url, const QVariantMap& options, const QVariantMap& metadata, const QString& audioStream = QString(), const QString& subtitleStream = QString()); + + // Append a media item to the internal playlist. If nothing is played yet, the + // newly appended item will start playing immediately. + // options: + // - startMilliseconds: start playback at this time (in ms) + // - autoplay: if false, start playback paused; if true, start normally + Q_INVOKABLE void queueMedia(const QString& url, const QVariantMap& options, const QVariantMap &metadata, const QString& audioStream, const QString& subtitleStream); + + // This clears all items queued with queueMedia(). + // It explicitly excludes the currently playing item. The main use of this function + // is updating the next item that should be played (for the purpose of gapless audio). + // If you want to wipe everything, use stop(). + Q_INVOKABLE void clearQueue(); + + Q_INVOKABLE virtual void seekTo(qint64 milliseconds); + + // Stop playback and clear all queued items. + Q_INVOKABLE virtual void stop(); + + Q_INVOKABLE virtual void pause(); + Q_INVOKABLE virtual void play(); + + /* 0-100 volume 0=mute and 100=normal */ + Q_INVOKABLE virtual void setVolume(quint8 volume); + Q_INVOKABLE virtual quint8 volume(); + + // Returns a QVariant of the following format: + // QVariantList (list of audio device entries) + // QVariantMap (an audio device entry) + // "name" -> QString (symbolic name/ID of the device) + // "description" -> QString (human readable description intended for display) + // + Q_INVOKABLE virtual QVariant getAudioDeviceList(); + // Uses the "name" from the device list. + Q_INVOKABLE virtual void setAudioDevice(const QString& name); + + Q_INVOKABLE virtual void setAudioStream(const QString& audioStream); + Q_INVOKABLE virtual void setSubtitleStream(const QString& subtitleStream); + + Q_INVOKABLE virtual void setAudioDelay(qint64 milliseconds); + Q_INVOKABLE virtual void setSubtitleDelay(qint64 milliseconds); + + // If enabled, hide the web view (whether it's OSD or not), and show video + // only. If no video is running, render a black background only. + Q_INVOKABLE virtual void setVideoOnlyMode(bool enable); + + void userCommand(const QString& command); + + const mpv::qt::Handle getMpvHandle() const { return m_mpv; } + + virtual void setWindow(QQuickWindow* window); + + QString videoInformation() const; + + static QStringList AudioCodecsAll() { return { "ac3", "dts", "eac3", "dts-hd", "truehd" }; }; + static QStringList AudioCodecsSPDIF() { return { "ac3", "dts" }; }; + +public Q_SLOTS: + void setAudioConfiguration(); + void updateAudioDeviceList(); + void updateSubtitleSettings(); + void updateVideoSettings(); + +private Q_SLOTS: + void handleMpvEvents(); + void onRestoreDisplay(); + void onRefreshRateChange(); + void onReloadAudio(); + +Q_SIGNALS: + void playing(const QString& url); + void buffering(float); + // playback has stopped due to a stop() or loadMedia() request + void stopped(const QString& url); + // playback has stopped because the current media was fully played + void finished(const QString& url); + // playback has stopped due to any reason - this always happens if the + // playing() signal was emitted + void playbackEnded(const QString& url); + // emitted if playback has ended, and no more items are queued for playback + void playbackAllDone(); + // emitted after playing(), and as soon as the the media is fully loaded, and + // playback starts normally + void playbackStarting(); + void paused(bool paused); + void windowVisible(bool visible); + // emitted as soon as the duration of the current file is known + void updateDuration(qint64 milliseconds); + + // an error happened during playback - this implies abort of playback + // the id is the (negative) error number, and the message parameter is a short + // English description of the error (always the same for the same id, no + // further information) + void error(int id, const QString& message); + + // current position in ms should be triggered 2 times a second + // when position updates + void positionUpdate(quint64); + + void onMpvEvents(); + +private: + // this is the function actually implemented in the backends. the variantmap contains + // a few known keys: + // * subtitleStreamIndex + // * subtitleStreamIdentifier + // * audioStreamIndex + // * audioStreamIdentifier + // * viewOffset + // + void loadWithOptions(const QVariantMap& options); + void setRpiWindow(QQuickWindow* window); + void setQtQuickWindow(QQuickWindow* window); + void handleMpvEvent(mpv_event *event); + // Potentially switch the display refresh rate, and return true if the refresh rate + // was actually changed. + bool switchDisplayFrameRate(); + void checkCurrentAudioDevice(const QSet& old_devs, const QSet& new_devs); + + mpv::qt::Handle m_mpv; + + double m_lastPositionUpdate; + qint64 m_playbackAudioDelay; + QString m_CurrentUrl; + bool m_playbackStartSent; + QQuickWindow* m_window; + float m_mediaFrameRate; + QTimer m_restoreDisplayTimer; + QTimer m_reloadAudioTimer; + QSet m_audioDevices; +}; + +#endif // PLAYERCOMPONENT_H diff --git a/src/player/PlayerQuickItem.cpp b/src/player/PlayerQuickItem.cpp new file mode 100644 index 0000000..71ccc22 --- /dev/null +++ b/src/player/PlayerQuickItem.cpp @@ -0,0 +1,250 @@ +#include "PlayerQuickItem.h" + +#include + +#include + +#include + +#include +#include + +#include "QsLog.h" +#include "utils/Utils.h" + +#ifdef Q_OS_WIN32 + +#include +#include + +typedef IDirect3D9* WINAPI pDirect3DCreate9(UINT); + +static IDirect3DDevice9* d3ddevice; + +// This must be run before the konvergo main window switches to FS mode. +void initD3DDevice(void) +{ + // Boilerplate for creating a "blank" D3D device. + // Most of this is copied from FFmpeg (LGPL). + pDirect3DCreate9 *createD3D = NULL; + HRESULT hr; + D3DPRESENT_PARAMETERS d3dpp = {}; + D3DDISPLAYMODE d3ddm; + UINT adapter = D3DADAPTER_DEFAULT; + + HMODULE d3dlib = LoadLibraryW(L"d3d9.dll"); + if (!d3dlib) { + QLOG_ERROR() << "Failed to load D3D9 library"; + return; + } + + createD3D = (pDirect3DCreate9 *)GetProcAddress(d3dlib, "Direct3DCreate9"); + if (!createD3D) { + QLOG_ERROR() << "Failed to locate Direct3DCreate9"; + return; + } + + IDirect3D9 *d3d9 = createD3D(D3D_SDK_VERSION); + if (!d3d9) { + QLOG_ERROR() << "Failed to create IDirect3D object"; + return; + } + + IDirect3D9_GetAdapterDisplayMode(d3d9, adapter, &d3ddm); + d3dpp.Windowed = TRUE; + d3dpp.BackBufferWidth = 640; + d3dpp.BackBufferHeight = 480; + d3dpp.BackBufferCount = 0; + d3dpp.BackBufferFormat = d3ddm.Format; + d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; + d3dpp.Flags = D3DPRESENTFLAG_VIDEO; + + hr = IDirect3D9_CreateDevice(d3d9, adapter, D3DDEVTYPE_HAL, GetShellWindow(), + D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE, + &d3dpp, &d3ddevice); + if (FAILED(hr)) { + QLOG_ERROR() << "Failed to create Direct3D device"; + return; + } + + QLOG_INFO() << "Successfully created a Direct3D device"; +}; + +// Special libmpv-specific pseudo extension for better behavior with OpenGL +// fullscreen modes. This is needed with some drivers which do not allow the +// libmpv DXVA code to create a new D3D device. +static void* __stdcall MPGetD3DInterface(const char* name) +{ + QLOG_INFO() << "Asking for " << qPrintable(QString::fromUtf8((name))); + if (strcmp(name, "IDirect3DDevice9") == 0) + { + QLOG_INFO() << "Returning device " << (void *)d3ddevice; + IDirect3DDevice9_AddRef(d3ddevice); + return (void *)d3ddevice; + } + return NULL; +} +#endif + +/////////////////////////////////////////////////////////////////////////////////////////////////// +static void* get_proc_address(void* ctx, const char* name) +{ + Q_UNUSED(ctx); + + QOpenGLContext* glctx = QOpenGLContext::currentContext(); + if (!glctx) + return NULL; + + void *res = (void *)glctx->getProcAddress(QByteArray(name)); +#ifdef Q_OS_WIN32 + if (strcmp(name, "glMPGetD3DInterface") == 0) + { + return (void *)&MPGetD3DInterface; + } + // wglGetProcAddress(), which is used by Qt, does not always resolve all + // builtin functions with all drivers (only extensions). Qt compensates this + // for a degree, but does this only for functions Qt happens to need. So + // we need our own falback as well. + // Warning: this might break if using ANGLE. + if (!res) + { + HMODULE handle = LoadLibrary("opengl32.dll"); + if (handle) + { + res = (void *)GetProcAddress(handle, name); + FreeLibrary(handle); + } + } +#endif + return res; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +PlayerRenderer::PlayerRenderer(mpv::qt::Handle mpv, QQuickWindow* window) +: m_mpv(mpv), m_mpvGL(0), m_window(window), m_size() +{ + m_mpvGL = (mpv_opengl_cb_context *)mpv_get_sub_api(m_mpv, MPV_SUB_API_OPENGL_CB); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool PlayerRenderer::init() +{ + const char *extensions = ""; +#ifdef Q_OS_WIN32 + // For the custom IDirect3DDevice9 hack above. + extensions = "GL_MP_D3D_interfaces"; +#endif + return mpv_opengl_cb_init_gl(m_mpvGL, extensions, get_proc_address, NULL) >= 0; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +PlayerRenderer::~PlayerRenderer() +{ + // Keep in mind that the m_mpv handle must be held until this is done. + if (m_mpvGL) + mpv_opengl_cb_uninit_gl(m_mpvGL); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PlayerRenderer::render() +{ + int fbo = m_window->renderTargetId(); + + m_window->resetOpenGLState(); + + // The negative height signals to mpv that the video should be flipped + // (according to the flipped OpenGL coordinate system). + mpv_opengl_cb_draw(m_mpvGL, fbo, m_size.width(), -m_size.height()); + + m_window->resetOpenGLState(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PlayerRenderer::swap() +{ + mpv_opengl_cb_report_flip(m_mpvGL, 0); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +PlayerQuickItem::PlayerQuickItem(QQuickItem* parent) +: QQuickItem(parent), m_mpvGL(NULL), m_renderer(NULL) +{ + connect(this, &QQuickItem::windowChanged, this, &PlayerQuickItem::onWindowChanged, Qt::DirectConnection); + connect(this, &PlayerQuickItem::onFatalError, this, &PlayerQuickItem::onHandleFatalError, Qt::QueuedConnection); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +PlayerQuickItem::~PlayerQuickItem() +{ + if (m_mpvGL) + mpv_opengl_cb_set_update_callback(m_mpvGL, NULL, NULL); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PlayerQuickItem::onWindowChanged(QQuickWindow* win) +{ + if (win) + { + connect(win, &QQuickWindow::beforeSynchronizing, this, &PlayerQuickItem::onSynchronize, Qt::DirectConnection); + connect(win, &QQuickWindow::sceneGraphInvalidated, this, &PlayerQuickItem::onInvalidate, Qt::DirectConnection); + connect(this, &PlayerQuickItem::onUpdate, win, &QQuickWindow::update, Qt::QueuedConnection); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PlayerQuickItem::onHandleFatalError(QString message) +{ + throw FatalException(message); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PlayerQuickItem::onSynchronize() +{ + if (!m_renderer && m_mpv) + { + m_renderer = new PlayerRenderer(m_mpv, window()); + if (!m_renderer->init()) + { + delete m_renderer; + emit onFatalError(tr("Could not initialize OpenGL.")); + return; + } + connect(window(), &QQuickWindow::beforeRendering, m_renderer, &PlayerRenderer::render, Qt::DirectConnection); + connect(window(), &QQuickWindow::frameSwapped, m_renderer, &PlayerRenderer::swap, Qt::DirectConnection); + window()->setPersistentOpenGLContext(true); + window()->setPersistentSceneGraph(true); + window()->setClearBeforeRendering(false); + } + if (m_renderer) + m_renderer->m_size = window()->size() * window()->devicePixelRatio(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PlayerQuickItem::onInvalidate() +{ + if (m_renderer) + delete m_renderer; + m_renderer = NULL; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PlayerQuickItem::on_update(void *ctx) +{ + PlayerQuickItem *self = (PlayerQuickItem *)ctx; + emit self->onUpdate(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PlayerQuickItem::initMpv(PlayerComponent* player) +{ + m_mpv = player->getMpvHandle(); + + m_mpvGL = (mpv_opengl_cb_context *)mpv_get_sub_api(m_mpv, MPV_SUB_API_OPENGL_CB); + if (!m_mpvGL) + throw FatalException(tr("OpenGL not enabled in libmpv.")); + + mpv_opengl_cb_set_update_callback(m_mpvGL, on_update, (void *)this); + + connect(player, &PlayerComponent::windowVisible, this, &QQuickItem::setVisible); + window()->update(); +} diff --git a/src/player/PlayerQuickItem.h b/src/player/PlayerQuickItem.h new file mode 100644 index 0000000..0fa10cf --- /dev/null +++ b/src/player/PlayerQuickItem.h @@ -0,0 +1,57 @@ +#ifndef PLAYERQUICKITEM_H +#define PLAYERQUICKITEM_H + +#include +#include + +#include +#include +#include + +#include "PlayerComponent.h" + +class PlayerRenderer : public QObject +{ + Q_OBJECT + friend class PlayerQuickItem; + + PlayerRenderer(mpv::qt::Handle mpv, QQuickWindow* window); + bool init(); + virtual ~PlayerRenderer(); + void render(); + void swap(); + + mpv::qt::Handle m_mpv; + mpv_opengl_cb_context* m_mpvGL; + QQuickWindow* m_window; + QSize m_size; +}; + +class PlayerQuickItem : public QQuickItem +{ + Q_OBJECT + friend class PlayerRenderer; + +public: + PlayerQuickItem(QQuickItem* parent = 0); + virtual ~PlayerQuickItem(); + void initMpv(PlayerComponent* player); + +signals: + void onUpdate(); + void onFatalError(QString message); + +private slots: + void onWindowChanged(QQuickWindow* win); + void onSynchronize(); + void onInvalidate(); + void onHandleFatalError(QString message); + +private: + static void on_update(void *ctx); + mpv::qt::Handle m_mpv; + mpv_opengl_cb_context* m_mpvGL; + PlayerRenderer* m_renderer; +}; + +#endif diff --git a/src/plugins/RPI_jpeg/QRPIJpegHandler.cpp b/src/plugins/RPI_jpeg/QRPIJpegHandler.cpp new file mode 100644 index 0000000..5080f40 --- /dev/null +++ b/src/plugins/RPI_jpeg/QRPIJpegHandler.cpp @@ -0,0 +1,217 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "QRPIJpegHandler.h" + +QT_BEGIN_NAMESPACE + +/////////////////////////////////////////////////////////////////////////////////////////////////// +QRPIJpegHandler::QRPIJpegHandler() : m_quality(100), m_decoder(NULL) +{ + setFormat("jpeg"); + + // initialize decoder parameters + memset(&m_dec_request, 0, sizeof(m_dec_request)); + m_dec_request.input = encodedInBuf; + m_dec_request.output = decodedBuf; + m_dec_request.output_handle = 0; + m_dec_request.output_alloc_size = MAX_DECODED; + m_dec_request.pixel_format = PIXEL_FORMAT_RGBA; + + BRCMJPEG_STATUS_T status = brcmjpeg_create(BRCMJPEG_TYPE_DECODER, &m_decoder); + if (status != BRCMJPEG_SUCCESS) + { + qWarning() << "QRPIJpegHandler : could not create decoder"; + brcmjpeg_release(m_decoder); + m_decoder = NULL; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +QRPIJpegHandler::~QRPIJpegHandler() +{ + if (m_decoder) + brcmjpeg_release(m_decoder); + + m_decoder = NULL; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool QRPIJpegHandler::canRead() const +{ + return canRead(device()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool QRPIJpegHandler::canRead(QIODevice* device) +{ + if (!device) + { + qWarning("QRPIJpegHandler::canRead() called with no device"); + return false; + } + + unsigned char buffer[2]; + if (device->peek((char*)buffer, 2) != 2) + return false; + + return uchar(buffer[0]) == 0xff && uchar(buffer[1]) == 0xd8; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool QRPIJpegHandler::read(QImage* image) +{ + if (device()) + { + + m_dec_request.buffer_width = 0; + m_dec_request.buffer_height = 0; + m_dec_request.input_size = device()->read((char*)encodedInBuf, sizeof(encodedInBuf)); + + QElapsedTimer timer; + timer.start(); + + BRCMJPEG_STATUS_T status = brcmjpeg_process(m_decoder, &m_dec_request); + + if (status == BRCMJPEG_SUCCESS) + { + + for (int i = 0; i < m_dec_request.height; i++) + { + memcpy(image->scanLine(i), decodedBuf + m_dec_request.buffer_width * i * 4, + m_dec_request.buffer_width * 4); + } + + //*image = decodedImage; + qDebug() << QDateTime::currentDateTime().toMSecsSinceEpoch() + << "QRPIJpegHandler : decoded a" + << m_dec_request.width << "x" << m_dec_request.height + << "image in" << timer.elapsed() << "ms"; + + return true; + } + else + { + qWarning() << "QRPIJpegHandler : Decoding failed with status" << status; + return false; + } + + } + else + { + qDebug() << "QRPIJpegHandler : read() was called with NULL device"; + return false; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool QRPIJpegHandler::write(const QImage& image) +{ + Q_UNUSED(image); + qWarning() << "QRPIJpegHandler : call to unsupported write()"; + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool QRPIJpegHandler::supportsOption(ImageOption option) const +{ + return option == Quality || option == Size || option == ImageFormat; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool QRPIJpegHandler::readJpegSize(QIODevice* device, QSize& size) const +{ + if (canRead(device)) + { + JFIFHEAD header; + int bytesRead = device->peek((char*)&header, sizeof(header)); + if (bytesRead >= (sizeof(header) - JFIF_DATA_SIZE)) + { + BYTE* dataptr = header.data; + + while (dataptr < ((BYTE*)&header + bytesRead)) + { + if (dataptr[0] != 0xFF) + { + qWarning() << "readJpegSize : got wrong marker " << dataptr[0]; + return false; + } + + // we look for size block marker + if (dataptr[1] == 0xC0 && dataptr[2] == 0x0) + { + size.setWidth(dataptr[8] + dataptr[7] * 256); + size.setHeight(dataptr[6] + dataptr[5] * 256); + return true; + } + + dataptr += dataptr[3] + (dataptr[2] * 256) + 2; + } + } + else + { + qWarning() << "readJpegSize : could not read " << sizeof(header) << "bytes, read " << bytesRead; + return false; + } + } + else + { + qWarning() << "readJpegSize : device " << device << "can't be read!" ; + return false; + } + + qWarning() << "readJpegSize : could not find the proper size marker"; + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +QVariant QRPIJpegHandler::option(ImageOption option) const +{ + switch (option) + { + case Size: + { + QSize size(0, 0); + readJpegSize(device(), size); + return QVariant(size); + break; + } + + case ImageFormat: + { + return QImage::Format_RGBA8888_Premultiplied; + } + + default: + { + qWarning() << "QRPIJpegHandler : requesting unsupported option" << option; + return QVariant(); + break; + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void QRPIJpegHandler::setOption(ImageOption option, const QVariant& value) +{ + switch (option) + { + case Quality: + m_quality = value.toInt(); + break; + + default: + qWarning() << "QRPIJpegHandler : setOption unsupported option" << option << "to" << value; + break; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +QByteArray QRPIJpegHandler::name() const { return "RPIjpeg"; } + +QT_END_NAMESPACE diff --git a/src/plugins/RPI_jpeg/QRPIJpegHandler.h b/src/plugins/RPI_jpeg/QRPIJpegHandler.h new file mode 100644 index 0000000..5409625 --- /dev/null +++ b/src/plugins/RPI_jpeg/QRPIJpegHandler.h @@ -0,0 +1,70 @@ +#ifndef QRPIJPEGHANDLER_H +#define QRPIJPEGHANDLER_H + +#include +#include +#include +#include "brcmjpeg.h" + +QT_BEGIN_NAMESPACE + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// JPEG header struct +#define JFIF_DATA_SIZE 4096 // additionnal data size for header parsing + +typedef unsigned char BYTE; + +typedef struct _JFIFHeader +{ + BYTE SOI[2]; /* 00h Start of Image Marker */ + BYTE APP0[2]; /* 02h Application Use Marker */ + BYTE Length[2]; /* 04h Length of APP0 Field */ + BYTE Identifier[5]; /* 06h "JFIF" (zero terminated) Id String */ + BYTE Version[2]; /* 07h JFIF Format Revision */ + BYTE Units; /* 09h Units used for Resolution */ + BYTE Xdensity[2]; /* 0Ah Horizontal Resolution */ + BYTE Ydensity[2]; /* 0Ch Vertical Resolution */ + BYTE XThumbnail; /* 0Eh Horizontal Pixel Count */ + BYTE YThumbnail; /* 0Fh Vertical Pixel Count */ + BYTE data[JFIF_DATA_SIZE]; +} JFIFHEAD; + +// Hardware decoder buffers +#define MAX_WIDTH 5000 +#define MAX_HEIGHT 5000 +#define MAX_ENCODED (15*1024*1024) +#define MAX_DECODED (MAX_WIDTH*MAX_HEIGHT*2) + +static BYTE encodedInBuf[MAX_ENCODED]; +static BYTE decodedBuf[MAX_DECODED]; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// RPI JPEG decoding handling class +class QRPIJpegHandler : public QImageIOHandler +{ +public: + QRPIJpegHandler(); + ~QRPIJpegHandler(); + + bool canRead() const Q_DECL_OVERRIDE; + bool read(QImage *image) Q_DECL_OVERRIDE; + bool write(const QImage &image) Q_DECL_OVERRIDE; + + QByteArray name() const Q_DECL_OVERRIDE; + + static bool canRead(QIODevice *device); + + QVariant option(ImageOption option) const Q_DECL_OVERRIDE; + void setOption(ImageOption option, const QVariant &value) Q_DECL_OVERRIDE; + bool supportsOption(ImageOption option) const Q_DECL_OVERRIDE; + +private: + bool readJpegSize(QIODevice *device, QSize &size) const; + int m_quality; + BRCMJPEG_REQUEST_T m_dec_request; + BRCMJPEG_T *m_decoder; +}; + +QT_END_NAMESPACE + +#endif // QRPIJPEGHANDLER_H diff --git a/src/plugins/RPI_jpeg/QRPIJpegPlugin.cpp b/src/plugins/RPI_jpeg/QRPIJpegPlugin.cpp new file mode 100644 index 0000000..6a27fdb --- /dev/null +++ b/src/plugins/RPI_jpeg/QRPIJpegPlugin.cpp @@ -0,0 +1,36 @@ +#include +#include "QRPIJpegPlugin.h" +#include "QRPIJpegHandler.h" + + +QT_BEGIN_NAMESPACE + +/////////////////////////////////////////////////////////////////////////////////////////////////// +QImageIOPlugin::Capabilities QRPIJpegPlugin::capabilities(QIODevice *device, const QByteArray &format) const +{ + if (format == "jpeg" || format == "jpg") + return Capabilities(CanRead); + if (!format.isEmpty()) + return 0; + if (!device->isOpen()) + return 0; + + Capabilities cap; + if (device->isReadable() && QRPIJpegHandler::canRead(device)) + cap |= CanRead; + if (device->isWritable()) + cap |= CanWrite; + + return cap; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +QImageIOHandler *QRPIJpegPlugin::create(QIODevice *device, const QByteArray &format) const +{ + QImageIOHandler *handler = new QRPIJpegHandler(); + handler->setDevice(device); + handler->setFormat(format); + return handler; +} + +QT_END_NAMESPACE diff --git a/src/plugins/RPI_jpeg/QRPIJpegPlugin.h b/src/plugins/RPI_jpeg/QRPIJpegPlugin.h new file mode 100644 index 0000000..0119e35 --- /dev/null +++ b/src/plugins/RPI_jpeg/QRPIJpegPlugin.h @@ -0,0 +1,15 @@ +#include +#include + +QT_BEGIN_NAMESPACE + +class QRPIJpegPlugin : public QImageIOPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QImageIOHandlerFactoryInterface" FILE "jpeg.json") +public: + Capabilities capabilities(QIODevice *device, const QByteArray &format) const Q_DECL_OVERRIDE; + QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const Q_DECL_OVERRIDE; +}; + +QT_END_NAMESPACE diff --git a/src/plugins/RPI_jpeg/RPI_jpeg.pro b/src/plugins/RPI_jpeg/RPI_jpeg.pro new file mode 100644 index 0000000..f3bc45f --- /dev/null +++ b/src/plugins/RPI_jpeg/RPI_jpeg.pro @@ -0,0 +1,30 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2015-05-29T20:58:57 +# +#------------------------------------------------- + +TARGET = RPI_jpeg + + +QT += core + +TEMPLATE = lib + +LIBS += -lmmal_core -lmmal_util -lmmal_vc_client + +DEFINES += RPI_JPEG_LIBRARY + +SOURCES += \ + QRPIJpegPlugin.cpp \ + QRPIJpegHandler.cpp \ + brcmjpeg.cpp + +target.path = $$[QT_INSTALL_PLUGINS]/imageformats +INSTALLS += target + + +HEADERS += \ + QRPIJpegPlugin.h \ + QRPIJpegHandler.h \ + brcmjpeg.h diff --git a/src/plugins/RPI_jpeg/brcmjpeg.cpp b/src/plugins/RPI_jpeg/brcmjpeg.cpp new file mode 100644 index 0000000..830d04d --- /dev/null +++ b/src/plugins/RPI_jpeg/brcmjpeg.cpp @@ -0,0 +1,914 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** \file + * Jpeg encoder and decoder library using the hardware jpeg codec + */ + +#include "interface/mmal/mmal.h" +#include "interface/mmal/util/mmal_component_wrapper.h" +#include "interface/mmal/util/mmal_util_params.h" +#include "interface/mmal/mmal_logging.h" +#include "brcmjpeg.h" + +/******************************************************************************* +* Defines +*******************************************************************************/ +#define MMAL_COMPONENT_IMAGE_DECODE "vc.aggregator.pipeline:ril.image_decode:video_convert" +#define MMAL_COMPONENT_IMAGE_ENCODE "vc.ril.image_encode" + +#define ENABLE_SLICE_MODE 0 + +#define CHECK_MMAL_STATUS(status, jerr, msg, ...) \ + if (status != MMAL_SUCCESS) {LOG_ERROR(msg, ## __VA_ARGS__); \ + err = BRCMJPEG_ERROR_##jerr; goto error;} + +/******************************************************************************* +* Type definitions +*******************************************************************************/ +struct BRCMJPEG_T +{ + BRCMJPEG_TYPE_T type; + unsigned int ref_count; + unsigned int init; + + MMAL_WRAPPER_T *mmal; + unsigned int slice_height; + + VCOS_MUTEX_T lock; + VCOS_MUTEX_T process_lock; + VCOS_SEMAPHORE_T sema; +}; + +/******************************************************************************* +* Local prototypes +*******************************************************************************/ +static BRCMJPEG_STATUS_T brcmjpeg_init_encoder(BRCMJPEG_T *); +static BRCMJPEG_STATUS_T brcmjpeg_init_decoder(BRCMJPEG_T *); +static BRCMJPEG_STATUS_T brcmjpeg_configure_encoder(BRCMJPEG_T *, BRCMJPEG_REQUEST_T *); +static BRCMJPEG_STATUS_T brcmjpeg_configure_decoder(BRCMJPEG_T *, BRCMJPEG_REQUEST_T *); +static BRCMJPEG_STATUS_T brcmjpeg_encode(BRCMJPEG_T *, BRCMJPEG_REQUEST_T *); +static BRCMJPEG_STATUS_T brcmjpeg_decode(BRCMJPEG_T *, BRCMJPEG_REQUEST_T *); +static void brcmjpeg_destroy(BRCMJPEG_T *); + +static MMAL_FOURCC_T brcmjpeg_pixfmt_to_encoding(BRCMJPEG_PIXEL_FORMAT_T); +static unsigned int brcmjpeg_copy_pixels(uint8_t *out, unsigned int out_size, + const uint8_t *in, unsigned int in_size, BRCMJPEG_PIXEL_FORMAT_T fmt, + unsigned int out_width, unsigned int out_height, + unsigned int in_width, unsigned int in_height, + unsigned int line_offset, unsigned int convert_from); + +static BRCMJPEG_T *brcmjpeg_encoder = NULL; +static BRCMJPEG_T *brcmjpeg_decoder = NULL; + +/******************************************************************************* +* Platform specific code +*******************************************************************************/ +static VCOS_ONCE_T once = VCOS_ONCE_INIT; +static VCOS_MUTEX_T brcmjpeg_lock; + +static void brcmjpeg_init_once(void) +{ + vcos_mutex_create(&brcmjpeg_lock, VCOS_FUNCTION); +} + +#define LOCK() vcos_mutex_lock(&brcmjpeg_lock) +#define UNLOCK() vcos_mutex_unlock(&brcmjpeg_lock) +#define LOCK_COMP(ctx) vcos_mutex_lock(&(ctx)->lock) +#define UNLOCK_COMP(ctx) vcos_mutex_unlock(&(ctx)->lock) +#define LOCK_PROCESS(ctx) vcos_mutex_lock(&(ctx)->process_lock) +#define UNLOCK_PROCESS(ctx) vcos_mutex_unlock(&(ctx)->process_lock) +#define WAIT(ctx) vcos_semaphore_wait(&(ctx)->sema) +#define SIGNAL(ctx) vcos_semaphore_post(&(ctx)->sema) + +/******************************************************************************* +* Implementation +*******************************************************************************/ + +BRCMJPEG_STATUS_T brcmjpeg_create(BRCMJPEG_TYPE_T type, BRCMJPEG_T **ctx) +{ + BRCMJPEG_STATUS_T status = BRCMJPEG_SUCCESS; + BRCMJPEG_T **comp; + + if (type == BRCMJPEG_TYPE_ENCODER) + comp = &brcmjpeg_encoder; + else + comp = &brcmjpeg_decoder; + + vcos_once(&once, brcmjpeg_init_once); + LOCK(); + if (!*comp) + { + int init1, init2, init3; + *comp = (BRCMJPEG_T*)calloc(sizeof(BRCMJPEG_T), 1); + if (!*comp) + { + UNLOCK(); + return BRCMJPEG_ERROR_NOMEM; + } + (*comp)->type = type; + init1 = vcos_mutex_create(&(*comp)->lock, "brcmjpeg lock") != VCOS_SUCCESS; + init2 = vcos_mutex_create(&(*comp)->process_lock, "brcmjpeg process lock") != VCOS_SUCCESS; + init3 = vcos_semaphore_create(&(*comp)->sema, "brcmjpeg sema", 0) != VCOS_SUCCESS; + if (init1 | init2 | init3) + { + if (init1) vcos_mutex_delete(&(*comp)->lock); + if (init2) vcos_mutex_delete(&(*comp)->process_lock); + if (init3) vcos_semaphore_delete(&(*comp)->sema); + free(comp); + UNLOCK(); + return BRCMJPEG_ERROR_NOMEM; + } + } + (*comp)->ref_count++; + UNLOCK(); + + LOCK_COMP(*comp); + if (!(*comp)->init) + { + if (type == BRCMJPEG_TYPE_ENCODER) + status = brcmjpeg_init_encoder(*comp); + else + status = brcmjpeg_init_decoder(*comp); + + (*comp)->init = status == BRCMJPEG_SUCCESS; + } + UNLOCK_COMP(*comp); + + if (status != BRCMJPEG_SUCCESS) + brcmjpeg_release(*comp); + + *ctx = *comp; + return status; +} + +void brcmjpeg_acquire(BRCMJPEG_T *ctx) +{ + LOCK_COMP(ctx); + ctx->ref_count++; + UNLOCK_COMP(ctx); +} + +void brcmjpeg_release(BRCMJPEG_T *ctx) +{ + LOCK_COMP(ctx); + if (--ctx->ref_count) + { + UNLOCK_COMP(ctx); + return; + } + + LOCK(); + if (ctx->type == BRCMJPEG_TYPE_ENCODER) + brcmjpeg_encoder = NULL; + else + brcmjpeg_decoder = NULL; + UNLOCK(); + UNLOCK_COMP(ctx); + + brcmjpeg_destroy(ctx); + return; +} + +BRCMJPEG_STATUS_T brcmjpeg_process(BRCMJPEG_T *ctx, BRCMJPEG_REQUEST_T *req) +{ + BRCMJPEG_STATUS_T status; + + /* Sanity check */ + if ((req->input && req->input_handle) || + (req->output && req->output_handle)) + { + LOG_ERROR("buffer pointer and handle both set (%p/%u %p/%u)", + req->input, req->input_handle, req->output, req->output_handle); + return BRCMJPEG_ERROR_REQUEST; + } + + LOCK_PROCESS(ctx); + if (ctx->type == BRCMJPEG_TYPE_ENCODER) + status = brcmjpeg_encode(ctx, req); + else + status = brcmjpeg_decode(ctx, req); + UNLOCK_PROCESS(ctx); + + return status; +} + +static void brcmjpeg_destroy(BRCMJPEG_T *ctx) +{ + if (ctx->mmal) + mmal_wrapper_destroy(ctx->mmal); + vcos_mutex_delete(&ctx->lock); + vcos_mutex_delete(&ctx->process_lock); + vcos_semaphore_delete(&ctx->sema); + free(ctx); +} + +static void brcmjpeg_mmal_cb(MMAL_WRAPPER_T *wrapper) +{ + BRCMJPEG_T *ctx = (BRCMJPEG_T*)wrapper->user_data; + SIGNAL(ctx); +} + +static BRCMJPEG_STATUS_T brcmjpeg_init_encoder(BRCMJPEG_T *ctx) +{ + MMAL_STATUS_T status; + BRCMJPEG_STATUS_T err = BRCMJPEG_SUCCESS; + + /* Create encoder component */ + status = mmal_wrapper_create(&ctx->mmal, MMAL_COMPONENT_IMAGE_ENCODE); + CHECK_MMAL_STATUS(status, INIT, "failed to create encoder"); + ctx->mmal->user_data = ctx; + ctx->mmal->callback = brcmjpeg_mmal_cb; + + /* Configure things that won't change from encode to encode */ + mmal_port_parameter_set_boolean(ctx->mmal->control, + MMAL_PARAMETER_EXIF_DISABLE, MMAL_TRUE); + + ctx->mmal->output[0]->format->encoding = MMAL_ENCODING_JPEG; + status = mmal_port_format_commit(ctx->mmal->output[0]); + CHECK_MMAL_STATUS(status, INIT, "failed to commit output port format"); + + ctx->mmal->output[0]->buffer_size = ctx->mmal->output[0]->buffer_size_min; + ctx->mmal->output[0]->buffer_num = 3; + status = mmal_wrapper_port_enable(ctx->mmal->output[0], 0); + CHECK_MMAL_STATUS(status, INIT, "failed to enable output port"); + + LOG_DEBUG("encoder initialised (output chunk size %i)", + ctx->mmal->output[0]->buffer_size); + return BRCMJPEG_SUCCESS; + + error: + return err; +} + +static BRCMJPEG_STATUS_T brcmjpeg_init_decoder(BRCMJPEG_T *ctx) +{ + MMAL_STATUS_T status; + BRCMJPEG_STATUS_T err = BRCMJPEG_SUCCESS; + + /* Create decoder component */ + status = mmal_wrapper_create(&ctx->mmal, MMAL_COMPONENT_IMAGE_DECODE); + CHECK_MMAL_STATUS(status, INIT, "failed to create decoder"); + ctx->mmal->user_data = ctx; + ctx->mmal->callback = brcmjpeg_mmal_cb; + + /* Configure things that won't change from decode to decode */ + ctx->mmal->input[0]->format->encoding = MMAL_ENCODING_JPEG; + status = mmal_port_format_commit(ctx->mmal->input[0]); + CHECK_MMAL_STATUS(status, INIT, "failed to commit input port format"); + + ctx->mmal->input[0]->buffer_size = ctx->mmal->input[0]->buffer_size_min; + ctx->mmal->input[0]->buffer_num = 3; + status = mmal_wrapper_port_enable(ctx->mmal->input[0], 0); + CHECK_MMAL_STATUS(status, INIT, "failed to enable input port"); + + LOG_DEBUG("decoder initialised (input chunk size %i)", + ctx->mmal->input[0]->buffer_size); + return BRCMJPEG_SUCCESS; + + error: + return BRCMJPEG_ERROR_INIT; +} + +/* Configuration which needs to be done on a per encode basis */ +static BRCMJPEG_STATUS_T brcmjpeg_configure_encoder(BRCMJPEG_T *ctx, + BRCMJPEG_REQUEST_T *req) +{ + MMAL_STATUS_T status = MMAL_SUCCESS; + MMAL_FOURCC_T encoding = brcmjpeg_pixfmt_to_encoding(req->pixel_format); + MMAL_PORT_T *port_in; + BRCMJPEG_STATUS_T err = BRCMJPEG_SUCCESS; + MMAL_BOOL_T slice_mode = MMAL_FALSE; + + if (encoding == MMAL_ENCODING_UNKNOWN) + status = MMAL_EINVAL; + CHECK_MMAL_STATUS(status, INPUT_FORMAT, "format not supported (%i)", + req->pixel_format); + + if (!req->buffer_width) + req->buffer_width = req->width; + if (!req->buffer_height) + req->buffer_height = req->height; + if (req->buffer_width < req->width || req->buffer_height < req->height) + status = MMAL_EINVAL; + CHECK_MMAL_STATUS(status, INPUT_FORMAT, "invalid buffer width/height " + "(%i<=%i %i<=%i)", req->buffer_width, req->width, req->buffer_height, + req->height); + + ctx->slice_height = 0; + ctx->mmal->status = MMAL_SUCCESS; + port_in = ctx->mmal->input[0]; + + /* The input port needs to be re-configured to take into account + * the properties of the new frame to encode */ + if (port_in->is_enabled) + { + status = mmal_wrapper_port_disable(port_in); + CHECK_MMAL_STATUS(status, EXECUTE, "failed to disable input port"); + } + + port_in->format->encoding = encoding; + port_in->format->es->video.width = + port_in->format->es->video.crop.width = req->width; + port_in->format->es->video.height = + port_in->format->es->video.crop.height = req->height; + port_in->buffer_num = 1; + + if (!req->input_handle && + (port_in->format->encoding == MMAL_ENCODING_I420 || + port_in->format->encoding == MMAL_ENCODING_I422)) + { + if (port_in->format->encoding == MMAL_ENCODING_I420) + port_in->format->encoding = MMAL_ENCODING_I420_SLICE; + else if (port_in->format->encoding == MMAL_ENCODING_I422) + port_in->format->encoding = MMAL_ENCODING_I422_SLICE; + slice_mode = MMAL_TRUE; + port_in->buffer_num = 3; + } + + status = mmal_port_format_commit(port_in); + CHECK_MMAL_STATUS(status, INPUT_FORMAT, "failed to commit input port format"); + + ctx->slice_height = slice_mode ? 16 : port_in->format->es->video.height; + port_in->buffer_size = port_in->buffer_size_min; + + if (req->input_handle) + status = mmal_wrapper_port_enable(port_in, MMAL_WRAPPER_FLAG_PAYLOAD_USE_SHARED_MEMORY); + else + status = mmal_wrapper_port_enable(port_in, MMAL_WRAPPER_FLAG_PAYLOAD_ALLOCATE); + CHECK_MMAL_STATUS(status, EXECUTE, "failed to enable input port"); + + mmal_port_parameter_set_uint32(ctx->mmal->output[0], + MMAL_PARAMETER_JPEG_Q_FACTOR, req->quality); + + if (!ctx->mmal->output[0]->is_enabled) + { + status = mmal_wrapper_port_enable(ctx->mmal->output[0], 0); + CHECK_MMAL_STATUS(status, EXECUTE, "failed to enable output port"); + } + + LOG_DEBUG("encoder configured (%4.4s:%ux%u|%ux%u slice: %u)", + (char *)&port_in->format->encoding, + port_in->format->es->video.crop.width, port_in->format->es->video.crop.height, + port_in->format->es->video.width, port_in->format->es->video.height, + ctx->slice_height); + return BRCMJPEG_SUCCESS; + + error: + return err; +} + +/* Configuration which needs to be done on a per decode basis */ +static BRCMJPEG_STATUS_T brcmjpeg_configure_decoder(BRCMJPEG_T *ctx, + BRCMJPEG_REQUEST_T *req) +{ + MMAL_STATUS_T status = MMAL_SUCCESS; + MMAL_FOURCC_T encoding = brcmjpeg_pixfmt_to_encoding(req->pixel_format); + MMAL_PORT_T *port_out; + BRCMJPEG_STATUS_T err = BRCMJPEG_SUCCESS; + + if (encoding != MMAL_ENCODING_I420 && + encoding != MMAL_ENCODING_I422 && + encoding != MMAL_ENCODING_RGBA) + status = MMAL_EINVAL; + CHECK_MMAL_STATUS(status, OUTPUT_FORMAT, "format not supported"); + + ctx->slice_height = 0; + ctx->mmal->status = MMAL_SUCCESS; + port_out = ctx->mmal->output[0]; + + /* The input port needs to be re-configured to take into account + * the properties of the new frame to decode */ + if (port_out->is_enabled) + { + status = mmal_wrapper_port_disable(port_out); + CHECK_MMAL_STATUS(status, EXECUTE, "failed to disable output port"); + } + + /* We assume that we do not know the format of the new jpeg to be decoded + * and configure the input port for autodetecting the new format */ + port_out->format->encoding = encoding; + port_out->format->es->video.width = + port_out->format->es->video.crop.width = 0; + port_out->format->es->video.height = + port_out->format->es->video.crop.height = 0; + status = mmal_port_format_commit(port_out); + CHECK_MMAL_STATUS(status, OUTPUT_FORMAT, "failed to commit output port format"); + + port_out->buffer_num = 1; + if (req->output_handle) + status = mmal_wrapper_port_enable(port_out, MMAL_WRAPPER_FLAG_PAYLOAD_USE_SHARED_MEMORY); + else + status = mmal_wrapper_port_enable(port_out, MMAL_WRAPPER_FLAG_PAYLOAD_ALLOCATE); + CHECK_MMAL_STATUS(status, EXECUTE, "failed to enable output port"); + + LOG_DEBUG("decoder configured (%4.4s:%ux%u|%ux%u)", (char *)&port_out->format->encoding, + port_out->format->es->video.crop.width, port_out->format->es->video.crop.height, + port_out->format->es->video.width, port_out->format->es->video.height); + return BRCMJPEG_SUCCESS; + + error: + return err; +} + +static BRCMJPEG_STATUS_T brcmjpeg_encode(BRCMJPEG_T *ctx, + BRCMJPEG_REQUEST_T *je) +{ + BRCMJPEG_STATUS_T err; + MMAL_STATUS_T status = MMAL_SUCCESS; + MMAL_BUFFER_HEADER_T *in, *out; + MMAL_BOOL_T eos = MMAL_FALSE; + const uint8_t *outBuf = je->output; + unsigned int loop = 0, slices = 0, outBufSize = je->output_alloc_size; + MMAL_PORT_T *port_in = ctx->mmal->input[0]; + MMAL_PORT_T *port_out = ctx->mmal->output[0]; + + je->output_size = 0; + err = brcmjpeg_configure_encoder(ctx, je); + if (err != BRCMJPEG_SUCCESS) + return err; + + /* Then we read the encoded data back from the encoder */ + + while (!eos && status == MMAL_SUCCESS) + { + /* send buffers to be filled */ + while (mmal_wrapper_buffer_get_empty(port_out, &out, 0) == MMAL_SUCCESS) + { + out->data = (uint8_t *)outBuf; + out->alloc_size = MMAL_MIN(port_out->buffer_size, outBufSize); + outBufSize -= out->alloc_size; + outBuf += out->alloc_size; + status = mmal_port_send_buffer(port_out, out); + CHECK_MMAL_STATUS(status, EXECUTE, "failed to send buffer"); + } + + /* Send slices to be encoded */ + if (slices * ctx->slice_height < port_in->format->es->video.height && + mmal_wrapper_buffer_get_empty(port_in, &in, 0) == MMAL_SUCCESS) + { + if (je->input_handle) + { + in->data = (uint8_t *)je->input_handle; + in->length = in->alloc_size = je->input_size; + } + else + { + in->length = brcmjpeg_copy_pixels(in->data, in->alloc_size, + je->input, je->input_size, je->pixel_format, + port_in->format->es->video.width, + ctx->slice_height, je->buffer_width, je->buffer_height, + slices * ctx->slice_height, 1); + if (!in->length) + status = MMAL_EINVAL; + CHECK_MMAL_STATUS(status, INPUT_BUFFER, "input buffer too small"); + } + + slices++; + if (slices * ctx->slice_height >= port_in->format->es->video.height) + in->flags = MMAL_BUFFER_HEADER_FLAG_EOS; + status = mmal_port_send_buffer(port_in, in); + CHECK_MMAL_STATUS(status, EXECUTE, "failed to send buffer"); + } + + status = mmal_wrapper_buffer_get_full(port_out, &out, 0); + if (status == MMAL_EAGAIN) + { + status = MMAL_SUCCESS; + WAIT(ctx); + continue; + } + CHECK_MMAL_STATUS(status, EXECUTE, "failed to get full buffer"); + + LOG_DEBUG("received %i bytes", out->length); + je->output_size += out->length; + eos = out->flags & MMAL_BUFFER_HEADER_FLAG_EOS; + + /* Detect when the encoder is running out of space for its output */ + if (++loop >= port_out->buffer_num && !eos && !out->length) + { + LOG_ERROR("no more output space for encoder"); + status = MMAL_EINVAL; + } + + mmal_buffer_header_release(out); + } + + /* Check if buffer was too small */ + CHECK_MMAL_STATUS(status, OUTPUT_BUFFER, "output buffer too small"); + + LOG_DEBUG("encoded W:%ixH:%i:%i (%i bytes) in %i slices", + je->width, je->height, je->pixel_format, je->output_size, slices); + mmal_port_flush(port_out); + return BRCMJPEG_SUCCESS; + + error: + mmal_wrapper_port_disable(port_in); + mmal_wrapper_port_disable(port_out); + return err; +} + +static BRCMJPEG_STATUS_T brcmjpeg_decode(BRCMJPEG_T *ctx, + BRCMJPEG_REQUEST_T *jd) +{ + BRCMJPEG_STATUS_T err; + MMAL_STATUS_T status; + MMAL_BUFFER_HEADER_T *in, *out; + MMAL_BOOL_T eos = MMAL_FALSE; + const uint8_t *inBuf = jd->input; + unsigned int slices = 0, inBufSize = jd->input_size; + MMAL_PORT_T *port_in = ctx->mmal->input[0]; + MMAL_PORT_T *port_out = ctx->mmal->output[0]; + LOG_DEBUG("decode %i bytes", jd->input_size); + + jd->output_size = 0; + err = brcmjpeg_configure_decoder(ctx, jd); + if (err != BRCMJPEG_SUCCESS) + return err; + + while (!eos) + { + /* Send as many chunks of data to decode as we can */ + while (inBufSize) + { + status = mmal_wrapper_buffer_get_empty(port_in, &in, 0); + if (status == MMAL_EAGAIN) + break; + CHECK_MMAL_STATUS(status, EXECUTE, "failed to get empty buffer (%i)", status); + + in->data = (uint8_t *)inBuf; + in->length = MMAL_MIN(port_in->buffer_size, inBufSize); + in->alloc_size = in->length; + inBufSize -= in->length; + inBuf += in->length; + in->flags = inBufSize ? 0 : MMAL_BUFFER_HEADER_FLAG_EOS; + LOG_DEBUG("send decode in (%i bytes)", in->length); + status = mmal_port_send_buffer(port_in, in); + CHECK_MMAL_STATUS(status, EXECUTE, "failed to send input buffer"); + } + + /* Check for decoded data */ + status = mmal_wrapper_buffer_get_full(port_out, &out, 0); + if (status == MMAL_EAGAIN) + { + WAIT(ctx); + continue; + } + CHECK_MMAL_STATUS(status, EXECUTE, "error decoding"); + + /* Check if a new format has been auto-detected by the decoder */ + if (out->cmd == MMAL_EVENT_FORMAT_CHANGED) + { + MMAL_EVENT_FORMAT_CHANGED_T *event = mmal_event_format_changed_get(out); + + if (event) + mmal_format_copy(port_out->format, event->format); + mmal_buffer_header_release(out); + + if (!event) + status = MMAL_EINVAL; + CHECK_MMAL_STATUS(status, EXECUTE, "invalid format change event"); + + LOG_DEBUG("new format (%4.4s:%ux%u|%ux%u)", (char *)&event->format->encoding, + event->format->es->video.crop.width, event->format->es->video.crop.height, + event->format->es->video.width, event->format->es->video.height); + + /* re-setup the output port for the new format */ + status = mmal_wrapper_port_disable(port_out); + CHECK_MMAL_STATUS(status, EXECUTE, "failed to disable output port"); + + ctx->slice_height = event->format->es->video.height; + if (ENABLE_SLICE_MODE && !jd->output_handle) + { + /* setup slice mode */ + if (port_out->format->encoding == MMAL_ENCODING_I420 || + port_out->format->encoding == MMAL_ENCODING_I422) + { + if (port_out->format->encoding == MMAL_ENCODING_I420) + port_out->format->encoding = MMAL_ENCODING_I420_SLICE; + if (port_out->format->encoding == MMAL_ENCODING_I422) + port_out->format->encoding = MMAL_ENCODING_I422_SLICE; + ctx->slice_height = 16; + port_out->buffer_num = 3; + } + } + + LOG_DEBUG("using slice size %u", ctx->slice_height); + status = mmal_port_format_commit(port_out); + CHECK_MMAL_STATUS(status, EXECUTE, "invalid format change event"); + port_out->buffer_size = port_out->buffer_size_min; + if (jd->output_handle) + status = mmal_wrapper_port_enable(port_out, MMAL_WRAPPER_FLAG_PAYLOAD_USE_SHARED_MEMORY); + else + status = mmal_wrapper_port_enable(port_out, MMAL_WRAPPER_FLAG_PAYLOAD_ALLOCATE); + CHECK_MMAL_STATUS(status, EXECUTE, "failed to enable output port"); + + /* send all our output buffers to the decoder */ + while (mmal_wrapper_buffer_get_empty(port_out, &out, 0) == MMAL_SUCCESS) + { + if (jd->output_handle) + { + out->data = (uint8_t*)jd->output_handle; + out->alloc_size = jd->output_alloc_size; + } + status = mmal_port_send_buffer(port_out, out); + CHECK_MMAL_STATUS(status, EXECUTE, "failed to send output buffer"); + } + + continue; + } + + /* We have part of our output frame */ + jd->width = port_out->format->es->video.crop.width; + if (!jd->width) + jd->width = port_out->format->es->video.width; + if (jd->output_handle) + jd->buffer_width = port_out->format->es->video.width; + if (!jd->buffer_width) + jd->buffer_width = jd->width; + jd->height = port_out->format->es->video.crop.height; + if (!jd->height) + jd->height = port_out->format->es->video.height; + if (jd->output_handle) + jd->buffer_height = port_out->format->es->video.height; + if (!jd->buffer_height) + jd->buffer_height = jd->height; + + if (jd->output_handle) + { + jd->output_size += out->length; + } + else + { + jd->output_size = brcmjpeg_copy_pixels(jd->output, jd->output_alloc_size, + out->data, out->length, jd->pixel_format, + jd->buffer_width, jd->buffer_height, + port_out->format->es->video.width, + ctx->slice_height, slices * ctx->slice_height, 0); + slices++; + } + + eos = out->flags & MMAL_BUFFER_HEADER_FLAG_EOS; + out->length = 0; + if (eos) + { + mmal_buffer_header_release(out); + } + else + { + status = mmal_port_send_buffer(port_out, out); + CHECK_MMAL_STATUS(status, EXECUTE, "failed to send output buffer"); + } + + if (!jd->output_size) + status = MMAL_EINVAL; + CHECK_MMAL_STATUS(status, OUTPUT_BUFFER, "invalid output buffer"); + } + + LOG_DEBUG("decoded W:%ixH%i:(W%ixH%i):%i in %i slices", + jd->width, jd->height, jd->buffer_width, jd->buffer_height, + jd->pixel_format, slices); + mmal_port_flush(port_in); + return BRCMJPEG_SUCCESS; + + error: + mmal_port_flush(port_in); + return err; +} + +/*****************************************************************************/ +static struct { + BRCMJPEG_PIXEL_FORMAT_T pixel_format; + MMAL_FOURCC_T encoding; +} mmal_raw_conversion[] = { + {PIXEL_FORMAT_I420, MMAL_ENCODING_I420}, + {PIXEL_FORMAT_YV12, MMAL_ENCODING_I420}, + {PIXEL_FORMAT_I422, MMAL_ENCODING_I422}, + {PIXEL_FORMAT_YV16, MMAL_ENCODING_I422}, + {PIXEL_FORMAT_YUYV, MMAL_ENCODING_I422}, + {PIXEL_FORMAT_RGBA, MMAL_ENCODING_RGBA}, + {PIXEL_FORMAT_UNKNOWN, MMAL_ENCODING_UNKNOWN} }; + +static MMAL_FOURCC_T brcmjpeg_pixfmt_to_encoding(BRCMJPEG_PIXEL_FORMAT_T pixel_format) +{ + unsigned int i; + for (i = 0; mmal_raw_conversion[i].encoding != MMAL_ENCODING_UNKNOWN; i++) + if (mmal_raw_conversion[i].pixel_format == pixel_format) + break; + return mmal_raw_conversion[i].encoding; +} + +// Copy a raw frame from 1 buffer to another, taking care of +// stride / height differences between the input and output buffers. +static unsigned int brcmjpeg_copy_pixels(uint8_t *out, unsigned int out_size, + const uint8_t *in, unsigned int in_size, BRCMJPEG_PIXEL_FORMAT_T fmt, + unsigned int out_width, unsigned int out_height, + unsigned int in_width, unsigned int in_height, + unsigned int line_offset, unsigned int convert_from) +{ + struct { + uint8_t *data; + unsigned int pitch; + unsigned int height; + } planes[2][3]; + unsigned int num_planes = 0; + unsigned int i, size = 0; + unsigned int in_height_full = in_height; + unsigned int out_height_full = out_height; + unsigned int k = convert_from ? 1 : 0; + + // Sanity check line_offset + if (line_offset >= (convert_from ? in_height : out_height)) + return 0; + + if (convert_from) + in_height -= line_offset; + else + out_height -= line_offset; + + if (fmt == PIXEL_FORMAT_I420 || + fmt == PIXEL_FORMAT_YV12) + { + planes[0][0].data = out; + planes[0][0].pitch = out_width; + planes[0][0].height = out_height; + + planes[1][0].data = (uint8_t *)in; + planes[1][0].pitch = in_width; + planes[1][0].height = in_height; + + planes[0][1].pitch = planes[0][2].pitch = out_width / 2; + planes[0][1].height = planes[0][2].height = out_height / 2; + planes[0][1].data = planes[0][0].data + out_width * out_height_full; + planes[0][2].data = planes[0][1].data + out_width * out_height_full / 4; + + planes[1][1].pitch = planes[1][2].pitch = in_width / 2; + planes[1][1].height = planes[1][2].height = in_height / 2; + planes[1][1].data = planes[1][0].data + in_width * in_height_full; + planes[1][2].data = planes[1][1].data + in_width * in_height_full / 4; + + if (fmt == PIXEL_FORMAT_YV12) + { + // We need to swap U and V + uint8_t *tmp = planes[1][2].data; + planes[1][2].data = planes[1][1].data; + planes[1][1].data = tmp; + } + + // Add the line offset + planes[k][0].data += planes[k][0].pitch * line_offset; + planes[k][1].data += planes[k][1].pitch * line_offset/2; + planes[k][2].data += planes[k][2].pitch * line_offset/2; + + num_planes = 3; + size = out_width * out_height_full * 3 / 2; + + if (in_size < in_width * in_height * 3 / 2) + return 0; + + } else if (fmt == PIXEL_FORMAT_I422 || + fmt == PIXEL_FORMAT_YV16 || + fmt == PIXEL_FORMAT_YUYV) + { + planes[0][0].data = out; + planes[0][0].pitch = out_width; + planes[0][0].height = out_height; + + planes[1][0].data = (uint8_t *)in; + planes[1][0].pitch = in_width; + planes[1][0].height = in_height; + + planes[0][1].pitch = planes[0][2].pitch = out_width / 2; + planes[0][1].height = planes[0][2].height = out_height; + planes[0][1].data = planes[0][0].data + out_width * out_height_full; + planes[0][2].data = planes[0][1].data + out_width * out_height_full / 2; + + planes[1][1].pitch = planes[1][2].pitch = in_width / 2; + planes[1][1].height = planes[1][2].height = in_height; + planes[1][1].data = planes[1][0].data + in_width * in_height_full; + planes[1][2].data = planes[1][1].data + in_width * in_height_full / 2; + + // Add the line offset + planes[k][0].data += planes[k][0].pitch * line_offset; + planes[k][1].data += planes[k][1].pitch * line_offset; + planes[k][2].data += planes[k][2].pitch * line_offset; + if (fmt == PIXEL_FORMAT_YUYV) + planes[k][0].data += planes[k][0].pitch * line_offset; + + if (fmt == PIXEL_FORMAT_YV16) + { + // We need to swap U and V + uint8_t *tmp = planes[1][2].data; + planes[1][2].data = planes[1][1].data; + planes[1][1].data = tmp; + } + + num_planes = 3; + size = out_width * out_height_full * 2; + + if (in_size < in_width * in_height * 2) + return 0; + } else if (fmt == PIXEL_FORMAT_RGBA) + { + planes[0][0].data = out; + planes[0][0].pitch = out_width * 4; + planes[0][0].height = out_height; + + planes[1][0].data = (uint8_t *)in; + planes[1][0].pitch = in_width * 4; + planes[1][0].height = in_height; + + // Add the line offset + planes[k][0].data += planes[k][0].pitch * line_offset; + + num_planes = 1; + size = out_width * out_height_full * 4; + + if (in_size < in_width * in_height * 4) + return 0; + } + + if (out_size < size) + return 0; + + // Special case for YUYV where don't just copy but convert to/from I422 + if (fmt == PIXEL_FORMAT_YUYV) + { + unsigned int width = in_width > out_width ? out_width : in_width; + unsigned int height = in_height > out_height ? out_height : in_height; + uint8_t *y = planes[convert_from ? 0 : 1][0].data; + uint8_t *u = planes[convert_from ? 0 : 1][1].data; + uint8_t *v = planes[convert_from ? 0 : 1][2].data; + uint8_t *yuyv = planes[convert_from ? 1 : 0][0].data; + unsigned int y_diff = (convert_from ? out_width : in_width) - width; + unsigned int yuyv_diff = ((convert_from ? in_width : out_width) - width) * 2; + + while (height--) + { + if (convert_from) + for (i = width / 2; i; i--) + { + *y++ = *yuyv++; + *u++ = *yuyv++; + *y++ = *yuyv++; + *v++ = *yuyv++; + } + else + for (i = width / 2; i; i--) + { + *yuyv++ = *y++; + *yuyv++ = *u++; + *yuyv++ = *y++; + *yuyv++ = *v++; + } + + yuyv += yuyv_diff; + y += y_diff; + u += y_diff >> 1; + v += y_diff >> 1; + } + + return size; + } + + for (i = 0; i < num_planes; i++) + { + unsigned int width = MMAL_MIN(planes[0][i].pitch, planes[1][i].pitch); + unsigned int height = MMAL_MIN(planes[0][i].height, planes[1][i].height); + uint8_t *data_out = planes[0][i].data; + uint8_t *data_in = planes[1][i].data; + + while (height--) + { + memcpy(data_out, data_in, width); + data_out += planes[0][i].pitch; + data_in += planes[1][i].pitch; + } + } + + return size; +} diff --git a/src/plugins/RPI_jpeg/brcmjpeg.h b/src/plugins/RPI_jpeg/brcmjpeg.h new file mode 100644 index 0000000..d787e99 --- /dev/null +++ b/src/plugins/RPI_jpeg/brcmjpeg.h @@ -0,0 +1,152 @@ +/* +Copyright (c) 2012, Broadcom Europe Ltd +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** \file + * Jpeg encoder and decoder library using the hardware jpeg codec + */ + +#ifndef BRCM_JPEG_H +#define BRCM_JPEG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** Status return codes from the API */ +typedef enum +{ + BRCMJPEG_SUCCESS = 0, + BRCMJPEG_ERROR_NOMEM, + BRCMJPEG_ERROR_INIT, + BRCMJPEG_ERROR_INPUT_FORMAT, + BRCMJPEG_ERROR_OUTPUT_FORMAT, + BRCMJPEG_ERROR_INPUT_BUFFER, + BRCMJPEG_ERROR_OUTPUT_BUFFER, + BRCMJPEG_ERROR_EXECUTE, + BRCMJPEG_ERROR_REQUEST, +} BRCMJPEG_STATUS_T; + +/** Type of the codec instance to create */ +typedef enum +{ + BRCMJPEG_TYPE_ENCODER = 0, + BRCMJPEG_TYPE_DECODER +} BRCMJPEG_TYPE_T; + +/** Pixel formats supported by the codec */ +typedef enum +{ + PIXEL_FORMAT_UNKNOWN = 0, + PIXEL_FORMAT_I420, /* planar YUV 4:2:0 */ + PIXEL_FORMAT_YV12, /* planar YVU 4:2:0 */ + PIXEL_FORMAT_I422, /* planar YUV 4:2:2 */ + PIXEL_FORMAT_YV16, /* planar YVU 4:2:2 */ + PIXEL_FORMAT_YUYV, /* interleaved YUV 4:2:2 */ + PIXEL_FORMAT_RGBA, /* interleaved RGBA */ +} BRCMJPEG_PIXEL_FORMAT_T; + +/** Definition of a codec request */ +typedef struct +{ + /** Pointer to the buffer containing the input data + * A client should set input OR input_handle, but not both. */ + const unsigned char *input; + /** Actual size of the input data */ + unsigned int input_size; + /** Handle to input buffer containing input data */ + unsigned int input_handle; + + /** Pointer to the buffer used for the output data + * A client should set output OR output_handle, but not both. */ + unsigned char *output; + /** Total size of the output buffer */ + unsigned int output_alloc_size; + /** Actual size of the output data (this is an output parameter) */ + unsigned int output_size; + /** Handle to the buffer used for the output data */ + unsigned int output_handle; + + /** Width of the raw frame (this is an input parameter for encode) */ + unsigned int width; + /** Height of the raw frame (this is an input parameter for encode) */ + unsigned int height; + /** Pixel format of the raw frame (this is an input parameter) */ + BRCMJPEG_PIXEL_FORMAT_T pixel_format; + + /** Width of the buffer containing the raw frame (input parameter). + * This is optional but if set, is used to specify the actual width + * of the buffer containing the raw frame */ + unsigned int buffer_width; + /** Height of the buffer containing the raw frame (input parameter). + * This is optional but if set, is used to specify the actual height + * of the buffer containing the raw frame */ + unsigned int buffer_height; + + /** Encode quality - 0 to 100 */ + unsigned int quality; +} BRCMJPEG_REQUEST_T; + +/** Type of the codec instance */ +typedef struct BRCMJPEG_T BRCMJPEG_T; + +/** Create an instance of the jpeg codec + * This will actually re-use an existing instance if one is + * available. + * + * @param type type of codec instance required + * @param ctx will point to the newly created instance + * @return BRCMJPEG_SUCCESS on success + */ +BRCMJPEG_STATUS_T brcmjpeg_create(BRCMJPEG_TYPE_T type, BRCMJPEG_T **ctx); + +/** Acquire a new reference on a codec instance + * + * @param ctx instance to acquire a reference on + */ +void brcmjpeg_acquire(BRCMJPEG_T *ctx); + +/** Release an instance of the jpeg codec + * This will only trigger the destruction of the codec instance when + * the last reference to it is being released. + * + * @param ctx instance to release + */ +void brcmjpeg_release(BRCMJPEG_T *ctx); + +/** Process a jpeg codec request + * + * @param ctx instance of codec to use + * @param request codec request to execute + * @return BRCMJPEG_SUCCESS on success + */ +BRCMJPEG_STATUS_T brcmjpeg_process(BRCMJPEG_T *ctx, BRCMJPEG_REQUEST_T *request); + +#ifdef __cplusplus +} +#endif + +#endif /* BRCM_JPEG_H */ diff --git a/src/plugins/RPI_jpeg/jpeg.json b/src/plugins/RPI_jpeg/jpeg.json new file mode 100644 index 0000000..297e4c2 --- /dev/null +++ b/src/plugins/RPI_jpeg/jpeg.json @@ -0,0 +1,4 @@ +{ + "Keys": [ "jpg", "jpeg" ], + "MimeTypes": [ "image/jpeg", "image/jpeg" ] +} \ No newline at end of file diff --git a/src/power/CMakeLists.txt b/src/power/CMakeLists.txt new file mode 100644 index 0000000..bec0514 --- /dev/null +++ b/src/power/CMakeLists.txt @@ -0,0 +1,17 @@ +add_sources(PowerComponent.h PowerComponent.cpp) + +if(APPLE) + add_sources(PowerComponentMac.cpp PowerComponentMac.h) +endif(APPLE) + +if(USE_X11POWER) + add_sources(PowerComponentX11.cpp PowerComponentX11.h) +endif(USE_X11POWER) + +if(OPENELEC) + add_sources(PowerComponentOE.cpp PowerComponentOE.h) +endif(OPENELEC) + +if(WIN32) + add_sources(PowerComponentWin.cpp PowerComponentWin.h) +endif(WIN32) diff --git a/src/power/PowerComponent.cpp b/src/power/PowerComponent.cpp new file mode 100644 index 0000000..23f37e2 --- /dev/null +++ b/src/power/PowerComponent.cpp @@ -0,0 +1,97 @@ +// +// Created by Tobias Hieta on 25/03/15. +// + +#include "PowerComponent.h" +#include "player/PlayerComponent.h" + +#ifdef Q_OS_MAC +#include "PowerComponentMac.h" +#elif KONVERGO_OPENELEC +#include "PowerComponentOE.h" +#elif USE_X11POWER +#include "PowerComponentX11.h" +#elif defined(Q_OS_WIN32) +#include "PowerComponentWin.h" +#endif + +///////////////////////////////////////////////////////////////////////////////////////// +PowerComponent& PowerComponent::Get() +{ +#ifdef Q_OS_MAC + static PowerComponentMac instance; + return instance; +#elif KONVERGO_OPENELEC + static PowerComponentOE instance; + return instance; +#elif USE_X11POWER + static PowerComponentX11 instance; + return instance; +#elif defined(Q_OS_WIN32) + static PowerComponentWin instance; + return instance; +#else + QLOG_WARN() << "Could not find a power component matching this platform. OS screensaver control disabled."; + + static PowerComponent instance; + return instance; +#endif +} + +///////////////////////////////////////////////////////////////////////////////////////// +bool PowerComponent::componentInitialize() +{ + PlayerComponent* player = &PlayerComponent::Get(); + if (!player) + return false; + + connect(player, &PlayerComponent::playing, this, &PowerComponent::playbackStarted); + connect(player, &PlayerComponent::playbackEnded, this, &PowerComponent::playbackEnded); + + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////// +void PowerComponent::setFullscreenState(bool fullscreen) +{ + m_fullscreenState = fullscreen; + redecideScreeensaverState(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void PowerComponent::redecideScreeensaverState() +{ + bool enable_os_screensaver = !m_fullscreenState && !m_videoPlaying; + if (m_currentScreensaverEnabled != enable_os_screensaver) + { + m_currentScreensaverEnabled = enable_os_screensaver; + if (enable_os_screensaver) + { + QLOG_DEBUG() << "Enabling OS screensaver"; + doEnableScreensaver(); + emit screenSaverEnabled(); + } + else + { + QLOG_DEBUG() << "Disabling OS screensaver"; + doDisableScreensaver(); + emit screenSaverDisabled(); + } + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +void PowerComponent::playbackStarted() +{ + m_videoPlaying = true; + redecideScreeensaverState(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void PowerComponent::playbackEnded() +{ + m_videoPlaying = false; + redecideScreeensaverState(); +} + + diff --git a/src/power/PowerComponent.h b/src/power/PowerComponent.h new file mode 100644 index 0000000..b3c9407 --- /dev/null +++ b/src/power/PowerComponent.h @@ -0,0 +1,58 @@ +#ifndef POWERMANAGER +#define POWERMANAGER + +#include +#include "ComponentManager.h" + +class PowerComponent : public ComponentBase +{ + Q_OBJECT +public: + static PowerComponent& Get(); + + PowerComponent(QObject* parent = 0) + : ComponentBase(parent), + m_currentScreensaverEnabled(true), + m_fullscreenState(false), + m_videoPlaying(false) + { } + + virtual bool componentInitialize(); + virtual bool componentExport() { return true; } + virtual const char* componentName() { return "power"; } + + void setFullscreenState(bool fullscreen); + +public Q_SLOTS: + virtual bool canPowerOff() { return false; } + virtual bool canReboot() { return false; } + virtual bool canSuspend() { return false; } + virtual bool canRelaunch() { return false; } + + virtual bool PowerOff() { return false; } + virtual bool Reboot() { return false; } + virtual bool Suspend() { return false; } + +private Q_SLOTS: + void playbackStarted(); + void playbackEnded(); + +Q_SIGNALS: + void screenSaverEnabled(); + void screenSaverDisabled(); + +protected: + virtual void doDisableScreensaver() {}; + virtual void doEnableScreensaver() {}; + +private: + void redecideScreeensaverState(); + + bool m_currentScreensaverEnabled; + bool m_fullscreenState; + bool m_videoPlaying; +}; + + +#endif // POWERMANAGER + diff --git a/src/power/PowerComponentMac.cpp b/src/power/PowerComponentMac.cpp new file mode 100644 index 0000000..0749dbe --- /dev/null +++ b/src/power/PowerComponentMac.cpp @@ -0,0 +1,24 @@ +#include "PowerComponentMac.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PowerComponentMac::doDisableScreensaver() +{ + if (m_assertion == 0) + { + CFStringRef why = CFSTR("tv.plex.player"); + IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, + kIOPMAssertionLevelOn, + why, + &m_assertion); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PowerComponentMac::doEnableScreensaver() +{ + if (m_assertion != 0) + { + IOPMAssertionRelease(m_assertion); + m_assertion = 0; + } +} diff --git a/src/power/PowerComponentMac.h b/src/power/PowerComponentMac.h new file mode 100644 index 0000000..1e1c776 --- /dev/null +++ b/src/power/PowerComponentMac.h @@ -0,0 +1,18 @@ +#ifndef POWERCOMPONENTMAC_H +#define POWERCOMPONENTMAC_H + +#include "PowerComponent.h" +#include + +class PowerComponentMac : public PowerComponent +{ +public: + PowerComponentMac() : PowerComponent(0), m_assertion(0) { } + virtual void doDisableScreensaver(); + virtual void doEnableScreensaver(); + +private: + IOPMAssertionID m_assertion = 0; +}; + +#endif // POWERCOMPONENTMAC_H diff --git a/src/power/PowerComponentOE.cpp b/src/power/PowerComponentOE.cpp new file mode 100644 index 0000000..de6171d --- /dev/null +++ b/src/power/PowerComponentOE.cpp @@ -0,0 +1,81 @@ +#include +#include +#include + +#include "PowerComponentOE.h" + +#define DBUS_SERVICE_NAME "org.freedesktop.login1" +#define DBUS_SERVICE_PATH "/org/freedesktop/login1" +#define DBUS_INTERFACE "org.freedesktop.login1.Manager" + +///////////////////////////////////////////////////////////////////////////////////////// +bool PowerComponentOE::callPowerMethod(QString method) +{ + if (QDBusConnection::systemBus().isConnected()) + { + QDBusInterface iface(DBUS_SERVICE_NAME, DBUS_SERVICE_PATH, DBUS_INTERFACE, + QDBusConnection::systemBus()); + + if (iface.isValid()) + { + QDBusReply reply = iface.call(method, true); + + if (reply.isValid()) + { + return true; + } + else + { + QLOG_ERROR() << "callPowerMethod : Error while calling" << method << ":" + << reply.error().message(); + return false; + } + } + else + { + QLOG_ERROR() << "callPowerMethod : failed to retrieve interface."; + } + } + else + { + QLOG_ERROR() << "callPowerMethod : could not find system bus"; + } + + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// +bool PowerComponentOE::isPowerMethodAvailable(QString method) +{ + if (QDBusConnection::systemBus().isConnected()) + { + QDBusInterface iface(DBUS_SERVICE_NAME, DBUS_SERVICE_PATH, DBUS_INTERFACE, + QDBusConnection::systemBus()); + + if (iface.isValid()) + { + QDBusReply reply = iface.call(method); + + if (reply.isValid()) + { + return (reply.value() == "yes"); + } + else + { + QLOG_ERROR() << "isPowerMethodAvailable : Error while calling" << method << ":" + << reply.error().message(); + return false; + } + } + else + { + QLOG_ERROR() << "isPowerMethodAvailable : failed to retrieve interface."; + } + } + else + { + QLOG_ERROR() << "isPowerMethodAvailable : could not find system bus"; + } + + return false; +} diff --git a/src/power/PowerComponentOE.h b/src/power/PowerComponentOE.h new file mode 100644 index 0000000..73fe36c --- /dev/null +++ b/src/power/PowerComponentOE.h @@ -0,0 +1,27 @@ +#ifndef POWERCOMPONENTOE_H +#define POWERCOMPONENTOE_H + +#include "PowerComponent.h" + +class PowerComponentOE : public PowerComponent +{ + public: + PowerComponentOE() : PowerComponent(0) {}; + ~PowerComponentOE() {}; + + public Q_SLOTS: + virtual bool canPowerOff() { return isPowerMethodAvailable("CanPowerOff"); } + virtual bool canReboot() { return isPowerMethodAvailable("CanReboot"); } + virtual bool canSuspend() { return isPowerMethodAvailable("CanSuspend"); } + virtual bool canRelaunch() { return true; } + + virtual bool PowerOff() { return callPowerMethod("PowerOff"); } + virtual bool Reboot() { return callPowerMethod("Reboot"); } + virtual bool Suspend() { return callPowerMethod("Suspend"); } + + private: + bool callPowerMethod(QString method); + bool isPowerMethodAvailable(QString method); +}; + +#endif // POWERCOMPONENTOE_H diff --git a/src/power/PowerComponentWin.cpp b/src/power/PowerComponentWin.cpp new file mode 100644 index 0000000..145f5f1 --- /dev/null +++ b/src/power/PowerComponentWin.cpp @@ -0,0 +1,15 @@ +#include + +#include "PowerComponentWin.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PowerComponentWin::doDisableScreensaver() +{ + SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED | ES_SYSTEM_REQUIRED); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PowerComponentWin::doEnableScreensaver() +{ + SetThreadExecutionState(ES_CONTINUOUS); +} diff --git a/src/power/PowerComponentWin.h b/src/power/PowerComponentWin.h new file mode 100644 index 0000000..4d46470 --- /dev/null +++ b/src/power/PowerComponentWin.h @@ -0,0 +1,14 @@ +#ifndef POWERCOMPONENTWIN_H +#define POWERCOMPONENTWIN_H + +#include "PowerComponent.h" + +class PowerComponentWin : public PowerComponent +{ +public: + PowerComponentWin() : PowerComponent(0) { } + virtual void doDisableScreensaver(); + virtual void doEnableScreensaver(); +}; + +#endif // POWERCOMPONENTWIN_H diff --git a/src/power/PowerComponentX11.cpp b/src/power/PowerComponentX11.cpp new file mode 100644 index 0000000..6f18da6 --- /dev/null +++ b/src/power/PowerComponentX11.cpp @@ -0,0 +1,55 @@ +#include + +#include "PowerComponentX11.h" +#include "QsLog.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +PowerComponentX11::PowerComponentX11() : PowerComponent(0) +{ + m_timer = new QTimer(this); + connect(m_timer, &QTimer::timeout, this, &PowerComponentX11::onTimer); + m_timer->setInterval(15 * 1000); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PowerComponentX11::onTimer() +{ + if (!m_process && !m_broken) + { + m_process = new QProcess(this); + m_process->setProcessChannelMode(QProcess::ForwardedChannels); + connect(m_process, (void (QProcess::*)(int,QProcess::ExitStatus))&QProcess::finished, + this, &PowerComponentX11::onProcessFinished); + connect(m_process, (void (QProcess::*)(QProcess::ProcessError))&QProcess::error, + this, &PowerComponentX11::onProcessError); + m_process->start("xdg-screensaver", {"reset"}); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PowerComponentX11::onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus) +{ + if (m_process) + m_process->deleteLater(); + m_process = 0; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PowerComponentX11::onProcessError(QProcess::ProcessError error) +{ + QLOG_ERROR() << "Disabling screensaver is not working. Make sure xdg-screensaver is installed."; + m_broken = true; + onProcessFinished(-1, QProcess::CrashExit); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PowerComponentX11::doDisableScreensaver() +{ + m_timer->start(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void PowerComponentX11::doEnableScreensaver() +{ + m_timer->stop(); +} diff --git a/src/power/PowerComponentX11.h b/src/power/PowerComponentX11.h new file mode 100644 index 0000000..ff1dac9 --- /dev/null +++ b/src/power/PowerComponentX11.h @@ -0,0 +1,31 @@ +#ifndef POWERCOMPONENTX11_H +#define POWERCOMPONENTX11_H + +#include +#include + +#include "PowerComponent.h" + +class PowerComponentX11 : public PowerComponent +{ + Q_OBJECT + +public: + PowerComponentX11(); + +protected: + virtual void doDisableScreensaver(); + virtual void doEnableScreensaver(); + +private slots: + void onTimer(); + void onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus); + void onProcessError(QProcess::ProcessError error); + +private: + bool m_broken = false; + QTimer* m_timer = 0; + QProcess* m_process = 0; +}; + +#endif // POWERCOMPONENTX11_H diff --git a/src/remote/CMakeLists.txt b/src/remote/CMakeLists.txt new file mode 100644 index 0000000..cd21d51 --- /dev/null +++ b/src/remote/CMakeLists.txt @@ -0,0 +1,3 @@ +find_all_sources(. REMOTE_SRCS) +add_sources(${REMOTE_SRCS}) + diff --git a/src/remote/GDMManager.cpp b/src/remote/GDMManager.cpp new file mode 100644 index 0000000..3684039 --- /dev/null +++ b/src/remote/GDMManager.cpp @@ -0,0 +1,79 @@ +#include "GDMManager.h" + +#include +#include + +#include "settings/SettingsComponent.h" +#include "QsLog.h" +#include "RemoteComponent.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +GDMManager::GDMManager(QObject *parent) : QObject(parent), m_port(-1) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void GDMManager::startAnnouncing() +{ + startListener(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void GDMManager::stopAnnouncing() +{ + m_socket.close(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void GDMManager::startListener() +{ + m_socket.bind(QHostAddress::AnyIPv4, 32412, QUdpSocket::ReuseAddressHint | QUdpSocket::ShareAddress); + + QHostAddress multicast("239.0.0.250"); + m_socket.joinMulticastGroup(multicast); + + connect(&m_socket, &QUdpSocket::readyRead, this, &GDMManager::readData); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void GDMManager::readData() +{ + while (m_socket.hasPendingDatagrams()) + { + QByteArray datagram; + datagram.resize(m_socket.pendingDatagramSize()); + + QHostAddress sender; + quint16 senderPort; + + m_socket.readDatagram(datagram.data(), datagram.size(), &sender, &senderPort); + parseData(datagram, sender, senderPort); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void GDMManager::parseData(const QByteArray& data, const QHostAddress& sender, quint16 port) +{ + if (data.startsWith("M-SEARCH *")) + m_socket.writeDatagram(getPacket(), sender, port); +} + +///////////////////////////////////////////////////////////////////////////////////////// +QByteArray GDMManager::getPacket() +{ + QByteArray packetData; + + // Header + packetData.append("HTTP/1.0 200 OK\r\n"); + packetData.append("Content-Type: plex/media-player\r\n"); + + QVariantMap headers = RemoteComponent::GDMInformation(); + + foreach (const QString& key, headers.keys()) + packetData.append(key + ": " + headers[key].toString() + "\r\n"); + + // terminate header + packetData.append("\r\n"); + + return packetData; +} \ No newline at end of file diff --git a/src/remote/GDMManager.h b/src/remote/GDMManager.h new file mode 100644 index 0000000..2853ec2 --- /dev/null +++ b/src/remote/GDMManager.h @@ -0,0 +1,30 @@ +#ifndef GDMANNOUNCER_H +#define GDMANNOUNCER_H + +#include +#include +#include +#include +#include + +class GDMManager : public QObject +{ + Q_OBJECT +public: + explicit GDMManager(QObject *parent = 0); + ~GDMManager() { stopAnnouncing(); } + void startAnnouncing(); + void stopAnnouncing(); + +private: + void startListener(); + void parseData(const QByteArray& data, const QHostAddress& sender, quint16 port); + void readData(); + + QByteArray getPacket(); + + QUdpSocket m_socket; + qint32 m_port; +}; + +#endif // GDMANNOUNCER_H diff --git a/src/remote/RemoteComponent.cpp b/src/remote/RemoteComponent.cpp new file mode 100644 index 0000000..c704bad --- /dev/null +++ b/src/remote/RemoteComponent.cpp @@ -0,0 +1,527 @@ +// +// Created by Tobias Hieta on 24/03/15. +// + +#include "RemoteComponent.h" + +#include +#include + +#include "QsLog.h" +#include "settings/SettingsComponent.h" +#include "utils/Utils.h" +#include "Version.h" + +static QMap resourceKeyMap = { + { "Name", "title" }, + { "Resource-Identifier", "machineIdentifier" }, + { "Product", "product" }, + { "Version", "version" }, + { "Protocol-Version", "protocolVersion" }, + { "Protocol-Capabilities", "protocolCapabilities" }, + { "Device-Class", "deviceClass" } +}; + +static QMap headerKeyMap = { + { "Name", "X-Plex-Device-Name" }, + { "Resource-Identifier", "X-Plex-Client-Identifier" }, + { "Product", "X-Plex-Product" }, + { "Version", "X-Plex-Version" } +}; + +///////////////////////////////////////////////////////////////////////////////////////// +RemoteComponent::RemoteComponent(QObject* parent) : ComponentBase(parent), m_commandId(0) +{ + m_gdmManager = new GDMManager(this); + m_networkAccessManager = new QNetworkAccessManager(this); +} + +///////////////////////////////////////////////////////////////////////////////////////// +bool RemoteComponent::componentInitialize() +{ + m_gdmManager->startAnnouncing(); + + // check for timed out subscribers + m_subscriberTimer.setInterval(5000); + connect(&m_subscriberTimer, &QTimer::timeout, this, &RemoteComponent::checkSubscribers); + m_subscriberTimer.start(); + + // connect the network access stuff + connect(m_networkAccessManager, &QNetworkAccessManager::finished, this, &RemoteComponent::timelineFinished); + + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////// +QVariantMap RemoteComponent::HeaderInformation() +{ + QVariantMap gdmInfo = GDMInformation(); + QVariantMap headerInfo; + + foreach (const QString& key, gdmInfo.keys()) + { + if (headerKeyMap.contains(key)) + headerInfo[headerKeyMap[key]] = gdmInfo[key]; + } + + headerInfo["X-Plex-Platform"] = QSysInfo::productType(); + headerInfo["X-Plex-Platform-Version"] = QSysInfo::productVersion(); + + return headerInfo; +} + +///////////////////////////////////////////////////////////////////////////////////////// +QVariantMap RemoteComponent::ResourceInformation() +{ + QVariantMap gdmInfo = GDMInformation(); + QVariantMap resourceInfo; + + foreach (const QString& key, gdmInfo.keys()) + { + if (resourceKeyMap.contains(key)) + resourceInfo[resourceKeyMap[key]] = gdmInfo[key]; + } + + resourceInfo["platform"] = QSysInfo::productType(); + resourceInfo["platformVersion"] = QSysInfo::productVersion(); + + return resourceInfo; +}; + +///////////////////////////////////////////////////////////////////////////////////////// +QVariantMap RemoteComponent::GDMInformation() +{ + QVariantMap headers = { + {"Name", Utils::ComputerName()}, + {"Port", SettingsComponent::Get().value(SETTINGS_SECTION_MAIN, "webserverport")}, + {"Version", Version::GetVersionString()}, + {"Product", "Plex Media Player"}, + {"Protocol", "plex"}, + {"Protocol-Version", "1"}, + {"Protocol-Capabilities", "navigation,playback,timeline,mirror,playqueues"}, + {"Device-Class", "PC"}, + {"Resource-Identifier", SettingsComponent::Get().value(SETTINGS_SECTION_WEBCLIENT, "clientID")} + }; + + return headers; +} + +///////////////////////////////////////////////////////////////////////////////////////// +void RemoteComponent::handleResource(QHttpRequest* request, QHttpResponse* response) +{ + if (request->method() == QHttpRequest::HTTP_GET) + { + QVariantMap headers = ResourceInformation(); + + QByteArray outputData; + QXmlStreamWriter output(&outputData); + output.setAutoFormatting(true); + output.writeStartDocument(); + output.writeStartElement("MediaContainer"); + output.writeStartElement("Player"); + + foreach (const QString& key, headers.keys()) + output.writeAttribute(key, headers[key].toString()); + + output.writeEndElement(); + output.writeEndDocument(); + + response->writeHead(QHttpResponse::STATUS_OK); + response->write(outputData); + response->end(); + } + else + { + response->writeHead(QHttpResponse::STATUS_METHOD_NOT_ALLOWED); + response->end(); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +QVariantMap RemoteComponent::QueryToMap(const QUrl& url) +{ + QUrlQuery query(url); + QVariantMap queryMap; + + QPair stringPair; + + foreach (stringPair, query.queryItems()) + { + QString key = stringPair.first; + QString value = stringPair.second; + + QVariantList l; + if (queryMap.contains(key)) + { + l = queryMap[key].toList(); + l.append(value); + } + else + { + l.append(value); + } + queryMap[key] = l; + } + + return queryMap; +} + +///////////////////////////////////////////////////////////////////////////////////////// +QVariantMap RemoteComponent::HeaderToMap(const HeaderHash& hash) +{ + QVariantMap variantMap; + foreach (const QString& key, hash.keys()) + variantMap[key] = hash[key]; + return variantMap; +} + +///////////////////////////////////////////////////////////////////////////////////////// +void RemoteComponent::handleCommand(QHttpRequest* request, QHttpResponse* response) +{ + + QVariantMap queryMap = QueryToMap(request->url()); + QVariantMap headerMap = HeaderToMap(request->headers()); + QString identifier = headerMap["x-plex-client-identifier"].toString(); + + response->setHeader("Access-Control-Allow-Origin", "*"); + response->setHeader("X-Plex-Client-Identifier", SettingsComponent::Get().value(SETTINGS_SECTION_WEBCLIENT, "clientID").toString()); + + // handle CORS requests here + if ((request->method() == QHttpRequest::HTTP_OPTIONS) && headerMap.contains("access-control-request-method")) + { + response->setHeader("Content-Type", "text/plain"); + response->setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT, HEAD"); + response->setHeader("Access-Control-Max-Age", "1209600"); + response->setHeader("Connection", "close"); + + if (headerMap.contains("access-control-request-headers")) + { + response->setHeader("Access-Control-Allow-Headers", headerMap["access-control-request-headers"].toString()); + } + + response->writeHead(QHttpResponse::STATUS_OK); + response->end(); + return; + } + + // we want to handle the subscription events in the host + // since we are going to handle the updating later. + // + if (request->path() == "/player/timeline/subscribe") + { + handleSubscription(request, response, false); + return; + } + else if (request->path() == "/player/timeline/unsubscribe") + { + subscriberRemove(request->headers()["x-plex-client-identifier"]); + response->writeHead(QHttpResponse::STATUS_OK); + response->end(); + return; + } + else if ((request->path() == "/player/timeline/poll")) + { + if (m_subscriberMap.contains(identifier) == false) + handleSubscription(request, response, true); + + RemotePollSubscriber *subscriber = (RemotePollSubscriber *)m_subscriberMap[identifier]; + subscriber->reSubscribe(); + subscriber->setHTTPResponse(response); + + // if we don't have to wait, just ship the update right away + // otherwise, this will wait until next update + if (!(queryMap.contains("wait") && ( queryMap["wait"].toList()[0].toInt() == 1))) + { + subscriber->sendUpdate(); + } + + return; + } + + + // handle commandID + if (headerMap.contains("x-plex-client-identifier") == false || queryMap.contains("commandID") == false) + { + QLOG_WARN() << "Can't find a X-Plex-Client-Identifier header"; + response->writeHead(QHttpResponse::STATUS_NOT_ACCEPTABLE); + response->end(); + return; + } + + quint64 commandId = 0; + { + QMutexLocker lk(&m_responseLock); + commandId = ++m_commandId; + m_responseMap[commandId] = response; + + connect(response, &QHttpResponse::done, this, &RemoteComponent::responseDone); + } + + { + QMutexLocker lk(&m_subscriberLock); + if (m_subscriberMap.contains(identifier) == false) + { + QLOG_WARN() << "Failed to lock up subscriber" << identifier; + response->writeHead(QHttpResponse::STATUS_NOT_ACCEPTABLE); + response->end(); + return; + } + + RemoteSubscriber* subscriber = m_subscriberMap[identifier]; + subscriber->setCommandId(m_commandId, queryMap["commandID"].toList()[0].toInt()); + } + + QVariantMap arg = { + { "method", request->methodString() }, + { "headers", headerMap }, + { "path", request->path() }, + { "query", queryMap }, + { "commandID", m_commandId} + }; + + emit commandReceived(arg); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void RemoteComponent::responseDone() +{ + QHttpResponse* response = dynamic_cast(sender()); + if (response) + { + QMutexLocker lk(&m_responseLock); + + int foundId = -1; + foreach(int responseId, m_responseMap.keys()) + { + if (m_responseMap[responseId] == response) + { + foundId = responseId; + break; + } + } + + if (foundId != -1) + m_responseMap.remove(foundId); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +void RemoteComponent::commandResponse(const QVariantMap& responseArguments) +{ + // check for minimum requirements in the responseArguments + if (responseArguments.contains("commandID") == false || + responseArguments.contains("responseCode") == false) + { + QLOG_WARN() << "responseArguments did not contain a commandId or responseCode"; + return; + } + + quint64 commandId = responseArguments["commandID"].toULongLong(); + uint responseCode = responseArguments["responseCode"].toUInt(); + + QMutexLocker lk(&m_responseLock); + if (m_responseMap.contains(commandId) == false) + { + QLOG_WARN() << "Could not find responseId:" << commandId << " - maybe it was removed because of a timeout?"; + return; + } + + QHttpResponse* response = m_responseMap[commandId]; + + // no need to hold the lock when we have changed m_responseMap + lk.unlock(); + + // add headers if we have them + if (responseArguments.contains("headers") && responseArguments["headers"].type() == QVariant::Map) + { + QVariantMap headers = responseArguments["headers"].toMap(); + foreach (const QString& key, headers.keys()) + response->setHeader(key, headers[key].toString()); + } + + // write the response HTTP code + response->writeHead(responseCode); + + // handle optional body argument + if (responseArguments.contains("body") && responseArguments["body"].type() == QVariant::String) + response->write(responseArguments["body"].toString().toUtf8()); + + response->end(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void RemoteComponent::handleSubscription(QHttpRequest* request, QHttpResponse* response, bool poll) +{ + QVariantMap headers = HeaderToMap(request->headers()); + + // check for required headers + if (headers.contains("x-plex-client-identifier") == false || + headers.contains("x-plex-device-name") == false) + { + QLOG_ERROR() << "Missing X-Plex headers in /timeline/subscribe request"; + response->writeHead(QHttpResponse::STATUS_BAD_REQUEST); + response->end(); + return; + } + + // check for required arguments + QVariantMap query = QueryToMap(request->url()); + + if (query.contains("commandID") == false || ((query.contains("port") == false) && !poll)) + { + QLOG_ERROR() << "Missing arguments to /timeline/subscribe request"; + response->writeHead(QHttpResponse::STATUS_BAD_REQUEST); + response->end(); + return; + } + + QString clientIdentifier(request->headers()["x-plex-client-identifier"]); + + QMutexLocker lk(&m_subscriberLock); + RemoteSubscriber* subscriber = 0; + + if (m_subscriberMap.contains(clientIdentifier)) + { + QLOG_DEBUG() << "Refreshed subscriber:" << clientIdentifier; + subscriber = m_subscriberMap[clientIdentifier]; + subscriber->reSubscribe(); + } + else + { + if (poll) + { + QLOG_DEBUG() << "New poll subscriber:" << clientIdentifier << request->headers()["x-plex-device-name"]; + subscriber = new RemotePollSubscriber(clientIdentifier, request->headers()["x-plex-device-name"], response, this); + } + else + { + QUrl address; + QString protocol = query.contains("protocol") ? query["protocol"].toList()[0].toString() : "http"; + int port = query.contains("port") ? query["port"].toList()[0].toInt() : 32400; + + address.setScheme(protocol); + address.setHost(request->remoteAddress()); + address.setPort(port); + + QLOG_DEBUG() << "New subscriber:" << clientIdentifier << request->headers()["x-plex-device-name"] << address.toString(); + subscriber = new RemoteSubscriber(clientIdentifier, request->headers()["x-plex-device-name"], address, this); + } + + m_subscriberMap[clientIdentifier] = subscriber; + + // if it's our first controller, we notify web for subscription + if (m_subscriberMap.size() == 1) + { + QLOG_DEBUG() << "First subscriber added, subscribing to web"; + subscribeToWeb(true); + } + } + + subscriber->setCommandId(m_commandId, query["commandID"].toList()[0].toInt()); + + if (!poll) + { + subscriber->sendUpdate(); + + response->writeHead(QHttpResponse::STATUS_OK); + response->end(); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +void RemoteComponent::subscribeToWeb(bool subscribe) +{ + QVariantMap arg = { + { "method", "GET" }, + { "path", "/player/timeline/" + (subscribe ? QLatin1String("subscribe") : QLatin1String("unsubscribe")) }, + { "commandID", m_commandId} + }; + + emit commandReceived(arg); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void RemoteComponent::checkSubscribers() +{ + QMutexLocker lk(&m_subscriberLock); + QList subsToRemove; + foreach(RemoteSubscriber* subscriber, m_subscriberMap.values()) + { + // was it more than 10 seconds since this client checked in last? + if (subscriber->lastSubscribe() > 90 * 1000) + { + QLOG_DEBUG() << "more than 10 seconds since we heard from:" << subscriber->deviceName() << "- unsubscribing.."; + subsToRemove << subscriber; + } + } + + lk.unlock(); + + foreach(RemoteSubscriber* sub, subsToRemove) + subscriberRemove(sub->clientIdentifier()); +} + +///////////////////////////////////////////////////////////////////////////////////////// +QNetworkAccessManager* RemoteComponent::getNetworkAccessManager() +{ + // we might want to set common options here. + return m_networkAccessManager; +} + + +///////////////////////////////////////////////////////////////////////////////////////// +void RemoteComponent::timelineFinished(QNetworkReply* reply) +{ + QString identifier = reply->request().attribute(QNetworkRequest::User).toString(); + + // ignore requests with no identifier + if (identifier.isEmpty()) + return; + + QMutexLocker lk(&m_subscriberLock); + if (m_subscriberMap.contains(identifier) == false) + { + QLOG_WARN() << "Got a networkreply with a identifier we don't know about:" << identifier; + return; + } + + RemoteSubscriber* sub = m_subscriberMap[identifier]; + lk.unlock(); + + sub->timelineFinished(reply); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void RemoteComponent::subscriberRemove(const QString& identifier) +{ + QMutexLocker lk(&m_subscriberLock); + if (m_subscriberMap.contains(identifier) == false) + { + QLOG_ERROR() << "Can't remove client:" << identifier << "since we don't know about it."; + return; + } + + RemoteSubscriber* subscriber = m_subscriberMap[identifier]; + m_subscriberMap.remove(identifier); + subscriber->deleteLater(); + + // if it's our first controller, we notify web for subscription + if (m_subscriberMap.size() == 0) + { + QLOG_DEBUG() << "Last subscriber removed, unsubscribing from web"; + subscribeToWeb(false); + } + QLOG_DEBUG() << "Removed subscriber:" << identifier; +} + +///////////////////////////////////////////////////////////////////////////////////////// +void RemoteComponent::timelineUpdate(quint64 commandID, const QString& timeline) +{ + QMutexLocker lk(&m_subscriberLock); + + foreach (RemoteSubscriber* subscriber, m_subscriberMap.values()) + { + subscriber->queueTimeline(commandID, timeline.toUtf8()); + subscriber->sendUpdate(); + } +} diff --git a/src/remote/RemoteComponent.h b/src/remote/RemoteComponent.h new file mode 100644 index 0000000..3a9d3c2 --- /dev/null +++ b/src/remote/RemoteComponent.h @@ -0,0 +1,73 @@ +// +// Created by Tobias Hieta on 24/03/15. +// + +#ifndef _KONVERGO_REMOTECOMPONENT_H_ +#define _KONVERGO_REMOTECOMPONENT_H_ + +#include +#include +#include +#include +#include + +#include "ComponentManager.h" +#include "GDMManager.h" +#include "qhttpresponse.h" +#include "RemoteSubscriber.h" + +class RemoteComponent : public ComponentBase +{ + Q_OBJECT + DEFINE_SINGLETON(RemoteComponent); + +public: + QNetworkAccessManager* getNetworkAccessManager(); + + virtual bool componentInitialize(); + virtual const char* componentName() { return "remote"; } + virtual bool componentExport() { return true; } + + static QVariantMap ResourceInformation(); + static QVariantMap GDMInformation(); + static QVariantMap HeaderInformation(); + + void handleResource(QHttpRequest* request, QHttpResponse* response); + void handleCommand(QHttpRequest* request, QHttpResponse* response); + + static QVariantMap HeaderToMap(const HeaderHash& hash); + static QVariantMap QueryToMap(const QUrl& url); + + Q_INVOKABLE void commandResponse(const QVariantMap& responseArguments); + Q_INVOKABLE QVariantMap resourceInfo() { return ResourceInformation(); } + Q_INVOKABLE void timelineUpdate(quint64 commandID, const QString& timeline); + + void subscriberRemove(const QString& identifier); + +Q_SIGNALS: + void commandReceived(const QVariantMap& commandInfo); + +private Q_SLOTS: + void checkSubscribers(); + void timelineFinished(QNetworkReply* reply); + void responseDone(); + +private: + RemoteComponent(QObject* parent = 0); + void handleSubscription(QHttpRequest * request, QHttpResponse * response, bool poll=false); + void subscribeToWeb(bool subscribe); + + GDMManager* m_gdmManager; + + quint64 m_commandId; + QMap m_responseMap; + QMutex m_responseLock; + + QMutex m_subscriberLock; + QMap m_subscriberMap; + QTimer m_subscriberTimer; + QNetworkAccessManager* m_networkAccessManager; +}; + + +#endif //_KONVERGO_REMOTECOMPONENT_H_ diff --git a/src/remote/RemoteSubscriber.cpp b/src/remote/RemoteSubscriber.cpp new file mode 100644 index 0000000..27ae277 --- /dev/null +++ b/src/remote/RemoteSubscriber.cpp @@ -0,0 +1,204 @@ +// +// Created by Tobias Hieta on 31/03/15. +// + +#include +#include +#include +#include +#include + +#include "RemoteSubscriber.h" +#include "RemoteComponent.h" +#include "settings/SettingsComponent.h" + +///////////////////////////////////////////////////////////////////////////////////////// +RemoteSubscriber::RemoteSubscriber(const QString& clientIdentifier, const QString& deviceName, const QUrl& address, QObject* parent) + : QObject(parent), m_address(address), m_clientIdentifier(clientIdentifier), m_deviceName(deviceName) +{ + m_subscribeTime.start(); + + if (!address.isEmpty()) + { + RemoteComponent* remote = dynamic_cast(parent); + Q_ASSERT(remote); + + m_netAccess = remote->getNetworkAccessManager(); + + // make first access faster by connecting directly to the host now. + if (address.scheme() == "https") + m_netAccess->connectToHostEncrypted(address.host(), address.port()); + else + m_netAccess->connectToHost(address.host(), address.port()); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +void RemoteSubscriber::reSubscribe() +{ + m_subscribeTime.restart(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +QString RemoteSubscriber::deviceName() +{ + return m_deviceName; +} + +///////////////////////////////////////////////////////////////////////////////////////// +QString RemoteSubscriber::clientIdentifier() +{ + return m_clientIdentifier; +} + +///////////////////////////////////////////////////////////////////////////////////////// +void RemoteSubscriber::sendUpdate() +{ + QUrl url(m_address); + url.setPath("/:/timeline"); + + QNetworkRequest request(url); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/xml"); + request.setAttribute(QNetworkRequest::User, m_clientIdentifier); + + QVariantMap headers = RemoteComponent::HeaderInformation(); + foreach (const QString& key, headers.keys()) + { + request.setRawHeader(key.toUtf8(), headers[key].toString().toUtf8()); + } + + m_netAccess->post(request, getTimeline()); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void RemoteSubscriber::timelineFinished(QNetworkReply* reply) +{ + if (reply->error() != QNetworkReply::NoError) + { + QLOG_ERROR() << "got error code when sending timeline:" << reply->errorString(); +#if 0 + if (++m_errors > 10) + { + QLOG_ERROR() << "More than 10 errors received. Dropping this subscriber"; + RemoteComponent::Get()->subscriberRemove(clientIdentifier()); + } +#endif + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +void RemoteSubscriber::queueTimeline(quint64 playerCommandID, const QByteArray& timelineData) +{ + QMutexLocker lk(&m_timelineLock); + + QDomDocument doc; + if (doc.setContent(timelineData) && doc.firstChildElement("MediaContainer").isNull() == false) + { + QDomElement node = doc.firstChildElement("MediaContainer"); + node.setAttribute("commandID", commandId(playerCommandID)); + m_timeline = doc; + } + else + QLOG_WARN() << "Failed to parse timeline data from player"; +} + +///////////////////////////////////////////////////////////////////////////////////////// +QByteArray RemoteSubscriber::getTimeline() +{ + QMutexLocker lk(&m_timelineLock); + + if (m_timeline.isNull()) + { + QByteArray xmlData; + QXmlStreamWriter writer(&xmlData); + + writer.writeStartDocument(); + writer.writeStartElement("MediaContainer"); + writer.writeAttribute("location", "navigation"); + writer.writeAttribute("commandID", QString::number(mostRecentCommandId())); + + writer.writeStartElement("Timeline"); + writer.writeAttribute("type", "music"); + writer.writeAttribute("state", "stopped"); + writer.writeEndElement(); + + writer.writeStartElement("Timeline"); + writer.writeAttribute("type", "video"); + writer.writeAttribute("state", "stopped"); + writer.writeEndElement(); + + writer.writeStartElement("Timeline"); + writer.writeAttribute("type", "photo"); + writer.writeAttribute("state", "stopped"); + writer.writeEndElement(); + + writer.writeEndElement(); + writer.writeEndDocument(); + + return xmlData; + } + else + { + return m_timeline.toByteArray(2); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +void RemoteSubscriber::setCommandId(quint64 playerCommandId, quint64 controllerCommandId) +{ + QMutexLocker lk(&m_commandIdMapLock); + + m_commandIdMap[playerCommandId] = controllerCommandId; + + // maintain a queue of playerCommandId's so we know in what order to + // drop them if we get a lot of them. before we queue the current one + // make sure that we remove any old reference to it so it won't get + // deleted from the map to early + // + m_commandIdQueue.removeAll(playerCommandId); + m_commandIdQueue.enqueue(playerCommandId); + + if (m_commandIdQueue.size() > 5) + { + // remove the last one + quint64 keyToRemove = m_commandIdQueue.dequeue(); + m_commandIdMap.remove(keyToRemove); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +RemotePollSubscriber::RemotePollSubscriber(const QString &clientIdentifier, const QString &deviceName, QHttpResponse *response, QObject *parent) : + RemoteSubscriber(clientIdentifier, deviceName, QUrl(""), parent), m_response(response) +{ + +} + +///////////////////////////////////////////////////////////////////////////////////////// +void RemotePollSubscriber::setHTTPResponse(QHttpResponse *response) +{ + m_response = response; + m_response->setHeader("Content-Type", "application/xml"); + m_response->setHeader("Access-Control-Expose-Headers", "X-Plex-Client-Identifier"); + + connect(m_response, &QHttpResponse::done, this, &RemotePollSubscriber::responseDone); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void RemotePollSubscriber::sendUpdate() +{ + if (m_response) + { + // if we have a response, we are handling a poll request + m_response->writeHead(QHttpResponse::STATUS_OK); + m_response->write(getTimeline()); + + m_response->end(); + m_response = NULL; + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +void RemotePollSubscriber::responseDone() +{ + m_response = NULL; +} diff --git a/src/remote/RemoteSubscriber.h b/src/remote/RemoteSubscriber.h new file mode 100644 index 0000000..2a9b49c --- /dev/null +++ b/src/remote/RemoteSubscriber.h @@ -0,0 +1,87 @@ +// +// Created by Tobias Hieta on 31/03/15. +// + +#ifndef KONVERGO_REMOTESUBSCRIBER_H +#define KONVERGO_REMOTESUBSCRIBER_H + +#include +#include +#include +#include +#include +#include + +#include "qhttpresponse.h" + +///////////////////////////////////////////////////////////////////////////////////////// +class RemoteSubscriber : public QObject +{ +public: + RemoteSubscriber(const QString& clientIdentifier, const QString& deviceName, const QUrl& address, QObject* parent = 0); + void reSubscribe(); + int lastSubscribe() const { return m_subscribeTime.elapsed(); } + + QString deviceName(); + QString clientIdentifier(); + virtual void sendUpdate(); + void timelineFinished(QNetworkReply* reply); + void queueTimeline(quint64 playerCommandID, const QByteArray& timelineData); + QByteArray getTimeline(); + void setCommandId(quint64 playerCommandId, quint64 controllerCommandId); + + quint64 mostRecentCommandId() + { + QMutexLocker lk(&m_commandIdMapLock); + if (!m_commandIdQueue.isEmpty() && !m_commandIdMap.isEmpty()) + return m_commandIdMap[m_commandIdQueue.last()]; + else + return 0; + } + + quint64 commandId(quint64 playerCommandId) + { + QMutexLocker lk(&m_commandIdMapLock); + if (m_commandIdMap.contains(playerCommandId)) + return m_commandIdMap[playerCommandId]; + lk.unlock(); + return mostRecentCommandId(); + } + +private: + QMutex m_commandIdMapLock; + QHash m_commandIdMap; + QQueue m_commandIdQueue; + + QNetworkAccessManager* m_netAccess; + QUrl m_address; + QTime m_subscribeTime; + + QMutex m_timelineLock; + QDomDocument m_timeline; + +protected: + QString m_clientIdentifier; + QString m_deviceName; + +#if 0 + quint16 m_errors; + #endif +}; + +///////////////////////////////////////////////////////////////////////////////////////// +class RemotePollSubscriber : public RemoteSubscriber +{ +public: + RemotePollSubscriber(const QString& clientIdentifier, const QString& deviceName, QHttpResponse *response, QObject* parent = 0); + void setHTTPResponse(QHttpResponse *response); + virtual void sendUpdate(); + +private : + QHttpResponse* m_response; + +public Q_SLOTS: + void responseDone(); +}; + +#endif //KONVERGO_REMOTESUBSCRIBER_H diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt new file mode 100644 index 0000000..493d7a9 --- /dev/null +++ b/src/server/CMakeLists.txt @@ -0,0 +1,3 @@ +add_sources( + HTTPServer.cpp HTTPServer.h +) \ No newline at end of file diff --git a/src/server/HTTPServer.cpp b/src/server/HTTPServer.cpp new file mode 100644 index 0000000..d822789 --- /dev/null +++ b/src/server/HTTPServer.cpp @@ -0,0 +1,170 @@ +#include "HTTPServer.h" + +#include +#include + +#include "QsLog.h" +#include "utils/Utils.h" +#include "settings/SettingsComponent.h" +#include "remote/RemoteComponent.h" +#include "Paths.h" + +#define WEB_CLIENT_PATH "/web/tv" + +///////////////////////////////////////////////////////////////////////////////////////// +HttpServer::HttpServer(QObject* parent) : QObject(parent) +{ + m_server = new QHttpServer(this); + m_baseUrl = ":/konvergo"; + m_port = (quint16)SettingsComponent::Get().value(SETTINGS_SECTION_MAIN, "webserverport").toUInt(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void HttpServer::start() +{ + connect(m_server, &QHttpServer::newRequest, this, &HttpServer::handleRequest); + if (!m_server->listen(QHostAddress::AnyIPv4, m_port)) + throw FatalException(tr("Could not start local web server:") + "
    " + m_server->errorString()); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void HttpServer::writeError(QHttpResponse* response, QHttpResponse::StatusCode errorCode) +{ + QByteArray errorStyle = ""; + response->writeHead(errorCode); + response->write(errorStyle); + + QByteArray error; + switch (errorCode) + { + case QHttpResponse::STATUS_FORBIDDEN: + error = "Access to file denied. You might want to check permissions"; + break; + case QHttpResponse::STATUS_NOT_FOUND: + error = "Could not find that file. Install might be broken?"; + break; + case QHttpResponse::STATUS_METHOD_NOT_ALLOWED: + error = "That method is not allowed."; + break; + case QHttpResponse::STATUS_NOT_IMPLEMENTED: + error = "This request is not yet implemented"; + break; + default: + error = "Generic error, something went wrong"; + break; + } + response->write("

    "); + response->write(error); + response->write("

    "); +} + +///////////////////////////////////////////////////////////////////////////////////////// +bool HttpServer::writeFile(const QString& file, QHttpResponse* response) +{ + QLOG_DEBUG() << "Going to request file:" << qPrintable(file); + if (QFile::exists(file)) + { + QFile fp(file); + if (fp.open(QFile::ReadOnly)) + { + response->writeHead(QHttpResponse::STATUS_OK); + response->write(fp.readAll()); + fp.close(); + return true; + } + else + { + writeError(response, QHttpResponse::STATUS_FORBIDDEN); + return false; + } + } + else + { + writeError(response, QHttpResponse::STATUS_NOT_FOUND); + return false; + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +void HttpServer::handleWebClientRequest(QHttpRequest* request, QHttpResponse* response) +{ + switch (request->method()) + { + case QHttpRequest::HTTP_GET: + { + // cut the WEB_CLIENT_PATH prefix + QString relativeUrl = request->path(); + relativeUrl.replace(WEB_CLIENT_PATH, ""); + QString rUrl = m_baseUrl + relativeUrl; + + writeFile(rUrl, response); + break; + } + + default: + { + writeError(response, QHttpResponse::STATUS_METHOD_NOT_ALLOWED); + QLOG_WARN() << "Method" << qPrintable(request->methodString()) << "is not supported"; + } + } + + response->end(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void HttpServer::handleResource(QHttpRequest* request, QHttpResponse* response) +{ + RemoteComponent::Get().handleResource(request, response); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void HttpServer::handleRemoteController(QHttpRequest* request, QHttpResponse* response) +{ + RemoteComponent::Get().handleCommand(request, response); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void HttpServer::handleFilesRequest(QHttpRequest* request, QHttpResponse* response) +{ + if (request->path() == "/files/qwebchannel.js") + writeFile(":/qtwebchannel/qwebchannel.js", response); + else + writeError(response, QHttpResponse::STATUS_NOT_FOUND); + + response->end(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void HttpServer::handleRequest(QHttpRequest* request, QHttpResponse* response) +{ + QLOG_DEBUG() << "Incoming request to:" << request->url().toString() << "from" << request->remoteAddress(); + + QString path = request->path(); + if (path.startsWith(WEB_CLIENT_PATH)) + { + handleWebClientRequest(request, response); + } + else if (path.startsWith("/resources")) + { + handleResource(request, response); + } + else if (path.startsWith("/player")) + { + handleRemoteController(request, response); + } + else if (path.startsWith("/files")) + { + handleFilesRequest(request, response); + } + else if (path == "/") + { + response->writeHead(QHttpResponse::STATUS_OK); + response->end("You should not be here :-)"); + } + else + { + writeError(response, QHttpResponse::STATUS_NOT_FOUND); + response->end(); + } +} + diff --git a/src/server/HTTPServer.h b/src/server/HTTPServer.h new file mode 100644 index 0000000..e42f12d --- /dev/null +++ b/src/server/HTTPServer.h @@ -0,0 +1,34 @@ +#ifndef HTTPSERVER_H +#define HTTPSERVER_H + +#include +#include +#include +#include + +#include "qhttpserver.h" + +class HttpServer : public QObject +{ + Q_OBJECT +public: + HttpServer(QObject* parent); + void start(); + +private slots: + void handleRequest(QHttpRequest *request, QHttpResponse *response); + void handleWebClientRequest(QHttpRequest * request, QHttpResponse * response); + void handleResource(QHttpRequest* request, QHttpResponse* response); + void handleRemoteController(QHttpRequest* request, QHttpResponse* response); + void handleFilesRequest(QHttpRequest* request, QHttpResponse* response); + void writeError(QHttpResponse* response, QHttpResponse::StatusCode errorCode); + +private: + bool writeFile(const QString& file, QHttpResponse* response); + + QHttpServer* m_server; + QString m_baseUrl; + quint16 m_port; +}; + +#endif // HTTPSERVER_H diff --git a/src/settings/AudioSettingsController.cpp b/src/settings/AudioSettingsController.cpp new file mode 100644 index 0000000..a38284a --- /dev/null +++ b/src/settings/AudioSettingsController.cpp @@ -0,0 +1,61 @@ +// +// Created by Tobias Hieta on 21/08/15. +// + +#include "AudioSettingsController.h" +#include "SettingsComponent.h" +#include "SettingsSection.h" +#include "PlayerComponent.h" + +///////////////////////////////////////////////////////////////////////////////////////// +AudioSettingsController::AudioSettingsController(QObject* parent) : QObject(parent) +{ + SettingsSection* audioSection = SettingsComponent::Get().getSection(SETTINGS_SECTION_AUDIO); + connect(audioSection, &SettingsSection::valuesUpdated, this, &AudioSettingsController::valuesUpdated); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void AudioSettingsController::setHiddenPassthrough(const QStringList& codecs, bool hidden) +{ + SettingsSection* audioSection = SettingsComponent::Get().getSection(SETTINGS_SECTION_AUDIO); + foreach(const QString& codec, codecs) + audioSection->setValueHidden("passthrough." + codec, hidden); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void AudioSettingsController::valuesUpdated(const QVariantMap& values) +{ + bool advanced = SettingsComponent::Get().value(SETTINGS_SECTION_AUDIO, "advanced").toBool(); + SettingsSection* audioSection = SettingsComponent::Get().getSection(SETTINGS_SECTION_AUDIO); + + if (values.contains("devicetype")) + { + QString type = values.value("devicetype").toString(); + if (type == AUDIO_DEVICE_TYPE_BASIC) + { + setHiddenPassthrough(PlayerComponent::AudioCodecsAll(), true); + } + else if (type == AUDIO_DEVICE_TYPE_HDMI) + { + setHiddenPassthrough(PlayerComponent::AudioCodecsAll(), !advanced); + } + else if (type == AUDIO_DEVICE_TYPE_SPDIF) + { + setHiddenPassthrough(PlayerComponent::AudioCodecsAll(), true); + setHiddenPassthrough(PlayerComponent::AudioCodecsSPDIF(), false); + } + + emit settingsUpdated(SETTINGS_SECTION_AUDIO, audioSection->descriptions()); + } + + if (values.contains("advanced")) + { + advanced = values.value("advanced").toBool(); + if (audioSection->value("devicetype") == AUDIO_DEVICE_TYPE_HDMI) + setHiddenPassthrough(PlayerComponent::AudioCodecsAll(), !advanced); + + audioSection->setValueHidden("exclusive", !advanced); + + emit settingsUpdated(SETTINGS_SECTION_AUDIO, audioSection->descriptions()); + } +} diff --git a/src/settings/AudioSettingsController.h b/src/settings/AudioSettingsController.h new file mode 100644 index 0000000..5f33846 --- /dev/null +++ b/src/settings/AudioSettingsController.h @@ -0,0 +1,22 @@ +// +// Created by Tobias Hieta on 21/08/15. +// + +#ifndef KONVERGO_AUDIOSETTINGSCONTROLLER_H +#define KONVERGO_AUDIOSETTINGSCONTROLLER_H + +#include + +class AudioSettingsController : public QObject +{ + Q_OBJECT +public: + AudioSettingsController(QObject* parent = 0); + Q_SLOT void valuesUpdated(const QVariantMap& values); + Q_SIGNAL void settingsUpdated(const QString& section, const QVariant& description); + +private: + void setHiddenPassthrough(const QStringList& codecs, bool hidden); +}; + +#endif //KONVERGO_AUDIOSETTINGSCONTROLLER_H diff --git a/src/settings/CMakeLists.txt b/src/settings/CMakeLists.txt new file mode 100644 index 0000000..6cdb928 --- /dev/null +++ b/src/settings/CMakeLists.txt @@ -0,0 +1,6 @@ +add_sources( + AudioSettingsController.cpp AudioSettingsController.h + SettingsComponent.cpp SettingsComponent.h + SettingsSection.cpp SettingsSection.h + SettingsValue.h +) \ No newline at end of file diff --git a/src/settings/SettingsComponent.cpp b/src/settings/SettingsComponent.cpp new file mode 100644 index 0000000..3891111 --- /dev/null +++ b/src/settings/SettingsComponent.cpp @@ -0,0 +1,505 @@ +#include "SettingsComponent.h" +#include "SettingsSection.h" +#include "Paths.h" +#include "utils/Utils.h" +#include "QsLog.h" +#include "AudioSettingsController.h" +#include "Names.h" + +#include +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////////////////////////////// +SettingsComponent::SettingsComponent(QObject *parent) : ComponentBase(parent), m_settingsVersion(-1) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void SettingsComponent::updatePossibleValues(const QString §ionID, const QString &key, const QVariantList &possibleValues) +{ + SettingsSection* section = getSection(sectionID); + if (!section) + { + QLOG_ERROR() << "Section" << sectionID << "is unknown"; + return; + } + section->updatePossibleValues(key, possibleValues); + QLOG_DEBUG() << "Updated possible values for:" << key << "to" << possibleValues; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +QVariant SettingsComponent::allValues(const QString& section) +{ + if (section.isEmpty()) + { + QVariantMap all; + foreach (const QString& sname, m_sections.keys()) + all[sname] = m_sections[sname]->allValues(); + return all; + } + else if (m_sections.contains(section)) + { + return m_sections[section]->allValues(); + } + else + { + // look for the value in the webclient section + return m_sections[SETTINGS_SECTION_WEBCLIENT]->value(section); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +static QByteArray loadFile(const QString& filename) +{ + QLOG_DEBUG() << "Opening:" << filename; + + QFile file(filename); + // Checking existence before opening is technically a race condition, but + // it looks like Qt doesn't let us distinguish errors on opening. + if (!file.exists()) + return ""; + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + { + QLOG_ERROR() << "Could not open" << filename; + return ""; + } + return file.readAll(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +static void writeFile(const QString& filename, const QByteArray& data) +{ + QLOG_DEBUG() << "Writing:" << filename; + + QSaveFile file(filename); + file.open(QIODevice::WriteOnly | QIODevice::Text); + file.write(data); + if (!file.commit()) + { + QLOG_ERROR() << "Could not write" << filename; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +static QJsonObject loadJson(const QString& filename) +{ + QJsonDocument json = QJsonDocument::fromJson(loadFile(filename)); + return json.object(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +static void writeJson(const QString& filename, const QJsonObject& data, bool pretty = true) +{ + QJsonDocument json(data); + writeFile(filename, json.toJson(pretty ? QJsonDocument::Indented : QJsonDocument::Compact)); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void SettingsComponent::load() +{ + loadConf(Paths::dataDir("plexmediaplayer.conf"), false); + loadConf(Paths::dataDir("storage.json"), true); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void SettingsComponent::loadConf(const QString& path, bool storage) +{ + QJsonObject json = loadJson(path); + + int version = json["version"].toInt(0); + if (version != m_settingsVersion) + { + QString backup = path + ".broken"; + QFile::remove(backup); + QFile::rename(path, backup); + if (version == 0) + QLOG_ERROR() << "Could not read config file."; + else + QLOG_ERROR() << "Config version is" << version << "but" << m_settingsVersion << "expected. Moving old config to" << backup; + return; + } + + QJsonObject jsonSections = json["sections"].toObject(); + + foreach (const QString& section, jsonSections.keys()) + { + QJsonObject jsonSection = jsonSections[section].toObject(); + + SettingsSection* sec = getSection(section); + if (!sec && storage) + { + sec = new SettingsSection(section, PLATFORM_ANY, -1, this); + sec->setHidden(true); + sec->setStorage(true); + m_sections.insert(section, sec); + } + else if (!sec) + { + QLOG_ERROR() << "Trying to load section:" << section << "from config file, but we don't want that."; + continue; + } + + foreach (const QString& setting, jsonSection.keys()) + sec->setValue(setting, jsonSection.value(setting).toVariant()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void SettingsComponent::save(bool justStorage) +{ + QVariantMap sections; + QVariantMap storage; + + foreach(SettingsSection* section, m_sections.values()) + { + if (section->isStorage()) + storage.insert(section->sectionName(), section->allValues()); + else + sections.insert(section->sectionName(), section->allValues()); + } + + if (!justStorage) + { + QJsonObject json; + json.insert("sections", QJsonValue::fromVariant(sections)); + json.insert("version", m_settingsVersion); + writeJson(Paths::dataDir("plexmediaplayer.conf"), json); + } + + QJsonObject storagejson; + storagejson.insert("sections", QJsonValue::fromVariant(storage)); + storagejson.insert("version", m_settingsVersion); + writeJson(Paths::dataDir("storage.json"), storagejson, false); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +QVariant SettingsComponent::value(const QString& sectionID, const QString &key) +{ + SettingsSection* section = getSection(sectionID); + if (!section) + { + QLOG_ERROR() << "Section" << sectionID << "is unknown"; + return QVariant(); + } + return section->value(key); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void SettingsComponent::setValue(const QString& sectionID, const QString &key, const QVariant &value) +{ + SettingsSection* section = getSection(sectionID); + if (!section) + { + QLOG_ERROR() << "Section" << sectionID << "is unknown"; + return; + } + section->setValue(key, value); + save(false); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void SettingsComponent::setValues(const QVariantMap& options) +{ + Q_ASSERT(options.contains("key")); + Q_ASSERT(options.contains("value")); + + QString key = options["key"].toString(); + QVariant values = options["value"]; + + bool updatedStorageOnly = true; + + if (values.type() == QVariant::Map || values.isNull()) + { + SettingsSection* section = getSection(key); + if (!section) + { + // let's create this section since it's most likely created by the webclient + section = new SettingsSection(key, PLATFORM_ANY, -1, this); + section->setHidden(true); + section->setStorage(true); + m_sections.insert(key, section); + } + + if (values.isNull()) + section->removeValues(); + else + section->setValues(values); + + updatedStorageOnly = section->isStorage(); + } + else if (values.type() == QVariant::String) + { + setValue(SETTINGS_SECTION_WEBCLIENT, key, values); + } + else + { + QLOG_WARN() << "the values sent was not a map, string or empty value. it will be ignored"; + + // return so we don't call save() + return; + } + + save(updatedStorageOnly); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void SettingsComponent::removeValue(const QString §ionOrKey) +{ + SettingsSection* section = getSection(sectionOrKey); + + if (section) + { + // we want to remove a full section + // dont remove the section, but remove all keys + section->removeValues(); + save(false); + } + else + { + // we want to remove a root key from webclient + // which is stored in webclient section + section = m_sections[SETTINGS_SECTION_WEBCLIENT]; + section->removeValue(sectionOrKey); + save(false); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void SettingsComponent::resetToDefault() +{ + foreach(SettingsSection *section, m_sections) + { + section->resetToDefault(); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +struct section_order_index +{ + inline bool operator ()(SettingsSection* a, SettingsSection* b) + { + return a->orderIndex() < b->orderIndex(); + } +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +QVariantList SettingsComponent::settingDescriptions() +{ + QJsonArray desc; + + QList sectionList = m_sections.values(); + std::sort(sectionList.begin(), sectionList.end(), section_order_index()); + + foreach(SettingsSection* section, sectionList) + { + if (!section->isHidden()) + desc.push_back(QJsonValue::fromVariant(section->descriptions())); + } + + QJsonDocument doc = QJsonDocument(desc); + QLOG_DEBUG() << "Settings description:\n" << qPrintable(doc.toJson()); + + return desc.toVariantList(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +bool SettingsComponent::loadDescription() +{ + QJsonParseError err; + auto doc = Utils::OpenJsonDocument(":/settings/settings_description.json", &err); + if (doc.isNull()) + { + QLOG_ERROR() << "Failed to read settings description:" << err.errorString(); + throw FatalException("Failed to read settings description!"); + } + + if (!doc.isArray()) + { + QLOG_ERROR() << "The object needs to be an array"; + return false; + } + + m_sectionIndex = 0; + + foreach(const QJsonValue& val, doc.array()) + { + if (!val.isObject()) + { + QLOG_ERROR() << "Hoped to find sections in the root array, but they where not JSON objects"; + return false; + } + + QJsonObject section = val.toObject(); + if (!section.contains("section")) + { + QLOG_ERROR() << "A section needs to contain the section keyword."; + return false; + } + + if (section["section"] == "__meta__") + m_settingsVersion = section.value("version").toInt(); + else + parseSection(section); + } + + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////// +void SettingsComponent::parseSection(const QJsonObject& sectionObject) +{ + QString sectionName = sectionObject.value("section").toString(); + if (!sectionObject.contains("values") || !sectionObject.value("values").isArray()) + { + QLOG_ERROR() << "section object:" << sectionName << "did not contain a values array"; + return; + } + + int platformMask = platformMaskFromObject(sectionObject); + + SettingsSection* section = new SettingsSection(sectionName, (quint8)platformMask, m_sectionIndex ++, this); + section->setHidden(sectionObject.value("hidden").toBool(false)); + section->setStorage(sectionObject.value("storage").toBool(false)); + + auto values = sectionObject.value("values").toArray(); + int order = 0; + foreach(auto val, values) + { + if (!val.isObject()) + continue; + + QJsonObject valobj = val.toObject(); + if (!valobj.contains("value") || !valobj.contains("default") || valobj.value("value").isNull()) + continue; + + int vPlatformMask = platformMaskFromObject(valobj); + SettingsValue* setting = new SettingsValue(valobj.value("value").toString(), valobj.value("default").toVariant(), (quint8)vPlatformMask, this); + setting->setHidden(valobj.value("hidden").toBool(false)); + setting->setIndexOrder(order ++); + + if (valobj.contains("possible_values") && valobj.value("possible_values").isArray()) + { + auto list = valobj.value("possible_values").toArray(); + foreach(const auto& v, list) + { + int platform = PLATFORM_ANY; + + auto vl = v.toArray(); + if (vl.size() < 2) + continue; + + if (vl.size() == 3 && vl.at(2).isObject()) + { + // platform options + QJsonObject options = vl.at(2).toObject(); + platform = platformMaskFromObject(options); + } + + if ((platform & Utils::CurrentPlatform()) == Utils::CurrentPlatform()) + setting->addPossibleValue(vl.at(1).toString(), vl.at(0).toVariant()); + } + } + + section->registerSetting(setting); + } + + m_sections.insert(sectionName, section); + QLOG_DEBUG() << "Added section:" << sectionName; +} + +///////////////////////////////////////////////////////////////////////////////////////// +int SettingsComponent::platformMaskFromObject(const QJsonObject& object) +{ + int platformMask = PLATFORM_ANY; + + // only include the listed platforms + if (object.contains("platforms")) + { + // start by resetting it to no platforms + platformMask = PLATFORM_UNKNOWN; + + QJsonValue platforms = object.value("platforms"); + + // platforms can be both array or a single string + if (platforms.isArray()) + { + foreach(const QJsonValue& pl, platforms.toArray()) + { + if (!pl.isString()) + continue; + + platformMask |= platformFromString(pl.toString()); + } + } + else + { + platformMask = platformFromString(platforms.toString()); + } + } + else if (object.contains("platforms_excluded")) + { + QJsonValue val = object.value("platforms_excluded"); + if (val.isArray()) + { + foreach(const QJsonValue& pl, val.toArray()) + { + if (!pl.isString()) + continue; + + platformMask &= ~platformFromString(pl.toString()); + } + } + else + { + platformMask &= ~platformFromString(val.toString()); + } + } + + return platformMask; +} + +///////////////////////////////////////////////////////////////////////////////////////// +Platform SettingsComponent::platformFromString(const QString& platformString) +{ + if (platformString == "osx") + return PLATFORM_OSX; + else if (platformString == "windows") + return PLATFORM_WINDOWS; + else if (platformString == "linux") + return PLATFORM_LINUX; + else if (platformString == "oe") + return PLATFORM_OE; + else if (platformString == "oe_rpi") + return PLATFORM_OE_RPI; + else if (platformString == "oe_x86") + return PLATFORM_OE_X86; + else if (platformString == "any") + return PLATFORM_ANY; + + return PLATFORM_UNKNOWN; +} + +///////////////////////////////////////////////////////////////////////////////////////// +bool SettingsComponent::componentInitialize() +{ + if (!loadDescription()) + return false; + + load(); + + // add our AudioSettingsController that will inspect audio settings and react. + // then run the signal the first time to make sure that we set the proper visibility + // on the items from the start. + // + AudioSettingsController* ctrl = new AudioSettingsController(this); + QVariantMap val; + val.insert("devicetype", value(SETTINGS_SECTION_AUDIO, "devicetype")); + val.insert("advanced", value(SETTINGS_SECTION_AUDIO, "advanced")); + ctrl->valuesUpdated(val); + connect(ctrl, &AudioSettingsController::settingsUpdated, this, &SettingsComponent::groupUpdate); + + return true; +} \ No newline at end of file diff --git a/src/settings/SettingsComponent.h b/src/settings/SettingsComponent.h new file mode 100644 index 0000000..ebdfe4d --- /dev/null +++ b/src/settings/SettingsComponent.h @@ -0,0 +1,78 @@ +#ifndef SETTINGSCOMPONENT_H +#define SETTINGSCOMPONENT_H + +#include +#include "utils/Utils.h" +#include "ComponentManager.h" +#include "SettingsValue.h" + +#define SETTINGS_SECTION_AUDIO "audio" +#define SETTINGS_SECTION_VIDEO "video" +#define SETTINGS_SECTION_MAIN "main" +#define SETTINGS_SECTION_STATE "state" +#define SETTINGS_SECTION_PATH "path" +#define SETTINGS_SECTION_WEBCLIENT "webclient" +#define SETTINGS_SECTION_SUBTITLES "subtitles" +#define SETTINGS_SECTION_OVERRIDES "overrides" +#define SETTINGS_SECTION_CEC "cec" +#define SETTINGS_SECTION_OPENELEC "openelec" + +#define AUDIO_DEVICE_TYPE_BASIC "basic" +#define AUDIO_DEVICE_TYPE_SPDIF "spdif" +#define AUDIO_DEVICE_TYPE_HDMI "hdmi" + + +class SettingsSection; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +class SettingsComponent : public ComponentBase +{ + Q_OBJECT + DEFINE_SINGLETON(SettingsComponent); + +public: + bool componentInitialize(); + + const char* componentName() { return "settings"; } + bool componentExport() { return true; } + + SettingsSection* getSection(const QString& sectionID) + { + if (m_sections.contains(sectionID)) + return m_sections.value(sectionID); + else + return nullptr; + } + + // JS interface + Q_INVOKABLE void setValue(const QString& sectionID, const QString& key, const QVariant& value); + Q_INVOKABLE void setValues(const QVariantMap& options); + Q_INVOKABLE QVariant value(const QString& sectionID, const QString& key); + Q_INVOKABLE QVariant allValues(const QString& section = ""); + Q_INVOKABLE void removeValue(const QString& sectionOrKey); + Q_INVOKABLE void resetToDefault(); + Q_INVOKABLE QVariantList settingDescriptions(); + + void updatePossibleValues(const QString& sectionID, const QString& key, const QVariantList& possibleValues); + + void save(bool justStorage); + void load(); + + Q_SIGNAL void groupUpdate(const QString& section, const QVariant& description); + +private: + explicit SettingsComponent(QObject *parent = 0); + bool loadDescription(); + void parseSection(const QJsonObject& sectionObject); + int platformMaskFromObject(const QJsonObject& object); + Platform platformFromString(const QString& platformString); + + QMap m_sections; + + int m_settingsVersion; + int m_sectionIndex; + + void loadConf(const QString& path, bool storage); +}; + +#endif // SETTINGSCOMPONENT_H diff --git a/src/settings/SettingsSection.cpp b/src/settings/SettingsSection.cpp new file mode 100644 index 0000000..1bd2e90 --- /dev/null +++ b/src/settings/SettingsSection.cpp @@ -0,0 +1,165 @@ +#include "SettingsSection.h" +#include "QsLog.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +SettingsSection::SettingsSection(const QString& sectionID, quint8 platforms, int _orderIndex, QObject* parent) + : QObject(parent), m_sectionID(sectionID), m_orderIndex(_orderIndex), m_platform(platforms), m_hidden(false), m_storage(false) +{ + m_values.clear(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void SettingsSection::registerSetting(SettingsValue* value) +{ + if (m_values.contains(value->key())) + { + QLOG_WARN() << QString("Trying to register %1.%2 multiple times").arg(m_sectionID).arg(value->key()); + return; + } + + value->setParent(this); + m_values[value->key()] = value; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void SettingsSection::setValues(const QVariant& values) +{ + QVariantMap map = values.toMap(); + QVariantMap updatedValues; + + // values not included in the map are reset to default + foreach (const QString& key, m_values.keys()) + { + if (!map.contains(key)) + map[key] = m_values[key]->defaultValue(); + } + + foreach (const QString& key, map.keys()) + { + if (key.isEmpty()) + continue; + + bool haveKey = m_values.contains(key); + if (haveKey && m_values[key]->value() == map[key]) + continue; + + if (haveKey) + { + m_values[key]->setValue(map[key]); + } + else + { + m_values[key] = new SettingsValue(key, QVariant(), PLATFORM_ANY, this); + m_values[key]->setValue(map[key]); + } + + updatedValues.insert(key, map[key]); + } + + if (updatedValues.size() > 0) + emit valuesUpdated(updatedValues); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +QVariant SettingsSection::value(const QString& key) +{ + if (m_values.contains(key)) + return m_values[key]->value(); + + QLOG_WARN() << "Looking for value:" << key << "in section:" << m_sectionID << "but it can't be found"; + return QVariant(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void SettingsSection::updatePossibleValues(const QString &key, const QVariantList &possibleValues) +{ + if (m_values.contains(key)) + m_values[key]->setPossibleValues(possibleValues); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool SettingsSection::setValue(const QString& key, const QVariant& value) +{ + if (key == "index") + return false; + + QVariantMap values; + // populate with default values (setValues() resets missing values) + foreach (const QString& entry, m_values.keys()) + values[entry] = m_values[entry]->value(); + + values[key] = value; + + setValues(values); + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void SettingsSection::removeValue(const QString &key) +{ + if (m_values.contains(key)) + { + m_values[key]->deleteLater(); + m_values.remove(key); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void SettingsSection::removeValues() +{ + m_values.clear(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +const QVariantMap SettingsSection::allValues() const +{ + QVariantMap values; + + foreach (SettingsValue* val, m_values.values()) + values[val->key()] = val->value(); + + return values; +} + +///////////////////////////////////////////////////////////////////////////////////////// +struct value_sort_order +{ + inline bool operator()(SettingsValue* a, SettingsValue* b) + { + return a->indexOrder() < b->indexOrder(); + } +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +const QVariantMap SettingsSection::descriptions() const +{ + QVariantMap map; + + map.insert("key", m_sectionID); + + QList list = m_values.values(); + std::sort(list.begin(), list.end(), value_sort_order()); + + QVariantList settings; + foreach(SettingsValue* value, list) + { + if (!value->isHidden()) + settings.push_back(value->descriptions()); + } + map.insert("settings", settings); + return map; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void SettingsSection::resetToDefault() +{ + foreach (const QString& key, m_values.keys()) + m_values[key]->reset(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +bool SettingsSection::isHidden() const +{ + bool correctPlatform = ((m_platform & Utils::CurrentPlatform()) == Utils::CurrentPlatform()); + return (m_hidden || !correctPlatform); +} \ No newline at end of file diff --git a/src/settings/SettingsSection.h b/src/settings/SettingsSection.h new file mode 100644 index 0000000..fe3d941 --- /dev/null +++ b/src/settings/SettingsSection.h @@ -0,0 +1,65 @@ +#ifndef SETTINGSSECTION_H +#define SETTINGSSECTION_H + +#include +#include +#include +#include "SettingsValue.h" +#include "SettingsComponent.h" +#include "QsLog.h" + +class SettingsSection : public QObject +{ + Q_OBJECT +public: + explicit SettingsSection(const QString& sectionID, quint8 platforms = PLATFORM_ANY, + int _orderIndex = -1, QObject* parent = 0); + + void updatePossibleValues(const QString& key, const QVariantList& possibleValues); + + void setValues(const QVariant& values); + bool setValue(const QString& key, const QVariant& value); + void removeValue(const QString& key); + void removeValues(); + void resetToDefault(); + void registerSetting(SettingsValue* value); + bool isHidden() const; + + QVariant value(const QString& key); + QString sectionName() const { return m_sectionID; } + + const QVariantMap allValues() const; + const QVariantMap descriptions() const; + + bool isValueHidden(const QString& key) const { return m_values[key]->isHidden(); } + int orderIndex() const { return m_orderIndex; } + + void setHidden(bool hidden=true) + { + m_hidden = hidden; + } + + void setValueHidden(const QString& value, bool hidden) + { + if (m_values.contains(value)) + m_values.value(value)->setHidden(hidden); + } + + void setStorage(bool storage) { m_storage = storage; } + bool isStorage() const + { + return m_storage; + } + + Q_SIGNAL void valuesUpdated(const QVariantMap& values); + +protected: + QHash m_values; + QString m_sectionID; + int m_orderIndex; + quint8 m_platform; + bool m_hidden; + bool m_storage; +}; + +#endif // SETTINGSSECTION_H diff --git a/src/settings/SettingsValue.h b/src/settings/SettingsValue.h new file mode 100644 index 0000000..cc1a050 --- /dev/null +++ b/src/settings/SettingsValue.h @@ -0,0 +1,137 @@ +// +// Created by Tobias Hieta on 12/08/15. +// + +#ifndef KONVERGO_SETTINGS_VALUE_H +#define KONVERGO_SETTINGS_VALUE_H + +#include +#include + +class SettingsValue : public QObject +{ + Q_OBJECT + +public: + SettingsValue(QObject* parent = 0) + : QObject(parent) + , m_platform(PLATFORM_UNKNOWN) + , m_hidden(true) + {} + + SettingsValue(const QString& _key, QVariant _defaultValue=QVariant(), quint8 platforms = PLATFORM_ANY, QObject* parent = 0) + : QObject(parent) + , m_key(_key) + , m_value(QVariant()) + , m_defaultValue(_defaultValue) + , m_platform(platforms) + , m_hidden(true) + {} + + const QString& key() const { return m_key; } + + const QVariant& value() const + { + if (!m_value.isValid()) + return m_defaultValue; + return m_value; + } + + void setValue(const QVariant& value) + { + m_value = value; + } + + const QVariant& defaultValue() const + { + return m_defaultValue; + } + + void setDefaultValue(const QVariant& defaultValue) + { + m_defaultValue = defaultValue; + } + + const QVariantList& possibleValues() const + { + return m_possibleValues; + } + + void setPossibleValues(const QVariantList& possibleValues) + { + m_possibleValues = possibleValues; + } + + const quint8 platform() const + { + return m_platform; + } + + void setPlatform(quint8 platform) + { + m_platform = platform; + } + + void setHidden(bool hidden) + { + m_hidden = hidden; + } + + bool isHidden() const + { + bool correctPlatform = ((m_platform & Utils::CurrentPlatform()) == Utils::CurrentPlatform()); + return (m_hidden || !correctPlatform); + } + + void addPossibleValue(const QString& key, const QVariant& value) + { + QVariantMap entry; + entry["value"] = value; + entry["title"] = key; + m_possibleValues << entry; + } + + void addNumericRange(double min, double max, double step = 1.0) + { + QVariantList values; + for (double v = min; v <= max; v += step) + addPossibleValue(QString::number(v), v); + } + + // goes back to use the default + void reset() + { + m_value.clear(); + } + + QVariantMap descriptions() const + { + QVariantMap ret; + ret.insert("key", m_key); + + if (!m_possibleValues.isEmpty()) + ret.insert("options", m_possibleValues); + + return ret; + } + + void setIndexOrder(int order) + { + m_indexOrder = order; + } + + int indexOrder() const { return m_indexOrder; } + +private: + QString m_key; + QVariant m_value; + QVariant m_defaultValue; + QVariantList m_possibleValues; + quint8 m_platform; + bool m_hidden; + + int m_indexOrder; +}; + +#endif //KONVERGO_SETTINGS_VALUE_H + diff --git a/src/shared/CMakeLists.txt b/src/shared/CMakeLists.txt new file mode 100644 index 0000000..ec986f1 --- /dev/null +++ b/src/shared/CMakeLists.txt @@ -0,0 +1,11 @@ +include_directories(${PROJECT_SOURCE_DIR}/src) + +add_library(shared STATIC + Paths.cpp Paths.h + LocalJsonClient.cpp LocalJsonClient.h + LocalJsonServer.cpp LocalJsonServer.h + UniqueApplication.h + ${CMAKE_BINARY_DIR}/src/Version.cpp + ${CMAKE_CURRENT_BINARY_DIR}/Names.cpp Names.h) + +set_target_properties(shared PROPERTIES CXX_STANDARD 11 CXX_STANDARD_REQUIRED TRUE) \ No newline at end of file diff --git a/src/shared/LocalJsonClient.cpp b/src/shared/LocalJsonClient.cpp new file mode 100644 index 0000000..dfc1b3f --- /dev/null +++ b/src/shared/LocalJsonClient.cpp @@ -0,0 +1,35 @@ +// +// Created by Tobias Hieta on 30/08/15. +// + +#include "Paths.h" +#include "LocalJsonClient.h" +#include "LocalJsonServer.h" +#include "utils/Utils.h" + +///////////////////////////////////////////////////////////////////////////////////////// +LocalJsonClient::LocalJsonClient(const QString serverPath, QObject* parent) : QLocalSocket(parent) +{ + m_serverPath = Paths::dataDir(serverPath); + connect(this, &QLocalSocket::readyRead, this, &LocalJsonClient::readyRead); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void LocalJsonClient::connectToServer() +{ + QLocalSocket::connectToServer(m_serverPath, QIODevice::ReadWrite); +} + +///////////////////////////////////////////////////////////////////////////////////////// +bool LocalJsonClient::sendMessage(const QVariantMap& message) +{ + return LocalJsonServer::sendMessage(message, this); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void LocalJsonClient::readyRead() +{ + QVariantList list = LocalJsonServer::readFromSocket(this); + foreach (const QVariant& msg, list) + emit messageReceived(msg.toMap()); +} diff --git a/src/shared/LocalJsonClient.h b/src/shared/LocalJsonClient.h new file mode 100644 index 0000000..259f33e --- /dev/null +++ b/src/shared/LocalJsonClient.h @@ -0,0 +1,26 @@ +// +// Created by Tobias Hieta on 30/08/15. +// + +#ifndef KONVERGO_LOCALJSONCLIENT_H +#define KONVERGO_LOCALJSONCLIENT_H + +#include + +class LocalJsonClient : public QLocalSocket +{ + Q_OBJECT +public: + explicit LocalJsonClient(const QString serverPath, QObject* parent = 0); + void connectToServer(); + bool sendMessage(const QVariantMap& message); + +Q_SIGNALS: + void messageReceived(const QVariantMap& message); + +private: + Q_SLOT void readyRead(); + QString m_serverPath; +}; + +#endif //KONVERGO_LOCALJSONCLIENT_H diff --git a/src/shared/LocalJsonServer.cpp b/src/shared/LocalJsonServer.cpp new file mode 100644 index 0000000..95865b2 --- /dev/null +++ b/src/shared/LocalJsonServer.cpp @@ -0,0 +1,104 @@ +// +// Created by Tobias Hieta on 30/08/15. +// + +#include "LocalJsonServer.h" +#include "Paths.h" +#include "QsLog.h" + +#include + +///////////////////////////////////////////////////////////////////////////////////////// +LocalJsonServer::LocalJsonServer(const QString& serverName, QObject* parent) : QObject(parent) +{ + m_server = new QLocalServer(this); + m_serverName = Paths::dataDir(serverName); + + connect(m_server, &QLocalServer::newConnection, this, &LocalJsonServer::serverClientConnected); +} + +///////////////////////////////////////////////////////////////////////////////////////// +bool LocalJsonServer::listen() +{ + while (!m_server->listen(m_serverName)) + { + if (m_server->serverError() == QAbstractSocket::AddressInUseError) + { + QFile(m_serverName).remove(); + continue; + } + + QLOG_WARN() << "Failed to listen to local socket:" << m_server->errorString(); + return false; + } + + QLOG_INFO() << "Listening to socket:" << m_serverName; + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////// +void LocalJsonServer::serverClientConnected() +{ + QLocalSocket* socket = m_server->nextPendingConnection(); + if (socket) + { + m_clientSockets << socket; + connect(socket, &QLocalSocket::readyRead, this, &LocalJsonServer::clientReadyRead); + emit clientConnected(socket); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +bool LocalJsonServer::sendMessage(const QVariantMap& message, QLocalSocket* socket) +{ + QJsonObject obj = QJsonObject::fromVariantMap(message); + + if (obj.isEmpty()) + return false; + + QJsonDocument doc(obj); + QByteArray data = doc.toJson(QJsonDocument::Compact); + data.append("\r\n"); + + if (!data.isEmpty()) + return (socket->write(data) == data.size()); + + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// +void LocalJsonServer::clientReadyRead() +{ + QLocalSocket* socket = dynamic_cast(sender()); + if (!socket) + return; + + QVariantList messages = readFromSocket(socket); + foreach (const QVariant& msg, messages) + emit messageReceived(msg.toMap()); +} + +///////////////////////////////////////////////////////////////////////////////////////// +QVariantList LocalJsonServer::readFromSocket(QLocalSocket* socket) +{ + QVariantList lst; + + while (socket->canReadLine()) + { + QByteArray data = socket->readLine(); + if (!data.isNull()) + { + QJsonParseError err; + QJsonDocument doc = QJsonDocument::fromJson(data, &err); + if (doc.isNull()) + { + QLOG_WARN() << "Failed to parse message from client:" << err.errorString(); + continue; + } + + lst << doc.toVariant(); + } + } + + return lst; +} \ No newline at end of file diff --git a/src/shared/LocalJsonServer.h b/src/shared/LocalJsonServer.h new file mode 100644 index 0000000..23fcd4d --- /dev/null +++ b/src/shared/LocalJsonServer.h @@ -0,0 +1,37 @@ +// +// Created by Tobias Hieta on 30/08/15. +// + +#ifndef KONVERGO_LOCALJSONSEVER_H +#define KONVERGO_LOCALJSONSEVER_H + +#include +#include +#include +#include + +class LocalJsonServer : public QObject +{ + Q_OBJECT +public: + explicit LocalJsonServer(const QString& serverName, QObject* parent = 0); + + bool listen(); + static bool sendMessage(const QVariantMap& message, QLocalSocket* socket); + static QVariantList readFromSocket(QLocalSocket* socket); + +Q_SIGNALS: + void clientConnected(QLocalSocket* socket); + void messageReceived(const QVariant& message); + +private Q_SLOTS: + void serverClientConnected(); + void clientReadyRead(); + +private: + QString m_serverName; + QLocalServer* m_server; + QList m_clientSockets; +}; + +#endif //KONVERGO_LOCALJSONSEVER_H diff --git a/src/shared/Names.cpp.in b/src/shared/Names.cpp.in new file mode 100644 index 0000000..e9fefb2 --- /dev/null +++ b/src/shared/Names.cpp.in @@ -0,0 +1,15 @@ +// +// Created by Tobias Hieta on 11/09/15. +// + +#include "Names.h" + +QString Names::MainName() +{ + return QString("@MAIN_NAME@"); +} + +QString Names::HelperName() +{ + return QString("@HELPER_NAME@"); +} \ No newline at end of file diff --git a/src/shared/Names.h b/src/shared/Names.h new file mode 100644 index 0000000..b67eec6 --- /dev/null +++ b/src/shared/Names.h @@ -0,0 +1,16 @@ +// +// Created by Tobias Hieta on 11/09/15. +// + +#ifndef NAMES_H +#define NAMES_H + +#include + +namespace Names +{ + QString MainName(); + QString HelperName(); +}; + +#endif //NAMES_H diff --git a/src/shared/Paths.cpp b/src/shared/Paths.cpp new file mode 100644 index 0000000..8885213 --- /dev/null +++ b/src/shared/Paths.cpp @@ -0,0 +1,74 @@ +// +// Created by Tobias Hieta on 01/09/15. +// + +#include "Paths.h" + +#include +#include +#include +#include +#include +#include "Names.h" + +///////////////////////////////////////////////////////////////////////////////////////// +static QDir writableLocation(QStandardPaths::StandardLocation loc) +{ + QDir d(QStandardPaths::writableLocation(loc)); + if (!d.mkpath(d.absolutePath() + "/" + Names::MainName())) + { + QLOG_WARN() << "Failed to create directory:" << d.absolutePath(); + return QDir(); + } + + d.cd(Names::MainName()); + + return d; +} + +///////////////////////////////////////////////////////////////////////////////////////// +QString Paths::resourceDir(const QString& file) +{ + auto resourceDir = QDir(QGuiApplication::applicationDirPath()); + +#ifdef Q_OS_MAC + resourceDir.cdUp(); + resourceDir.cd("Resources"); +#endif + + return resourceDir.filePath(file); +} + +///////////////////////////////////////////////////////////////////////////////////////// +QString Paths::dataDir(const QString& file) +{ + QDir d = writableLocation(QStandardPaths::GenericDataLocation); + if (file.isEmpty()) + return d.absolutePath(); + return d.filePath(file); +} + +///////////////////////////////////////////////////////////////////////////////////////// +QString Paths::cacheDir(const QString& file) +{ + QDir d = writableLocation(QStandardPaths::GenericCacheLocation); + if (file.isEmpty()) + return d.absolutePath(); + return d.filePath(file); +} + +///////////////////////////////////////////////////////////////////////////////////////// +QString Paths::logDir(const QString& file) +{ +#ifdef Q_OS_MAC + QDir ldir = QDir(QStandardPaths::locate(QStandardPaths::HomeLocation, "", QStandardPaths::LocateDirectory)); + ldir.mkpath(ldir.absolutePath() + "/Library/Logs/" + Names::MainName()); + ldir.cd("Library/Logs/" + Names::MainName()); + return ldir.filePath(file); +#else + QDir ldir = writableLocation(QStandardPaths::GenericDataLocation); + ldir.mkpath(ldir.absolutePath() + "/logs"); + ldir.cd("logs"); + return ldir.filePath(file); +#endif +} diff --git a/src/shared/Paths.h b/src/shared/Paths.h new file mode 100644 index 0000000..e87fc54 --- /dev/null +++ b/src/shared/Paths.h @@ -0,0 +1,18 @@ +// +// Created by Tobias Hieta on 01/09/15. +// + +#ifndef KONVERGO_PATHS_H +#define KONVERGO_PATHS_H + +#include + +namespace Paths +{ + QString resourceDir(const QString& file = QString()); + QString dataDir(const QString& file = QString()); + QString cacheDir(const QString& file = QString()); + QString logDir(const QString& file = QString()); +}; + +#endif //KONVERGO_PATHS_H diff --git a/src/shared/UniqueApplication.h b/src/shared/UniqueApplication.h new file mode 100644 index 0000000..44a09a4 --- /dev/null +++ b/src/shared/UniqueApplication.h @@ -0,0 +1,74 @@ +// +// Created by Tobias Hieta on 27/08/15. +// + +#ifndef KONVERGO_UNIQUEAPPLICATION_H +#define KONVERGO_UNIQUEAPPLICATION_H + +#include +#include "Paths.h" +#include "LocalJsonServer.h" +#include "LocalJsonClient.h" +#include "utils/Utils.h" + +#define SOCKET_NAME "pmpUniqueApplication" + +class UniqueApplication : public QObject +{ + Q_OBJECT +public: + UniqueApplication(QObject* parent = 0, const QString& socketname = SOCKET_NAME) : QObject(parent) + { + m_socketName = socketname; + } + + void listen() + { + m_server = new LocalJsonServer(m_socketName, this); + + connect(m_server, &LocalJsonServer::messageReceived, [=](const QVariant& message) + { + QVariantMap map = message.toMap(); + if (map.contains("command") && map.value("command").toString() == "appStart") + emit otherApplicationStarted(); + }); + + if (!m_server->listen()) + throw FatalException("Failed to listen to uniqueApp socket!"); + } + + bool ensureUnique() + { + auto socket = new LocalJsonClient(m_socketName, this); + socket->connectToServer(); + + // we will just assume that the app isn't running if we get a error here + if (!socket->waitForConnected(1000)) + { + if (socket->error() != QLocalSocket::SocketTimeoutError) + { + // since we are unique we will start to listen and claim this socket. + listen(); + return true; + } + } + + QVariantMap m; + m.insert("command", "appStart"); + socket->sendMessage(m); + socket->waitForBytesWritten(2000); + + socket->close(); + socket->deleteLater(); + + return false; + } + + Q_SIGNAL void otherApplicationStarted(); + +private: + LocalJsonServer* m_server; + QString m_socketName; +}; + +#endif //KONVERGO_UNIQUEAPPLICATION_H diff --git a/src/system/CMakeLists.txt b/src/system/CMakeLists.txt new file mode 100644 index 0000000..4edd725 --- /dev/null +++ b/src/system/CMakeLists.txt @@ -0,0 +1,6 @@ +if (OPENELEC) +add_subdirectory(openelec) +endif(OPENELEC) + +find_all_sources(. SRC) +add_sources(${SRC}) \ No newline at end of file diff --git a/src/system/OEUpdateManager.cpp b/src/system/OEUpdateManager.cpp new file mode 100644 index 0000000..7a78576 --- /dev/null +++ b/src/system/OEUpdateManager.cpp @@ -0,0 +1,67 @@ +#include +#include +#include "QsLog.h" +#include "OEUpdateManager.h" +#include "SystemComponent.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +QString OEUpdateManager::HaveUpdate() +{ + return ""; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool OEUpdateManager::applyUpdate(const QString& version) +{ + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void OEUpdateManager::doUpdate(const QString& version) +{ + // grab the update file + QString packagePath = GetPath("", version, true); + QDir packageDir(packagePath); + + QStringList updateFiles = packageDir.entryList(QStringList( "*.tar"), QDir::Files, QDir::Time); + + if (updateFiles.size()) + { + // copy the update files to /storage/.update + QString destUpdatePath = "/storage/.update/" + updateFiles.at(0); + if (packageDir.rename(packagePath + updateFiles.at(0), destUpdatePath)) + { + if (isMiniUpdateArchive(destUpdatePath)) + { + // if we have a miniupdate, just exit + QLOG_DEBUG() << "Exiting to apply application update " << destUpdatePath; + SystemComponent::Get().exit(); + } + else + { + // now reboot to do the update + QLOG_DEBUG() << "Rebooting to apply system update " << destUpdatePath; + QProcess::startDetached("reboot"); + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool OEUpdateManager::isMiniUpdateArchive(QString archivePath) +{ + QProcess process; + + process.start("/bin/tar", QStringList() << "-tf" << archivePath); + if (process.waitForFinished(1000) && (process.exitCode() == 0)) + { + QByteArray output = process.readAllStandardOutput(); + return output.contains(QByteArray("bin/") + Names::MainName().toUtf8()); + } + else + { + QLOG_ERROR() << "Unable to list update archive files : " << QString(process.readAllStandardError()); + } + + return false; +} diff --git a/src/system/OEUpdateManager.h b/src/system/OEUpdateManager.h new file mode 100644 index 0000000..c12c504 --- /dev/null +++ b/src/system/OEUpdateManager.h @@ -0,0 +1,20 @@ +#ifndef OEUPDATEMANAGER_H +#define OEUPDATEMANAGER_H + +#include "UpdateManager.h" + +class OEUpdateManager : public UpdateManager +{ +public: + OEUpdateManager(QObject *parent = 0) {}; + ~OEUpdateManager() {}; + + virtual QString HaveUpdate(); + virtual bool applyUpdate(const QString &version); + virtual void doUpdate(const QString& version); + +private: + bool isMiniUpdateArchive(QString archivePath); +}; + +#endif // OEUPDATEMANAGER_H diff --git a/src/system/SystemComponent.cpp b/src/system/SystemComponent.cpp new file mode 100644 index 0000000..7e32225 --- /dev/null +++ b/src/system/SystemComponent.cpp @@ -0,0 +1,267 @@ +#include +#include +#include +#include +#include +#include + +#include "SystemComponent.h" +#include "Version.h" +#include "QsLog.h" +#include "settings/SettingsComponent.h" +#include "ui/KonvergoWindow.h" +#include "Paths.h" +#include "Names.h" + +#define MOUSE_TIMEOUT 5 * 1000 + +#define KONVERGO_PRODUCTID_DEFAULT 3 +#define KONVERGO_PRODUCTID_OPENELEC 4 + +// Platform types map +QMap platformTypeNames = { \ + { SystemComponent::platformTypeOsx, "macosx" }, \ + { SystemComponent::platformTypeWindows, "windows" }, + { SystemComponent::platformTypeLinux, "linux" }, + { SystemComponent::platformTypeUnknown, "unknown" }, +}; + +// platform Archictecture map +QMap platformArchNames = { \ + { SystemComponent::platformArchX86, "x86" }, \ + { SystemComponent::platformArchRpi2, "rpi2" }, + { SystemComponent::platformArchUnknown, "unknown" } +}; + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +SystemComponent::SystemComponent(QObject* parent) : ComponentBase(parent), m_platformType(platformTypeUnknown), m_platformArch(platformArchUnknown), m_doLogMessages(false) +{ + m_mouseOutTimer = new QTimer(this); + m_mouseOutTimer->setSingleShot(true); + connect(m_mouseOutTimer, &QTimer::timeout, [&] () { setCursorVisibility(false); }); + + m_mouseOutTimer->start(MOUSE_TIMEOUT); + +// define OS Type +#if defined(Q_OS_MAC) + m_platformType = platformTypeOsx; +#elif defined(Q_OS_WIN) + m_platformType = platformTypeWindows; +#elif defined(Q_OS_LINUX) + m_platformType = platformTypeLinux; +#endif + +// define target type +#if TARGET_RPI + m_platformArch = platformArchRpi2; +#else + m_platformArch = platformArchX86; +#endif + +#if KONVERGO_OPENELEC + m_platformModfiers << SYSTEM_MODIFIER_OPENELEC; +#endif +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +QString SystemComponent::getPlatformTypeString() const +{ + return platformTypeNames[m_platformType]; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +QString SystemComponent::getPlatformArchString() const +{ + return platformArchNames[m_platformArch]; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +QVariantMap SystemComponent::systemInformation() const +{ + QVariantMap info; + QString build; + QString dist; + QString arch; + int productid = KONVERGO_PRODUCTID_DEFAULT; + +#ifdef Q_OS_WIN + arch = "x86"; +#else + arch = QSysInfo::currentCpuArchitecture(); +#endif + + build = getPlatformTypeString(); + dist = getPlatformTypeString(); + +#if defined(KONVERGO_OPENELEC) + productid = KONVERGO_PRODUCTID_OPENELEC; + dist = "openelec"; + + if (m_platformArch == platformArchRpi2) + { + build = "rpi2"; + } + else + { + build = "generic"; + } +#endif + + + info["build"] = build + "-" + arch; + info["dist"] = dist; + info["version"] = Version::GetVersionString(); + info["productid"] = productid; + + QLOG_DEBUG() << QString( + "System Information : build(%1)-arch(%2).dist(%3).version(%4).productid(%5)") + .arg(build) + .arg(arch) + .arg(dist) + .arg(Version::GetVersionString()) + .arg(productid); + return info; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void SystemComponent::exit() +{ + qApp->quit(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void SystemComponent::restart() +{ + qApp->quit(); + QProcess::startDetached(qApp->arguments()[0], qApp->arguments()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void SystemComponent::info(QString text) +{ + if (QsLogging::Logger::instance().loggingLevel() <= QsLogging::InfoLevel) + QsLogging::Logger::Helper(QsLogging::InfoLevel).stream() << "JS:" << qPrintable(text); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void SystemComponent::setCursorVisibility(bool visible) +{ + if (visible) + { + m_mouseOutTimer->start(MOUSE_TIMEOUT); + + while (qApp->overrideCursor()) + qApp->restoreOverrideCursor(); + } + else + { + if (!qApp->overrideCursor()) + { + if (m_mouseOutTimer->isActive()) + m_mouseOutTimer->stop(); + + qApp->setOverrideCursor(QCursor(Qt::BlankCursor)); + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +QString SystemComponent::getUserAgent() +{ + QString userAgent = QString("Konvergo-%1-%2 %3").arg(getPlatformTypeString()) + .arg(getPlatformArchString()) + .arg(Version::GetVersionString()); + if (m_platformModfiers.size()) + userAgent += QString(" (%1)").arg(m_platformModfiers.join(',')); + + return userAgent; +} + +#ifdef Q_OS_UNIX +///////////////////////////////////////////////////////////////////////////////////////// +QMap SystemComponent::networkInterfaces() +{ + QMap info; + + foreach(const QNetworkInterface& interface, QNetworkInterface::allInterfaces()) + { + if (interface.isValid() == false || (interface.hardwareAddress().isEmpty() == true) || interface.IsLoopBack == false) + continue; + + QString interfaceName = QString("Interface%1 ").arg(interface.index()); + info[interfaceName + "Name"] = interface.humanReadableName(); + info[interfaceName + "HW address"] = interface.hardwareAddress(); + info[interfaceName + "Status"] = (interface.flags() & QNetworkInterface::IsUp) ? "Up" : "Down"; + + int i = 0; + foreach(const QNetworkAddressEntry& address, interface.addressEntries()) + { + info[interfaceName + QString("Address%1").arg(i)] = QString("%1/%2").arg(address.ip().toString()).arg(address.netmask().toString()); + i++; + } + } + return info; +} +#endif + +///////////////////////////////////////////////////////////////////////////////////////// +QString SystemComponent::debugInformation() +{ + QString debugInfo; + QTextStream stream(&debugInfo); + + stream << "Plex Media Player" << endl; + stream << " Version: " << Version::GetVersionString() << " built: " << Version::GetBuildDate() << endl; + stream << " Web Client Version: " << Version::GetWebVersion() << endl; + stream << " Platform: " << getPlatformTypeString() << "-" << getPlatformArchString() << endl; + stream << " Qt version: " << qVersion() << endl; + stream << endl; + + stream << "Files" << endl; + stream << " Log file: " << Paths::logDir(Names::MainName() + ".log") << endl; + stream << " Config file: " << Paths::dataDir(Names::MainName() + ".conf") << endl; + stream << endl; + +#ifdef Q_OS_UNIX + stream << "Network" << endl; + QMap networks = networkInterfaces(); + foreach(const QString& net, networks.keys()) + stream << " " << net << ": " << networks[net] << endl; +#endif + + stream << flush; + return debugInfo; +} + +///////////////////////////////////////////////////////////////////////////////////////// +int SystemComponent::networkPort() const +{ + return SettingsComponent::Get().value(SETTINGS_SECTION_MAIN, "webserverport").toInt(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +QStringList SystemComponent::networkAddresses() const +{ + QStringList list; + foreach(const QHostAddress& address, QNetworkInterface::allAddresses()) + { + if (! address.isLoopback() && (address.protocol() == QAbstractSocket::IPv4Protocol)) + list << address.toString(); + } + + return list; +} + +///////////////////////////////////////////////////////////////////////////////////////// +void SystemComponent::userInformation(const QVariantMap& userModel) +{ + QString username = userModel["username"].toString(); + QLOG_DEBUG() << "Change user to " << username; +} + +///////////////////////////////////////////////////////////////////////////////////////// +void SystemComponent::openExternalUrl(const QString& url) +{ + QDesktopServices::openUrl(QUrl(url)); +} diff --git a/src/system/SystemComponent.h b/src/system/SystemComponent.h new file mode 100644 index 0000000..7ee8a06 --- /dev/null +++ b/src/system/SystemComponent.h @@ -0,0 +1,81 @@ +#ifndef __SYSTEM_COMPONENT_H__ +#define __SYSTEM_COMPONENT_H__ + +#include "ComponentManager.h" +#include +#include "utils/Utils.h" +#include "Paths.h" +#include "Names.h" + +// System modifiers +#define SYSTEM_MODIFIER_OPENELEC "OpenELEC" + +class SystemComponent : public ComponentBase +{ + Q_OBJECT + DEFINE_SINGLETON(SystemComponent); + +public: + virtual bool componentExport() { return true; } + virtual const char* componentName() { return "system"; } + virtual bool componentInitialize() { return true; } + + Q_INVOKABLE QVariantMap systemInformation() const; + Q_INVOKABLE void exit(); + Q_INVOKABLE void restart(); + + Q_INVOKABLE void info(QString text); + + Q_INVOKABLE void setCursorVisibility(bool visible); + + Q_INVOKABLE QString getUserAgent(); + + Q_INVOKABLE QString debugInformation(); + + Q_INVOKABLE QString logFilePath() { return Paths::logDir(Names::MainName() + ".log"); } + + Q_INVOKABLE QStringList networkAddresses() const; + Q_INVOKABLE int networkPort() const; + + Q_INVOKABLE void userInformation(const QVariantMap& userModel); + + Q_INVOKABLE void openExternalUrl(const QString& url); + + // possible os types type enum + enum PlatformType + { + platformTypeUnknown, + platformTypeOsx, + platformTypeWindows, + platformTypeLinux + }; + + // possible values for target types + enum PlatformArch + { + platformArchUnknown, + platformArchX86, + platformArchRpi2 + }; + + inline PlatformType getPlatformType() { return m_platformType; } + inline PlatformArch getPlatformArch() { return m_platformArch; } + + QString getPlatformTypeString() const; + QString getPlatformArchString() const; + + inline bool isOpenELEC() { return m_platformModfiers.contains(SYSTEM_MODIFIER_OPENELEC); } + +private: + SystemComponent(QObject* parent = 0); + + static QMap networkInterfaces(); + + QTimer* m_mouseOutTimer; + PlatformType m_platformType; + PlatformArch m_platformArch; + QStringList m_platformModfiers; + bool m_doLogMessages; +}; + +#endif diff --git a/src/system/UpdateManager.cpp b/src/system/UpdateManager.cpp new file mode 100644 index 0000000..94c84b4 --- /dev/null +++ b/src/system/UpdateManager.cpp @@ -0,0 +1,184 @@ +#include "QsLog.h" + +#include +#include +#include +#include +#include "UpdateManager.h" +#include "utils/Utils.h" +#include "utils/HelperLauncher.h" +#include "system/SystemComponent.h" + +#if KONVERGO_OPENELEC +#include "OEUpdateManager.h" +#endif + +UpdateManager* g_updateManager; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +UpdateManager* UpdateManager::Get() +{ + if (!g_updateManager) + { +#if KONVERGO_OPENELEC + g_updateManager = new OEUpdateManager(NULL); +#else + g_updateManager = new UpdateManager(NULL); +#endif + } + + return g_updateManager; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +QString UpdateManager::HaveUpdate() +{ + QDir updateDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/updates/"); + if (!updateDir.exists()) + { + QLOG_DEBUG() << "No Update directory found, exiting"; + return ""; + } + QStringList nonAppliedUpdates; + + // sort update directories, sort by the newest directory first, that way + // we apply the latest update downloaded. + // + foreach (const QString& dir, updateDir.entryList(QDir::NoDotAndDotDot | QDir::Dirs, QDir::Time)) + { + // check if this version has been applied + QString readyFile(GetPath("_readyToApply", dir, false)); + QLOG_DEBUG() << "Checking for:" << readyFile; + if (QFile::exists(readyFile)) + { + QLOG_DEBUG() << dir << "is not applied"; + return dir; + } + } + + QLOG_DEBUG() << "No valid/applicable update found."; + return ""; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool UpdateManager::applyUpdate(const QString& version) +{ + // make sure we have a manifest for this version. + QLOG_DEBUG() << "Applying Update to version" << version; + + QString manifestPath = GetPath("manifest.xml.bz2", version, false); + QString packagePath = QFileInfo(manifestPath).dir().absolutePath() + "/packages"; + + if (!QFile(manifestPath).exists() || !QDir(packagePath).exists()) + { + QLOG_ERROR() << "Could not find:" << manifestPath << "or" << packagePath; + return false; + } + + QString updaterName("updater"); +#ifdef Q_OS_WIN + updaterName = "updater.exe"; +#endif + + QFile updaterFile(Paths::resourceDir(updaterName)); + + if (!updaterFile.exists()) + { + QLOG_ERROR() << "Missing updater:" << updaterFile.fileName(); + return false; + } + + // copy the updater to a temporary directory so that we don't overwrite it. + QString updaterPath = QDir::temp().absoluteFilePath(updaterName); + + // we need to remove it first, since QFile::copy() will fail otherwise. + if (QFile::exists(updaterPath)) + { + if (!QFile::remove(updaterPath)) + { + QLOG_DEBUG() << "Failed to remove updater from " << updaterPath; + } + } + + if (!QFile::copy(updaterFile.fileName(), updaterPath)) + { + QLOG_ERROR() << "Failed to copy the updater to:" << updaterPath; + return false; + } + + QStringList args; + args << "--script=" + manifestPath; + args << "--package-dir=" + packagePath; + args << "--auto-close"; + +#ifdef Q_OS_MAC + args << "--install-dir=" + QDir(QCoreApplication::applicationDirPath() + "/../../").absolutePath(); +#else + args << "--install-dir=" + QDir(QCoreApplication::applicationDirPath()).absolutePath(); +#endif + + args << "--wait=" + QString::number(QCoreApplication::applicationPid()); + + // beyond this point we don't want to try to install this update again. + QFile::remove(GetPath("_readyToApply", version, false)); + + QLOG_DEBUG() << "Executing:" << updaterPath << args; + QProcess* process = new QProcess(NULL); + if (process->startDetached(updaterPath, args, QDir::temp().absolutePath())) + { + QLOG_DEBUG() << "Updater running, shutting down Plex Media Player"; + + return true; + } + + QLOG_ERROR() << "Failed to execute the updater"; + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +QString UpdateManager::GetPath(const QString& file, const QString& version, bool package) +{ + QString filePath(file); + + // put the packages in the packages subdir. + if (package) + filePath = "packages/" + file; + + return QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/updates/" + version + "/" + filePath; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void UpdateManager::doUpdate(const QString& version) +{ + QLOG_DEBUG() << "Update competed, restarting system"; + if (!applyUpdate(version)) + { + QLOG_ERROR() "Failed to apply update"; + } + else + { + QLOG_DEBUG() "Update was applied successfully"; + } + + HelperLauncher::Get().stop(); + SystemComponent::Get().exit(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +bool UpdateManager::CheckForUpdates() +{ + QString updateVersion = Get()->HaveUpdate(); + if (!updateVersion.isEmpty()) + { + QLOG_DEBUG() << "We want to apply update:" << updateVersion; + + // if this call succeeds we need to shut down and let + // the updater do it's work. Otherwise we'll just + // start the application as normal and pretend that + // nothing happened + // + if (Get()->applyUpdate(updateVersion)) + return true; + } + return false; +} \ No newline at end of file diff --git a/src/system/UpdateManager.h b/src/system/UpdateManager.h new file mode 100644 index 0000000..0084de4 --- /dev/null +++ b/src/system/UpdateManager.h @@ -0,0 +1,24 @@ +#ifndef UPDATEMANAGER_H +#define UPDATEMANAGER_H + +#include + +class UpdateManager : public QObject +{ + Q_OBJECT +public: + static bool CheckForUpdates(); + + explicit UpdateManager(QObject *parent = 0) {}; + ~UpdateManager() {}; + + static UpdateManager* Get(); + + virtual QString HaveUpdate(); + virtual bool applyUpdate(const QString &version); + virtual void doUpdate(const QString& version); + + static QString GetPath(const QString &file, const QString& version, bool package); +}; + +#endif // UPDATEMANAGER_H diff --git a/src/system/UpdaterComponent.cpp b/src/system/UpdaterComponent.cpp new file mode 100644 index 0000000..73a7286 --- /dev/null +++ b/src/system/UpdaterComponent.cpp @@ -0,0 +1,198 @@ +#include "UpdaterComponent.h" +#include "QsLog.h" +#include "utils/Utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "settings/SettingsComponent.h" +#include "UpdateManager.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +UpdaterComponent::UpdaterComponent(QObject *parent) : ComponentBase(parent), m_netManager(this) +{ + m_file = NULL; + m_manifest = NULL; + + connect(&m_netManager, &QNetworkAccessManager::finished, + this, &UpdaterComponent::dlComplete); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void UpdaterComponent::dlComplete(QNetworkReply* reply) +{ + QLOG_DEBUG() << "Hello, dlComplete"; + if (reply->error() == QNetworkReply::NoError) + { + QUrl redirectURL = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); + QLOG_DEBUG() << "Redirect:" << redirectURL.toString(); + if (!redirectURL.isEmpty()) + { + // redirection, check that we get redirected to something we expect. + if (redirectURL.toString().startsWith("https://nightlies.plex.tv") || + redirectURL.toString().startsWith("https://downloads.plex.tv")) + { + QNetworkReply* redirReply = m_netManager.get(QNetworkRequest(redirectURL)); + if (m_manifest->m_reply == reply) + m_manifest->setReply(redirReply); + else if (m_file->m_reply == reply) + { + m_file->setReply(redirReply); + //connect(redirReply, &QNetworkReply::downloadProgress, this, &UpdaterComponent::downloadProgress); + } + + QLOG_DEBUG() << "Redirecting to:" << redirectURL.toString(); + + return; + } + } + } + else + { + QLOG_ERROR() << "Error downloading:" << reply->url() << "-" << reply->errorString(); + emit downloadError(reply->errorString()); + + if (m_hasManifest) + m_manifest->m_reply->abort(); + + m_file->m_reply->abort(); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void UpdaterComponent::downloadFile(Update* update) +{ + QNetworkRequest request(update->m_url); + request.setPriority(QNetworkRequest::LowPriority); + request.setAttribute(QNetworkRequest::BackgroundRequestAttribute, true); + if (update->setReply(m_netManager.get(request))) + QLOG_INFO() << "Downloading update:" << update->m_url << "to:" << update->m_localPath; + else + QLOG_ERROR() << "Failed to start download:" << update->m_url; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool UpdaterComponent::fileComplete(Update* update) +{ + Q_UNUSED(update); + if (m_file->isReady() && (m_manifest->isReady() || !m_hasManifest)) + { + QLOG_DEBUG() << "Both files downloaded"; + // create a file that shows that we are ready + // to apply this update + // + QFile readyFile(UpdateManager::GetPath("_readyToApply", m_version, false)); + if (readyFile.open(QFile::WriteOnly)) + { + readyFile.write("FOO"); + readyFile.close(); + } + + emit downloadComplete(m_version); + + delete m_file; + delete m_manifest; + + m_file = NULL; + m_manifest = NULL; + + return true; + } + + if (update) + { + QLOG_DEBUG() << "File " << update->m_localPath << " download completed"; + } + + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void UpdaterComponent::downloadUpdate(const QVariantMap& updateInfo) +{ + if (isDownloading()) + return; + + QLOG_INFO() << updateInfo; + + if (!updateInfo.contains("version") || + !updateInfo.contains("manifestURL") || !updateInfo.contains("manifestHash") || + !updateInfo.contains("fileURL") || !updateInfo.contains("fileHash") || !updateInfo.contains("fileName")) + { + QLOG_ERROR() << "updateInfo was missing fields required to carry out this action."; + return; + } + + m_version = updateInfo["version"].toString(); + + m_manifest = new Update(updateInfo["manifestURL"].toString(), + UpdateManager::GetPath("manifest.xml.bz2", m_version, false), + updateInfo["manifestHash"].toString(), this); + + // determine if we have a manifest (some distros don't like OE) + m_hasManifest = ((!m_manifest->m_url.isEmpty()) && (!m_manifest->m_hash.isEmpty())); + + m_file = new Update(updateInfo["fileURL"].toString(), + UpdateManager::GetPath(updateInfo["fileName"].toString(), m_version, true), + updateInfo["fileHash"].toString(), this); + + + if (m_hasManifest) + connect(m_manifest, &Update::fileDone, this, &UpdaterComponent::fileComplete); + + connect(m_file, &Update::fileDone, this, &UpdaterComponent::fileComplete); + + // create directories we need + QDir dr(QFileInfo(m_file->m_localPath).dir()); + if (!dr.exists()) + { + if (!dr.mkpath(".")) + { + QLOG_ERROR() << "Failed to create update directory:" << dr.absolutePath(); + emit downloadError("Failed to create download directory"); + return; + } + } + + // this will first check if the files are done + // and in that case emit the done signal. + if (fileComplete(NULL)) + return; + + if (!m_manifest->isReady() && m_hasManifest) + downloadFile(m_manifest); + + if (!m_file->isReady()) + downloadFile(m_file); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool UpdaterComponent::isDownloading() +{ + if ((m_manifest && m_manifest->m_reply && m_manifest->m_reply->isRunning()) || + (m_file && m_file->m_reply && m_file->m_reply->isRunning())) + return true; + + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void UpdaterComponent::doUpdate() +{ + // make sure we kill off the helper first: + HelperLauncher::Get().stop(); + UpdateManager::Get()->doUpdate(m_version); +} + + diff --git a/src/system/UpdaterComponent.h b/src/system/UpdaterComponent.h new file mode 100644 index 0000000..4f8417c --- /dev/null +++ b/src/system/UpdaterComponent.h @@ -0,0 +1,164 @@ +#ifndef UPDATERCOMPONENT_H +#define UPDATERCOMPONENT_H + +#include +#include +#include +#include +#include +#include +#include + +#include "ComponentManager.h" +#include "QsLog.h" + +#include + +/////////////////////////////////////////////////////////////////////////////////////////////////// +class Update : public QObject +{ + Q_OBJECT +public: + Update(const QString& url = "", const QString& localPath = "", + const QString& hash = "", QObject* parent = NULL) : QObject(parent) + { + m_url = url; + m_localPath = localPath; + m_hash = hash; + m_reply = NULL; + m_openFile = new QFile(m_localPath); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////// + bool setReply(QNetworkReply* reply) + { + if (m_reply) + { + disconnect(m_reply, 0, 0, 0); + m_reply->deleteLater(); + m_openFile->close(); + } + + m_reply = reply; + m_timeStarted = time(NULL); + + connect(m_reply, &QNetworkReply::readyRead, this, &Update::write); + connect(m_reply, &QNetworkReply::finished, this, &Update::finished); + + if (m_openFile->open(QFile::WriteOnly)) + return true; + + m_reply->deleteLater(); + m_reply = NULL; + + return false; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////// + void write() + { + m_openFile->write(m_reply->read(m_reply->bytesAvailable())); + QThread::yieldCurrentThread(); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////// + void finished() + { + m_openFile->close(); + m_reply->deleteLater(); + m_reply = NULL; + + QLOG_DEBUG() << "Update downloaded, took:" << time(NULL) - m_timeStarted << "seconds"; + + emit fileDone(this); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////// + bool isReady() + { + if ((m_reply && m_reply->isRunning()) || + (m_openFile && m_openFile->isOpen())) + return false; + + QFile file(m_localPath); + if (file.exists()) + { + QString fileHash = hashFile(); + if (!fileHash.isEmpty() && fileHash == m_hash) + return true; + } + + return false; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////// + QString hashFile() + { + QFile file(m_localPath); + QCryptographicHash hash(QCryptographicHash::Sha1); + + if (file.open(QFile::ReadOnly)) + { + while (!file.atEnd()) + hash.addData(file.read(8192)); + + QByteArray binhash = hash.result(); + return binhash.toHex(); + } + + return ""; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////// + QString m_url; + QString m_localPath; + QString m_hash; + + QNetworkReply* m_reply; + QFile* m_openFile; + time_t m_timeStarted; + +signals: + void fileDone(Update* update); +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +class UpdaterComponent : public ComponentBase +{ + Q_OBJECT + DEFINE_SINGLETON(UpdaterComponent); + +public: + virtual bool componentExport() { return true; } + virtual const char* componentName() { return "updater"; } + virtual bool componentInitialize() { return true; } + + Q_INVOKABLE void downloadUpdate(const QVariantMap &updateInfo); + Q_INVOKABLE void doUpdate(); + + +signals: + void downloadError(const QString& error); + void downloadComplete(const QString& version); + void downloadProgress(qint64 bytesReceived, qint64 total); + +private slots: + void dlComplete(QNetworkReply *reply); + bool fileComplete(Update *update); + +private: + explicit UpdaterComponent(QObject *parent = 0); + + bool isDownloading(); + void downloadFile(Update *update); + + QString m_version; + + Update* m_manifest; + Update* m_file; + bool m_hasManifest; + + QNetworkAccessManager m_netManager; +}; + +#endif // UPDATERCOMPONENT_H diff --git a/src/system/openelec/CMakeLists.txt b/src/system/openelec/CMakeLists.txt new file mode 100644 index 0000000..feca048 --- /dev/null +++ b/src/system/openelec/CMakeLists.txt @@ -0,0 +1,2 @@ +find_all_sources(. SRC) +add_sources(${SRC}) diff --git a/src/system/openelec/OESystemComponent.cpp b/src/system/openelec/OESystemComponent.cpp new file mode 100644 index 0000000..0eac9fd --- /dev/null +++ b/src/system/openelec/OESystemComponent.cpp @@ -0,0 +1,41 @@ +#include "settings/SettingsComponent.h" +#include "OESystemComponent.h" +#include "QsLog.h" +#include +#include + +/////////////////////////////////////////////////////////////////////////////////////////////////// +OESystemComponent::OESystemComponent(QObject *parent) : ComponentBase(parent) +{ + +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool OESystemComponent::componentInitialize() +{ + setHostName(SettingsComponent::Get().value(SETTINGS_SECTION_OPENELEC, "systemname").toString()); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool OESystemComponent::setHostName(QString name) +{ + // first we change the hostname name for this session + char* hostname = name.toUtf8().data(); + sethostname(hostname, strlen(hostname)); + + // then edit the hostname file so that its persistent + QFile file("/storage/.cache/hostname"); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) + { + QLOG_ERROR() << "setHostName : Failed to open" << file.fileName(); + return false; + } + + QTextStream writer(&file); + writer << name; + file.close(); + + return true; +} diff --git a/src/system/openelec/OESystemComponent.h b/src/system/openelec/OESystemComponent.h new file mode 100644 index 0000000..cc61616 --- /dev/null +++ b/src/system/openelec/OESystemComponent.h @@ -0,0 +1,23 @@ +#ifndef OESYSTEMCOMPONENT_H +#define OESYSTEMCOMPONENT_H + +#include + +#include "ComponentManager.h" + +class OESystemComponent : public ComponentBase +{ + Q_OBJECT + DEFINE_SINGLETON(OESystemComponent) + +public: + OESystemComponent(QObject *parent = 0); + + virtual bool componentExport() { return true; } + virtual const char* componentName() { return "oesystem"; } + virtual bool componentInitialize(); + + bool setHostName(QString name); +}; + +#endif // OESYSTEMCOMPONENT_H diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt new file mode 100644 index 0000000..3e7960b --- /dev/null +++ b/src/tools/CMakeLists.txt @@ -0,0 +1,2 @@ +#add_subdirectory(socket-client) +add_subdirectory(helper) \ No newline at end of file diff --git a/src/tools/helper/CMakeLists.txt b/src/tools/helper/CMakeLists.txt new file mode 100644 index 0000000..97f5994 --- /dev/null +++ b/src/tools/helper/CMakeLists.txt @@ -0,0 +1,30 @@ +include_directories(${PROJECT_SOURCE_DIR}/src) + +add_executable(${HELPER_TARGET} WIN32 + HelperMain.cpp + CrashUploader.cpp + CrashUploader.h + HelperSocket.cpp + HelperSocket.h +) + +target_link_libraries(${HELPER_TARGET} ${Qt5Core_LIBRARIES} ${Qt5Network_LIBRARIES} qslog shared) +set_target_properties(${HELPER_TARGET} PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED TRUE + OUTPUT_NAME ${HELPER_NAME} +) + +if(APPLE) + set(PLATFORM_RESOURCE_SUFFIX /../Resources) +endif(APPLE) + +add_custom_command( + TARGET ${HELPER_TARGET} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy $ $${PLATFORM_RESOURCE_SUFFIX} +) + +if(NOT APPLE) + install(TARGETS ${HELPER_TARGET} DESTINATION ${INSTALL_BIN_DIR}) +endif(NOT APPLE) \ No newline at end of file diff --git a/src/tools/helper/CrashUploader.cpp b/src/tools/helper/CrashUploader.cpp new file mode 100644 index 0000000..06b1f70 --- /dev/null +++ b/src/tools/helper/CrashUploader.cpp @@ -0,0 +1,222 @@ +// +// Created by Tobias Hieta on 26/08/15. +// + +#include "CrashUploader.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Paths.h" +#include "Version.h" +#include "utils/Utils.h" +#include "QsLog.h" +#include "HelperSettings.h" + +#define UPLOAD_URL "http://64.71.188.207:443/" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Delete crash dumps that are not from the current version. They are most likely useless. +void CrashUploader::deleteOldCrashDumps() +{ + QDir dir(m_old); + foreach (const QString& entry, dir.entryList(QDir::Dirs | QDir::NoDot | QDir::NoDotDot)) + { + if (entry != Version::GetCanonicalVersionString()) + { + QDir subdir = dir; + if (subdir.cd(entry)) + { + QLOG_INFO() << "Wiping uninteresting crashdumps:" << subdir.path(); + subdir.removeRecursively(); + } + } + } + + // clean out any things that are in progress. + QDir progressDir(m_processing); + progressDir.removeRecursively(); + QDir().mkpath(m_processing); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void CrashUploader::addFormField(QHttpMultiPart* multipart, const QString& name, const QString& value) +{ + QHttpPart textPart; + textPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"" + name + "\"")); + textPart.setBody(value.toUtf8()); + multipart->append(textPart); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void CrashUploader::uploadCrashDump(const QString& version, const QString& path) +{ + // move the file to the in progress directory so that we don't accidentially upload it twice + QString inProgressDir = m_processing + "/" + version + "/"; + QDir().mkpath(inProgressDir); + + QString inProgressPath = inProgressDir + QFileInfo(path).fileName(); + QString uuid = QFileInfo(path).baseName(); + QFile file(path); + file.rename(inProgressPath); + file.setFileName(inProgressPath); + + QLOG_DEBUG() << "Crashdump:" << inProgressPath; + + if (!file.open(QIODevice::ReadOnly)) + { + QLOG_ERROR() << "Could not open crashdump file. will try again later."; + moveFileBackToIncoming(version, inProgressPath); + m_scanTimer->start(5000); + return; + } + + QLOG_INFO() << "Uploading crashdump:" << inProgressPath; + + QNetworkRequest req(QUrl(UPLOAD_URL)); + + QHttpMultiPart* multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + + addFormField(multiPart, "version", version); + addFormField(multiPart, "product", "plexmediaplayer"); + addFormField(multiPart, "uuid", uuid); + addFormField(multiPart, "serverUuid", HelperSettings().value("clientId", "NOCLIENTID").toString()); + addFormField(multiPart, "userId", HelperSettings().value("userId", "NOUSERID").toString()); + addFormField(multiPart, "platform", QSysInfo::productType() + "-" + QSysInfo::currentCpuArchitecture()); + + QHttpPart dataPart; + dataPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream")); + dataPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"dumpfileb64\"")); + dataPart.setBody(file.readAll().toBase64()); + multiPart->append(dataPart); + + QNetworkReply* reply = m_manager->post(req, multiPart); + multiPart->setParent(reply); + + connect(reply, static_cast(&QNetworkReply::finished), [=]() + { + QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); + + // if we get a 503 code it means that we are either sending crashes to quickly or that + // the server doesn't want these any more. In any case we can just discard it and move + // forward with our lives. + // + if (!statusCode.isValid() || (statusCode.toInt() != 200 && statusCode.toInt() != 503)) + { + QLOG_WARN() << "Failed to submit report with uuid:" << uuid << "will try again later"; + moveFileBackToIncoming(version, inProgressPath); + m_scanTimer->start(5000); + return; + } + + watchCrashDir(false); + QDir().mkpath(m_old + "/" + version); + QFile::rename(inProgressPath, m_old + "/" + version + "/" + QFileInfo(inProgressPath).fileName()); + watchCrashDir(true); + + if (statusCode.toInt() == 200) + { + QLOG_INFO() << "Submitted crash report with uuid:" << uuid; + } + else + { + QLOG_INFO() << "Server didn't want our crash report with uuid:" << uuid << "saving it in old for now"; + } + }); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void CrashUploader::moveFileBackToIncoming(const QString& version, const QString& filename) +{ + watchCrashDir(false); + QFile::rename(filename, incomingCurrentVersion() + "/" + QFileInfo(filename).fileName()); + watchCrashDir(true); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void CrashUploader::uploadAndDeleteCrashDumps() +{ + QMutexLocker lk(&m_scanLock); + QDir dir(m_incoming); + + QStringList filters; + filters << "*.dmp"; + + QLOG_DEBUG() << "Scanning:" << dir.path() << "for crashdumps."; + + watchCrashDir(false); + + // loop over all incoming directories, should give us version numbers in d + foreach (const QString& version, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) + { + QDir versionDir(dir); + versionDir.cd(version); + + QLOG_DEBUG() << "Scanning:" << versionDir.path(); + + int numUploads = 0; + foreach (const QString& entry, versionDir.entryList(filters, QDir::Files)) + { + // we only upload 20 crash reports per version + if (numUploads > 20) + { + QLOG_DEBUG() << "We have uploaded more than 20 crash reports, removing:" << entry; + QFile::remove(versionDir.filePath(entry)); + } + + uploadCrashDump(version, versionDir.filePath(entry)); + numUploads ++; + } + } + + watchCrashDir(true); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void CrashUploader::watchCrashDir(bool watch) +{ + if (watch) + m_watcher->addPath(incomingCurrentVersion()); + else + m_watcher->removePath(incomingCurrentVersion()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +CrashUploader::CrashUploader(QObject* parent) : QObject(parent) +{ + m_manager = new QNetworkAccessManager(this); + m_watcher = new QFileSystemWatcher(this); + m_scanTimer = new QTimer(this); + m_scanTimer->setSingleShot(true); + + connect(m_scanTimer, &QTimer::timeout, this, &CrashUploader::uploadAndDeleteCrashDumps); + + connect(m_watcher, &QFileSystemWatcher::directoryChanged, [=](const QString& dir) + { + // wait 2 seconds before starting process dumps. + m_scanTimer->start(2000); + }); + + m_old = Paths::cacheDir("crashdumps/old"); + m_processing = Paths::cacheDir("crashdumps/processing"); + m_incoming = Paths::cacheDir("crashdumps/incoming"); + + startUploader(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +bool CrashUploader::startUploader() +{ + deleteOldCrashDumps(); + uploadAndDeleteCrashDumps(); + + m_watcher->addPath(incomingCurrentVersion()); + + return true; +} diff --git a/src/tools/helper/CrashUploader.h b/src/tools/helper/CrashUploader.h new file mode 100644 index 0000000..ea418e9 --- /dev/null +++ b/src/tools/helper/CrashUploader.h @@ -0,0 +1,41 @@ +// +// Created by Tobias Hieta on 26/08/15. +// + +#ifndef KONVERGO_CRASHUPLOADER_H +#define KONVERGO_CRASHUPLOADER_H + +#include +#include +#include +#include +#include +#include +#include "Version.h" + +class CrashUploader : public QObject +{ + Q_OBJECT +public: + CrashUploader(QObject* parent = 0); + bool startUploader(); + +private: + Q_SLOT void uploadCrashDump(const QString& version, const QString& path); + + QString incomingCurrentVersion() { return m_incoming + "/" + Version::GetVersionString(); } + void deleteOldCrashDumps(); + void addFormField(QHttpMultiPart* multipart, const QString& name, const QString& value); + void uploadAndDeleteCrashDumps(); + void moveFileBackToIncoming(const QString& version, const QString& filename); + void watchCrashDir(bool watch); + + QNetworkAccessManager* m_manager; + QFileSystemWatcher* m_watcher; + QHash m_urlToFileMap; + QString m_old, m_incoming, m_processing; + QTimer* m_scanTimer; + QMutex m_scanLock; +}; + +#endif //KONVERGO_CRASHUPLOADER_H diff --git a/src/tools/helper/HelperMain.cpp b/src/tools/helper/HelperMain.cpp new file mode 100644 index 0000000..9c8c240 --- /dev/null +++ b/src/tools/helper/HelperMain.cpp @@ -0,0 +1,66 @@ +// +// Created by Tobias Hieta on 26/08/15. +// + +#include +#include +#include "QsLog.h" +#include "utils/Utils.h" +#include "Version.h" +#include "CrashUploader.h" +#include "HelperSocket.h" +#include "UniqueApplication.h" +#include "Names.h" + +using namespace QsLogging; + +int main(int argc, char** argv) +{ + QCoreApplication app(argc, argv); + QCoreApplication::setApplicationName(Names::MainName()); + QCoreApplication::setApplicationVersion(Version::GetVersionString()); + QCoreApplication::setOrganizationDomain("plex.tv"); + + UniqueApplication uapp(NULL, "pmpHelperUniqueApplication"); + if (!uapp.ensureUnique()) + { + fprintf(stderr, "Other helper already running, refusing to start.\n"); + return EXIT_FAILURE; + } + + // Note where the logfile is going to be + qDebug("Logging to %s", qPrintable(Paths::logDir(Names::HelperName() + ".log"))); + + // init logging. + DestinationPtr dest = DestinationFactory::MakeFileDestination( + Paths::logDir("pmphelper.log"), + EnableLogRotation, + MaxSizeBytes(1024 * 1024), + MaxOldLogCount(2)); + + // make sure we get a fresh log + dest->rotate(); + + Logger::instance().addDestination(dest); + Logger::instance().setLoggingLevel(DebugLevel); + + QLOG_DEBUG() << "Helper (" << Version::GetVersionString() << ") up and running"; + + QObject* helperObject = new QObject; + + try + { + new CrashUploader(helperObject); + new HelperSocket(helperObject); + } + catch (FatalException& e) + { + qWarning() << "Failed to setup the helper:" << e.message(); + } + + app.exec(); + + delete helperObject; + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/src/tools/helper/HelperSettings.h b/src/tools/helper/HelperSettings.h new file mode 100644 index 0000000..640f879 --- /dev/null +++ b/src/tools/helper/HelperSettings.h @@ -0,0 +1,19 @@ +// +// Created by Tobias Hieta on 01/09/15. +// + +#ifndef KONVERGO_HELPERSETTINGS_H +#define KONVERGO_HELPERSETTINGS_H + +#include +#include "Paths.h" + +class HelperSettings : public QSettings +{ +public: + HelperSettings() : QSettings(Paths::dataDir("helper.conf"), QSettings::IniFormat) + { + } +}; + +#endif //KONVERGO_HELPERSETTINGS_H diff --git a/src/tools/helper/HelperSocket.cpp b/src/tools/helper/HelperSocket.cpp new file mode 100644 index 0000000..5a58346 --- /dev/null +++ b/src/tools/helper/HelperSocket.cpp @@ -0,0 +1,57 @@ +// +// Created by Tobias Hieta on 28/08/15. +// + +#include "HelperSocket.h" +#include "utils/Utils.h" +#include "Version.h" +#include "QsLog.h" +#include "HelperSettings.h" + +#include + +///////////////////////////////////////////////////////////////////////////////////////// +HelperSocket::HelperSocket(QObject* parent) +{ + m_server = new LocalJsonServer("pmpHelper", this); + + connect(m_server, &LocalJsonServer::clientConnected, this, &HelperSocket::clientConnected); + connect(m_server, &LocalJsonServer::messageReceived, this, &HelperSocket::message); + + if (!m_server->listen()) + throw FatalException("Could not listen to helper socket!"); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void HelperSocket::clientConnected(QLocalSocket* socket) +{ + QLOG_DEBUG() << "Server connected."; + QVariantMap hello; + hello.insert("version", Version::GetVersionString()); + hello.insert("path", QCoreApplication::applicationFilePath()); + m_server->sendMessage(hello, socket); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void HelperSocket::message(const QVariant& message) +{ + QVariantMap map = message.toMap(); + if (map.contains("command")) + { + if (map.value("command").toString() == "quit") + { + QLOG_DEBUG() << "Asked to quit."; + qApp->quit(); + } + else if (map.value("command").toString() == "info") + { + QLOG_DEBUG() << "Updating clientID."; + QVariantMap arg = map.value("argument").toMap(); + if (arg.contains("clientId")) + HelperSettings().setValue("clientId", arg.value("clientId").toString()); + + if (arg.contains("userId")) + HelperSettings().setValue("userId", arg.value("userId").toString()); + } + } +} \ No newline at end of file diff --git a/src/tools/helper/HelperSocket.h b/src/tools/helper/HelperSocket.h new file mode 100644 index 0000000..2e21c8b --- /dev/null +++ b/src/tools/helper/HelperSocket.h @@ -0,0 +1,23 @@ +// +// Created by Tobias Hieta on 28/08/15. +// + +#ifndef KONVERGO_HELPERSOCKET_H +#define KONVERGO_HELPERSOCKET_H + +#include "Paths.h" +#include "LocalJsonServer.h" + +class HelperSocket : public QObject +{ + Q_OBJECT +public: + HelperSocket(QObject* parent = 0); + +private: + Q_SLOT void clientConnected(QLocalSocket* socket); + Q_SLOT void message(const QVariant& message); + LocalJsonServer* m_server; +}; + +#endif //KONVERGO_HELPERSOCKET_H diff --git a/src/tools/socket-client/CMakeLists.txt b/src/tools/socket-client/CMakeLists.txt new file mode 100644 index 0000000..7fb52e9 --- /dev/null +++ b/src/tools/socket-client/CMakeLists.txt @@ -0,0 +1,11 @@ +include_directories(${PROJECT_SOURCE_DIR}/src) + +add_executable(socket-client + SocketClient.cpp + $ +) +set_target_properties(socket-client PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED TRUE +) +target_link_libraries(socket-client ${Qt5Network_LIBRARIES} ${Qt5Core_LIBRARIES} qslog) \ No newline at end of file diff --git a/src/tools/socket-client/SocketClient.cpp b/src/tools/socket-client/SocketClient.cpp new file mode 100644 index 0000000..9b44241 --- /dev/null +++ b/src/tools/socket-client/SocketClient.cpp @@ -0,0 +1,72 @@ +// +// Created by Tobias Hieta on 26/08/15. +// + +#include +#include +#include +#include "LocalJsonClient.h" + +class SocketClient : public QObject +{ + Q_OBJECT +public: + SocketClient() : QObject(NULL) + { + m_socket = new LocalJsonClient("inputSocket", this); + m_socket->connectToServer(); + + connect(m_socket, &LocalJsonClient::messageReceived, this, &SocketClient::gotMessage); + } + + void sendCommand(const QString& command) + { + m_commands.enqueue(command); + } + + Q_SLOT void gotMessage(const QVariantMap& message) + { + qDebug() << message; + doSendCommand(); + } + + void doSendCommand() + { + while (!m_commands.isEmpty()) + { + QString cmd = m_commands.dequeue(); + + QVariantMap obj; + obj.insert("client", "socket-client"); + obj.insert("source", "direct"); + obj.insert("keycode", cmd); + + m_socket->sendMessage(obj); + + qDebug() << "Sending:" << cmd; + } + + m_socket->waitForBytesWritten(60 * 1000); + m_socket->close(); + exit(EXIT_SUCCESS); + } + +private: + LocalJsonClient* m_socket; + QQueue m_commands; +}; + +int main(int argc, char** argv) +{ + QCoreApplication app(argc, argv); + + SocketClient* client = new SocketClient(); + + for (int i = 1; i < argc; i ++) + client->sendCommand(QString::fromUtf8(argv[i])); + + return app.exec(); +} + + +#include "SocketClient.moc" \ No newline at end of file diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt new file mode 100644 index 0000000..fb59720 --- /dev/null +++ b/src/ui/CMakeLists.txt @@ -0,0 +1,2 @@ +find_all_sources(. UI_SRCS) +add_sources(${UI_SRCS} webview.qml errormessage.qml) diff --git a/src/ui/KonvergoEngine.h b/src/ui/KonvergoEngine.h new file mode 100644 index 0000000..61d58f5 --- /dev/null +++ b/src/ui/KonvergoEngine.h @@ -0,0 +1,20 @@ +#ifndef KONVERGOENGINE_H +#define KONVERGOENGINE_H + +#include + +static QQmlApplicationEngine* g_qmlEngine = NULL; + +class KonvergoEngine +{ +public: + static QQmlApplicationEngine* Get() + { + if (!g_qmlEngine) + g_qmlEngine = new QQmlApplicationEngine(); + + return g_qmlEngine; + } +}; + +#endif // KONVERGOENGINE_H diff --git a/src/ui/KonvergoWindow.cpp b/src/ui/KonvergoWindow.cpp new file mode 100644 index 0000000..184d358 --- /dev/null +++ b/src/ui/KonvergoWindow.cpp @@ -0,0 +1,349 @@ +#include "KonvergoWindow.h" +#include +#include +#include +#include +#include + +#include "input/InputKeyboard.h" +#include "settings/SettingsComponent.h" +#include "settings/SettingsSection.h" +#include "system/SystemComponent.h" +#include "player/PlayerComponent.h" +#include "display/DisplayComponent.h" +#include "QsLog.h" +#include "power/PowerComponent.h" +#include "utils/Utils.h" +#include "KonvergoEngine.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool MouseEventFilter::eventFilter(QObject* watched, QEvent* event) +{ + SystemComponent& system = SystemComponent::Get(); + + if (event->type() == QEvent::KeyPress) + { + // In konvergo we intercept all keyboard events and translate them + // into web client actions. We need to do this so that we can remap + // keyboard buttons to different events. + // + QKeyEvent* kevent = dynamic_cast(event); + if (kevent) + { + system.setCursorVisibility(false); + if (kevent->spontaneous()) + { + InputKeyboard::Get().keyPress(QKeySequence(kevent->key() | kevent->modifiers())); + return true; + } + } + } + else if (event->type() == QEvent::MouseMove) + { + system.setCursorVisibility(true); + } + else if (event->type() == QEvent::Wheel) + { + return true; + } + else if (event->type() == QEvent::MouseButtonPress) + { + // ignore right clicks that would show context menu + QMouseEvent *mouseEvent = dynamic_cast(event); + if ((mouseEvent) && (mouseEvent->button() == Qt::RightButton)) + return true; + } + + return QObject::eventFilter(watched, event); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +KonvergoWindow::KonvergoWindow(QWindow* parent) : QQuickWindow(parent), m_debugLayer(false) +{ + // NSWindowCollectionBehaviorFullScreenPrimary is only set on OSX if Qt::WindowFullscreenButtonHint is set on the window. + setFlags(flags() | Qt::WindowFullscreenButtonHint); + + m_eventFilter = new MouseEventFilter(this); + installEventFilter(m_eventFilter); + + m_infoTimer = new QTimer(this); + m_infoTimer->setInterval(1000); + + connect(m_infoTimer, &QTimer::timeout, this, &KonvergoWindow::updateDebugInfo); + +#ifdef TARGET_RPI + // On RPI, we use dispmanx layering - the video is on a layer below Konvergo, + // and during playback the Konvergo window is partially transparent. The OSD + // will be visible on top of the video as part of the Konvergo window. + setColor(QColor("transparent")); +#else + setColor(QColor("black")); +#endif + + loadGeometry(); + + connect(SettingsComponent::Get().getSection(SETTINGS_SECTION_MAIN), &SettingsSection::valuesUpdated, + this, &KonvergoWindow::updateFullscreenSetting); + + connect(this, &KonvergoWindow::visibilityChanged, + this, &KonvergoWindow::onVisibilityChanged); + + connect(this, &KonvergoWindow::enableVideoWindowSignal, + this, &KonvergoWindow::enableVideoWindow, Qt::QueuedConnection); + +// connect(QGuiApplication::desktop(), &QDesktopWidget::screenCountChanged, +// this, &KonvergoWindow::onScreenCountChanged); + + connect(&PlayerComponent::Get(), &PlayerComponent::windowVisible, + this, &KonvergoWindow::playerWindowVisible); + + connect(&PlayerComponent::Get(), &PlayerComponent::playbackStarting, + this, &KonvergoWindow::playerPlaybackStarting); + + // this is using old syntax because ... reasons. QQuickCloseEvent is not public class + connect(this, SIGNAL(closing(QQuickCloseEvent*)), this, SLOT(closingWindow())); + + // make sure that we handle some of the host commands that can be emitted + connect(&InputComponent::Get(), &InputComponent::receivedHostCommand, + this, &KonvergoWindow::handleHostCommand); + + connect(qApp, &QCoreApplication::aboutToQuit, this, &KonvergoWindow::saveGeometry); + +#ifdef Q_OS_MAC + // this is such a hack. But I could not get it to enter into fullscreen + // mode if I didn't trigger this after a while. + // + QTimer::singleShot(500, [=]() { + updateFullscreenState(); + }); +#else + if (SettingsComponent::Get().value(SETTINGS_SECTION_MAIN, "fakefullscreen").toBool()) + updateFullscreenState(); + else if (SettingsComponent::Get().value(SETTINGS_SECTION_MAIN, "fullscreen").toBool() || SystemComponent::Get().isOpenELEC()) + setWindowState(Qt::WindowFullScreen); +#endif + + emit enableVideoWindowSignal(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void KonvergoWindow::closingWindow() +{ + if (SettingsComponent::Get().value(SETTINGS_SECTION_MAIN, "fullscreen").toBool() == false) + saveGeometry(); + + qApp->quit(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +KonvergoWindow::~KonvergoWindow() +{ + removeEventFilter(m_eventFilter); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void KonvergoWindow::saveGeometry() +{ + QRect rc = geometry(); + QJsonObject obj; + obj.insert("x", rc.x()); + obj.insert("y", rc.y()); + obj.insert("width", rc.width()); + obj.insert("height", rc.height()); + SettingsComponent::Get().setValue(SETTINGS_SECTION_STATE, "geometry", obj.toVariantMap()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void KonvergoWindow::loadGeometry() +{ + QRect rc = loadGeometryRect(); + if (rc.isValid()) + setGeometry(rc); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +QRect KonvergoWindow::loadGeometryRect() +{ + QJsonObject obj = QJsonObject::fromVariantMap(SettingsComponent::Get().value(SETTINGS_SECTION_STATE, "geometry").toMap()); + if (obj.isEmpty()) + return QRect(); + + QRect rc(obj["x"].toInt(), obj["y"].toInt(), obj["width"].toInt(), obj["height"].toInt()); + + if (rc.width() < 1280) + rc.setWidth(1280); + + if (rc.height() < 720) + rc.setHeight(720); + + // make sure our poisition is contained in one of the current screens + foreach (QScreen *screen, QGuiApplication::screens()) + { + if (screen->availableGeometry().contains(rc)) + return rc; + } + + // otherwise default to center of current screen + return QRect((screen()->geometry().width() - geometry().width()) / 2, + (screen()->geometry().height() - geometry().height()) / 2, + geometry().width(), + geometry().height()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void KonvergoWindow::enableVideoWindow() +{ + PlayerComponent::Get().setWindow(this); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void KonvergoWindow::setFullScreen(bool enable) +{ + QLOG_DEBUG() << "setting fullscreen = " << enable; + + SettingsComponent::Get().setValue(SETTINGS_SECTION_MAIN, "fullscreen", enable); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void KonvergoWindow::playerWindowVisible(bool visible) +{ + // adjust webengineview transparecy depending on player visibility + QQuickItem *web = findChild("web"); + if (web) + web->setProperty("backgroundColor", visible ? "transparent" : "#111111"); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void KonvergoWindow::updateFullscreenSetting(const QVariantMap& values) +{ + if (values.find("fullscreen") == values.end()) + return; + + updateFullscreenState(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void KonvergoWindow::updateFullscreenState() +{ + if (SettingsComponent::Get().value(SETTINGS_SECTION_MAIN, "fullscreen").toBool() || SystemComponent::Get().isOpenELEC()) + { + // if we were go from windowed to fullscreen + // we want to stor our current windowed position + if (!isFullScreen()) + saveGeometry(); + + setVisibility(QWindow::FullScreen); + } + else + { + setVisibility(QWindow::Windowed); + loadGeometry(); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void KonvergoWindow::onVisibilityChanged(QWindow::Visibility visibility) +{ + QLOG_DEBUG() << (visibility == QWindow::FullScreen ? "FullScreen" : "Windowed") << "visbility set to " << visibility; + + if (visibility == QWindow::Windowed) + loadGeometry(); + + if (visibility == QWindow::FullScreen) + PowerComponent::Get().setFullscreenState(true); + else if (visibility == QWindow::Windowed) + PowerComponent::Get().setFullscreenState(false); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void KonvergoWindow::playerPlaybackStarting() +{ +#if defined(Q_OS_MAC) + // On OSX, initializing VideoTooolbox (hardware decoder API) will mysteriously + // show the hidden mouse pointer again. The VTDecompressionSessionCreate API + // function does this, and we have no influence over its behavior. To make sure + // the cursor is gone again when starting playback, listen to the player's + // playbackStarting signal, at which point decoder initialization is guaranteed + // to be completed. Then we just have to set the cursor again on the Cocoa level. + if (QGuiApplication::overrideCursor()) + QGuiApplication::setOverrideCursor(QCursor(Qt::BlankCursor)); +#endif +} + +///////////////////////////////////////////////////////////////////////////////////////// +void KonvergoWindow::RegisterClass() +{ + qmlRegisterType("Konvergo", 1, 0, "KonvergoWindow"); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void KonvergoWindow::onScreenCountChanged(int newCount) +{ + updateFullscreenState(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void KonvergoWindow::updateDebugInfo() +{ + if (m_debugInfo.size() == 0) + m_debugInfo = SystemComponent::Get().debugInformation(); + m_videoInfo = PlayerComponent::Get().videoInformation(); + emit debugInfoChanged(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void KonvergoWindow::handleHostCommand(QString hostCommand) +{ + QLOG_DEBUG() << "Got command:" << hostCommand; + QString arguments = ""; + int arguments_start = hostCommand.indexOf(":"); + if (arguments_start > 0) + { + arguments = hostCommand.mid(arguments_start + 1); + hostCommand = hostCommand.mid(0, arguments_start); + } + if (hostCommand == "fullscreen") + { + SettingsComponent::Get().setValue(SETTINGS_SECTION_MAIN, "fullscreen", !SettingsComponent::Get().value(SETTINGS_SECTION_MAIN, "fullscreen").toBool()); + } + else if (hostCommand == "close") + { + close(); + } + else if (hostCommand == "player") + { + PlayerComponent::Get().userCommand(arguments); + } + else if (hostCommand == "toggleDebug") + { + if (property("showDebugLayer").toBool()) + { + m_infoTimer->stop(); + setProperty("showDebugLayer", false); + } + else + { + m_infoTimer->start(); + updateDebugInfo(); + setProperty("showDebugLayer", true); + } + } + else if (hostCommand == "recreateRpiUi") + { + DisplayManager* display_manager = DisplayComponent::Get().getDisplayManager(); + if (display_manager) + display_manager->resetRendering(); + } + else if (hostCommand == "reload") + { + emit reloadWebClient(); + } + else if (hostCommand == "crash!") + { + *(volatile int*)0=0; + } + else + { + QLOG_WARN() << "unknown host command" << hostCommand; + } +} diff --git a/src/ui/KonvergoWindow.h b/src/ui/KonvergoWindow.h new file mode 100644 index 0000000..e007965 --- /dev/null +++ b/src/ui/KonvergoWindow.h @@ -0,0 +1,76 @@ +#ifndef KONVERGOWINDOW_H +#define KONVERGOWINDOW_H + +#include +#include + +/////////////////////////////////////////////////////////////////////////////////////////////////// +class MouseEventFilter : public QObject +{ + Q_OBJECT +public: + MouseEventFilter(QObject* parent = 0) : QObject(parent) {} + +protected: + bool eventFilter(QObject* watched, QEvent* event); +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +class KonvergoWindow : public QQuickWindow +{ + Q_OBJECT + Q_PROPERTY(bool fullScreen READ isFullScreen WRITE setFullScreen NOTIFY fullScreenSwitched) + Q_PROPERTY(bool showDebugLayer MEMBER m_debugLayer NOTIFY debugLayerChanged) + Q_PROPERTY(QString debugInfo MEMBER m_debugInfo NOTIFY debugInfoChanged) + Q_PROPERTY(QString videoInfo MEMBER m_videoInfo NOTIFY debugInfoChanged) +public: + static void RegisterClass(); + + KonvergoWindow(QWindow* parent = 0); + ~KonvergoWindow(); + + bool isFullScreen() + { + return ((flags() & Qt::FramelessWindowHint) || (visibility() == QWindow::FullScreen)); + } + + void setFullScreen(bool enable); + + Q_SLOT void otherAppFocus() + { + setWindowState((Qt::WindowState)((windowState() & ~Qt::WindowMinimized) | Qt::WindowActive)); + raise(); + } + +Q_SIGNALS: + void fullScreenSwitched(); + void enableVideoWindowSignal(); + void debugLayerChanged(); + void debugInfoChanged(); + void reloadWebClient(); + +private slots: + void closingWindow(); + void enableVideoWindow(); + void onVisibilityChanged(QWindow::Visibility visibility); + void updateFullscreenSetting(const QVariantMap& values); + void updateFullscreenState(); + void onScreenCountChanged(int newCount); + void handleHostCommand(QString hostCommand); + void updateDebugInfo(); + void playerWindowVisible(bool visible); + void playerPlaybackStarting(); + +private: + void saveGeometry(); + void loadGeometry(); + QRect loadGeometryRect(); + +private: + bool m_debugLayer; + MouseEventFilter* m_eventFilter; + QTimer* m_infoTimer; + QString m_debugInfo, m_videoInfo; +}; + +#endif // KONVERGOWINDOW_H diff --git a/src/ui/errormessage.qml b/src/ui/errormessage.qml new file mode 100644 index 0000000..de7c2f3 --- /dev/null +++ b/src/ui/errormessage.qml @@ -0,0 +1,22 @@ +import QtQuick 2.2 +import QtQuick.Dialogs 1.1 + +MessageDialog { + id: messageDialog + title: errorTitle + text: errorTitle + informativeText: errorText + icon: StandardIcon.Critical + standardButtons: StandardButton.Help | StandardButton.Close + + onRejected: { + Qt.quit() + } + + onHelp: { + Qt.openUrlExternally("https://forums.plex.tv") + Qt.quit() + } + + Component.onCompleted: visible = true +} diff --git a/src/ui/webview.qml b/src/ui/webview.qml new file mode 100644 index 0000000..38e4fd0 --- /dev/null +++ b/src/ui/webview.qml @@ -0,0 +1,147 @@ +import QtQuick 2.4 +import Konvergo 1.0 +import QtWebEngine 1.1 +import QtWebChannel 1.0 + +KonvergoWindow +{ + id: mainWindow + title: "Plex Media Player" + objectName: "mainWindow" + visible: true + minimumHeight: 720 + minimumWidth: 1280 + + MpvVideo + { + id: video + objectName: "video" + // It's not a real item. Its renderer draws onto the view's background. + width: 0 + height: 0 + visible: false + } + + WebEngineView + { + id: web + objectName: "web" + width: Math.min((parent.height * 16) / 9, parent.width) + height: Math.min((parent.width * 9) / 16, parent.height) + anchors.centerIn: parent + settings.errorPageEnabled: false + settings.localContentCanAccessRemoteUrls: true + profile.httpUserAgent: components.system.getUserAgent() + url: components.settings.value("path", "startupurl") + + Component.onCompleted: + { + // set the transparency + // (setting this here as a UserAgent workaround at least for qt5.5) + backgroundColor : "#111111" + forceActiveFocus() + mainWindow.reloadWebClient.connect(reload) + } + + onLoadingChanged: + { + // we use a timer here to switch to the webview since + // it take a few moments for the webview to render + // after it has loaded. + // + if (loadRequest.status == WebEngineView.LoadSucceededStatus) + { + console.log("Loaded web-client successfully from: " + web.url); + } + else if (loadRequest.status == WebEngineView.LoadFailedStatus) + { + errorLabel.visible = true + errorLabel.text = "Error loading client, this is bad and should not happen
    " + + "You can try to reload or head to our support page

    Actual Error:
    " +
    +                          loadRequest.url + "\n" + loadRequest.errorString + " [" + loadRequest.errorCode + "]


    " + + "Provide the logfile as well." + } + } + + onJavaScriptConsoleMessage: + { + components.system.info(message) + } + + onCertificateError: { + error.ignoreCertificateError() + console.log(error.url + " :" + error.description + error.error) + } + } + + Text + { + id: errorLabel + z: 5 + anchors.centerIn: parent + color: "#999999" + linkColor: "#cc7b19" + text: "Generic error" + font.pixelSize: 32 + font.bold: true + visible: false + verticalAlignment: Text.AlignVCenter + textFormat: Text.StyledText + onLinkActivated: + { + if (link == "reload") + { + web.reload() + errorLabel.visible = false + } + else + { + Qt.openUrlExternally(link) + } + } + } + + Rectangle + { + id: debug + color: "black" + z: 10 + anchors.centerIn: parent + width: parent.width + height: parent.height + opacity: 0.7 + visible: mainWindow.showDebugLayer + + Text + { + id: debugLabel + width: (parent.width - 50) / 2 + height: parent.height - 25 + anchors.left: parent.left + anchors.leftMargin: 25 + anchors.top: parent.top + anchors.topMargin: 10 + color: "white" + font.pixelSize: width / 45 + + text: mainWindow.debugInfo + } + + Text + { + id: videoLabel + width: (parent.width - 50) / 2 + height: parent.height - 25 + anchors.right: parent.right + anchors.rightMargin: 25 + anchors.top: parent.top + anchors.topMargin: 10 + color: "white" + font.pixelSize: width / 45 + + text: mainWindow.videoInfo + } + } + + property QtObject webChannel: web.webChannel +} \ No newline at end of file diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt new file mode 100644 index 0000000..e4cd81c --- /dev/null +++ b/src/utils/CMakeLists.txt @@ -0,0 +1,12 @@ + +add_sources( + CachedRegexMatcher.cpp CachedRegexMatcher.h + PlatformUtils.cpp PlatformUtils.h + HelperLauncher.h HelperLauncher.cpp + Utils.cpp Utils.h +) + +if(APPLE) + add_sources(HelperLaunchd.cpp HelperLaunchd.cpp) + add_subdirectory(osx) +endif(APPLE) diff --git a/src/utils/CachedRegexMatcher.cpp b/src/utils/CachedRegexMatcher.cpp new file mode 100644 index 0000000..38d5e98 --- /dev/null +++ b/src/utils/CachedRegexMatcher.cpp @@ -0,0 +1,77 @@ +// +// Created by Tobias Hieta on 20/08/15. +// + +#include "CachedRegexMatcher.h" +#include "QsLog.h" + +///////////////////////////////////////////////////////////////////////////////////////// +bool CachedRegexMatcher::addMatcher(const QString& pattern, const QVariant& result) +{ + QRegExp matcher(pattern); + if (!matcher.isValid()) + { + QLOG_WARN() << "Could not compile pattern:" << pattern; + return false; + } + + m_matcherList.push_back(qMakePair(matcher, result)); + QLOG_DEBUG() << "Added pattern:" << matcher; + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////// +QVariant CachedRegexMatcher::match(const QString& input) +{ + // first we check if this match has already happened before + if (m_matcherCache.contains(input)) + { + QLOG_DEBUG() << "Found cached match for input:" << input; + return m_matcherCache.value(input); + } + + // otherwise try to iterate our list and find a match + foreach(const MatcherValuePair& matcher, m_matcherList) + { + QRegExp re(matcher.first); + + if (re.indexIn(input) != -1) + { + // found match + QLOG_DEBUG() << "Matched:" << "to pattern:" << re.pattern(); + + QVariant returnValue = matcher.second; + + if (re.captureCount() > 0 && matcher.second.type() == QVariant::String) + { + QString value(matcher.second.toString()); + + QLOG_DEBUG() << "Captured:" << re.captureCount(); + for (int i = 0; i < re.captureCount(); i ++) + { + QLOG_DEBUG() << "capture" << i+1 << ":" << re.cap(i+1); + value = value.arg(re.cap(i+1)); + } + QLOG_DEBUG() << "After captures the final value is:" << value; + returnValue = QVariant(value); + } + + // now cache the match and the final value + m_matcherCache.insert(input, returnValue); + + return returnValue; + } + } + + QLOG_DEBUG() << "No match for:" << input; + + // no match at all + return QVariant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void CachedRegexMatcher::clear() +{ + m_matcherCache.clear(); + m_matcherList.clear(); +} diff --git a/src/utils/CachedRegexMatcher.h b/src/utils/CachedRegexMatcher.h new file mode 100644 index 0000000..608e8f4 --- /dev/null +++ b/src/utils/CachedRegexMatcher.h @@ -0,0 +1,30 @@ +// +// Created by Tobias Hieta on 20/08/15. +// + +#ifndef KONVERGO_CACHEDREGEXMATCHER_H +#define KONVERGO_CACHEDREGEXMATCHER_H + +#include +#include +#include +#include + +typedef QPair MatcherValuePair; +typedef QList MatcherValueList; + +class CachedRegexMatcher : public QObject +{ +public: + CachedRegexMatcher(QObject* parent = 0) : QObject(parent) {} + + bool addMatcher(const QString& pattern, const QVariant& result); + QVariant match(const QString& input); + void clear(); + +private: + MatcherValueList m_matcherList; + QHash m_matcherCache; +}; + +#endif //KONVERGO_CACHEDREGEXMATCHER_H diff --git a/src/utils/HelperLaunchd.cpp b/src/utils/HelperLaunchd.cpp new file mode 100644 index 0000000..1b24020 --- /dev/null +++ b/src/utils/HelperLaunchd.cpp @@ -0,0 +1,141 @@ +// +// Created by Tobias Hieta on 18/09/15. +// + +#include +#include + +#include "HelperLaunchd.h" + +#include "QsLog.h" +#include "plistparser.h" +#include "plistserializer.h" +#include "HelperLauncher.h" + +#define LAUNCHCTL_PATH "/bin/launchctl" + +///////////////////////////////////////////////////////////////////////////////////////// +HelperLaunchd::HelperLaunchd(QObject* parent) : QObject(parent) +{ + m_launchctl = new QProcess(this); + + connect(m_launchctl, static_cast(&QProcess::error), [=](QProcess::ProcessError error){ + QLOG_ERROR() << "When trying to execute launchctl:" << m_launchctl->errorString(); + }); + + connect(m_launchctl, static_cast(&QProcess::finished), [=](int exitCode, QProcess::ExitStatus status){ + if (status == QProcess::NormalExit) + { + QLOG_INFO() << "Ran launchctl successfully"; + } + else + { + QLOG_ERROR() << "Failed to run launchctl:" << m_launchctl->errorString(); + } + }); +} + +///////////////////////////////////////////////////////////////////////////////////////// +QString HelperLaunchd::launchPlistPath() +{ + QDir homeDir = QStandardPaths::standardLocations(QStandardPaths::HomeLocation).first(); + if (homeDir.cd("Library") && homeDir.cd("LaunchAgents")) + return homeDir.filePath("tv.plex.player-helper.plist"); + return ""; +} + +///////////////////////////////////////////////////////////////////////////////////////// +bool HelperLaunchd::checkHelperPath() +{ + QString plistFile = launchPlistPath(); + if (!plistFile.isEmpty()) + { + QFile fp(plistFile); + if (fp.open(QIODevice::ReadOnly)) + { + PListParser parser; + QVariantMap plistValues = parser.parsePList(&fp).toMap(); + + return plistValues.value("Program").toString() == HelperLauncher::HelperPath(); + } + } + + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// +bool HelperLaunchd::writePlist() +{ + QVariantMap launchPlist; + + launchPlist.insert("Label", "tv.plex.player"); + launchPlist.insert("RunAtLoad", true); + + QVariantMap keepAlive; + keepAlive.insert("SuccessfulExit", false); + launchPlist.insert("Keep-Alive", keepAlive); + + launchPlist.insert("ProcessType", "Background"); + launchPlist.insert("Program", HelperLauncher::HelperPath()); + + PListSerializer plistOut; + QString output = plistOut.toPList(launchPlist); + + if (!output.isEmpty()) + { + QFile fp(launchPlistPath()); + if (fp.open(QIODevice::WriteOnly | QIODevice::Truncate)) + { + fp.write(output.toUtf8()); + } + else + { + QLOG_WARN() << "Failed to write launchd plist file:" << launchPlistPath(); + return false; + } + } + + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// +bool HelperLaunchd::loadHelper() +{ + QLOG_DEBUG() << "Loading helper"; + QStringList args; + args << "load"; + args << launchPlistPath(); + m_launchctl->start(LAUNCHCTL_PATH, args); + m_launchctl->waitForFinished(5000); + return m_launchctl->exitStatus() == QProcess::NormalExit; +} + +///////////////////////////////////////////////////////////////////////////////////////// +bool HelperLaunchd::unloadHelper() +{ + QLOG_DEBUG() << "Unloading helper"; + QStringList args; + args << "unload"; + args << launchPlistPath(); + m_launchctl->start(LAUNCHCTL_PATH, args); + m_launchctl->waitForFinished(5000); + + return m_launchctl->exitStatus() == QProcess::NormalExit; +} + +///////////////////////////////////////////////////////////////////////////////////////// +void HelperLaunchd::start() +{ + unloadHelper(); + + if (!checkHelperPath()) + writePlist(); + + loadHelper(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void HelperLaunchd::stop() +{ + unloadHelper(); +} diff --git a/src/utils/HelperLaunchd.h b/src/utils/HelperLaunchd.h new file mode 100644 index 0000000..d72d61a --- /dev/null +++ b/src/utils/HelperLaunchd.h @@ -0,0 +1,30 @@ +// +// Created by Tobias Hieta on 18/09/15. +// + +#ifndef PLEXTHEATER_HELPERLAUNCHD_H +#define PLEXTHEATER_HELPERLAUNCHD_H + +#include +#include + +class HelperLaunchd : public QObject +{ + Q_OBJECT +public: + HelperLaunchd(QObject* parent = 0); + + void start(); + void stop(); + +private: + bool checkHelperPath(); + bool writePlist(); + bool loadHelper(); + bool unloadHelper(); + QString launchPlistPath(); + + QProcess* m_launchctl; +}; + +#endif //PLEXTHEATER_HELPERLAUNCHD_H diff --git a/src/utils/HelperLauncher.cpp b/src/utils/HelperLauncher.cpp new file mode 100644 index 0000000..b97a727 --- /dev/null +++ b/src/utils/HelperLauncher.cpp @@ -0,0 +1,182 @@ +// +// Created by Tobias Hieta on 28/08/15. +// + +#include "HelperLauncher.h" +#include "QsLog.h" +#include "settings/SettingsComponent.h" +#include "settings/SettingsSection.h" +#include "utils/Utils.h" +#include "Names.h" + +#include + +///////////////////////////////////////////////////////////////////////////////////////// +HelperLauncher::HelperLauncher(QObject* parent) : QObject(parent) +{ + start(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void HelperLauncher::start() +{ + m_jsonClient = new LocalJsonClient("pmpHelper"); + connect(m_jsonClient, &QLocalSocket::connected, this, &HelperLauncher::didConnect); + connect(m_jsonClient, static_cast(&QLocalSocket::error), this, &HelperLauncher::socketError); + connect(m_jsonClient, &LocalJsonClient::messageReceived, this, &HelperLauncher::gotMessage); + connect(m_jsonClient, &QLocalSocket::disconnected, this, &HelperLauncher::socketDisconnect); + +#ifdef Q_OS_MAC + m_launchd = new HelperLaunchd(this); +#endif + + m_helperProcess = new QProcess(this); + + connect(SettingsComponent::Get().getSection(SETTINGS_SECTION_WEBCLIENT), &SettingsSection::valuesUpdated, [=](const QVariantMap& values) + { + if (values.contains("clientID")) + updateClientId(); + }); +} + +///////////////////////////////////////////////////////////////////////////////////////// +bool HelperLauncher::connectToHelper() +{ + if (m_jsonClient->state() == QLocalSocket::ConnectedState || + m_jsonClient->state() == QLocalSocket::ConnectingState) + return true; + + QLOG_DEBUG() << "Connecting to helper"; + m_jsonClient->connectToServer(); + + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////// +bool HelperLauncher::killHelper() +{ + QVariantMap msg; + msg.insert("command", "quit"); + m_jsonClient->sendMessage(msg); + + if (m_jsonClient->waitForBytesWritten(1000)) + { + QLOG_DEBUG() << "Waiting for helper to die"; + if (!m_jsonClient->waitForDisconnected(3000)) + { + QLOG_ERROR() << "Helper refused to disconnect. Didn't it get the PM?"; + return false; + } + + QLOG_DEBUG() << "Helper gone"; + return true; + } + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// +void HelperLauncher::gotMessage(const QVariantMap& message) +{ + if (message.value("version").toString() != Version::GetVersionString() || + message.value("path").toString() != HelperPath()) + { + QLOG_WARN() << "Running helper does not match our current version. Killing it and starting a new one."; + killHelper(); + } + else + { + QLOG_DEBUG() << "Helper is running version:" << message.value("version").toString(); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +void HelperLauncher::socketError(QLocalSocket::LocalSocketError error) +{ + QLOG_DEBUG() << "Failed to connect to helper:" << m_jsonClient->errorString(); + + if (error == QLocalSocket::ConnectionRefusedError || + error == QLocalSocket::ServerNotFoundError) + launch(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void HelperLauncher::socketDisconnect() +{ + QLOG_DEBUG() << "Disconnected from helper, trying to relaunch"; + connectToHelper(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void HelperLauncher::updateClientId() +{ + // update clientId if we have it + if (!SettingsComponent::Get().value(SETTINGS_SECTION_WEBCLIENT, "clientID").toString().isEmpty()) + { + QVariantMap msg; + msg.insert("command", "info"); + + QVariantMap arg; + arg.insert("clientId", SettingsComponent::Get().value(SETTINGS_SECTION_WEBCLIENT, "clientID").toString()); + + QString userId = Utils::CurrentUserId(); + if (!userId.isEmpty()) + arg.insert("userId", userId); + + msg.insert("argument", arg); + + m_jsonClient->sendMessage(msg); + } +}; + +///////////////////////////////////////////////////////////////////////////////////////// +void HelperLauncher::didConnect() +{ + QLOG_DEBUG() << "Connected to helper"; + updateClientId(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +void HelperLauncher::launch() +{ + QLOG_DEBUG() << "Launching helper:" << HelperPath(); + +#ifdef Q_OS_MAC + m_launchd->start(); +#else + if (!m_helperProcess->startDetached(HelperPath(), QStringList())) + { + QLOG_ERROR() << "Failed to open helper: " + m_helperProcess->errorString(); + throw FatalException("Failed to launch helper: " + m_helperProcess->errorString()); + } +#endif + + QTimer::singleShot(1000, this, &HelperLauncher::connectToHelper); +} + +///////////////////////////////////////////////////////////////////////////////////////// +QString HelperLauncher::HelperPath() +{ + QString programName = Names::HelperName(); +#ifdef Q_OS_WIN + programName += ".exe"; +#endif + + QString helperPath = SettingsComponent::Get().value(SETTINGS_SECTION_PATH, "helperprogram").toString(); + + // fallback to the resource dir + if (helperPath.isEmpty() || !QFile().exists(helperPath)) + helperPath = Paths::resourceDir(programName); + + if (!QFile().exists(helperPath)) + throw FatalException("Can't find helper program"); + + return helperPath; +} + +///////////////////////////////////////////////////////////////////////////////////////// +void HelperLauncher::stop() +{ + // this method needs to disconnect all signals from the helper as well, so it doesn't start up again. + disconnect(m_jsonClient); + killHelper(); +} diff --git a/src/utils/HelperLauncher.h b/src/utils/HelperLauncher.h new file mode 100644 index 0000000..f8ff9be --- /dev/null +++ b/src/utils/HelperLauncher.h @@ -0,0 +1,53 @@ +// +// Created by Tobias Hieta on 28/08/15. +// + +#ifndef KONVERGO_HELPERLAUNCHER_H +#define KONVERGO_HELPERLAUNCHER_H + +#include +#include +#include +#include + +#include "LocalJsonClient.h" +#include "tools/helper/HelperSocket.h" +#include "Version.h" +#include "utils/Utils.h" + +#ifdef Q_OS_MAC +#include "HelperLaunchd.h" +#endif + +class HelperLauncher : public QObject +{ + Q_OBJECT + DEFINE_SINGLETON(HelperLauncher) +public: + bool connectToHelper(); + static QString HelperPath(); + void stop(); + void start(); + +private Q_SLOTS: + void gotMessage(const QVariantMap& message); + void socketError(QLocalSocket::LocalSocketError error); + void didConnect(); + void launch(); + void socketDisconnect(); + bool killHelper(); + +private: + explicit HelperLauncher(QObject* parent = 0); + + QProcess* m_helperProcess; + LocalJsonClient* m_jsonClient; + + void updateClientId(); + +#ifdef Q_OS_MAC + HelperLaunchd* m_launchd; +#endif +}; + +#endif //KONVERGO_HELPERLAUNCHER_H diff --git a/src/utils/PlatformUtils.cpp b/src/utils/PlatformUtils.cpp new file mode 100644 index 0000000..a76ab27 --- /dev/null +++ b/src/utils/PlatformUtils.cpp @@ -0,0 +1,19 @@ +// +// Created by Tobias Hieta on 15/05/15. +// + +#include "PlatformUtils.h" + +#ifdef Q_OS_UNIX +#include +#endif + +bool PlatformUtils::isProcessAlive(Q_PID pid) +{ +#ifdef Q_OS_UNIX + int ret = kill(pid, 0); + return ret == 0; +#endif + + return false; +} \ No newline at end of file diff --git a/src/utils/PlatformUtils.h b/src/utils/PlatformUtils.h new file mode 100644 index 0000000..510913f --- /dev/null +++ b/src/utils/PlatformUtils.h @@ -0,0 +1,18 @@ +// +// Created by Tobias Hieta on 15/05/15. +// + +#ifndef KONVERGO_PLATFORMUTILS_H +#define KONVERGO_PLATFORMUTILS_H + +#include + +class PlatformUtils +{ +public: + PlatformUtils() { } + static bool isProcessAlive(Q_PID pid); +}; + + +#endif //KONVERGO_PLATFORMUTILS_H diff --git a/src/utils/Utils.cpp b/src/utils/Utils.cpp new file mode 100644 index 0000000..4b416ad --- /dev/null +++ b/src/utils/Utils.cpp @@ -0,0 +1,98 @@ +#include "Utils.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "settings/SettingsComponent.h" +#include "settings/SettingsSection.h" + +#include "osx/OSXUtils.h" +#include "QsLog.h" + + +///////////////////////////////////////////////////////////////////////////////////////// +QString Utils::ComputerName() +{ +#ifdef Q_OS_MAC + return OSXUtils::computerName(); +#else + return QHostInfo::localHostName(); +#endif +} + +///////////////////////////////////////////////////////////////////////////////////////// +QJsonDocument Utils::OpenJsonDocument(const QString& path, QJsonParseError* err) +{ + QFile fp(path); + QByteArray fdata; + QRegExp commentMatch("^\\s*(\\S+\\s*=([^\\//]|\\//?)*)?(//.*)?$"); + + if (fp.open(QFile::ReadOnly)) + { + while(true) + { + QByteArray row = fp.readLine(); + + if (row.isEmpty()) + break; + + // filter all comments + if (commentMatch.indexIn(row) != -1) + continue; + + fdata.append(row); + } + + fp.close(); + } + + return QJsonDocument::fromJson(fdata, err); +} + +///////////////////////////////////////////////////////////////////////////////////////// +Platform Utils::CurrentPlatform() +{ +#if defined(Q_OS_MAC) + return PLATFORM_OSX; +#elif KONVERGO_OPENELEC + #if TARGET_RPI + return PLATFORM_OE_RPI; + #else + return PLATFORM_OE_X86; + #endif +#elif defined(Q_OS_LINUX) + return PLATFORM_LINUX; +#elif defined(Q_OS_WIN32) + return PLATFORM_WINDOWS; +#else + return PLATFORM_UNKNOWN; +#endif +} + +///////////////////////////////////////////////////////////////////////////////////////// +QString Utils::CurrentUserId() +{ + SettingsSection* connections = SettingsComponent::Get().getSection("connections"); + if (!connections) + return QString(); + + QVariant ulist = connections->value("users"); + if (ulist.isValid()) + { + QVariantList users = ulist.toList(); + if (users.size() > 0) + { + QVariantMap user = users.at(0).toMap(); + return user.value("id").toString(); + } + } + + return QString(); +} diff --git a/src/utils/Utils.h b/src/utils/Utils.h new file mode 100644 index 0000000..3aa88c2 --- /dev/null +++ b/src/utils/Utils.h @@ -0,0 +1,57 @@ +#ifndef UTILS_H +#define UTILS_H + +#include +#include +#include +#include +#include + +#ifdef Q_OS_MAC + #include "osx/OSXUtils.h" +#endif + +#define DEFINE_SINGLETON(cls) \ + public: \ + static cls& Get() \ + { \ + static cls __instance; \ + return __instance; \ + } \ + + +class FatalException : public QException +{ +public: + explicit FatalException(const QString& message) : m_message(message) {} + const QString& message() const { return m_message; } + + ~FatalException() throw() { } + +private: + QString m_message; +}; + + enum Platform + { + PLATFORM_UNKNOWN = 0, + PLATFORM_OSX = (1 << 0), + PLATFORM_LINUX = (1 << 1), + PLATFORM_OE_X86 = (1 << 2), + PLATFORM_OE_RPI = (1 << 3), + PLATFORM_WINDOWS = (1 << 4), + PLATFORM_OE = (PLATFORM_OE_RPI | PLATFORM_OE_X86), + PLATFORM_ANY = (PLATFORM_OSX | PLATFORM_WINDOWS | PLATFORM_LINUX | PLATFORM_OE) + }; + +#define PLATFORM_ANY_EXCEPT(x) (PLATFORM_ANY & (~(x))) + +namespace Utils +{ + Platform CurrentPlatform(); + QJsonDocument OpenJsonDocument(const QString& path, QJsonParseError* err); + QString CurrentUserId(); + QString ComputerName(); +} + +#endif // UTILS_H diff --git a/src/utils/osx/CMakeLists.txt b/src/utils/osx/CMakeLists.txt new file mode 100644 index 0000000..e533a89 --- /dev/null +++ b/src/utils/osx/CMakeLists.txt @@ -0,0 +1,3 @@ +set(OSXUTILS_SRC OSXUtils.h OSXUtils.mm) +add_sources(${OSXUTILS_SRC}) + diff --git a/src/utils/osx/OSXUtils.h b/src/utils/osx/OSXUtils.h new file mode 100644 index 0000000..a0614fd --- /dev/null +++ b/src/utils/osx/OSXUtils.h @@ -0,0 +1,13 @@ +#ifndef OSXUTILS_H +#define OSXUTILS_H + +#include + +class OSXUtils +{ +public: + static void SetMenuBarVisible(bool visible); + static QString computerName(); +}; + +#endif /* OSXUTILS_H */ diff --git a/src/utils/osx/OSXUtils.mm b/src/utils/osx/OSXUtils.mm new file mode 100644 index 0000000..86220ad --- /dev/null +++ b/src/utils/osx/OSXUtils.mm @@ -0,0 +1,24 @@ +#include "OSXUtils.h" +#import + +///////////////////////////////////////////////////////////////////////////////////////// +void OSXUtils::SetMenuBarVisible(bool visible) +{ + if(visible) + { + [[NSApplication sharedApplication] + setPresentationOptions: NSApplicationPresentationDefault]; + } + else + { + [[NSApplication sharedApplication] + setPresentationOptions: NSApplicationPresentationHideMenuBar | + NSApplicationPresentationHideDock]; + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +QString OSXUtils::computerName() +{ + return QString::fromNSString([[NSHost currentHost] localizedName]); +} diff --git a/vagrant/ubuntu-x86_64/Vagrantfile b/vagrant/ubuntu-x86_64/Vagrantfile new file mode 100644 index 0000000..aee686b --- /dev/null +++ b/vagrant/ubuntu-x86_64/Vagrantfile @@ -0,0 +1,21 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +# All Vagrant configuration is done below. The "2" in Vagrant.configure +# configures the configuration version (we support older styles for +# backwards compatibility). Please don't change it unless you know what +# you're doing. +Vagrant.configure(2) do |config| + config.vm.box = "ubuntu/trusty64" + + config.vm.synced_folder "../..", "/source", type: "nfs" + config.vm.network "private_network", type: :dhcp + + config.vm.provider "virtualbox" do |v| + v.memory = 4096 + v.cpus = 4 + end + + config.vm.provision :shell, path: "bootstrap.sh" + +end diff --git a/vagrant/ubuntu-x86_64/bootstrap.sh b/vagrant/ubuntu-x86_64/bootstrap.sh new file mode 100755 index 0000000..aa8995d --- /dev/null +++ b/vagrant/ubuntu-x86_64/bootstrap.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +set -e + +if [ ! -e /swapfile ]; then + fallocate -l 16G /swapfile + chmod 600 /swapfile + mkswap /swapfile + swapon /swapfile + echo "/swapfile none swap sw 0 0" >>/etc/fstab +fi + +cp /vagrant/sources.list /etc/apt/sources.list + +add-apt-repository -y ppa:beineri/opt-qt55-trusty +apt-get update -y +apt-get upgrade -y +apt-get install -y git cmake ninja-build build-essential libssl-dev libgles2-mesa-dev qt54-meta-full pkg-config autoconf libtool libfreetype6-dev libfribidi-dev libfontconfig-dev libharfbuzz-dev yasm libgnutls-dev libbz2-dev libxrandr-dev libglew-dev libsdl2-dev libcec-dev + +sudo -u vagrant cp /vagrant/build-deps.sh . +chmod 755 build-deps.sh +sudo -u vagrant ./build-deps.sh \ No newline at end of file diff --git a/vagrant/ubuntu-x86_64/build-deps.sh b/vagrant/ubuntu-x86_64/build-deps.sh new file mode 100755 index 0000000..09e2306 --- /dev/null +++ b/vagrant/ubuntu-x86_64/build-deps.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +set -e + +rm -rf libass ffmpeg mpv + +git clone --depth 1 git://github.com/libass/libass +git clone --depth 1 git://github.com/ffmpeg/ffmpeg +git clone git://github.com/mpv-player/mpv + +cd libass +./autogen.sh +./configure +make -j 4 +sudo make install + +cd .. + +cd ffmpeg +./configure --enable-gnutls --enable-shared --disable-static +make -j 4 +sudo make install + +cd .. + +cd mpv +./bootstrap.py +./waf configure --enable-libmpv-shared --disable-cplayer +./waf build -j5 +sudo ./waf install + +cd .. diff --git a/vagrant/ubuntu-x86_64/sources.list b/vagrant/ubuntu-x86_64/sources.list new file mode 100644 index 0000000..6fe3ded --- /dev/null +++ b/vagrant/ubuntu-x86_64/sources.list @@ -0,0 +1,4 @@ +deb mirror://mirrors.ubuntu.com/mirrors.txt trusty main restricted universe multiverse +deb mirror://mirrors.ubuntu.com/mirrors.txt trusty-updates main restricted universe multiverse +deb mirror://mirrors.ubuntu.com/mirrors.txt trusty-backports main restricted universe multiverse +deb mirror://mirrors.ubuntu.com/mirrors.txt trusty-security main restricted universe multiverse \ No newline at end of file diff --git a/vagrant/windows-x86_64/Vagrantfile b/vagrant/windows-x86_64/Vagrantfile new file mode 100644 index 0000000..3007c36 --- /dev/null +++ b/vagrant/windows-x86_64/Vagrantfile @@ -0,0 +1,36 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +# All Vagrant configuration is done below. The "2" in Vagrant.configure +# configures the configuration version (we support older styles for +# backwards compatibility). Please don't change it unless you know what +# you're doing. +Vagrant.configure(2) do |config| + config.vm.box = "win8-konvergo" + config.vm.guest = :windows + config.vm.communicator = :winrm + config.winrm.timeout = 500 + config.winrm.host = "127.0.0.1" + config.winrm.port = 55985 + config.vm.synced_folder "../..", "/c/source/konvergo", + create: true, + type: "rsync", + rsync__exclude: ["dependencies", "vagrant", ".git"], + rsync__args: ["--verbose", "--archive", "--delete", "-zz"] + config.vm.synced_folder "../../../plex-dependency-builder", "/c/source/dep-builder", + create: true, + type: "rsync", + rsync__exclude: ["build", "output", ".git", "*.pyc"], + rsync__args: ["--verbose", "--archive", "--delete", "-zz"] + config.vm.network "forwarded_port", host: 33389, guest: 3389 + config.ssh.private_key_path = "vagrant_insecure_key" + + config.vm.provider "virtualbox" do |v| + v.memory = 4096 + v.cpus = 4 + v.gui = true + end + + #config.vm.provision :shell, path: "bootstrap.sh" + +end diff --git a/vagrant/windows-x86_64/vagrant_insecure_key b/vagrant/windows-x86_64/vagrant_insecure_key new file mode 100644 index 0000000..4635e5b --- /dev/null +++ b/vagrant/windows-x86_64/vagrant_insecure_key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAxlu1Hgq4GRz6wtKUeLbYqOp1E41jkxam+v+t/l7YyBOECjo5 +gAjt0WBmjmz/JCLaVyEclJE6IE+68D9ADgLS9uMDOhjTPIwUEODaevfPQVoD+TKk +JsV9eNVw2PHcWv/yFaj4NsLCZDDCkSdTZOwwUmh1Rwq2a8O/LCdEOEdtWMgyLZMW +O3DpgHl2eUOicSYmnQR6yD+vRgQbDupDIj5xBGqDmNmJOguJFnnwuNUz/UONPWfQ +NkI8EJtK29nC/kXI+suqi/Uf1dVIFK3snQ/DSxJ5kiuXN/u6WPqyx0BK00fMlHxA +DBHAo13dIbxTdwsiE+lFlqpzOkfp3Kkzybl5EwIDAQABAoIBADJk1izduEvZCvQ4 +H7RTb9PqTPg8sZGK7b1S+9dmLVkXbDJU/IrEufa5sRR6KI/7stDz/pjYFJZk6y9i +QlnM6eADaKx35iK5y9dF6PvYvXTy85N6BC49elUalOLo/3y3Db/m/7iZ3l0p7iSG +gy9euiAVncYAuLvELWuARlqt+t/U+fRbhRdvwJMVunmWVMEKI2v41tvKHoIHP1hB +alyVwDJ8D9Vog5WGykjrOlHkfMBzlKyXXQg0DR1LFlT7NxtRGGWi1mJANMGTz5Mw +nrn+P2TRUHXYphMjFkX9oHuxFwYAwbBUZRln6VkYNS22e1wCA3Wdq5gnJKO65/MJ +1FgkRNECgYEA8XTdXea3tzpHBIYffitmy92ZnFn7AF/ByG3beS26Hpn3e+zFKFjX +HFuI3yloKnq+gxEKvzw1xd5LE0iybu2KsPkKN9IR4sAb24BGfUCpcnbf86EZIr6w +6nMMC8bnjyMLr2DQgGqaf+KCPmW68uJdQfhLfOxVqV2Y0+6LSZpuVY8CgYEA0k5K +IbL2JOpe4wADauqmaJV4K9IZ18PSbjB/kroO15AdchvSjnuBnKzVpo+o1mM9tiD8 +NQ3V472kU37Vl26cGfayeOPqNBvlgMR+xq8DD84wqJR26zyKqOPAHoJeCsM2H1Zy +3lKylx3iwAIHsggwTCv1V+eyyAjJmQV9pr+wij0CgYAhMvNY/OcXv2CSY6qi227X +6xE66ThU1dW8LDa4E6x6lqrrlCOLp6N0F7XbEUNsS9I0DTzQDIRMP4NNFHrMO8cN +DED2aeDhr45lAN8wS5rAzU1/nw/oshV7NvRaDjA6gxQROhuQ/JdfrBJCaBRrXup5 +rp0agRhKID9qLGtkZGJ1SwKBgBIa31LrXzlqW+Ta+XBY5x6Vz5SmL8ddel42GqV9 +Ew4HwCE0t/nANDZSwsnmEDj0rjlhuZSvrjw+9Uii4Kx6v0GU9WRm6qC1zho50Fg2 +dOMFczbwIRS7T4yensmBm6r9h0lHXfg50lokHoM9KYBwKAhGuIZq0f9aklB0Io8a +bY5ZAoGBAIk3P+uIsegkmgehFyxHx/GrahUbipOMo6a9njMAUsMRhWoLIs6oUQiC +IJ/eHpon9QH8JgXTphnecvFCe70Puww6RHgE5PAX4sJmSE2CgtrDimh8DTLoFiAw +EFSOHDquhX0fO03YzL9CKx+8lzQ0ISwseI07Uq1LKIyMEEW3O8Ps +-----END RSA PRIVATE KEY----- diff --git a/vagrant/windows-x86_64/vagrant_insecure_key.pub b/vagrant/windows-x86_64/vagrant_insecure_key.pub new file mode 100644 index 0000000..c367e11 --- /dev/null +++ b/vagrant/windows-x86_64/vagrant_insecure_key.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDGW7UeCrgZHPrC0pR4ttio6nUTjWOTFqb6/63+XtjIE4QKOjmACO3RYGaObP8kItpXIRyUkTogT7rwP0AOAtL24wM6GNM8jBQQ4Np6989BWgP5MqQmxX141XDY8dxa//IVqPg2wsJkMMKRJ1Nk7DBSaHVHCrZrw78sJ0Q4R21YyDItkxY7cOmAeXZ5Q6JxJiadBHrIP69GBBsO6kMiPnEEaoOY2Yk6C4kWefC41TP9Q409Z9A2QjwQm0rb2cL+Rcj6y6qL9R/V1UgUreydD8NLEnmSK5c3+7pY+rLHQErTR8yUfEAMEcCjXd0hvFN3CyIT6UWWqnM6R+ncqTPJuXkT tru@imuc.local