Repository: MathewSachin/Captura
Branch: master
Commit: b0cc46780b18
Files: 720
Total size: 1.5 MB
Directory structure:
gitextract_9xzvr3yn/
├── .gitattributes
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ ├── feature_request.md
│ │ └── question.md
│ └── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Inno.iss
├── LICENSE.md
├── README.md
├── SUPPORT.md
├── appveyor.yml
├── build.cake
├── choco/
│ ├── captura.nuspec
│ └── tools/
│ └── chocolateyinstall.ps1
├── crowdin.yml
├── docs/
│ ├── Acknowledgements.md
│ ├── Build.md
│ ├── CI.md
│ ├── Cake.md
│ ├── Changelogs/
│ │ ├── README.md
│ │ ├── v7.0.1.md
│ │ ├── v8.0.0.md
│ │ └── v9.0.0.md
│ ├── Choco.md
│ ├── Cmdline/
│ │ ├── Arg-Source.md
│ │ ├── README.md
│ │ ├── Verb-FFmpeg.md
│ │ ├── Verb-List.md
│ │ ├── Verb-Shot.md
│ │ └── Verb-Start.md
│ ├── Directories.md
│ ├── FAQ.md
│ ├── FFmpeg.md
│ ├── Portable.md
│ ├── Projects.md
│ ├── README.md
│ ├── Screenshots/
│ │ ├── Dark.md
│ │ └── Light.md
│ ├── Setup.md
│ └── System-Requirements.md
├── licenses/
│ ├── Captura, Screna.txt
│ ├── CommandLineParser.txt
│ ├── CroppingAdorner.txt
│ ├── DirectShowLib.txt
│ ├── FFMpeg.txt
│ ├── Inno.txt
│ ├── MUI.Extended.Toolkit.txt
│ ├── MUI.txt
│ ├── Media Foundation .NET.txt
│ ├── MouseKeyHook.txt
│ ├── NAudio.txt
│ ├── Newtonsoft.Json.txt
│ ├── Ooki.Dialogs.txt
│ ├── ReactiveProperty.txt
│ ├── ScreenToGif.txt
│ ├── SharpAvi.txt
│ ├── SharpDX.txt
│ ├── System.Reactive.txt
│ ├── WPFNotifyIcon.txt
│ └── WpfToolkit.txt
├── scripts/
│ ├── apikeys.cake
│ ├── backup.cake
│ ├── choco.cake
│ ├── constants.cake
│ └── version.cake
└── src/
├── Captura/
│ ├── App.xaml
│ ├── App.xaml.cs
│ ├── Captura.csproj
│ ├── Controls/
│ │ ├── CollapsedBar.xaml
│ │ ├── CollapsedBar.xaml.cs
│ │ ├── FontSelector.xaml
│ │ ├── FontSelector.xaml.cs
│ │ ├── HotkeySelector.cs
│ │ ├── ImageOverlaySettingsControl.xaml
│ │ ├── ImageOverlaySettingsControl.xaml.cs
│ │ ├── LayerFrame.xaml
│ │ ├── LayerFrame.xaml.cs
│ │ ├── ModernButton.cs
│ │ ├── ModernPasswordBox.xaml
│ │ ├── ModernPasswordBox.xaml.cs
│ │ ├── ModernToggleButton.cs
│ │ ├── NotificationBalloon.xaml
│ │ ├── NotificationBalloon.xaml.cs
│ │ ├── NotificationStack.xaml
│ │ ├── NotificationStack.xaml.cs
│ │ ├── OutputFolderControl.xaml
│ │ ├── OutputFolderControl.xaml.cs
│ │ ├── PauseButton.xaml
│ │ ├── PauseButton.xaml.cs
│ │ ├── PositionSettingsControl.xaml
│ │ ├── PositionSettingsControl.xaml.cs
│ │ ├── PuncturedRegion.xaml
│ │ ├── PuncturedRegion.xaml.cs
│ │ ├── RecentItem.xaml
│ │ ├── RecentItem.xaml.cs
│ │ ├── ScreenShotBalloon.xaml
│ │ ├── ScreenShotBalloon.xaml.cs
│ │ ├── ScreenShotButton.xaml
│ │ ├── ScreenShotButton.xaml.cs
│ │ ├── StatusBar.xaml
│ │ ├── StatusBar.xaml.cs
│ │ ├── StripedBorder.xaml
│ │ ├── StripedBorder.xaml.cs
│ │ ├── TextOverlaySettingsControl.xaml
│ │ ├── TextOverlaySettingsControl.xaml.cs
│ │ ├── VideoSourceKindList.xaml
│ │ └── VideoSourceKindList.xaml.cs
│ ├── Models/
│ │ ├── AudioPlayer.cs
│ │ ├── CmdOptions.cs
│ │ ├── Dpi.cs
│ │ ├── FFmpegViewsProvider.cs
│ │ ├── HitType.cs
│ │ ├── HotkeyListener.cs
│ │ ├── HotkeySetup.cs
│ │ ├── HotkeyViewActor.cs
│ │ ├── IRemoveRequester.cs
│ │ ├── MainModule.cs
│ │ ├── MainWindowHelper.cs
│ │ ├── MainWindowProvider.cs
│ │ ├── MessageProvider.cs
│ │ ├── PreviewWindowService.cs
│ │ ├── RegionSelectorProvider.cs
│ │ ├── ServiceLocator.cs
│ │ ├── SingleInstanceManager.cs
│ │ ├── SystemInfo.cs
│ │ ├── SystemTray.cs
│ │ └── VideoSourcePicker.cs
│ ├── Pages/
│ │ ├── AboutPage.xaml
│ │ ├── AboutPage.xaml.cs
│ │ ├── AudioPage.xaml
│ │ ├── AudioPage.xaml.cs
│ │ ├── CensorOverlaysPage.xaml
│ │ ├── CensorOverlaysPage.xaml.cs
│ │ ├── CrashLogsPage.xaml
│ │ ├── CrashLogsPage.xaml.cs
│ │ ├── FFmpegCodecsPage.xaml
│ │ ├── FFmpegCodecsPage.xaml.cs
│ │ ├── FFmpegLogsPage.xaml
│ │ ├── FFmpegLogsPage.xaml.cs
│ │ ├── FFmpegPage.xaml
│ │ ├── FFmpegPage.xaml.cs
│ │ ├── FileNameFormatPage.xaml
│ │ ├── FileNameFormatPage.xaml.cs
│ │ ├── HomePage.xaml
│ │ ├── HomePage.xaml.cs
│ │ ├── HotkeysPage.xaml
│ │ ├── HotkeysPage.xaml.cs
│ │ ├── ImageOverlaysPage.xaml
│ │ ├── ImageOverlaysPage.xaml.cs
│ │ ├── InterfacePage.xaml
│ │ ├── InterfacePage.xaml.cs
│ │ ├── KeystrokesPage.xaml
│ │ ├── KeystrokesPage.xaml.cs
│ │ ├── LicensesPage.xaml
│ │ ├── LicensesPage.xaml.cs
│ │ ├── MainPage.xaml
│ │ ├── MainPage.xaml.cs
│ │ ├── MouseOverlayPage.xaml
│ │ ├── MouseOverlayPage.xaml.cs
│ │ ├── OverlayPage.xaml
│ │ ├── OverlayPage.xaml.cs
│ │ ├── ProxyPage.xaml
│ │ ├── ProxyPage.xaml.cs
│ │ ├── RecentPage.xaml
│ │ ├── RecentPage.xaml.cs
│ │ ├── ScreenShotsPage.xaml
│ │ ├── ScreenShotsPage.xaml.cs
│ │ ├── SettingsPage.xaml
│ │ ├── SettingsPage.xaml.cs
│ │ ├── SoundsPage.xaml
│ │ ├── SoundsPage.xaml.cs
│ │ ├── TextOverlaysPage.xaml
│ │ ├── TextOverlaysPage.xaml.cs
│ │ ├── TrayIconPage.xaml
│ │ ├── TrayIconPage.xaml.cs
│ │ ├── VideoPage.xaml
│ │ ├── VideoPage.xaml.cs
│ │ ├── WebcamPage.xaml
│ │ └── WebcamPage.xaml.cs
│ ├── Presentation/
│ │ ├── CroppingAdorner.cs
│ │ ├── OverlayPositionAdorner.cs
│ │ ├── PuncturedRect.cs
│ │ ├── Themes/
│ │ │ ├── Expander.xaml
│ │ │ ├── Generic.xaml
│ │ │ ├── LICENSE.md
│ │ │ ├── ModernButton.xaml
│ │ │ ├── ModernToggleButton.xaml
│ │ │ ├── ModernTogglePill.xaml
│ │ │ ├── RegionPickerMagnifier.xaml
│ │ │ ├── RoundSlider.xaml
│ │ │ └── VirtualizingItemsControl.xaml
│ │ └── WpfExtensions.cs
│ ├── Properties/
│ │ └── AssemblyInfo.cs
│ ├── ValueConverters/
│ │ ├── DrawingToWpfColorConverter.cs
│ │ ├── GetTypeConverter.cs
│ │ ├── InkToolToIconConverter.cs
│ │ ├── IntegralTimeSpanConverter.cs
│ │ ├── IsLessThanConverter.cs
│ │ ├── IsPlayingToButtonStyleConverter.cs
│ │ ├── LanguageConverter.cs
│ │ ├── NegatingConverter.cs
│ │ ├── NotNullConverter.cs
│ │ ├── NotRecordingConverter.cs
│ │ ├── OneWayConverter.cs
│ │ ├── SecondsToTimeSpanConverter.cs
│ │ ├── StateToRecordButtonGeometryConverter.cs
│ │ ├── StateToTaskbarOverlayConverter.cs
│ │ ├── StateToTrayIconSourceConverter.cs
│ │ ├── StaticResourceConverter.cs
│ │ ├── TimeSpanToSecondsConverter.cs
│ │ └── ValueConverters.xaml
│ ├── ViewModels/
│ │ ├── AboutViewModel.cs
│ │ ├── ExceptionViewModel.cs
│ │ ├── Overlays/
│ │ │ ├── CensorOverlayReactor.cs
│ │ │ ├── ImageOverlayReactor.cs
│ │ │ ├── PositionOverlayReactor.cs
│ │ │ ├── TextOverlayReactor.cs
│ │ │ └── WebcamOverlayReactor.cs
│ │ ├── RegionSelectorViewModel.cs
│ │ ├── ScreenPickerViewModel.cs
│ │ └── TrimmerViewModel.cs
│ ├── Windows/
│ │ ├── ErrorWindow.xaml
│ │ ├── ErrorWindow.xaml.cs
│ │ ├── ExceptionWindow.xaml
│ │ ├── ExceptionWindow.xaml.cs
│ │ ├── FFmpegDownloaderWindow.xaml
│ │ ├── FFmpegDownloaderWindow.xaml.cs
│ │ ├── MainWindow.xaml
│ │ ├── MainWindow.xaml.cs
│ │ ├── RegionPickerWindow.xaml
│ │ ├── RegionPickerWindow.xaml.cs
│ │ ├── RegionSelector.xaml
│ │ ├── RegionSelector.xaml.cs
│ │ ├── ScreenPickerWindow.xaml
│ │ ├── ScreenPickerWindow.xaml.cs
│ │ ├── SettingsWindow.xaml
│ │ ├── SettingsWindow.xaml.cs
│ │ ├── TrimmerWindow.xaml
│ │ ├── TrimmerWindow.xaml.cs
│ │ ├── VideoSourcePickerWindow.xaml
│ │ ├── VideoSourcePickerWindow.xaml.cs
│ │ ├── YouTubeUploaderWindow.xaml
│ │ └── YouTubeUploaderWindow.xaml.cs
│ └── app.config
├── Captura.Audio/
│ ├── Captura.Audio.csproj
│ ├── IAudioFileWriter.cs
│ ├── IAudioItem.cs
│ ├── IAudioProvider.cs
│ ├── IAudioSource.cs
│ ├── IAudioWriterItem.cs
│ └── WaveFormat/
│ ├── WaveFormat.cs
│ └── WaveFormatEncoding.cs
├── Captura.Base/
│ ├── Captura.Base.csproj
│ ├── ComparableExtensions.cs
│ ├── DelegateCommand.cs
│ ├── FrameExtensions.cs
│ ├── IImageUploader.cs
│ ├── IOverlay.cs
│ ├── IRecorder.cs
│ ├── IScreen.cs
│ ├── IWindow.cs
│ ├── Images/
│ │ ├── IBitmapFrame.cs
│ │ ├── IBitmapImage.cs
│ │ ├── IBitmapLoader.cs
│ │ ├── IEditableFrame.cs
│ │ ├── IFont.cs
│ │ ├── IFrameWrapper.cs
│ │ ├── IImageProvider.cs
│ │ ├── IImageWriterItem.cs
│ │ ├── IImagingSystem.cs
│ │ ├── INV12Frame.cs
│ │ ├── ImageFormats.cs
│ │ └── RepeatFrame.cs
│ ├── Modifiers.cs
│ ├── Notification/
│ │ ├── INotification.cs
│ │ └── NotificationAction.cs
│ ├── NotifyPropertyChanged.cs
│ ├── Recent/
│ │ ├── IRecentItem.cs
│ │ ├── IRecentList.cs
│ │ └── RecentAction.cs
│ ├── Services/
│ │ ├── Binder.cs
│ │ ├── IAudioPlayer.cs
│ │ ├── IBinder.cs
│ │ ├── IClipboardService.cs
│ │ ├── IDialogService.cs
│ │ ├── IFpsManager.cs
│ │ ├── IIconSet.cs
│ │ ├── IMainWindow.cs
│ │ ├── IMessageProvider.cs
│ │ ├── IModule.cs
│ │ ├── IPlatformServices.cs
│ │ ├── IPreviewWindow.cs
│ │ ├── IRegionProvider.cs
│ │ ├── ISystemTray.cs
│ │ ├── IVideoSourcePicker.cs
│ │ ├── IWebCamProvider.cs
│ │ ├── IWebcamCapture.cs
│ │ ├── IWebcamItem.cs
│ │ └── ServiceProvider.cs
│ ├── Settings/
│ │ ├── Alignment.cs
│ │ ├── AudioSettings.cs
│ │ ├── MouseOverlaySettings.cs
│ │ ├── PositionedOverlaySettings.cs
│ │ ├── PropertyStore.cs
│ │ ├── ProxySettings.cs
│ │ ├── StepsSettings.cs
│ │ ├── TextOverlaySettings.cs
│ │ └── VideoSettings.cs
│ ├── SoundKind.cs
│ ├── SyncContextManager.cs
│ ├── UploadResult.cs
│ ├── Video/
│ │ ├── IVideoConverter.cs
│ │ ├── IVideoFileWriter.cs
│ │ ├── IVideoItem.cs
│ │ ├── IVideoSourceProvider.cs
│ │ ├── IVideoWriterItem.cs
│ │ ├── IVideoWriterProvider.cs
│ │ ├── RecorderMode.cs
│ │ ├── VideoConverterParams.cs
│ │ └── VideoWriterArgs.cs
│ └── WindowClosedException.cs
├── Captura.Console/
│ ├── App.config
│ ├── Captura.Console.csproj
│ ├── CmdOptions/
│ │ ├── CommonCmdOptions.cs
│ │ ├── FFMpegCmdOptions.cs
│ │ ├── ICmdlineVerb.cs
│ │ ├── ListCmdOptions.cs
│ │ ├── ShotCmdOptions.cs
│ │ ├── StartCmdOptions.cs
│ │ ├── UploadCmdOptions.cs
│ │ ├── UploadService.cs
│ │ └── VerbsModule.cs
│ ├── ConsoleLister.cs
│ ├── ConsoleManager.cs
│ ├── FFmpegConsoleManager.cs
│ ├── Program.cs
│ ├── Properties/
│ │ ├── AssemblyInfo.cs
│ │ └── launchSettings.json
│ └── User32.cs
├── Captura.Core/
│ ├── ApiKeys.cs
│ ├── Captura.Core.csproj
│ ├── CoreModule.cs
│ ├── Extensions.cs
│ ├── FpsManager.cs
│ ├── MaterialDesignIcons.cs
│ ├── Models/
│ │ ├── AroundMouseItem.cs
│ │ ├── AroundMouseSourceProvider.cs
│ │ ├── Discard/
│ │ │ ├── DiscardWriter.cs
│ │ │ ├── DiscardWriterItem.cs
│ │ │ └── DiscardWriterProvider.cs
│ │ ├── EditorWriter.cs
│ │ ├── ImageWriterItems/
│ │ │ ├── ClipboardWriter.cs
│ │ │ ├── DiskWriter.cs
│ │ │ └── ImageUploadWriter.cs
│ │ ├── NoVideoItem.cs
│ │ ├── NoVideoSourceProvider.cs
│ │ ├── Notifications/
│ │ │ ├── FileSaveNotification.cs
│ │ │ ├── ImageUploadNotification.cs
│ │ │ └── TextNotification.cs
│ │ ├── Recents/
│ │ │ ├── FileRecentItem.cs
│ │ │ ├── FileRecentSerializer.cs
│ │ │ ├── IRecentItemSerializer.cs
│ │ │ ├── RecentFileType.cs
│ │ │ ├── RecentListRepository.cs
│ │ │ ├── UploadRecentItem.cs
│ │ │ └── UploadRecentSerializer.cs
│ │ ├── RecorderState.cs
│ │ ├── StepWriters/
│ │ │ ├── ImageFolderWriter.cs
│ │ │ ├── ImageFolderWriterItem.cs
│ │ │ └── StepsVideoWriterItem.cs
│ │ ├── UpdateCheckers/
│ │ │ ├── DevUpdateChecker.cs
│ │ │ ├── IUpdateChecker.cs
│ │ │ └── UpdateChecker.cs
│ │ ├── WebcamSourceProvider.cs
│ │ └── WebcamVideoItem.cs
│ ├── NoWebcamItem.cs
│ ├── Settings/
│ │ ├── Models/
│ │ │ ├── AroundMouseSettings.cs
│ │ │ ├── ScreenShotSettings.cs
│ │ │ ├── SoundSettings.cs
│ │ │ ├── TraySettings.cs
│ │ │ ├── VisualSettings.cs
│ │ │ └── WebcamOverlaySettings.cs
│ │ └── Settings.cs
│ ├── ViewModels/
│ │ ├── RecordingModel.cs
│ │ ├── RecordingModelParams.cs
│ │ ├── ScreenShotModel.cs
│ │ ├── TimerModel.cs
│ │ ├── ViewModelBase.cs
│ │ └── WebcamModel.cs
│ ├── WebcamImageProvider.cs
│ ├── WebcamOverlay.cs
│ └── WithPreviewWriter.cs
├── Captura.FFmpeg/
│ ├── ArgsBuilder/
│ │ ├── FFmpegArgs.cs
│ │ ├── FFmpegArgsBuilder.cs
│ │ ├── FFmpegInputArgs.cs
│ │ └── FFmpegOutputArgs.cs
│ ├── Audio/
│ │ ├── FFmpegAudioArgsProvider.cs
│ │ ├── FFmpegAudioItem.cs
│ │ └── FFmpegAudioWriter.cs
│ ├── Captura.FFmpeg.csproj
│ ├── DownloadFFmpeg.cs
│ ├── FFMpegLogItem.cs
│ ├── FFmpegDownloaderProgress.cs
│ ├── FFmpegDownloaderState.cs
│ ├── FFmpegException.cs
│ ├── FFmpegModule.cs
│ ├── FFmpegNotFoundException.cs
│ ├── FFmpegService.cs
│ ├── FFmpegTrimmer.cs
│ ├── FFmpgDownloadModel.cs
│ ├── IFFmpegLogEntry.cs
│ ├── IFFmpegLogRepository.cs
│ ├── IFFmpegViewsProvider.cs
│ ├── Settings/
│ │ ├── FFMpegSettings.cs
│ │ ├── FFmpegCodecSettings.cs
│ │ └── X264Settings.cs
│ └── Video/
│ ├── Codecs/
│ │ ├── CustomFFmpegVideoCodec.cs
│ │ ├── CustomStreamingVideoCodec.cs
│ │ ├── FFmpegVideoCodec.cs
│ │ ├── NvencVideoCodec.cs
│ │ ├── QsvHevcVideoCodec.cs
│ │ ├── StreamingVideoCodec.cs
│ │ ├── TempFileVideoCodec.cs
│ │ ├── TwitchVideoCodec.cs
│ │ ├── Vp8VideoCodec.cs
│ │ ├── Vp9VideoCodec.cs
│ │ ├── X264VideoCodec.cs
│ │ ├── XvidVideoCodec.cs
│ │ └── YouTubeLiveVideoCodec.cs
│ ├── FFmpegGifConverter.cs
│ ├── FFmpegReplayWriter.cs
│ ├── FFmpegVideoConverter.cs
│ ├── FFmpegVideoWriter.cs
│ ├── FFmpegVideoWriterArgs.cs
│ ├── FFmpegWriterProvider.cs
│ └── StreamingWriterProvider.cs
├── Captura.Fakes/
│ ├── Captura.Fakes.csproj
│ ├── FakeAudioPlayer.cs
│ ├── FakeFFmpegLogRepository.cs
│ ├── FakeFFmpegViewsProvider.cs
│ ├── FakeMessageProvider.cs
│ ├── FakePreviewWindow.cs
│ ├── FakeRegionProvider.cs
│ ├── FakeSystemTray.cs
│ ├── FakeVideoSourcePicker.cs
│ ├── FakeWindowProvider.cs
│ └── FakesModule.cs
├── Captura.Hotkeys/
│ ├── Captura.Hotkeys.csproj
│ ├── HotKey.cs
│ ├── HotKeyManager.cs
│ ├── HotkeyModel.cs
│ ├── IHotkeyActor.cs
│ ├── IHotkeyListener.cs
│ ├── Kernel32.cs
│ ├── Service.cs
│ ├── ServiceName.cs
│ └── User32.cs
├── Captura.Imgur/
│ ├── Captura.Imgur.csproj
│ ├── IImgurApiKeys.cs
│ ├── ImgurData.cs
│ ├── ImgurRefreshTokenResponse.cs
│ ├── ImgurResponse.cs
│ ├── ImgurSettings.cs
│ ├── ImgurUploadResponse.cs
│ └── ImgurUploader.cs
├── Captura.Loc/
│ ├── Captura.Loc.csproj
│ ├── ILocalizationProvider.cs
│ ├── LanguageFields.cs
│ ├── LanguageManager.cs
│ ├── Languages/
│ │ ├── ar.json
│ │ ├── be.json
│ │ ├── ca.json
│ │ ├── cs.json
│ │ ├── da.json
│ │ ├── de.json
│ │ ├── el.json
│ │ ├── en.json
│ │ ├── es.json
│ │ ├── fi.json
│ │ ├── fr.json
│ │ ├── he.json
│ │ ├── hi.json
│ │ ├── id.json
│ │ ├── is.json
│ │ ├── it.json
│ │ ├── ja.json
│ │ ├── kab.json
│ │ ├── ko.json
│ │ ├── ml.json
│ │ ├── nl.json
│ │ ├── no.json
│ │ ├── pl.json
│ │ ├── pt-BR.json
│ │ ├── pt.json
│ │ ├── ro.json
│ │ ├── ru.json
│ │ ├── sl.json
│ │ ├── sv.json
│ │ ├── th.json
│ │ ├── tr.json
│ │ ├── uk.json
│ │ ├── vi.json
│ │ ├── zh-CN.json
│ │ └── zh-TW.json
│ ├── ObjectLocalizer.cs
│ └── TextLocalizer.cs
├── Captura.MouseKeyHook/
│ ├── Captura.MouseKeyHook.csproj
│ ├── IMouseKeyHook.cs
│ ├── KeyOverlay.cs
│ ├── KeyRecord/
│ │ ├── DummyKeyRecord.cs
│ │ ├── IKeyRecord.cs
│ │ ├── KeyRecord.cs
│ │ ├── KeyRecords.cs
│ │ └── RepeatKeyRecord.cs
│ ├── KeymapViewModel.cs
│ ├── Models/
│ │ ├── Keymap.cs
│ │ ├── KeystrokesSettings.cs
│ │ ├── MappingGroup.cs
│ │ ├── ModifierStates.cs
│ │ └── MouseClickSettings.cs
│ ├── MouseClickOverlay.cs
│ ├── MouseKeyHook.cs
│ ├── MouseKeyOverlay.cs
│ ├── ScrollOverlay.cs
│ ├── Steps/
│ │ ├── IRecordStep.cs
│ │ ├── KeyModifiedStep.cs
│ │ ├── KeyStep.cs
│ │ ├── MouseClickStep.cs
│ │ ├── MouseDragBeginStep.cs
│ │ ├── MouseDragStep.cs
│ │ ├── ScrollStep.cs
│ │ └── StepsRecorder.cs
│ └── keymaps/
│ ├── de.json
│ ├── en-IN.json
│ ├── en.json
│ ├── ml.json
│ ├── schema.json
│ └── tr.json
├── Captura.NAudio/
│ ├── Captura.NAudio.csproj
│ ├── MixedAudioProvider.cs
│ ├── NAudioItem.cs
│ ├── NAudioNotificationClient.cs
│ ├── NAudioProvider.cs
│ ├── NAudioSource.cs
│ ├── WasapiCaptureProvider.cs
│ ├── WasapiLoopbackCaptureProvider.cs
│ └── WaveFormatExtensions.cs
├── Captura.SharpAvi/
│ ├── AviCodec.cs
│ ├── AviWriter.cs
│ ├── Captura.SharpAvi.csproj
│ ├── IAudioProviderAdapter.cs
│ ├── SharpAviItem.cs
│ └── SharpAviWriterProvider.cs
├── Captura.ViewCore/
│ ├── Captura.ViewCore.csproj
│ ├── FFmpegLog.cs
│ ├── FileContentItem.cs
│ ├── FileNameFormatGroup.cs
│ ├── FileNameFormatItem.cs
│ ├── HotkeyActor.cs
│ ├── RememberByName.cs
│ ├── SoundsViewModelItem.cs
│ ├── ViewCoreModule.cs
│ └── ViewModels/
│ ├── AudioSourceViewModel.cs
│ ├── CensorOverlaysViewModel.cs
│ ├── CrashLogsViewModel.cs
│ ├── CustomImageOverlaysViewModel.cs
│ ├── CustomOverlaysViewModel.cs
│ ├── FFmpegCodecsViewModel.cs
│ ├── FFmpegLogViewModel.cs
│ ├── FFmpgDownloadViewModel.cs
│ ├── FileNameFormatViewModel.cs
│ ├── HotkeysViewModel.cs
│ ├── LicensesViewModel.cs
│ ├── MainViewModel.cs
│ ├── OverlayListViewModel.cs
│ ├── ProxySettingsViewModel.cs
│ ├── RecentViewModel.cs
│ ├── RecordingViewModel.cs
│ ├── ScreenShotViewModel.cs
│ ├── SoundsViewModel.cs
│ ├── UpdateCheckerViewModel.cs
│ ├── VideoSourcesViewModel.cs
│ ├── VideoWritersViewModel.cs
│ ├── ViewConditionsModel.cs
│ └── YouTubeUploaderViewModel.cs
├── Captura.Windows/
│ ├── Captura.Windows.csproj
│ ├── Capture/
│ │ ├── DxgiTargetDeviceContext.cs
│ │ ├── GdiTargetDeviceContext.cs
│ │ ├── ITargetDeviceContext.cs
│ │ ├── MouseCursor.cs
│ │ ├── RegionProvider.cs
│ │ ├── ScreenShot.cs
│ │ ├── WindowProvider.cs
│ │ └── WindowScreenShotBackdrop.cs
│ ├── ClipboardService.cs
│ ├── DesktopDuplication/
│ │ ├── AcquireResult.cs
│ │ ├── DeskDuplFullScreenImageProvider.cs
│ │ ├── DeskDuplImageProvider.cs
│ │ ├── DeskDuplOutputEntry.cs
│ │ ├── DesktopDuplicator.cs
│ │ ├── DuplCapture.cs
│ │ ├── FrameGrabber.cs
│ │ ├── FullScreenDesktopDuplicator.cs
│ │ └── MousePointer/
│ │ ├── ColorPointerShape.cs
│ │ ├── DXMousePointer.cs
│ │ ├── IPointerShape.cs
│ │ ├── MaskedColorPointerShape.cs
│ │ ├── MaskedPointerShape.cs
│ │ └── MonochromePointerShape.cs
│ ├── DialogService.cs
│ ├── Imaging/
│ │ ├── Direct2D/
│ │ │ ├── D3D9PreviewAssister.cs
│ │ │ ├── Direct2DEditor.cs
│ │ │ ├── Direct2DEditorSession.cs
│ │ │ ├── Direct2DFont.cs
│ │ │ ├── Direct2DImage.cs
│ │ │ └── Texture2DFrame.cs
│ │ └── Drawing/
│ │ ├── DrawingFont.cs
│ │ ├── DrawingFrame.cs
│ │ ├── DrawingImage.cs
│ │ ├── DrawingImagingSystem.cs
│ │ ├── GraphicsBitmapLoader.cs
│ │ ├── GraphicsEditor.cs
│ │ └── GraphicsExtensions.cs
│ ├── MediaFoundation/
│ │ ├── MfAudioItem.cs
│ │ ├── MfAudioWriter.cs
│ │ ├── MfColorConverter.cs
│ │ ├── MfItem.cs
│ │ ├── MfManager.cs
│ │ ├── MfWriter.cs
│ │ ├── MfWriterProvider.cs
│ │ ├── PackedLong.cs
│ │ └── RateControlMode.cs
│ ├── Native/
│ │ ├── DwmApi.cs
│ │ ├── EnumWindowsProc.cs
│ │ ├── Enums/
│ │ │ ├── DrawIconExFlags.cs
│ │ │ ├── FileOperationFlags.cs
│ │ │ ├── FileOperationType.cs
│ │ │ ├── GetWindowEnum.cs
│ │ │ ├── GetWindowLongValue.cs
│ │ │ ├── SetWindowPositionFlags.cs
│ │ │ └── WindowStyles.cs
│ │ ├── Gdi32.cs
│ │ ├── Kernel32.cs
│ │ ├── NativeExtensions.cs
│ │ ├── Shell32.cs
│ │ ├── Structs/
│ │ │ ├── CursorInfo.cs
│ │ │ ├── IconInfo.cs
│ │ │ ├── RECT.cs
│ │ │ └── ShFileOpStruct.cs
│ │ ├── UnsafeBitmap.cs
│ │ └── User32.cs
│ ├── ScreenWrapper.cs
│ ├── Webcam/
│ │ ├── CaptureWebcam.cs
│ │ ├── DummyForm.cs
│ │ ├── Filter.cs
│ │ ├── GraphState.cs
│ │ ├── README.md
│ │ ├── WebcamCapture.cs
│ │ ├── WebcamItem.cs
│ │ └── WebcamProvider.cs
│ ├── Window.cs
│ ├── WindowsModule.cs
│ ├── WindowsPlatformServices.cs
│ └── WindowsSettings.cs
├── Captura.YouTube/
│ ├── Captura.YouTube.csproj
│ ├── IYouTubeApiKeys.cs
│ ├── YouTubePrivacyStatus.cs
│ ├── YouTubeUploadRequest.cs
│ └── YouTubeUploader.cs
├── Captura.sln
├── Directory.Build.props
├── PostBuild.targets
├── Screna/
│ ├── AudioFileWriter.cs
│ ├── AudioRecorder.cs
│ ├── Extensions.cs
│ ├── ImageProviders/
│ │ ├── AroundMouseImageProvider.cs
│ │ └── OverlayedImageProvider.cs
│ ├── MultiDisposeFrame.cs
│ ├── MultiRecorder.cs
│ ├── Overlays/
│ │ ├── CensorOverlay.cs
│ │ ├── CustomImageOverlay.cs
│ │ ├── CustomOverlay.cs
│ │ ├── ElapsedOverlay.cs
│ │ ├── ImageOverlay.cs
│ │ ├── MousePointerOverlay.cs
│ │ ├── Settings/
│ │ │ ├── CensorOverlaySettings.cs
│ │ │ ├── CustomImageOverlaySettings.cs
│ │ │ ├── CustomOverlaySettings.cs
│ │ │ └── ImageOverlaySettings.cs
│ │ └── TextOverlay.cs
│ ├── Recorder.cs
│ ├── RegionItem.cs
│ ├── ScreenShot.cs
│ ├── Screna.csproj
│ ├── Timing.cs
│ ├── VideoItems/
│ │ ├── FullScreenItem.cs
│ │ ├── ScreenItem.cs
│ │ ├── WaveItem.cs
│ │ └── WindowItem.cs
│ └── VideoSourceProviders/
│ ├── FullScreenSourceProvider.cs
│ ├── RegionSourceProvider.cs
│ ├── ScreenSourceProvider.cs
│ ├── VideoSourceProviderBase.cs
│ └── WindowSourceProvider.cs
└── Tests/
├── AudioFileWriterTests.cs
├── ConsoleStartTests.cs
├── Fixtures/
│ ├── AppRunnerFixture.cs
│ ├── MoqFixture.cs
│ ├── TestCollection.cs
│ └── TestManagerFixture.cs
├── ImageProviderTests.cs
├── Models/
│ └── FakePropertyStore.cs
├── PropertyStoreTests.cs
├── RecorderTests.cs
├── ScreenShotTests.cs
├── Tests.csproj
└── WindowTests.cs
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
# Auto detect text files and perform LF normalization
* text=auto
*.cs diff=csharp
================================================
FILE: .github/FUNDING.yml
================================================
github: [MathewSachin]
custom: https://mathewsachin.github.io/Captura/donate
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. Windows 8.1]
- RAM: [e.g. 4GB]
- CPU: [e.g. i5 2GHz]
- Graphic Card:
- Captura Version: [e.g. v8.0.0]
**Additional context**
Add any other context about the problem here like FFmpeg Log or error messages.
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
================================================
FILE: .github/ISSUE_TEMPLATE/question.md
================================================
---
name: Question
about: For asking questions or discussions
title: ''
labels: question
assignees: ''
---
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
Please don't send pull-requests for translation. Use the [Crowdin](https://crowdin.com/project/captura) project instead.
================================================
FILE: .gitignore
================================================
# Build Output
bin/
obj/
# NuGet
*.nupkg
packages/
# Visual Studio
.vs/
.vscode/
publish/
TestResults/
*.suo
*.user
# Cake
/tools
/temp/
/dist/
*.pfx
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
### Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
### Our Standards
Examples of behavior that contributes to creating a positive environment include:
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or electronic address, without explicit permission
- Other conduct which could reasonably be considered inappropriate in a professional setting
### Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
### Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
### Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at `mathew DOT sachin DOT git AT outlook DOT com`. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
### Attribution
This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.4, available at http://contributor-covenant.org/version/1/4
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing
### Star :star:
The easiest way to show your support is by starring the repository
### Reporting Bugs
- Check the [FAQ](https://mathewsachin.github.io/Captura/faq).
- Check if an issue has already been reported. If so, comment on it and provide further info if any.
- Follow the `Bug Report` issue template and provide relevant information.
### Get in touch
You can chat with us on [Gitter](https://gitter.im/MathewSachin/Captura).
### Contributing Code
- Make sure to follow the code style followed throughout the project.
- If you add a new dependency, make sure it is compatible with MIT license.
*e.g. GPL is not compatible with MIT license.*
Also, add the license file to the **licenses** folder.
- If you want to add a new feature, discuss it first by opening an issue on GitHub whether it aligns with the goals of the project.
- If possible, write tests.
### Translation
Don't send pull-requests for translation.
Use [Crowdin](https://crowdin.com/project/captura) instead.
If you want to add a new language, open a discussion on the Crowdin project or a new issue here on GitHub to let me know.
### Testing
A lot of help is needed in this area.
Writing tests or just manually testing and reporting or fixing the issues found would be a great help.
### Website
The website in written in Angular and resides in a [separate repository](https://github.com/MathewSachin/ng-captura).
### Donation :heavy_dollar_sign:
You can donate on [PayPal](https://www.paypal.me/MathewSachin).
You can see the list of donations on the [website](https://mathewsachin.github.io/Captura/donate).
### Spread the word
Recommend to others, write reviews, etc.
================================================
FILE: Inno.iss
================================================
; Override version before compiling
;#define MyAppVersion "6.0.0"
#define MyAppName "Captura"
#define MyAppPublisher "Mathew Sachin"
#define MyAppURL "https://MathewSachin.github.io/Captura"
#define MyAppExeName "captura.exe"
[Setup]
AppId={{C1670C5E-5042-4300-9491-6BFFF963823F}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
AppVerName={#MyAppName} v{#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={pf}\{#MyAppName}
DisableProgramGroupPage=yes
OutputBaseFilename=Captura-Setup
Compression=lzma
SolidCompression=yes
SetupIconFile=src/Captura/Images/Captura.ico
OutputDir=temp
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
Name: "danish"; MessagesFile: "compiler:Languages\Danish.isl"
Name: "dutch"; MessagesFile: "compiler:Languages\Dutch.isl"
Name: "finnish"; MessagesFile: "compiler:Languages\Finnish.isl"
Name: "french"; MessagesFile: "compiler:Languages\French.isl"
Name: "german"; MessagesFile: "compiler:Languages\German.isl"
Name: "hebrew"; MessagesFile: "compiler:Languages\Hebrew.isl"
Name: "italian"; MessagesFile: "compiler:Languages\Italian.isl"
Name: "norwegian"; MessagesFile: "compiler:Languages\Norwegian.isl"
Name: "polish"; MessagesFile: "compiler:Languages\Polish.isl"
Name: "portuguese"; MessagesFile: "compiler:Languages\Portuguese.isl"
Name: "russian"; MessagesFile: "compiler:Languages\Russian.isl"
Name: "spanish"; MessagesFile: "compiler:Languages\Spanish.isl"
Name: "ukrainian"; MessagesFile: "compiler:Languages\Ukrainian.isl"
[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
; Remove Assemblies from previous installation to prevent conflicts
[InstallDelete]
Type: files; Name: "{app}\lib\*.dll"
[Files]
Source: "dist\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
[Icons]
Name: "{commonprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Name: "{commondesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
[Run]
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
================================================
FILE: LICENSE.md
================================================
MIT License
Copyright (c) 2020 Mathew Sachin
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.
================================================
FILE: README.md
================================================

[](https://ci.appveyor.com/project/MathewSachin/Captura)
[](LICENSE.md)
[](https://gitter.im/MathewSachin/Captura)
[](https://mathewsachin.github.io/Captura/download)
[](https://mathewsachin.github.io/Captura/donate)
[](https://crowdin.com/project/captura)
© [Copyright 2019](LICENSE.md) Mathew Sachin
:link:
Capture Screen, WebCam, Audio, Cursor, Mouse Clicks and Keystrokes.
## Features
- Take ScreenShots
- Capture ScreenCasts (Avi/Gif/Mp4)
- Capture with/without Mouse Cursor
- Capture Specific Regions, Screens or Windows
- Capture Mouse Clicks or Keystrokes
- Mix Audio recorded from Microphone and Speaker Output
- Capture from WebCam.
- Can be used from [Command-line](https://mathewsachin.github.io/Captura/cmdline) (*BETA*).
- Available in [multiple languages](https://mathewsachin.github.io/Captura/translation)
- Configurable [Hotkeys](https://mathewsachin.github.io/Captura/hotkeys)
## Installation
[latest]: https://github.com/MathewSachin/Captura/releases/latest
Portable and Setup builds for the latest release can be downloaded from [here][latest].
### Chocolatey
```powershell
choco install captura -y
```
### Dev Builds
See the [Continuous Integration page](docs/CI.md).
## Docs
[Build Notes](docs/Build.md) | [System Requirements](docs/System-Requirements.md) | [Contributing](CONTRIBUTING.md)
[ScreenShots](docs/Screenshots) | [Command-line](docs/Cmdline/README.md) | [Hotkeys](https://mathewsachin.github.io/Captura/hotkeys)
[FAQ](docs/FAQ.md) | [Code of Conduct](CODE_OF_CONDUCT.md) | [Changelog](docs/Changelogs/README.md)
[Continuous Integration](docs/CI.md) | [FFmpeg](docs/FFmpeg.md)
## License
[MIT License](LICENSE.md)
Check [here](licenses/) for licenses of dependencies.
================================================
FILE: SUPPORT.md
================================================
# Support
## GitHub Issues
Use [GitHub issues](https://github.com/MathewSachin/Captura/issues) for Bug Reports, Feature Requests and Questions.
## Gitter
[](https://gitter.im/MathewSachin/Captura)
## Mail
Try to use GitHub Issues instead of mailing, it allows us to track your Feature Requests and Bug Reports better.
Use mail only in cases where personal information is involved.
Please specify what you want help with in the mail itself instead of asking for other contact information like phone number or social account details.
Avoid sending mails regarding selling Captura or making a product similar to Captura as those won't be entertained.
`mathew DOT sachin DOT git AT outlook DOT com`
================================================
FILE: appveyor.yml
================================================
version: 0.0.{build}
branches:
except:
- l10n_master # Crowdin
- gh-pages # Website
image: Visual Studio 2019
configuration:
- Debug
- Release
# Used in build.cake
environment:
imgur_client_id:
secure: Y/2WUSisk7oLSQNY1YzUxw==
yt_client_id:
secure: CPwUWOhTyZ2IiE+lvVilTKQz1h8sbn7FmkTJvtTlY6mJf2pDlfDhFmhDk1i47M9OPnD4y8a8BlB6yfkpVt/0U/PODAXPzFR0QuOWIfJvltc=
yt_client_secret:
secure: fsl/BRldjlmEJCroWa0iqw5tAxbGx84BbHT3BpzaeX4=
choco_key:
secure: h5jOVeiDmjLnF3EotkOhBFJharX/9TWx6OWykiXn30pWSIfVjSvAaCJM1Y48zjXr
git_key:
secure: eRA1NCG/iXaLTQaBbe2X5Tpje+2Hvk3WOPEP3ZLPFMqW7ZS0ZMTb4UXpw7w59pbc
install:
- dotnet tool install -g Cake.Tool --version 0.32.1
- choco install innosetup -y --no-progress
# Build using Cake
build_script:
- ps: >-
$tag = $env:APPVEYOR_REPO_TAG_NAME
if ($tag -ne $null)
{
$env:prerelease = $tag.Contains("-")
if ($tag -match "^build-number-")
{
Write-Host "Build number tag is skipped"
Exit-AppveyorBuild
}
else
{
dotnet-cake --target=CI --configuration=$env:CONFIGURATION --build_version=$tag
}
}
else
{
$version = "v0.0." + $env:APPVEYOR_BUILD_NUMBER
dotnet-cake --target=CI --configuration=$env:CONFIGURATION --build_version=$version
}
test: off
artifacts:
- path: temp/Captura-Portable.zip
name: Portable
- path: temp/Captura-Setup.exe
name: Setup
- path: temp/captura.*.nupkg
name: Chocolatey
deploy:
- provider: GitHub
tag: $(APPVEYOR_REPO_TAG_NAME)
release: Captura $(APPVEYOR_REPO_TAG_NAME)
prerelease: $(prerelease)
description: "[Changelog](https://github.com/MathewSachin/Captura/tree/master/docs/Changelogs)"
auth_token: $(git_key)
artifact: Portable, Setup
on:
configuration: Release
appveyor_repo_tag: true
- provider: NuGet
server: https://chocolatey.org
api_key: $(choco_key)
artifact: Chocolatey
on:
configuration: Release
appveyor_repo_tag: true
================================================
FILE: build.cake
================================================
#tool nuget:?package=xunit.runner.console&version=2.4.1
#l "scripts/backup.cake"
#l "scripts/constants.cake"
#l "scripts/choco.cake"
#l "scripts/apikeys.cake"
#l "scripts/version.cake"
using System.Collections.Generic;
#region Fields
readonly var target = Argument("target", "Default");
readonly var configuration = Argument("configuration", Release);
#endregion
#region Functions
void PopulateOutput()
{
// Copy License files
CopyDirectory(licensesFolder, distFolder + Directory("licenses"));
var consoleBinFolder = sourceFolder + Directory("Captura.Console/bin") + Directory(configuration);
var uiBinFolder = sourceFolder + Directory("Captura/bin") + Directory(configuration);
// Copy Languages
CopyDirectory(uiBinFolder + Directory("Languages"), distFolder + Directory("languages"));
// Copy executables and config files
CopyFiles(consoleBinFolder.Path + "/*.exe*", distFolder);
CopyFiles(uiBinFolder.Path + "/*.exe*", distFolder);
// Copy Keymap files
CopyDirectory(uiBinFolder + Directory("keymaps"), distFolder + Directory("keymaps"));
// For Debug builds
if (configuration != Release)
{
// Assemblies, Symbol Files and XML Documentation
foreach (var extension in new [] { ".dll", ".pdb", ".xml" })
{
CopyFiles(consoleBinFolder.Path + "/*" + extension, distFolder);
CopyFiles(uiBinFolder.Path + "/*" + extension, distFolder);
}
}
else
{
CopyDirectory(consoleBinFolder + Directory("lib"), distFolder + Directory("lib"));
CopyDirectory(uiBinFolder + Directory("lib"), distFolder + Directory("lib"));
}
}
void PackPortable()
{
// Portable build directories
var settingsDir = distFolder + Directory("Settings");
var codecsDir = distFolder + Directory("Codecs");
CreateDirectory(settingsDir);
CreateDirectory(codecsDir);
Zip(distFolder, PortablePath);
var dirDeleteSettings = new DeleteDirectorySettings {
Recursive = true,
Force = true
};
DeleteDirectory(settingsDir, dirDeleteSettings);
DeleteDirectory(codecsDir, dirDeleteSettings);
}
#endregion
#region Setup / Teardown
Setup(context =>
{
EnsureDirectoryExists(tempFolder);
EnsureDirectoryExists(distFolder);
HandleVersion();
if (configuration == Release)
{
EmbedApiKeys();
}
});
Teardown(context =>
{
RestoreBackups();
});
#endregion
#region Tasks
var buildTask = Task("Build")
.Does(() =>
{
MSBuild(slnPath, settings =>
{
settings.SetConfiguration(configuration)
.SetVerbosity(Verbosity.Minimal)
.WithTarget("Rebuild")
.WithRestore()
.UseToolVersion(MSBuildToolVersion.VS2019);
});
});
var cleanOutputTask = Task("Clean-Output").Does(() => CleanDirectory(distFolder));
var populateOutputTask = Task("Populate-Output")
.IsDependentOn(cleanOutputTask)
.IsDependentOn(buildTask)
.Does(() => PopulateOutput());
var packPortableTask = Task("Pack-Portable")
.IsDependentOn(populateOutputTask)
.Does(() => PackPortable());
var packSetupTask = Task("Pack-Setup")
.IsDependentOn(populateOutputTask)
.Does(() =>
{
const string InnoScriptPath = "Inno.iss";
InnoSetup(InnoScriptPath, new InnoSetupSettings
{
QuietMode = InnoSetupQuietMode.Quiet,
ArgumentCustomization = Args => Args.Append($"/DMyAppVersion={version}")
});
});
var packChocoTask = Task("Pack-Choco")
.IsDependentOn(packPortableTask)
.Does(() => PackChoco());
var testTask = Task("Test")
.IsDependentOn(buildTask)
.Does(() => XUnit2(sourceFolder + File($"Tests/bin/{configuration}/**/Captura.Tests.dll")));
var defaultTask = Task("Default")
.IsDependentOn(packPortableTask)
.IsDependentOn(packSetupTask)
.IsDependentOn(packChocoTask);
#endregion
Task("CI")
.IsDependentOn(testTask)
.IsDependentOn(packPortableTask)
.IsDependentOn(packSetupTask)
.IsDependentOn(packChocoTask)
.Does(() => { });
// Start
RunTarget(target);
================================================
FILE: choco/captura.nuspec
================================================
captura
8.0.0
Captura
Mathew Sachin
https://MathewSachin.github.io/Captura
https://github.com/MathewSachin/Captura
https://raw.githubusercontent.com/MathewSachin/Captura/master/src/Captura/Images/Logo.png
https://github.com/MathewSachin/Captura/blob/master/LICENSE.md
https://github.com/MathewSachin/Captura/tree/master/choco
https://github.com/MathewSachin/Captura/issues
https://MathewSachin.github.io/Captura
https://gitter.im/MathewSachin/Captura
(c) 2018 Mathew Sachin
captura screen capture recording loopback screenshot screencast
Capture Screen/Audio/Cursor/Clicks/Keystrokes
Captura is an application to capture Screen/Audio/Cursor/Clicks/Keystrokes.
[Changelog](https://MathewSachin.github.io/Captura/changelog)
================================================
FILE: choco/tools/chocolateyinstall.ps1
================================================
$params = @{
'PackageName' = 'Captura';
'Url' = "https://github.com/MathewSachin/Captura/releases/download/$tag/Captura-Portable.zip";
'UnzipLocation' = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)";
'Checksum' = $checksum;
'ChecksumType' = 'sha256';
};
Install-ChocolateyZipPackage @params
================================================
FILE: crowdin.yml
================================================
files:
- source: /src/Captura.Loc/Languages/en.json
translation: /src/Captura.Loc/Languages/%two_letters_code%.json
languages_mapping:
two_letters_code:
zh-CN: zh-CN
zh-TW: zh-TW
pt-BR: pt-BR
================================================
FILE: docs/Acknowledgements.md
================================================
# Acknowledgements
## Tools and Services
### Resharper
Kudos to Jetbrains for providing open-source license for [Resharper Ultimate](https://www.jetbrains.com/resharper/).
Resharper makes development much intutive and easier. You should really check it out.
If you become a regular contributor to Captura, you may request for the Resharper license.
### Crowdin
Great thanks to [Crowdin](https://crowdin.com/) for providing open-source license.
The translation for Captura is done on Crowdin.
Also, thanks to everyone who contributed the translations.
### AppVeyor
We use [AppVeyor](https://www.appveyor.com/) for Continuous Integration. The build and release processes run there.
It's free for open-source projects and especially good for .NET projects.
### GitHub
I can't thank [GitHub](https://github.com/) enough for being an open platform for developers.
The source code and the website are both hosted here.
### Cake Build
[Cake](https://cakebuild.net/) allows us to write our build scripts in C# instead of using other scripting languages.
### Visual Studio Community
[Visual Studio IDE](https://visualstudio.microsoft.com/) for free for open-source and small organisations.
### Visual Studio Code
[Visual Studio Code](https://code.visualstudio.com) is an open-source text editor used to build the website and write the documentation.
### Inno Setup
Setup files for Captura are built using [Inno Setup](http://www.jrsoftware.org/isinfo.php).
## Libraries
### CommandLineParser
[GitHub](https://github.com/commandlineparser/commandline/) -
[MIT License](https://github.com/commandlineparser/commandline/blob/master/License.md)
```
NuGet Install CommandLineParser
```
### CroppingAdorner
[CodeProject Article](https://www.codeproject.com/Articles/23158/A-Photoshop-like-Cropping-Adorner-for-WPF) -
[Code Project Open License (CPOL)](https://www.codeproject.com/info/cpol10.aspx)
### DirectShowLib
[SourceForge](http://directshownet.sourceforge.net/)
### FFmpeg
[Website](https://ffmpeg.org/)
### MouseKeyHook
[GitHub](https://github.com/gmamaladze/globalmousekeyhook) -
[MIT License](https://github.com/gmamaladze/globalmousekeyhook/blob/vNext/LICENSE.txt)
```
nuget install MouseKeyHook
```
### MUI.Extended.Toolkit
[GitHub](https://github.com/samoatesgames/mui.extended.toolkit) -
[MIT License](https://github.com/samoatesgames/mui.extended.toolkit/blob/master/LICENSE)
### MUI
[GitHub](https://github.com/firstfloorsoftware/mui) -
[MS-PL](https://github.com/firstfloorsoftware/mui/blob/master/LICENSE.md)
### NAudio
[GitHub](https://github.com/naudio/NAudio) -
[MS-PL](https://github.com/naudio/NAudio/blob/master/license.txt)
### Newtonsoft.Json
[Website](https://www.newtonsoft.com/json) -
[GitHub](https://github.com/JamesNK/Newtonsoft.Json) -
[MIT License](https://github.com/JamesNK/Newtonsoft.Json/blob/master/LICENSE.md)
### Ooki.Dialogs
[Website](http://www.ookii.org/software/dialogs/)
### ReactiveProperty
[GitHub](https://github.com/runceel/ReactiveProperty)
### ScreenToGif
[Website](https://www.screentogif.com/)
### SharpAvi
[GitHub](https://github.com/baSSiLL/SharpAvi)
### SharpDX
[GitHub](https://github.com/sharpdx/SharpDX)
### WPFNotifyIcon
[Website](http://www.hardcodet.net/wpf-notifyicon)
### WpfToolkit
[GitHub](https://github.com/xceedsoftware/wpftoolkit)
### Media Foundation .NET
[SourceForge](http://mfnet.sourceforge.net/)
### ReactiveX
[GitHub](https://github.com/dotnet/reactive)
================================================
FILE: docs/Build.md
================================================
# Building
## Setting up locally
### Prerequisites
- Visual Studio 2019 or newer with .NET desktop development workload.
- .Net Core 2.1 or greater
- Cake tool
Install: `dotnet tool install -g Cake.Tool --version 0.32.1`
- Some features have other specific requirements, see [here](https://mathewsachin.github.io/Captura/sys-req).
### Steps
1. Clone
`git clone https://github.com/MathewSachin/Captura.git`
2. Setup Api Keys. These are loaded from environment variables during development and embedded into the app on production builds.
Environment Variable | Description
---------------------|-------------
imgur_client_id | Imgur Client Id
yt_client_id | YouTube Client Id
yt_client_secret | YouTube Client Secret
Imgur credentials are only required if you want to upload to Imgur. See [here](https://apidocs.imgur.com/) for more info.
YouTube credentials are only required if you want to upload to YouTube. See [here](https://developers.google.com/youtube/registering_an_application) for more info.
3. Download FFmpeg from within the app or from https://ffmpeg.zeranoe.com/builds/ or use a custom build.
4. Now, you're good to go. You can build using Visual Studio or the [cake script](Cake.md).
================================================
FILE: docs/CI.md
================================================
# Continuous Integration
We use [Appveyor](https://www.appveyor.com/) for CI.
When a commit is pushed to the GitHub repository, AppVeyor clones the repo and builds and tests it using the Cake build script.
If it is a tag build, the release is deployed to GitHub Releases as a draft and to Chocolatey.
### Getting dev builds
> Dev builds can be unstable and should be used for testing purposes only.
1. Go to [AppVeyor project](https://ci.appveyor.com/project/MathewSachin/Captura/branch/master) page.
2. Select Build Configuration: **Debug** or **Release**.

3. Open **Artifacts** tab.

4. Download the Portable or Setup version according to your need.

================================================
FILE: docs/Cake.md
================================================
# Cake build script
### Installing Cake
.NET Core 2.1 is required.
```
dotnet tool install -g Cake.Tool --version 0.32.1
```
### Running the build script
```
dotnet-cake
```
### Arguments
Option | Description
---------------|--------------
-build_version | Build version. Should be like v9.0.0 for stable and CI builds and like v9.0.0-beta3 for prerelease builds. AssemblyInfo.cs files are updated based on this value during build.
-configuration | Configuration: Release or Debug
-target | The Task to run in the build script. See build.cake
```
dotnet-cake --target=CI --configuration=Release
```
================================================
FILE: docs/Changelogs/README.md
================================================
# Changelogs
- [v9.0.0](v9.0.0.md)
- [v8.0.0](v8.0.0.md)
- [v7.0.1](v7.0.1.md)
================================================
FILE: docs/Changelogs/v7.0.1.md
================================================
# v7.0.1
**Fix:** FFmpeg download link gives 404.
================================================
FILE: docs/Changelogs/v8.0.0.md
================================================
# v8.0.0
> This changelog is incomplete.
### Audio / Video Sources
- Stop Window capture when Window is closed.
- **Fix:** Mouse cursor position is wrong after moving region selector.
- **Fix:** Unable to resize Region Selector after stopping recording.
- Desktop Duplication is supported with Variable Frame Rate Gif.
- **Fix:** Desktop Duplication recording does not crash when Screen enters non-recordable mode. e.g. Sign-in screen.
- Added an option to playback recorded audio in real-time in Config | Extras.
- Record and ScreenShot buttons on Region Selector.
- Added Window picker and Screen picker which prompt for selector on starting recording or taking screenshot.
- More than 2 audio sources can be simultaneously recorded.
- Audio sources can be changed during recording. A checkbox next to the Audio heading determines whether audio will be recorded.
- Shows Error Message when bass.dll or bassmix.dll is not present.
- Hide when Recording option is configured on Region Selector itself.
- Refresh retains selected Audio/Video sources/codecs and Webcam.
- Basic drawing support in Region Selector.
### Preview
- Added option to Preview while recording.
- Supports full screen view.
### Overlays
- Overlays are now configured on a separate window.
- Overlays can be positioned by dragging boxes over a Background on the window.
- Separate colors for Right and Middle mouse click overlays.
- Support for custom image overlays.
- Overlay customization from UI is used in Console.
- Option to display Mouse Pointer Overlay to make easier to track Mouse Pointer.
- Added minimal mouse click animation.
- Elapsed is separated from TextOverlays and can be toggled from Main View.
### FFmpeg
- Option to resize FFmpeg output video size.
- FFmpeg Log maintains multiple logs.
- FFmpeg Log copies complete output to clipboard.
- Multiple Custom FFmpeg Codecs.
- Increased FFmpeg thread_queue_size
- Ensure previous frame is written when writing asynchronously.
### Hotkey Window
Separate window to manage hotkeys with option to add, delete or edit action and keys.
### Image Editor
- Added a minimal image editor.
- Added a new ScreenShot target: Editor.
### Image Cropper
Added option to crop images.
### Audio / Video Trimmer
Added option to trim Audio and Video.
### Translation
Added new translations:
- Japanese
- Chinese (Simplified)
- Chinese (Traditional)
### Other
- **Fix:**captura shot failing for fullscreen screenshots.
- **Fix:** Main window webcam preview position on high DPI.
- Duration is considered after Start Delay has elapsed.
- Duration and Start Delay are stored in Settings.
- Added option to use System Proxy.
- **Fix:** Mouse Cursor moves slowly when recording from Command-line
- Webcam capture support from Command-line.
- Option to Minimize to System Tray on Startup.
- Option to Minimize to System Tray when Closed.
- Add a Translator window to aid in translation. Can be opened from About tab.
- More icons in the UI.
- Multiple selectable Screenshot Save Locations.
- Display Licenses in a Window.
- Notification Stack
- Added an Exception Dialog.
- Stop Recorder when Frames are not being writen. This happens when either the FrameRate is too high or a codec is slow. No of frames to be written are checked with a maximum value to determine whether to stop recording. This helps prevent crash from 100% RAM usage and causing the system to hang.
- Ability to delete uploaded images from Imgur
================================================
FILE: docs/Changelogs/v9.0.0.md
================================================
# v9.0.0
> Not released yet!
- Translations updated.
- Added Portuguese, Brazilian translation.
- Fallback to NAudio for audio when BASS is not available.
- Update check in footer.
- Open Webcam preview window by clicking on small preview.
- **Fix:** Recording failing due to `System can't keep up with the Recording. Frames are not being written. Retry again or try with a smaller region, lower Frame Rate or another Codec.`
- Replaced the Gif encoder with FFmpeg Gif (Post-Processing) encoder.
- Discard writer renamed to Preview Only and automatically shows Preview window on starting recording.
- Use system confirmation dialog when deleting from recent list.
- Added YouTube upload feature. Can be used from recent list context menu.
- Remove notifications when notification stack hides.
- Region dimension boxes on Main UI.
- FFmpeg Log can be opened from Home page.
- PasswordBox for Proxy and Streaming keys.
- Region Selector can be Moved/Resized using Keyboard.
- **Fix:** Window position is not remembered.
- **Fix:** Remove 5 fps limit on Preview window.
- **Fix:** Arrow and Only Audio icons.
- **Fix:** weird behaviour of Region Selector border.
- **Fix:** DPI issues when saving from Image Editor.
- **Fix:** BASS WaveFormat for SeparateAudioFilePerSource.
- **Fix:** Smooth rendering of mouse clicks.
- **Fix:** Streaming FrameRate was always set to 10.
- Added Webcam only mode.
- Settings, Codecs folders in AppDir for portability.
- Support `%CAPTURA_PATH%` to point to app directory in FFmpeg, Output and Settings paths.
- Change `Minimize on Capture Start` to minimize to tray.
- Show a simpler Error Window on Exceptions.
================================================
FILE: docs/Choco.md
================================================
# Chocolatey
Chocolatey package is created using the Cake build script and deployed on tag builds by AppVeyor.
### Installation
```
choco install captura -y
```
================================================
FILE: docs/Cmdline/Arg-Source.md
================================================
# Using the source Argument
## Desktop
Use the `desktop` parameter to capture the entire Desktop **(Default)**.
Works with both `captura-cli start` and `captura-cli shot`.
This is the default option, so is as good as not using this option.
e.g.
```
captura-cli start --source desktop
```
## Region
Use Left, Top, Width and Height resp. as comma separated values to represent the region to capture.
Works with both `captura-cli start` and `captura-cli shot`.
The dimensions of the region must be even. If not, they are decreased by 1 as required.
e.g.
```
captura-cli shot --source 100,100,300,400
```
## Screen
Use `screen:` as the argument. `index` is a zero-based index identifying the screen.
Works with both `captura-cli start` and `captura-cli shot`.
You can use `captura-cli list` to check screen indices.
e.g.
```
captura-cli start --source screen:1
```
## No Video
Use `none` for No Video.
Available only with `captura-cli start`.
Can be used for audio only recording.
e.g. Record only the speaker output.
```
captura-cli start --source none --speaker 0
```
## Window
Use `win:` as the argument. `hWnd` is handle of the window.
When using with `captura-cli start`, the window handle must be in the `captura-cli list` output.
You can use `captura-cli list` to check visible window handles.
## Webcam
Use `webcam` as the argument to capture only the webcam.
Can only work with `captura-cli start`.
Use the `--webcam` argument to set the webcam.
You can use `captura-cli list` to check available webcams.
e.g.
```
captura-cli start --source webcam --webcam 0
```
================================================
FILE: docs/Cmdline/README.md
================================================
# Command-line
Project | Executable
-------------|----------------
UI | captura.exe
Command-line | captura-cli.exe
We use the [CommandLineParser](https://nuget.org/packages/CommandLineParser) NuGet package.
The console projects uses default settings with a few modifications and does not save settings.
> Command-line support is not very stable. Please report any bugs you find.
### Why a separate Console app?
There are many issues related to using a WPF application as a console app.
- WPF applications don't block the console. So, we cannot use it for scenarios like wait till capture is completed.
- Writing to console does not work by default
- If `AttachConsole` is used, the written content interferes with console prompt.
- And more ...
### Command-line parameters for UI version
Argument | Description
-------------|----------------------------------
--reset | Reset all Settings
--tray | Starts minimized to System Tray
--no-persist | Don't Save any changes in Settings
--no-hotkey | Don't Register Hotkeys.
--settings | Custom settings folder
e.g. Start captura minimized to tray
```
captura --tray
```
### Implemented Verbs
- [list](Verb-List.md)
List available Screens, Windows, Audio Sources, Webcams, etc.
- [start](Verb-Start.md)
Start a Recording
- [shot](Verb-Shot.md)
Take a ScreenShot
- [ffmpeg](Verb-FFmpeg.md)
Allows installation of ffmpeg from command-line.
- help
Provides help on using the console app.
- version
Prints the version of the console app.
================================================
FILE: docs/Cmdline/Verb-FFmpeg.md
================================================
# Verb: FFmpeg
Can be used to install FFmpeg
e.g. Install FFmpeg to Codecs directory
```
captura-cli ffmpeg --install Codecs
```
================================================
FILE: docs/Cmdline/Verb-List.md
================================================
# Verb: list
Displays the following information:
- Version
- If FFmpeg is available
- FFmpeg encoders
- If SharpAvi is available
- SharpAvi encoders
- If MouseKeyHook is available
- Visible Windows with hWnd
- Screens if there are more than 1
- Available Microphones
- Available Speaker output sources
```
captura list
```
================================================
FILE: docs/Cmdline/Verb-Shot.md
================================================
# Verb: shot
Takes a screenshot
Argument | Description
-----------------|--------------------------------------------
`--cursor` | Include cursor in the screenshot
`--source` | The source to take screenshot of. See [here](Arg-Source.md).
`-f` or `--file` | Output file path.
e.g. Take a screenshot containing cursor.
```
captura-cli shot --cursor
```
================================================
FILE: docs/Cmdline/Verb-Start.md
================================================
# Verb: start
Starts Recording.
There are two modes.
When Length is specified, recording runs until specified Length.
When Length is not specified, press q to quit message is displayed.
Argument | Description
----------------------|-------------------------------------------------------------------------
`--cursor` | Include cursor
`--keys` | Include keystrokes
`--clicks` | Include mouse clicks
`--delay` | Delay before starting recording (in ms)
`-t` or `--length` | Length of recording (in s)
`--source` | The source to record from. See [here](Arg-Source.md).
`--mic` | The microphone index to use. (-1 = none (Default)) (0 is first device).
`--speaker` | The speaker output index to use. (-1 = none (Default)) (0 is first device).
`--webcam` | Webcam to use. (-1 = none (Default)) (0 is first webcam).
`-r` or `--framerate` | Frame Rate (Default is 10).
`--encoder` | The video encoder to use. See below.
`--vq` | Video Quality (1 to 100) (Default is 70).
`--aq` | Audio Quality (1 to 100) (Default is 50).
`-f` or `--file` | Output file path.
`-y` | Overwrite existing file.
`--replay` | Replay recording. Specify duration in seconds as parameter. e.g. `--replay 20`.
e.g. Record 10 seconds with cursor and keystrokes and audio from first speaker output.
```
captura-cli start --length 10 --cursor --keys --speaker=0
```
## Using the Encoder argument
By default, SharpAvi Motion JPEG encoder is used.
### SharpAvi
Use `sharpavi:` as argument. `index` is a zero-based index identifying the encoder.
You can use `captura-cli list` to check encoder indices.
e.g.
```
captura-cli start --encoder sharpavi:0
```
## Media Foundation
Use `mf` as argument.
e.g.
```
captura-cli start --encoder mf
```
### FFmpeg
Use `ffmpeg:` as argument. `index` is a zero-based index identifying the encoder.
You can use `captura-cli list` to check encoder indices.
e.g.
```
captura-cli start --encoder ffmpeg:0
```
### Stream
Use `stream:` as argument. `url` is the rtmp url of the streaming service.
e.g. Stream to Twitch
```
captura-cli start --encoder stream:rtmp://live.twitch.tv/app/TWITCH_KEY
```
### Steps
Use `steps:video` and `steps:images` as `encoder` for Steps recording mode.
#### Record steps to video (avi)
```
captura-cli start --encoder steps:video
```
#### Record steps to a folder containing images (png)
```
captura-cli start --encoder steps:images
```
================================================
FILE: docs/Directories.md
================================================
# Directory Structure
- **src**
Contains the source code. It is organized into multiple [projects](Projects.md).
- **temp**
Used for temporary files generated when building using the Cake script.
- **dist**
Cake build output folder.
- **choco**
Files for generating the Chocolatey package.
- **licenses**
Licenses of dependencies.
- **scripts**
Build scripts used by `build.cake`.
- **tools**
Created by Cake build to store its dependencies.
================================================
FILE: docs/FAQ.md
================================================
# Frequently Asked Questions
## Will Captura support Linux or Mac?
Captura is written using .NET Framework, which at present, is supported only on Windows.
Software written using .NET Framework can work on Linux and Mac using Mono but the native calls and UI pose a problem to that.
Also, the recently released .Net Core only has support for console applications.
## Does Captura support DirectX Game Video Recording?
Some games can be recorded when running on Windows 8 and above. In Captura v8.0.0 there was a separate `Desktop Duplication` option which can also record games which support that. From v9.0.0, `Desktop Duplication` is the default mode.
## Why is maximum frame rate 30fps?
Captura is not very fast on low-end systems. This limit on framerate is a protection against Captura consuming all of your CPU/Memory/Disk and causing your system to hang.
Starting from v8.0.0, you can remove this limitation by going to Config / Extras / Remove FPS Limit.
For reference, Captura can capture 1920x1080 screen at 40fps on my system without audio using FFmpeg x264 codec. My system specifications: 8GB RAM DDR4, Intel i5 6th Gen CPU 2.3 GHz, Windows 10.
## Why is the length of captured video shorter than recording duration?
This happens when Captura drops frames when your system can't keep with the specified frame rate. Try using a lower value of framerate, faster codec or smaller region.
## Why does Captura run out of resources (high memory/CPU/disk usage) during recording?
Atleast 2 GHz CPU and 4 GB RAM are recommended.
This may happen if frames are not being captured as fast as the framerate set. Try a lower value of framerate, faster codec or smaller region. Also, try terminating unnecessary applications running in background using Task Manager. We admit that the technology employed in Captura is not fast.
## Why does my Antivirus say that Captura is virus infected?
Captura is virus-free. It does not include any spam, adware or spyware.
It is probably due to the keystrokes capture feature being mistaken for a keylogger.
================================================
FILE: docs/FFmpeg.md
================================================
# FFmpeg
> It is recommended to always download the latest version of FFmpeg using FFmpeg Downloader. Older versions of FFmpeg can cause unexpected behaviour.
[FFmpeg](http://ffmpeg.org/) is an open-source cross-platform solution to record, convert and stream audio and video.
It adds support for more output formats like **H.264** for Video and **Mp3**, **AAC** etc. when capturing **Only Audio**.
FFmpeg is configured on the **FFmpeg** section in the **Configure** tab.
Due to its large size (approx. 30MB), it is not included in the downloads.
If you already have FFmpeg on your system, you can just set the path to the folder containing it.
If it is installed globally (available in PATH), you don't have to do anything.
If you don't have FFmpeg or want to update, use the inbuilt **FFmpeg Downloader**.
FFmpeg needs to be downloaded only once.
In cases where the **FFmpeg Downloader** fails, please download manually from and set FFmpeg folder in `Configure | FFmpeg`.
If you don't want to use FFmpeg, you can switch to `SharpAvi`.
================================================
FILE: docs/Portable.md
================================================
# Portable
Starting from v9.0.0, for portable builds:
1. Settings are stored in `Settings` folder in app folder by default.
Deleting the `Settings` folder will cause settings to be saved at `%AppData%\Captura`.
This can be overridden by using the `--settings` command-line argument.
2. FFmpeg is downloaded into `Codecs` folder in app folder by default.
Deleting the `Codecs` folder will cause FFmpeg to be downloaded into `%LocalAppData%\Captura`.
This can be overriden from UI.
When setting FFmpeg folder, Settings folder or output folder, `%CAPTURA_PATH%` can be used to refer to the app folder.
e.g. `%CAPTURA_PATH%/Settings` means `Settings` folder in app folder.
================================================
FILE: docs/Projects.md
================================================
# Project Structure
## Base
`Captura.Base` contains common interfaces and base classes. This project is referenced by all other projects.
## Localization
`Captura.Loc` contains the localization code.
## Audio
`Captura.Audio` contains the audio interfaces which are implemented by specific libraries like `Captura.NAudio`.
## Screna, Core
`Screna` and `Captura.Core` projects contain the bulk of the code and are depended on by both UI and Console projects.
## Windows
`Captura.Windows` conatins code that is completely specific to the Windows OS.
## Console
`Captura.Console` builds the console application.
## View Core
`Captura.ViewCore` project contains View models and is depended on by the UI project.
## UI
`Captura` is a WPF project containing the UI.
## Other
The remaining projects add specific features like Imgur, SharpAvi, FFmpeg, etc.
================================================
FILE: docs/README.md
================================================
# Docs
> INCOMPLETE. WORK IN PROGRESS. BEING WRITTEN FOR v9.0.0 WHICH IS YET TO BE RELEASED.
================================================
FILE: docs/Screenshots/Dark.md
================================================
# Screenshots - Dark Theme
## Home

## Collapsed

## Recent

## Configure

## Hotkeys

## FFmpeg

## Overlays

## About

================================================
FILE: docs/Screenshots/Light.md
================================================
# Screenshots - Light Theme
## Home

## Collapsed

## Recent

## Configure

## Hotkeys

## FFmpeg

## Overlays

## About

================================================
FILE: docs/Setup.md
================================================
# Setup
We use [Inno Setup](http://www.jrsoftware.org/isinfo.php) to create the installer for Captura.
================================================
FILE: docs/System-Requirements.md
================================================
# System Requirements
System Requirements for the app to run.
## Operating System
Windows 10 is recommended.
Atleast Windows 7 is required.
If you are on Window 7, make sure *Aero* is enabled.
Screen recording is more efficient on Windows 8 and above as compared to Windows 7.
## Hardware
- 2 GHz CPU (Recommended)
- 4 GB RAM (Recommended)
## .NET Framework
[.NET Framework v4.7.2 Runtime](https://dotnet.microsoft.com/download/dotnet-framework/net472) is required.
## FFmpeg
The app automatically prompts to download FFmpeg if it is not already present on your system.
## Intel QSV
Using the **FFmpeg Intel QSV HEVC** encoder requires the processor to be **Skylake (6th generation)** or later.
## NVenc
See if your GPU supports NVenc for H.264 and H.265 in the [Support Matrix](https://developer.nvidia.com/video-encode-decode-gpu-support-matrix.)
## Lagarith codec
Lagarith codec can be used with SharpAvi.
- Install the codec from its [official website](https://lags.leetcode.net/codec.html).
- Make sure it is configured to use RGB mode.
- Make sure that Null Frames is disabled.
RGB mode and Null Frames can be configured from the registry:
##### HKEY_CURRENT_USER\Software\Lagarith:
- Mode = RGB
- NullFrames= 2294784
================================================
FILE: licenses/Captura, Screna.txt
================================================
The MIT License
(c) Copyright 2018 Mathew Sachin.
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.
================================================
FILE: licenses/CommandLineParser.txt
================================================
The MIT License (MIT)
Copyright (c) 2005 - 2015 Giacomo Stelluti Scala & Contributors
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.
================================================
FILE: licenses/CroppingAdorner.txt
================================================
Code Project Open License (CPOL)
https://www.codeproject.com/info/cpol10.aspx
https://www.codeproject.com/Articles/23158/A-Photoshop-like-Cropping-Adorner-for-WPF
================================================
FILE: licenses/DirectShowLib.txt
================================================
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
Copyright (C)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!
================================================
FILE: licenses/FFMpeg.txt
================================================
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
Copyright (C)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!
================================================
FILE: licenses/Inno.txt
================================================
Inno Setup License
==================
Except where otherwise noted, all of the documentation and software included
in the Inno Setup package is copyrighted by Jordan Russell.
Copyright (C) 1997-2013 Jordan Russell. All rights reserved.
Portions Copyright (C) 2000-2013 Martijn Laan. All rights reserved.
This software is provided "as-is," without any express or implied warranty.
In no event shall the author be held liable for any damages arising from the
use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter and redistribute it,
provided that the following conditions are met:
1. All redistributions of source code files must retain all copyright
notices that are currently in place, and this list of conditions without
modification.
2. All redistributions in binary form must retain all occurrences of the
above copyright notice and web site addresses that are currently in
place (for example, in the About boxes).
3. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software to
distribute a product, an acknowledgment in the product documentation
would be appreciated but is not required.
4. Modified versions in source or binary form must be plainly marked as
such, and must not be misrepresented as being the original software.
Jordan Russell
jr-2010 AT jrsoftware.org
http://www.jrsoftware.org/
================================================
FILE: licenses/MUI.Extended.Toolkit.txt
================================================
MIT License
Copyright (c) 2016 Sam Oates
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.
================================================
FILE: licenses/MUI.txt
================================================
Microsoft Public License (Ms-PL)
This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software.
1. Definitions
The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law.
A "contribution" is the original software, or any additions or changes to the software.
A "contributor" is any person that distributes its contribution under this license.
"Licensed patents" are a contributor's patent claims that read directly on its contribution.
2. Grant of Rights
(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
3. Conditions and Limitations
(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement.
================================================
FILE: licenses/Media Foundation .NET.txt
================================================
GNU LIBRARY GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1991 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the library GPL. It is
numbered 2 because it goes with version 2 of the ordinary GPL.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Library General Public License, applies to some
specially designated Free Software Foundation software, and to any
other libraries whose authors decide to use it. You can use it for
your libraries, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if
you distribute copies of the library, or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link a program with the library, you must provide
complete object files to the recipients so that they can relink them
with the library, after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
Our method of protecting your rights has two steps: (1) copyright
the library, and (2) offer you this license which gives you legal
permission to copy, distribute and/or modify the library.
Also, for each distributor's protection, we want to make certain
that everyone understands that there is no warranty for this free
library. If the library is modified by someone else and passed on, we
want its recipients to know that what they have is not the original
version, so that any problems introduced by others will not reflect on
the original authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that companies distributing free
software will individually obtain patent licenses, thus in effect
transforming the program into proprietary software. To prevent this,
we have made it clear that any patent must be licensed for everyone's
free use or not licensed at all.
Most GNU software, including some libraries, is covered by the ordinary
GNU General Public License, which was designed for utility programs. This
license, the GNU Library General Public License, applies to certain
designated libraries. This license is quite different from the ordinary
one; be sure to read it in full, and don't assume that anything in it is
the same as in the ordinary license.
The reason we have a separate public license for some libraries is that
they blur the distinction we usually make between modifying or adding to a
program and simply using it. Linking a program with a library, without
changing the library, is in some sense simply using the library, and is
analogous to running a utility program or application program. However, in
a textual and legal sense, the linked executable is a combined work, a
derivative of the original library, and the ordinary General Public License
treats it as such.
Because of this blurred distinction, using the ordinary General
Public License for libraries did not effectively promote software
sharing, because most developers did not use the libraries. We
concluded that weaker conditions might promote sharing better.
However, unrestricted linking of non-free programs would deprive the
users of those programs of all benefit from the free status of the
libraries themselves. This Library General Public License is intended to
permit developers of non-free programs to use free libraries, while
preserving your freedom as a user of such programs to change the free
libraries that are incorporated in them. (We have not seen how to achieve
this as regards changes in header files, but we have achieved it as regards
changes in the actual functions of the Library.) The hope is that this
will lead to faster development of free libraries.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, while the latter only
works together with the library.
Note that it is possible for a library to be covered by the ordinary
General Public License rather than by this special one.
GNU LIBRARY GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library which
contains a notice placed by the copyright holder or other authorized
party saying it may be distributed under the terms of this Library
General Public License (also called "this License"). Each licensee is
addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also compile or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
c) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
d) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the source code distributed need not include anything that is normally
distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Library General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
Copyright (C)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!
================================================
FILE: licenses/MouseKeyHook.txt
================================================
Copyright (c) 2004-2015, George Mamaladze 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 asssdwd 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: licenses/NAudio.txt
================================================
Microsoft Public License (Ms-PL)
This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software.
1. Definitions
The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law.
A "contribution" is the original software, or any additions or changes to the software.
A "contributor" is any person that distributes its contribution under this license.
"Licensed patents" are a contributor's patent claims that read directly on its contribution.
2. Grant of Rights
(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
3. Conditions and Limitations
(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement.
================================================
FILE: licenses/Newtonsoft.Json.txt
================================================
The MIT License (MIT)
Copyright (c) 2007 James Newton-King
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.
================================================
FILE: licenses/Ooki.Dialogs.txt
================================================
License agreement for Ookii.Dialogs.
Copyright Sven Groot (Ookii.org) 2009
All rights reserved.
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.
3) Neither the name of the ORGANIZATION 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 OWNER 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: licenses/ReactiveProperty.txt
================================================
The MIT License (MIT)
Copyright (c) 2018 neuecc, xin9le, okazuki
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.
================================================
FILE: licenses/ScreenToGif.txt
================================================
Captura.Webcam is adapted from ScreenToGif
# Microsoft Public License (Ms-PL)
This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software.
## 1. Definitions
The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law.
A "contribution" is the original software, or any additions or changes to the software.
A "contributor" is any person that distributes its contribution under this license.
"Licensed patents" are a contributor's patent claims that read directly on its contribution.
## 2. Grant of Rights
(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
## 3. Conditions and Limitations
(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement.
================================================
FILE: licenses/SharpAvi.txt
================================================
The MIT License
Copyright (c) 2013-2014 Vasili Maslov
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.
================================================
FILE: licenses/SharpDX.txt
================================================
Copyright (c) 2010-2014 SharpDX - Alexandre Mutel
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.
================================================
FILE: licenses/System.Reactive.txt
================================================
Copyright (c) .NET Foundation and Contributors
All Rights Reserved
Licensed under the Apache License, Version 2.0 (the "License"); you
may not use this file except in compliance with the License. You may
obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied. See the License for the specific language governing permissions
and limitations under the License.
================================================
FILE: licenses/WPFNotifyIcon.txt
================================================
Code Project Open License (CPOL)
https://www.codeproject.com/info/cpol10.aspx
================================================
FILE: licenses/WpfToolkit.txt
================================================
# Microsoft Public License (Ms-PL)
This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software.
## 1. Definitions
The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law.
A "contribution" is the original software, or any additions or changes to the software.
A "contributor" is any person that distributes its contribution under this license.
"Licensed patents" are a contributor's patent claims that read directly on its contribution.
## 2. Grant of Rights
(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
## 3. Conditions and Limitations
(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement.
================================================
FILE: scripts/apikeys.cake
================================================
#l "constants.cake"
#l "backup.cake"
using System.Text.RegularExpressions;
IEnumerable GetVariables(string ApiKeysContent)
{
var match = Regex.Match(ApiKeysContent, "Get\\(\"(.*)\"\\)");
while (match.Success)
{
yield return match.Groups[1].ToString();
match = match.NextMatch();
}
}
void EmbedApiKeys()
{
var apiKeysPath = sourceFolder + File("Captura.Core/ApiKeys.cs");
Information("Embedding Api Keys from Environment Variables ...");
CreateBackup(apiKeysPath, tempFolder + File("ApiKeys.cs"));
var content = FileRead(apiKeysPath);
foreach (var variable in GetVariables(content))
{
if (HasEnvironmentVariable(variable))
{
content = content.Replace($"Get(\"{variable}\")", $"\"{EnvironmentVariable(variable)}\"");
}
}
FileWrite(apiKeysPath, content);
}
================================================
FILE: scripts/backup.cake
================================================
readonly var Backups = new List();
class Backup
{
public Backup(string OriginalPath, string BackupPath)
{
this.OriginalPath = OriginalPath;
this.BackupPath = BackupPath;
}
public string OriginalPath { get; }
public string BackupPath { get; }
}
void CreateBackup(string OriginalPath, string BackupPath)
{
var backup = new Backup(OriginalPath, BackupPath);
CopyFile(OriginalPath, BackupPath);
Backups.Add(backup);
}
void RestoreBackups()
{
Backups.ForEach(M =>
{
DeleteFile(M.OriginalPath);
MoveFile(M.BackupPath, M.OriginalPath);
});
}
string FileRead(string FileName) => System.IO.File.ReadAllText(FileName);
void FileWrite(string FileName, string Content) => System.IO.File.WriteAllText(FileName, Content);
================================================
FILE: scripts/choco.cake
================================================
#l "constants.cake"
#l "backup.cake"
#l "version.cake"
readonly var chocoVersion = tag?.Substring(1) ?? "";
readonly var ChocoPkgPath = tempFolder + File($"captura.{chocoVersion}.nupkg");
void PackChoco()
{
var checksum = CalculateFileHash(PortablePath).ToHex();
var chocoInstallScript = chocoFolder + File("tools/chocolateyinstall.ps1");
var originalContent = FileRead(chocoInstallScript);
var newContent = $"$tag = '{tag}'; $checksum = '{checksum}'; {originalContent}";
CreateBackup(chocoInstallScript, tempFolder + File("cinst.ps1"));
FileWrite(chocoInstallScript, newContent);
ChocolateyPack(chocoFolder + File("captura.nuspec"), new ChocolateyPackSettings
{
Version = chocoVersion,
ArgumentCustomization = Args => Args.Append($"--outputdirectory {tempFolder}")
});
}
================================================
FILE: scripts/constants.cake
================================================
readonly var sourceFolder = Directory("src");
readonly var tempFolder = Directory("temp");
readonly var distFolder = Directory("dist");
readonly var licensesFolder = Directory("licenses");
readonly var chocoFolder = Directory("choco");
readonly var slnPath = sourceFolder + File("Captura.sln");
readonly var PortablePath = tempFolder + File("Captura-Portable.zip");
readonly var SetupPath = tempFolder + File("Captura-Setup.exe");
const string Release = "Release";
================================================
FILE: scripts/version.cake
================================================
#l "constants.cake"
#l "backup.cake"
using static System.Text.RegularExpressions.Regex;
// version parameter is already used by cake.exe
var tag = Argument("build_version", "v0.0.0");
var version = tag;
void UpdateVersion(string AssemblyInfoPath)
{
var content = FileRead(AssemblyInfoPath);
var start = content.IndexOf("AssemblyVersion");
var end = content.IndexOf(")", start);
var replace = content.Replace(content.Substring(start, end - start + 1), $"AssemblyVersion(\"{version}\")");
FileWrite(AssemblyInfoPath, replace);
}
void HandleVersion()
{
const string StableVersionRegex = @"^v\d+\.\d+\.\d+$";
const string PrereleaseVersionRegex = @"^v\d+\.\d+\.\d+-[^\s]+$";
// Stable Release or CI build
if (IsMatch(version, StableVersionRegex))
{
version = version.Substring(1);
}
// Prerelease
else if (IsMatch(version, PrereleaseVersionRegex))
{
version = version.Split('-')[0].Substring(1);
}
else throw new ArgumentException("Invalid Version Format", "build_version");
var assemblyInfoFile = File("Properties/AssemblyInfo.cs");
var uiAssemblyInfo = sourceFolder + Directory("Captura") + assemblyInfoFile;
var consoleAssemblyInfo = sourceFolder + Directory("Captura.Console") + assemblyInfoFile;
// Update AssemblyInfo files
CreateBackup(uiAssemblyInfo, tempFolder + File("AssemblyInfo.cs"));
CreateBackup(consoleAssemblyInfo, tempFolder + File("console.cs"));
UpdateVersion(uiAssemblyInfo);
UpdateVersion(consoleAssemblyInfo);
}
================================================
FILE: src/Captura/App.xaml
================================================
================================================
FILE: src/Captura/App.xaml.cs
================================================
using FirstFloor.ModernUI.Presentation;
using System;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Threading;
using Captura.Loc;
using Captura.Models;
using Captura.MouseKeyHook;
using Captura.ViewModels;
using Captura.Views;
using CommandLine;
namespace Captura
{
public partial class App
{
public App()
{
SingleInstanceManager.SingleInstanceCheck();
// Splash Screen should be created manually and after single-instance is checked
ShowSplashScreen();
}
public static CmdOptions CmdOptions { get; private set; }
void App_OnDispatcherUnhandledException(object Sender, DispatcherUnhandledExceptionEventArgs Args)
{
var dir = Path.Combine(ServiceProvider.SettingsDir, "Crashes");
Directory.CreateDirectory(dir);
File.WriteAllText(Path.Combine(dir, $"{DateTime.Now:yyyy-MM-dd-HH-mm-ss}.txt"), Args.Exception.ToString());
Args.Handled = true;
new ErrorWindow(Args.Exception, Args.Exception.Message).ShowDialog();
}
void ShowSplashScreen()
{
var splashScreen = new SplashScreen("Images/Logo.png");
splashScreen.Show(true);
}
void Application_Startup(object Sender, StartupEventArgs Args)
{
AppDomain.CurrentDomain.UnhandledException += OnCurrentDomainOnUnhandledException;
ServiceProvider.LoadModule(new CoreModule());
ServiceProvider.LoadModule(new ViewCoreModule());
Parser.Default.ParseArguments(Args.Args)
.WithParsed(M => CmdOptions = M);
if (CmdOptions.Settings != null)
{
ServiceProvider.SettingsDir = CmdOptions.Settings;
}
var settings = ServiceProvider.Get();
InitTheme(settings);
BindLanguageSetting(settings);
BindKeymapSetting(settings);
}
void OnCurrentDomainOnUnhandledException(object S, UnhandledExceptionEventArgs E)
{
var dir = Path.Combine(ServiceProvider.SettingsDir, "Crashes");
Directory.CreateDirectory(dir);
File.WriteAllText(Path.Combine(dir, $"{DateTime.Now:yyyy-MM-dd-HH-mm-ss}.txt"), E.ExceptionObject.ToString());
if (E.ExceptionObject is Exception e)
{
Current.Dispatcher.Invoke(() => new ErrorWindow(e, e.Message).ShowDialog());
}
Shutdown();
}
static void BindKeymapSetting(Settings Settings)
{
var keymap = ServiceProvider.Get();
if (!string.IsNullOrWhiteSpace(Settings.Keystrokes.KeymapName))
{
var matched = keymap.AvailableKeymaps.FirstOrDefault(M => M.Name == Settings.Keystrokes.KeymapName);
if (matched != null)
keymap.SelectedKeymap = matched;
}
keymap.PropertyChanged += (S, E) => Settings.Keystrokes.KeymapName = keymap.SelectedKeymap.Name;
}
static void BindLanguageSetting(Settings Settings)
{
var loc = LanguageManager.Instance;
if (!string.IsNullOrWhiteSpace(Settings.UI.Language))
{
var matchedCulture = loc.AvailableCultures.FirstOrDefault(M => M.Name == Settings.UI.Language);
if (matchedCulture != null)
loc.CurrentCulture = matchedCulture;
}
loc.LanguageChanged += L => Settings.UI.Language = L.Name;
}
static void InitTheme(Settings Settings)
{
if (!CmdOptions.Reset)
{
Settings.Load();
}
if (Settings.UI.DarkTheme)
{
AppearanceManager.Current.ThemeSource = AppearanceManager.DarkThemeSource;
}
var accent = Settings.UI.AccentColor;
if (!string.IsNullOrEmpty(accent))
{
AppearanceManager.Current.AccentColor = WpfExtensions.ParseColor(accent);
}
}
}
}
================================================
FILE: src/Captura/Captura.csproj
================================================
WinExe
Captura
captura
net472
false
true
false
true
Images\Captura.ico
false
================================================
FILE: src/Captura/Controls/CollapsedBar.xaml
================================================
================================================
FILE: src/Captura/Controls/CollapsedBar.xaml.cs
================================================
using System.Windows;
namespace Captura
{
public partial class CollapsedBar
{
public CollapsedBar()
{
InitializeComponent();
}
void OpenSettings(object Sender, RoutedEventArgs E)
{
SettingsWindow.ShowInstance();
}
}
}
================================================
FILE: src/Captura/Controls/FontSelector.xaml
================================================
================================================
FILE: src/Captura/Controls/FontSelector.xaml.cs
================================================
using System.Windows;
namespace Captura
{
public partial class FontSelector
{
public FontSelector()
{
InitializeComponent();
}
public static readonly DependencyProperty SelectedFontProperty = DependencyProperty.Register(
nameof(SelectedFont),
typeof(string),
typeof(FontSelector),
new FrameworkPropertyMetadata("Arial"));
public string SelectedFont
{
get => (string) GetValue(SelectedFontProperty);
set => SetValue(SelectedFontProperty, value);
}
}
}
================================================
FILE: src/Captura/Controls/HotkeySelector.cs
================================================
using System.Windows;
using System.Windows.Forms;
using System.Windows.Input;
using System.Windows.Media;
using Captura.Hotkeys;
using Button = System.Windows.Controls.Button;
using KeyEventArgs = System.Windows.Input.KeyEventArgs;
namespace Captura
{
public class HotkeySelector : Button
{
bool _editing;
static readonly SolidColorBrush RedBrush = new SolidColorBrush(WpfExtensions.ParseColor("#ef5350"));
static readonly SolidColorBrush GreenBrush = new SolidColorBrush(WpfExtensions.ParseColor("#43a047"));
static readonly SolidColorBrush WhiteBrush = new SolidColorBrush(Colors.White);
public static readonly DependencyProperty HotkeyModelProperty = DependencyProperty.Register(nameof(HotkeyModel),
typeof(Hotkey),
typeof(HotkeySelector),
new UIPropertyMetadata(HotkeyModelChangedCallback));
static void HotkeyModelChangedCallback(DependencyObject Sender, DependencyPropertyChangedEventArgs Args)
{
if (Sender is HotkeySelector selector && Args.NewValue is Hotkey hotkey)
{
selector.TextColor();
hotkey.PropertyChanged += (S, E) =>
{
if (E.PropertyName == nameof(Hotkey.IsActive))
selector.TextColor();
};
selector.Content = hotkey.ToString();
}
}
public Hotkey HotkeyModel
{
get => (Hotkey) GetValue(HotkeyModelProperty);
set => SetValue(HotkeyModelProperty, value);
}
void HotkeyEdited(Key NewKey, Modifiers NewModifiers)
{
HotkeyEdited((Keys) KeyInterop.VirtualKeyFromKey(NewKey), NewModifiers);
}
void TextColor()
{
if (HotkeyModel.IsActive)
{
Background = HotkeyModel.IsRegistered ? GreenBrush : RedBrush;
Foreground = WhiteBrush;
}
else
{
ClearValue(BackgroundProperty);
ClearValue(ForegroundProperty);
}
}
void HotkeyEdited(Keys NewKey, Modifiers NewModifiers)
{
HotkeyModel.Change(NewKey, NewModifiers);
// Red Text on Error
TextColor();
Content = HotkeyModel.ToString();
_editing = false;
}
protected override void OnClick()
{
base.OnClick();
_editing = !_editing;
Content = _editing ? "Press new Hotkey..." : HotkeyModel.ToString();
}
protected override void OnLostFocus(RoutedEventArgs E)
{
base.OnLostFocus(E);
CancelEditing();
}
void CancelEditing()
{
if (!_editing)
return;
_editing = false;
Content = HotkeyModel.ToString();
}
static bool IsValid(KeyEventArgs E)
{
return E.Key != Key.None // Some key must pe pressed
&& !E.KeyboardDevice.Modifiers.HasFlag(ModifierKeys.Windows) // Windows Key is reserved by OS
&& E.Key != Key.LeftCtrl && E.Key != Key.RightCtrl // Modifier Keys alone are not supported
&& E.Key != Key.LeftAlt && E.Key != Key.RightAlt
&& E.Key != Key.LeftShift && E.Key != Key.RightShift;
}
protected override void OnPreviewKeyDown(KeyEventArgs E)
{
// Ignore Repeats
if (E.IsRepeat)
{
E.Handled = true;
return;
}
if (_editing)
{
// Suppress event propagation
E.Handled = true;
switch (E.Key)
{
case Key.Escape:
CancelEditing();
break;
case Key.System:
if (E.SystemKey == Key.LeftAlt || E.SystemKey == Key.RightAlt)
Content = "Alt + ...";
else HotkeyEdited(E.SystemKey, Modifiers.Alt);
break;
default:
if (IsValid(E))
HotkeyEdited(E.Key, (Modifiers)E.KeyboardDevice.Modifiers);
else
{
var modifiers = E.KeyboardDevice.Modifiers;
Content = "";
if (modifiers.HasFlag(ModifierKeys.Control))
Content += "Ctrl + ";
if (modifiers.HasFlag(ModifierKeys.Alt))
Content += "Alt + ";
if (modifiers.HasFlag(ModifierKeys.Shift))
Content += "Shift + ";
Content += "...";
}
break;
}
}
base.OnPreviewKeyDown(E);
}
protected override void OnPreviewKeyUp(KeyEventArgs E)
{
// Ignore Repeats
if (E.IsRepeat)
return;
if (_editing)
{
// Suppress event propagation
E.Handled = true;
// PrintScreen is not recognized in KeyDown
switch (E.Key)
{
case Key.Snapshot:
HotkeyEdited(Keys.PrintScreen, (Modifiers)E.KeyboardDevice.Modifiers);
break;
case Key.System when E.SystemKey == Key.Snapshot:
HotkeyEdited(Keys.PrintScreen, Modifiers.Alt);
break;
}
}
base.OnPreviewKeyUp(E);
}
}
}
================================================
FILE: src/Captura/Controls/ImageOverlaySettingsControl.xaml
================================================
================================================
FILE: src/Captura/Controls/ImageOverlaySettingsControl.xaml.cs
================================================
namespace Captura
{
public partial class ImageOverlaySettingsControl
{
public ImageOverlaySettingsControl()
{
InitializeComponent();
}
}
}
================================================
FILE: src/Captura/Controls/LayerFrame.xaml
================================================
================================================
FILE: src/Captura/Controls/LayerFrame.xaml.cs
================================================
using System;
using System.Windows;
namespace Captura
{
public partial class LayerFrame
{
public LayerFrame()
{
InitializeComponent();
}
public event Action PositionUpdated;
public void RaisePositionChanged(Rect Rect)
{
PositionUpdated?.Invoke(Rect);
}
}
}
================================================
FILE: src/Captura/Controls/ModernButton.cs
================================================
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Captura
{
public class ModernButton : Button
{
public static readonly DependencyProperty IconDataProperty = DependencyProperty.Register(nameof(IconData), typeof(Geometry), typeof(ModernButton));
public ModernButton() { DefaultStyleKey = typeof(ModernButton); }
public Geometry IconData
{
get => (Geometry)GetValue(IconDataProperty);
set => SetValue(IconDataProperty, value);
}
}
}
================================================
FILE: src/Captura/Controls/ModernPasswordBox.xaml
================================================
================================================
FILE: src/Captura/Controls/ModernPasswordBox.xaml.cs
================================================
using System.Windows;
namespace Captura
{
public partial class ModernPasswordBox
{
public ModernPasswordBox()
{
InitializeComponent();
PswBox.PasswordChanged += (S, E) => Password = PswBox.Password;
}
public static readonly DependencyProperty PasswordProperty = DependencyProperty.Register(
nameof(Password),
typeof(string),
typeof(ModernPasswordBox),
new UIPropertyMetadata((S, E) =>
{
if (S is ModernPasswordBox modernPswBox && E.NewValue is string psw)
{
if (modernPswBox.PswBox.Password != psw)
modernPswBox.PswBox.Password = psw;
}
}));
public string Password
{
get => (string) GetValue(PasswordProperty);
set => SetValue(PasswordProperty, value);
}
public static readonly DependencyProperty PasswordVisibleProperty = DependencyProperty.Register(
nameof(PasswordVisible),
typeof(bool),
typeof(ModernPasswordBox));
public bool PasswordVisible
{
get => (bool)GetValue(PasswordVisibleProperty);
set => SetValue(PasswordVisibleProperty, value);
}
void ToggleVisibility(object Sender, RoutedEventArgs E)
{
PasswordVisible = !PasswordVisible;
}
}
}
================================================
FILE: src/Captura/Controls/ModernToggleButton.cs
================================================
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Captura
{
public class ModernToggleButton : CheckBox
{
public static readonly DependencyProperty IconDataProperty = DependencyProperty.Register(nameof(IconData), typeof(Geometry), typeof(ModernToggleButton));
public ModernToggleButton() { DefaultStyleKey = typeof(ModernToggleButton); }
public Geometry IconData
{
get => (Geometry)GetValue(IconDataProperty);
set => SetValue(IconDataProperty, value);
}
}
}
================================================
FILE: src/Captura/Controls/NotificationBalloon.xaml
================================================
================================================
FILE: src/Captura/Controls/NotificationBalloon.xaml.cs
================================================
using System.Windows;
using System.Windows.Input;
using System;
namespace Captura
{
public partial class NotificationBalloon : IRemoveRequester
{
public INotification Notification { get; }
public NotificationBalloon(INotification Notification)
{
this.Notification = Notification;
Notification.RemoveRequested += OnClose;
InitializeComponent();
}
void OnClose()
{
RemoveRequested?.Invoke();
}
public event Action RemoveRequested;
void CloseButton_Click(object Sender, RoutedEventArgs E) => OnClose();
void TextBlock_MouseUp(object Sender, MouseButtonEventArgs E)
{
Notification.RaiseClick();
OnClose();
}
}
}
================================================
FILE: src/Captura/Controls/NotificationStack.xaml
================================================
================================================
FILE: src/Captura/Controls/NotificationStack.xaml.cs
================================================
using System;
using System.Linq;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Threading;
using MouseEventArgs = System.Windows.Input.MouseEventArgs;
namespace Captura
{
public partial class NotificationStack
{
static readonly TimeSpan TimeoutToHide = TimeSpan.FromSeconds(5);
DateTime _lastMouseMoveTime;
readonly DispatcherTimer _timer;
public NotificationStack()
{
InitializeComponent();
_timer = new DispatcherTimer
{
Interval = TimeSpan.FromSeconds(1)
};
_timer.Tick += TimerOnTick;
_timer.Start();
}
void TimerOnTick(object Sender, EventArgs Args)
{
var now = DateTime.Now;
var elapsed = now - _lastMouseMoveTime;
var unfinished = ItemsControl.Items
.OfType()
.Any(M => !M.Notification.Finished);
if (unfinished)
{
_lastMouseMoveTime = now;
}
if (elapsed < TimeoutToHide)
return;
if (!unfinished)
{
OnClose();
}
}
public void Hide()
{
BeginAnimation(OpacityProperty, new DoubleAnimation(0, new Duration(TimeSpan.FromMilliseconds(100))));
if (_timer.IsEnabled)
_timer.Stop();
}
public void Show()
{
_lastMouseMoveTime = DateTime.Now;
BeginAnimation(OpacityProperty, new DoubleAnimation(1, new Duration(TimeSpan.FromMilliseconds(300))));
if (!_timer.IsEnabled)
_timer.Start();
}
void OnClose()
{
Hide();
var copy = ItemsControl.Items.OfType().ToArray();
foreach (var frameworkElement in copy)
{
Remove(frameworkElement);
}
}
void CloseButton_Click(object Sender, RoutedEventArgs E) => OnClose();
///
/// Slides out element while decreasing opacity, then decreases height, then removes.
///
void Remove(FrameworkElement Element)
{
var transform = new TranslateTransform();
Element.RenderTransform = transform;
var translateAnim = new DoubleAnimation(500, new Duration(TimeSpan.FromMilliseconds(200)));
var opactityAnim = new DoubleAnimation(0, new Duration(TimeSpan.FromMilliseconds(200)));
var heightAnim = new DoubleAnimation(Element.ActualHeight, 0, new Duration(TimeSpan.FromMilliseconds(200)));
heightAnim.Completed += (S, E) =>
{
ItemsControl.Items.Remove(Element);
if (ItemsControl.Items.Count == 0)
{
Hide();
}
};
opactityAnim.Completed += (S, E) => Element.BeginAnimation(HeightProperty, heightAnim);
transform.BeginAnimation(TranslateTransform.XProperty, translateAnim);
Element.BeginAnimation(OpacityProperty, opactityAnim);
}
const int MaxItems = 5;
public void Add(FrameworkElement Element)
{
if (Element is IRemoveRequester removeRequester)
{
removeRequester.RemoveRequested += () => Remove(Element);
}
if (Element is ScreenShotBalloon ssBalloon)
ssBalloon.Expander.IsExpanded = true;
foreach (var item in ItemsControl.Items)
{
if (item is ScreenShotBalloon screenShotBalloon)
{
screenShotBalloon.Expander.IsExpanded = false;
}
}
ItemsControl.Items.Insert(0, Element);
if (ItemsControl.Items.Count > MaxItems)
{
var itemsToRemove = ItemsControl.Items
.OfType()
.Skip(MaxItems)
.ToArray();
foreach (var frameworkElement in itemsToRemove)
{
if (frameworkElement is NotificationBalloon progressBalloon && !progressBalloon.Notification.Finished)
continue;
Remove(frameworkElement);
}
}
}
void NotificationStack_OnMouseMove(object Sender, MouseEventArgs E)
{
if (ItemsControl.Items.Count == 0)
return;
_lastMouseMoveTime = DateTime.Now;
Show();
}
}
}
================================================
FILE: src/Captura/Controls/OutputFolderControl.xaml
================================================
================================================
FILE: src/Captura/Controls/OutputFolderControl.xaml.cs
================================================
using System.Windows.Input;
using Captura.ViewModels;
namespace Captura
{
public partial class OutputFolderControl
{
public OutputFolderControl()
{
InitializeComponent();
}
void SelectTargetFolder(object Sender, MouseButtonEventArgs E)
{
if (DataContext is MainViewModel vm)
{
vm.SelectOutputFolderCommand.ExecuteIfCan();
}
}
}
}
================================================
FILE: src/Captura/Controls/PauseButton.xaml
================================================
================================================
FILE: src/Captura/Controls/PauseButton.xaml.cs
================================================
namespace Captura
{
public partial class PauseButton
{
public PauseButton()
{
InitializeComponent();
}
}
}
================================================
FILE: src/Captura/Controls/PositionSettingsControl.xaml
================================================
================================================
FILE: src/Captura/Controls/PositionSettingsControl.xaml.cs
================================================
namespace Captura
{
public partial class PositionSettingsControl
{
public PositionSettingsControl()
{
InitializeComponent();
}
}
}
================================================
FILE: src/Captura/Controls/PuncturedRegion.xaml
================================================
================================================
FILE: src/Captura/Controls/PuncturedRegion.xaml.cs
================================================
using System.Windows;
namespace Captura
{
public partial class PuncturedRegion
{
public Rect? Region
{
get => (Rect?)GetValue(RegionProperty);
set => SetValue(RegionProperty, value);
}
public static readonly DependencyProperty RegionProperty = DependencyProperty.Register(
nameof(Region),
typeof(Rect?),
typeof(PuncturedRegion),
new PropertyMetadata(RegionChanged));
static void RegionChanged(DependencyObject Obj, DependencyPropertyChangedEventArgs E)
{
if (Obj is PuncturedRegion r)
{
switch (E.NewValue)
{
case null:
// Must not be collapsed, since ActualWidth and ActualHeight are used.
r.Visibility = Visibility.Hidden;
break;
case Rect region:
var w = r.ActualWidth;
var h = r.ActualHeight;
r.BorderTop.Margin = new Thickness();
r.BorderTop.Width = w;
r.BorderTop.Height = region.Top.Clip(0, h);
r.BorderBottom.Margin = new Thickness(0, region.Bottom, 0, 0);
r.BorderBottom.Width = w;
r.BorderBottom.Height = (h - region.Bottom).Clip(0, h);
r.BorderLeft.Margin = new Thickness(0, region.Top, 0, 0);
r.BorderLeft.Width = region.Left.Clip(0, w);
r.BorderLeft.Height = region.Height;
r.BorderRight.Margin = new Thickness(region.Right, region.Top, 0, 0);
r.BorderRight.Width = (w - region.Right).Clip(0, w);
r.BorderRight.Height = region.Height;
r.Visibility = Visibility.Visible;
break;
}
}
}
public PuncturedRegion()
{
InitializeComponent();
}
}
}
================================================
FILE: src/Captura/Controls/RecentItem.xaml
================================================
================================================
FILE: src/Captura/Controls/RecentItem.xaml.cs
================================================
namespace Captura
{
public partial class RecentItem
{
public RecentItem()
{
InitializeComponent();
}
}
}
================================================
FILE: src/Captura/Controls/ScreenShotBalloon.xaml
================================================
================================================
FILE: src/Captura/Controls/ScreenShotBalloon.xaml.cs
================================================
using System.Windows;
using System.Windows.Input;
using System.IO;
using System.Diagnostics;
using System;
using System.Windows.Media.Imaging;
using Captura.Models;
namespace Captura
{
public partial class ScreenShotBalloon : IRemoveRequester
{
readonly string _filePath;
public ScreenShotBalloon(string FilePath)
{
_filePath = FilePath;
DataContext = Path.GetFileName(FilePath);
InitializeComponent();
// Do not assign image directly, cache it, else the file can't be deleted.
var image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = new Uri(FilePath);
image.EndInit();
Img.Source = image;
}
void CloseButton_Click(object Sender, RoutedEventArgs E) => OnClose();
void OnClose()
{
RemoveRequested?.Invoke();
}
public event Action RemoveRequested;
void Image_MouseUp(object Sender, MouseButtonEventArgs E)
{
ServiceProvider.LaunchFile(new ProcessStartInfo(_filePath));
OnClose();
}
void EditButton_OnClick(object Sender, RoutedEventArgs E)
{
var winserv = ServiceProvider.Get();
winserv.EditImage(_filePath);
}
}
}
================================================
FILE: src/Captura/Controls/ScreenShotButton.xaml
================================================
================================================
FILE: src/Captura/Controls/ScreenShotButton.xaml.cs
================================================
namespace Captura
{
public partial class ScreenShotButton
{
public ScreenShotButton()
{
InitializeComponent();
}
}
}
================================================
FILE: src/Captura/Controls/StatusBar.xaml
================================================
================================================
FILE: src/Captura/Controls/StatusBar.xaml.cs
================================================
namespace Captura
{
public partial class StatusBar
{
public StatusBar()
{
InitializeComponent();
}
}
}
================================================
FILE: src/Captura/Controls/StripedBorder.xaml
================================================
================================================
FILE: src/Captura/Controls/StripedBorder.xaml.cs
================================================
namespace Captura
{
public partial class StripedBorder
{
public StripedBorder()
{
InitializeComponent();
}
}
}
================================================
FILE: src/Captura/Controls/TextOverlaySettingsControl.xaml
================================================
================================================
FILE: src/Captura/Controls/TextOverlaySettingsControl.xaml.cs
================================================
namespace Captura
{
public partial class TextOverlaySettingsControl
{
public TextOverlaySettingsControl()
{
InitializeComponent();
}
}
}
================================================
FILE: src/Captura/Controls/VideoSourceKindList.xaml
================================================
================================================
FILE: src/Captura/Controls/VideoSourceKindList.xaml.cs
================================================
using System.Windows.Controls;
using System.Windows.Input;
using Captura.Video;
namespace Captura
{
public partial class VideoSourceKindList
{
public VideoSourceKindList()
{
InitializeComponent();
}
void OnVideoSourceReSelect(object Sender, MouseButtonEventArgs E)
{
if (Sender is ListViewItem item && item.IsSelected)
{
if (item.DataContext is IVideoSourceProvider provider)
{
provider.OnSelect();
}
}
}
}
}
================================================
FILE: src/Captura/Models/AudioPlayer.cs
================================================
using System;
using System.IO;
using System.Windows.Media;
namespace Captura.Audio
{
// ReSharper disable once ClassNeverInstantiated.Global
public class AudioPlayer : IAudioPlayer
{
readonly SoundSettings _settings;
readonly MediaPlayer _mediaPlayer;
public AudioPlayer(SoundSettings Settings)
{
_settings = Settings;
_mediaPlayer = new MediaPlayer();
}
void PlaySound(string Path)
{
if (!File.Exists(Path))
return;
_mediaPlayer.Open(new Uri(Path));
_mediaPlayer.Play();
}
public void Play(SoundKind SoundKind)
{
if (_settings.Items.TryGetValue(SoundKind, out var value))
PlaySound(value);
}
}
}
================================================
FILE: src/Captura/Models/CmdOptions.cs
================================================
using CommandLine;
namespace Captura
{
///
/// Command-line options for the WPF app.
///
// ReSharper disable once ClassNeverInstantiated.Global
public class CmdOptions
{
[Option("reset", HelpText = "Reset all setting values to default.")]
public bool Reset { get; set; }
[Option("tray", HelpText = "Start minimized into the system tray.")]
public bool Tray { get; set; }
[Option("no-hotkey", HelpText = "Do not Register hotkeys.")]
public bool NoHotkeys { get; set; }
[Option("no-persist", HelpText = "Do not save any changes in settings.")]
public bool NoPersist { get; set; }
[Option("settings", HelpText = "Settings Directory")]
public string Settings { get; set; }
}
}
================================================
FILE: src/Captura/Models/Dpi.cs
================================================
using System.Windows.Interop;
namespace Captura
{
///
/// Provides DPI scaling factor.
/// Only needs to be used when dealing with WPF since their sizes are specified in Device Independent Pixels.
///
// ReSharper disable once ClassNeverInstantiated.Global
public class Dpi
{
static Dpi()
{
using var src = new HwndSource(new HwndSourceParameters());
if (src.CompositionTarget != null)
{
var matrix = src.CompositionTarget.TransformToDevice;
X = (float) matrix.M11;
Y = (float) matrix.M22;
}
}
public static float X { get; }
public static float Y { get; }
}
}
================================================
FILE: src/Captura/Models/FFmpegViewsProvider.cs
================================================
using System.Windows;
using Captura.Audio;
using Captura.Loc;
using Captura.Models;
using Captura.Views;
using FirstFloor.ModernUI.Windows.Controls;
namespace Captura.FFmpeg
{
// ReSharper disable once ClassNeverInstantiated.Global
public class FFmpegViewsProvider : IFFmpegViewsProvider
{
readonly ILocalizationProvider _loc;
readonly IAudioPlayer _audioPlayer;
readonly FFmpegSettings _settings;
readonly IDialogService _dialogService;
public FFmpegViewsProvider(ILocalizationProvider Loc,
IAudioPlayer AudioPlayer,
FFmpegSettings Settings,
IDialogService DialogService)
{
_loc = Loc;
_audioPlayer = AudioPlayer;
_settings = Settings;
_dialogService = DialogService;
}
public void ShowLogs()
{
SettingsWindow.ShowFFmpegLogs();
}
public void ShowUnavailable()
{
Application.Current.Dispatcher.Invoke(() =>
{
var dialog = new ModernDialog
{
Title = "FFmpeg Unavailable",
Content = "FFmpeg was not found on your system.\n\nSelect FFmpeg Folder if you alrady have FFmpeg on your system, else Download FFmpeg."
};
// Yes -> Select FFmpeg Folder
dialog.YesButton.Content = _loc.SelectFFmpegFolder;
dialog.YesButton.Click += (S, E) => PickFolder();
// No -> Download FFmpeg
dialog.NoButton.Content = _loc.DownloadFFmpeg;
dialog.NoButton.Click += (S, E) => ShowDownloader();
dialog.CancelButton.Content = "Cancel";
dialog.Buttons = new[] { dialog.YesButton, dialog.NoButton, dialog.CancelButton };
_audioPlayer.Play(SoundKind.Error);
dialog.ShowDialog();
});
}
public void ShowDownloader()
{
FFmpegDownloaderWindow.ShowInstance();
}
public void PickFolder()
{
var folder = _dialogService.PickFolder(_settings.GetFolderPath(), _loc.SelectFFmpegFolder);
if (!string.IsNullOrWhiteSpace(folder))
_settings.FolderPath = folder;
}
}
}
================================================
FILE: src/Captura/Models/HitType.cs
================================================
namespace Captura
{
enum HitType
{
None,
Body,
Left,
Right,
Top,
Bottom,
UpperLeft,
UpperRight,
LowerRight,
LowerLeft
}
}
================================================
FILE: src/Captura/Models/HotkeyListener.cs
================================================
using System;
using System.Windows.Interop;
namespace Captura.Hotkeys
{
public class HotkeyListener : IHotkeyListener
{
const int WindowsMessageHotkey = 786;
public HotkeyListener()
{
ComponentDispatcher.ThreadPreprocessMessage += (ref MSG Message, ref bool Handled) =>
{
if (Message.message == WindowsMessageHotkey)
{
var id = Message.wParam.ToInt32();
HotkeyReceived?.Invoke(id);
}
};
}
public event Action HotkeyReceived;
}
}
================================================
FILE: src/Captura/Models/HotkeySetup.cs
================================================
using System.Linq;
using Captura.Models;
namespace Captura.Hotkeys
{
// ReSharper disable once ClassNeverInstantiated.Global
public class HotkeySetup
{
readonly HotKeyManager _hotKeyManager;
readonly IMessageProvider _messageProvider;
public HotkeySetup(HotKeyManager HotKeyManager,
IMessageProvider MessageProvider)
{
_hotKeyManager = HotKeyManager;
_messageProvider = MessageProvider;
}
public void Setup()
{
_hotKeyManager.RegisterAll();
}
public void ShowUnregistered()
{
var notRegisteredOnStartup = _hotKeyManager
.Hotkeys
.Where(M => M.IsActive && !M.IsRegistered)
.ToArray();
if (notRegisteredOnStartup.Length <= 0)
return;
var message = "The following Hotkeys could not be registered:\nOther programs might be using them.\nTry changing them.\n\n";
foreach (var hotkey in notRegisteredOnStartup)
{
message += $"{hotkey.Service.Description} - {hotkey}\n\n";
}
_messageProvider.ShowError(message, "Failed to Register Hotkeys");
}
}
}
================================================
FILE: src/Captura/Models/HotkeyViewActor.cs
================================================
namespace Captura.Hotkeys
{
public class HotkeyViewActor : IHotkeyActor
{
public void Act(ServiceName Service)
{
switch (Service)
{
case ServiceName.ShowMainWindow:
MainWindow.Instance.ShowAndFocus();
break;
}
}
}
}
================================================
FILE: src/Captura/Models/IRemoveRequester.cs
================================================
using System;
namespace Captura
{
public interface IRemoveRequester
{
event Action RemoveRequested;
}
}
================================================
FILE: src/Captura/Models/MainModule.cs
================================================
using System;
using Captura.Audio;
using Captura.FFmpeg;
using Captura.Hotkeys;
using Captura.Models;
using Captura.Video;
using Captura.ViewModels;
using Hardcodet.Wpf.TaskbarNotification;
namespace Captura
{
public class MainModule : IModule
{
public void OnLoad(IBinder Binder)
{
// Use singleton to ensure the same instance is used every time.
Binder.Bind();
Binder.Bind();
Binder.Bind();
Binder.Bind();
Binder.Bind();
Binder.Bind();
Binder.Bind();
Binder.Bind();
Binder.Bind();
Binder.BindSingleton();
Binder.BindSingleton();
Binder.BindSingleton();
// Bind as a Function to ensure the UI objects are referenced only after they have been created.
Binder.Bind>(() => () => MainWindow.Instance.SystemTray);
Binder.Bind(() => new MainWindowProvider(() => MainWindow.Instance));
}
public void Dispose() { }
}
}
================================================
FILE: src/Captura/Models/MainWindowHelper.cs
================================================
using Captura.Hotkeys;
using Captura.ViewModels;
namespace Captura
{
// ReSharper disable once ClassNeverInstantiated.Global
class MainWindowHelper
{
public MainWindowHelper(MainViewModel MainViewModel,
HotkeySetup HotkeySetup,
TimerModel TimerModel,
Settings Settings,
RecordingViewModel RecordingViewModel)
{
this.MainViewModel = MainViewModel;
this.HotkeySetup = HotkeySetup;
this.TimerModel = TimerModel;
this.Settings = Settings;
this.RecordingViewModel = RecordingViewModel;
}
public MainViewModel MainViewModel { get; }
public HotkeySetup HotkeySetup { get; }
public TimerModel TimerModel { get; }
public Settings Settings { get; }
public RecordingViewModel RecordingViewModel { get; }
}
}
================================================
FILE: src/Captura/Models/MainWindowProvider.cs
================================================
using System;
using System.Diagnostics;
using System.Windows;
using Captura.Views;
namespace Captura.Models
{
class MainWindowProvider : IMainWindow
{
readonly Func _window;
public MainWindowProvider(Func Window)
{
_window = Window;
}
public bool IsVisible
{
get => _window.Invoke().IsVisible;
set
{
if (value)
_window.Invoke().Show();
else _window.Invoke().Hide();
}
}
public bool IsMinimized
{
get => _window.Invoke().WindowState == WindowState.Minimized;
set => _window.Invoke().WindowState = value ? WindowState.Minimized : WindowState.Normal;
}
public void EditImage(string FileName)
{
var settings = ServiceProvider.Get().ScreenShots;
Process.Start(settings.ExternalEditor, $"\"{FileName}\"");
}
public void TrimMedia(string FileName)
{
var win = new TrimmerWindow();
win.Open(FileName);
win.ShowAndFocus();
}
public void UploadToYouTube(string FileName)
{
var win = new YouTubeUploaderWindow();
win.Open(FileName);
win.ShowAndFocus();
}
}
}
================================================
FILE: src/Captura/Models/MessageProvider.cs
================================================
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using Captura.Audio;
using Captura.Loc;
using Captura.Views;
using FirstFloor.ModernUI.Windows.Controls;
namespace Captura.Models
{
// ReSharper disable once ClassNeverInstantiated.Global
public class MessageProvider : IMessageProvider
{
readonly IAudioPlayer _audioPlayer;
readonly ILocalizationProvider _loc;
public MessageProvider(IAudioPlayer AudioPlayer, ILocalizationProvider Loc)
{
_audioPlayer = AudioPlayer;
_loc = Loc;
}
public void ShowError(string Message, string Header = null)
{
Application.Current.Dispatcher.Invoke(() =>
{
var dialog = new ModernDialog
{
Title = _loc.ErrorOccurred,
Content = new StackPanel
{
Children =
{
new TextBlock
{
Text = Header,
Margin = new Thickness(0, 0, 0, 10),
FontSize = 15
},
new ScrollViewer
{
Content = Message,
HorizontalScrollBarVisibility = ScrollBarVisibility.Auto,
Padding = new Thickness(0, 0, 0, 10)
}
}
}
};
dialog.OkButton.Content = _loc.Ok;
dialog.Buttons = new[] { dialog.OkButton };
dialog.BackgroundContent = new Grid
{
Background = new SolidColorBrush(Color.FromArgb(255, 244, 67, 54)),
VerticalAlignment = VerticalAlignment.Top,
Height = 10
};
_audioPlayer.Play(SoundKind.Error);
dialog.ShowDialog();
});
}
public void ShowException(Exception Exception, string Message, bool Blocking = false)
{
Application.Current.Dispatcher.Invoke(() =>
{
var win = new ErrorWindow(Exception, Message);
_audioPlayer.Play(SoundKind.Error);
if (Blocking)
{
win.ShowDialog();
}
else win.ShowAndFocus();
});
}
public bool ShowYesNo(string Message, string Title)
{
return Application.Current.Dispatcher.Invoke(() =>
{
var dialog = new ModernDialog
{
Title = Title,
Content = new ScrollViewer
{
Content = Message,
HorizontalScrollBarVisibility = ScrollBarVisibility.Auto,
Padding = new Thickness(0, 0, 0, 10)
}
};
var result = false;
dialog.YesButton.Content = _loc.Yes;
dialog.YesButton.Click += (S, E) => result = true;
dialog.NoButton.Content = _loc.No;
dialog.Buttons = new[] { dialog.YesButton, dialog.NoButton };
dialog.ShowDialog();
return result;
});
}
}
}
================================================
FILE: src/Captura/Models/PreviewWindowService.cs
================================================
using System;
using System.Windows;
using System.Windows.Interop;
using Captura.Windows.DirectX;
using Captura.Windows.Gdi;
using Reactive.Bindings.Extensions;
using SharpDX.Direct3D9;
namespace Captura.Video
{
// ReSharper disable once ClassNeverInstantiated.Global
public class PreviewWindowService : IPreviewWindow
{
D3D9PreviewAssister _d3D9PreviewAssister;
IntPtr _backBufferPtr;
Texture _texture;
readonly VisualSettings _visualSettings;
public void Show()
{
_visualSettings.Expanded = true;
}
public bool IsVisible { get; private set; }
public PreviewWindowService(VisualSettings VisualSettings)
{
_visualSettings = VisualSettings;
VisualSettings.ObserveProperty(M => M.Expanded)
.Subscribe(M => IsVisible = M);
}
IBitmapFrame _lastFrame;
public void Display(IBitmapFrame Frame)
{
if (Frame is RepeatFrame)
return;
if (!IsVisible)
{
Frame.Dispose();
return;
}
var win = MainWindow.Instance;
win.Dispatcher.Invoke(() =>
{
win.DisplayImage.Image = null;
_lastFrame?.Dispose();
_lastFrame = Frame;
Frame = Frame.Unwrap();
switch (Frame)
{
case DrawingFrame drawingFrame:
try
{
// TODO: Preview is not shown during Webcam only recordings
// This check swallows errors
var h = drawingFrame.Bitmap.Height;
if (h == 0)
return;
}
catch { return; }
win.WinFormsHost.Visibility = Visibility.Visible;
win.DisplayImage.Image = drawingFrame.Bitmap;
break;
case Texture2DFrame texture2DFrame:
win.WinFormsHost.Visibility = Visibility.Collapsed;
if (_d3D9PreviewAssister == null)
{
_d3D9PreviewAssister = new D3D9PreviewAssister(ServiceProvider.Get());
_texture = _d3D9PreviewAssister.GetSharedTexture(texture2DFrame.PreviewTexture);
using var surface = _texture.GetSurfaceLevel(0);
_backBufferPtr = surface.NativePointer;
}
Invalidate(_backBufferPtr, texture2DFrame.Width, texture2DFrame.Height);
break;
}
});
}
void Invalidate(IntPtr BackBufferPtr, int Width, int Height)
{
var win = MainWindow.Instance;
win.D3DImage.Lock();
win.D3DImage.SetBackBuffer(D3DResourceType.IDirect3DSurface9, BackBufferPtr);
if (BackBufferPtr != IntPtr.Zero)
win.D3DImage.AddDirtyRect(new Int32Rect(0, 0, Width, Height));
win.D3DImage.Unlock();
}
public void Dispose()
{
var win = MainWindow.Instance;
win.Dispatcher.Invoke(() =>
{
win.DisplayImage.Image = null;
win.WinFormsHost.Visibility = Visibility.Collapsed;
_lastFrame?.Dispose();
_lastFrame = null;
if (_d3D9PreviewAssister != null)
{
Invalidate(IntPtr.Zero, 0, 0);
_texture.Dispose();
_d3D9PreviewAssister.Dispose();
_d3D9PreviewAssister = null;
}
});
}
}
}
================================================
FILE: src/Captura/Models/RegionSelectorProvider.cs
================================================
using System;
using System.Drawing;
using System.Windows;
using Captura.ViewModels;
namespace Captura.Video
{
// ReSharper disable once ClassNeverInstantiated.Global
public class RegionSelectorProvider : IRegionProvider
{
readonly Lazy _regionSelector;
readonly RegionItem _regionItem;
readonly RegionSelectorViewModel _viewModel;
public RegionSelectorProvider(RegionSelectorViewModel ViewModel,
IPlatformServices PlatformServices)
{
_viewModel = ViewModel;
_regionSelector = new Lazy(() => new RegionSelector(ViewModel));
_regionItem = new RegionItem(this, PlatformServices);
}
public bool SelectorVisible
{
get => _regionSelector.Value.Visibility == Visibility.Visible;
set
{
if (value)
_regionSelector.Value.Show();
else _regionSelector.Value.Hide();
}
}
public Rectangle SelectedRegion
{
get => _viewModel.SelectedRegion;
set => _viewModel.SelectedRegion = value;
}
public IVideoItem VideoSource => _regionItem;
public IntPtr Handle => _regionSelector.Value.Handle;
}
}
================================================
FILE: src/Captura/Models/ServiceLocator.cs
================================================
using Captura.Models;
using Captura.MouseKeyHook;
using Captura.ViewModels;
using Captura.Webcam;
// ReSharper disable MemberCanBeMadeStatic.Global
namespace Captura
{
///
/// Used as a Static Resource to inject ViewModels into UI.
///
public class ServiceLocator
{
static ServiceLocator()
{
ServiceProvider.LoadModule(new MainModule());
}
public WebcamPage WebcamPage => ServiceProvider.Get();
public MainViewModel MainViewModel => ServiceProvider.Get();
public RecentViewModel RecentViewModel => ServiceProvider.Get();
public ScreenShotViewModel ScreenShotViewModel => ServiceProvider.Get();
public AboutViewModel AboutViewModel => ServiceProvider.Get();
public RegionSelectorViewModel RegionSelectorViewModel => ServiceProvider.Get();
public FFmpegDownloadViewModel FFmpegDownloadViewModel => ServiceProvider.Get();
public FFmpegLogViewModel FFmpegLog => ServiceProvider.Get();
public IFpsManager FpsManager => ServiceProvider.Get();
public FFmpegCodecsViewModel FFmpegCodecsViewModel => ServiceProvider.Get();
public ProxySettingsViewModel ProxySettingsViewModel => ServiceProvider.Get();
public LicensesViewModel LicensesViewModel => ServiceProvider.Get();
public CrashLogsViewModel CrashLogsViewModel => ServiceProvider.Get();
public FileNameFormatViewModel FileNameFormatViewModel => ServiceProvider.Get();
public YouTubeUploaderViewModel YouTubeUploaderViewModel => ServiceProvider.Get();
public SoundsViewModel SoundsViewModel => ServiceProvider.Get();
public KeymapViewModel Keymap => ServiceProvider.Get();
public EditorWriter EditorWriter => ServiceProvider.Get();
public HotkeysViewModel HotkeysViewModel => ServiceProvider.Get();
public IIconSet Icons => ServiceProvider.Get();
public UpdateCheckerViewModel UpdateCheckerViewModel => ServiceProvider.Get();
public CustomImageOverlaysViewModel CustomImageOverlays => ServiceProvider.Get();
public CustomOverlaysViewModel CustomOverlays => ServiceProvider.Get();
public CensorOverlaysViewModel CensorOverlays => ServiceProvider.Get();
public ViewConditionsModel ViewConditions => ServiceProvider.Get();
public TimerModel TimerModel => ServiceProvider.Get();
public AudioSourceViewModel AudioSource => ServiceProvider.Get();
public WebcamModel WebcamModel => ServiceProvider.Get();
public VideoWritersViewModel VideoWritersViewModel => ServiceProvider.Get();
public VideoSourcesViewModel VideoSourcesViewModel => ServiceProvider.Get();
public RecordingViewModel RecordingViewModel => ServiceProvider.Get();
}
}
================================================
FILE: src/Captura/Models/SingleInstanceManager.cs
================================================
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Captura.Models
{
public static class SingleInstanceManager
{
// ReSharper disable once NotAccessedField.Local
static Mutex _mutex;
// ReSharper disable once NotAccessedField.Local
static Task _task;
// Mutex allows us to know whether another instance is already open
const string MutexId = "captura-mutex-304bae7c-e520-4bfe-a308-c99476062091";
// EventWaitHandle allows us to communicate to a already running instance
const string EventWaitHandleId = "captura-wait-304bae7c-e520-4bfe-a308-c99476062091";
public static void SingleInstanceCheck()
{
_mutex = new Mutex(true, MutexId, out var createdNew);
if (!createdNew)
{
// Bring the already running instance to the foreground
var handle = CreateWaitHandle();
handle.Set();
// Exit duplicate instance
Environment.Exit(0);
}
}
static EventWaitHandle CreateWaitHandle()
{
return new EventWaitHandle(false, EventResetMode.AutoReset, EventWaitHandleId);
}
public static void StartListening(Action Callback)
{
// First instance listens for events from other instances in a loop
_task = Task.Run(() =>
{
var waitHandle = CreateWaitHandle();
while (true)
{
waitHandle.WaitOne();
// Callback should bring first instance to foreground
Callback.Invoke();
}
});
}
}
}
================================================
FILE: src/Captura/Models/SystemInfo.cs
================================================
using System;
using System.Management;
using System.Text;
using Captura.Audio;
using Captura.ViewModels;
using Captura.Webcam;
namespace Captura.Models
{
public static class SystemInfo
{
public static string GetInfo()
{
var sb = new StringBuilder();
sb.AppendLine($"{nameof(Captura)} v{AboutViewModel.Version}");
sb.AppendLine(OsInfo());
sb.AppendLine($"{(Environment.Is64BitOperatingSystem ? 64 : 32)}-bit OS");
sb.AppendLine($"{(Environment.Is64BitProcess ? 64 : 32)}-bit Process");
sb.AppendLine($"{Environment.ProcessorCount} processor(s)");
sb.AppendLine();
sb.Append(CpuInfo());
sb.Append(RamInfo());
sb.Append(GpuInfo());
var platformServices = ServiceProvider.Get();
try
{
sb.AppendLine($"Desktop: {platformServices.DesktopRectangle}");
foreach (var screen in platformServices.EnumerateScreens())
{
sb.AppendLine($"Screen: {screen.DeviceName}: {screen.Rectangle}");
}
}
catch
{
sb.AppendLine("Unable to get screen dimensions");
}
var audioSource = ServiceProvider.Get();
try
{
foreach (var mic in audioSource.Microphones)
{
sb.AppendLine($"Mic: {mic.Name}");
}
foreach (var speaker in audioSource.Speakers)
{
sb.AppendLine($"Speaker: {speaker.Name}");
}
}
catch
{
sb.AppendLine("Unable to get audio devices");
}
try
{
var webcamSource = ServiceProvider.Get();
foreach (var webcam in webcamSource.GetSources())
{
sb.AppendLine($"Webcam: {webcam.Name}");
}
}
catch
{
sb.AppendLine("Unable to get webcams");
}
sb.AppendLine();
return sb.ToString();
}
static string DeviceInformation(string ClassName, string[] Properties)
{
var sb = new StringBuilder();
foreach (var instance in new ManagementClass(ClassName).GetInstances())
{
foreach (var property in Properties)
{
try
{
sb.AppendLine($"{property}: {instance.Properties[property].Value}");
}
catch
{
//Add codes to manage more information
}
}
sb.AppendLine();
}
return sb.ToString();
}
static string CpuInfo()
{
return DeviceInformation("Win32_Processor", new[]
{
"Name",
"NumberOfCores",
"NumberOfLogicalProcessors"
});
}
static string GpuInfo()
{
return DeviceInformation("Win32_VideoController", new[]
{
"Name",
"AdapterRAM"
});
}
static string OsInfo()
{
var devInfo = DeviceInformation("Win32_OperatingSystem", new[]
{
"Name"
});
return $"OS: {devInfo.Split(':')[1].Trim()}";
}
static string RamInfo()
{
return DeviceInformation("Win32_PhysicalMemory", new[]
{
"Name",
"Capacity"
});
}
}
}
================================================
FILE: src/Captura/Models/SystemTray.cs
================================================
using Hardcodet.Wpf.TaskbarNotification;
using System;
using System.Windows.Controls.Primitives;
using Captura.Audio;
namespace Captura.Models
{
// ReSharper disable once ClassNeverInstantiated.Global
class SystemTray : ISystemTray, IDisposable
{
bool _first = true;
///
/// Using a Function ensures that the object is used only after it is initialised.
///
readonly Func _trayIcon;
readonly Settings _settings;
readonly IAudioPlayer _audioPlayer;
readonly NotificationStack _notificationStack = new NotificationStack();
public SystemTray(Func TaskbarIcon, Settings Settings, IAudioPlayer AudioPlayer)
{
_trayIcon = TaskbarIcon;
_settings = Settings;
_audioPlayer = AudioPlayer;
_notificationStack.Opacity = 0;
}
public void HideNotification()
{
_notificationStack.Hide();
}
void Show()
{
var trayIcon = _trayIcon.Invoke();
if (trayIcon != null && _first)
{
trayIcon.ShowCustomBalloon(_notificationStack, PopupAnimation.None, null);
_first = false;
}
_audioPlayer.Play(SoundKind.Notification);
_notificationStack.Show();
}
public void ShowScreenShotNotification(string FilePath)
{
if (!_settings.Tray.ShowNotifications)
return;
_notificationStack.Add(new ScreenShotBalloon(FilePath));
Show();
}
public void ShowNotification(INotification Notification)
{
if (!_settings.Tray.ShowNotifications)
return;
_notificationStack.Add(new NotificationBalloon(Notification));
Show();
}
public void Dispose()
{
_trayIcon.Invoke()?.Dispose();
}
}
}
================================================
FILE: src/Captura/Models/VideoSourcePicker.cs
================================================
using System;
using System.Drawing;
namespace Captura.Video
{
// ReSharper disable once ClassNeverInstantiated.Global
public class VideoSourcePicker : IVideoSourcePicker
{
public IWindow PickWindow(Predicate Filter = null)
{
return VideoSourcePickerWindow.PickWindow(Filter);
}
public IScreen PickScreen()
{
return ScreenPickerWindow.PickScreen();
}
public Rectangle? PickRegion()
{
return RegionPickerWindow.PickRegion();
}
}
}
================================================
FILE: src/Captura/Pages/AboutPage.xaml
================================================
© Mathew Sachin
================================================
FILE: src/Captura/Pages/AboutPage.xaml.cs
================================================
using System.Windows;
using Captura.Views;
using Microsoft.Win32;
namespace Captura
{
public partial class AboutPage
{
void OpenAudioVideoTrimmer(object Sender, RoutedEventArgs E)
{
new TrimmerWindow().ShowAndFocus();
}
async void UploadToImgur(object Sender, RoutedEventArgs E)
{
var ofd = new OpenFileDialog
{
Filter = "Image Files|*.png;*.jpg;*.jpeg;*.bmp;*.wmp;*.tiff",
CheckFileExists = true,
CheckPathExists = true
};
if (ofd.ShowDialog().GetValueOrDefault())
{
var imgSystem = ServiceProvider.Get();
using var img = imgSystem.LoadBitmap(ofd.FileName);
await img.UploadImage();
}
}
}
}
================================================
FILE: src/Captura/Pages/AudioPage.xaml
================================================
================================================
FILE: src/Captura/Pages/AudioPage.xaml.cs
================================================
using Captura.Models;
namespace Captura
{
public partial class AudioPage
{
public AudioPage()
{
IsVisibleChanged += (S, E) =>
{
var audioSourceVm = ServiceProvider.Get();
audioSourceVm.ListeningPeakLevel = IsVisible;
};
InitializeComponent();
}
}
}
================================================
FILE: src/Captura/Pages/CensorOverlaysPage.xaml
================================================
================================================
FILE: src/Captura/Pages/CensorOverlaysPage.xaml.cs
================================================
namespace Captura
{
public partial class CensorOverlaysPage
{
public CensorOverlaysPage()
{
InitializeComponent();
}
}
}
================================================
FILE: src/Captura/Pages/CrashLogsPage.xaml
================================================
================================================
FILE: src/Captura/Pages/CrashLogsPage.xaml.cs
================================================
namespace Captura.Views
{
public partial class CrashLogsPage
{
public CrashLogsPage()
{
InitializeComponent();
}
}
}
================================================
FILE: src/Captura/Pages/FFmpegCodecsPage.xaml
================================================
================================================
FILE: src/Captura/Pages/FFmpegCodecsPage.xaml.cs
================================================
namespace Captura
{
public partial class FFmpegCodecsPage
{
public FFmpegCodecsPage()
{
InitializeComponent();
}
}
}
================================================
FILE: src/Captura/Pages/FFmpegLogsPage.xaml
================================================
================================================
FILE: src/Captura/Pages/FFmpegLogsPage.xaml.cs
================================================
namespace Captura
{
public partial class FFmpegLogsPage
{
public FFmpegLogsPage()
{
InitializeComponent();
}
}
}
================================================
FILE: src/Captura/Pages/FFmpegPage.xaml
================================================
================================================
FILE: src/Captura/Pages/FFmpegPage.xaml.cs
================================================
using System.Windows;
using System.Windows.Input;
using Captura.ViewModels;
using Captura.Views;
namespace Captura
{
public partial class FFmpegPage
{
void FFmpegDownload(object Sender, RoutedEventArgs E)
{
FFmpegDownloaderWindow.ShowInstance();
}
void SelectFFmpegFolder(object Sender, MouseButtonEventArgs E)
{
if (DataContext is MainViewModel vm)
{
vm.SelectFFmpegFolderCommand.ExecuteIfCan();
}
}
}
}
================================================
FILE: src/Captura/Pages/FileNameFormatPage.xaml
================================================
================================================
FILE: src/Captura/Pages/FileNameFormatPage.xaml.cs
================================================
namespace Captura
{
public partial class FileNameFormatPage
{
public FileNameFormatPage()
{
InitializeComponent();
}
}
}
================================================
FILE: src/Captura/Pages/HomePage.xaml
================================================
================================================
FILE: src/Captura/Pages/HomePage.xaml.cs
================================================
namespace Captura
{
public partial class HomePage
{
public HomePage()
{
InitializeComponent();
}
}
}
================================================
FILE: src/Captura/Pages/HotkeysPage.xaml
================================================
================================================
FILE: src/Captura/Pages/HotkeysPage.xaml.cs
================================================
namespace Captura.Views
{
public partial class HotkeysPage
{
public HotkeysPage()
{
InitializeComponent();
}
}
}
================================================
FILE: src/Captura/Pages/ImageOverlaysPage.xaml
================================================
================================================
FILE: src/Captura/Pages/ImageOverlaysPage.xaml.cs
================================================
namespace Captura
{
public partial class ImageOverlaysPage
{
public ImageOverlaysPage()
{
InitializeComponent();
}
}
}
================================================
FILE: src/Captura/Pages/InterfacePage.xaml
================================================
================================================
FILE: src/Captura/Pages/InterfacePage.xaml.cs
================================================
using System.Windows;
using System.Windows.Media;
using Captura.ViewModels;
using FirstFloor.ModernUI.Presentation;
namespace Captura
{
public partial class InterfacePage
{
void SelectedAccentColorChanged(object Sender, RoutedPropertyChangedEventArgs E)
{
if (E.NewValue != null && DataContext is ViewModelBase vm)
{
AppearanceManager.Current.AccentColor = E.NewValue.Value;
vm.Settings.UI.AccentColor = E.NewValue.Value.ToString();
}
}
void DarkThemeClick(object Sender, RoutedEventArgs E)
{
if (DataContext is ViewModelBase vm)
{
AppearanceManager.Current.ThemeSource = vm.Settings.UI.DarkTheme
? AppearanceManager.DarkThemeSource
: AppearanceManager.LightThemeSource;
}
}
}
}
================================================
FILE: src/Captura/Pages/KeystrokesPage.xaml
================================================
================================================
FILE: src/Captura/Pages/KeystrokesPage.xaml.cs
================================================
namespace Captura
{
public partial class KeystrokesPage
{
public KeystrokesPage()
{
InitializeComponent();
}
}
}
================================================
FILE: src/Captura/Pages/LicensesPage.xaml
================================================
================================================
FILE: src/Captura/Pages/LicensesPage.xaml.cs
================================================
namespace Captura.Views
{
public partial class LicensesPage
{
public LicensesPage()
{
InitializeComponent();
}
}
}
================================================
FILE: src/Captura/Pages/MainPage.xaml
================================================
================================================
FILE: src/Captura/Pages/MainPage.xaml.cs
================================================
namespace Captura
{
public partial class MainPage
{
}
}
================================================
FILE: src/Captura/Pages/MouseOverlayPage.xaml
================================================
================================================
FILE: src/Captura/Pages/MouseOverlayPage.xaml.cs
================================================
namespace Captura
{
public partial class MouseOverlayPage
{
public MouseOverlayPage()
{
InitializeComponent();
}
}
}
================================================
FILE: src/Captura/Pages/OverlayPage.xaml
================================================
================================================
FILE: src/Captura/Pages/OverlayPage.xaml.cs
================================================
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Reactive.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using Captura.MouseKeyHook;
using Captura.Video;
using Captura.ViewModels;
using Reactive.Bindings;
using Reactive.Bindings.Extensions;
using Color = System.Windows.Media.Color;
using Point = System.Windows.Point;
namespace Captura
{
public partial class OverlayPage
{
OverlayPage()
{
InitializeComponent();
Loaded += OnLoaded;
Unloaded += (S, E) => Grid.Children.Clear();
}
static readonly Lazy LazyInstance = new Lazy(() => new OverlayPage());
public static OverlayPage Instance => LazyInstance.Value;
void AddToGrid(LayerFrame Frame, bool CanResize)
{
Grid.Children.Add(Frame);
Panel.SetZIndex(Frame, 0);
var layer = AdornerLayer.GetAdornerLayer(Frame);
var adorner = new OverlayPositionAdorner(Frame, CanResize);
layer.Add(adorner);
adorner.PositionUpdated += Frame.RaisePositionChanged;
}
LayerFrame Generate(PositionedOverlaySettings Settings, string Text, Color BackgroundColor)
{
var control = new LayerFrame
{
Border =
{
Background = new SolidColorBrush(BackgroundColor)
},
HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Top,
Label =
{
Content = Text,
Foreground = new SolidColorBrush(Colors.White)
}
};
var vm = new PositionOverlayReactor(Settings);
control.BindOne(HorizontalAlignmentProperty, vm.HAlignment);
control.BindOne(VerticalAlignmentProperty, vm.VAlignment);
control.BindOne(MarginProperty, vm.Margin);
control.PositionUpdated += Rect =>
{
Settings.X = (int)Rect.X;
Settings.Y = (int)Rect.Y;
};
return control;
}
LayerFrame Image(ImageOverlaySettings Settings, string Text)
{
var control = Generate(Settings, Text, Colors.Brown);
var vm = new ImageOverlayReactor(Settings);
control.Bind(WidthProperty, vm.Width);
control.Bind(HeightProperty, vm.Height);
control.BindOne(OpacityProperty, vm.Opacity);
return control;
}
LayerFrame Text(TextOverlaySettings Settings, string Text)
{
var control = Generate(Settings, Text, Settings.BackgroundColor.ToWpfColor());
var vm = new TextOverlayReactor(Settings);
control.Label.BindOne(FontFamilyProperty, vm.FontFamily);
control.Label.BindOne(FontSizeProperty, vm.FontSize);
// Border.PaddingProperty is different from PaddingProperty
control.Border.BindOne(Border.PaddingProperty, vm.Padding);
control.Label.BindOne(ForegroundProperty, vm.Foreground);
control.Border.BindOne(BackgroundProperty, vm.Background);
control.Border.BindOne(Border.BorderThicknessProperty, vm.BorderThickness);
control.Border.BindOne(Border.BorderBrushProperty, vm.BorderBrush);
control.Border.BindOne(Border.CornerRadiusProperty, vm.CornerRadius);
return control;
}
LayerFrame Censor(CensorOverlaySettings Settings)
{
var control = Generate(Settings, "Censored", Colors.Black);
var vm = new CensorOverlayReactor(Settings);
control.Bind(WidthProperty, vm.Width);
control.Bind(HeightProperty, vm.Height);
control.BindOne(VisibilityProperty, vm.Visible);
return control;
}
LayerFrame Keystrokes(KeystrokesSettings Settings)
{
var control = Text(Settings, "Keystrokes");
var visibilityProp = Settings
.ObserveProperty(M => M.SeparateTextFile)
.Select(M => M ? Visibility.Collapsed : Visibility.Visible)
.ToReadOnlyReactivePropertySlim();
control.BindOne(VisibilityProperty, visibilityProp);
return control;
}
readonly List _textOverlays = new List();
readonly List _imageOverlays = new List();
readonly List _censorOverlays = new List();
void UpdateOverlays(IEnumerable Settings,
List LayerFrames,
Func LayerFrameGenerator,
bool CanResize,
int ZIndex)
{
foreach (var layerFrame in LayerFrames)
{
Grid.Children.Remove(layerFrame);
}
LayerFrames.Clear();
LayerFrames.AddRange(Settings.Select(LayerFrameGenerator));
foreach (var layerFrame in LayerFrames)
{
AddToGrid(layerFrame, CanResize);
Panel.SetZIndex(layerFrame, ZIndex);
}
}
void UpdateCensorOverlays(IEnumerable Settings)
{
UpdateOverlays(Settings, _censorOverlays, Censor, true, -1);
}
void UpdateTextOverlays(IEnumerable Settings)
{
UpdateOverlays(Settings, _textOverlays, Setting =>
{
var control = Text(Setting, Setting.Text);
var visibilityProp = Setting
.ObserveProperty(M => M.Display)
.Select(M => M ? Visibility.Visible : Visibility.Collapsed)
.ToReadOnlyReactivePropertySlim();
control.BindOne(VisibilityProperty, visibilityProp);
var textProp = Setting
.ObserveProperty(M => M.Text)
.ToReadOnlyReactivePropertySlim();
control.Label.BindOne(ContentProperty, textProp);
return control;
}, false, 1);
}
void UpdateImageOverlays(IEnumerable Settings)
{
UpdateOverlays(Settings, _imageOverlays, Setting =>
{
var control = Image(Setting, Setting.Source);
var img = new Image
{
Stretch = Stretch.Fill
};
control.Label.Content = img;
var visibilityProp = Setting
.ObserveProperty(M => M.Display)
.Select(M => M ? Visibility.Visible : Visibility.Collapsed)
.ToReadOnlyReactivePropertySlim();
control.BindOne(VisibilityProperty, visibilityProp);
var srcProp = Setting
.ObserveProperty(M => M.Source)
.ToReadOnlyReactivePropertySlim();
img.BindOne(System.Windows.Controls.Image.SourceProperty, srcProp);
return control;
}, true, 2);
}
async void OnLoaded(object Sender, RoutedEventArgs RoutedEventArgs)
{
await UpdateBackground();
PlaceOverlays();
UpdateScale();
}
async Task UpdateBackground()
{
Img.Source = await WpfExtensions.GetBackground();
}
void UpdateScale()
{
if (Img.Source == null)
return;
var scaleX = Img.ActualWidth / Img.Source.Width;
var scaleY = Img.ActualHeight / Img.Source.Height;
Scale.ScaleX = scaleX / Dpi.X;
Scale.ScaleY = scaleY / Dpi.Y;
}
void PlaceOverlays()
{
var settings = ServiceProvider.Get();
var censorOverlayVm = ServiceProvider.Get();
UpdateCensorOverlays(censorOverlayVm.Collection);
(censorOverlayVm.Collection as INotifyCollectionChanged).CollectionChanged += (S, E) => UpdateCensorOverlays(censorOverlayVm.Collection);
PrepareMousePointer(settings.MousePointerOverlay);
PrepareMouseClick(settings.Clicks);
var keystrokes = Keystrokes(settings.Keystrokes);
AddToGrid(keystrokes, false);
var elapsed = Text(settings.Elapsed, "00:00:00");
AddToGrid(elapsed, false);
var textOverlayVm = ServiceProvider.Get();
UpdateTextOverlays(textOverlayVm.Collection);
(textOverlayVm.Collection as INotifyCollectionChanged).CollectionChanged += (S, E) => UpdateTextOverlays(textOverlayVm.Collection);
var imgOverlayVm = ServiceProvider.Get();
UpdateImageOverlays(imgOverlayVm.Collection);
(imgOverlayVm.Collection as INotifyCollectionChanged).CollectionChanged += (S, E) => UpdateImageOverlays(imgOverlayVm.Collection);
}
void PrepareMouseClick(MouseClickSettings Settings)
{
void Update()
{
var d = (Settings.Radius + Settings.BorderThickness) * 2;
MouseClick.Width = MouseClick.Height = d;
MouseClick.StrokeThickness = Settings.BorderThickness;
MouseClick.Stroke = new SolidColorBrush(Settings.BorderColor.ToWpfColor());
}
Update();
Settings.PropertyChanged += (S, E) => Dispatcher.Invoke(Update);
}
void PrepareMousePointer(MouseOverlaySettings Settings)
{
void Update()
{
var d = (Settings.Radius + Settings.BorderThickness) * 2;
MousePointer.Width = MousePointer.Height = d;
MousePointer.StrokeThickness = Settings.BorderThickness;
MousePointer.Stroke = new SolidColorBrush(Settings.BorderColor.ToWpfColor());
MousePointer.Fill = new SolidColorBrush(Settings.Color.ToWpfColor());
}
Update();
Settings.PropertyChanged += (S, E) => Dispatcher.Invoke(Update);
}
void OverlayWindow_OnSizeChanged(object Sender, SizeChangedEventArgs E)
{
UpdateScale();
}
static Color GetClickColor(MouseButton Button)
{
var settings = ServiceProvider.Get();
switch (Button)
{
case MouseButton.Middle:
return settings.Clicks.MiddleClickColor.ToWpfColor();
case MouseButton.Right:
return settings.Clicks.RightClickColor.ToWpfColor();
default:
return settings.Clicks.Color.ToWpfColor();
}
}
bool _dragging;
void UpdateMouseClickPosition(Point Position)
{
MouseClick.Margin = new Thickness(Position.X - MouseClick.ActualWidth / 2, Position.Y - MouseClick.ActualHeight / 2, 0, 0);
}
void UIElement_OnMouseDown(object Sender, MouseButtonEventArgs E)
{
_dragging = true;
UpdateMouseClickPosition(E.GetPosition(Grid));
MouseClick.Fill = new SolidColorBrush(GetClickColor(E.ChangedButton));
MouseClick.BeginAnimation(OpacityProperty, new DoubleAnimation(1, new Duration(TimeSpan.FromMilliseconds(200))));
}
void MouseClickEnd()
{
MouseClick.BeginAnimation(OpacityProperty, new DoubleAnimation(0, new Duration(TimeSpan.FromMilliseconds(300))));
_dragging = false;
}
void UIElement_OnMouseUp(object Sender, MouseButtonEventArgs E)
{
MouseClickEnd();
}
bool IsOutsideGrid(Point Point)
{
return Point.X <= 0 || Point.Y <= 0
|| Point.X + MouseClick.ActualWidth / 2 >= Grid.ActualWidth
|| Point.Y + MouseClick.ActualHeight / 2 >= Grid.ActualHeight;
}
void UIElement_OnMouseMove(object Sender, MouseEventArgs E)
{
if (ServiceProvider.Get().MousePointerOverlay.Display)
MousePointer.Visibility = Visibility.Visible;
var position = E.GetPosition(Grid);
if (IsOutsideGrid(position))
{
MousePointer.Visibility = Visibility.Collapsed;
return;
}
if (_dragging)
{
UpdateMouseClickPosition(position);
}
position.X -= MouseClick.ActualWidth / 2;
position.Y -= MouseClick.ActualHeight / 2;
MousePointer.Margin = new Thickness(position.X, position.Y, 0, 0);
}
void UIElement_OnMouseLeave(object Sender, MouseEventArgs E)
{
MouseClickEnd();
MousePointer.Visibility = Visibility.Collapsed;
}
}
}
================================================
FILE: src/Captura/Pages/ProxyPage.xaml
================================================
================================================
FILE: src/Captura/Pages/ProxyPage.xaml.cs
================================================
namespace Captura.Views
{
public partial class ProxyPage
{
}
}
================================================
FILE: src/Captura/Pages/RecentPage.xaml
================================================
================================================
FILE: src/Captura/Pages/RecentPage.xaml.cs
================================================
namespace Captura.Views
{
public partial class RecentPage
{
}
}
================================================
FILE: src/Captura/Pages/ScreenShotsPage.xaml
================================================
================================================
FILE: src/Captura/Pages/ScreenShotsPage.xaml.cs
================================================
namespace Captura
{
public partial class ScreenShotsPage
{
public ScreenShotsPage()
{
InitializeComponent();
}
}
}
================================================
FILE: src/Captura/Pages/SettingsPage.xaml
================================================
================================================
FILE: src/Captura/Pages/SettingsPage.xaml.cs
================================================
namespace Captura
{
public partial class SettingsPage
{
public SettingsPage()
{
InitializeComponent();
}
}
}
================================================
FILE: src/Captura/Pages/SoundsPage.xaml
================================================
================================================
FILE: src/Captura/Pages/SoundsPage.xaml.cs
================================================
using System.Windows;
using System.Windows.Input;
using Captura.ViewModels;
namespace Captura
{
public partial class SoundsPage
{
public SoundsPage()
{
InitializeComponent();
}
void SetFile(object Sender, MouseButtonEventArgs E)
{
if (Sender is FrameworkElement element && element.DataContext is SoundsViewModelItem vm)
{
vm.SetCommand.ExecuteIfCan();
}
}
}
}
================================================
FILE: src/Captura/Pages/TextOverlaysPage.xaml
================================================
================================================
FILE: src/Captura/Pages/TextOverlaysPage.xaml.cs
================================================
namespace Captura
{
public partial class TextOverlaysPage
{
public TextOverlaysPage()
{
InitializeComponent();
}
}
}
================================================
FILE: src/Captura/Pages/TrayIconPage.xaml
================================================
================================================
FILE: src/Captura/Pages/TrayIconPage.xaml.cs
================================================
namespace Captura
{
public partial class TrayIconPage
{
public TrayIconPage()
{
InitializeComponent();
}
}
}
================================================
FILE: src/Captura/Pages/VideoPage.xaml
================================================
This Limit option is to prevent system crashes due to using high FPS on full screen or large region sizes.
Use GDI instead of DesktopDuplication
================================================
FILE: src/Captura/Pages/VideoPage.xaml.cs
================================================
namespace Captura
{
public partial class VideoPage
{
public VideoPage()
{
InitializeComponent();
}
}
}
================================================
FILE: src/Captura/Pages/WebcamPage.xaml
================================================
================================================
FILE: src/Captura/Pages/WebcamPage.xaml.cs
================================================
using System;
using System.Drawing;
using System.Reactive.Linq;
using WSize = System.Windows.Size;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Interop;
using Captura.ViewModels;
using Captura.Webcam;
using Captura.Windows.Gdi;
using Reactive.Bindings;
using Reactive.Bindings.Extensions;
using Xceed.Wpf.Toolkit.Core.Utilities;
namespace Captura
{
public partial class WebcamPage
{
readonly WebcamModel _webcamModel;
readonly ScreenShotModel _screenShotModel;
readonly IPlatformServices _platformServices;
readonly WebcamOverlayReactor _reactor;
public WebcamPage(WebcamModel WebcamModel,
ScreenShotModel ScreenShotModel,
IPlatformServices PlatformServices,
WebcamOverlaySettings WebcamSettings)
{
_webcamModel = WebcamModel;
_screenShotModel = ScreenShotModel;
_platformServices = PlatformServices;
_reactor = new WebcamOverlayReactor(WebcamSettings);
Loaded += OnLoaded;
InitializeComponent();
}
bool _loaded;
async void OnLoaded(object Sender, RoutedEventArgs E)
{
await UpdateBackground();
if (_loaded)
return;
_loaded = true;
var control = PreviewTarget;
control.BindOne(MarginProperty,
_reactor.Location.Select(M => new Thickness(M.X, M.Y, 0, 0)).ToReadOnlyReactivePropertySlim());
control.BindOne(WidthProperty,
_reactor.Size.Select(M => M.Width).ToReadOnlyReactivePropertySlim());
control.BindOne(HeightProperty,
_reactor.Size.Select(M => M.Height).ToReadOnlyReactivePropertySlim());
control.BindOne(OpacityProperty, _reactor.Opacity);
}
async Task UpdateBackground()
{
Img.Source = await WpfExtensions.GetBackground();
}
IReadOnlyReactiveProperty _webcamCapture;
public void SetupPreview()
{
_webcamModel.PreviewClicked += SettingsWindow.ShowWebcamPage;
IsVisibleChanged += (S, E) =>
{
if (IsVisible && _webcamCapture == null)
{
_webcamCapture = _webcamModel.InitCapture();
if (_webcamCapture.Value is { } capture)
{
_reactor.WebcamSize.OnNext(new WSize(capture.Width, capture.Height));
UpdateWebcamPreview();
}
}
else if (!IsVisible && _webcamCapture != null)
{
_webcamModel.ReleaseCapture();
_webcamCapture = null;
}
};
void OnRegionChange()
{
if (!IsVisible)
return;
_reactor.FrameSize.OnNext(new WSize(Img.ActualWidth, Img.ActualHeight));
}
PreviewGrid.LayoutUpdated += (S, E) => OnRegionChange();
_webcamModel
.ObserveProperty(M => M.SelectedCam)
.Subscribe(M => UpdateWebcamPreview());
_reactor.Location
.CombineLatest(_reactor.Size, (M, N) =>
{
UpdateWebcamPreview();
return 0;
})
.Subscribe();
UpdateWebcamPreview();
}
async void CaptureImage_OnClick(object Sender, RoutedEventArgs E)
{
try
{
var img = _webcamCapture.Value?.Capture(GraphicsBitmapLoader.Instance);
await _screenShotModel.SaveScreenShot(img);
}
catch { }
}
Rectangle GetPreviewWindowRect()
{
var parentWindow = VisualTreeHelperEx.FindAncestorByType(this);
var relativePt = PreviewGrid.TranslatePoint(new System.Windows.Point(0, 0), parentWindow);
var position = _reactor.Location.Value;
var size = _reactor.Size.Value;
var rect = new RectangleF((float)(relativePt.X + position.X),
(float)(relativePt.Y + position.Y),
(float)(size.Width),
(float)(size.Height));
return rect.ApplyDpi();
}
void UpdateWebcamPreview()
{
if (PresentationSource.FromVisual(this) is HwndSource source)
{
var win = _platformServices.GetWindow(source.Handle);
var rect = GetPreviewWindowRect();
_webcamCapture?.Value?.UpdatePreview(win, rect);
}
}
}
}
================================================
FILE: src/Captura/Presentation/CroppingAdorner.cs
================================================
using System;
using System.Windows.Shapes;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Point = System.Drawing.Point;
namespace Captura
{
public class CroppingAdorner : Adorner
{
#region Private variables
// Width of the thumbs. I know these really aren't "pixels", but px is still a good mnemonic.
const int CpxThumbWidth = 6;
// PuncturedRect to hold the "Cropping" portion of the adorner
readonly PuncturedRect _prCropMask;
// Canvas to hold the thumbs so they can be moved in response to the user
readonly Canvas _cnvThumbs;
// Cropping adorner uses Thumbs for visual elements.
// The Thumbs have built-in mouse input handling.
readonly CropThumb _crtTopLeft;
readonly CropThumb _crtTopRight;
readonly CropThumb _crtBottomLeft;
readonly CropThumb _crtBottomRight;
readonly CropThumb _crtTop;
readonly CropThumb _crtLeft;
readonly CropThumb _crtBottom;
readonly CropThumb _crtRight;
readonly Thumb _crtMove;
readonly Border _checkButton;
// To store and manage the adorner's visual children.
readonly VisualCollection _vc;
#endregion
#region Routed Events
public static readonly RoutedEvent CropChangedEvent = EventManager.RegisterRoutedEvent(
nameof(CropChanged),
RoutingStrategy.Bubble,
typeof(RoutedEventHandler),
typeof(CroppingAdorner));
public event RoutedEventHandler CropChanged
{
add => AddHandler(CropChangedEvent, value);
remove => RemoveHandler(CropChangedEvent, value);
}
public event Action Checked;
#endregion
#region Dependency Properties
public static readonly DependencyProperty FillProperty = Shape.FillProperty.AddOwner(typeof(CroppingAdorner));
public Brush Fill
{
get => (Brush)GetValue(FillProperty);
set => SetValue(FillProperty, value);
}
static void FillPropChanged(DependencyObject D, DependencyPropertyChangedEventArgs Args)
{
if (D is CroppingAdorner crp)
{
crp._prCropMask.Fill = (Brush)Args.NewValue;
}
}
#endregion
#region Constructor
static CroppingAdorner()
{
var clr = Colors.Red;
clr.A = 80;
FillProperty.OverrideMetadata(typeof(CroppingAdorner),
new PropertyMetadata(
new SolidColorBrush(clr),
FillPropChanged));
}
public CroppingAdorner(UIElement AdornedElement, Rect RectInit)
: base(AdornedElement)
{
_vc = new VisualCollection(this);
_prCropMask = new PuncturedRect
{
IsHitTestVisible = false,
RectInterior = RectInit,
Fill = Fill
};
_vc.Add(_prCropMask);
_cnvThumbs = new Canvas
{
HorizontalAlignment = HorizontalAlignment.Stretch,
VerticalAlignment = VerticalAlignment.Stretch
};
_vc.Add(_cnvThumbs);
_crtMove = new Thumb
{
Cursor = Cursors.Hand,
Opacity = 0
};
_cnvThumbs.Children.Add(_crtMove);
BuildCorner(ref _crtTop, Cursors.SizeNS);
BuildCorner(ref _crtBottom, Cursors.SizeNS);
BuildCorner(ref _crtLeft, Cursors.SizeWE);
BuildCorner(ref _crtRight, Cursors.SizeWE);
BuildCorner(ref _crtTopLeft, Cursors.SizeNWSE);
BuildCorner(ref _crtTopRight, Cursors.SizeNESW);
BuildCorner(ref _crtBottomLeft, Cursors.SizeNESW);
BuildCorner(ref _crtBottomRight, Cursors.SizeNWSE);
var btn = new ModernButton
{
IconData = Geometry.Parse(ServiceProvider.Get().Check),
Cursor = Cursors.Hand,
Foreground = new SolidColorBrush(Colors.White)
};
_checkButton = new Border
{
CornerRadius = new CornerRadius(20),
VerticalAlignment = VerticalAlignment.Top,
HorizontalAlignment = HorizontalAlignment.Left,
Background = new SolidColorBrush(Colors.LimeGreen),
BorderBrush = new SolidColorBrush(Colors.Black),
BorderThickness = new Thickness(0.3),
Child = btn
};
_cnvThumbs.Children.Add(_checkButton);
btn.Click += (S, E) => Checked?.Invoke();
// Add handlers for Cropping.
_crtBottomLeft.DragDelta += (S, E) => HandleDrag(S, E, 1, 0, -1, 1);
_crtBottomRight.DragDelta += (S, E) => HandleDrag(S, E, 0, 0, 1, 1);
_crtTopLeft.DragDelta += (S, E) => HandleDrag(S, E, 1, 1, -1, -1);
_crtTopRight.DragDelta += (S, E) => HandleDrag(S, E, 0, 1, 1, -1);
_crtTop.DragDelta += (S, E) => HandleDrag(S, E, 0, 1, 0, -1);
_crtBottom.DragDelta += (S, E) => HandleDrag(S, E, 0, 0, 0, 1);
_crtRight.DragDelta += (S, E) => HandleDrag(S, E, 0, 0, 1, 0);
_crtLeft.DragDelta += (S, E) => HandleDrag(S, E, 1, 0, -1, 0);
_crtMove.DragDelta += HandleMove;
// We have to keep the clipping interior within the bounds of the adorned element
// so we have to track it's size to guarantee that...
if (AdornedElement is FrameworkElement fel)
{
fel.SizeChanged += AdornedElement_SizeChanged;
}
}
#endregion
#region Thumb handlers
// Generic handler for Cropping
void HandleThumb(
double DeltaRatioLeft,
double DeltaRatioTop,
double DeltaRatioWidth,
double DeltaRatioHeight,
double DeltaX,
double DeltaY)
{
var rcInterior = _prCropMask.RectInterior;
if (rcInterior.Width + DeltaRatioWidth * DeltaX < 0)
{
DeltaX = -rcInterior.Width / DeltaRatioWidth;
}
if (rcInterior.Height + DeltaRatioHeight * DeltaY < 0)
{
DeltaY = -rcInterior.Height / DeltaRatioHeight;
}
rcInterior = new Rect(
rcInterior.Left + DeltaRatioLeft * DeltaX,
rcInterior.Top + DeltaRatioTop * DeltaY,
rcInterior.Width + DeltaRatioWidth * DeltaX,
rcInterior.Height + DeltaRatioHeight * DeltaY);
_prCropMask.RectInterior = rcInterior;
SetThumbs(_prCropMask.RectInterior);
RaiseEvent(new RoutedEventArgs(CropChangedEvent, this));
}
void HandleMove(object Sender, DragDeltaEventArgs Args)
{
if (AdornedElement is FrameworkElement fel)
{
var rcInterior = _prCropMask.RectInterior;
var left = rcInterior.Left + Args.HorizontalChange;
var top = rcInterior.Top + Args.VerticalChange;
if (left < 0)
left = 0;
if (left + rcInterior.Width > fel.ActualWidth)
left = fel.ActualWidth - rcInterior.Width;
if (top < 0)
top = 0;
if (top + rcInterior.Height > fel.ActualHeight)
top = fel.ActualHeight - rcInterior.Height;
rcInterior = new Rect(left, top, rcInterior.Width, rcInterior.Height);
_prCropMask.RectInterior = rcInterior;
SetThumbs(_prCropMask.RectInterior);
RaiseEvent(new RoutedEventArgs(CropChangedEvent, this));
}
}
void HandleDrag(object Sender, DragDeltaEventArgs Args, int L, int T, int W, int H)
{
if (Sender is CropThumb)
{
HandleThumb(L, T, W, H, Args.HorizontalChange, Args.VerticalChange);
}
}
#endregion
void AdornedElement_SizeChanged(object Sender, SizeChangedEventArgs E)
{
var ratio = E.NewSize.Width / E.PreviousSize.Width;
var rcInterior = _prCropMask.RectInterior;
double intLeft = rcInterior.Left * ratio,
intTop = rcInterior.Top * ratio,
intWidth = rcInterior.Width * ratio,
intHeight = rcInterior.Height * ratio;
_prCropMask.RectInterior = new Rect(intLeft, intTop, intWidth, intHeight);
}
#region Arranging/positioning
void SetThumbs(Rect Rect)
{
_crtBottomRight.SetPos(Rect.Right, Rect.Bottom);
_crtTopLeft.SetPos(Rect.Left, Rect.Top);
_crtTopRight.SetPos(Rect.Right, Rect.Top);
_crtBottomLeft.SetPos(Rect.Left, Rect.Bottom);
_crtTop.SetPos(Rect.Left + Rect.Width / 2, Rect.Top);
_crtBottom.SetPos(Rect.Left + Rect.Width / 2, Rect.Bottom);
_crtLeft.SetPos(Rect.Left, Rect.Top + Rect.Height / 2);
_crtRight.SetPos(Rect.Right, Rect.Top + Rect.Height / 2);
_crtMove.Width = Rect.Width;
_crtMove.Height = Rect.Height;
Canvas.SetLeft(_crtMove, Rect.Left);
Canvas.SetTop(_crtMove, Rect.Top);
Canvas.SetLeft(_checkButton, Rect.Right - _checkButton.ActualWidth - 15);
Canvas.SetTop(_checkButton, Rect.Bottom - _checkButton.ActualHeight - 10);
}
// Arrange the Adorners.
protected override Size ArrangeOverride(Size FinalSize)
{
var rcExterior = new Rect(0, 0, AdornedElement.RenderSize.Width, AdornedElement.RenderSize.Height);
_prCropMask.RectExterior = rcExterior;
var rcInterior = _prCropMask.RectInterior;
_prCropMask.Arrange(rcExterior);
SetThumbs(rcInterior);
_cnvThumbs.Arrange(rcExterior);
return FinalSize;
}
#endregion
public Rect SelectedRegion => _prCropMask.RectInterior;
public BitmapSource BpsCrop(BitmapSource Bmp)
{
var ratio = Bmp.PixelWidth / AdornedElement.RenderSize.Width;
var rcInterior = _prCropMask.RectInterior;
Point ToPoint(double X, double Y)
{
return new Point((int)(X * ratio), (int)(Y * ratio));
}
var pxFromSize = ToPoint(rcInterior.Width, rcInterior.Height);
var pxFromPos = ToPoint(rcInterior.Left, rcInterior.Top);
var pxWhole = ToPoint(AdornedElement.RenderSize.Width, AdornedElement.RenderSize.Height);
pxFromSize.X = Math.Max(Math.Min(pxWhole.X - pxFromPos.X, pxFromSize.X), 0);
pxFromSize.Y = Math.Max(Math.Min(pxWhole.Y - pxFromPos.Y, pxFromSize.Y), 0);
if (pxFromSize.X == 0 || pxFromSize.Y == 0)
{
return null;
}
var rcFrom = new Int32Rect(pxFromPos.X, pxFromPos.Y, pxFromSize.X, pxFromSize.Y);
return new CroppedBitmap(Bmp, rcFrom);
}
void BuildCorner(ref CropThumb Thumb, Cursor CustomCursor)
{
if (Thumb != null)
return;
Thumb = new CropThumb(CpxThumbWidth)
{
Cursor = CustomCursor
};
_cnvThumbs.Children.Add(Thumb);
}
#region Visual tree overrides
// Override the VisualChildrenCount and GetVisualChild properties to interface with
// the adorner's visual collection.
protected override int VisualChildrenCount => _vc.Count;
protected override Visual GetVisualChild(int Index) => _vc[Index];
#endregion
class CropThumb : Thumb
{
readonly int _width;
public CropThumb(int Width)
{
_width = Width;
}
protected override Visual GetVisualChild(int Index) => null;
protected override void OnRender(DrawingContext DrawingContext)
{
DrawingContext.DrawRoundedRectangle(Brushes.White, new Pen(Brushes.Black, 1), new Rect(new Size(_width, _width)), 1, 1);
}
public void SetPos(double X, double Y)
{
Canvas.SetTop(this, Y - _width / 2.0);
Canvas.SetLeft(this, X - _width / 2.0);
}
}
}
}
================================================
FILE: src/Captura/Presentation/OverlayPositionAdorner.cs
================================================
using System;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
namespace Captura
{
public class OverlayPositionAdorner : Adorner
{
#region Thumbs
readonly Thumb _topLeft;
readonly Thumb _topRight;
readonly Thumb _bottomLeft;
readonly Thumb _bottomRight;
readonly Thumb _top;
readonly Thumb _left;
readonly Thumb _right;
readonly Thumb _bottom;
readonly Thumb _center;
#endregion
readonly bool _canResize;
readonly VisualCollection _visualChildren;
public OverlayPositionAdorner(UIElement Element, bool CanResize = true) : base(Element)
{
_canResize = CanResize;
_visualChildren = new VisualCollection(this);
BuildAdornerThumb(ref _center, Cursors.Hand);
_center.Opacity = 0;
_center.DragDelta += (S, E) => HandleDrag(HitType.Body, E);
if (CanResize)
{
BuildAdornerThumb(ref _topLeft, Cursors.SizeNWSE);
BuildAdornerThumb(ref _topRight, Cursors.SizeNESW);
BuildAdornerThumb(ref _bottomLeft, Cursors.SizeNESW);
BuildAdornerThumb(ref _bottomRight, Cursors.SizeNWSE);
BuildAdornerThumb(ref _top, Cursors.SizeNS);
BuildAdornerThumb(ref _left, Cursors.SizeWE);
BuildAdornerThumb(ref _right, Cursors.SizeWE);
BuildAdornerThumb(ref _bottom, Cursors.SizeNS);
_topLeft.DragDelta += (S, E) => HandleDrag(HitType.UpperLeft, E);
_topRight.DragDelta += (S, E) => HandleDrag(HitType.UpperRight, E);
_bottomLeft.DragDelta += (S, E) => HandleDrag(HitType.LowerLeft, E);
_bottomRight.DragDelta += (S, E) => HandleDrag(HitType.LowerRight, E);
_top.DragDelta += (S, E) => HandleDrag(HitType.Top, E);
_left.DragDelta += (S, E) => HandleDrag(HitType.Left, E);
_right.DragDelta += (S, E) => HandleDrag(HitType.Right, E);
_bottom.DragDelta += (S, E) => HandleDrag(HitType.Bottom, E);
}
Opacity = 0.01;
MouseEnter += (S, E) => Opacity = 1;
MouseLeave += (S, E) => Opacity = 0.01;
}
void HandleDrag(HitType MouseHitType, DragDeltaEventArgs Args)
{
if (!(AdornedElement is FrameworkElement fel))
return;
var offsetX = (int) Args.HorizontalChange;
var offsetY = (int) Args.VerticalChange;
var har = fel.HorizontalAlignment == HorizontalAlignment.Right;
var vab = fel.VerticalAlignment == VerticalAlignment.Bottom;
var newX = (int)(har ? fel.Margin.Right : fel.Margin.Left);
var newY = (int)(vab ? fel.Margin.Bottom : fel.Margin.Top);
var newWidth = (int) fel.ActualWidth;
var newHeight = (int) fel.ActualHeight;
void ModifyX(bool Possitive)
{
if (Possitive)
newX += offsetX;
else newX -= offsetX;
}
void ModifyY(bool Possitive)
{
if (Possitive)
newY += offsetY;
else newY -= offsetY;
}
void ModifyWidth(bool Possitive)
{
if (Possitive)
newWidth += offsetX;
else newWidth -= offsetX;
}
void ModifyHeight(bool Possitive)
{
if (Possitive)
newHeight += offsetY;
else newHeight -= offsetY;
}
switch (MouseHitType)
{
case HitType.Body:
ModifyX(!har);
ModifyY(!vab);
break;
case HitType.UpperLeft:
if (har)
{
ModifyWidth(false);
}
else
{
ModifyX(true);
ModifyWidth(false);
}
if (vab)
{
ModifyHeight(false);
}
else
{
ModifyY(true);
ModifyHeight(false);
}
break;
case HitType.UpperRight:
if (har)
{
ModifyX(false);
ModifyWidth(true);
}
else ModifyWidth(true);
if (vab)
{
ModifyHeight(false);
}
else
{
ModifyY(true);
ModifyHeight(false);
}
break;
case HitType.LowerRight:
if (har)
{
ModifyX(false);
ModifyWidth(true);
}
else ModifyWidth(true);
if (vab)
{
ModifyY(false);
ModifyHeight(true);
}
else ModifyHeight(true);
break;
case HitType.LowerLeft:
if (har)
{
ModifyWidth(false);
}
else
{
ModifyX(true);
ModifyWidth(false);
}
if (vab)
{
ModifyY(false);
ModifyHeight(true);
}
else ModifyHeight(true);
break;
case HitType.Left:
if (har)
{
ModifyWidth(false);
}
else
{
ModifyX(true);
ModifyWidth(false);
}
break;
case HitType.Right:
if (har)
{
ModifyX(false);
ModifyWidth(true);
}
else ModifyWidth(true);
break;
case HitType.Bottom:
if (vab)
{
ModifyY(false);
ModifyHeight(true);
}
else ModifyHeight(true);
break;
case HitType.Top:
if (vab)
{
ModifyHeight(false);
}
else
{
ModifyY(true);
ModifyHeight(false);
}
break;
}
if (newWidth > 0 && newHeight > 0)
{
if (newX < 0)
{
newX = 0;
}
if (newY < 0)
{
newY = 0;
}
double left = 0, top = 0, right = 0, bottom = 0;
if (har)
right = newX;
else left = newX;
if (vab)
bottom = newY;
else top = newY;
fel.Margin = new Thickness(left, top, right, bottom);
PositionUpdated?.Invoke(new Rect(newX, newY, newWidth, newHeight));
if (MouseHitType != HitType.Body)
{
fel.Width = newWidth;
fel.Height = newHeight;
}
}
}
public event Action PositionUpdated;
void BuildAdornerThumb(ref Thumb CornerThumb, Cursor CustomizedCursors)
{
if (CornerThumb != null)
return;
CornerThumb = new Thumb
{
Cursor = CustomizedCursors,
Height = 10,
Width = 10,
Opacity = 0.5,
Background = new SolidColorBrush(Colors.Red)
};
_visualChildren.Add(CornerThumb);
}
protected override Size ArrangeOverride(Size FinalSize)
{
base.ArrangeOverride(FinalSize);
var desireWidth = AdornedElement.RenderSize.Width;
var desireHeight = AdornedElement.RenderSize.Height;
var adornerWidth = DesiredSize.Width;
var adornerHeight = DesiredSize.Height;
_center.Height = desireHeight;
_center.Width = desireWidth;
_center.Arrange(new Rect(0, 0, desireWidth, desireHeight));
if (_canResize)
{
_topLeft.Arrange(new Rect(-adornerWidth / 2, -adornerHeight / 2, adornerWidth, adornerHeight));
_topRight.Arrange(new Rect(desireWidth - adornerWidth / 2, -adornerHeight / 2, adornerWidth,
adornerHeight));
_bottomLeft.Arrange(new Rect(-adornerWidth / 2, desireHeight - adornerHeight / 2, adornerWidth,
adornerHeight));
_bottomRight.Arrange(new Rect(desireWidth - adornerWidth / 2, desireHeight - adornerHeight / 2,
adornerWidth, adornerHeight));
_top.Arrange(new Rect(desireWidth / 2 - adornerWidth / 2, -adornerHeight / 2, adornerWidth,
adornerHeight));
_left.Arrange(new Rect(-adornerWidth / 2, desireHeight / 2 - adornerHeight / 2, adornerWidth,
adornerHeight));
_right.Arrange(new Rect(desireWidth - adornerWidth / 2, desireHeight / 2 - adornerHeight / 2,
adornerWidth, adornerHeight));
_bottom.Arrange(new Rect(desireWidth / 2 - adornerWidth / 2, desireHeight - adornerHeight / 2,
adornerWidth, adornerHeight));
}
return FinalSize;
}
protected override int VisualChildrenCount => _visualChildren.Count;
protected override Visual GetVisualChild(int Index) => _visualChildren[Index];
}
}
================================================
FILE: src/Captura/Presentation/PuncturedRect.cs
================================================
using System;
using System.Windows.Shapes;
using System.Windows;
using System.Windows.Media;
namespace Captura
{
public class PuncturedRect : Shape
{
public static readonly DependencyProperty RectInteriorProperty =
DependencyProperty.Register(
nameof(RectInterior),
typeof(Rect),
typeof(PuncturedRect),
new FrameworkPropertyMetadata(
new Rect(0, 0, 0, 0),
FrameworkPropertyMetadataOptions.AffectsRender,
null,
CoerceRectInterior
),
null
);
static object CoerceRectInterior(DependencyObject D, object Value)
{
if (D is PuncturedRect pr && Value is Rect rcProposed)
{
var rcExterior = pr.RectExterior;
var left = Math.Max(rcProposed.Left, rcExterior.Left);
var top = Math.Max(rcProposed.Top, rcExterior.Top);
var width = Math.Min(rcProposed.Right, rcExterior.Right) - left;
var height = Math.Min(rcProposed.Bottom, rcExterior.Bottom) - top;
return new Rect(left, top, width, height);
}
return Value;
}
public Rect RectInterior
{
get => (Rect)GetValue(RectInteriorProperty);
set => SetValue(RectInteriorProperty, value);
}
public static readonly DependencyProperty RectExteriorProperty =
DependencyProperty.Register(
nameof(RectExterior),
typeof(Rect),
typeof(PuncturedRect),
new FrameworkPropertyMetadata(
new Rect(0, 0, double.MaxValue, double.MaxValue),
FrameworkPropertyMetadataOptions.AffectsMeasure |
FrameworkPropertyMetadataOptions.AffectsArrange |
FrameworkPropertyMetadataOptions.AffectsParentMeasure |
FrameworkPropertyMetadataOptions.AffectsParentArrange |
FrameworkPropertyMetadataOptions.AffectsRender
),
null
);
public Rect RectExterior
{
get => (Rect)GetValue(RectExteriorProperty);
set => SetValue(RectExteriorProperty, value);
}
public PuncturedRect() : this(new Rect(0, 0, double.MaxValue, double.MaxValue), new Rect()) { }
public PuncturedRect(Rect RectExterior, Rect RectInterior)
{
this.RectInterior = RectInterior;
this.RectExterior = RectExterior;
}
protected override Geometry DefiningGeometry
{
get
{
var pthgExt = new PathGeometry();
var pthfExt = new PathFigure {StartPoint = RectExterior.TopLeft};
pthfExt.Segments.Add(new LineSegment(RectExterior.TopRight, false));
pthfExt.Segments.Add(new LineSegment(RectExterior.BottomRight, false));
pthfExt.Segments.Add(new LineSegment(RectExterior.BottomLeft, false));
pthfExt.Segments.Add(new LineSegment(RectExterior.TopLeft, false));
pthgExt.Figures.Add(pthfExt);
var rectIntSect = Rect.Intersect(RectExterior, RectInterior);
var pthgInt = new PathGeometry();
var pthfInt = new PathFigure {StartPoint = rectIntSect.TopLeft};
pthfInt.Segments.Add(new LineSegment(rectIntSect.TopRight, false));
pthfInt.Segments.Add(new LineSegment(rectIntSect.BottomRight, false));
pthfInt.Segments.Add(new LineSegment(rectIntSect.BottomLeft, false));
pthfInt.Segments.Add(new LineSegment(rectIntSect.TopLeft, false));
pthgInt.Figures.Add(pthfInt);
return new CombinedGeometry(GeometryCombineMode.Exclude, pthgExt, pthgInt);
}
}
}
}
================================================
FILE: src/Captura/Presentation/Themes/Expander.xaml
================================================
================================================
FILE: src/Captura/Presentation/Themes/Generic.xaml
================================================
================================================
FILE: src/Captura/Presentation/Themes/LICENSE.md
================================================
ModernUI
(c) FirstFloor Software
Microsoft Public License (Ms-PL)
This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software.
1. Definitions
The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law.
A "contribution" is the original software, or any additions or changes to the software.
A "contributor" is any person that distributes its contribution under this license.
"Licensed patents" are a contributor's patent claims that read directly on its contribution.
2. Grant of Rights
(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
3. Conditions and Limitations
(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement.
================================================
FILE: src/Captura/Presentation/Themes/ModernButton.xaml
================================================
================================================
FILE: src/Captura/Presentation/Themes/ModernToggleButton.xaml
================================================
================================================
FILE: src/Captura/Presentation/Themes/ModernTogglePill.xaml
================================================
================================================
FILE: src/Captura/Presentation/Themes/RegionPickerMagnifier.xaml
================================================
================================================
FILE: src/Captura/Presentation/Themes/RoundSlider.xaml
================================================
================================================
FILE: src/Captura/Presentation/Themes/VirtualizingItemsControl.xaml
================================================
================================================
FILE: src/Captura/Presentation/WpfExtensions.cs
================================================
using System;
using System.Drawing;
using System.IO;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using Captura.Video;
using Captura.ViewModels;
using Microsoft.Win32;
using Reactive.Bindings;
using Color = System.Windows.Media.Color;
using ColorConverter = System.Windows.Media.ColorConverter;
using DColor = System.Drawing.Color;
namespace Captura
{
public static class WpfExtensions
{
public static void ShowAndFocus(this Window W)
{
if (W.IsVisible && W.WindowState == WindowState.Minimized)
{
W.WindowState = WindowState.Normal;
}
W.Show();
W.Activate();
}
public static Rectangle ApplyDpi(this RectangleF Rectangle)
{
return new Rectangle((int)(Rectangle.Left * Dpi.X),
(int)(Rectangle.Top * Dpi.Y),
(int)(Rectangle.Width * Dpi.X),
(int)(Rectangle.Height * Dpi.Y));
}
public static DColor ToDrawingColor(this Color C)
{
return DColor.FromArgb(C.A, C.R, C.G, C.B);
}
public static Color ToWpfColor(this DColor C)
{
return Color.FromArgb(C.A, C.R, C.G, C.B);
}
public static Color ParseColor(string S)
{
if (ColorConverter.ConvertFromString(S) is Color c)
return c;
return Colors.Transparent;
}
public static void Shake(this FrameworkElement Element)
{
Element.Dispatcher.Invoke(() =>
{
var transform = new TranslateTransform();
Element.RenderTransform = transform;
const int delta = 5;
var animation = new DoubleAnimationUsingKeyFrames
{
AutoReverse = true,
RepeatBehavior = new RepeatBehavior(1),
Duration = new Duration(TimeSpan.FromMilliseconds(200)),
KeyFrames =
{
new EasingDoubleKeyFrame(0, KeyTime.FromPercent(0)),
new EasingDoubleKeyFrame(delta, KeyTime.FromPercent(0.25)),
new EasingDoubleKeyFrame(0, KeyTime.FromPercent(0.5)),
new EasingDoubleKeyFrame(-delta, KeyTime.FromPercent(0.75)),
new EasingDoubleKeyFrame(0, KeyTime.FromPercent(1))
}
};
transform.BeginAnimation(TranslateTransform.XProperty, animation);
});
}
public static bool SaveToPickedFile(this BitmapSource Bitmap, string DefaultFileName = null)
{
var sfd = new SaveFileDialog
{
AddExtension = true,
DefaultExt = ".png",
Filter = "PNG Image|*.png|JPEG Image|*.jpg;*.jpeg|Bitmap Image|*.bmp|TIFF Image|*.tiff"
};
if (DefaultFileName != null)
{
sfd.FileName = Path.GetFileNameWithoutExtension(DefaultFileName);
var dir = Path.GetDirectoryName(DefaultFileName);
if (dir != null)
{
sfd.InitialDirectory = dir;
}
}
else sfd.FileName = "Untitled";
if (!sfd.ShowDialog().GetValueOrDefault())
return false;
BitmapEncoder encoder;
// Filter Index starts from 1
switch (sfd.FilterIndex)
{
case 2:
encoder = new JpegBitmapEncoder();
break;
case 3:
encoder = new BmpBitmapEncoder();
break;
case 4:
encoder = new TiffBitmapEncoder();
break;
default:
encoder = new PngBitmapEncoder();
break;
}
encoder.Frames.Add(BitmapFrame.Create(Bitmap));
using (var stream = sfd.OpenFile())
{
encoder.Save(stream);
}
return true;
}
public static void Bind(this FrameworkElement Control, DependencyProperty DependencyProperty, IReactiveProperty Property)
{
Control.SetBinding(DependencyProperty,
new Binding(nameof(Property.Value))
{
Source = Property,
Mode = BindingMode.TwoWay
});
}
public static void BindOne(this FrameworkElement Control, DependencyProperty DependencyProperty, IReadOnlyReactiveProperty Property)
{
Control.SetBinding(DependencyProperty,
new Binding(nameof(Property.Value))
{
Source = Property,
Mode = BindingMode.OneWay
});
}
public static async Task GetBackground()
{
var vm = ServiceProvider.Get();
IBitmapImage bmp;
switch (vm.SelectedVideoSourceKind?.Source)
{
case NoVideoItem _:
bmp = ScreenShot.Capture();
break;
default:
var screenShotModel = ServiceProvider.Get();
bmp = await screenShotModel.GetScreenShot(vm.SelectedVideoSourceKind, true);
break;
}
if (bmp == null)
{
bmp = ScreenShot.Capture();
}
using (bmp)
{
var stream = new MemoryStream();
bmp.Save(stream, ImageFormats.Png);
stream.Seek(0, SeekOrigin.Begin);
var decoder = new PngBitmapDecoder(stream, BitmapCreateOptions.None, BitmapCacheOption.Default);
return decoder.Frames[0];
}
}
}
}
================================================
FILE: src/Captura/Properties/AssemblyInfo.cs
================================================
using System.Reflection;
[assembly: AssemblyTitle(nameof(Captura))]
[assembly: AssemblyDescription("Captures Screen/Window as ScreenShot/ScreenCast along with Audio from Microphone/Loopback, Mouse Cursor, Clicks and Keystrokes")]
[assembly: AssemblyCompany("Mathew Sachin")]
[assembly: AssemblyProduct(nameof(Captura))]
[assembly: AssemblyCopyright("(c) 2018 Mathew Sachin")]
[assembly: AssemblyTrademark(nameof(Captura))]
[assembly: AssemblyVersion("0.0.0")]
================================================
FILE: src/Captura/ValueConverters/DrawingToWpfColorConverter.cs
================================================
using System;
using System.Drawing;
using System.Globalization;
using System.Windows.Data;
using WpfColor = System.Windows.Media.Color;
namespace Captura
{
public class DrawingToWpfColorConverter : IValueConverter
{
public object Convert(object Value, Type TargetType, object Parameter, CultureInfo Culture)
{
if (Value is Color c)
return c.ToWpfColor();
return Binding.DoNothing;
}
public object ConvertBack(object Value, Type TargetType, object Parameter, CultureInfo Culture)
{
switch (Value)
{
case string s:
return ColorTranslator.FromHtml(s);
case WpfColor c:
return c.ToString();
default:
return Binding.DoNothing;
}
}
}
}
================================================
FILE: src/Captura/ValueConverters/GetTypeConverter.cs
================================================
using System;
using System.Globalization;
namespace Captura
{
public class GetTypeConverter : OneWayConverter
{
public override object Convert(object Value, Type TargetType, object Parameter, CultureInfo Culture)
{
return Value?.GetType();
}
}
}
================================================
FILE: src/Captura/ValueConverters/InkToolToIconConverter.cs
================================================
using System;
using System.Globalization;
using System.Windows.Controls;
using System.Windows.Media;
namespace Captura
{
public class InkToolToIconConverter : OneWayConverter
{
static string GetPath(object Value)
{
var icons = ServiceProvider.Get();
switch (Value)
{
case InkCanvasEditingMode.Ink:
return icons.Pencil;
case InkCanvasEditingMode.EraseByPoint:
return icons.Eraser;
case InkCanvasEditingMode.EraseByStroke:
return icons.StrokeEraser;
case InkCanvasEditingMode.Select:
return icons.Select;
}
return icons.Cursor;
}
public override object Convert(object Value, Type TargetType, object Parameter, CultureInfo Culture)
{
return Geometry.Parse(GetPath(Value));
}
}
}
================================================
FILE: src/Captura/ValueConverters/IntegralTimeSpanConverter.cs
================================================
using System;
using System.Windows.Data;
using System.Globalization;
namespace Captura
{
public class IntegralTimeSpanConverter : OneWayConverter
{
public override object Convert(object Value, Type TargetType, object Parameter, CultureInfo Culture)
{
if (Value is TimeSpan t)
return TimeSpan.FromSeconds((int) t.TotalSeconds);
return Binding.DoNothing;
}
}
}
================================================
FILE: src/Captura/ValueConverters/IsLessThanConverter.cs
================================================
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace Captura
{
public class IsLessThanConverter : OneWayConverter
{
public override object Convert(object Value, Type TargetType, object Parameter, CultureInfo Culture)
{
if (double.TryParse(Value?.ToString(), out var left) && double.TryParse(Parameter?.ToString(), out var right))
{
var b = left < right;
if (TargetType == typeof(Visibility))
return b ? Visibility.Visible : Visibility.Collapsed;
return b;
}
return Binding.DoNothing;
}
}
}
================================================
FILE: src/Captura/ValueConverters/IsPlayingToButtonStyleConverter.cs
================================================
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
namespace Captura
{
public class IsPlayingToButtonStyleConverter : OneWayConverter
{
public override object Convert(object Value, Type TargetType, object Parameter, CultureInfo Culture)
{
if (Value is bool b)
{
var icons = ServiceProvider.Get();
var icon = Geometry.Parse(b ? icons.Stop : icons.Play);
var color = b ? Colors.OrangeRed : Colors.LimeGreen;
return new Style(typeof(ModernButton), (Style) Application.Current.Resources[typeof(ModernButton)])
{
Setters =
{
new Setter(ModernButton.IconDataProperty, icon),
new Setter(Control.ForegroundProperty, new SolidColorBrush(color))
}
};
}
return Binding.DoNothing;
}
}
}
================================================
FILE: src/Captura/ValueConverters/LanguageConverter.cs
================================================
using System;
using System.Windows.Data;
using System.Globalization;
namespace Captura
{
public class LanguageConverter : IValueConverter
{
public object Convert(object Value, Type TargetType, object Parameter, CultureInfo Culture)
{
if (Value is CultureInfo c)
{
switch (c.Name)
{
case "en":
return "English";
case "zh-CN":
return "Chinese (Simplified) (中文简体)";
case "zh-TW":
return "Chinese (Traditional) (中文繁体)";
default:
return $"{c.DisplayName} ({c.NativeName})";
}
}
return Binding.DoNothing;
}
public object ConvertBack(object Value, Type TargetType, object Parameter, CultureInfo Culture)
{
return Binding.DoNothing;
}
}
}
================================================
FILE: src/Captura/ValueConverters/NegatingConverter.cs
================================================
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace Captura
{
public class NegatingConverter : IValueConverter
{
static object DoConvert(object Value)
{
if (Value is bool b)
return !b;
return Binding.DoNothing;
}
public object Convert(object Value, Type TargetType, object Parameter, CultureInfo Culture)
{
switch (Value)
{
case bool b when TargetType == typeof(Visibility):
return b ? Visibility.Collapsed : Visibility.Visible;
case bool b:
return !b;
case Visibility visibility when TargetType == typeof(Visibility):
return visibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible;
case Visibility visibility:
return visibility == Visibility.Collapsed;
default:
return Binding.DoNothing;
}
}
public object ConvertBack(object Value, Type TargetType, object Parameter, CultureInfo Culture)
{
return DoConvert(Value);
}
}
}
================================================
FILE: src/Captura/ValueConverters/NotNullConverter.cs
================================================
using System;
using System.Collections;
using System.Globalization;
using System.Windows;
namespace Captura
{
public class NotNullConverter : OneWayConverter
{
public override object Convert(object Value, Type TargetType, object Parameter, CultureInfo Culture)
{
var b = Value != null;
switch (Value)
{
case ICollection collection:
b = collection.Count != 0;
break;
case string str:
b = !string.IsNullOrWhiteSpace(str);
break;
case int i:
b = i != 0;
break;
case double d:
b = Math.Abs(d) > 0.01;
break;
}
if ((Parameter is bool inverse || Parameter is string s && bool.TryParse(s, out inverse)) && inverse)
b = !b;
if (TargetType == typeof(Visibility))
return b ? Visibility.Visible : Visibility.Collapsed;
return b;
}
}
}
================================================
FILE: src/Captura/ValueConverters/NotRecordingConverter.cs
================================================
using Captura.Models;
using System;
using System.Globalization;
using System.Windows.Data;
namespace Captura
{
public class NotRecordingConverter : OneWayConverter
{
public override object Convert(object Value, Type TargetType, object Parameter, CultureInfo Culture)
{
if (Value is RecorderState state)
return state == RecorderState.NotRecording;
return Binding.DoNothing;
}
}
}
================================================
FILE: src/Captura/ValueConverters/OneWayConverter.cs
================================================
using System;
using System.Globalization;
using System.Windows.Data;
namespace Captura
{
public abstract class OneWayConverter : IValueConverter
{
public abstract object Convert(object Value, Type TargetType, object Parameter, CultureInfo Culture);
public virtual object ConvertBack(object Value, Type TargetType, object Parameter, CultureInfo Culture)
{
return Binding.DoNothing;
}
}
}
================================================
FILE: src/Captura/ValueConverters/SecondsToTimeSpanConverter.cs
================================================
using System;
using System.Windows.Data;
using System.Globalization;
namespace Captura
{
public class SecondsToTimeSpanConverter : IValueConverter
{
public object Convert(object Value, Type TargetType, object Parameter, CultureInfo Culture)
{
if (Value is int seconds)
return TimeSpan.FromSeconds(seconds);
return Binding.DoNothing;
}
public object ConvertBack(object Value, Type TargetType, object Parameter, CultureInfo Culture)
{
if (Value is TimeSpan t)
return t.TotalSeconds;
return Binding.DoNothing;
}
}
}
================================================
FILE: src/Captura/ValueConverters/StateToRecordButtonGeometryConverter.cs
================================================
using Captura.Models;
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Media;
namespace Captura
{
public class StateToRecordButtonGeometryConverter : OneWayConverter
{
public override object Convert(object Value, Type TargetType, object Parameter, CultureInfo Culture)
{
var icons = ServiceProvider.Get();
if (Value is RecorderState state)
{
return Geometry.Parse(state == RecorderState.NotRecording
? icons.Record
: icons.Stop);
}
return Binding.DoNothing;
}
}
}
================================================
FILE: src/Captura/ValueConverters/StateToTaskbarOverlayConverter.cs
================================================
using Captura.Models;
using System;
using System.Globalization;
namespace Captura
{
public class StateToTaskbarOverlayConverter : OneWayConverter
{
public override object Convert(object Value, Type TargetType, object Parameter, CultureInfo Culture)
{
switch(Value)
{
case RecorderState.Recording:
return "/Images/record.ico";
case RecorderState.Paused:
return "/Images/pause.ico";
default:
return null;
}
}
}
}
================================================
FILE: src/Captura/ValueConverters/StateToTrayIconSourceConverter.cs
================================================
using Captura.Models;
using System;
using System.Globalization;
namespace Captura
{
public class StateToTrayIconSourceConverter : OneWayConverter
{
public override object Convert(object Value, Type TargetType, object Parameter, CultureInfo Culture)
{
switch (Value)
{
case RecorderState.Recording:
return "/Images/record.ico";
case RecorderState.Paused:
return "/Images/pause.ico";
default:
return "/Images/Captura.ico";
}
}
}
}
================================================
FILE: src/Captura/ValueConverters/StaticResourceConverter.cs
================================================
using System;
using System.Globalization;
using System.Windows;
namespace Captura
{
public class StaticResourceConverter : OneWayConverter
{
public override object Convert(object Value, Type TargetType, object Parameter, CultureInfo Culture)
{
return Value != null ? Application.Current.Resources[Value] : null;
}
}
}
================================================
FILE: src/Captura/ValueConverters/TimeSpanToSecondsConverter.cs
================================================
using System;
using System.Windows.Data;
using System.Globalization;
namespace Captura
{
public class TimeSpanToSecondsConverter : IValueConverter
{
public object Convert(object Value, Type TargetType, object Parameter, CultureInfo Culture)
{
if (Value is TimeSpan t)
return t.TotalSeconds;
return Binding.DoNothing;
}
public object ConvertBack(object Value, Type TargetType, object Parameter, CultureInfo Culture)
{
if (Value is double seconds)
return TimeSpan.FromSeconds(seconds);
return Binding.DoNothing;
}
}
}
================================================
FILE: src/Captura/ValueConverters/ValueConverters.xaml
================================================
================================================
FILE: src/Captura/ViewModels/AboutViewModel.cs
================================================
using System;
using System.Diagnostics;
using System.Windows.Input;
using Captura.Loc;
using Reactive.Bindings;
namespace Captura.ViewModels
{
// ReSharper disable once ClassNeverInstantiated.Global
public class AboutViewModel : ViewModelBase
{
public ICommand HyperlinkCommand { get; }
public static Version Version { get; }
public string AppVersion { get; }
static AboutViewModel()
{
Version = ServiceProvider.AppVersion;
}
public AboutViewModel(Settings Settings, ILocalizationProvider Loc) : base(Settings, Loc)
{
AppVersion = "v" + Version.ToString(3);
HyperlinkCommand = new ReactiveCommand()
.WithSubscribe(M => Process.Start(M));
}
}
}
================================================
FILE: src/Captura/ViewModels/ExceptionViewModel.cs
================================================
using System;
using System.Collections.ObjectModel;
using System.Reactive.Linq;
using System.Text;
using System.Windows.Input;
using Captura.Models;
using Reactive.Bindings;
using Reactive.Bindings.Extensions;
namespace Captura
{
public class ExceptionViewModel : NotifyPropertyChanged
{
public ExceptionViewModel()
{
CopyToClipboardCommand = Exceptions
.ObserveProperty(M => M.Count)
.Select(M => M > 0)
.ToReactiveCommand()
.WithSubscribe(OnCopyToClipboard);
}
void OnCopyToClipboard()
{
var sb = new StringBuilder();
sb.Append(SystemInfo.GetInfo());
sb.AppendLine();
sb.Append(Exceptions[0]);
sb.ToString().WriteToClipboard();
}
string _message = "An unhandled exception occurred. Here are the details.";
public string Message
{
get => _message;
set => Set(ref _message, value);
}
public void Init(Exception Exception, string Msg)
{
if (Msg != null)
Message = Msg;
while (Exception != null)
{
Exceptions.Add(Exception);
Exception = Exception.InnerException;
}
SelectedException = Exceptions[0];
}
public ObservableCollection Exceptions { get; } = new ObservableCollection();
Exception _selectedException;
public Exception SelectedException
{
get => _selectedException;
set => Set(ref _selectedException, value);
}
public ICommand CopyToClipboardCommand { get; }
}
}
================================================
FILE: src/Captura/ViewModels/Overlays/CensorOverlayReactor.cs
================================================
using System.Reactive.Linq;
using System.Windows;
using Captura.Video;
using Reactive.Bindings;
using Reactive.Bindings.Extensions;
namespace Captura.ViewModels
{
public class CensorOverlayReactor
{
public CensorOverlayReactor(CensorOverlaySettings Settings)
{
Width = Settings
.ToReactivePropertyAsSynchronized(M => M.Width);
Height = Settings
.ToReactivePropertyAsSynchronized(M => M.Height);
Visible = Settings
.ObserveProperty(M => M.Display)
.Select(M => M ? Visibility.Visible : Visibility.Collapsed)
.ToReadOnlyReactivePropertySlim();
}
public IReactiveProperty Width { get; }
public IReactiveProperty Height { get; }
public IReadOnlyReactiveProperty Visible { get; }
}
}
================================================
FILE: src/Captura/ViewModels/Overlays/ImageOverlayReactor.cs
================================================
using System.Reactive.Linq;
using Captura.Video;
using Reactive.Bindings;
using Reactive.Bindings.Extensions;
namespace Captura.ViewModels
{
public class ImageOverlayReactor
{
public ImageOverlayReactor(ImageOverlaySettings Settings)
{
Width = Settings
.ToReactivePropertyAsSynchronized(M => M.ResizeWidth);
Height = Settings
.ToReactivePropertyAsSynchronized(M => M.ResizeHeight);
Opacity = Settings
.ObserveProperty(M => M.Opacity)
.Select(M => M / 100.0)
.ToReadOnlyReactivePropertySlim();
}
public IReactiveProperty Width { get; }
public IReactiveProperty Height { get; }
public IReadOnlyReactiveProperty Opacity { get; }
}
}
================================================
FILE: src/Captura/ViewModels/Overlays/PositionOverlayReactor.cs
================================================
using System.Reactive.Linq;
using System.Windows;
using Captura.Video;
using Reactive.Bindings;
using Reactive.Bindings.Extensions;
namespace Captura.ViewModels
{
public class PositionOverlayReactor
{
public PositionOverlayReactor(PositionedOverlaySettings Settings)
{
HAlignment = Settings
.ObserveProperty(M => M.HorizontalAlignment)
.Select(M =>
{
switch (M)
{
case Alignment.Start:
return HorizontalAlignment.Left;
case Alignment.Center:
return HorizontalAlignment.Center;
case Alignment.End:
return HorizontalAlignment.Right;
default:
return HorizontalAlignment.Stretch;
}
})
.ToReadOnlyReactivePropertySlim();
VAlignment = Settings
.ObserveProperty(M => M.VerticalAlignment)
.Select(M =>
{
switch (M)
{
case Alignment.Start:
return VerticalAlignment.Top;
case Alignment.Center:
return VerticalAlignment.Center;
case Alignment.End:
return VerticalAlignment.Bottom;
default:
return VerticalAlignment.Stretch;
}
})
.ToReadOnlyReactivePropertySlim();
Margin = Settings
.ObserveProperty(M => M.HorizontalAlignment)
.CombineLatest(
Settings
.ObserveProperty(M => M.VerticalAlignment),
Settings
.ObserveProperty(M => M.X),
Settings
.ObserveProperty(M => M.Y),
MarginPropSelector)
.ToReadOnlyReactivePropertySlim();
}
static Thickness MarginPropSelector(Alignment HAlign, Alignment VAlign, int X, int Y)
{
int left = 0, top = 0, right = 0, bottom = 0;
switch (HAlign)
{
case Alignment.Start:
case Alignment.Center:
left = X;
break;
case Alignment.End:
right = X;
break;
}
switch (VAlign)
{
case Alignment.Start:
case Alignment.Center:
top = Y;
break;
case Alignment.End:
bottom = Y;
break;
}
return new Thickness(left, top, right, bottom);
}
public IReadOnlyReactiveProperty VAlignment { get; }
public IReadOnlyReactiveProperty HAlignment { get; }
public IReadOnlyReactiveProperty Margin { get; }
}
}
================================================
FILE: src/Captura/ViewModels/Overlays/TextOverlayReactor.cs
================================================
using System.Reactive.Linq;
using System.Windows;
using System.Windows.Media;
using Captura.Video;
using Reactive.Bindings;
using Reactive.Bindings.Extensions;
namespace Captura.ViewModels
{
public class TextOverlayReactor
{
public TextOverlayReactor(TextOverlaySettings Settings)
{
FontFamily = Settings
.ObserveProperty(M => M.FontFamily)
.Select(M => new FontFamily(M))
.ToReadOnlyReactivePropertySlim();
FontSize = Settings
.ObserveProperty(M => M.FontSize)
.ToReadOnlyReactivePropertySlim();
Padding = new[]
{
Settings
.ObserveProperty(M => M.HorizontalPadding),
Settings
.ObserveProperty(M => M.VerticalPadding)
}
.CombineLatest(M =>
{
var w = M[0];
var h = M[1];
return new Thickness(w, h, w, h);
})
.ToReadOnlyReactivePropertySlim();
Foreground = Settings
.ObserveProperty(M => M.FontColor)
.Select(M => new SolidColorBrush(M.ToWpfColor()))
.ToReadOnlyReactivePropertySlim();
Background = Settings
.ObserveProperty(M => M.BackgroundColor)
.Select(M => new SolidColorBrush(M.ToWpfColor()))
.ToReadOnlyReactivePropertySlim();
BorderThickness = Settings
.ObserveProperty(M => M.BorderThickness)
.Select(M => new Thickness(M))
.ToReadOnlyReactivePropertySlim();
BorderBrush = Settings
.ObserveProperty(M => M.BorderColor)
.Select(M => new SolidColorBrush(M.ToWpfColor()))
.ToReadOnlyReactivePropertySlim();
CornerRadius = Settings
.ObserveProperty(M => M.CornerRadius)
.Select(M => new CornerRadius(M))
.ToReadOnlyReactivePropertySlim();
}
public IReadOnlyReactiveProperty FontFamily { get; }
public IReadOnlyReactiveProperty FontSize { get; }
public IReadOnlyReactiveProperty Padding { get; }
public IReadOnlyReactiveProperty Foreground { get; }
public IReadOnlyReactiveProperty Background { get; }
public IReadOnlyReactiveProperty BorderThickness { get; }
public IReadOnlyReactiveProperty BorderBrush { get; }
public IReadOnlyReactiveProperty CornerRadius { get; }
}
}
================================================
FILE: src/Captura/ViewModels/Overlays/WebcamOverlayReactor.cs
================================================
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Windows;
using Reactive.Bindings;
using Reactive.Bindings.Extensions;
using Point = System.Drawing.Point;
namespace Captura.ViewModels
{
public class WebcamOverlayReactor
{
public WebcamOverlayReactor(WebcamOverlaySettings Settings)
{
Location = Settings.ObserveProperty(M => M.XLoc)
.CombineLatest(
Settings.ObserveProperty(M => M.YLoc),
FrameSize,
WebcamSize,
Settings.ObserveProperty(M => M.Scale),
(X, Y, FrameSize, WebcamSize, Scale) => Settings.GetPosition(FrameSize.ToDrawingSize(), WebcamSize.ToDrawingSize()))
.ToReadOnlyReactivePropertySlim();
Size = Settings.ObserveProperty(M => M.Scale)
.CombineLatest(
FrameSize,
WebcamSize,
(Scale, FrameSize, WebcamSize) => Settings.GetSize(FrameSize.ToDrawingSize(), WebcamSize.ToDrawingSize()).ToWpfSize())
.ToReadOnlyReactivePropertySlim();
Opacity = Settings
.ObserveProperty(M => M.Opacity)
.Select(M => M / 100.0)
.ToReadOnlyReactivePropertySlim();
}
public Subject FrameSize { get; } = new Subject();
public Subject WebcamSize { get; } = new Subject();
public IReadOnlyReactiveProperty Size { get; }
public IReadOnlyReactiveProperty Opacity { get; }
public IReadOnlyReactiveProperty Location { get; }
}
}
================================================
FILE: src/Captura/ViewModels/RegionSelectorViewModel.cs
================================================
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Controls;
using System.Windows.Input;
using Reactive.Bindings;
using Color = System.Windows.Media.Color;
// ReSharper disable MemberCanBePrivate.Global
namespace Captura.ViewModels
{
// ReSharper disable once ClassNeverInstantiated.Global
public class RegionSelectorViewModel : NotifyPropertyChanged
{
int _left = 50,
_top = 50,
_width = 500,
_height = 500;
const int MinWidth = 10,
MinHeight = 10,
KeyMoveDelta = 1;
public const int BorderSize = 3;
public RegionSelectorViewModel()
{
MoveLeftCommand = new ReactiveCommand()
.WithSubscribe(() => Left -= KeyMoveDelta);
MoveRightCommand = new ReactiveCommand()
.WithSubscribe(() => Left += KeyMoveDelta);
MoveUpCommand = new ReactiveCommand()
.WithSubscribe(() => Top -= KeyMoveDelta);
MoveDownCommand = new ReactiveCommand()
.WithSubscribe(() => Top += KeyMoveDelta);
IncreaseWidthCommand = new ReactiveCommand()
.WithSubscribe(() => Width += KeyMoveDelta);
DecreaseWidthCommand = new ReactiveCommand()
.WithSubscribe(() => Width -= KeyMoveDelta);
IncreaseHeightCommand = new ReactiveCommand()
.WithSubscribe(() => Height += KeyMoveDelta);
DecreaseHeightCommand = new ReactiveCommand()
.WithSubscribe(() => Height -= KeyMoveDelta);
}
public static Dictionary Tools { get; } = new Dictionary
{
[InkCanvasEditingMode.None] = "Pointer",
[InkCanvasEditingMode.Ink] = "Pencil",
[InkCanvasEditingMode.EraseByPoint] = "Eraser",
[InkCanvasEditingMode.EraseByStroke] = "Stroke Eraser"
};
public IReactiveProperty SelectedTool { get; } = new ReactivePropertySlim(Tools.Keys.First());
public IReactiveProperty BrushSize { get; } = new ReactiveProperty(10);
public IReactiveProperty BrushColor { get; } = new ReactiveProperty(Color.FromRgb(27, 27, 27));
public int Left
{
get => _left;
set
{
Set(ref _left, value);
RaisePropertyChanged(nameof(LeftDip));
}
}
public double LeftDip
{
get => Left / Dpi.X - BorderSize;
set => Left = (int)Math.Round((value + BorderSize) * Dpi.X);
}
public int Top
{
get => _top;
set
{
Set(ref _top, value);
RaisePropertyChanged(nameof(TopDip));
}
}
public double TopDip
{
get => Top / Dpi.Y - BorderSize;
set => Top = (int)Math.Round((value + BorderSize) * Dpi.Y);
}
public int Width
{
get => _width;
set
{
Set(ref _width, Math.Max(value, MinWidth));
RaisePropertyChanged(nameof(WidthDip));
}
}
public double WidthDip
{
get => Width / Dpi.X + 2 * BorderSize;
set => Width = (int)Math.Round((value - 2 * BorderSize) * Dpi.X);
}
public int Height
{
get => _height;
set
{
Set(ref _height, Math.Max(value, MinHeight));
RaisePropertyChanged(nameof(HeightDip));
}
}
public double HeightDip
{
get => Height / Dpi.Y + 2 * BorderSize;
set => Height = (int)Math.Round((value - 2 * BorderSize) * Dpi.Y);
}
public Rectangle SelectedRegion
{
get => new Rectangle(Left, Top, Width, Height);
set
{
Left = value.Left;
Top = value.Top;
Width = value.Width;
Height = value.Height;
}
}
public void ResizeFromTop(double VerticalChangeDip)
{
var verticalChange = (int) (VerticalChangeDip * Dpi.Y);
var oldBottom = Top + Height;
var top = Top + verticalChange;
if (top <= 0)
{
Top = 0;
Height = oldBottom;
return;
}
var height = Height - verticalChange;
if (height > MinHeight)
{
Top = top;
Height = height;
}
else
{
Height = MinHeight;
Top = oldBottom - MinHeight;
}
}
public void ResizeFromLeft(double HorizontalChangeDip)
{
var horizontalChange = (int) (HorizontalChangeDip * Dpi.X);
var oldRight = Left + Width;
var left = Left + horizontalChange;
if (left <= 0)
{
Left = 0;
Width = oldRight;
return;
}
var width = Width - horizontalChange;
if (width > MinWidth)
{
Left = left;
Width = width;
}
else
{
Width = MinWidth;
Left = oldRight - MinWidth;
}
}
public ICommand MoveLeftCommand { get; }
public ICommand MoveRightCommand { get; }
public ICommand MoveUpCommand { get; }
public ICommand MoveDownCommand { get; }
public ICommand IncreaseWidthCommand { get; }
public ICommand DecreaseWidthCommand { get; }
public ICommand IncreaseHeightCommand { get; }
public ICommand DecreaseHeightCommand { get; }
public ReactiveCommand ClearAllDrawingsCommand { get; } = new ReactiveCommand();
}
}
================================================
FILE: src/Captura/ViewModels/ScreenPickerViewModel.cs
================================================
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Captura.Video;
namespace Captura.ViewModels
{
public class ScreenPickerViewModel
{
public ScreenPickerViewModel(IScreen Screen, double Scale)
{
this.Screen = Screen;
using var bmp = ScreenShot.Capture(Screen.Rectangle);
var stream = new MemoryStream();
bmp.Save(stream, ImageFormats.Png);
stream.Seek(0, SeekOrigin.Begin);
var decoder = new PngBitmapDecoder(stream, BitmapCreateOptions.None, BitmapCacheOption.Default);
Image = new ImageBrush(decoder.Frames[0]);
Left = (Screen.Rectangle.Left / Dpi.X - SystemParameters.VirtualScreenLeft) * Scale;
Top = (Screen.Rectangle.Top / Dpi.Y - SystemParameters.VirtualScreenTop) * Scale;
Width = Screen.Rectangle.Width / Dpi.X * Scale;
Height = Screen.Rectangle.Height / Dpi.Y * Scale;
}
public double Left { get; }
public double Top { get; }
public double Width { get; }
public double Height { get; }
public IScreen Screen { get; }
public Brush Image { get; }
}
}
================================================
FILE: src/Captura/ViewModels/TrimmerViewModel.cs
================================================
using System;
using System.IO;
using System.Reactive.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;
using Captura.FFmpeg;
using Microsoft.Win32;
using Reactive.Bindings;
using Reactive.Bindings.Extensions;
namespace Captura
{
public class TrimmerViewModel : NotifyPropertyChanged, IDisposable
{
MediaElement _player;
readonly DispatcherTimer _timer;
public bool IsDragging { get; set; }
readonly IReactiveProperty _isOpened = new ReactivePropertySlim();
readonly IReactiveProperty _isTrimming = new ReactivePropertySlim();
public void AssignPlayer(MediaElement Player)
{
_player = Player;
_player.MediaOpened += (S, E) =>
{
From = TimeSpan.Zero;
if (_player.NaturalDuration.HasTimeSpan)
{
To = End = _player.NaturalDuration.TimeSpan;
}
else To = End = TimeSpan.Zero;
PlaybackPosition = From;
_isOpened.Value = true;
};
_timer.Start();
}
public TrimmerViewModel()
{
_timer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(100)
};
_timer.Tick += (Sender, Args) =>
{
if (IsDragging)
return;
RaisePropertyChanged(nameof(PlaybackPosition));
if (IsPlaying && _player.Position > To || _player.Position < From)
{
Stop();
}
};
OpenCommand = _isTrimming
.Select(M => !M)
.ToReactiveCommand()
.WithSubscribe(Open);
PlayCommand = new[]
{
_isOpened,
_isTrimming
.Select(M => !M)
}
.CombineLatestValuesAreAllTrue()
.ToReactiveCommand()
.WithSubscribe(Play);
TrimCommand = new[]
{
_isOpened,
_isTrimming
.Select(M => !M)
}
.CombineLatestValuesAreAllTrue()
.ToReactiveCommand()
.WithSubscribe(Trim);
}
TimeSpan _from, _to, _end;
public TimeSpan From
{
get => _from;
set
{
_from = value;
if (IsPlaying && value + TimeSpan.FromSeconds(1) >= _player.Position)
{
Stop();
}
if (!IsPlaying)
{
PlaybackPosition = value;
}
OnPropertyChanged();
}
}
void Stop()
{
if (!_isPlaying)
return;
_player.Stop();
IsPlaying = false;
PlaybackPosition = From;
}
void Play()
{
if (IsPlaying)
Stop();
else
{
_player.Position = From;
_player.Play();
IsPlaying = true;
}
}
public TimeSpan To
{
get => _to;
set
{
_to = value;
if (IsPlaying && _player.Position + TimeSpan.FromSeconds(1) >= value)
{
Stop();
}
if (!IsPlaying)
{
PlaybackPosition = value;
}
OnPropertyChanged();
}
}
public TimeSpan End
{
get => _end;
set => Set(ref _end, value);
}
string _fileName;
public string FileName
{
get => _fileName;
private set => Set(ref _fileName, value);
}
string _filePath;
public string FilePath
{
get => _filePath;
set
{
_filePath = value;
FileName = Path.GetFileNameWithoutExtension(value);
OnPropertyChanged();
}
}
public ICommand OpenCommand { get; }
public ICommand PlayCommand { get; }
public ICommand TrimCommand { get; }
void Open()
{
var ofd = new OpenFileDialog
{
CheckPathExists = true,
CheckFileExists = true
};
if (ofd.ShowDialog().GetValueOrDefault())
{
Open(ofd.FileName);
}
}
public void Open(string Path)
{
_isOpened.Value = false;
_player.Source = new Uri(Path);
var oldVol = _player.Volume;
// Force Load
_player.Play();
_player.Stop();
_player.Volume = oldVol;
FilePath = Path;
}
bool _isPlaying;
public bool IsPlaying
{
get => _isPlaying;
set => Set(ref _isPlaying, value);
}
public TimeSpan PlaybackPosition
{
get => _player?.Position ?? TimeSpan.Zero;
set => _player.Position = value;
}
public void Dispose()
{
_player.Close();
_player.Source = null;
}
async void Trim()
{
if (!FFmpegService.FFmpegExists)
{
ServiceProvider.Get().ShowUnavailable();
return;
}
var ext = Path.GetExtension(FilePath);
var sfd = new SaveFileDialog
{
AddExtension = true,
DefaultExt = ext,
Filter = $"*{ext}|*{ext}",
FileName = Path.GetFileName(FilePath),
InitialDirectory = Path.GetDirectoryName(FilePath),
CheckPathExists = true
};
if (!sfd.ShowDialog().GetValueOrDefault())
return;
var hasAudio = _player.HasAudio;
_player.Close();
_isTrimming.Value = true;
var trimmer = new FFmpegTrimmer();
try
{
await trimmer.Run(FilePath,
From,
To,
sfd.FileName,
hasAudio);
}
finally
{
_isTrimming.Value = false;
}
MessageBox.Show("Done");
}
}
}
================================================
FILE: src/Captura/Windows/ErrorWindow.xaml
================================================
================================================
FILE: src/Captura/Windows/ErrorWindow.xaml.cs
================================================
using System;
using System.Windows;
namespace Captura.Views
{
public partial class ErrorWindow
{
readonly Exception _exception;
readonly string _message;
public ErrorWindow(Exception Exception, string Message = null)
{
_exception = Exception;
_message = Message;
InitializeComponent();
if (DataContext is ExceptionViewModel vm)
{
vm.Init(Exception, Message);
}
}
void ViewDetails_OnClick(object Sender, RoutedEventArgs E)
{
new ExceptionWindow(_exception, _message).ShowAndFocus();
Close();
}
void OpenFFmpegLog(object Sender, RoutedEventArgs E)
{
SettingsWindow.ShowFFmpegLogs();
}
}
}
================================================
FILE: src/Captura/Windows/ExceptionWindow.xaml
================================================
================================================
FILE: src/Captura/Windows/ExceptionWindow.xaml.cs
================================================
using System;
using System.Windows;
namespace Captura.Views
{
public partial class ExceptionWindow
{
public ExceptionWindow(Exception Exception, string Message = null)
{
InitializeComponent();
if (DataContext is ExceptionViewModel vm)
{
vm.Init(Exception, Message);
}
}
void Close_OnClick(object Sender, RoutedEventArgs E)
{
Close();
}
void OpenFFmpegLog(object Sender, RoutedEventArgs E)
{
SettingsWindow.ShowFFmpegLogs();
}
}
}
================================================
FILE: src/Captura/Windows/FFmpegDownloaderWindow.xaml
================================================
FFmpeg adds support for more output formats to Captura.
Latest build of FFmpeg is downloaded. This can also be used to update FFmpeg.
The Download size is nearly 70 MB.
================================================
FILE: src/Captura/Windows/FFmpegDownloaderWindow.xaml.cs
================================================
using System.Windows;
using System.Windows.Input;
using System.Windows.Shell;
using Captura.ViewModels;
namespace Captura.Views
{
public partial class FFmpegDownloaderWindow
{
FFmpegDownloaderWindow()
{
InitializeComponent();
if (DataContext is FFmpegDownloadViewModel vm)
{
Closing += async (S, E) =>
{
if (!await vm.Cancel())
{
E.Cancel = true;
}
};
vm.ProgressChanged += P =>
{
Dispatcher.Invoke(() =>
{
TaskbarItemInfo.ProgressState = TaskbarItemProgressState.Normal;
TaskbarItemInfo.ProgressValue = P / 100.0;
});
};
vm.AfterDownload += Success =>
{
Dispatcher.Invoke(() =>
{
TaskbarItemInfo.ProgressState = Success ? TaskbarItemProgressState.None : TaskbarItemProgressState.Error;
TaskbarItemInfo.ProgressValue = 1;
});
};
}
}
void CloseButton_Click(object Sender, RoutedEventArgs E) => Close();
void SelectTargetFolder(object Sender, MouseButtonEventArgs E)
{
if (DataContext is FFmpegDownloadViewModel vm)
{
vm.SelectFolderCommand.ExecuteIfCan();
}
}
static FFmpegDownloaderWindow _downloader;
public static void ShowInstance()
{
if (_downloader == null)
{
_downloader = new FFmpegDownloaderWindow();
_downloader.Closed += (Sender, Args) => _downloader = null;
}
_downloader.ShowAndFocus();
}
}
}
================================================
FILE: src/Captura/Windows/MainWindow.xaml
================================================
================================================
FILE: src/Captura/Windows/MainWindow.xaml.cs
================================================
using System.Drawing;
using System.Linq;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Input;
using Captura.Models;
namespace Captura
{
public partial class MainWindow
{
public static MainWindow Instance { get; private set; }
readonly MainWindowHelper _helper;
public MainWindow()
{
Instance = this;
InitializeComponent();
_helper = ServiceProvider.Get();
_helper.MainViewModel.Init(!App.CmdOptions.NoPersist, !App.CmdOptions.Reset);
_helper.HotkeySetup.Setup();
_helper.TimerModel.Init();
Loaded += (Sender, Args) =>
{
RepositionWindowIfOutside();
ServiceProvider.Get().SetupPreview();
_helper.HotkeySetup.ShowUnregistered();
};
if (App.CmdOptions.Tray || _helper.Settings.Tray.MinToTrayOnStartup)
Hide();
Closing += (Sender, Args) =>
{
if (!TryExit())
Args.Cancel = true;
};
// Register to bring this instance to foreground when other instances are launched.
SingleInstanceManager.StartListening(WakeApp);
}
void WakeApp()
{
Dispatcher.Invoke(() =>
{
if (WindowState == WindowState.Minimized)
{
WindowState = WindowState.Normal;
}
Activate();
});
}
void RepositionWindowIfOutside()
{
// Window dimensions taking care of DPI
var rect = new RectangleF((float) Left,
(float) Top,
(float) ActualWidth,
(float) ActualHeight).ApplyDpi();
if (!Screen.AllScreens.Any(M => M.Bounds.Contains(rect)))
{
Left = 50;
Top = 50;
}
}
void Grid_PreviewMouseLeftButtonDown(object Sender, MouseButtonEventArgs Args)
{
DragMove();
Args.Handled = true;
}
void MinButton_Click(object Sender, RoutedEventArgs Args) => SystemCommands.MinimizeWindow(this);
void CloseButton_Click(object Sender, RoutedEventArgs Args)
{
if (_helper.Settings.Tray.MinToTrayOnClose)
{
Hide();
}
else Close();
}
void SystemTray_TrayMouseDoubleClick(object Sender, RoutedEventArgs Args)
{
if (Visibility == Visibility.Visible)
{
Hide();
}
else this.ShowAndFocus();
}
bool TryExit()
{
if (!_helper.RecordingViewModel.CanExit())
return false;
ServiceProvider.Dispose();
return true;
}
void MenuExit_Click(object Sender, RoutedEventArgs Args) => Close();
void HideButton_Click(object Sender, RoutedEventArgs Args) => Hide();
void ShowMainWindow(object Sender, RoutedEventArgs E) => this.ShowAndFocus();
}
}
================================================
FILE: src/Captura/Windows/RegionPickerWindow.xaml
================================================
================================================
FILE: src/Captura/Windows/RegionPickerWindow.xaml.cs
================================================
using System;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media.Imaging;
using Captura.Video;
using Captura.ViewModels;
using MouseEventArgs = System.Windows.Input.MouseEventArgs;
using Point = System.Windows.Point;
namespace Captura
{
public partial class RegionPickerWindow
{
readonly IWindow[] _windows;
readonly IPlatformServices _platformServices;
Predicate Predicate { get; set; }
RegionPickerWindow()
{
InitializeComponent();
Left = SystemParameters.VirtualScreenLeft;
Top = SystemParameters.VirtualScreenTop;
Width = SystemParameters.VirtualScreenWidth;
Height = SystemParameters.VirtualScreenHeight;
UpdateBackground();
_platformServices = ServiceProvider.Get();
_windows = _platformServices
.EnumerateAllWindows()
.ToArray();
}
void UpdateBackground()
{
using var bmp = ScreenShot.Capture();
var stream = new MemoryStream();
bmp.Save(stream, ImageFormats.Png);
stream.Seek(0, SeekOrigin.Begin);
var decoder = new PngBitmapDecoder(stream, BitmapCreateOptions.None, BitmapCacheOption.Default);
BgImg.Source = decoder.Frames[0];
}
void CloseClick(object Sender, RoutedEventArgs E)
{
_start = _end = null;
Close();
}
void UpdateSizeDisplay(Rect? Rect)
{
if (Rect == null)
{
SizeText.Visibility = Visibility.Collapsed;
}
else
{
var rect = Rect.Value;
SizeText.Text = $"{(int) rect.Width} x {(int) rect.Height}";
SizeText.Margin = new Thickness(rect.Left + rect.Width / 2 - SizeText.ActualWidth / 2, rect.Top + rect.Height / 2 - SizeText.ActualHeight / 2, 0, 0);
SizeText.Visibility = Visibility.Visible;
}
}
void WindowMouseMove(object Sender, MouseEventArgs E)
{
if (_isDragging)
{
_end = E.GetPosition(Grid);
var r = GetRegion();
UpdateSizeDisplay(r);
if (r == null)
{
Unhighlight();
return;
}
HighlightRegion(r.Value);
}
else
{
var point = _platformServices.CursorPosition;
_selectedWindow = _windows
.Where(M => Predicate?.Invoke(M) ?? true)
.FirstOrDefault(M => M.Rectangle.Contains(point));
if (_selectedWindow == null)
{
UpdateSizeDisplay(null);
Unhighlight();
}
else
{
var rect = GetSelectedWindowRectangle().Value;
UpdateSizeDisplay(rect);
HighlightRegion(rect);
}
}
}
Rect? GetSelectedWindowRectangle()
{
if (_selectedWindow == null)
return null;
var rect = _selectedWindow.Rectangle;
return new Rect(-Left + rect.X / Dpi.X,
-Top + rect.Y / Dpi.Y,
rect.Width / Dpi.X,
rect.Height / Dpi.Y);
}
bool _isDragging;
Point? _start, _end;
IWindow _selectedWindow;
void WindowMouseLeftButtonDown(object Sender, MouseButtonEventArgs E)
{
_isDragging = true;
_start = E.GetPosition(Grid);
_end = null;
}
void WindowMouseLeftButtonUp(object Sender, MouseButtonEventArgs E)
{
if (!_isDragging)
return;
var current = E.GetPosition(Grid);
if (current != _start)
{
_end = E.GetPosition(Grid);
}
else if (GetSelectedWindowRectangle() is Rect rect)
{
_start = rect.Location;
_end = new Point(rect.Right, rect.Bottom);
}
Close();
}
Rect? GetRegion()
{
if (_start == null || _end == null)
{
return null;
}
var end = _end.Value;
var start = _start.Value;
if (end.X < start.X)
{
var t = start.X;
start.X = end.X;
end.X = t;
}
if (end.Y < start.Y)
{
var t = start.Y;
start.Y = end.Y;
end.Y = t;
}
var width = end.X - start.X;
var height = end.Y - start.Y;
if (width < 0.01 || height < 0.01)
{
return null;
}
return new Rect(start.X, start.Y, width, height);
}
Rectangle? GetRegionScaled()
{
var rect = GetRegion();
if (rect == null)
{
return null;
}
var r = rect.Value;
return new Rectangle((int) ((Left + r.X) * Dpi.X),
(int)((Top + r.Y) * Dpi.Y),
(int)(r.Width * Dpi.X),
(int)(r.Height * Dpi.Y));
}
public static Rectangle? PickRegion()
{
var picker = new RegionPickerWindow();
picker.ShowDialog();
return picker.GetRegionScaled();
}
void Unhighlight()
{
Border.Visibility = Visibility.Collapsed;
PunctRegion.Region = null;
}
void HighlightRegion(Rect Region)
{
var border = RegionSelectorViewModel.BorderSize;
var regionWithBorder = new Rect(Region.X - border,
Region.Y - border,
Region.Width + 2 * border,
Region.Height + 2 * border);
Border.Margin = new Thickness(regionWithBorder.X, regionWithBorder.Y, 0, 0);
Border.Width = regionWithBorder.Width;
Border.Height = regionWithBorder.Height;
PunctRegion.Region = regionWithBorder;
Border.Visibility = Visibility.Visible;
}
}
}
================================================
FILE: src/Captura/Windows/RegionSelector.xaml
================================================
================================================
FILE: src/Captura/Windows/RegionSelector.xaml.cs
================================================
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using Captura.ViewModels;
using Color = System.Windows.Media.Color;
using Cursors = System.Windows.Input.Cursors;
namespace Captura
{
public partial class RegionSelector
{
readonly RegionSelectorViewModel _viewModel;
public RegionSelector(RegionSelectorViewModel ViewModel)
{
_viewModel = ViewModel;
InitializeComponent();
// Prevent Closing by User
Closing += (S, E) => E.Cancel = true;
ViewModel
.BrushColor
.Subscribe(M => InkCanvas.DefaultDrawingAttributes.Color = M);
ViewModel
.BrushSize
.Subscribe(M => InkCanvas.DefaultDrawingAttributes.Height = InkCanvas.DefaultDrawingAttributes.Width = M);
ViewModel
.SelectedTool
.Subscribe(OnToolChange);
ViewModel
.ClearAllDrawingsCommand
.Subscribe(() => InkCanvas.Strokes.Clear());
InkCanvas.DefaultDrawingAttributes.FitToCurve = true;
}
void OnToolChange(InkCanvasEditingMode Tool)
{
InkCanvas.EditingMode = Tool;
if (Tool == InkCanvasEditingMode.Ink)
{
InkCanvas.UseCustomCursor = true;
InkCanvas.Cursor = Cursors.Pen;
}
else InkCanvas.UseCustomCursor = false;
InkCanvas.Background = new SolidColorBrush(Tool == InkCanvasEditingMode.None
? Colors.Transparent
: Color.FromArgb(1, 0, 0, 0));
}
// Prevent Maximizing
protected override void OnStateChanged(EventArgs E)
{
if (WindowState != WindowState.Normal)
WindowState = WindowState.Normal;
base.OnStateChanged(E);
}
protected override void OnRenderSizeChanged(SizeChangedInfo SizeInfo)
{
InkCanvas.Strokes.Clear();
base.OnRenderSizeChanged(SizeInfo);
}
public IntPtr Handle => new WindowInteropHelper(this).Handle;
void UIElement_OnPreviewMouseLeftButtonDown(object Sender, MouseButtonEventArgs E)
{
DragMove();
}
void Thumb_OnDragDelta(object Sender, DragDeltaEventArgs E)
{
void DoTop() => _viewModel.ResizeFromTop(E.VerticalChange);
void DoLeft() => _viewModel.ResizeFromLeft(E.HorizontalChange);
void DoBottom()
{
var height = Region.Height + E.VerticalChange;
if (height > 0)
Region.Height = height;
}
void DoRight()
{
var width = Region.Width + E.HorizontalChange;
if (width > 0)
Region.Width = width;
}
if (Sender is FrameworkElement element)
{
switch (element.Tag)
{
case "Bottom":
DoBottom();
break;
case "Left":
DoLeft();
break;
case "Right":
DoRight();
break;
case "TopLeft":
DoTop();
DoLeft();
break;
case "TopRight":
DoTop();
DoRight();
break;
case "BottomLeft":
DoBottom();
DoLeft();
break;
case "BottomRight":
DoBottom();
DoRight();
break;
}
}
}
}
}
================================================
FILE: src/Captura/Windows/ScreenPickerWindow.xaml
================================================
================================================
FILE: src/Captura/Windows/ScreenPickerWindow.xaml.cs
================================================
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using Captura.Video;
using Captura.ViewModels;
using Reactive.Bindings;
namespace Captura
{
public partial class ScreenPickerWindow
{
const double Scale = 0.15;
public ObservableCollection ScreenPickerViewModels { get; } = new ObservableCollection();
public ICommand SelectScreenCommand { get; }
ScreenPickerWindow()
{
SelectScreenCommand = new ReactiveCommand()
.WithSubscribe(M =>
{
SelectedScreen = M;
Close();
});
InitializeComponent();
ScreenContainer.Width = SystemParameters.VirtualScreenWidth * Scale;
ScreenContainer.Height = SystemParameters.VirtualScreenHeight * Scale;
var platformServices = ServiceProvider.Get();
var screens = platformServices.EnumerateScreens().ToArray();
foreach (var screen in screens)
{
ScreenPickerViewModels.Add(new ScreenPickerViewModel(screen, Scale));
}
}
public IScreen SelectedScreen { get; private set; }
void CloseClick(object Sender, RoutedEventArgs E)
{
SelectedScreen = null;
Close();
}
public static IScreen PickScreen()
{
var picker = new ScreenPickerWindow
{
Owner = MainWindow.Instance
};
picker.ShowDialog();
return picker.SelectedScreen;
}
}
}
================================================
FILE: src/Captura/Windows/SettingsWindow.xaml
================================================
================================================
FILE: src/Captura/Windows/SettingsWindow.xaml.cs
================================================
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Navigation;
namespace Captura
{
public partial class SettingsWindow
{
SettingsWindow()
{
InitializeComponent();
}
static SettingsWindow _instance;
public static void ShowInstance()
{
if (_instance == null)
{
_instance = new SettingsWindow();
_instance.Closed += (S, E) => _instance = null;
}
_instance.ShowAndFocus();
}
void OnGoToPage(object Sender, ExecutedRoutedEventArgs E)
{
if (Sender is Frame frame)
{
switch (E.Parameter)
{
case string s:
frame.Navigate(new Uri(s, UriKind.RelativeOrAbsolute));
break;
case { } o:
frame.Navigate(o);
break;
}
}
}
static void ShowPage(string PageName)
{
ShowInstance();
_instance.NavFrame.Navigate(new Uri($"/Pages/{PageName}Page.xaml", UriKind.RelativeOrAbsolute));
}
public static void ShowFFmpegLogs() => ShowPage("FFmpegLogs");
public static void ShowWebcamPage()
{
ShowInstance();
_instance.NavFrame.Navigate(ServiceProvider.Get());
}
void OnGoBack(object Sender, RoutedEventArgs E)
{
NavFrame.GoBack();
}
void OnGoNext(object Sender, RoutedEventArgs E)
{
NavFrame.GoForward();
}
void NavFrame_OnNavigated(object Sender, NavigationEventArgs E)
{
if (E.Content is Page page)
{
var transform = new TranslateTransform();
page.RenderTransform = transform;
var anim = new DoubleAnimation
{
Duration = TimeSpan.FromSeconds(0.3),
DecelerationRatio = 0.5,
From = 100,
To = 0
};
transform.BeginAnimation(TranslateTransform.XProperty, anim);
}
else Console.WriteLine(E.Content);
}
}
}
================================================
FILE: src/Captura/Windows/TrimmerWindow.xaml
================================================
================================================
FILE: src/Captura/Windows/TrimmerWindow.xaml.cs
================================================
using System;
using System.Windows.Controls;
using System.Windows.Input;
namespace Captura.Views
{
public partial class TrimmerWindow
{
public TrimmerWindow()
{
InitializeComponent();
if (DataContext is TrimmerViewModel vm)
{
vm.AssignPlayer(MediaElement);
Closing += (S, E) => vm.Dispose();
}
}
public void Open(string FileName)
{
if (DataContext is TrimmerViewModel vm)
{
vm.Open(FileName);
}
}
void Slider_PreviewMouseLeftButtonUp(object Sender, MouseButtonEventArgs E)
{
if (DataContext is TrimmerViewModel vm && Sender is Slider slider)
{
if (!vm.IsDragging)
return;
vm.PlaybackPosition = TimeSpan.FromSeconds(slider.Value);
vm.IsDragging = false;
}
}
void Slider_PreviewMouseLeftButtonDown(object Sender, MouseButtonEventArgs E)
{
if (DataContext is TrimmerViewModel vm)
{
vm.IsDragging = true;
}
}
void Slider_MouseLeftButtonUp(object Sender, MouseButtonEventArgs E)
{
if (DataContext is TrimmerViewModel vm && Sender is Slider slider)
{
vm.PlaybackPosition = TimeSpan.FromSeconds(slider.Value);
}
}
}
}
================================================
FILE: src/Captura/Windows/VideoSourcePickerWindow.xaml
================================================
================================================
FILE: src/Captura/Windows/VideoSourcePickerWindow.xaml.cs
================================================
using System;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using Captura.Video;
using Color = System.Windows.Media.Color;
using Cursors = System.Windows.Input.Cursors;
using HorizontalAlignment = System.Windows.HorizontalAlignment;
using MouseEventArgs = System.Windows.Input.MouseEventArgs;
namespace Captura
{
public partial class VideoSourcePickerWindow
{
enum VideoPickerMode
{
Window,
Screen
}
readonly VideoPickerMode _mode;
Predicate Predicate { get; set; }
VideoSourcePickerWindow(VideoPickerMode Mode)
{
_mode = Mode;
InitializeComponent();
Left = SystemParameters.VirtualScreenLeft;
Top = SystemParameters.VirtualScreenTop;
Width = SystemParameters.VirtualScreenWidth;
Height = SystemParameters.VirtualScreenHeight;
UpdateBackground();
var platformServices = ServiceProvider.Get();
_screens = platformServices.EnumerateScreens().ToArray();
_windows = platformServices.EnumerateWindows().ToArray();
ShowCancelText();
}
readonly IScreen[] _screens;
readonly IWindow[] _windows;
public IScreen SelectedScreen { get; private set; }
public IWindow SelectedWindow { get; private set; }
void UpdateBackground()
{
using var bmp = ScreenShot.Capture();
var stream = new MemoryStream();
bmp.Save(stream, ImageFormats.Png);
stream.Seek(0, SeekOrigin.Begin);
var decoder = new PngBitmapDecoder(stream, BitmapCreateOptions.None, BitmapCacheOption.Default);
Background = new ImageBrush(decoder.Frames[0]);
}
void BeginClose()
{
var duration = new Duration(TimeSpan.FromMilliseconds(200));
var opacityAnim = new DoubleAnimation(0, duration);
opacityAnim.Completed += (S, E) => Close();
BeginAnimation(OpacityProperty, opacityAnim);
}
void ShowCancelText()
{
foreach (var screen in _screens)
{
var bounds = screen.Rectangle;
var left = -Left + bounds.Left / Dpi.X;
var top = -Top + bounds.Top / Dpi.Y;
var width = bounds.Width / Dpi.X;
var height = bounds.Height / Dpi.Y;
var container = new ContentControl
{
Width = width,
Height = height,
Margin = new Thickness(left, top, 0, 0),
VerticalAlignment = VerticalAlignment.Top,
HorizontalAlignment = HorizontalAlignment.Left
};
var textBlock = new TextBlock
{
Text = $"Select {_mode} or Press Esc to Cancel",
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Padding = new Thickness(10, 5, 10, 5),
Foreground = new SolidColorBrush(Colors.White),
Background = new SolidColorBrush(Color.FromArgb(183, 0, 0, 0))
};
container.Content = textBlock;
Grid.Children.Add(container);
}
}
void CloseClick(object Sender, RoutedEventArgs E)
{
SelectedScreen = null;
SelectedWindow = null;
BeginClose();
}
Rectangle? _lastRectangle;
void UpdateBorderAndCursor(Rectangle? Rect)
{
if (_lastRectangle == Rect)
return;
_lastRectangle = Rect;
var storyboard = new Storyboard();
var duration = new Duration(TimeSpan.FromMilliseconds(100));
if (Rect == null)
{
Cursor = Cursors.Arrow;
var opacityAnim = new DoubleAnimation(0, duration);
Storyboard.SetTargetProperty(opacityAnim, new PropertyPath(nameof(Opacity)));
storyboard.Children.Add(opacityAnim);
}
else
{
Cursor = Cursors.Hand;
var opacityAnim = new DoubleAnimation(1, duration);
Storyboard.SetTargetProperty(opacityAnim, new PropertyPath(nameof(Opacity)));
storyboard.Children.Add(opacityAnim);
var rect = Rect.Value;
var margin = new Thickness(-Left + rect.Left / Dpi.X, -Top + rect.Top / Dpi.Y, 0, 0);
var marginAnim = new ThicknessAnimation(margin, duration);
Storyboard.SetTargetProperty(marginAnim, new PropertyPath(nameof(Margin)));
storyboard.Children.Add(marginAnim);
var widthAnim = new DoubleAnimation(Border.ActualWidth, rect.Width / Dpi.X, duration);
Storyboard.SetTargetProperty(widthAnim, new PropertyPath(nameof(Width)));
storyboard.Children.Add(widthAnim);
var heightAnim = new DoubleAnimation(Border.ActualHeight, rect.Height / Dpi.Y, duration);
Storyboard.SetTargetProperty(heightAnim, new PropertyPath(nameof(Height)));
storyboard.Children.Add(heightAnim);
}
Border.BeginStoryboard(storyboard);
}
void WindowMouseMove(object Sender, MouseEventArgs E)
{
var platformServices = ServiceProvider.Get();
var point = platformServices.CursorPosition;
switch (_mode)
{
case VideoPickerMode.Screen:
SelectedScreen = _screens.FirstOrDefault(M => M.Rectangle.Contains(point));
UpdateBorderAndCursor(SelectedScreen?.Rectangle);
break;
case VideoPickerMode.Window:
SelectedWindow = _windows
.Where(M => Predicate?.Invoke(M) ?? true)
.FirstOrDefault(M => M.Rectangle.Contains(point));
UpdateBorderAndCursor(SelectedWindow?.Rectangle);
break;
}
}
void WindowMouseLeftButtonDown(object Sender, MouseButtonEventArgs E)
{
switch (_mode)
{
case VideoPickerMode.Screen when SelectedScreen != null:
case VideoPickerMode.Window when SelectedWindow != null:
BeginClose();
break;
}
}
public static IScreen PickScreen()
{
var picker = new VideoSourcePickerWindow(VideoPickerMode.Screen);
picker.ShowDialog();
return picker.SelectedScreen;
}
public static IWindow PickWindow(Predicate Filter)
{
var picker = new VideoSourcePickerWindow(VideoPickerMode.Window)
{
Border =
{
BorderThickness = new Thickness(5)
},
Predicate = Filter
};
picker.ShowDialog();
return picker.SelectedWindow;
}
}
}
================================================
FILE: src/Captura/Windows/YouTubeUploaderWindow.xaml
================================================
================================================
FILE: src/Captura/Windows/YouTubeUploaderWindow.xaml.cs
================================================
using System.Windows;
using Captura.ViewModels;
namespace Captura
{
public partial class YouTubeUploaderWindow
{
public YouTubeUploaderWindow()
{
InitializeComponent();
Closing += async (S, E) =>
{
if (DataContext is YouTubeUploaderViewModel vm)
{
if (!await vm.Cancel())
{
E.Cancel = true;
}
}
};
}
public async void Open(string FileName)
{
if (DataContext is YouTubeUploaderViewModel vm)
{
await vm.Init(FileName);
}
}
void Cancel_Click(object Sender, RoutedEventArgs E)
{
Close();
}
}
}
================================================
FILE: src/Captura/app.config
================================================
================================================
FILE: src/Captura.Audio/Captura.Audio.csproj
================================================
netstandard2.0
================================================
FILE: src/Captura.Audio/IAudioFileWriter.cs
================================================
using System;
namespace Captura.Audio
{
///
/// Encodes Audio into an audio file.
///
public interface IAudioFileWriter : IDisposable
{
///
/// Writes to file.
///
void Write(byte[] Data, int Offset, int Count);
///
/// Writes all buffered data to file.
///
void Flush();
}
}
================================================
FILE: src/Captura.Audio/IAudioItem.cs
================================================
using System;
namespace Captura.Audio
{
public interface IAudioItem : IDisposable
{
string Name { get; }
bool IsLoopback { get; }
void StartListeningForPeakLevel();
void StopListeningForPeakLevel();
double PeakLevel { get; }
}
}
================================================
FILE: src/Captura.Audio/IAudioProvider.cs
================================================
using System;
namespace Captura.Audio
{
///
/// Provides Recorded Audio.
///
public interface IAudioProvider : IDisposable
{
///
/// Gets the Recording WaveFormat.
///
WaveFormat WaveFormat { get; }
///
/// Start Recording.
///
void Start();
///
/// Stop Recording.
///
void Stop();
///
/// Read data into a buffer.
///
/// Buffer to read data into.
/// Offset from which data should be written to the buffer.
/// Number of bytes to write to the buffer.
/// Number of bytes read.
int Read(byte[] Buffer, int Offset, int Length);
}
}
================================================
FILE: src/Captura.Audio/IAudioSource.cs
================================================
using System;
using System.Collections.Generic;
namespace Captura.Audio
{
public interface IAudioSource : IDisposable
{
string Name { get; }
IEnumerable Microphones { get; }
IAudioItem DefaultMicrophone { get; }
IEnumerable Speakers { get; }
IAudioItem DefaultSpeaker { get; }
IAudioProvider GetAudioProvider(IAudioItem Microphone, IAudioItem Speaker);
event Action DevicesUpdated;
}
}
================================================
FILE: src/Captura.Audio/IAudioWriterItem.cs
================================================
namespace Captura.Audio
{
public interface IAudioWriterItem
{
string Name { get; }
string Extension { get; }
IAudioFileWriter GetAudioFileWriter(string FileName, WaveFormat Wf, int AudioQuality);
}
}
================================================
FILE: src/Captura.Audio/WaveFormat/WaveFormat.cs
================================================
using System;
using System.IO;
namespace Captura.Audio
{
///
/// Represents a Wave file format
///
public class WaveFormat
{
///
/// Creates a new PCM 44.1Khz stereo 16 bit format
///
public WaveFormat() : this(44100, 16, 2) { }
///
/// Creates a new 16 bit wave format with the specified sample
/// rate and channel count
///
/// Sample Rate
/// Number of channels
public WaveFormat(int SampleRate, int Channels) : this(SampleRate, 16, Channels) { }
///
/// Creates a new PCM format with the specified sample rate, bit depth and channels
///
public WaveFormat(int SampleRate, int BitsPerSample, int Channels)
{
if (Channels < 1)
throw new ArgumentOutOfRangeException(nameof(Channels), "Channels must be 1 or greater");
// minimum 16 bytes, sometimes 18 for PCM
Encoding = WaveFormatEncoding.Pcm;
this.Channels = (short)Channels;
this.SampleRate = SampleRate;
this.BitsPerSample = (short)BitsPerSample;
ExtraSize = 0;
BlockAlign = (short)(Channels * (BitsPerSample / 8));
AverageBytesPerSecond = this.SampleRate * BlockAlign;
}
///
/// Creates a new 32 bit IEEE floating point wave format
///
/// sample rate
/// number of channels
public static WaveFormat CreateIeeeFloatWaveFormat(int SampleRate, int Channels)
{
return new WaveFormat
{
Encoding = WaveFormatEncoding.Float,
Channels = (short)Channels,
BitsPerSample = 32,
SampleRate = SampleRate,
BlockAlign = (short)(4 * Channels),
AverageBytesPerSecond = SampleRate * 4 * Channels,
ExtraSize = 0
};
}
///
/// Returns the encoding type used
///
public WaveFormatEncoding Encoding { get; set; }
///
/// Writes this WaveFormat object to a stream
///
/// the output stream
public virtual void Serialize(BinaryWriter Writer)
{
Writer.Write((short)Encoding);
Writer.Write((short)Channels);
Writer.Write(SampleRate);
Writer.Write(AverageBytesPerSecond);
Writer.Write((short)BlockAlign);
Writer.Write((short)BitsPerSample);
Writer.Write((short)ExtraSize);
}
///
/// Returns the number of channels (1=mono,2=stereo etc)
///
public int Channels { get; set; }
///
/// Returns the sample rate (samples per second)
///
public int SampleRate { get; set; }
///
/// Returns the average number of bytes used per second
///
public int AverageBytesPerSecond { get; set; }
///
/// Returns the block alignment
///
public int BlockAlign { get; set; }
///
/// Returns the number of bits per sample (usually 16 or 32, sometimes 24 or 8)
/// Can be 0 for some codecs
///
public int BitsPerSample { get; set; }
///
/// Returns the number of extra bytes used by this waveformat.
/// Often 0, except for compressed formats which store extra data after the WAVEFORMATEX header
///
public int ExtraSize { get; set; }
}
}
================================================
FILE: src/Captura.Audio/WaveFormat/WaveFormatEncoding.cs
================================================
namespace Captura.Audio
{
///
/// WaveFormat Encoding
///
public enum WaveFormatEncoding : ushort
{
///
/// Unknown.
///
Unknown = 0x0000,
///
/// Pulse Code Modulation.
///
Pcm = 0x0001,
///
/// IEEE Float.
///
Float = 0x0003,
///
/// MPEG Layer 3 (MP3).
///
Mp3 = 0x0055,
///
/// Wave Format Extensible.
///
Extensible = 0xFFFE
}
}
================================================
FILE: src/Captura.Base/Captura.Base.csproj
================================================
netstandard2.0
================================================
FILE: src/Captura.Base/ComparableExtensions.cs
================================================
using System;
namespace Captura
{
public static class ComparableExtensions
{
public static T Clip(this T Value, T Minimum, T Maximum) where T : IComparable
{
if (Value.CompareTo(Minimum) < 0)
return Minimum;
if (Value.CompareTo(Maximum) > 0)
return Maximum;
return Value;
}
}
}
================================================
FILE: src/Captura.Base/DelegateCommand.cs
================================================
using System;
using System.Windows.Input;
using Captura.Models;
namespace Captura
{
public class DelegateCommand : ICommand
{
readonly Action