├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── dependabot.yml └── workflows │ └── winget.yml ├── .gitignore ├── .metadata ├── LICENSE ├── README.md ├── README_zh_CN.md ├── analysis_options.yaml ├── assets ├── fonts │ ├── IBMPlexSans-Bold.ttf │ ├── IBMPlexSans-ExtraLight.ttf │ ├── IBMPlexSans-Light.ttf │ ├── IBMPlexSans-Medium.ttf │ ├── IBMPlexSans-Regular.ttf │ ├── IBMPlexSans-SemiBold.ttf │ ├── IBMPlexSans-Thin.ttf │ ├── Inter-Regular.ttf │ ├── Inter-SemiBold.ttf │ └── Inter-Thin.ttf ├── icons │ ├── alt.svg │ ├── arrow-down.svg │ ├── arrow-left.svg │ ├── arrow-right.svg │ ├── arrow-up.svg │ ├── backspace.svg │ ├── caps-lock.svg │ ├── caps-locked.svg │ ├── control.svg │ ├── delete.svg │ ├── drag.svg │ ├── end.svg │ ├── enter.svg │ ├── escape.svg │ ├── function.svg │ ├── home.svg │ ├── insert.svg │ ├── left-click.svg │ ├── mac-meta.svg │ ├── meta.svg │ ├── mouse.svg │ ├── num-lock.svg │ ├── num-locked.svg │ ├── page-down.svg │ ├── page-up.svg │ ├── pause.svg │ ├── print-screen.svg │ ├── right-click.svg │ ├── scroll-lock.svg │ ├── scroll-locked.svg │ ├── scroll.svg │ ├── shift.svg │ ├── space.svg │ └── tab.svg ├── img │ ├── align-bottom.svg │ ├── align-horizontally.svg │ ├── align-left.svg │ ├── align-right.svg │ ├── align-top.svg │ ├── align-vertically.svg │ ├── arrow-right.svg │ ├── chevron-down.svg │ ├── clipboard-tick.svg │ ├── clipboard.svg │ ├── cog-wheel.svg │ ├── cross.svg │ ├── discord-logo.svg │ ├── edit.svg │ ├── error.svg │ ├── github-logo.svg │ ├── grab.svg │ ├── keyboard.svg │ ├── keycap-grid.svg │ ├── linked.svg │ ├── logo.svg │ ├── mail.svg │ ├── monitor.svg │ ├── more.svg │ ├── mouse.svg │ ├── opencollective-logo.svg │ ├── tray-off.ico │ ├── tray-off.png │ ├── tray-on.ico │ ├── tray-on.png │ └── unlinked.svg └── logo.png ├── lib ├── app.dart ├── config │ ├── assets.dart │ ├── config.dart │ ├── extensions.dart │ ├── style.dart │ └── theme.dart ├── domain │ ├── services │ │ ├── key_maps.dart │ │ ├── raw_keyboard_mouse.dart │ │ └── services.dart │ └── vault │ │ ├── json.dart │ │ └── vault.dart ├── main.dart ├── providers │ ├── key_event.dart │ ├── key_event_data.dart │ ├── key_style.dart │ └── providers.dart └── windows │ ├── error │ └── error.dart │ ├── key_visualizer │ ├── key_visualizer.dart │ └── widgets │ │ ├── animations │ │ ├── animations.dart │ │ ├── fade_keycap_animation.dart │ │ ├── grow_keycap_animation.dart │ │ ├── key_cap_animation.dart │ │ ├── slide_keycap_animation.dart │ │ └── wham_keycap_animation.dart │ │ ├── keycap_group.dart │ │ ├── keycap_wrapper.dart │ │ ├── keycaps │ │ ├── elevated_keycap.dart │ │ ├── flat_keycap.dart │ │ ├── keycap.dart │ │ ├── keycap_content.dart │ │ ├── keycaps.dart │ │ ├── mechanical_keycap.dart │ │ ├── minimal_keycap.dart │ │ ├── plastic_keycap.dart │ │ └── retro_keycap.dart │ │ └── widgets.dart │ ├── mouse_visualizer │ ├── mouse_visualizer.dart │ └── widgets │ │ ├── cursor_highlight.dart │ │ ├── cursor_highlight_wrapper.dart │ │ ├── filled_cursor_higlight.dart │ │ ├── focus_cursor_higlight.dart │ │ ├── static_cursor_higlight.dart │ │ └── widgets.dart │ ├── settings │ ├── settings.dart │ ├── views │ │ ├── about.dart │ │ ├── appearance.dart │ │ ├── general.dart │ │ ├── mouse.dart │ │ ├── style │ │ │ ├── background.dart │ │ │ ├── border.dart │ │ │ ├── color.dart │ │ │ ├── layout.dart │ │ │ ├── style.dart │ │ │ └── typography.dart │ │ └── views.dart │ └── widgets │ │ ├── color_picker.dart │ │ ├── color_swatches.dart │ │ ├── cross_dropdown.dart │ │ ├── cross_expansion_tile.dart │ │ ├── cross_icon_button.dart │ │ ├── cross_slider.dart │ │ ├── cross_switch.dart │ │ ├── cross_text_button.dart │ │ ├── gradient_picker.dart │ │ ├── hotkey_input.dart │ │ ├── panel_item.dart │ │ ├── sidebar.dart │ │ ├── sub_panel_item.dart │ │ └── widgets.dart │ └── shared │ ├── shared.dart │ ├── spacing.dart │ └── svg_icon.dart ├── linux ├── .gitignore ├── CMakeLists.txt ├── flutter │ ├── CMakeLists.txt │ ├── generated_plugin_registrant.cc │ ├── generated_plugin_registrant.h │ └── generated_plugins.cmake ├── main.cc ├── my_application.cc └── my_application.h ├── macos ├── .gitignore ├── Flutter │ ├── Flutter-Debug.xcconfig │ ├── Flutter-Release.xcconfig │ └── GeneratedPluginRegistrant.swift ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── app_icon_1024.png │ │ │ ├── app_icon_128.png │ │ │ ├── app_icon_16.png │ │ │ ├── app_icon_256.png │ │ │ ├── app_icon_32.png │ │ │ ├── app_icon_512.png │ │ │ └── app_icon_64.png │ ├── Base.lproj │ │ └── MainMenu.xib │ ├── Configs │ │ ├── AppInfo.xcconfig │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ └── Warnings.xcconfig │ ├── DebugProfile.entitlements │ ├── Info.plist │ ├── MainFlutterWindow.swift │ └── Release.entitlements └── RunnerTests │ └── RunnerTests.swift ├── previews ├── banner.png ├── banner.svg ├── settings.svg └── visualizer-bar.svg ├── pubspec.lock ├── pubspec.yaml └── windows ├── .gitignore ├── CMakeLists.txt ├── flutter ├── CMakeLists.txt ├── generated_plugin_registrant.cc ├── generated_plugin_registrant.h └── generated_plugins.cmake └── runner ├── CMakeLists.txt ├── Runner.rc ├── flutter_window.cpp ├── flutter_window.h ├── main.cpp ├── resource.h ├── resources └── app_icon.ico ├── runner.exe.manifest ├── utils.cpp ├── utils.h ├── win32_window.cpp └── win32_window.h /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [mulaRahul] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: keyviz 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **Version**: v0.x.x 14 | 15 | **To Reproduce** 16 | Steps to reproduce the behavior: 17 | 1. Go to '...' 18 | 2. Click on '....' 19 | 3. Scroll down to '....' 20 | 4. See error 21 | 22 | **Expected behavior** 23 | A clear and concise description of what you expected to happen. 24 | 25 | **Screenshots** 26 | If applicable, add screenshots to help explain your problem. 27 | 28 | **Desktop (please complete the following information):** 29 | - Windows 10/11 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | - package-ecosystem: "pub" 8 | directory: "/" 9 | schedule: 10 | interval: "daily" 11 | -------------------------------------------------------------------------------- /.github/workflows/winget.yml: -------------------------------------------------------------------------------- 1 | name: Publish to Winget 2 | 3 | on: 4 | release: 5 | types: [released] 6 | 7 | jobs: 8 | publish: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: vedantmgoyal2009/winget-releaser@v2 12 | with: 13 | identifier: mulaRahul.Keyviz 14 | token: ${{ secrets.WINGET_TOKEN }} 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | .vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | **/doc/api/ 26 | **/ios/Flutter/.last_build_id 27 | .dart_tool/ 28 | .flutter-plugins 29 | .flutter-plugins-dependencies 30 | .packages 31 | .pub-cache/ 32 | .pub/ 33 | /build/ 34 | 35 | # Symbolication related 36 | app.*.symbols 37 | 38 | # Obfuscation related 39 | app.*.map.json -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled. 5 | 6 | version: 7 | revision: 796c8ef79279f9c774545b3771238c3098dbefab 8 | channel: stable 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: 796c8ef79279f9c774545b3771238c3098dbefab 17 | base_revision: 796c8ef79279f9c774545b3771238c3098dbefab 18 | - platform: android 19 | create_revision: 796c8ef79279f9c774545b3771238c3098dbefab 20 | base_revision: 796c8ef79279f9c774545b3771238c3098dbefab 21 | - platform: ios 22 | create_revision: 796c8ef79279f9c774545b3771238c3098dbefab 23 | base_revision: 796c8ef79279f9c774545b3771238c3098dbefab 24 | - platform: linux 25 | create_revision: 796c8ef79279f9c774545b3771238c3098dbefab 26 | base_revision: 796c8ef79279f9c774545b3771238c3098dbefab 27 | - platform: macos 28 | create_revision: 796c8ef79279f9c774545b3771238c3098dbefab 29 | base_revision: 796c8ef79279f9c774545b3771238c3098dbefab 30 | - platform: web 31 | create_revision: 796c8ef79279f9c774545b3771238c3098dbefab 32 | base_revision: 796c8ef79279f9c774545b3771238c3098dbefab 33 | - platform: windows 34 | create_revision: 796c8ef79279f9c774545b3771238c3098dbefab 35 | base_revision: 796c8ef79279f9c774545b3771238c3098dbefab 36 | 37 | # User provided section 38 | 39 | # List of Local paths (relative to this file) that should be 40 | # ignored by the migrate tool. 41 | # 42 | # Files that are not part of the templates will be ignored by default. 43 | unmanaged_files: 44 | - 'lib/main.dart' 45 | - 'ios/Runner.xcodeproj/project.pbxproj' 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![keyviz-2.0](previews/banner.svg) 2 | 3 | Keyviz is a free and open-source software to visualise your keystrokes and mouse actions in real time! Let your audience know what handy shortcuts/keys you're pressing during screencasts, presentations, collaborations, or whenever you need it. 4 | 5 | **English** | [简体中文](./README_zh_CN.md) 6 | 7 | # ⌨️ Keystrokes & 🖱️ Mouse Actions 8 | 9 | Now you can visualize mouse actions! Not only mouse clicks, you can also visualize mouse actions along with keystrokes like Cmd + Click, Alt + Drag, etc. 10 | 11 | ![key-visualizer](previews/visualizer-bar.svg) 12 | 13 | # 🎨 Stylize 14 | 15 | Don't restrain yourself to just black & white! You can customize every aspect of the visualization. The visualisation's style, size, colour (modifier and regular keys), border, icon, etc. 16 | 17 | ![settings-window](previews/settings.svg) 18 | 19 | Powerful and easy-to-use configuration options. 20 | 21 | - Filter normal keys and only display shortcuts like Cmd + K **(Default)** 22 | - Adjust the visualisation position on the screen 23 | - Decide how much the visualisation lingers on the screen before animating out 24 | - Switch between animation presets to animate your visualisation in & out 25 | 26 |
27 | 28 | # 📥 Installation 29 | 30 | You can download the latest version of keyviz from the [Github Releases](https://github.com/mulaRahul/keyviz/releases) page. For the installer, unzip the downloaded file, run the installer and follow the familiar steps to install keyviz. 31 | 32 | Below are the platform specifics options and requirements - 33 | 34 |
35 | 36 | 🪟 Windows 37 | 38 | ### 👜 Microsoft Store 39 | You can download keyviz directly from the [microsoft store](https://apps.microsoft.com/detail/Keyviz/9phzpj643p7l?mode=direct). 40 | 41 | ### 🥄 Scoop 42 | ```bash 43 | scoop bucket add extras # first, add the bucket 44 | scoop install keyviz 45 | ``` 46 | 47 | ### 📦 Winget 48 | ```bash 49 | winget install mulaRahul.Keyviz 50 | ``` 51 | 52 |
53 | 54 |
55 | *.dll missing error? 56 | 57 | If you're getting a `.dll` missing error after installing the application, you're missing the required Visual C++ redistributables. You can get the same from here [VSC++ Redist](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170). 58 | 59 |
60 | 61 |
62 | 63 |
64 | 65 |
66 | 67 | 🍎 MacOS 68 | 69 | ### 🔒 Permission 70 | 71 | Keyviz requires **Input Monitoring** and **Accessibility** permissions. Enable the same in settings - 72 |
73 | ``` 74 | Settings > Privacy & Security > Input Monitoring/Accessibility 75 | ``` 76 | 77 |
78 | 79 |
80 | 81 |
82 | 83 |
84 | 85 | 🐧 Linux 86 | 87 | ### ❗ v2.x.x Requirements 88 | ```bash 89 | sudo apt-get install libayatana-appindicator3-dev 90 | ``` 91 | or 92 | ```bash 93 | sudo apt-get install appindicator3-0.1 libappindicator3-dev 94 | ``` 95 | 96 |
97 | 98 |
99 | 100 | 101 |
102 | 103 | # 🛠️ Build Instructions 104 | 105 | You can always further develop/build the project by yourself. First of all ensure that you've setup Flutter on your system. If not follow this [guide](https://docs.flutter.dev/get-started/install). 106 | 107 | After setting up flutter, clone the repository if you have `git` installed or download the zip and unpack the same. 108 | 109 | ```bash 110 | mkdir keyviz 111 | cd keyviz 112 | git clone https://github.com/mulaRahul/keyviz.git . 113 | ``` 114 | 115 | Move inside the flutter project and run the build command to create an executable - 116 | 117 | ```bash 118 | flutter build windows 119 | ``` 120 | 121 |
122 | 123 | # 💖 Support 124 | 125 | As keyviz is freeware, the only way I can earn is through your generous donations. It helps free my time and work more on keyviz. 126 | -------------------------------------------------------------------------------- /README_zh_CN.md: -------------------------------------------------------------------------------- 1 | ![keyviz-2.0](previews/banner.svg) 2 | 3 | Keyviz,一个免费开源的实时键鼠输入可视化软件,让观众了解你在演示的过程中按下了什么快捷键 4 | 5 | **简体中文** | [English](./README.md) 6 | 7 | # ⌨️ 键盘输入 & 🖱️ 鼠标操作 8 | 9 | Keyviz也可以显示鼠标、键盘+鼠标的操作,比如 Cmd + ClickAlt + Drag 10 | 11 | ![key-visualizer](previews/visualizer-bar.svg) 12 | 13 | # 🎨 个性化 14 | 15 | 不只有黑与白,Keyviz可以高度自定义按键的显示效果,包括但不限于:预设风格、尺寸、键位图标显示切换(Shift的↑)、辅助键和常规键的颜色、边框 16 | 17 | ![settings-window](previews/settings.svg) 18 | 19 | 强大易用的设置菜单 20 | 21 | - 隐藏常规键,留下组合键,比如只显示 Cmd + K**(默认)** 22 | - 显示位置(区域、距离主显示器边缘的距离) 23 | - 按键显示的停留时间 24 | - 按键切入切出的动画 25 | 26 |
27 | 28 | # 📥 安装 29 | 30 | 在 [Github Releases](https://github.com/mulaRahul/keyviz/releases) 下载最新版,根据操作系统安装/解压即用,或者通过下面的包管理器安装 31 | 32 |
33 | 🥄 Scoop 34 | 35 | ```bash 36 | scoop bucket add extras # first, add the bucket 37 | scoop install keyviz 38 | ``` 39 | 40 |
41 | 42 |
43 | 🪟 Winget 44 | 45 | ```bash 46 | winget install mulaRahul.Keyviz 47 | ``` 48 | 49 |
50 | 51 |
52 | 53 |
54 | 提示缺少*.dll 55 | 56 | 如果运行程序弹出了缺少`.dll`文件的错误,大概率是环境缺少了VC++运行库,你可以在[**这里**](https://learn.microsoft.com/zh-cn/cpp/windows/latest-supported-vc-redist?view=msvc-170)下载安装 57 | 58 |
59 | 60 |
61 | 62 | # 🛠️ 构建说明 63 | 64 | 在进一步开发、编译之前,请确保在系统上安装好了Flutter,可以参照[官方的安装指南](https://docs.flutter.dev/get-started/install) 65 | 66 | 安装并设置好Flutter后,克隆仓库或下载zip并解压 67 | 68 | ```bash 69 | mkdir keyviz 70 | git clone https://github.com/mulaRahul/keyviz.git . 71 | ``` 72 | 73 | cd到项目文件夹内 开始编译 74 | 75 | ```bash 76 | cd keyviz 77 | # 获取依赖 78 | flutter pub get 79 | # 编译可执行文件 80 | flutter build windows 81 | ``` 82 | 83 |
84 | 85 | # 💖 支持该项目 86 | 87 | Keyviz是一个免费项目,唯一的收益来源只有你们的慷慨捐赠,这将有助于我腾出更多的空余时间用于开发Keyviz 88 | 89 |
90 | 91 |
92 | 93 | 译于23/7/18,v2.0.0a发布的七天后,有些条目是作者还没改上去的,部分描述其实跟软件本体差挺多的 94 |
95 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at 17 | # https://dart-lang.github.io/linter/lints/index.html. 18 | # 19 | # Instead of disabling a lint rule for the entire project in the 20 | # section below, it can also be suppressed for a single line of code 21 | # or a specific dart file by using the `// ignore: name_of_lint` and 22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 23 | # producing the lint. 24 | rules: 25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 27 | 28 | # Additional information about this file can be found at 29 | # https://dart.dev/guides/language/analysis-options 30 | -------------------------------------------------------------------------------- /assets/fonts/IBMPlexSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mulaRahul/keyviz/0452c3c060d2b6fe5706cc85f43c44992523ab4e/assets/fonts/IBMPlexSans-Bold.ttf -------------------------------------------------------------------------------- /assets/fonts/IBMPlexSans-ExtraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mulaRahul/keyviz/0452c3c060d2b6fe5706cc85f43c44992523ab4e/assets/fonts/IBMPlexSans-ExtraLight.ttf -------------------------------------------------------------------------------- /assets/fonts/IBMPlexSans-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mulaRahul/keyviz/0452c3c060d2b6fe5706cc85f43c44992523ab4e/assets/fonts/IBMPlexSans-Light.ttf -------------------------------------------------------------------------------- /assets/fonts/IBMPlexSans-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mulaRahul/keyviz/0452c3c060d2b6fe5706cc85f43c44992523ab4e/assets/fonts/IBMPlexSans-Medium.ttf -------------------------------------------------------------------------------- /assets/fonts/IBMPlexSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mulaRahul/keyviz/0452c3c060d2b6fe5706cc85f43c44992523ab4e/assets/fonts/IBMPlexSans-Regular.ttf -------------------------------------------------------------------------------- /assets/fonts/IBMPlexSans-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mulaRahul/keyviz/0452c3c060d2b6fe5706cc85f43c44992523ab4e/assets/fonts/IBMPlexSans-SemiBold.ttf -------------------------------------------------------------------------------- /assets/fonts/IBMPlexSans-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mulaRahul/keyviz/0452c3c060d2b6fe5706cc85f43c44992523ab4e/assets/fonts/IBMPlexSans-Thin.ttf -------------------------------------------------------------------------------- /assets/fonts/Inter-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mulaRahul/keyviz/0452c3c060d2b6fe5706cc85f43c44992523ab4e/assets/fonts/Inter-Regular.ttf -------------------------------------------------------------------------------- /assets/fonts/Inter-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mulaRahul/keyviz/0452c3c060d2b6fe5706cc85f43c44992523ab4e/assets/fonts/Inter-SemiBold.ttf -------------------------------------------------------------------------------- /assets/fonts/Inter-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mulaRahul/keyviz/0452c3c060d2b6fe5706cc85f43c44992523ab4e/assets/fonts/Inter-Thin.ttf -------------------------------------------------------------------------------- /assets/icons/alt.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/arrow-down.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/arrow-left.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/arrow-right.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/arrow-up.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/backspace.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/caps-lock.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/caps-locked.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/control.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 8 | -------------------------------------------------------------------------------- /assets/icons/delete.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/drag.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /assets/icons/end.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/enter.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/escape.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/function.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/home.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/insert.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/left-click.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /assets/icons/mac-meta.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/icons/meta.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/mouse.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/icons/num-lock.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/num-locked.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/page-down.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/page-up.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/pause.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/print-screen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/right-click.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /assets/icons/scroll-lock.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/scroll-locked.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/scroll.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/icons/shift.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/space.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/icons/tab.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/img/align-bottom.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/img/align-horizontally.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/img/align-left.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/img/align-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/img/align-top.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/img/align-vertically.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/img/arrow-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/img/chevron-down.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/img/clipboard-tick.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/img/clipboard.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /assets/img/cog-wheel.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/img/cross.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/img/discord-logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/img/edit.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/img/error.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/img/github-logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/img/grab.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /assets/img/keyboard.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/img/linked.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/img/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/img/mail.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/img/monitor.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /assets/img/more.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /assets/img/mouse.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/img/opencollective-logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/img/tray-off.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mulaRahul/keyviz/0452c3c060d2b6fe5706cc85f43c44992523ab4e/assets/img/tray-off.ico -------------------------------------------------------------------------------- /assets/img/tray-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mulaRahul/keyviz/0452c3c060d2b6fe5706cc85f43c44992523ab4e/assets/img/tray-off.png -------------------------------------------------------------------------------- /assets/img/tray-on.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mulaRahul/keyviz/0452c3c060d2b6fe5706cc85f43c44992523ab4e/assets/img/tray-on.ico -------------------------------------------------------------------------------- /assets/img/tray-on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mulaRahul/keyviz/0452c3c060d2b6fe5706cc85f43c44992523ab4e/assets/img/tray-on.png -------------------------------------------------------------------------------- /assets/img/unlinked.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mulaRahul/keyviz/0452c3c060d2b6fe5706cc85f43c44992523ab4e/assets/logo.png -------------------------------------------------------------------------------- /lib/app.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:provider/provider.dart'; 3 | 4 | import 'config/theme.dart'; 5 | import 'providers/key_event.dart'; 6 | import 'providers/key_style.dart'; 7 | import 'windows/error/error.dart'; 8 | import 'windows/settings/settings.dart'; 9 | import 'windows/key_visualizer/key_visualizer.dart'; 10 | import 'windows/mouse_visualizer/mouse_visualizer.dart'; 11 | 12 | class KeyvizApp extends StatelessWidget { 13 | const KeyvizApp({super.key}); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return MaterialApp( 18 | title: "Keyviz", 19 | theme: lightTheme, 20 | darkTheme: darkTheme, 21 | themeMode: ThemeMode.system, 22 | home: GestureDetector( 23 | onTap: _removePrimaryFocus, 24 | child: MultiProvider( 25 | providers: [ 26 | ChangeNotifierProvider(create: (_) => KeyEventProvider()), 27 | ChangeNotifierProvider(create: (_) => KeyStyleProvider()), 28 | ], 29 | child: const Material( 30 | type: MaterialType.transparency, 31 | child: Stack( 32 | fit: StackFit.expand, 33 | children: [ 34 | ErrorView(), 35 | KeyVisualizer(), 36 | SettingsWindow(), 37 | MouseVisualizer(), 38 | ], 39 | ), 40 | ), 41 | ), 42 | ), 43 | ); 44 | } 45 | 46 | _removePrimaryFocus() { 47 | FocusManager.instance.primaryFocus?.unfocus(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/config/assets.dart: -------------------------------------------------------------------------------- 1 | class VuesaxIcons { 2 | VuesaxIcons._(); 3 | 4 | static const _dir = "assets/img"; 5 | 6 | static const cogWheel = "$_dir/cog-wheel.svg"; 7 | static const edit = "$_dir/edit.svg"; 8 | static const monitor = "$_dir/monitor.svg"; 9 | static const more = "$_dir/more.svg"; 10 | static const mouse = "$_dir/mouse.svg"; 11 | static const keyboard = "$_dir/keyboard.svg"; 12 | static const cross = "$_dir/cross.svg"; 13 | static const chevronDown = "$_dir/chevron-down.svg"; 14 | static const alignTop = "$_dir/align-top.svg"; 15 | static const alignVertically = "$_dir/align-vertically.svg"; 16 | static const alignBottom = "$_dir/align-bottom.svg"; 17 | static const alignLeft = "$_dir/align-left.svg"; 18 | static const alignHorizontally = "$_dir/align-horizontally.svg"; 19 | static const alignRight = "$_dir/align-right.svg"; 20 | static const linked = "$_dir/linked.svg"; 21 | static const unlinked = "$_dir/unlinked.svg"; 22 | static const clipboard = "$_dir/clipboard.svg"; 23 | static const clipboardTick = "$_dir/clipboard-tick.svg"; 24 | static const arrowRight = "$_dir/arrow-right.svg"; 25 | static const mail = "$_dir/mail.svg"; 26 | static const error = "$_dir/error.svg"; 27 | } 28 | 29 | class KeyIcons { 30 | KeyIcons._(); 31 | 32 | static const _dir = "assets/icons"; 33 | // modifiers 34 | static const control = "$_dir/control.svg"; 35 | static const alt = "$_dir/alt.svg"; 36 | static const shift = "$_dir/shift.svg"; 37 | static const meta = "$_dir/meta.svg"; 38 | // normals 39 | static const backspace = "$_dir/backspace.svg"; 40 | static const enter = "$_dir/enter.svg"; 41 | static const space = "$_dir/space.svg"; 42 | static const tab = "$_dir/tab.svg"; 43 | static const contextMenu = "$_dir/context-menu.svg"; 44 | static const printScreen = "$_dir/print-screen.svg"; 45 | static const pause = "$_dir/pause.svg"; 46 | // navigations 47 | static const escape = "$_dir/escape.svg"; 48 | static const insert = "$_dir/insert.svg"; 49 | static const delete = "$_dir/delete.svg"; 50 | static const home = "$_dir/home.svg"; 51 | static const end = "$_dir/end.svg"; 52 | static const pageUp = "$_dir/page-up.svg"; 53 | static const pageDown = "$_dir/page-down.svg"; 54 | // arrows 55 | static const arrowUp = "$_dir/arrow-up.svg"; 56 | static const arrowDown = "$_dir/arrow-down.svg"; 57 | static const arrowLeft = "$_dir/arrow-left.svg"; 58 | static const arrowRight = "$_dir/arrow-right.svg"; 59 | // locks 60 | static const capsLock = "$_dir/caps-lock.svg"; 61 | static const capsLocked = "$_dir/caps-locked.svg"; 62 | static const scrollLock = "$_dir/scroll-lock.svg"; 63 | static const scrollLocked = "$_dir/scroll-locked.svg"; 64 | static const numLock = "$_dir/num-lock.svg"; 65 | static const numLocked = "$_dir/num-locked.svg"; 66 | // mouse 67 | static const leftClick = "$_dir/left-click.svg"; 68 | static const rightClick = "$_dir/right-click.svg"; 69 | static const drag = "$_dir/drag.svg"; 70 | static const scroll = "$_dir/scroll.svg"; 71 | 72 | // platform 73 | static const macMeta = "$_dir/mac-meta.svg"; 74 | } 75 | -------------------------------------------------------------------------------- /lib/config/config.dart: -------------------------------------------------------------------------------- 1 | export 'assets.dart'; 2 | export 'style.dart'; 3 | export 'extensions.dart'; 4 | -------------------------------------------------------------------------------- /lib/config/extensions.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:provider/provider.dart'; 3 | 4 | import 'package:keyviz/providers/key_event.dart'; 5 | import 'package:keyviz/providers/key_style.dart'; 6 | 7 | extension Cap on String { 8 | static const _space = " "; 9 | 10 | String capitalize() { 11 | if (contains(_space)) { 12 | final words = []; 13 | for (final word in split(_space)) { 14 | words.add(_capitalize(word)); 15 | } 16 | return words.join(_space); 17 | } else { 18 | return _capitalize(this); 19 | } 20 | } 21 | 22 | String _capitalize(String txt) { 23 | return "${txt[0].toUpperCase()}${txt.substring(1).toLowerCase()}"; 24 | } 25 | } 26 | 27 | extension Ease on BuildContext { 28 | ThemeData get theme => Theme.of(this); 29 | bool get isDark => theme.brightness == Brightness.dark; 30 | ColorScheme get colorScheme => theme.colorScheme; 31 | TextTheme get textTheme => theme.textTheme; 32 | KeyEventProvider get keyEvent => read(); 33 | KeyStyleProvider get keyStyle => read(); 34 | } 35 | 36 | extension HexColor on Color { 37 | /// String is in the format "aabbcc" or "ffaabbcc" with an optional leading "#". 38 | static Color fromHex(String hexString) { 39 | final buffer = StringBuffer(); 40 | if (hexString.length == 6 || hexString.length == 7) buffer.write('ff'); 41 | buffer.write(hexString.replaceFirst('#', '')); 42 | return Color(int.parse(buffer.toString(), radix: 16)); 43 | } 44 | 45 | /// Prefixes a hash sign if [leadingHashSign] is set to `true` (default is `true`). 46 | String toHex({bool leadingHashSign = true}) => '${leadingHashSign ? '#' : ''}' 47 | // '${alpha.toRadixString(16).padLeft(2, '0')}' 48 | '${red.toRadixString(16).padLeft(2, '0').toUpperCase()}' 49 | '${green.toRadixString(16).padLeft(2, '0').toUpperCase()}' 50 | '${blue.toRadixString(16).padLeft(2, '0').toUpperCase()}'; 51 | } 52 | 53 | extension HexCode on HSVColor { 54 | /// Prefixes a hash sign if [leadingHashSign] is set to `true` (default is `true`). 55 | String toHex({bool leadingHashSign = true}) { 56 | final rgb = toColor(); 57 | 58 | return '${leadingHashSign ? '#' : ''}' 59 | // '${alpha.toRadixString(16).padLeft(2, '0')}' 60 | '${rgb.red.toRadixString(16).padLeft(2, '0').toUpperCase()}' 61 | '${rgb.green.toRadixString(16).padLeft(2, '0').toUpperCase()}' 62 | '${rgb.blue.toRadixString(16).padLeft(2, '0').toUpperCase()}'; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /lib/config/style.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | // size & spacing 4 | const double defaultPadding = 16; 5 | final BorderRadius defaultBorderRadius = BorderRadius.circular(defaultPadding); 6 | 7 | // motion & duration 8 | const shortDelay = Duration(milliseconds: 200); 9 | const transitionDuration = Duration(milliseconds: 200); 10 | -------------------------------------------------------------------------------- /lib/domain/services/raw_keyboard_mouse.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/services.dart'; 2 | 3 | // key id's for fake mouse events 4 | const leftClickId = 0x00900000011; 5 | const rightClickId = 0x00900000012; 6 | const dragId = 0x00900000013; 7 | const scrollId = 0x00900000014; 8 | 9 | // an implementation of [RawKeyEventData] which is used 10 | // to fake mouse events as keyboard events 11 | class RawKeyEventDataMouse extends RawKeyEventData { 12 | const RawKeyEventDataMouse(this.id); 13 | 14 | final int id; 15 | 16 | @override 17 | KeyboardSide? getModifierSide(ModifierKey key) { 18 | return null; 19 | } 20 | 21 | @override 22 | bool isModifierPressed(ModifierKey key, 23 | {KeyboardSide side = KeyboardSide.any}) { 24 | return false; 25 | } 26 | 27 | @override 28 | String get keyLabel { 29 | switch (id) { 30 | case leftClickId: 31 | return "Left Click"; 32 | 33 | case rightClickId: 34 | return "Right Click"; 35 | 36 | case dragId: 37 | return "Drag"; 38 | 39 | case scrollId: 40 | return "Scroll"; 41 | } 42 | return ''; 43 | } 44 | 45 | @override 46 | LogicalKeyboardKey get logicalKey => LogicalKeyboardKey(id); 47 | 48 | @override 49 | PhysicalKeyboardKey get physicalKey => PhysicalKeyboardKey(id); 50 | 51 | // mouse left button down/up 52 | const RawKeyEventDataMouse.leftClick() : id = leftClickId; 53 | 54 | // mouse right button down/up 55 | const RawKeyEventDataMouse.rightClick() : id = rightClickId; 56 | 57 | // mouse left/right button down and mouse moving 58 | const RawKeyEventDataMouse.drag() : id = dragId; 59 | 60 | // mouse wheel event 61 | const RawKeyEventDataMouse.scroll() : id = scrollId; 62 | } 63 | -------------------------------------------------------------------------------- /lib/domain/services/services.dart: -------------------------------------------------------------------------------- 1 | export 'key_maps.dart'; 2 | export 'raw_keyboard_mouse.dart'; 3 | -------------------------------------------------------------------------------- /lib/domain/vault/json.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'dart:convert'; 3 | 4 | import 'package:flutter/foundation.dart'; 5 | import 'package:path_provider/path_provider.dart'; 6 | 7 | Future _file(String filePath) async { 8 | // local appdata directory 9 | final Directory appDir = await getApplicationDocumentsDirectory(); 10 | // json file 11 | return File("${appDir.path}/$filePath"); 12 | } 13 | 14 | // load Map from a json file 15 | Future?> load(String filePath) async { 16 | final File file = await _file(filePath); 17 | // file exists 18 | if (await file.exists()) { 19 | try { 20 | // read file content 21 | final String fileContent = await file.readAsString(); 22 | // parse json 23 | return json.decode(fileContent); 24 | } catch (exception) { 25 | debugPrint(exception.toString()); 26 | } 27 | } 28 | debugPrint("[ json:load ]: $file not found!"); 29 | return null; 30 | } 31 | 32 | // dump/write Map to a json file 33 | Future dump( 34 | Map data, 35 | String filePath, { 36 | bool createFileIfNotFound = true, 37 | }) async { 38 | final File file = await _file(filePath); 39 | // file exists 40 | if (await file.exists()) { 41 | await file.writeAsString(json.encode(data)); 42 | } else if (createFileIfNotFound) { 43 | await file.create(recursive: true); 44 | await file.writeAsString(json.encode(data)); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/domain/vault/vault.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:keyviz/config/extensions.dart'; 4 | 5 | import 'json.dart' as json; 6 | 7 | const _configDataFile = "config.json"; 8 | const _styleDataFile = "style.json"; 9 | 10 | class Vault { 11 | const Vault._(); 12 | 13 | static Future?> loadConfigData() async { 14 | return await json.load(_configDataFile); 15 | } 16 | 17 | static Future?> loadStyleData() async { 18 | return await json.load(_styleDataFile); 19 | } 20 | 21 | static save(BuildContext context) { 22 | json.dump(context.keyEvent.toJson, _configDataFile); 23 | json.dump(context.keyStyle.toJson, _styleDataFile); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | import 'package:flutter_acrylic/flutter_acrylic.dart'; 6 | import 'package:hid_listener/hid_listener.dart'; 7 | import 'package:macos_window_utils/macos_window_utils.dart'; 8 | import 'package:window_manager/window_manager.dart'; 9 | 10 | import 'app.dart'; 11 | 12 | void main() async { 13 | // ensure flutter plugins are intialized and ready to use 14 | WidgetsFlutterBinding.ensureInitialized(); 15 | await Window.initialize(); 16 | await windowManager.ensureInitialized(); 17 | 18 | if (getListenerBackend() != null) { 19 | if (!getListenerBackend()!.initialize()) { 20 | print("Failed to initialize listener backend"); 21 | } 22 | } else { 23 | print("No listener backend for this platform"); 24 | } 25 | 26 | runApp(const KeyvizApp()); 27 | 28 | _initWindow(); 29 | } 30 | 31 | _initWindow() async { 32 | await windowManager.waitUntilReadyToShow( 33 | WindowOptions( 34 | skipTaskbar: true, 35 | alwaysOnTop: true, 36 | fullScreen: !Platform.isMacOS, 37 | titleBarStyle: TitleBarStyle.hidden, 38 | ), 39 | () async { 40 | windowManager.setIgnoreMouseEvents(true); 41 | windowManager.setHasShadow(false); 42 | windowManager.setAsFrameless(); 43 | }, 44 | ); 45 | 46 | if (Platform.isMacOS) { 47 | WindowManipulator.makeWindowFullyTransparent(); 48 | await WindowManipulator.zoomWindow(); 49 | } else { 50 | Window.setEffect( 51 | effect: WindowEffect.transparent, 52 | color: Colors.transparent, 53 | ); 54 | } 55 | windowManager.blur(); 56 | } 57 | -------------------------------------------------------------------------------- /lib/providers/providers.dart: -------------------------------------------------------------------------------- 1 | export 'key_event.dart'; 2 | export 'key_event_data.dart'; 3 | export 'key_style.dart'; 4 | -------------------------------------------------------------------------------- /lib/windows/error/error.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:keyviz/windows/shared/shared.dart'; 3 | import 'package:provider/provider.dart'; 4 | 5 | import 'package:keyviz/config/config.dart'; 6 | import 'package:keyviz/providers/key_event.dart'; 7 | 8 | class ErrorView extends StatelessWidget { 9 | const ErrorView({super.key}); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Selector( 14 | selector: (_, keyEvent) => keyEvent.hasError, 15 | builder: (context, hasError, __) => hasError 16 | ? Center( 17 | child: Container( 18 | width: 360, 19 | padding: const EdgeInsets.all(defaultPadding * 2), 20 | decoration: BoxDecoration( 21 | color: Colors.red[100], 22 | border: Border.all(color: context.colorScheme.error), 23 | borderRadius: BorderRadius.circular(defaultPadding), 24 | ), 25 | child: Column( 26 | mainAxisSize: MainAxisSize.min, 27 | children: [ 28 | // Align( 29 | // alignment: Alignment.centerRight, 30 | // child: IconButton( 31 | // onPressed: windowManager.close, 32 | // tooltip: "Quit App", 33 | // icon: SvgIcon.cross( 34 | // size: defaultPadding * .6, 35 | // color: context.colorScheme.error, 36 | // ), 37 | // ), 38 | // ), 39 | SvgIcon( 40 | size: defaultPadding * 3, 41 | color: context.colorScheme.error, 42 | icon: VuesaxIcons.error, 43 | ), 44 | const ColumnGap(), 45 | Text( 46 | "Cannot register keyboard/mouse listener! " 47 | "Please quit the app from the system tray.", 48 | style: context.textTheme.labelLarge?.copyWith( 49 | color: context.colorScheme.error, 50 | ), 51 | textAlign: TextAlign.center, 52 | ), 53 | ], 54 | ), 55 | ), 56 | ) 57 | : const SizedBox(), 58 | ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/windows/key_visualizer/widgets/animations/animations.dart: -------------------------------------------------------------------------------- 1 | export 'fade_keycap_animation.dart'; 2 | export 'grow_keycap_animation.dart'; 3 | export 'key_cap_animation.dart'; 4 | export 'slide_keycap_animation.dart'; 5 | export 'wham_keycap_animation.dart'; 6 | -------------------------------------------------------------------------------- /lib/windows/key_visualizer/widgets/animations/fade_keycap_animation.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'key_cap_animation.dart'; 4 | 5 | class FadeKeyCapAnimation extends KeyCapAnimation { 6 | const FadeKeyCapAnimation({ 7 | super.key, 8 | required super.show, 9 | required super.child, 10 | }); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return AnimatedOpacity( 15 | duration: animationDuration(context), 16 | curve: Curves.easeInOut, 17 | opacity: show ? 1 : 0, 18 | child: child, 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/windows/key_visualizer/widgets/animations/grow_keycap_animation.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'key_cap_animation.dart'; 4 | 5 | class GrowKeyCapAnimation extends KeyCapAnimation { 6 | const GrowKeyCapAnimation({ 7 | super.key, 8 | required super.show, 9 | required super.child, 10 | }); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return AnimatedScale( 15 | duration: animationDuration(context), 16 | curve: Curves.easeInOutCubicEmphasized, 17 | scale: show ? 1 : 0, 18 | child: child, 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/windows/key_visualizer/widgets/animations/key_cap_animation.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:provider/provider.dart'; 3 | 4 | import 'package:keyviz/providers/key_event.dart'; 5 | 6 | // abstract class to be implement by KeyCapAnimations 7 | abstract class KeyCapAnimation extends StatelessWidget { 8 | const KeyCapAnimation({super.key, required this.show, required this.child}); 9 | 10 | // animation in/out state 11 | final bool show; 12 | 13 | // key cap widget 14 | final Widget child; 15 | 16 | // utility getter for animation duration 17 | Duration animationDuration(BuildContext context) => 18 | context.read().animationDuration; 19 | } 20 | -------------------------------------------------------------------------------- /lib/windows/key_visualizer/widgets/animations/slide_keycap_animation.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | import 'package:keyviz/config/extensions.dart'; 6 | 7 | import 'key_cap_animation.dart'; 8 | 9 | class SlideKeyCapAnimation extends KeyCapAnimation { 10 | const SlideKeyCapAnimation({ 11 | super.key, 12 | required super.show, 13 | required super.child, 14 | }); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return context.keyStyle.backgroundEnabled 19 | // clipping in place 20 | ? AnimatedSlide( 21 | offset: Offset(0, show ? 0 : 1.25), 22 | duration: animationDuration(context), 23 | curve: Curves.easeInOutCubicEmphasized, 24 | child: child, 25 | ) 26 | // no clipping, add fade effect 27 | : TweenAnimationBuilder( 28 | tween: Tween(begin: 0, end: show ? 1 : 0), 29 | duration: animationDuration(context), 30 | curve: Curves.easeInOutCubicEmphasized, 31 | builder: (_, value, child) { 32 | return FractionalTranslation( 33 | translation: Offset(0, lerpDouble(1.25, 0, value) ?? 0), 34 | child: Opacity(opacity: value, child: child), 35 | ); 36 | }, 37 | child: child, 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/windows/key_visualizer/widgets/animations/wham_keycap_animation.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | import 'key_cap_animation.dart'; 6 | 7 | class WhamKeyCapAnimation extends KeyCapAnimation { 8 | const WhamKeyCapAnimation({ 9 | super.key, 10 | required super.show, 11 | required super.child, 12 | }); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return TweenAnimationBuilder( 17 | tween: Tween(begin: 0, end: show ? 1 : 0), 18 | duration: animationDuration(context), 19 | curve: Curves.easeInOutCubicEmphasized, 20 | builder: (context, value, child) { 21 | return Transform.scale( 22 | scale: lerpDouble(1.25, 1, value), 23 | child: Opacity(opacity: value, child: child), 24 | ); 25 | }, 26 | child: child, 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/windows/key_visualizer/widgets/keycap_wrapper.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:provider/provider.dart'; 3 | 4 | import 'package:keyviz/providers/providers.dart'; 5 | 6 | import 'animations/animations.dart'; 7 | import 'keycaps/keycaps.dart'; 8 | 9 | class KeyCapWrapper extends StatelessWidget { 10 | const KeyCapWrapper({super.key, required this.groupId, required this.keyId}); 11 | 12 | final String groupId; 13 | final int keyId; 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Selector( 18 | builder: (context, event, _) { 19 | return event == null 20 | ? const SizedBox() 21 | : _AnimationWrapper( 22 | show: event.show, 23 | child: _KeyCap(event), 24 | ); 25 | }, 26 | selector: (_, keyStyle) => keyStyle.keyboardEvents[groupId]?[keyId], 27 | ); 28 | } 29 | } 30 | 31 | class _AnimationWrapper extends StatelessWidget { 32 | const _AnimationWrapper({required this.show, required this.child}); 33 | 34 | final bool show; 35 | final Widget child; 36 | 37 | @override 38 | Widget build(BuildContext context) { 39 | switch (context.read().keyCapAnimation) { 40 | case KeyCapAnimationType.none: 41 | return child; 42 | 43 | case KeyCapAnimationType.fade: 44 | return FadeKeyCapAnimation(show: show, child: child); 45 | 46 | case KeyCapAnimationType.slide: 47 | return SlideKeyCapAnimation(show: show, child: child); 48 | 49 | case KeyCapAnimationType.grow: 50 | return GrowKeyCapAnimation(show: show, child: child); 51 | 52 | case KeyCapAnimationType.wham: 53 | return WhamKeyCapAnimation(show: show, child: child); 54 | } 55 | } 56 | } 57 | 58 | class _KeyCap extends StatelessWidget { 59 | const _KeyCap(this.event); 60 | 61 | final KeyEventData event; 62 | 63 | @override 64 | Widget build(BuildContext context) { 65 | final keyCapStyle = context.select( 66 | (keyStyle) => keyStyle.keyCapStyle, 67 | ); 68 | switch (keyCapStyle) { 69 | case KeyCapStyle.minimal: 70 | return MinimalKeyCap(event: event); 71 | 72 | case KeyCapStyle.flat: 73 | return FlatKeyCap(event: event); 74 | 75 | case KeyCapStyle.elevated: 76 | return ElevatedKeyCap(event: event); 77 | 78 | case KeyCapStyle.plastic: 79 | return PlasticKeyCap(event: event); 80 | 81 | // case KeyCapStyle.retro: 82 | // return RetroKeyCap(event: event); 83 | 84 | case KeyCapStyle.mechanical: 85 | return MechanicalKeyCap(event: event); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /lib/windows/key_visualizer/widgets/keycaps/elevated_keycap.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'keycap.dart'; 4 | import 'keycap_content.dart'; 5 | 6 | class ElevatedKeyCap extends KeyCap { 7 | const ElevatedKeyCap({super.key, required super.event}); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | final style = keyStyle(context); 12 | final size = style.minContainerSize; 13 | 14 | return SizedBox( 15 | height: style.keycapHeight, 16 | child: IntrinsicWidth( 17 | child: Stack( 18 | alignment: Alignment.bottomCenter, 19 | children: [ 20 | Container( 21 | height: size.height, 22 | constraints: BoxConstraints( 23 | minWidth: size.width, 24 | ), 25 | decoration: BoxDecoration( 26 | color: secondaryColor(style), 27 | border: border(style), 28 | borderRadius: style.outerBorderRadius, 29 | ), 30 | ), 31 | AnimatedPadding( 32 | curve: Curves.easeInOutCubicEmphasized, 33 | duration: animationDuration(context), 34 | padding: event.pressed ? EdgeInsets.zero : style.containerPadding, 35 | child: Container( 36 | height: size.height, 37 | constraints: BoxConstraints(minWidth: size.width), 38 | padding: style.contentPadding, 39 | decoration: BoxDecoration( 40 | color: primaryColor(style), 41 | border: border(style), 42 | borderRadius: style.borderRadius, 43 | ), 44 | alignment: style.childrenAlignment, 45 | child: KeyCapContent(event), 46 | ), 47 | ), 48 | ], 49 | ), 50 | ), 51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/windows/key_visualizer/widgets/keycaps/flat_keycap.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'keycap.dart'; 4 | import 'keycap_content.dart'; 5 | 6 | class FlatKeyCap extends KeyCap { 7 | const FlatKeyCap({super.key, required super.event}); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | final style = keyStyle(context); 12 | final size = style.minContainerSize; 13 | 14 | return AnimatedScale( 15 | scale: event.pressed ? .75 : 1, 16 | duration: animationDuration(context), 17 | curve: Curves.easeInOutCubicEmphasized, 18 | child: Container( 19 | height: size.height, 20 | constraints: BoxConstraints( 21 | minWidth: size.width, 22 | ), 23 | padding: style.contentPadding, 24 | decoration: BoxDecoration( 25 | color: primaryColor(style), 26 | border: border(style), 27 | gradient: primaryGradient(style), 28 | borderRadius: style.borderRadius, 29 | ), 30 | alignment: style.childrenAlignment, 31 | child: KeyCapContent(event), 32 | ), 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/windows/key_visualizer/widgets/keycaps/keycap.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | import 'package:provider/provider.dart'; 3 | 4 | import 'package:keyviz/config/extensions.dart'; 5 | import 'package:keyviz/providers/providers.dart'; 6 | 7 | // abstract class to be implemented by every key cap class 8 | abstract class KeyCap extends StatelessWidget { 9 | const KeyCap({super.key, required this.event}); 10 | 11 | // key event data 12 | final KeyEventData event; 13 | 14 | // utility getter key style provider 15 | KeyStyleProvider keyStyle(BuildContext context) => 16 | Provider.of(context); 17 | 18 | // primary color 19 | Color? primaryColor(KeyStyleProvider style) { 20 | if (style.isGradient) return null; 21 | 22 | return style.differentColorForModifiers && event.isModifier 23 | ? style.mPrimaryColor1 24 | : style.primaryColor1; 25 | } 26 | 27 | // primary gradient 28 | LinearGradient? primaryGradient( 29 | KeyStyleProvider style, { 30 | GradientTransform? transform, 31 | }) { 32 | if (!style.isGradient) return null; 33 | 34 | return LinearGradient( 35 | transform: transform, 36 | begin: Alignment.topCenter, 37 | end: Alignment.bottomCenter, 38 | colors: (style.differentColorForModifiers && event.isModifier) 39 | ? [style.mPrimaryColor1, style.mPrimaryColor2] 40 | : [style.primaryColor1, style.primaryColor2], 41 | ); 42 | } 43 | 44 | // secondary color 45 | Color? secondaryColor(KeyStyleProvider style) { 46 | if (style.isGradient) return null; 47 | 48 | return style.differentColorForModifiers && event.isModifier 49 | ? style.mSecondaryColor1 50 | : style.secondaryColor1; 51 | } 52 | 53 | // secondary gradient 54 | LinearGradient? secondaryGradient( 55 | KeyStyleProvider style, { 56 | GradientTransform? transform, 57 | }) { 58 | if (!style.isGradient) return null; 59 | 60 | return LinearGradient( 61 | transform: transform, 62 | begin: Alignment.topCenter, 63 | end: Alignment.bottomCenter, 64 | colors: (style.differentColorForModifiers && event.isModifier) 65 | ? [style.mSecondaryColor1, style.mSecondaryColor2] 66 | : [style.secondaryColor1, style.secondaryColor2], 67 | ); 68 | } 69 | 70 | // border 71 | Border? border(KeyStyleProvider style) { 72 | if (!style.borderEnabled) return null; 73 | 74 | return Border.all( 75 | color: borderColor(style), 76 | width: style.borderWidth, 77 | strokeAlign: BorderSide.strokeAlignOutside, 78 | ); 79 | } 80 | 81 | // border color 82 | Color borderColor(KeyStyleProvider style) { 83 | return style.differentColorForModifiers && event.isModifier 84 | ? style.mBorderColor 85 | : style.borderColor; 86 | } 87 | 88 | // utility getter animation duration 89 | Duration animationDuration(BuildContext context) => 90 | context.keyEvent.animationDuration; 91 | } 92 | -------------------------------------------------------------------------------- /lib/windows/key_visualizer/widgets/keycaps/keycaps.dart: -------------------------------------------------------------------------------- 1 | export 'elevated_keycap.dart'; 2 | export 'flat_keycap.dart'; 3 | export 'keycap.dart'; 4 | export 'mechanical_keycap.dart'; 5 | export 'minimal_keycap.dart'; 6 | export 'plastic_keycap.dart'; 7 | export 'retro_keycap.dart'; 8 | -------------------------------------------------------------------------------- /lib/windows/key_visualizer/widgets/keycaps/minimal_keycap.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:keyviz/config/extensions.dart'; 4 | import 'package:keyviz/providers/key_style.dart'; 5 | import 'package:keyviz/windows/shared/shared.dart'; 6 | 7 | import 'keycap.dart'; 8 | 9 | class MinimalKeyCap extends KeyCap { 10 | const MinimalKeyCap({super.key, required super.event}); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | final style = keyStyle(context); 15 | 16 | final fontColor = style.differentColorForModifiers && event.isModifier 17 | ? style.mFontColor 18 | : style.fontColor; 19 | 20 | final textStyle = TextStyle( 21 | height: 1.2, 22 | fontFamily: "Inter", 23 | color: fontColor, 24 | fontSize: style.fontSize, 25 | ); 26 | 27 | // show icon 28 | if (event.isModifier && 29 | style.modifierTextLength == ModifierTextLength.iconOnly) { 30 | if (event.glyph == null) { 31 | return event.icon == null 32 | ? Text(_label(style), style: textStyle) 33 | : SvgIcon( 34 | icon: event.icon!, 35 | color: fontColor, 36 | size: style.fontSize, 37 | ); 38 | } else { 39 | return Text(event.glyph!, style: textStyle); 40 | } 41 | } 42 | 43 | return Text(_label(style), style: textStyle); 44 | } 45 | 46 | String _label(KeyStyleProvider style) { 47 | final value = style.modifierTextLength == ModifierTextLength.shortLength 48 | ? event.shortLabel ?? event.label 49 | : event.label; 50 | 51 | switch (style.textCap) { 52 | case TextCap.lower: 53 | return value.toLowerCase(); 54 | 55 | case TextCap.capitalize: 56 | return value.capitalize(); 57 | 58 | case TextCap.upper: 59 | return value.toUpperCase(); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/windows/key_visualizer/widgets/keycaps/plastic_keycap.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:vector_math/vector_math_64.dart'; 5 | 6 | import 'keycap.dart'; 7 | import 'keycap_content.dart'; 8 | 9 | class PlasticKeyCap extends KeyCap { 10 | const PlasticKeyCap({super.key, required super.event}); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | final style = keyStyle(context); 15 | final size = style.minContainerSize; 16 | final outerSize = style.minOuterContainerSize; 17 | 18 | return Container( 19 | height: outerSize.height, 20 | decoration: BoxDecoration( 21 | border: border(style), 22 | color: secondaryColor(style), 23 | gradient: secondaryGradient( 24 | style, 25 | transform: GradientRotation(radians(-30)), 26 | ), 27 | borderRadius: style.outerBorderRadius, 28 | ), 29 | padding: style.containerPadding, 30 | child: TweenAnimationBuilder( 31 | duration: animationDuration(context), 32 | curve: Curves.easeInOutCubicEmphasized, 33 | tween: Tween(begin: 0, end: event.pressed ? 1 : 0), 34 | builder: (context, value, child) { 35 | return Transform.translate( 36 | offset: Offset( 37 | 0, 38 | style.fontSize * (lerpDouble(0.0, .25, value) ?? 0), 39 | ), 40 | child: child, 41 | ); 42 | }, 43 | child: Container( 44 | height: size.height, 45 | constraints: BoxConstraints(minWidth: size.width), 46 | padding: style.contentPadding, 47 | decoration: BoxDecoration( 48 | border: border(style), 49 | color: primaryColor(style), 50 | gradient: primaryGradient( 51 | style, 52 | transform: GradientRotation(radians(-30)), 53 | ), 54 | borderRadius: style.borderRadius, 55 | ), 56 | alignment: style.childrenAlignment, 57 | child: KeyCapContent(event), 58 | ), 59 | ), 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/windows/key_visualizer/widgets/keycaps/retro_keycap.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'keycap.dart'; 4 | 5 | class RetroKeyCap extends KeyCap { 6 | const RetroKeyCap({super.key, required super.event}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | // TODO: implement 11 | return const Placeholder(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/windows/key_visualizer/widgets/widgets.dart: -------------------------------------------------------------------------------- 1 | export 'keycap_group.dart'; 2 | export 'keycap_wrapper.dart'; 3 | -------------------------------------------------------------------------------- /lib/windows/mouse_visualizer/mouse_visualizer.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:provider/provider.dart'; 5 | import 'package:tuple/tuple.dart'; 6 | 7 | import 'package:keyviz/providers/providers.dart'; 8 | 9 | import 'widgets/widgets.dart'; 10 | 11 | class MouseVisualizer extends StatelessWidget { 12 | const MouseVisualizer({super.key}); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return Selector>( 17 | builder: (context, tuple, child) { 18 | return tuple.item1 19 | ? Positioned( 20 | left: tuple.item2.dx, 21 | top: Platform.isMacOS ? null : tuple.item2.dy, 22 | // On macOS, the mouse offset is from the bottomLeft 23 | // instead of the topLeft 24 | bottom: Platform.isMacOS ? tuple.item2.dy : null, 25 | child: IgnorePointer( 26 | child: FractionalTranslation( 27 | translation: Offset(-.5, Platform.isMacOS ? .5 : -.5), 28 | child: const _MouseVisualizer(), 29 | ), 30 | ), 31 | ) 32 | : const SizedBox(); 33 | }, 34 | selector: (_, keyEvent) => Tuple2( 35 | keyEvent.showMouseClicks, 36 | keyEvent.cursorOffset, 37 | ), 38 | ); 39 | } 40 | } 41 | 42 | class _MouseVisualizer extends StatelessWidget { 43 | const _MouseVisualizer(); 44 | 45 | @override 46 | Widget build(BuildContext context) { 47 | return Selector>( 48 | builder: (context, tuple, _) => MouseHighlightWrapper( 49 | clicked: tuple.item1, 50 | keepHighlight: tuple.item2, 51 | ), 52 | selector: (_, keyEvent) => Tuple2( 53 | keyEvent.mouseButtonDown, 54 | keyEvent.highlightCursor, 55 | ), 56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/windows/mouse_visualizer/widgets/cursor_highlight.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:provider/provider.dart'; 3 | 4 | import 'package:keyviz/config/extensions.dart'; 5 | import 'package:keyviz/providers/providers.dart'; 6 | 7 | // abstract class to implemented by cursor higlights 8 | abstract class CursorHighlight extends StatelessWidget { 9 | const CursorHighlight({ 10 | super.key, 11 | required this.clicked, 12 | required this.keepHighlight, 13 | }); 14 | 15 | final bool clicked; 16 | final bool keepHighlight; 17 | 18 | // utility getter click size 19 | double highlightSize(BuildContext context) { 20 | return context.select( 21 | (style) => style.cursorHighlightSize, 22 | ); 23 | } 24 | 25 | // utility getter click color 26 | Color color(BuildContext context) { 27 | return context.select( 28 | (style) => style.clickColor, 29 | ); 30 | } 31 | 32 | // utility getter animation duration 33 | Duration animationDuration(BuildContext context) => 34 | context.keyEvent.animationDuration; 35 | } 36 | -------------------------------------------------------------------------------- /lib/windows/mouse_visualizer/widgets/cursor_highlight_wrapper.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:provider/provider.dart'; 3 | 4 | import 'package:keyviz/providers/key_style.dart'; 5 | 6 | import 'widgets.dart'; 7 | 8 | class MouseHighlightWrapper extends StatelessWidget { 9 | const MouseHighlightWrapper({ 10 | super.key, 11 | required this.clicked, 12 | required this.keepHighlight, 13 | }); 14 | 15 | final bool clicked; 16 | final bool keepHighlight; 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | final clickAnimationType = 21 | context.select( 22 | (keyStyle) => keyStyle.clickAnimation, 23 | ); 24 | 25 | switch (clickAnimationType) { 26 | case MouseClickAnimation.static: 27 | return StaticCursorHighlight( 28 | clicked: clicked, 29 | keepHighlight: keepHighlight, 30 | ); 31 | 32 | case MouseClickAnimation.focus: 33 | return FocusCursorHighlight( 34 | clicked: clicked, 35 | keepHighlight: keepHighlight, 36 | ); 37 | 38 | case MouseClickAnimation.filled: 39 | return FilledCursorHighlight( 40 | clicked: clicked, 41 | keepHighlight: keepHighlight, 42 | ); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/windows/mouse_visualizer/widgets/filled_cursor_higlight.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | import 'cursor_highlight.dart'; 6 | 7 | class FilledCursorHighlight extends CursorHighlight { 8 | const FilledCursorHighlight({ 9 | super.key, 10 | required super.clicked, 11 | required super.keepHighlight, 12 | }); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | final size = highlightSize(context); 17 | 18 | return TweenAnimationBuilder( 19 | tween: Tween(begin: 0, end: clicked ? 1 : 0), 20 | duration: animationDuration(context), 21 | curve: Curves.easeInOutCubicEmphasized, 22 | builder: (context, value, _) { 23 | return SizedBox.square( 24 | dimension: size * (lerpDouble(1, .6, value) ?? 1), 25 | child: DecoratedBox( 26 | decoration: BoxDecoration( 27 | shape: BoxShape.circle, 28 | color: keepHighlight 29 | ? color(context).withOpacity(lerpDouble(.25, .5, value) ?? .5) 30 | : color(context).withOpacity(lerpDouble(0, .5, value) ?? 0), 31 | ), 32 | ), 33 | ); 34 | }, 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/windows/mouse_visualizer/widgets/focus_cursor_higlight.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | import 'cursor_highlight.dart'; 6 | 7 | class FocusCursorHighlight extends CursorHighlight { 8 | const FocusCursorHighlight({ 9 | super.key, 10 | required super.clicked, 11 | required super.keepHighlight, 12 | }); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | final size = highlightSize(context); 17 | 18 | return TweenAnimationBuilder( 19 | tween: Tween(begin: 0, end: clicked ? 1 : 0), 20 | duration: animationDuration(context), 21 | curve: Curves.easeInOutCubicEmphasized, 22 | builder: (context, value, _) { 23 | return SizedBox.square( 24 | dimension: size * (lerpDouble(1, .6, value) ?? 1), 25 | child: DecoratedBox( 26 | decoration: BoxDecoration( 27 | shape: BoxShape.circle, 28 | border: Border.all( 29 | width: lerpDouble(1, 4, value) ?? 1, 30 | color: keepHighlight 31 | ? color(context) 32 | : color(context).withOpacity(value), 33 | ), 34 | ), 35 | ), 36 | ); 37 | }, 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/windows/mouse_visualizer/widgets/static_cursor_higlight.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'cursor_highlight.dart'; 4 | 5 | class StaticCursorHighlight extends CursorHighlight { 6 | const StaticCursorHighlight({ 7 | super.key, 8 | required super.clicked, 9 | required super.keepHighlight, 10 | }); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | final size = highlightSize(context); 15 | 16 | return Transform.scale( 17 | scale: clicked 18 | ? keepHighlight 19 | ? .6 20 | : 1 21 | : keepHighlight 22 | ? 1 23 | : 0, 24 | child: SizedBox.square( 25 | dimension: size, 26 | child: DecoratedBox( 27 | decoration: BoxDecoration( 28 | shape: BoxShape.circle, 29 | border: Border.all(color: color(context)), 30 | ), 31 | ), 32 | ), 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/windows/mouse_visualizer/widgets/widgets.dart: -------------------------------------------------------------------------------- 1 | export 'static_cursor_higlight.dart'; 2 | export 'focus_cursor_higlight.dart'; 3 | export 'filled_cursor_higlight.dart'; 4 | export 'cursor_highlight_wrapper.dart'; 5 | -------------------------------------------------------------------------------- /lib/windows/settings/views/mouse.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:provider/provider.dart'; 3 | 4 | import 'package:keyviz/config/config.dart'; 5 | import 'package:keyviz/providers/key_event.dart'; 6 | import 'package:keyviz/providers/key_style.dart'; 7 | 8 | import '../widgets/widgets.dart'; 9 | 10 | class MouseTabView extends StatelessWidget { 11 | const MouseTabView({super.key}); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return Column( 16 | children: [ 17 | PanelItem( 18 | title: "Visualize Clicks", 19 | subtitle: "Show clicks when a mouse button is pressed", 20 | action: Selector( 21 | selector: (_, keyEvent) => keyEvent.showMouseClicks, 22 | builder: (context, showMouseClicks, _) => XSwitch( 23 | value: showMouseClicks, 24 | onChange: (value) { 25 | context.keyEvent.showMouseClicks = value; 26 | }, 27 | ), 28 | ), 29 | ), 30 | const Divider(), 31 | Selector( 32 | selector: (_, keyEvent) => keyEvent.showMouseClicks, 33 | builder: (context, enabled, _) { 34 | return PanelItem( 35 | enabled: enabled, 36 | title: "Click Animation", 37 | action: Selector( 38 | selector: (_, keyStyle) => keyStyle.clickAnimation, 39 | builder: (context, value, _) { 40 | return XDropdown( 41 | value: value, 42 | options: MouseClickAnimation.values, 43 | onChanged: (value) { 44 | context.keyStyle.clickAnimation = value; 45 | }, 46 | ); 47 | }, 48 | ), 49 | ); 50 | }, 51 | ), 52 | const Divider(), 53 | Selector( 54 | selector: (_, keyEvent) => keyEvent.showMouseClicks, 55 | builder: (context, enabled, _) => PanelItem( 56 | enabled: enabled, 57 | title: "Click Color", 58 | subtitle: "Color of the highlight around your mouse cursor", 59 | actionFlex: 2, 60 | action: RawColorInputSubPanelItem( 61 | label: "Mouse Click Color", 62 | defaultValue: context.keyStyle.clickColor, 63 | onChanged: (color) => context.keyStyle.clickColor = color, 64 | ), 65 | ), 66 | ), 67 | const Divider(), 68 | Selector( 69 | selector: (_, keyEvent) => keyEvent.showMouseClicks, 70 | builder: (_, enabled, __) => PanelItem( 71 | enabled: enabled, 72 | title: "Keep Highlight", 73 | subtitle: "Show the highlight around mouse cursor all time", 74 | action: Selector( 75 | selector: (_, keyEvent) => keyEvent.highlightCursor, 76 | builder: (context, highlightCursor, _) => XSwitch( 77 | value: highlightCursor, 78 | onChange: (value) { 79 | context.keyEvent.highlightCursor = value; 80 | }, 81 | ), 82 | ), 83 | ), 84 | ), 85 | const Divider(), 86 | PanelItem( 87 | title: "Show Mouse Events", 88 | subtitle: "Visualize mouse events like click, drag, etc. " 89 | "along with key events", 90 | action: Selector( 91 | selector: (_, keyEvent) => keyEvent.showMouseEvents, 92 | builder: (context, showMouseEvents, _) => XSwitch( 93 | value: showMouseEvents, 94 | onChange: (value) { 95 | context.keyEvent.showMouseEvents = value; 96 | }, 97 | ), 98 | ), 99 | ), 100 | ], 101 | ); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /lib/windows/settings/views/style/background.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:provider/provider.dart'; 3 | 4 | import 'package:keyviz/config/config.dart'; 5 | import 'package:keyviz/providers/key_style.dart'; 6 | import 'package:keyviz/windows/shared/shared.dart'; 7 | 8 | import '../../widgets/widgets.dart'; 9 | 10 | class BackgroundView extends StatelessWidget { 11 | const BackgroundView({super.key}); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return Selector( 16 | selector: (_, keyStyle) => keyStyle.backgroundEnabled, 17 | builder: (context, enabled, _) { 18 | return XExpansionTile( 19 | title: "Background", 20 | children: [ 21 | SubPanelItemGroup( 22 | items: [ 23 | RawSubPanelItem( 24 | title: "Enable", 25 | child: XSwitch( 26 | value: enabled, 27 | onChange: (value) { 28 | context.keyStyle.backgroundEnabled = value; 29 | }, 30 | ), 31 | ), 32 | RawColorInputSubPanelItem( 33 | enabled: enabled, 34 | label: "Background Color", 35 | defaultValue: context.keyStyle.backgroundColor, 36 | onChanged: (color) { 37 | context.keyStyle.backgroundColor = color; 38 | }, 39 | ), 40 | ], 41 | ), 42 | const VerySmallColumnGap(), 43 | SubPanelItem( 44 | title: "Opacity", 45 | enabled: enabled, 46 | child: Selector( 47 | selector: (_, keyStyle) => keyStyle.backgroundOpacity, 48 | builder: (context, opacity, _) => XSlider( 49 | max: 100, 50 | suffix: "%", 51 | value: opacity * 100, 52 | onChanged: (value) { 53 | context.keyStyle.backgroundOpacity = value / 100; 54 | }, 55 | ), 56 | ), 57 | ), 58 | Selector( 59 | selector: (_, keyStyle) { 60 | return keyStyle.keyCapStyle == KeyCapStyle.minimal; 61 | }, 62 | builder: (context, isMinimal, child) => isMinimal 63 | ? Column( 64 | children: [ 65 | const VerySmallColumnGap(), 66 | SubPanelItem( 67 | enabled: enabled, 68 | title: "Rounded Corner", 69 | child: Selector( 70 | selector: (_, keyStyle) => keyStyle.cornerSmoothing, 71 | builder: (context, cornerSmoothing, _) { 72 | return XSlider( 73 | max: 100, 74 | suffix: "%", 75 | value: cornerSmoothing * 100, 76 | onChanged: (value) { 77 | context.keyStyle.cornerSmoothing = 78 | value / 100; 79 | }, 80 | ); 81 | }, 82 | ), 83 | ), 84 | ], 85 | ) 86 | : const SizedBox(), 87 | ), 88 | ], 89 | ); 90 | }, 91 | ); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /lib/windows/settings/views/style/style.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:provider/provider.dart'; 3 | 4 | import 'package:keyviz/config/config.dart'; 5 | import 'package:keyviz/providers/key_style.dart'; 6 | import 'package:keyviz/windows/shared/shared.dart'; 7 | 8 | import '../../widgets/widgets.dart'; 9 | import 'typography.dart'; 10 | import 'background.dart'; 11 | import 'border.dart'; 12 | import 'color.dart'; 13 | import 'layout.dart'; 14 | 15 | class StyleTabView extends StatelessWidget { 16 | const StyleTabView({super.key}); 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | const div = Divider(height: defaultPadding); 21 | 22 | return Column( 23 | children: [ 24 | Padding( 25 | padding: const EdgeInsets.only(bottom: defaultPadding * .5), 26 | child: PanelItem( 27 | title: "Preset", 28 | action: Selector( 29 | selector: (_, keyStyle) => keyStyle.keyCapStyle, 30 | builder: (context, preset, _) { 31 | return XDropdown( 32 | value: preset, 33 | options: KeyCapStyle.values, 34 | onChanged: (value) => context.keyStyle.keyCapStyle = value, 35 | ); 36 | }, 37 | ), 38 | ), 39 | ), 40 | div, 41 | const TypographyView(), 42 | div, 43 | const LayoutView(), 44 | div, 45 | Selector( 46 | selector: (_, keyStyle) { 47 | return keyStyle.keyCapStyle == KeyCapStyle.minimal; 48 | }, 49 | builder: (_, isMinimal, __) { 50 | return isMinimal 51 | ? const SizedBox() 52 | : const Column( 53 | children: [ColorView(), div, BorderView(), div], 54 | ); 55 | }, 56 | ), 57 | const BackgroundView(), 58 | const SmallColumnGap(), 59 | ], 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/windows/settings/views/views.dart: -------------------------------------------------------------------------------- 1 | export 'appearance.dart'; 2 | export 'general.dart'; 3 | export 'mouse.dart'; 4 | export 'style/style.dart'; 5 | export 'about.dart'; 6 | -------------------------------------------------------------------------------- /lib/windows/settings/widgets/color_swatches.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_animate/flutter_animate.dart'; 3 | 4 | import 'package:keyviz/config/config.dart'; 5 | 6 | class ColorSwatches extends StatefulWidget { 7 | const ColorSwatches({ 8 | super.key, 9 | required this.show, 10 | required this.onSelected, 11 | }); 12 | 13 | final bool show; 14 | final void Function(Color color) onSelected; 15 | 16 | @override 17 | State createState() => _ColorSwatchesState(); 18 | } 19 | 20 | class _ColorSwatchesState extends State { 21 | double get _target => widget.show ? 1 : 0; 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | return Container( 26 | width: defaultPadding * 16.65, 27 | padding: const EdgeInsets.all(defaultPadding), 28 | decoration: BoxDecoration( 29 | color: context.colorScheme.primaryContainer, 30 | borderRadius: defaultBorderRadius, 31 | border: Border.all(color: context.colorScheme.outline), 32 | boxShadow: const [ 33 | BoxShadow( 34 | offset: Offset(0, defaultPadding * 0.5), 35 | color: Colors.black12, 36 | blurRadius: defaultPadding * 2, 37 | ) 38 | ], 39 | ), 40 | child: Wrap( 41 | spacing: defaultPadding * .5, 42 | runSpacing: defaultPadding * .5, 43 | children: [ 44 | for (final color in [ 45 | Colors.white, 46 | const Color(0xfff2f2f2), 47 | const Color(0xffcccccc), 48 | const Color(0xff545454), 49 | const Color(0xff1a1a1a), 50 | Colors.black, 51 | ...Colors.primaries, 52 | ]) 53 | GestureDetector( 54 | onTap: () => widget.onSelected(color), 55 | child: SizedBox.square( 56 | dimension: defaultPadding * 2, 57 | child: DecoratedBox( 58 | decoration: BoxDecoration( 59 | color: color, 60 | border: color == context.colorScheme.primaryContainer 61 | ? Border.all(color: context.colorScheme.outline) 62 | : null, 63 | borderRadius: BorderRadius.circular(defaultPadding * .5), 64 | ), 65 | ), 66 | ), 67 | ), 68 | ], 69 | ), 70 | ) 71 | .animate(target: _target) 72 | .effect( 73 | duration: transitionDuration, 74 | curve: Curves.easeInOutCubicEmphasized, 75 | ) 76 | .scaleXY(begin: .6, end: 1) 77 | .fade(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /lib/windows/settings/widgets/cross_dropdown.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_svg/flutter_svg.dart'; 3 | import 'package:keyviz/config/config.dart'; 4 | 5 | class XDropdown extends StatelessWidget { 6 | const XDropdown({ 7 | super.key, 8 | required this.value, 9 | required this.options, 10 | required this.onChanged, 11 | this.decorated = true, 12 | this.labelBuilder, 13 | }); 14 | 15 | final T value; 16 | final bool decorated; 17 | final List options; 18 | final String Function(T option)? labelBuilder; 19 | final ValueChanged onChanged; 20 | 21 | @override 22 | Widget build(BuildContext context) { 23 | final child = DropdownButton( 24 | value: value, 25 | items: [ 26 | for (final option in options) 27 | DropdownMenuItem( 28 | value: option, 29 | child: Text(labelBuilder?.call(option) ?? option.toString()), 30 | ), 31 | ], 32 | onChanged: (v) { 33 | if (v != null) onChanged(v); 34 | }, 35 | // style 36 | isDense: true, 37 | isExpanded: true, 38 | padding: decorated 39 | ? const EdgeInsets.symmetric( 40 | horizontal: defaultPadding * .5, 41 | vertical: defaultPadding * .2, 42 | ) 43 | : EdgeInsets.zero, 44 | borderRadius: BorderRadius.circular(defaultPadding * .6), 45 | dropdownColor: context.colorScheme.primaryContainer, 46 | style: context.textTheme.labelSmall?.copyWith( 47 | fontSize: defaultPadding * .75, 48 | ), 49 | icon: SvgPicture.asset( 50 | VuesaxIcons.chevronDown, 51 | colorFilter: ColorFilter.mode( 52 | context.colorScheme.secondary, 53 | BlendMode.srcIn, 54 | ), 55 | ), 56 | underline: const SizedBox(), 57 | ); 58 | 59 | return decorated 60 | ? DecoratedBox( 61 | decoration: BoxDecoration( 62 | color: context.colorScheme.primaryContainer, 63 | borderRadius: BorderRadius.circular(defaultPadding * .6), 64 | border: Border.all(color: context.colorScheme.outline), 65 | ), 66 | child: child, 67 | ) 68 | : child; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /lib/windows/settings/widgets/cross_expansion_tile.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_animate/flutter_animate.dart'; 3 | 4 | import 'package:keyviz/config/config.dart'; 5 | import 'package:keyviz/windows/shared/shared.dart'; 6 | 7 | class XExpansionTile extends StatefulWidget { 8 | const XExpansionTile({ 9 | super.key, 10 | required this.title, 11 | required this.children, 12 | }); 13 | 14 | final String title; 15 | final List children; 16 | 17 | @override 18 | State createState() => _XExpansionTileState(); 19 | } 20 | 21 | class _XExpansionTileState extends State { 22 | bool _expanded = false; 23 | 24 | @override 25 | Widget build(BuildContext context) { 26 | return Theme( 27 | data: context.theme.copyWith( 28 | dividerColor: Colors.transparent, 29 | ), 30 | child: ExpansionTile( 31 | title: Text( 32 | widget.title, 33 | style: context.textTheme.titleMedium, 34 | ), 35 | tilePadding: EdgeInsets.zero, 36 | controlAffinity: ListTileControlAffinity.leading, 37 | leading: AnimatedRotation( 38 | turns: _expanded ? 0 : -.25, 39 | duration: transitionDuration, 40 | curve: Curves.easeOutCubic, 41 | child: const SvgIcon.chevronDown(size: defaultPadding * .4), 42 | ), 43 | childrenPadding: const EdgeInsets.only( 44 | left: defaultPadding * 2.25, 45 | bottom: defaultPadding, 46 | ), 47 | expandedAlignment: Alignment.centerLeft, 48 | expandedCrossAxisAlignment: CrossAxisAlignment.start, 49 | children: [ 50 | for (int i = 0; i < widget.children.length; i++) 51 | widget.children[i] is VerySmallColumnGap 52 | ? widget.children[i] // don't animate column gap 53 | : widget.children[i] 54 | .animate(target: _expanded ? 1 : 0) 55 | .fadeIn(delay: 75.ms * i) 56 | ], 57 | onExpansionChanged: (expanded) => setState(() => _expanded = expanded), 58 | ), 59 | ); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/windows/settings/widgets/cross_icon_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:keyviz/config/config.dart'; 3 | import 'package:keyviz/windows/shared/shared.dart'; 4 | 5 | class XIconButton extends StatelessWidget { 6 | const XIconButton({ 7 | super.key, 8 | required this.icon, 9 | required this.onTap, 10 | required this.selected, 11 | this.size = 40.0, 12 | this.tooltip, 13 | this.iconSize, 14 | }); 15 | 16 | final String icon; 17 | final double size; 18 | final double? iconSize; 19 | final bool selected; 20 | final String? tooltip; 21 | final VoidCallback onTap; 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | return IconButton( 26 | onPressed: onTap, 27 | style: IconButton.styleFrom( 28 | padding: const EdgeInsets.all(defaultPadding * .4), 29 | ), 30 | tooltip: tooltip, 31 | icon: SvgIcon( 32 | icon: icon, 33 | size: size * .6, 34 | color: selected 35 | ? context.colorScheme.primary 36 | : context.colorScheme.outline, 37 | ), 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/windows/settings/widgets/cross_slider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:keyviz/config/config.dart'; 4 | 5 | class XSlider extends StatelessWidget { 6 | const XSlider({ 7 | super.key, 8 | required this.value, 9 | this.defaultValue, 10 | required this.onChanged, 11 | this.min = 0, 12 | this.max = 10, 13 | this.suffix, 14 | this.divisions, 15 | this.labelWidth, 16 | this.width = defaultPadding * 10, 17 | }); 18 | 19 | final double value; 20 | final double min; 21 | final double max; 22 | final double width; 23 | final String? suffix; 24 | final num? defaultValue; 25 | final int? divisions; 26 | final double? labelWidth; 27 | final ValueChanged onChanged; 28 | 29 | @override 30 | Widget build(BuildContext context) { 31 | return Row( 32 | mainAxisAlignment: MainAxisAlignment.end, 33 | children: [ 34 | SizedBox( 35 | width: width, 36 | child: Slider.adaptive( 37 | value: value, 38 | min: min, 39 | max: max, 40 | divisions: divisions, 41 | onChanged: onChanged, 42 | ), 43 | ), 44 | SizedBox( 45 | width: labelWidth ?? context.textTheme.labelMedium!.fontSize! * 3.5, 46 | child: RichText( 47 | textAlign: TextAlign.end, 48 | text: TextSpan( 49 | text: "${value.toInt()}", 50 | style: context.textTheme.labelMedium, 51 | children: suffix == null 52 | ? null 53 | : [ 54 | TextSpan( 55 | text: suffix, 56 | style: context.textTheme.bodyMedium, 57 | ), 58 | ], 59 | ), 60 | ), 61 | ), 62 | ], 63 | ); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /lib/windows/settings/widgets/cross_switch.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:keyviz/config/config.dart'; 4 | 5 | class XSwitch extends StatelessWidget { 6 | const XSwitch({ 7 | super.key, 8 | required this.value, 9 | required this.onChange, 10 | }); 11 | 12 | final bool value; 13 | final ValueChanged onChange; 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return SizedBox( 18 | height: defaultPadding * 1.5, 19 | child: FittedBox( 20 | fit: BoxFit.contain, 21 | child: Switch( 22 | value: value, 23 | onChanged: onChange, 24 | ), 25 | ), 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/windows/settings/widgets/cross_text_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:keyviz/config/config.dart'; 4 | 5 | class XTextButton extends StatelessWidget { 6 | const XTextButton( 7 | this.text, { 8 | super.key, 9 | required this.onTap, 10 | required this.selected, 11 | }); 12 | 13 | final String text; 14 | final bool selected; 15 | final VoidCallback onTap; 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return GestureDetector( 20 | onTap: onTap, 21 | child: Container( 22 | padding: const EdgeInsets.symmetric( 23 | horizontal: defaultPadding * .6, 24 | ), 25 | decoration: BoxDecoration( 26 | color: selected ? context.colorScheme.primary : Colors.transparent, 27 | borderRadius: BorderRadius.circular(defaultPadding * .6), 28 | ), 29 | alignment: Alignment.center, 30 | child: Text( 31 | text, 32 | style: context.textTheme.labelSmall?.copyWith( 33 | fontSize: 14, 34 | color: selected 35 | ? context.colorScheme.onPrimary 36 | : context.colorScheme.tertiary, 37 | // fontWeight: selected ? FontWeight.bold : FontWeight.normal, 38 | ), 39 | ), 40 | ), 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/windows/settings/widgets/panel_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:keyviz/config/config.dart'; 4 | 5 | class PanelItem extends StatelessWidget { 6 | const PanelItem({ 7 | super.key, 8 | required this.title, 9 | required this.action, 10 | this.asRow = true, 11 | this.enabled = true, 12 | this.subtitle, 13 | this.actionFlex = 1, 14 | this.crossAxisAlignment, 15 | }); 16 | 17 | final String title; 18 | final String? subtitle; 19 | final Widget action; 20 | final bool asRow; 21 | final bool enabled; 22 | final int actionFlex; 23 | final CrossAxisAlignment? crossAxisAlignment; 24 | 25 | @override 26 | Widget build(BuildContext context) { 27 | final label = Column( 28 | crossAxisAlignment: CrossAxisAlignment.start, 29 | children: [ 30 | Text( 31 | title, 32 | style: context.textTheme.titleMedium, 33 | ), 34 | const SizedBox(height: defaultPadding * .25), 35 | if (subtitle != null) 36 | Text( 37 | subtitle!, 38 | style: context.textTheme.bodyMedium, 39 | ), 40 | ], 41 | ); 42 | 43 | return Opacity( 44 | opacity: enabled ? 1 : .4, 45 | child: asRow 46 | ? Row( 47 | crossAxisAlignment: crossAxisAlignment ?? 48 | (subtitle == null 49 | ? CrossAxisAlignment.center 50 | : CrossAxisAlignment.start), 51 | children: [ 52 | Expanded( 53 | flex: 3, 54 | child: Padding( 55 | padding: const EdgeInsets.only(right: defaultPadding * .5), 56 | child: label, 57 | ), 58 | ), 59 | Expanded( 60 | flex: actionFlex, 61 | child: Align( 62 | alignment: Alignment.topRight, 63 | child: IgnorePointer( 64 | ignoring: !enabled, 65 | child: action, 66 | ), 67 | ), 68 | ), 69 | ], 70 | ) 71 | : Column( 72 | crossAxisAlignment: 73 | crossAxisAlignment ?? CrossAxisAlignment.start, 74 | children: [ 75 | label, 76 | const SizedBox(height: defaultPadding * .75), 77 | IgnorePointer(ignoring: !enabled, child: action), 78 | ], 79 | ), 80 | ); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /lib/windows/settings/widgets/sidebar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:keyviz/config/config.dart'; 4 | import 'package:keyviz/windows/shared/shared.dart'; 5 | 6 | enum SettingsTab { 7 | general(VuesaxIcons.cogWheel), 8 | mouse(VuesaxIcons.mouse), 9 | keycap(VuesaxIcons.keyboard), 10 | appearance(VuesaxIcons.monitor), 11 | about(VuesaxIcons.more); 12 | 13 | const SettingsTab(this.icon); 14 | final String icon; 15 | } 16 | 17 | class SideBar extends StatelessWidget { 18 | const SideBar({ 19 | super.key, 20 | required this.currentTab, 21 | required this.onChange, 22 | }); 23 | 24 | final SettingsTab currentTab; 25 | final void Function(SettingsTab tab) onChange; 26 | 27 | @override 28 | Widget build(BuildContext context) { 29 | final children = []; 30 | const tabs = SettingsTab.values; 31 | 32 | for (int i = 0; i < tabs.length; i++) { 33 | final tab = tabs[i]; 34 | // position conditions 35 | final isFirst = i == 0; 36 | final isLast = i == tabs.length - 1; 37 | 38 | // add spacing between buttons 39 | if (!isFirst) { 40 | // push the About button to the end 41 | if (isLast) { 42 | children.add(const Spacer()); 43 | } 44 | // add column gap 45 | else { 46 | children.add(const SizedBox(height: defaultPadding * .25)); 47 | } 48 | } 49 | 50 | // add icon button 51 | children.add( 52 | _IconButton( 53 | icon: tab.icon, 54 | tooltip: tab.name, 55 | onTap: () => onChange(tab), 56 | selected: currentTab == tab, 57 | ), 58 | ); 59 | } 60 | 61 | return Column(children: children); 62 | } 63 | } 64 | 65 | class _IconButton extends StatelessWidget { 66 | const _IconButton({ 67 | required this.icon, 68 | required this.tooltip, 69 | required this.onTap, 70 | required this.selected, 71 | this.size = 40.0, 72 | }); 73 | 74 | final String icon; 75 | final String tooltip; 76 | final bool selected; 77 | final VoidCallback onTap; 78 | final double size; 79 | 80 | @override 81 | Widget build(BuildContext context) { 82 | return IconButton( 83 | onPressed: onTap, 84 | tooltip: tooltip.capitalize(), 85 | icon: SvgIcon( 86 | size: size * .44, 87 | icon: icon, 88 | color: selected 89 | ? context.colorScheme.primary 90 | : context.isDark 91 | ? context.colorScheme.outline 92 | : context.colorScheme.tertiary, 93 | ), 94 | style: IconButton.styleFrom( 95 | fixedSize: Size.square(size), 96 | backgroundColor: selected 97 | ? context.colorScheme.secondaryContainer 98 | : context.colorScheme.secondaryContainer.withOpacity(0), 99 | shape: RoundedRectangleBorder( 100 | borderRadius: BorderRadius.circular(defaultPadding * .5), 101 | ), 102 | ), 103 | ); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /lib/windows/settings/widgets/widgets.dart: -------------------------------------------------------------------------------- 1 | export 'color_picker.dart'; 2 | export 'color_swatches.dart'; 3 | export 'cross_dropdown.dart'; 4 | export 'cross_expansion_tile.dart'; 5 | export 'cross_icon_button.dart'; 6 | export 'cross_slider.dart'; 7 | export 'cross_switch.dart'; 8 | export 'cross_text_button.dart'; 9 | export 'gradient_picker.dart'; 10 | export 'panel_item.dart'; 11 | export 'sidebar.dart'; 12 | export 'sub_panel_item.dart'; 13 | -------------------------------------------------------------------------------- /lib/windows/shared/shared.dart: -------------------------------------------------------------------------------- 1 | export 'spacing.dart'; 2 | export 'svg_icon.dart'; 3 | -------------------------------------------------------------------------------- /lib/windows/shared/spacing.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:keyviz/config/style.dart'; 4 | 5 | class VerySmallRowGap extends StatelessWidget { 6 | const VerySmallRowGap({super.key}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return const SizedBox(width: defaultPadding * .6); 11 | } 12 | } 13 | 14 | class SmallRowGap extends StatelessWidget { 15 | const SmallRowGap({super.key}); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return const SizedBox(width: defaultPadding); 20 | } 21 | } 22 | 23 | class RowGap extends StatelessWidget { 24 | const RowGap({super.key}); 25 | 26 | @override 27 | Widget build(BuildContext context) { 28 | return const SizedBox(width: defaultPadding * 2); 29 | } 30 | } 31 | 32 | class VerySmallColumnGap extends StatelessWidget { 33 | const VerySmallColumnGap({super.key}); 34 | 35 | @override 36 | Widget build(BuildContext context) { 37 | return const SizedBox(height: defaultPadding * .6); 38 | } 39 | } 40 | 41 | class SmallColumnGap extends StatelessWidget { 42 | const SmallColumnGap({super.key}); 43 | 44 | @override 45 | Widget build(BuildContext context) { 46 | return const SizedBox(height: defaultPadding); 47 | } 48 | } 49 | 50 | class ColumnGap extends StatelessWidget { 51 | const ColumnGap({super.key}); 52 | 53 | @override 54 | Widget build(BuildContext context) { 55 | return const SizedBox(height: defaultPadding * 2); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lib/windows/shared/svg_icon.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_svg/flutter_svg.dart'; 3 | import 'package:keyviz/config/config.dart'; 4 | 5 | class SvgIcon extends StatelessWidget { 6 | const SvgIcon({ 7 | super.key, 8 | this.color, 9 | required this.icon, 10 | this.size = defaultPadding, 11 | }); 12 | 13 | final String icon; 14 | final Color? color; 15 | final double size; 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return SvgPicture.asset( 20 | icon, 21 | height: size, 22 | width: size, 23 | fit: BoxFit.contain, 24 | colorFilter: ColorFilter.mode( 25 | color ?? context.colorScheme.secondary, BlendMode.srcIn), 26 | ); 27 | } 28 | 29 | const SvgIcon.chevronDown({ 30 | super.key, 31 | this.color, 32 | this.size = defaultPadding / 2, 33 | }) : icon = VuesaxIcons.chevronDown; 34 | 35 | const SvgIcon.cross({ 36 | super.key, 37 | this.color, 38 | this.size = defaultPadding / 2, 39 | }) : icon = VuesaxIcons.cross; 40 | 41 | const SvgIcon.arrowRight({ 42 | super.key, 43 | this.color, 44 | this.size = defaultPadding / 2, 45 | }) : icon = VuesaxIcons.arrowRight; 46 | } 47 | -------------------------------------------------------------------------------- /linux/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral 2 | -------------------------------------------------------------------------------- /linux/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file controls Flutter-level build steps. It should not be edited. 2 | cmake_minimum_required(VERSION 3.10) 3 | 4 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 5 | 6 | # Configuration provided via flutter tool. 7 | include(${EPHEMERAL_DIR}/generated_config.cmake) 8 | 9 | # TODO: Move the rest of this into files in ephemeral. See 10 | # https://github.com/flutter/flutter/issues/57146. 11 | 12 | # Serves the same purpose as list(TRANSFORM ... PREPEND ...), 13 | # which isn't available in 3.10. 14 | function(list_prepend LIST_NAME PREFIX) 15 | set(NEW_LIST "") 16 | foreach(element ${${LIST_NAME}}) 17 | list(APPEND NEW_LIST "${PREFIX}${element}") 18 | endforeach(element) 19 | set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) 20 | endfunction() 21 | 22 | # === Flutter Library === 23 | # System-level dependencies. 24 | find_package(PkgConfig REQUIRED) 25 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 26 | pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) 27 | pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) 28 | 29 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") 30 | 31 | # Published to parent scope for install step. 32 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 33 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 34 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 35 | set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) 36 | 37 | list(APPEND FLUTTER_LIBRARY_HEADERS 38 | "fl_basic_message_channel.h" 39 | "fl_binary_codec.h" 40 | "fl_binary_messenger.h" 41 | "fl_dart_project.h" 42 | "fl_engine.h" 43 | "fl_json_message_codec.h" 44 | "fl_json_method_codec.h" 45 | "fl_message_codec.h" 46 | "fl_method_call.h" 47 | "fl_method_channel.h" 48 | "fl_method_codec.h" 49 | "fl_method_response.h" 50 | "fl_plugin_registrar.h" 51 | "fl_plugin_registry.h" 52 | "fl_standard_message_codec.h" 53 | "fl_standard_method_codec.h" 54 | "fl_string_codec.h" 55 | "fl_value.h" 56 | "fl_view.h" 57 | "flutter_linux.h" 58 | ) 59 | list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") 60 | add_library(flutter INTERFACE) 61 | target_include_directories(flutter INTERFACE 62 | "${EPHEMERAL_DIR}" 63 | ) 64 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") 65 | target_link_libraries(flutter INTERFACE 66 | PkgConfig::GTK 67 | PkgConfig::GLIB 68 | PkgConfig::GIO 69 | ) 70 | add_dependencies(flutter flutter_assemble) 71 | 72 | # === Flutter tool backend === 73 | # _phony_ is a non-existent file to force this command to run every time, 74 | # since currently there's no way to get a full input/output list from the 75 | # flutter tool. 76 | add_custom_command( 77 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 78 | ${CMAKE_CURRENT_BINARY_DIR}/_phony_ 79 | COMMAND ${CMAKE_COMMAND} -E env 80 | ${FLUTTER_TOOL_ENVIRONMENT} 81 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" 82 | ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} 83 | VERBATIM 84 | ) 85 | add_custom_target(flutter_assemble DEPENDS 86 | "${FLUTTER_LIBRARY}" 87 | ${FLUTTER_LIBRARY_HEADERS} 88 | ) 89 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | void fl_register_plugins(FlPluginRegistry* registry) { 18 | g_autoptr(FlPluginRegistrar) flutter_acrylic_registrar = 19 | fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterAcrylicPlugin"); 20 | flutter_acrylic_plugin_register_with_registrar(flutter_acrylic_registrar); 21 | g_autoptr(FlPluginRegistrar) hid_listener_registrar = 22 | fl_plugin_registry_get_registrar_for_plugin(registry, "HidListenerPlugin"); 23 | hid_listener_plugin_register_with_registrar(hid_listener_registrar); 24 | g_autoptr(FlPluginRegistrar) screen_retriever_registrar = 25 | fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin"); 26 | screen_retriever_plugin_register_with_registrar(screen_retriever_registrar); 27 | g_autoptr(FlPluginRegistrar) tray_manager_registrar = 28 | fl_plugin_registry_get_registrar_for_plugin(registry, "TrayManagerPlugin"); 29 | tray_manager_plugin_register_with_registrar(tray_manager_registrar); 30 | g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = 31 | fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); 32 | url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); 33 | g_autoptr(FlPluginRegistrar) window_manager_registrar = 34 | fl_plugin_registry_get_registrar_for_plugin(registry, "WindowManagerPlugin"); 35 | window_manager_plugin_register_with_registrar(window_manager_registrar); 36 | g_autoptr(FlPluginRegistrar) window_size_registrar = 37 | fl_plugin_registry_get_registrar_for_plugin(registry, "WindowSizePlugin"); 38 | window_size_plugin_register_with_registrar(window_size_registrar); 39 | } 40 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void fl_register_plugins(FlPluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | flutter_acrylic 7 | hid_listener 8 | screen_retriever 9 | tray_manager 10 | url_launcher_linux 11 | window_manager 12 | window_size 13 | ) 14 | 15 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 16 | ) 17 | 18 | set(PLUGIN_BUNDLED_LIBRARIES) 19 | 20 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 21 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) 22 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 23 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 24 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 25 | endforeach(plugin) 26 | 27 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 28 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) 29 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 30 | endforeach(ffi_plugin) 31 | -------------------------------------------------------------------------------- /linux/main.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | #include 3 | 4 | int main(int argc, char** argv) { 5 | // hid_listener instance 6 | HidListener listener; 7 | 8 | g_autoptr(MyApplication) app = my_application_new(); 9 | return g_application_run(G_APPLICATION(app), argc, argv); 10 | } 11 | -------------------------------------------------------------------------------- /linux/my_application.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | #include 4 | #ifdef GDK_WINDOWING_X11 5 | #include 6 | #endif 7 | 8 | #include "flutter/generated_plugin_registrant.h" 9 | 10 | struct _MyApplication { 11 | GtkApplication parent_instance; 12 | char** dart_entrypoint_arguments; 13 | }; 14 | 15 | G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) 16 | 17 | // Implements GApplication::activate. 18 | static void my_application_activate(GApplication* application) { 19 | MyApplication* self = MY_APPLICATION(application); 20 | GtkWindow* window = 21 | GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); 22 | 23 | // Use a header bar when running in GNOME as this is the common style used 24 | // by applications and is the setup most users will be using (e.g. Ubuntu 25 | // desktop). 26 | // If running on X and not using GNOME then just use a traditional title bar 27 | // in case the window manager does more exotic layout, e.g. tiling. 28 | // If running on Wayland assume the header bar will work (may need changing 29 | // if future cases occur). 30 | gboolean use_header_bar = TRUE; 31 | #ifdef GDK_WINDOWING_X11 32 | GdkScreen* screen = gtk_window_get_screen(window); 33 | if (GDK_IS_X11_SCREEN(screen)) { 34 | const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); 35 | if (g_strcmp0(wm_name, "GNOME Shell") != 0) { 36 | use_header_bar = FALSE; 37 | } 38 | } 39 | #endif 40 | if (use_header_bar) { 41 | GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); 42 | gtk_widget_show(GTK_WIDGET(header_bar)); 43 | gtk_header_bar_set_title(header_bar, "keyviz"); 44 | gtk_header_bar_set_show_close_button(header_bar, TRUE); 45 | gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); 46 | } else { 47 | gtk_window_set_title(window, "keyviz"); 48 | } 49 | 50 | gtk_window_set_default_size(window, 1280, 720); 51 | gtk_widget_show(GTK_WIDGET(window)); 52 | 53 | g_autoptr(FlDartProject) project = fl_dart_project_new(); 54 | fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); 55 | 56 | FlView* view = fl_view_new(project); 57 | gtk_widget_show(GTK_WIDGET(view)); 58 | gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); 59 | 60 | fl_register_plugins(FL_PLUGIN_REGISTRY(view)); 61 | 62 | gtk_widget_grab_focus(GTK_WIDGET(view)); 63 | } 64 | 65 | // Implements GApplication::local_command_line. 66 | static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { 67 | MyApplication* self = MY_APPLICATION(application); 68 | // Strip out the first argument as it is the binary name. 69 | self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); 70 | 71 | g_autoptr(GError) error = nullptr; 72 | if (!g_application_register(application, nullptr, &error)) { 73 | g_warning("Failed to register: %s", error->message); 74 | *exit_status = 1; 75 | return TRUE; 76 | } 77 | 78 | g_application_activate(application); 79 | *exit_status = 0; 80 | 81 | return TRUE; 82 | } 83 | 84 | // Implements GObject::dispose. 85 | static void my_application_dispose(GObject* object) { 86 | MyApplication* self = MY_APPLICATION(object); 87 | g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); 88 | G_OBJECT_CLASS(my_application_parent_class)->dispose(object); 89 | } 90 | 91 | static void my_application_class_init(MyApplicationClass* klass) { 92 | G_APPLICATION_CLASS(klass)->activate = my_application_activate; 93 | G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; 94 | G_OBJECT_CLASS(klass)->dispose = my_application_dispose; 95 | } 96 | 97 | static void my_application_init(MyApplication* self) {} 98 | 99 | MyApplication* my_application_new() { 100 | return MY_APPLICATION(g_object_new(my_application_get_type(), 101 | "application-id", APPLICATION_ID, 102 | "flags", G_APPLICATION_NON_UNIQUE, 103 | nullptr)); 104 | } 105 | -------------------------------------------------------------------------------- /linux/my_application.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_MY_APPLICATION_H_ 2 | #define FLUTTER_MY_APPLICATION_H_ 3 | 4 | #include 5 | 6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, 7 | GtkApplication) 8 | 9 | /** 10 | * my_application_new: 11 | * 12 | * Creates a new Flutter-based application. 13 | * 14 | * Returns: a new #MyApplication. 15 | */ 16 | MyApplication* my_application_new(); 17 | 18 | #endif // FLUTTER_MY_APPLICATION_H_ 19 | -------------------------------------------------------------------------------- /macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/dgph 7 | **/xcuserdata/ 8 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Flutter/GeneratedPluginRegistrant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | import FlutterMacOS 6 | import Foundation 7 | 8 | import hid_listener 9 | import macos_window_utils 10 | import path_provider_foundation 11 | import screen_retriever 12 | import tray_manager 13 | import url_launcher_macos 14 | import window_manager 15 | import window_size 16 | 17 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { 18 | HidListenerPlugin.register(with: registry.registrar(forPlugin: "HidListenerPlugin")) 19 | MacOSWindowUtilsPlugin.register(with: registry.registrar(forPlugin: "MacOSWindowUtilsPlugin")) 20 | PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) 21 | ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin")) 22 | TrayManagerPlugin.register(with: registry.registrar(forPlugin: "TrayManagerPlugin")) 23 | UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) 24 | WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin")) 25 | WindowSizePlugin.register(with: registry.registrar(forPlugin: "WindowSizePlugin")) 26 | } 27 | -------------------------------------------------------------------------------- /macos/Podfile: -------------------------------------------------------------------------------- 1 | platform :osx, '10.14.6' 2 | 3 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 4 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 5 | 6 | project 'Runner', { 7 | 'Debug' => :debug, 8 | 'Profile' => :release, 9 | 'Release' => :release, 10 | } 11 | 12 | def flutter_root 13 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) 14 | unless File.exist?(generated_xcode_build_settings_path) 15 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" 16 | end 17 | 18 | File.foreach(generated_xcode_build_settings_path) do |line| 19 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 20 | return matches[1].strip if matches 21 | end 22 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" 23 | end 24 | 25 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 26 | 27 | flutter_macos_podfile_setup 28 | 29 | target 'Runner' do 30 | use_frameworks! 31 | use_modular_headers! 32 | 33 | flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) 34 | target 'RunnerTests' do 35 | inherit! :search_paths 36 | end 37 | end 38 | 39 | post_install do |installer| 40 | installer.pods_project.targets.each do |target| 41 | flutter_additional_macos_build_settings(target) 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /macos/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - FlutterMacOS (1.0.0) 3 | - hid_listener (1.0.0): 4 | - FlutterMacOS 5 | - macos_window_utils (1.0.0): 6 | - FlutterMacOS 7 | - path_provider_foundation (0.0.1): 8 | - Flutter 9 | - FlutterMacOS 10 | - screen_retriever (0.0.1): 11 | - FlutterMacOS 12 | - tray_manager (0.0.1): 13 | - FlutterMacOS 14 | - url_launcher_macos (0.0.1): 15 | - FlutterMacOS 16 | - window_manager (0.2.0): 17 | - FlutterMacOS 18 | - window_size (0.0.2): 19 | - FlutterMacOS 20 | 21 | DEPENDENCIES: 22 | - FlutterMacOS (from `Flutter/ephemeral`) 23 | - hid_listener (from `Flutter/ephemeral/.symlinks/plugins/hid_listener/macos`) 24 | - macos_window_utils (from `Flutter/ephemeral/.symlinks/plugins/macos_window_utils/macos`) 25 | - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) 26 | - screen_retriever (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos`) 27 | - tray_manager (from `Flutter/ephemeral/.symlinks/plugins/tray_manager/macos`) 28 | - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) 29 | - window_manager (from `Flutter/ephemeral/.symlinks/plugins/window_manager/macos`) 30 | - window_size (from `Flutter/ephemeral/.symlinks/plugins/window_size/macos`) 31 | 32 | EXTERNAL SOURCES: 33 | FlutterMacOS: 34 | :path: Flutter/ephemeral 35 | hid_listener: 36 | :path: Flutter/ephemeral/.symlinks/plugins/hid_listener/macos 37 | macos_window_utils: 38 | :path: Flutter/ephemeral/.symlinks/plugins/macos_window_utils/macos 39 | path_provider_foundation: 40 | :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin 41 | screen_retriever: 42 | :path: Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos 43 | tray_manager: 44 | :path: Flutter/ephemeral/.symlinks/plugins/tray_manager/macos 45 | url_launcher_macos: 46 | :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos 47 | window_manager: 48 | :path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos 49 | window_size: 50 | :path: Flutter/ephemeral/.symlinks/plugins/window_size/macos 51 | 52 | SPEC CHECKSUMS: 53 | FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 54 | hid_listener: 3c81908608eecb6fa0f5cc3cc41f896f298f45d0 55 | macos_window_utils: 933f91f64805e2eb91a5bd057cf97cd097276663 56 | path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c 57 | screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38 58 | tray_manager: 9064e219c56d75c476e46b9a21182087930baf90 59 | url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95 60 | window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8 61 | window_size: 339dafa0b27a95a62a843042038fa6c3c48de195 62 | 63 | PODFILE CHECKSUM: 16208599a12443d53889ba2270a4985981cfb204 64 | 65 | COCOAPODS: 1.15.2 66 | -------------------------------------------------------------------------------- /macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 43 | 49 | 50 | 51 | 52 | 53 | 63 | 65 | 71 | 72 | 73 | 74 | 80 | 82 | 88 | 89 | 90 | 91 | 93 | 94 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @NSApplicationMain 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "app_icon_16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "app_icon_32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "app_icon_32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "app_icon_64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "app_icon_128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "app_icon_256.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "app_icon_256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "app_icon_512.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "app_icon_512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "app_icon_1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mulaRahul/keyviz/0452c3c060d2b6fe5706cc85f43c44992523ab4e/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mulaRahul/keyviz/0452c3c060d2b6fe5706cc85f43c44992523ab4e/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mulaRahul/keyviz/0452c3c060d2b6fe5706cc85f43c44992523ab4e/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mulaRahul/keyviz/0452c3c060d2b6fe5706cc85f43c44992523ab4e/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mulaRahul/keyviz/0452c3c060d2b6fe5706cc85f43c44992523ab4e/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mulaRahul/keyviz/0452c3c060d2b6fe5706cc85f43c44992523ab4e/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mulaRahul/keyviz/0452c3c060d2b6fe5706cc85f43c44992523ab4e/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = keyviz 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = com.example.keyviz 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2023 com.example. All rights reserved. 15 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.network.server 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /macos/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | $(PRODUCT_COPYRIGHT) 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | import hid_listener 4 | import window_manager 5 | 6 | class MainFlutterWindow: NSWindow { 7 | // hid_listener instance 8 | let listener = HidListener() 9 | 10 | override func awakeFromNib() { 11 | let flutterViewController = FlutterViewController() 12 | let windowFrame = self.frame 13 | self.contentViewController = flutterViewController 14 | self.setFrame(windowFrame, display: true) 15 | 16 | RegisterGeneratedPlugins(registry: flutterViewController) 17 | 18 | super.awakeFromNib() 19 | } 20 | override public func order(_ place: NSWindow.OrderingMode, relativeTo otherWin: Int) { 21 | super.order(place, relativeTo: otherWin) 22 | hiddenWindowAtLaunch() 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import FlutterMacOS 2 | import Cocoa 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /previews/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mulaRahul/keyviz/0452c3c060d2b6fe5706cc85f43c44992523ab4e/previews/banner.png -------------------------------------------------------------------------------- /previews/banner.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: keyviz 2 | description: visualize your keypress and mouse actions 3 | 4 | publish_to: 'none' 5 | 6 | version: 2.0.0-alpha2 7 | 8 | environment: 9 | sdk: '>=3.0.5 <4.0.0' 10 | 11 | dependencies: 12 | flutter: 13 | sdk: flutter 14 | 15 | # cross-platform HID (Human Input Device) listener 16 | hid_listener: ^2.0.1 17 | 18 | window_size: 19 | git: 20 | url: https://github.com/google/flutter-desktop-embedding.git 21 | path: plugins/window_size 22 | ref: 6c66ad2 23 | 24 | tuple: ^2.0.2 25 | provider: ^6.1.2 26 | flutter_svg: ^2.0.9 27 | vector_math: ^2.1.4 28 | tray_manager: ^0.2.2 29 | url_launcher: ^6.2.5 30 | window_manager: ^0.4.0 31 | path_provider: ^2.1.2 32 | flutter_animate: ^4.5.0 33 | flutter_acrylic: ^1.1.3 34 | macos_window_utils: ^1.4.0 35 | 36 | dev_dependencies: 37 | flutter_test: 38 | sdk: flutter 39 | flutter_lints: ^4.0.0 40 | msix: ^3.16.7 41 | 42 | flutter: 43 | 44 | uses-material-design: true 45 | 46 | assets: 47 | - assets/img/ 48 | - assets/icons/ 49 | 50 | fonts: 51 | - family: IBM Plex Sans 52 | fonts: 53 | - asset: assets/fonts/IBMPlexSans-Thin.ttf 54 | weight: 100 55 | - asset: assets/fonts/IBMPlexSans-ExtraLight.ttf 56 | weight: 200 57 | - asset: assets/fonts/IBMPlexSans-Light.ttf 58 | weight: 300 59 | - asset: assets/fonts/IBMPlexSans-Regular.ttf 60 | weight: 400 61 | - asset: assets/fonts/IBMPlexSans-Medium.ttf 62 | weight: 500 63 | - asset: assets/fonts/IBMPlexSans-SemiBold.ttf 64 | weight: 700 65 | - asset: assets/fonts/IBMPlexSans-Bold.ttf 66 | weight: 900 67 | 68 | - family: Inter 69 | fonts: 70 | - asset: assets/fonts/Inter-Thin.ttf 71 | weight: 200 72 | - asset: assets/fonts/Inter-Regular.ttf 73 | - asset: assets/fonts/Inter-SemiBold.ttf 74 | weight: 600 -------------------------------------------------------------------------------- /windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /windows/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file controls Flutter-level build steps. It should not be edited. 2 | cmake_minimum_required(VERSION 3.14) 3 | 4 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 5 | 6 | # Configuration provided via flutter tool. 7 | include(${EPHEMERAL_DIR}/generated_config.cmake) 8 | 9 | # TODO: Move the rest of this into files in ephemeral. See 10 | # https://github.com/flutter/flutter/issues/57146. 11 | set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") 12 | 13 | # Set fallback configurations for older versions of the flutter tool. 14 | if (NOT DEFINED FLUTTER_TARGET_PLATFORM) 15 | set(FLUTTER_TARGET_PLATFORM "windows-x64") 16 | endif() 17 | 18 | # === Flutter Library === 19 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") 20 | 21 | # Published to parent scope for install step. 22 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 23 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 24 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 25 | set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) 26 | 27 | list(APPEND FLUTTER_LIBRARY_HEADERS 28 | "flutter_export.h" 29 | "flutter_windows.h" 30 | "flutter_messenger.h" 31 | "flutter_plugin_registrar.h" 32 | "flutter_texture_registrar.h" 33 | ) 34 | list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") 35 | add_library(flutter INTERFACE) 36 | target_include_directories(flutter INTERFACE 37 | "${EPHEMERAL_DIR}" 38 | ) 39 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") 40 | add_dependencies(flutter flutter_assemble) 41 | 42 | # === Wrapper === 43 | list(APPEND CPP_WRAPPER_SOURCES_CORE 44 | "core_implementations.cc" 45 | "standard_codec.cc" 46 | ) 47 | list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") 48 | list(APPEND CPP_WRAPPER_SOURCES_PLUGIN 49 | "plugin_registrar.cc" 50 | ) 51 | list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") 52 | list(APPEND CPP_WRAPPER_SOURCES_APP 53 | "flutter_engine.cc" 54 | "flutter_view_controller.cc" 55 | ) 56 | list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") 57 | 58 | # Wrapper sources needed for a plugin. 59 | add_library(flutter_wrapper_plugin STATIC 60 | ${CPP_WRAPPER_SOURCES_CORE} 61 | ${CPP_WRAPPER_SOURCES_PLUGIN} 62 | ) 63 | apply_standard_settings(flutter_wrapper_plugin) 64 | set_target_properties(flutter_wrapper_plugin PROPERTIES 65 | POSITION_INDEPENDENT_CODE ON) 66 | set_target_properties(flutter_wrapper_plugin PROPERTIES 67 | CXX_VISIBILITY_PRESET hidden) 68 | target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) 69 | target_include_directories(flutter_wrapper_plugin PUBLIC 70 | "${WRAPPER_ROOT}/include" 71 | ) 72 | add_dependencies(flutter_wrapper_plugin flutter_assemble) 73 | 74 | # Wrapper sources needed for the runner. 75 | add_library(flutter_wrapper_app STATIC 76 | ${CPP_WRAPPER_SOURCES_CORE} 77 | ${CPP_WRAPPER_SOURCES_APP} 78 | ) 79 | apply_standard_settings(flutter_wrapper_app) 80 | target_link_libraries(flutter_wrapper_app PUBLIC flutter) 81 | target_include_directories(flutter_wrapper_app PUBLIC 82 | "${WRAPPER_ROOT}/include" 83 | ) 84 | add_dependencies(flutter_wrapper_app flutter_assemble) 85 | 86 | # === Flutter tool backend === 87 | # _phony_ is a non-existent file to force this command to run every time, 88 | # since currently there's no way to get a full input/output list from the 89 | # flutter tool. 90 | set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") 91 | set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) 92 | add_custom_command( 93 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 94 | ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} 95 | ${CPP_WRAPPER_SOURCES_APP} 96 | ${PHONY_OUTPUT} 97 | COMMAND ${CMAKE_COMMAND} -E env 98 | ${FLUTTER_TOOL_ENVIRONMENT} 99 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" 100 | ${FLUTTER_TARGET_PLATFORM} $ 101 | VERBATIM 102 | ) 103 | add_custom_target(flutter_assemble DEPENDS 104 | "${FLUTTER_LIBRARY}" 105 | ${FLUTTER_LIBRARY_HEADERS} 106 | ${CPP_WRAPPER_SOURCES_CORE} 107 | ${CPP_WRAPPER_SOURCES_PLUGIN} 108 | ${CPP_WRAPPER_SOURCES_APP} 109 | ) 110 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | void RegisterPlugins(flutter::PluginRegistry* registry) { 18 | FlutterAcrylicPluginRegisterWithRegistrar( 19 | registry->GetRegistrarForPlugin("FlutterAcrylicPlugin")); 20 | HidListenerPluginWindowsRegisterWithRegistrar( 21 | registry->GetRegistrarForPlugin("HidListenerPluginWindows")); 22 | ScreenRetrieverPluginRegisterWithRegistrar( 23 | registry->GetRegistrarForPlugin("ScreenRetrieverPlugin")); 24 | TrayManagerPluginRegisterWithRegistrar( 25 | registry->GetRegistrarForPlugin("TrayManagerPlugin")); 26 | UrlLauncherWindowsRegisterWithRegistrar( 27 | registry->GetRegistrarForPlugin("UrlLauncherWindows")); 28 | WindowManagerPluginRegisterWithRegistrar( 29 | registry->GetRegistrarForPlugin("WindowManagerPlugin")); 30 | WindowSizePluginRegisterWithRegistrar( 31 | registry->GetRegistrarForPlugin("WindowSizePlugin")); 32 | } 33 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void RegisterPlugins(flutter::PluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | flutter_acrylic 7 | hid_listener 8 | screen_retriever 9 | tray_manager 10 | url_launcher_windows 11 | window_manager 12 | window_size 13 | ) 14 | 15 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 16 | ) 17 | 18 | set(PLUGIN_BUNDLED_LIBRARIES) 19 | 20 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 21 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) 22 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 23 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 24 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 25 | endforeach(plugin) 26 | 27 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 28 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) 29 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 30 | endforeach(ffi_plugin) 31 | -------------------------------------------------------------------------------- /windows/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(runner LANGUAGES CXX) 3 | 4 | # Define the application target. To change its name, change BINARY_NAME in the 5 | # top-level CMakeLists.txt, not the value here, or `flutter run` will no longer 6 | # work. 7 | # 8 | # Any new source files that you add to the application should be added here. 9 | add_executable(${BINARY_NAME} WIN32 10 | "flutter_window.cpp" 11 | "main.cpp" 12 | "utils.cpp" 13 | "win32_window.cpp" 14 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 15 | "Runner.rc" 16 | "runner.exe.manifest" 17 | ) 18 | 19 | # Apply the standard set of build settings. This can be removed for applications 20 | # that need different build settings. 21 | apply_standard_settings(${BINARY_NAME}) 22 | 23 | # Add preprocessor definitions for the build version. 24 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") 25 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") 26 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") 27 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") 28 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") 29 | 30 | # Disable Windows macros that collide with C++ standard library functions. 31 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") 32 | 33 | # Add dependency libraries and include directories. Add any application-specific 34 | # dependencies here. 35 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) 36 | target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") 37 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 38 | 39 | # Run the Flutter tool portions of the build. This must not be removed. 40 | add_dependencies(${BINARY_NAME} flutter_assemble) 41 | -------------------------------------------------------------------------------- /windows/runner/Runner.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #pragma code_page(65001) 4 | #include "resource.h" 5 | 6 | #define APSTUDIO_READONLY_SYMBOLS 7 | ///////////////////////////////////////////////////////////////////////////// 8 | // 9 | // Generated from the TEXTINCLUDE 2 resource. 10 | // 11 | #include "winres.h" 12 | 13 | ///////////////////////////////////////////////////////////////////////////// 14 | #undef APSTUDIO_READONLY_SYMBOLS 15 | 16 | ///////////////////////////////////////////////////////////////////////////// 17 | // English (United States) resources 18 | 19 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 20 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // Icon 51 | // 52 | 53 | // Icon with lowest ID value placed first to ensure application icon 54 | // remains consistent on all systems. 55 | IDI_APP_ICON ICON "resources\\app_icon.ico" 56 | 57 | 58 | ///////////////////////////////////////////////////////////////////////////// 59 | // 60 | // Version 61 | // 62 | 63 | #if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) 64 | #define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD 65 | #else 66 | #define VERSION_AS_NUMBER 1,0,0,0 67 | #endif 68 | 69 | #if defined(FLUTTER_VERSION) 70 | #define VERSION_AS_STRING FLUTTER_VERSION 71 | #else 72 | #define VERSION_AS_STRING "1.0.0" 73 | #endif 74 | 75 | VS_VERSION_INFO VERSIONINFO 76 | FILEVERSION VERSION_AS_NUMBER 77 | PRODUCTVERSION VERSION_AS_NUMBER 78 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 79 | #ifdef _DEBUG 80 | FILEFLAGS VS_FF_DEBUG 81 | #else 82 | FILEFLAGS 0x0L 83 | #endif 84 | FILEOS VOS__WINDOWS32 85 | FILETYPE VFT_APP 86 | FILESUBTYPE 0x0L 87 | BEGIN 88 | BLOCK "StringFileInfo" 89 | BEGIN 90 | BLOCK "040904e4" 91 | BEGIN 92 | VALUE "CompanyName", "com.example" "\0" 93 | VALUE "FileDescription", "keyviz" "\0" 94 | VALUE "FileVersion", VERSION_AS_STRING "\0" 95 | VALUE "InternalName", "keyviz" "\0" 96 | VALUE "LegalCopyright", "Copyright (C) 2023 com.example. All rights reserved." "\0" 97 | VALUE "OriginalFilename", "keyviz.exe" "\0" 98 | VALUE "ProductName", "keyviz" "\0" 99 | VALUE "ProductVersion", VERSION_AS_STRING "\0" 100 | END 101 | END 102 | BLOCK "VarFileInfo" 103 | BEGIN 104 | VALUE "Translation", 0x409, 1252 105 | END 106 | END 107 | 108 | #endif // English (United States) resources 109 | ///////////////////////////////////////////////////////////////////////////// 110 | 111 | 112 | 113 | #ifndef APSTUDIO_INVOKED 114 | ///////////////////////////////////////////////////////////////////////////// 115 | // 116 | // Generated from the TEXTINCLUDE 3 resource. 117 | // 118 | 119 | 120 | ///////////////////////////////////////////////////////////////////////////// 121 | #endif // not APSTUDIO_INVOKED 122 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.cpp: -------------------------------------------------------------------------------- 1 | #include "flutter_window.h" 2 | 3 | #include 4 | 5 | #include "flutter/generated_plugin_registrant.h" 6 | 7 | FlutterWindow::FlutterWindow(const flutter::DartProject& project) 8 | : project_(project) {} 9 | 10 | FlutterWindow::~FlutterWindow() {} 11 | 12 | bool FlutterWindow::OnCreate() { 13 | if (!Win32Window::OnCreate()) { 14 | return false; 15 | } 16 | 17 | RECT frame = GetClientArea(); 18 | 19 | // The size here must match the window dimensions to avoid unnecessary surface 20 | // creation / destruction in the startup path. 21 | flutter_controller_ = std::make_unique( 22 | frame.right - frame.left, frame.bottom - frame.top, project_); 23 | // Ensure that basic setup of the controller was successful. 24 | if (!flutter_controller_->engine() || !flutter_controller_->view()) { 25 | return false; 26 | } 27 | RegisterPlugins(flutter_controller_->engine()); 28 | SetChildContent(flutter_controller_->view()->GetNativeWindow()); 29 | 30 | flutter_controller_->engine()->SetNextFrameCallback([&]() { 31 | this->Show(); 32 | }); 33 | 34 | // Flutter can complete the first frame before the "show window" callback is 35 | // registered. The following call ensures a frame is pending to ensure the 36 | // window is shown. It is a no-op if the first frame hasn't completed yet. 37 | flutter_controller_->ForceRedraw(); 38 | 39 | return true; 40 | } 41 | 42 | void FlutterWindow::OnDestroy() { 43 | if (flutter_controller_) { 44 | flutter_controller_ = nullptr; 45 | } 46 | 47 | Win32Window::OnDestroy(); 48 | } 49 | 50 | LRESULT 51 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message, 52 | WPARAM const wparam, 53 | LPARAM const lparam) noexcept { 54 | // Give Flutter, including plugins, an opportunity to handle window messages. 55 | if (flutter_controller_) { 56 | std::optional result = 57 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, 58 | lparam); 59 | if (result) { 60 | return *result; 61 | } 62 | } 63 | 64 | switch (message) { 65 | case WM_FONTCHANGE: 66 | flutter_controller_->engine()->ReloadSystemFonts(); 67 | break; 68 | } 69 | 70 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam); 71 | } 72 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_FLUTTER_WINDOW_H_ 2 | #define RUNNER_FLUTTER_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "win32_window.h" 10 | 11 | // A window that does nothing but host a Flutter view. 12 | class FlutterWindow : public Win32Window { 13 | public: 14 | // Creates a new FlutterWindow hosting a Flutter view running |project|. 15 | explicit FlutterWindow(const flutter::DartProject& project); 16 | virtual ~FlutterWindow(); 17 | 18 | protected: 19 | // Win32Window: 20 | bool OnCreate() override; 21 | void OnDestroy() override; 22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 23 | LPARAM const lparam) noexcept override; 24 | 25 | private: 26 | // The project to run. 27 | flutter::DartProject project_; 28 | 29 | // The Flutter instance hosted by this window. 30 | std::unique_ptr flutter_controller_; 31 | }; 32 | 33 | #endif // RUNNER_FLUTTER_WINDOW_H_ 34 | -------------------------------------------------------------------------------- /windows/runner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "flutter_window.h" 7 | #include "utils.h" 8 | 9 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, 10 | _In_ wchar_t *command_line, _In_ int show_command) { 11 | // hid_listener instance 12 | HidListener listener; 13 | 14 | // Attach to console when present (e.g., 'flutter run') or create a 15 | // new console when running with a debugger. 16 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { 17 | CreateAndAttachConsole(); 18 | } 19 | 20 | // Initialize COM, so that it is available for use in the library and/or 21 | // plugins. 22 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 23 | 24 | flutter::DartProject project(L"data"); 25 | 26 | std::vector command_line_arguments = 27 | GetCommandLineArguments(); 28 | 29 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); 30 | 31 | FlutterWindow window(project); 32 | Win32Window::Point origin(10, 10); 33 | Win32Window::Size size(1280, 720); 34 | if (!window.Create(L"keyviz", origin, size)) { 35 | return EXIT_FAILURE; 36 | } 37 | window.SetQuitOnClose(true); 38 | 39 | ::MSG msg; 40 | while (::GetMessage(&msg, nullptr, 0, 0)) { 41 | ::TranslateMessage(&msg); 42 | ::DispatchMessage(&msg); 43 | } 44 | 45 | ::CoUninitialize(); 46 | return EXIT_SUCCESS; 47 | } 48 | -------------------------------------------------------------------------------- /windows/runner/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Runner.rc 4 | // 5 | #define IDI_APP_ICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mulaRahul/keyviz/0452c3c060d2b6fe5706cc85f43c44992523ab4e/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /windows/runner/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | void CreateAndAttachConsole() { 11 | if (::AllocConsole()) { 12 | FILE *unused; 13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) { 14 | _dup2(_fileno(stdout), 1); 15 | } 16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) { 17 | _dup2(_fileno(stdout), 2); 18 | } 19 | std::ios::sync_with_stdio(); 20 | FlutterDesktopResyncOutputStreams(); 21 | } 22 | } 23 | 24 | std::vector GetCommandLineArguments() { 25 | // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. 26 | int argc; 27 | wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); 28 | if (argv == nullptr) { 29 | return std::vector(); 30 | } 31 | 32 | std::vector command_line_arguments; 33 | 34 | // Skip the first argument as it's the binary name. 35 | for (int i = 1; i < argc; i++) { 36 | command_line_arguments.push_back(Utf8FromUtf16(argv[i])); 37 | } 38 | 39 | ::LocalFree(argv); 40 | 41 | return command_line_arguments; 42 | } 43 | 44 | std::string Utf8FromUtf16(const wchar_t* utf16_string) { 45 | if (utf16_string == nullptr) { 46 | return std::string(); 47 | } 48 | int target_length = ::WideCharToMultiByte( 49 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 50 | -1, nullptr, 0, nullptr, nullptr) 51 | -1; // remove the trailing null character 52 | int input_length = (int)wcslen(utf16_string); 53 | std::string utf8_string; 54 | if (target_length <= 0 || target_length > utf8_string.max_size()) { 55 | return utf8_string; 56 | } 57 | utf8_string.resize(target_length); 58 | int converted_length = ::WideCharToMultiByte( 59 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 60 | input_length, utf8_string.data(), target_length, nullptr, nullptr); 61 | if (converted_length == 0) { 62 | return std::string(); 63 | } 64 | return utf8_string; 65 | } 66 | -------------------------------------------------------------------------------- /windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | #include 5 | #include 6 | 7 | // Creates a console for the process, and redirects stdout and stderr to 8 | // it for both the runner and the Flutter library. 9 | void CreateAndAttachConsole(); 10 | 11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string 12 | // encoded in UTF-8. Returns an empty std::string on failure. 13 | std::string Utf8FromUtf16(const wchar_t* utf16_string); 14 | 15 | // Gets the command line arguments passed in as a std::vector, 16 | // encoded in UTF-8. Returns an empty std::vector on failure. 17 | std::vector GetCommandLineArguments(); 18 | 19 | #endif // RUNNER_UTILS_H_ 20 | -------------------------------------------------------------------------------- /windows/runner/win32_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_WIN32_WINDOW_H_ 2 | #define RUNNER_WIN32_WINDOW_H_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // A class abstraction for a high DPI-aware Win32 Window. Intended to be 11 | // inherited from by classes that wish to specialize with custom 12 | // rendering and input handling 13 | class Win32Window { 14 | public: 15 | struct Point { 16 | unsigned int x; 17 | unsigned int y; 18 | Point(unsigned int x, unsigned int y) : x(x), y(y) {} 19 | }; 20 | 21 | struct Size { 22 | unsigned int width; 23 | unsigned int height; 24 | Size(unsigned int width, unsigned int height) 25 | : width(width), height(height) {} 26 | }; 27 | 28 | Win32Window(); 29 | virtual ~Win32Window(); 30 | 31 | // Creates a win32 window with |title| that is positioned and sized using 32 | // |origin| and |size|. New windows are created on the default monitor. Window 33 | // sizes are specified to the OS in physical pixels, hence to ensure a 34 | // consistent size this function will scale the inputted width and height as 35 | // as appropriate for the default monitor. The window is invisible until 36 | // |Show| is called. Returns true if the window was created successfully. 37 | bool Create(const std::wstring& title, const Point& origin, const Size& size); 38 | 39 | // Show the current window. Returns true if the window was successfully shown. 40 | bool Show(); 41 | 42 | // Release OS resources associated with window. 43 | void Destroy(); 44 | 45 | // Inserts |content| into the window tree. 46 | void SetChildContent(HWND content); 47 | 48 | // Returns the backing Window handle to enable clients to set icon and other 49 | // window properties. Returns nullptr if the window has been destroyed. 50 | HWND GetHandle(); 51 | 52 | // If true, closing this window will quit the application. 53 | void SetQuitOnClose(bool quit_on_close); 54 | 55 | // Return a RECT representing the bounds of the current client area. 56 | RECT GetClientArea(); 57 | 58 | protected: 59 | // Processes and route salient window messages for mouse handling, 60 | // size change and DPI. Delegates handling of these to member overloads that 61 | // inheriting classes can handle. 62 | virtual LRESULT MessageHandler(HWND window, 63 | UINT const message, 64 | WPARAM const wparam, 65 | LPARAM const lparam) noexcept; 66 | 67 | // Called when CreateAndShow is called, allowing subclass window-related 68 | // setup. Subclasses should return false if setup fails. 69 | virtual bool OnCreate(); 70 | 71 | // Called when Destroy is called. 72 | virtual void OnDestroy(); 73 | 74 | private: 75 | friend class WindowClassRegistrar; 76 | 77 | // OS callback called by message pump. Handles the WM_NCCREATE message which 78 | // is passed when the non-client area is being created and enables automatic 79 | // non-client DPI scaling so that the non-client area automatically 80 | // responds to changes in DPI. All other messages are handled by 81 | // MessageHandler. 82 | static LRESULT CALLBACK WndProc(HWND const window, 83 | UINT const message, 84 | WPARAM const wparam, 85 | LPARAM const lparam) noexcept; 86 | 87 | // Retrieves a class instance pointer for |window| 88 | static Win32Window* GetThisFromHandle(HWND const window) noexcept; 89 | 90 | // Update the window frame's theme to match the system theme. 91 | static void UpdateTheme(HWND const window); 92 | 93 | bool quit_on_close_ = false; 94 | 95 | // window handle for top level window. 96 | HWND window_handle_ = nullptr; 97 | 98 | // window handle for hosted content. 99 | HWND child_content_ = nullptr; 100 | }; 101 | 102 | #endif // RUNNER_WIN32_WINDOW_H_ 103 | --------------------------------------------------------------------------------