├── .github └── ISSUE_TEMPLATE │ └── issue.md ├── CONTRIBUTING.md ├── LICENSE ├── LICENSE.Addendum.txt ├── README.md ├── qqX ├── qqX.builtins ├── echo │ ├── LICENSE │ ├── chunkcheck │ ├── quickemu │ ├── quickget │ └── windowskey ├── freebird │ ├── LICENSE │ ├── chunkcheck │ ├── quickemu │ └── quickget ├── freespirit │ ├── LICENSE │ ├── chunkcheck │ ├── quickemu │ └── quickget └── homebird │ ├── LICENSE │ ├── chunkcheck │ ├── quickemu │ └── quickget ├── qqX.custom ├── qqX.custom.readme.txt └── xx_custom_filemanage_qcow_drive ├── qqX.lib ├── floatversion └── fv ├── qqX.main ├── qqX_Initialize ├── qqX_MSR_functions ├── qqX_UI_chrome_up ├── qqX_VM_array_and_Select ├── qqX_VM_array_routines ├── qqX_VM_selector_styles ├── qqX_configuration_items ├── qqX_configuration_tune_up ├── qqX_disk_maintenance ├── qqX_filemanage_qcow_drive ├── qqX_input_and_mouse_tidyup ├── qqX_main_menu_actions ├── qqX_main_menu_interface ├── qqX_main_menu_routines ├── qqX_quick_reroute_emu ├── qqX_quick_reroute_get ├── qqX_read_main_settings ├── qqX_selfcheck_and_update └── qqX_wrap_quickemu ├── qqX.system ├── Func_echo_colors ├── icons │ ├── qqX.ascii.logo.8bit.25.txt │ ├── qqX.ascii.logo.8bit.30.txt │ ├── qqX.ascii.logo.8bit.35.txt │ ├── qqX.blu.logo.128.png │ ├── qqX.blu.logo.256.png │ ├── qqX.blu.logo.500.png │ ├── qqX.blu.logo.96.png │ ├── qqX.std.logo.128.png │ ├── qqX.std.logo.256.png │ ├── qqX.std.logo.500.png │ └── qqX.std.logo.96.png ├── qqX_add_in_boot_level_chrome ├── qqX_copy_over ├── qqX_settings_update_transposer ├── qqX_system_install └── qqX_termfind ├── qqX_settings ├── qqX_setup_and_install └── qqX_wiki_Install.pdf /.github/ISSUE_TEMPLATE/issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Issue 3 | about: 'Bug report - qqX specific ' 4 | title: 'Bug report - qqX specific ' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### General Description 11 | 12 | Include: Host OS and version, qqX, Quickemu & Qemu versions ... 13 | 14 | ### Screenshots & Error messages 15 | 16 | ### Description of tests conducted and results 17 | 18 | Standard elimination and substitution methods should be used prior to posting: 19 | 20 | - Does it happen to all VM's & does it happen when you try directly with quickemu itself? 21 | 22 | Have you tried different Virtual Machines? 23 | 24 | - If it only happens to one VM then re-install the machine or try creating another version. 25 | 26 | Did the problems start when somthing was updated? 27 | 28 | - Both older and newer versions of Quickemu are available as qqX builtins 29 | 30 | - Make sure that you have checked the [qqX branches](https://github.com/TuxVinyards/qqX/branches). Fixes may already have been committed. 31 | 32 | When checking the issues list make sure the filter is not just `is:issue is:open` 33 | 34 | Also try the closed tab `is:issue is:closed` 35 | 36 | Problems with quickemu or quickget should be submitted at 37 | 38 | Qemu problems at 39 | 40 | For advice and discussion try the quickemu forum at 41 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Ideas and Creativity are most important 2 | 3 | - Try to follow the Coding Style and Guidance outlined at the end of the qqX main script 4 | 5 | - But don't worry if you only know POSIX Shell and Coding Style is getting in the way 6 | 7 | Things can always get sorted out in a separate branch if need be, later. 8 | 9 | - Quickget built-ins do need frequent updating. New additions are always welcome. 10 | 11 | - Check the [freespirit-next](https://github.com/TuxVinyards/quickemu/branches) and [dev-next](https://github.com/TuxVinyards/qqX/branches) branches 12 | 13 | ## Do explain what's going and why 14 | 15 | - If no one can follow or understand what you have done, it won't get added 16 | 17 | ## Try to split your work up 18 | 19 | - Larger contributions _should_ always be written in several commits 20 | 21 | ## Test your work 22 | 23 | - Ideally, it should have been linted with ShellCheck 24 | 25 | - Complex code should ideally be tested with different distro's and different desktops and managers 26 | 27 | ## Screenshots can help 28 | 29 | - Sometimes a picture can tell a thousand words ... 30 | -------------------------------------------------------------------------------- /LICENSE.Addendum.txt: -------------------------------------------------------------------------------- 1 | 2 | ## Copyright (c) Alex Genovese https://github.com/TuxVinyards 3 | 4 | 5 | # SMALL CODE SNIPPETS eg the function printColor MAY BE USED 6 | # PERMISSIVELY in projects as MIT or similar, providing CLEAR ATTRIBUTIONS are shown. 7 | 8 | # Otherwise: 9 | 10 | 11 | # Licence GPL3 https://www.gnu.org/licenses 12 | 13 | # This program is free software: you can redistribute it and/or modify 14 | # it under the terms of the GNU General Public License as published by 15 | # the Free Software Foundation, either version 3 of the License, or 16 | # (at your option) any later version. 17 | 18 | # This program is distributed in the hope that it will be useful, 19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | # GNU General Public License for more details. 22 | 23 | # https://www.gnu.org/licenses 24 | 25 | 26 | # IF CODE IN GENERAL BECOMES USED IN ANY OTHER PROJECT, 27 | # THE GPL3 LICENCE APPLIES & YOU SHOULD SHOW CLEAR ATTRIBUTIONS. 28 | 29 | 30 | 31 | qqX - quickemu quickget X terminal project 32 | 33 | 34 | A independent text based menu interface designed to work with QEMU & with the 'quickemu' project 35 | 36 | https://github.com/quickemu-project/quickemu https://gitlab.com/qemu-project/qemu 37 | 38 | Users should install 'qemu' & 'quickemu' separately, as normal. 39 | 40 | # 41 | # 42 | 43 | Some scripts use code from ffX: 44 | 45 | ffX - the ff(mpeg) X-terminal project - AV processing scripts for ffmpeg 46 | 47 | https://github.com/theffxproject 48 | 49 | 50 | Other parts build on the work with quickemu-wrap: 51 | 52 | quickemu-mod / quickemu-wrap > 53 | 54 | https://github.com/TuxVinyards/quickemu-mod 55 | 56 | 57 | All scripts from these projects are copyright (c) Alex Genovese 58 | 59 | license: GPL3 https://www.gnu.org/licenses 60 | 61 | Any references in qqX to the ffX project imply GPL3 and its transfer between the projects 62 | 63 | 64 | # 65 | # 66 | 67 | 68 | For any sections of code used directly from the ORIGINAL quickemu project: 69 | 70 | MIT License 71 | 72 | Copyright (c) 2020 Wimpy's World 73 | 74 | Permission is hereby granted, free of charge, to any person obtaining a copy 75 | of this software and associated documentation files (the "Software"), to deal 76 | in the Software without restriction, including without limitation the rights 77 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 78 | copies of the Software, and to permit persons to whom the Software is 79 | furnished to do so, subject to the following conditions: 80 | 81 | The above copyright notice and this permission notice shall be included in all 82 | copies or substantial portions of the Software. 83 | 84 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 85 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 86 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 87 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 88 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 89 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 90 | SOFTWARE. 91 | 92 | 93 | See https://github.com/quickemu-project/quickemu 94 | 95 | 96 | # 97 | 98 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # qqX - quickemu quickget X terminal project 2 | 3 | - Create an easy to build collection of distros and desktops for testing, using and evaluating 4 | 5 | - _Safe, carefully written, well tested and checked_ 6 | 7 | ![qqX-vmm](https://github.com/TuxVinyards/qqX/assets/3956806/18e5c495-8072-49a5-8b9c-e1302549efcf) 8 | 9 | - Allows the easy running of Linux, Windows, MacOS, etc with [Qemu / KVM](https://qemu.readthedocs.io) 10 | 11 | ## Basically Better 12 | 13 | - Adds [fixes, tools and general improvements](https://github.com/TuxVinyards/qqX/wiki/FreeBird) to the Quickemu system 14 | 15 | - More up-to-date and easier to mod than Gnome Boxes or VirtualBox 16 | 17 | - Much easier than the complex Qemu front-ends based on LibVirt. 18 | 19 | ### Launch from the Linux desktop or from the command line 20 | 21 | - Easy to use menu system with unique hybrid interface 22 | 23 | ## [Latest release 1.12.07](https://github.com/TuxVinyards/qqX/releases/latest) 24 | 25 | - Adds several downstream fixes: Solus, Parrot, Sparky 26 | 27 | - Adds Nobara and a few UI improvements 28 | 29 | Includes earlier downstream fixes for Quickemu issues: 30 | 31 | - Qemu version 10.0.0 32 | 33 | - Windows Installer 34 | 35 | More in the [release notes](#release-notes-and-highlights) and wiki pages 36 | 37 | ## Ease of the GUI 38 | 39 | ![qqX-mouse-click-iso](https://github.com/TuxVinyards/qqX/assets/3956806/ae6c9fe1-0c46-4e05-b75a-b6964f35bad2) 40 | 41 | - Download, right click and install 42 | 43 | ## Power and simplicity of the command line 44 | 45 | Search the quickget distro list with _alphabetical zooming_ 46 | 47 | - Simple list browse, download, and organise countless distros 48 | 49 | - Includes distro homepage browser, link checker and download size reportage 50 | 51 | ![alpha-zoom-qget](https://github.com/TuxVinyards/qqX/assets/3956806/bfde0aef-9094-443d-a11d-5bd6745e5702) 52 | 53 | - Or at the terminal, simply `qqX /path/distro.iso` 54 | 55 | ## Straightforward and easy to work with 56 | 57 | - Clear to read display modes, utilities, optimizers and tools 58 | 59 | - Easy to fix, mod and customise 60 | 61 | - Choice of Custom Menus or 5 built-in _menu styles_ 62 | 63 | ![new -vm-selector-qqX](https://github.com/TuxVinyards/qqX/assets/3956806/42a4b480-4d7d-47fe-91f5-0069fa1511a8) 64 | 65 | - Reliable text [translation](https://github.com/TuxVinyards/qqX/wiki/Translation) methods. 66 | 67 | ## The latest updates 68 | 69 | [No waiting months](https://github.com/quickemu-project/quickemu/releases) for Quickemu bug fixes and changes 70 | 71 | - qqX's built-in code [system](https://github.com/TuxVinyards/qqX/wiki/FreeBird) ensures that any new input can be quickly put to work 72 | 73 | - Choice of 3 easy-swap _rolling release_ models: Leading Edge, Standard or Slow 74 | 75 | ## Easy utilities 76 | 77 | - Safeguard your machines with easy snapshot management 78 | 79 | - Make sure drives are healthy with Qcow2 repair, resize and diagnostics 80 | 81 | - Edit machine partitions by booting from maintenance ISO's 82 | 83 | - View inside virtual machines using a file manager 84 | 85 | ![qcow2-disk-utils-qf-1920](https://github.com/user-attachments/assets/3262f7be-ddcb-4b98-b44a-6e75ca82eab0) 86 | 87 | - Configuration tune-up wizard 88 | 89 | ![windows-insider-sdl-1920](https://github.com/user-attachments/assets/c586e2e2-d285-49a2-93c2-2ce506ba2f36) 90 | 91 | - And more 92 | 93 | ## How to Install 94 | 95 | For standard Linux, most users will only need to install `qemu` `spice-gtk` and `qqX` 96 | 97 | - Some distro versions may need `curl` `7z` `jq` `bc` and possibly `xrandr` 98 | 99 | - The qqX latest release is available at [https://github.com/TuxVinyards/qqX/releases/latest](https://github.com/TuxVinyards/qqX/releases/latest) 100 | 101 | That's it. 102 | 103 | - Built-in updater included ..... 104 | 105 | Start from scratch. Or safely test out the qqX difference on your existing VM's. 106 | 107 | - Fully compatible with Quickemu machines 108 | 109 | - Quickemu and Quickgui may be installed but are _not essential_. 110 | 111 | - [Full installer details](https://github.com/TuxVinyards/qqX/wiki) in the wiki pages 112 | 113 | ## Reliability 114 | 115 | - All scripts are carefully Shellcheck linted & have full error handling routines. 116 | 117 | - qqX always makes backups, as is _standard good practice_ with _any_ software. 118 | 119 | ## FAQs and Help 120 | 121 | - Lots of _Quickemu Help_ available in the [qqX wiki pages](https://github.com/TuxVinyards/qqX/wiki/FAQs-and-Help) too, not just qqX 122 | 123 | - Full Quickemu [MacOS installation guide](https://github.com/TuxVinyards/qqX/wiki/Mac-OS) and [Windows Installer walk-tru](https://github.com/TuxVinyards/qqX/wiki/Windows-OS) 124 | 125 | - Qemu / qqX guide to disk resizing and [disk maintenance](https://github.com/TuxVinyards/qqX/wiki/Disks-and-Resizing) 126 | 127 | - And more ... 128 | 129 | Also try the Quickemu forum at 130 | 131 | ## Release notes and highlights 132 | 133 | ### Release 1.11 134 | 135 | @ Nov 27th - 1.11.0.2 136 | 137 | - New feature [_mounting of qcow2 drives in a file manager_](https://github.com/TuxVinyards/qqX/wiki/Disks-and-Resizing#other-tools) 138 | 139 | ### Release 1.12 140 | 141 | @ Feb 23 - 1.12.0 142 | 143 | - Distro storage size analysis and size disparity checks 144 | 145 | ![new size-ui](https://github.com/user-attachments/assets/0df37ca8-b4c1-4af8-a883-82ef60744649) 146 | 147 | - And extended **stop, start and resume** for downloads 148 | 149 | ![stop-start-resume](https://github.com/user-attachments/assets/d710f47f-a2b9-4bda-beda-5f5290dfad52) 150 | 151 | @ March 14 - 1.12.01 152 | 153 | - [Downstream fix for the Quickemu Windows Installer issue #1475](https://github.com/quickemu-project/quickemu/issues/1475) 154 | 155 | - [Windows Installer walk-tru](https://github.com/TuxVinyards/qqX/wiki/Windows-OS) in the qqX wiki 156 | 157 | @ March 28 - 1.12.02 158 | 159 | - Distro finder for searching large distro collections 160 | 161 | @ April 17 - 1.12.04 162 | 163 | - Upgrades [floatversion](https://github.com/TuxVinyards/floatversion) to 1.4.01 164 | 165 | And updates Quickget with _downstream_ distro fixes for: 166 | 167 | - AntiX 168 | - Freedos 169 | - Garuda 170 | - Kolibri 171 | - Oracle 172 | - Parrot Sec 173 | - PC Linux 174 | 175 | @ Apr 25 - 1.12.05 176 | 177 | - Adds upstream fix for Bazzite 178 | 179 | Plus downsteam fixes for : 180 | 181 | - Fedora 42 KDE 182 | - VanillaOS 183 | - VX 184 | 185 | @ May 2 - 1.12.06 186 | 187 | - Adds fixes for QuickEmu and new Qemu version 10.0.0 188 | - Improves UI for dependency checker 189 | 190 | @ May 2 - 1.12.07 191 | 192 | - Adds several downstream fixes: Solus, Parrot, Sparky 193 | - Adds Nobara and a few UI improvents 194 | 195 | Full details in [releases](https://github.com/TuxVinyards/qqX/releases) and [commits](https://github.com/TuxVinyards/qqX/commits/main/) 196 | 197 | ## Relationship with Quickemu 198 | 199 | Lack of upstream maintenance and creative differences managed to form [several growing rifts](https://github.com/orgs/quickemu-project/discussions/925) within the Quickemu project during 2024 and in June a growing count of developers decided to walk away. 200 | 201 | - An increasing number of fixes for Quickemu are now having to be added downstream 202 | 203 | - See [FreeBird](https://github.com/TuxVinyards/qqX/wiki/FreeBird) wiki page for details on the built-ins 204 | 205 | There needed to be an improvement in relationships, so that more people could benefit from the work being done here. 206 | 207 | - Reinstating the [qqX listing](https://github.com/TuxVinyards/qqX/wiki/FreeBird#creative-differences) on the Quickemu wiki pages would have been a good move. 208 | 209 | ![qqX-on-the-quickemu-wiki](https://github.com/user-attachments/assets/3c11249e-4898-4882-a82e-c460472e8099) 210 | 211 | ### Competition 212 | 213 | qqX was always intended as an extra optional interface for Quickemu. It was never intended to be a competitive project. 214 | 215 | However during 2025, the upstream stance hardened further and any [attempts to provide simple help or to address issues within the upstream code base](https://github.com/quickemu-project/quickemu/issues/1660) continued to be misconstrued as competitive attacks. Instead of rifts healing, recent pushes for improvements in relationships have failed abysmally. 216 | 217 | Please _be reassured_ that qqX _will continue to support virtual machines_ that have been created via either command line QuickEmu or QuickGUI or vice versa, despite being forced into becoming a competitor. 218 | 219 | ## Downstream fixes for Quickemu 220 | 221 | - Available via qqX's [freebird](https://github.com/TuxVinyards/qqX/wiki/FreeBird) or in the qqX code itself 222 | 223 | - Fixes problems causing Intel processors to run MacOS very slowly 224 | 225 | - Fixes the problem where telescoped/nested VM grind to a halt 226 | 227 | - Windows language fixes mean that [all](https://github.com/quickemu-project/quickemu/issues/1113) the downloads work, not just English 228 | 229 | - Fixes the Quickemu Windows Installer issue [#1475](https://github.com/quickemu-project/quickemu/issues/1475) 230 | 231 | - The use of [floatversion](https://github.com/TuxVinyards/floatversion) allows multiple distro downloads not available in Quickemu 232 | 233 | - And more 234 | 235 | ## Why Bash? 236 | 237 | ### Keeping it Simple 238 | 239 | The qqX coding is clear and annotated sufficiently for newcomers to find their feet 240 | 241 | - qqX uses a more modern interpretation of Bash than that used in Quickemu 242 | 243 | - but anything in quickemu can still be easily modded, shaped and improved 244 | 245 | QEMU / KVM does all the heavy lifting 246 | 247 | - Much easier for community contributions 248 | 249 | - Much easier to edit and easy to fix 250 | 251 | ### ... and Straightforward 252 | 253 | qqX follows quickemu's pattern of using human orientated configuration files 254 | 255 | - none of the complexities of only having machine readable configurations, as with LibVirt 256 | 257 | Requires no special setup as do languages such as Rust, C or Vala 258 | 259 | - no potentially steep [learning](https://gitlab.gnome.org/GNOME/gnome-boxes/-/tree/main/src?ref_type=heads) curves 260 | 261 | Bash is automatically there on almost every Linux system, directly on the command line. 262 | 263 | - easily integrates with the Linux core utilities and sub-system 264 | 265 | - is more performant than basic Posix Shell 266 | 267 | Improving your knowledge of Bash is always time well spent 268 | 269 | - Most users already know or can understand at least a little of it. 270 | 271 | ## Development and Roadmap 272 | 273 | See the specific [wiki](https://github.com/TuxVinyards/qqX/wiki/Development) for _more details_ and for _what's happening_ in future releases 274 | 275 | ## Contributions 276 | 277 | If you are more comfortable with older style Bash, this is permitted in the Quickemu scripts 278 | 279 | - [Downstream fixes for Quickget](https://github.com/TuxVinyards/qqX/wiki/FreeBird#exclusive-quickget-fixes) are always needed 280 | 281 | - Quickget could still do with further [floatversion](https://github.com/TuxVinyards/floatversion) tweaks ... 282 | 283 | Anyone wanting to pick up on those should add them to the latest [freespirit](https://github.com/TuxVinyards/quickemu/branches) or [dev-next](https://github.com/TuxVinyards/qqX/branches) branch 284 | 285 | Also if anyone has used the custom menu feature and wants to share their work, that would be great as well 286 | 287 | ## Why the Vinyards? 288 | 289 | Linux development, with the preferable Olde English spelling, amongst the vineyards 290 | 291 | Alex Genovese lives in a wine growing region of Italy, in a castled village dating from the 1300's 292 | 293 | "qqX is something to work on when it's too hot or it's raining and I can't be outside" 294 | 295 | 1533 (1651 pub.), Henry Cornelius Agrippa, De Occulta Philosophia: 296 | > therefore they who are more religiously and holily instructed, 297 | neither set a tree nor plant their _**vinyard**_, nor undertake any mean work without divine invocation 298 | 299 | ## why 'X' ? 300 | 301 | Traditionally Linux has used the X window system from X.org and classically the 'Xterm' command line interface. 302 | 303 | - [https://en.wikipedia.org/wiki/X_Window_System](https://en.wikipedia.org/wiki/X_Window_System) 304 | 305 | - [https://en.wikipedia.org/wiki/Wayland_(protocol)](https://en.wikipedia.org/wiki/Wayland_\(protocol\)) 306 | 307 | Technically speaking, qqX runs in a 'terminal emulator' and can also run with Wayland display systems as well as with 'X' 308 | -------------------------------------------------------------------------------- /qqX.builtins/echo/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Wimpy's World 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /qqX.builtins/echo/chunkcheck: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pathlib import Path 4 | import struct 5 | import hashlib 6 | import argparse 7 | v1_prod_pubkey = 0xC3E748CAD9CD384329E10E25A91E43E1A762FF529ADE578C935BDDF9B13F2179D4855E6FC89E9E29CA12517D17DFA1EDCE0BEBF0EA7B461FFE61D94E2BDF72C196F89ACD3536B644064014DAE25A15DB6BB0852ECBD120916318D1CCDEA3C84C92ED743FC176D0BACA920D3FCF3158AFF731F88CE0623182A8ED67E650515F75745909F07D415F55FC15A35654D118C55A462D37A3ACDA08612F3F3F6571761EFCCBCC299AEE99B3A4FD6212CCFFF5EF37A2C334E871191F7E1C31960E010A54E86FA3F62E6D6905E1CD57732410A3EB0C6B4DEFDABE9F59BF1618758C751CD56CEF851D1C0EAA1C558E37AC108DA9089863D20E2E7E4BF475EC66FE6B3EFDCF 8 | # v2_prod_pubkey = 0xCB45C5E53217D4499FB80B2D96AA4F964EB551F1DA4EBFA4F5E23F87BFE82FC113590E536757F329D6EAD1F267771EE342F5A5E61514DD3D3383187E663929D577D94648F262EBA1157E152DB5273D10AE3A6A058CB9CD64D01267DAC82ED3B7BC1631D078C911414129CDAAA0FFB0A8E2A7ADD6F32FB09A7E98D259BFF6ED10808D1BDA58CAF7355DFF1A085A18B11657D2617447BF657140D599364E5AC8E626276AC03BC2417831D9E61B25154AFE9F2D8271E9CE22D2783803083A5A7A575774688721097DC5E4B32D118CF6317A7083BA15BA608430A8C8C6B7DA2D932D81F571603A9363AC0197AB670242D9C9180D97A10900F11FE3D9246CF14F0883 9 | # v2_dev_pubkey = 0xB372CEC9E05E71FB3FAA08C34E3256FB312EA821638A243EF8A5DEA46FCDA33F00F88FC2933FB276D37B914F89BAD5B5D75771E342265B771995AE8F43B4DFF3F21A877FE777A8B419587C8718D36204FA1922A575AD5207D5D6B8C10F84DDCA661B731E7E7601D64D4A894F487FE1AA1DDC2A1697A3553B1DD85D5750DF2AA9D988E83C4C70BBBE4747219F9B92B199FECB16091896EBB441606DEC20F446249D5568BB51FC87BA7F85E6295FBE811B0A314408CD31921C360608A0FF7F87BD733560FE1C96E472834CAB6BE016C35727754273125089BE043FD3B26F0B2DE141E05990CE922F1702DA0A2F4E9F8760D0FA712DDB9928E0CDAC14501ED5E2C3 10 | 11 | ChunkListHeader = struct.Struct('<4sIBBBxQQQ') 12 | assert ChunkListHeader.size == 0x24 13 | 14 | Chunk = struct.Struct(' 0 29 | assert chunk_offset == 0x24 30 | assert signature_offset == chunk_offset + Chunk.size * chunk_count 31 | for i in range(chunk_count): 32 | data = f.read(Chunk.size) 33 | hash_ctx.update(data) 34 | chunk_size, chunk_sha256 = Chunk.unpack(data) 35 | yield chunk_size, chunk_sha256 36 | digest = hash_ctx.digest() 37 | if signature_method == 1: 38 | data = f.read(256) 39 | assert len(data) == 256 40 | signature = int.from_bytes(data, 'little') 41 | plaintext = 0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff003031300d0609608648016503040201050004200000000000000000000000000000000000000000000000000000000000000000 | int.from_bytes(digest, 'big') 42 | assert pow(signature, 0x10001, v1_prod_pubkey) == plaintext 43 | elif signature_method == 2: 44 | data = f.read(32) 45 | assert data == digest 46 | else: 47 | raise NotImplementedError 48 | assert f.read(1) == b'' 49 | 50 | def check_chunklist(path, chunklist_path): 51 | with open(path, 'rb') as f: 52 | for chunk_size, chunk_sha256 in parse_chunklist(chunklist_path): 53 | chunk = f.read(chunk_size) 54 | assert len(chunk) == chunk_size 55 | assert hashlib.sha256(chunk).digest() == chunk_sha256 56 | assert f.read(1) == b'' 57 | 58 | def main(): 59 | parser = argparse.ArgumentParser() 60 | parser.add_argument('vmdir', type=Path) 61 | args = parser.parse_args() 62 | vmdir = args.vmdir 63 | check_chunklist(vmdir / 'RecoveryImage.dmg', vmdir / 'RecoveryImage.chunklist') 64 | 65 | if __name__ == "__main__": 66 | main() 67 | -------------------------------------------------------------------------------- /qqX.builtins/echo/windowskey: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | 4 | import html.parser 5 | import os 6 | import sys 7 | import urllib.request 8 | 9 | 10 | """ 11 | Download Windows product keys from MicroSoft 12 | """ 13 | 14 | key_page_url = "https://docs.microsoft.com/en-us/windows-server/get-started/kms-client-activation-keys" 15 | 16 | 17 | def usage(): 18 | script = os.path.basename(sys.argv[0]) 19 | message = f"""Usage: {script} [windows-version] 20 | 21 | To specify the version of Windows you'd like, pass a string that matches the 22 | name of the operating system you'd like to download. Case doesn't matter, so 23 | you can use "windows 10" or "Windows 10". 24 | 25 | e.g. 26 | 27 | {script} "Windows 10" 28 | {script} "enterprise" 29 | 30 | """ 31 | print(message, file=sys.stderr) 32 | sys.exit(0) 33 | 34 | 35 | def download_page(url): 36 | response = urllib.request.urlopen(url) 37 | return response.read().decode("utf-8") 38 | 39 | 40 | class WindowsKeyPageParser(html.parser.HTMLParser): 41 | def __init__(self, *, convert_charrefs=True): 42 | super().__init__(convert_charrefs=True) 43 | self.product_keys = {} 44 | self.parsing_os = False 45 | 46 | def handle_starttag(self, tag, attrs): 47 | self.parsing_os = tag == "td" 48 | 49 | def handle_endtag(self, tag): 50 | self.parsing_os = False 51 | 52 | def handle_data(self, data): 53 | if self.parsing_os: 54 | self.stash_table_cell(data) 55 | 56 | def stash_table_cell(self, data): 57 | if "Windows" in data: 58 | self.current_os = data 59 | else: 60 | product_key = data 61 | self.product_keys[self.current_os] = product_key 62 | 63 | 64 | def find_keys_for_all_versions(markup): 65 | parser = WindowsKeyPageParser() 66 | parser.feed(markup) 67 | return parser.product_keys 68 | 69 | 70 | if __name__ == "__main__": 71 | try: 72 | arg = sys.argv[1] 73 | except IndexError: 74 | windows_version = "" 75 | else: 76 | if arg in ["-h", "--help"]: 77 | usage() 78 | windows_version = arg 79 | 80 | markup = download_page(key_page_url) 81 | product_keys = find_keys_for_all_versions(markup) 82 | 83 | for os_name, product_key in product_keys.items(): 84 | if windows_version.lower() in os_name.lower(): 85 | print(f"{os_name}: {product_key}") 86 | -------------------------------------------------------------------------------- /qqX.builtins/freebird/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Wimpy's World 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /qqX.builtins/freebird/chunkcheck: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pathlib import Path 4 | import struct 5 | import hashlib 6 | import argparse 7 | v1_prod_pubkey = 0xC3E748CAD9CD384329E10E25A91E43E1A762FF529ADE578C935BDDF9B13F2179D4855E6FC89E9E29CA12517D17DFA1EDCE0BEBF0EA7B461FFE61D94E2BDF72C196F89ACD3536B644064014DAE25A15DB6BB0852ECBD120916318D1CCDEA3C84C92ED743FC176D0BACA920D3FCF3158AFF731F88CE0623182A8ED67E650515F75745909F07D415F55FC15A35654D118C55A462D37A3ACDA08612F3F3F6571761EFCCBCC299AEE99B3A4FD6212CCFFF5EF37A2C334E871191F7E1C31960E010A54E86FA3F62E6D6905E1CD57732410A3EB0C6B4DEFDABE9F59BF1618758C751CD56CEF851D1C0EAA1C558E37AC108DA9089863D20E2E7E4BF475EC66FE6B3EFDCF 8 | # v2_prod_pubkey = 0xCB45C5E53217D4499FB80B2D96AA4F964EB551F1DA4EBFA4F5E23F87BFE82FC113590E536757F329D6EAD1F267771EE342F5A5E61514DD3D3383187E663929D577D94648F262EBA1157E152DB5273D10AE3A6A058CB9CD64D01267DAC82ED3B7BC1631D078C911414129CDAAA0FFB0A8E2A7ADD6F32FB09A7E98D259BFF6ED10808D1BDA58CAF7355DFF1A085A18B11657D2617447BF657140D599364E5AC8E626276AC03BC2417831D9E61B25154AFE9F2D8271E9CE22D2783803083A5A7A575774688721097DC5E4B32D118CF6317A7083BA15BA608430A8C8C6B7DA2D932D81F571603A9363AC0197AB670242D9C9180D97A10900F11FE3D9246CF14F0883 9 | # v2_dev_pubkey = 0xB372CEC9E05E71FB3FAA08C34E3256FB312EA821638A243EF8A5DEA46FCDA33F00F88FC2933FB276D37B914F89BAD5B5D75771E342265B771995AE8F43B4DFF3F21A877FE777A8B419587C8718D36204FA1922A575AD5207D5D6B8C10F84DDCA661B731E7E7601D64D4A894F487FE1AA1DDC2A1697A3553B1DD85D5750DF2AA9D988E83C4C70BBBE4747219F9B92B199FECB16091896EBB441606DEC20F446249D5568BB51FC87BA7F85E6295FBE811B0A314408CD31921C360608A0FF7F87BD733560FE1C96E472834CAB6BE016C35727754273125089BE043FD3B26F0B2DE141E05990CE922F1702DA0A2F4E9F8760D0FA712DDB9928E0CDAC14501ED5E2C3 10 | 11 | ChunkListHeader = struct.Struct('<4sIBBBxQQQ') 12 | assert ChunkListHeader.size == 0x24 13 | 14 | Chunk = struct.Struct(' 0 29 | assert chunk_offset == 0x24 30 | assert signature_offset == chunk_offset + Chunk.size * chunk_count 31 | for i in range(chunk_count): 32 | data = f.read(Chunk.size) 33 | hash_ctx.update(data) 34 | chunk_size, chunk_sha256 = Chunk.unpack(data) 35 | yield chunk_size, chunk_sha256 36 | digest = hash_ctx.digest() 37 | if signature_method == 1: 38 | data = f.read(256) 39 | assert len(data) == 256 40 | signature = int.from_bytes(data, 'little') 41 | plaintext = 0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff003031300d0609608648016503040201050004200000000000000000000000000000000000000000000000000000000000000000 | int.from_bytes(digest, 'big') 42 | assert pow(signature, 0x10001, v1_prod_pubkey) == plaintext 43 | elif signature_method == 2: 44 | data = f.read(32) 45 | assert data == digest 46 | else: 47 | raise NotImplementedError 48 | assert f.read(1) == b'' 49 | 50 | def check_chunklist(path, chunklist_path): 51 | with open(path, 'rb') as f: 52 | for chunk_size, chunk_sha256 in parse_chunklist(chunklist_path): 53 | chunk = f.read(chunk_size) 54 | assert len(chunk) == chunk_size 55 | assert hashlib.sha256(chunk).digest() == chunk_sha256 56 | assert f.read(1) == b'' 57 | 58 | def main(): 59 | parser = argparse.ArgumentParser() 60 | parser.add_argument('vmdir', type=Path) 61 | args = parser.parse_args() 62 | vmdir = args.vmdir 63 | check_chunklist(vmdir / 'RecoveryImage.dmg', vmdir / 'RecoveryImage.chunklist') 64 | 65 | if __name__ == "__main__": 66 | main() 67 | -------------------------------------------------------------------------------- /qqX.builtins/freespirit/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Wimpy's World 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /qqX.builtins/freespirit/chunkcheck: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pathlib import Path 4 | import struct 5 | import hashlib 6 | import argparse 7 | v1_prod_pubkey = 0xC3E748CAD9CD384329E10E25A91E43E1A762FF529ADE578C935BDDF9B13F2179D4855E6FC89E9E29CA12517D17DFA1EDCE0BEBF0EA7B461FFE61D94E2BDF72C196F89ACD3536B644064014DAE25A15DB6BB0852ECBD120916318D1CCDEA3C84C92ED743FC176D0BACA920D3FCF3158AFF731F88CE0623182A8ED67E650515F75745909F07D415F55FC15A35654D118C55A462D37A3ACDA08612F3F3F6571761EFCCBCC299AEE99B3A4FD6212CCFFF5EF37A2C334E871191F7E1C31960E010A54E86FA3F62E6D6905E1CD57732410A3EB0C6B4DEFDABE9F59BF1618758C751CD56CEF851D1C0EAA1C558E37AC108DA9089863D20E2E7E4BF475EC66FE6B3EFDCF 8 | # v2_prod_pubkey = 0xCB45C5E53217D4499FB80B2D96AA4F964EB551F1DA4EBFA4F5E23F87BFE82FC113590E536757F329D6EAD1F267771EE342F5A5E61514DD3D3383187E663929D577D94648F262EBA1157E152DB5273D10AE3A6A058CB9CD64D01267DAC82ED3B7BC1631D078C911414129CDAAA0FFB0A8E2A7ADD6F32FB09A7E98D259BFF6ED10808D1BDA58CAF7355DFF1A085A18B11657D2617447BF657140D599364E5AC8E626276AC03BC2417831D9E61B25154AFE9F2D8271E9CE22D2783803083A5A7A575774688721097DC5E4B32D118CF6317A7083BA15BA608430A8C8C6B7DA2D932D81F571603A9363AC0197AB670242D9C9180D97A10900F11FE3D9246CF14F0883 9 | # v2_dev_pubkey = 0xB372CEC9E05E71FB3FAA08C34E3256FB312EA821638A243EF8A5DEA46FCDA33F00F88FC2933FB276D37B914F89BAD5B5D75771E342265B771995AE8F43B4DFF3F21A877FE777A8B419587C8718D36204FA1922A575AD5207D5D6B8C10F84DDCA661B731E7E7601D64D4A894F487FE1AA1DDC2A1697A3553B1DD85D5750DF2AA9D988E83C4C70BBBE4747219F9B92B199FECB16091896EBB441606DEC20F446249D5568BB51FC87BA7F85E6295FBE811B0A314408CD31921C360608A0FF7F87BD733560FE1C96E472834CAB6BE016C35727754273125089BE043FD3B26F0B2DE141E05990CE922F1702DA0A2F4E9F8760D0FA712DDB9928E0CDAC14501ED5E2C3 10 | 11 | ChunkListHeader = struct.Struct('<4sIBBBxQQQ') 12 | assert ChunkListHeader.size == 0x24 13 | 14 | Chunk = struct.Struct(' 0 29 | assert chunk_offset == 0x24 30 | assert signature_offset == chunk_offset + Chunk.size * chunk_count 31 | for i in range(chunk_count): 32 | data = f.read(Chunk.size) 33 | hash_ctx.update(data) 34 | chunk_size, chunk_sha256 = Chunk.unpack(data) 35 | yield chunk_size, chunk_sha256 36 | digest = hash_ctx.digest() 37 | if signature_method == 1: 38 | data = f.read(256) 39 | assert len(data) == 256 40 | signature = int.from_bytes(data, 'little') 41 | plaintext = 0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff003031300d0609608648016503040201050004200000000000000000000000000000000000000000000000000000000000000000 | int.from_bytes(digest, 'big') 42 | assert pow(signature, 0x10001, v1_prod_pubkey) == plaintext 43 | elif signature_method == 2: 44 | data = f.read(32) 45 | assert data == digest 46 | else: 47 | raise NotImplementedError 48 | assert f.read(1) == b'' 49 | 50 | def check_chunklist(path, chunklist_path): 51 | with open(path, 'rb') as f: 52 | for chunk_size, chunk_sha256 in parse_chunklist(chunklist_path): 53 | chunk = f.read(chunk_size) 54 | assert len(chunk) == chunk_size 55 | assert hashlib.sha256(chunk).digest() == chunk_sha256 56 | assert f.read(1) == b'' 57 | 58 | def main(): 59 | parser = argparse.ArgumentParser() 60 | parser.add_argument('vmdir', type=Path) 61 | args = parser.parse_args() 62 | vmdir = args.vmdir 63 | check_chunklist(vmdir / 'RecoveryImage.dmg', vmdir / 'RecoveryImage.chunklist') 64 | 65 | if __name__ == "__main__": 66 | main() 67 | -------------------------------------------------------------------------------- /qqX.builtins/homebird/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Wimpy's World 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /qqX.builtins/homebird/chunkcheck: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pathlib import Path 4 | import struct 5 | import hashlib 6 | import argparse 7 | v1_prod_pubkey = 0xC3E748CAD9CD384329E10E25A91E43E1A762FF529ADE578C935BDDF9B13F2179D4855E6FC89E9E29CA12517D17DFA1EDCE0BEBF0EA7B461FFE61D94E2BDF72C196F89ACD3536B644064014DAE25A15DB6BB0852ECBD120916318D1CCDEA3C84C92ED743FC176D0BACA920D3FCF3158AFF731F88CE0623182A8ED67E650515F75745909F07D415F55FC15A35654D118C55A462D37A3ACDA08612F3F3F6571761EFCCBCC299AEE99B3A4FD6212CCFFF5EF37A2C334E871191F7E1C31960E010A54E86FA3F62E6D6905E1CD57732410A3EB0C6B4DEFDABE9F59BF1618758C751CD56CEF851D1C0EAA1C558E37AC108DA9089863D20E2E7E4BF475EC66FE6B3EFDCF 8 | # v2_prod_pubkey = 0xCB45C5E53217D4499FB80B2D96AA4F964EB551F1DA4EBFA4F5E23F87BFE82FC113590E536757F329D6EAD1F267771EE342F5A5E61514DD3D3383187E663929D577D94648F262EBA1157E152DB5273D10AE3A6A058CB9CD64D01267DAC82ED3B7BC1631D078C911414129CDAAA0FFB0A8E2A7ADD6F32FB09A7E98D259BFF6ED10808D1BDA58CAF7355DFF1A085A18B11657D2617447BF657140D599364E5AC8E626276AC03BC2417831D9E61B25154AFE9F2D8271E9CE22D2783803083A5A7A575774688721097DC5E4B32D118CF6317A7083BA15BA608430A8C8C6B7DA2D932D81F571603A9363AC0197AB670242D9C9180D97A10900F11FE3D9246CF14F0883 9 | # v2_dev_pubkey = 0xB372CEC9E05E71FB3FAA08C34E3256FB312EA821638A243EF8A5DEA46FCDA33F00F88FC2933FB276D37B914F89BAD5B5D75771E342265B771995AE8F43B4DFF3F21A877FE777A8B419587C8718D36204FA1922A575AD5207D5D6B8C10F84DDCA661B731E7E7601D64D4A894F487FE1AA1DDC2A1697A3553B1DD85D5750DF2AA9D988E83C4C70BBBE4747219F9B92B199FECB16091896EBB441606DEC20F446249D5568BB51FC87BA7F85E6295FBE811B0A314408CD31921C360608A0FF7F87BD733560FE1C96E472834CAB6BE016C35727754273125089BE043FD3B26F0B2DE141E05990CE922F1702DA0A2F4E9F8760D0FA712DDB9928E0CDAC14501ED5E2C3 10 | 11 | ChunkListHeader = struct.Struct('<4sIBBBxQQQ') 12 | assert ChunkListHeader.size == 0x24 13 | 14 | Chunk = struct.Struct(' 0 29 | assert chunk_offset == 0x24 30 | assert signature_offset == chunk_offset + Chunk.size * chunk_count 31 | for i in range(chunk_count): 32 | data = f.read(Chunk.size) 33 | hash_ctx.update(data) 34 | chunk_size, chunk_sha256 = Chunk.unpack(data) 35 | yield chunk_size, chunk_sha256 36 | digest = hash_ctx.digest() 37 | if signature_method == 1: 38 | data = f.read(256) 39 | assert len(data) == 256 40 | signature = int.from_bytes(data, 'little') 41 | plaintext = 0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff003031300d0609608648016503040201050004200000000000000000000000000000000000000000000000000000000000000000 | int.from_bytes(digest, 'big') 42 | assert pow(signature, 0x10001, v1_prod_pubkey) == plaintext 43 | elif signature_method == 2: 44 | data = f.read(32) 45 | assert data == digest 46 | else: 47 | raise NotImplementedError 48 | assert f.read(1) == b'' 49 | 50 | def check_chunklist(path, chunklist_path): 51 | with open(path, 'rb') as f: 52 | for chunk_size, chunk_sha256 in parse_chunklist(chunklist_path): 53 | chunk = f.read(chunk_size) 54 | assert len(chunk) == chunk_size 55 | assert hashlib.sha256(chunk).digest() == chunk_sha256 56 | assert f.read(1) == b'' 57 | 58 | def main(): 59 | parser = argparse.ArgumentParser() 60 | parser.add_argument('vmdir', type=Path) 61 | args = parser.parse_args() 62 | vmdir = args.vmdir 63 | check_chunklist(vmdir / 'RecoveryImage.dmg', vmdir / 'RecoveryImage.chunklist') 64 | 65 | if __name__ == "__main__": 66 | main() 67 | -------------------------------------------------------------------------------- /qqX.custom/qqX.custom.readme.txt: -------------------------------------------------------------------------------- 1 | 2 | The folder 'qqX.custom' is designed to be installable at the default location, 3 | or other, and to be qqX upgrade resistant. 4 | 5 | Manual creation of the folder is required. 6 | 7 | The qqX installer/upgrader will/should ignore all direct references to it. 8 | 9 | Chmod a +rx will be given recursively to the whole of the /usr/share/qqX folder, 10 | or other. Anything placed there will become executable .... 11 | 12 | 13 | -------------------------------------------------------------------------------- /qqX.custom/xx_custom_filemanage_qcow_drive: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ## qqX component to be located in folder 'qqX.main' 4 | 5 | # NB Avoid use of "#!/usr/bin/env bash" in this script 6 | # as it will mask process names & inhibit process controls. 7 | 8 | ## Copyright (c) Alex Genovese https://github.com/TuxVinyards 9 | # SMALL CODE SNIPPETS eg the function printColor MAY BE USED 10 | # PERMISSIVELY in projects as MIT or similar, providing CLEAR ATTRIBUTIONS are shown. 11 | # Otherwise: 12 | # Licence GPL3 https://www.gnu.org/licenses 13 | ## qqX - quickemu quickget X terminal project 14 | 15 | ## ShellCheck global disables: 16 | # https://www.shellcheck.net/wiki/SC2242 as it clashes with use of exit traps used to keep mouse click scripts open 17 | # And SC1090,SC1091,SC2024, SC2154 for necessity of dynamic file sourcing 18 | 19 | # shellcheck disable=SC2242,SC1090,SC1091,SC2034,SC2154,SC2009 20 | 21 | # MOUNTS and UNMOUNTS the Shared Drive, or other, if present, in the designated HOST File Manager 22 | # To be run in a separate terminal to ensure qqX main does NOT get elevated permissions 23 | 24 | # DUMMY CUSTOM VERSION that may be edited and placed at "$qqX_CustomFolder/custom_filemanage_qcow_drive" 25 | # and which will be auto-detected and run by function 'qcow2_filemanagement_launcher' 26 | 27 | # Variance theoretically can exist in the functioning of nbd file mounting routines 28 | # so some distro desktop combinations may need some form of custom tweaking ?? 29 | 30 | source "/tmp/qqX_nbd_vars" 31 | source "$qqX_MainFolder/qqX_filemanage_qcow_drive" -------------------------------------------------------------------------------- /qqX.lib/fv: -------------------------------------------------------------------------------- 1 | floatversion -------------------------------------------------------------------------------- /qqX.main/qqX_Initialize: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ## qqX component to be located in folder 'qqX.main' 4 | 5 | ## Copyright (c) Alex Genovese https://github.com/TuxVinyards 6 | # SMALL CODE SNIPPETS eg the function printColor MAY BE USED 7 | # PERMISSIVELY in projects as MIT or similar, providing CLEAR ATTRIBUTIONS are shown. 8 | # Otherwise: 9 | # Licence GPL3 https://www.gnu.org/licenses 10 | ## qqX - quickemu quickget X terminal project 11 | 12 | ## ShellCheck global disables: 13 | # https://www.shellcheck.net/wiki/SC2242 as it clashes with use of exit traps used to keep mouse click scripts open 14 | # And SC1090,SC1091,SC2024, SC2154 for necessity of dynamic file sourcing 15 | 16 | # shellcheck disable=SC2242,SC1090,SC1091,SC2034,SC2154 17 | 18 | qqX_logo_plain() { 19 | # early loader 20 | echo " _ _ \/ " 21 | echo " (_| (_| /\ " 22 | echo " |. |. " 23 | } 24 | 25 | if [[ $NoClear ]]; then 26 | printf () { 27 | # shellcheck disable=SC2199 28 | # checks for occurence of the clearing pattern within the parameter text, treating the text as a single string 29 | if [[ "$@" == '\033c'* ]]; then 30 | # shellcheck disable=SC2124 31 | # see http://www.tldp.org/LDP/abs/html/internalvariables.html#APPREF 32 | # and http://mywiki.wooledge.org/BashGuide/Parameters#Special_Parameters_and_Variables 33 | PFtext="$@" 34 | PFtext=${PFtext//'\033c'/} # remove all escape clears from the printf instruction 35 | command echo 36 | command echo " ***************************************************************************" 37 | sleep "$NoClear" 38 | command printf "$PFtext" 39 | else 40 | # let all other printf commands through 41 | command printf "$@" 42 | fi 43 | } 44 | else 45 | printf () { 46 | # shellcheck disable=SC2199 47 | if [[ "$@" == '\033c'* ]]; then 48 | # add belt and braces for differing terminal behaviour eg. konsole 49 | tput clear 50 | command printf "$@" 51 | else 52 | command printf "$@" 53 | fi 54 | } 55 | fi 56 | 57 | # Usage needs to be loaded early on, so it can be shown if initial help is required 58 | show_CLI_usage () { 59 | 60 | printf "\n Command line Usage:" 61 | printf "\n\n 'qqX' to start the distro/vm selector menu," 62 | printf "\n\n qqX \"vm-name.conf\" or \"path/vm-name.conf\" to start a specific machine," 63 | printf "\n\n qqX --version & qqX --help as standard" 64 | printf "\n\n To set up an ISO: qqX \"iso_name.iso\" or \"/path/iso_name.iso\" " 65 | printf "\n\n Or to analyse a Qcow2 disk: qqX \"/path/disk_name.qcow2\" then follow the menu." 66 | printf "\n" 67 | printf "\n\n Mouse click - Right:" 68 | printf "\n\n On an ISO file and using 'open-with' will open the qqX distro installer." 69 | printf "\n\n On a .qcow2 disk will open-with to the qqX disk analyser & mounter." 70 | printf "\n\n On a .conf file will open-with to the VM control menu." 71 | printf "\n" 72 | printf "\n\n Mouse click - Left:" 73 | printf "\n\n Double click .conf files or select 'run in terminal' " 74 | printf "\n\n Adjust behaviour using qqX [tune]" 75 | printf "\n" 76 | printf "\n\n If distro behaviour blocks qqX desktop launchers from opening a terminal," 77 | printf "\n\n or if the distro stops qqX from opening new terminals:" 78 | printf "\n\n Use the distro's standard terminal opening method then type \'qqX\'" 79 | printf "\n\n or alternatively \'qqX --qqf\' to open the Qcow2 mounter." 80 | printf "\n" 81 | printf "\n\n More Help:" 82 | printf "\n\n https://github.com/TuxVinyards/qqX/wiki/Command-line-and-Mouse \n\n" 83 | 84 | } 85 | 86 | # Pick up the SETTINGS file 87 | # qqX version 1.7 starts on the removal of legacy 'qwrap' code lines and introduces a custom settings file 88 | 89 | if [[ ! -f "$HOME/.qqX/qqX_settings" ]]; then 90 | printf "\n\n ERROR with settings file detection " 91 | printf "\n\n Re-check Installation &/or Installation instructions \n\n" ; sleep 10 ; exit 1 92 | else 93 | qqX_SettingsFile="$HOME/.qqX/qqX_settings" 94 | source "$qqX_SettingsFile" 95 | [[ -f "$HOME/.qqX/custom_settings" ]] && source "$HOME/.qqX/custom_settings" 96 | fi 97 | 98 | if [[ $AllowMoreLocale ]]; then 99 | # Use C.UTF-8 where possible as has more locale https://wiki.archlinux.org/title/Locale 100 | if grep -sqi 'C.utf8' <<< "$(locale -a)"; then export "LC_NUMERIC=C.UTF-8" ; export "LC_COLLATE=C.UTF-8" 101 | else export "LC_NUMERIC=C" ; export "LC_COLLATE=C" ; fi 102 | else 103 | if grep -sqi 'C.utf8' <<< "$(locale -a)"; then export "LC_ALL=C.UTF-8" ; else export "LC_ALL=C" ; fi 104 | fi 105 | 106 | 107 | # Check any MOUSE CLICK or COMMAND LINE parameters and set any initial flags 108 | # Further processing is done in the 'qqX_input_and_mouse_tidyup' script, after other functions have been loaded 109 | 110 | if [[ $Trace_InputVars ]]; then echo "1: $*" ; echo ; sleep "$Trace_InputVars" ; fi 111 | 112 | if [[ $1 == "--help" || $1 == "-h" ]] ; then echo; show_CLI_usage ; exit ; fi 113 | 114 | # @ Ver 1.11, the 'XDG' trigger parameter is now not needed and is deprecated 115 | [[ $1 == "XDG" ]] && shift 116 | # --vm is a legacy carry-over from the quickemu api 117 | [[ $1 == "--vm" || $1 == "-vm" ]] && shift 118 | # If starting from the main menu, this will be present from the .desktop launcher but will need removing. 119 | # On right click starts, the '%f' gets auto-replaced by a filename. 120 | [[ $1 == '%f' ]] && shift 121 | 122 | 123 | if [[ $Trace_InputVars ]]; then echo "2: $*" ; echo ; sleep "$Trace_InputVars" ; fi 124 | 125 | # SORT OUT if '$1' is a readable file or drive, what MIME-TYPE it is, and what main folder to CD to. 126 | 127 | if [[ -e "$1" ]]; then 128 | 129 | XDG_FileType_Recognised= 130 | XDG_MimeType_Recognised= 131 | XDG_MimeType_is_ISO= 132 | XDG_MimeType_is_QCOW= 133 | XDG_ISO_Name= 134 | XDG_RealPathName="$(realpath "$1")" 135 | XDG_MimeType="$(mimetype "$XDG_RealPathName" 2>/dev/null)" 136 | Current_VM_Folder="$(dirname "$XDG_RealPathName")" 137 | if [[ $Trace_InputVars ]]; then 138 | echo "RP $XDG_RealPathName" ; echo "MT $XDG_MimeType" ; echo "CF $Current_VM_Folder" ; echo ; sleep "$Trace_InputVars" 139 | fi 140 | 141 | if [[ "$XDG_MimeType" == *'application/x-qemu-disk' ]]; then 142 | 143 | XDG_MimeType_is_QCOW=1 144 | # is a right-click start on a qcow2 disk (flag is set for later) 145 | QCOW_Input_Dir="$(dirname "$XDG_RealPathName")" 146 | QCOW_Input_DiskName="$(basename "$XDG_RealPathName")" 147 | QCOW_Input_Conf_Dir="$(dirname "$QCOW_Input_Dir")" 148 | QCOW_Input_VM_Dir_Name="$(basename "$QCOW_Input_Dir")" 149 | 150 | if grep -qs "disk_img="'"'"$QCOW_Input_VM_Dir_Name/$QCOW_Input_DiskName" "$QCOW_Input_Dir.conf" ; then 151 | XDG_Input_NON_qqX= ; XDG_Input_VM_qqX=1 152 | else 153 | XDG_Input_NON_qqX=1 ; XDG_Input_VM_qqX= 154 | fi 155 | # .desktop starts tend to open in the $HOME folder even if right clicking somewhwere else 156 | # standard right-click open-with actions will use the current folder 157 | if [[ $XDG_Input_NON_qqX ]]; then 158 | cd "$QCOW_Input_Dir" || printf "\n\n XDG .desktop starter > Disk's start Folder not found " 159 | Current_VM_Folder="$QCOW_Input_Dir" 160 | else 161 | cd "$QCOW_Input_Conf_Dir" || printf "\n\n XDG .desktop starter > Disk's start Folder not found " 162 | Current_VM_Folder="$QCOW_Input_Conf_Dir" 163 | fi 164 | if [[ $Trace_InputVars ]]; then 165 | echo "In D $QCOW_Input_Dir Conf D $QCOW_Input_Conf_Dir" 166 | echo "VDN $QCOW_Input_VM_Dir_Name N $QCOW_Input_DiskName " 167 | echo "VDN $QCOW_Input_VM_Dir_Name N $QCOW_Input_DiskName " 168 | echo "VM_qqX = $XDG_Input_VM_qqX NON_qqX = $XDG_Input_NON_qqX " 169 | echo "pwd $(pwd)" 170 | echo; sleep "$Trace_InputVars" 171 | fi 172 | 173 | elif [[ "$XDG_RealPathName" == *'.iso' ]]; then 174 | XDG_FileType_Recognised=1 175 | XDG_ISO_Name="$1" 176 | if [[ "$XDG_MimeType" == *'application/x-cd-image' || "$XDG_MimeType" == *'application/vnd.efi.iso' ]]; then 177 | XDG_MimeType_is_ISO=1 ; XDG_MimeType_Recognised=1 178 | fi 179 | 180 | elif [[ "$XDG_MimeType" == *'text/plain' ]] || [[ $XDG_RealPathName == *'.conf' ]] ; then 181 | # NB .conf files may also be mime sym-links 182 | if [[ $XDG_RealPathName == *'.conf' ]] && grep -qs 'guest_os' "$XDG_RealPathName" ; then 183 | # then is a conf file open-with and appears to conform to standard quickemu .conf layout 184 | # (also see api mouse script for qcow checks) 185 | XDG_Input_NON_qqX= 186 | XDG_Input_VM_qqX=1 187 | VM_Conf_Dir="$(dirname "$XDG_RealPathName")" 188 | VM_Conf_File="$(basename "$XDG_RealPathName")" 189 | VM_InstanceName="${VM_Conf_File/'.conf'/}" 190 | if [[ $Trace_InputVars ]]; then 191 | echo "R: $XDG_RealPathName" ; echo 192 | echo "D: $VM_Conf_Dir" ; echo 193 | echo "C: $VM_Conf_File" ; echo 194 | echo "N: $VM_InstanceName" ; echo ; sleep "$Trace_InputVars" 195 | fi 196 | shift $# 197 | if [[ $VM_InstanceName ]]; then 198 | # set to General VM folder, where the .conf files are ... and flag to run the VM selector 199 | cd "$VM_Conf_Dir" || printf "\n\n XDG .desktop starter > VM Folder not found " 200 | Current_VM_Folder="$VM_Conf_Dir" 201 | fi 202 | else 203 | XDG_MimeType_is_QCOW= 204 | QCOW_Input_Conf_Dir= 205 | echo 206 | qqX_logo_plain 207 | echo; echo " $XDG_RealPathName"; echo 208 | echo " qqX has been associated to open .conf (text) files via the file properties 'open-with' option." 209 | echo; echo; echo "<<" 210 | head -n 20 "$XDG_RealPathName" 211 | echo ">>" ; echo 212 | FileLines="$(grep -c ^ "$XDG_RealPathName")" 213 | printf "\n This current file is plain text only. " 214 | [[ $FileLines -gt 20 ]] && printf " File length %s lines \n" "$FileLines" && TextAll="[a] view full text & quit " 215 | echo 216 | echo " [enter] to run qqX $TextAll [q] quit" 217 | echo 218 | read -rp " > " TextOpener 219 | echo 220 | if [[ $TextOpener == "a" ]]; then cat "$XDG_RealPathName"; echo; echo " [enter] to quit "; echo; read -rp " > "; exit 221 | elif [[ $TextOpener == "q" ]]; then exit 222 | else VM_InstanceName= ; XDG_MimeType_Recognised=1 223 | fi 224 | fi 225 | 226 | else 227 | 228 | XDG_StartVariant=1 229 | XDG_MimeType_Recognised= 230 | XDG_FileType_Recognised= 231 | XDG_MimeType_is_QCOW= 232 | # set to $HOME folder and flag to run the VM selector 233 | cd "$HOME" || printf "\n\n XDG .desktop starter > HOME Folder not found " 234 | Current_VM_Folder="$HOME" 235 | 236 | fi 237 | 238 | if [[ $Trace_InputVars ]]; then echo "3: $*" ; echo ; sleep "$Trace_InputVars" ; fi 239 | 240 | else Current_VM_Folder="$(pwd)" 241 | 242 | fi 243 | 244 | # establishes a non-volatile transfer variable and flags qqX main loops to trigger 'input_and_mouse_tidy' script 245 | if [[ $1 ]]; then Initial_Input="$1" ; shift ; else Initial_Input= 246 | fi 247 | 248 | 249 | # vim:tabstop=2:shiftwidth=2:expandtab -------------------------------------------------------------------------------- /qqX.main/qqX_MSR_functions: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ## qqX component to be located in folder 'qqX.main' 4 | 5 | ## Copyright (c) Alex Genovese https://github.com/TuxVinyards 6 | # SMALL CODE SNIPPETS eg the function printColor MAY BE USED 7 | # PERMISSIVELY in projects as MIT or similar, providing CLEAR ATTRIBUTIONS are shown. 8 | # Otherwise: 9 | # Licence GPL3 https://www.gnu.org/licenses 10 | ## qqX - quickemu quickget X terminal project 11 | 12 | ## ShellCheck global disables: 13 | # https://www.shellcheck.net/wiki/SC2242 as it clashes with use of exit traps used to keep mouse click scripts open 14 | # And SC1090,SC1091,SC2024, SC2154 for necessity of dynamic file sourcing 15 | 16 | # shellcheck disable=SC2242,SC1090,SC1091,SC2034,SC2154 17 | 18 | 19 | find_kvm_msr_default_and_status () { 20 | 21 | # Gives values to VARIABLES: 'KVM_MSR_DefaultConf' & 'KVM_MSR_status' as Y or N 22 | 23 | # Finds and flags if MSRS has a config conflict for use by function 'print_kvm_status_warnings' & other 24 | 25 | KVM_MSR_Error= 26 | KVM_MSR_Warn= 27 | 28 | KVM_MSR_status="$(cat /sys/module/kvm/parameters/ignore_msrs 2>/dev/null)" 29 | 30 | [[ ! $KVM_MSR_ModProbeFile ]] && KVM_MSR_ModProbeFile="/etc/modprobe.d/kvm-quickemu.conf" 31 | 32 | KVM_MSR_DefaultConf="$(cat "$KVM_MSR_ModProbeFile" 2>/dev/null)" 33 | 34 | [[ "$KVM_MSR_DefaultConf" == *'=Y' ]] && KVM_MSR_default="Y" 35 | 36 | [[ "$KVM_MSR_DefaultConf" == *'=N' ]] || [[ ! -e "$KVM_MSR_ModProbeFile" ]] && KVM_MSR_default="N" 37 | 38 | if [[ $VM_InstanceName ]]; then 39 | 40 | if [[ "$VM_InstanceName" == *windows* ]] || [[ "$VM_InstanceName" == *macos* ]] ; then 41 | 42 | if [[ $KVM_MSR_status == "N" ]]; then KVM_MSR_Error=1 ; else KVM_MSR_Error= ; fi 43 | 44 | elif [[ "$VM_InstanceName" != *windows* ]] && [[ "$VM_InstanceName" != *macos* ]] ; then 45 | 46 | if [[ $KVM_MSR_status == "Y" ]]; then KVM_MSR_Warn=1 ; else KVM_MSR_Warn= ; fi 47 | 48 | fi 49 | 50 | fi 51 | } 52 | 53 | print_kvm_status_warnings () { 54 | 55 | find_kvm_msr_default_and_status 56 | 57 | if [[ $VM_InstanceName ]]; then 58 | 59 | if [[ $KVM_MSR_Warn ]] && [[ $Verbose_MSR_dialog || $MSR_Warnings ]] || [[ ! $MSR_Warnings && $ShowUtilsMenu ]] ; then 60 | 61 | Show_MSR_Warnings=1 ; else Show_MSR_Warnings= ; fi 62 | 63 | if [[ $KVM_MSR_status == "Y" ]] ; then 64 | 65 | [[ $KVM_MSR_Error || $Show_MSR_Warnings ]] && printf "\n /sys/module/kvm/parameters/ignore_msrs = Y" 66 | 67 | if [[ $KVM_MSR_Error ]]; then printColor " ERROR > [m] msrs help\n" 68 | 69 | elif [[ $Show_MSR_Warnings ]]; then printf " [m] msrs help\n" ; fi 70 | 71 | else 72 | 73 | [[ $KVM_MSR_Error || $Show_MSR_Warnings ]] && printf "\n /sys/module/kvm/parameters/ignore_msrs = N" 74 | 75 | if [[ $KVM_MSR_Error ]]; then printColor " ERROR > [m] msrs help\n" 76 | 77 | elif [[ $Show_MSR_Warnings ]] ; then printf " [m] msrs help\n" ; fi 78 | 79 | fi 80 | 81 | fi 82 | 83 | } 84 | 85 | 86 | toggle_msr_defaults () { 87 | 88 | # Modded & now reversible rewrite of original quickemu's function 'ignore_msrs_always' 89 | 90 | Verbose_MSR_dialog=1 91 | 92 | # https://www.linux-kvm.org/page/Category:Docs 93 | 94 | if [[ ! -d /etc/modprobe.d ]]; then 95 | 96 | printf "\n ERROR! /etc/modprobe.d was not found. \n\n See notes, it may be possible to manually create modprobe.d/kvm-quickemu.conf \n\n" 97 | 98 | else 99 | 100 | printColor "\n\n Configure default, boot-up, KVM behaviour " 101 | printf "for unhandled machine-specific registers " 102 | printf "\n\n Although the normal Linux setting is N, both Windows and MacOS require Y (true) 'ignore'" 103 | printf "\n\n But as Linux distros can generally use either, set to Y if Windows or MacOS are present." 104 | 105 | find_kvm_msr_default_and_status 106 | printf "\n\n Status: /sys/module/kvm/parameters/ignore_msrs = %s Current Default = %s" "$KVM_MSR_status" "$KVM_MSR_default" 107 | 108 | [[ ! $KVM_MSR_ModProbeFile ]] && KVM_MSR_ModProbeFile="/etc/modprobe.d/kvm-quickemu.conf" 109 | 110 | [[ ! -e "$KVM_MSR_ModProbeFile" ]] && printf "\n\n \'%s\' needs to be created " "$KVM_MSR_ModProbeFile" 111 | printf "\n\n [y] to set Y [n] to set N [b] to go back \n\n" 112 | read -rp " > " Set_MSR_defaults 113 | 114 | # set .conf file content & update initramfs in all kernels (y/n or none) 115 | 116 | if [[ $Set_MSR_defaults == "y" ]]; then 117 | 118 | printf "\n\n Updating 'initramfs' may take a moment or two ... \n\n" 119 | 120 | # As per flexiondotorg solution in original quickemu, needs 'tee' to get this to work, 121 | # but route tee's stdout to null to tidy the screen 122 | 123 | echo "options kvm ignore_msrs=Y" | sudo tee "$KVM_MSR_ModProbeFile" 1> /dev/null 124 | sudo update-initramfs -k all -u 125 | 126 | elif [[ $Set_MSR_defaults == "n" ]]; then 127 | 128 | printf "\n\n Updating 'initramfs' may take a moment or two ... \n\n" 129 | 130 | echo "options kvm ignore_msrs=N" | sudo tee "$KVM_MSR_ModProbeFile" 1> /dev/null 131 | sudo update-initramfs -k all -u 132 | 133 | fi 134 | 135 | find_kvm_msr_default_and_status 136 | printf "\n\n Status: /sys/module/kvm/parameters/ignore_msrs = %s Current Default = %s" "$KVM_MSR_status" "$KVM_MSR_default" 137 | 138 | printf "\n\n A qqX restart may be required" 139 | 140 | fi 141 | 142 | } 143 | 144 | 145 | show_kvm_msr_general_note () { 146 | 147 | printColor "\n If expecting to use Windows or MacOS, you should modify the system boot settings. " 148 | printf "\n\n qqX has a built in function that can do this for you & allow for future adjustments." 149 | printf "\n\n " 150 | [[ $MSR_DistrosFound ]] && printColor "Windows or MacOS distros appear present. " 151 | printf "See the further notes for details." 152 | 153 | printColor "\n\n\n Status: /sys/module/kvm/parameters/ignore_msrs = %s Current Default = %s" "$KVM_MSR_status" "$KVM_MSR_default" 154 | printf "\n\n Windows or MacOS should be set to 'Y' whilst Linux distros can generally use either." 155 | 156 | } 157 | 158 | show_kvm_sudo_security_note () { 159 | 160 | printColor "\n For session adjustments qqX requires 'sudo' permissions to echo true or false to 'ignore_msrs'" 161 | printf "\n\n This allows you to create a temporary MSRS status that may be changed at any time," 162 | printf "\n\n allowing you to match the selected guest VM that you want to run." 163 | 164 | printf "\n\n\n Linux distros don't *seem* to be affected when set as true \n\n but KVM does have a default of false ..." 165 | 166 | printColor "\n\n\n If you have concerns about this script, or about giving elevated permissions, " 167 | printf "\n\n then the script should be checked or you should issue these commands manually:" 168 | 169 | printf "\n\n\n Use shift-crtl-c (or your distros variant) to copy the displayed sudo command, then" 170 | printf "\n\n open a side terminal & use shift-crtl-v to paste it. In this way, elevated permissions" 171 | printf "\n\n will only exist in the side terminal & will cease as soon as it is closed." 172 | 173 | printf "\n\n\n Run the command & close the terminal." 174 | printf "\n\n Return to qqX & select 'leave as ...' The msrs settings will auto-update. " 175 | printf "\n\n" 176 | 177 | } 178 | 179 | 180 | select_msr_config () { 181 | 182 | # Normally called when MSRS/OS conflict previously detected, 183 | # or if selector is being used to change current status. 184 | 185 | find_kvm_msr_default_and_status 186 | KVM_MSR_selector= 187 | 188 | while true ; do 189 | 190 | [[ $KVM_MSR_selector_LoadHelp ]] && show_kvm_msr_general_note 191 | 192 | if [[ $MSR_DistrosFound && $KVM_MSR_status == "N" ]]; then 193 | 194 | printf "\n\n Sudo command to set as Y: echo 1 | sudo tee /sys/module/kvm/parameters/ignore_msrs" 195 | printColor "\n\n [y] to set session to Y " 196 | printf " [enter] leave as N" 197 | 198 | elif [[ $MSR_DistrosFound && $KVM_MSR_status == "Y" ]]; then 199 | 200 | printf "\n\n Sudo command to set as N: echo 0 | sudo tee /sys/module/kvm/parameters/ignore_msrs" 201 | printColor "\n\n [enter] leave as Y " 202 | printf " [n] to set session to N" 203 | 204 | elif [[ ! $MSR_DistrosFound && $KVM_MSR_status == "N" ]]; then 205 | 206 | printf "\n\n Sudo command to set as N: echo 0 | sudo tee /sys/module/kvm/parameters/ignore_msrs" 207 | printColor "\n\n [enter] leave as N" 208 | printf " [y] to set session to Y" 209 | 210 | elif [[ ! $MSR_DistrosFound && $KVM_MSR_status == "Y" ]]; then 211 | 212 | printf "\n\n Sudo command to set as N: echo 0 | sudo tee /sys/module/kvm/parameters/ignore_msrs" 213 | printColor "\n\n [n] to set session to N " 214 | printf " [enter] leave as Y" 215 | 216 | fi 217 | 218 | if [[ $KVM_MSR_selector == "h" ]] || [[ $KVM_MSR_selector_LoadHelp ]] ; then 219 | 220 | if [[ $FurtherNoteFlag ]]; then FurtherNoteFlag= ; else printf " [f] see further notes" ; fi 221 | printf " [d] to set the defaults (on boot up) \n\n" 222 | 223 | else printf " [h] see help \n\n" 224 | 225 | fi 226 | 227 | read -rp " > " KVM_MSR_selector 228 | printf "\n" 229 | 230 | [[ ! $KVM_MSR_selector ]] && break 231 | 232 | if [[ $KVM_MSR_selector == "y" ]] || [[ $KVM_MSR_selector == "n" ]]; then 233 | 234 | # As per the solution in original quickemu, needs 'tee' to get this to work, 235 | # but route tee's stdout to null to tidy the screen 236 | 237 | [[ $KVM_MSR_selector == "y" ]] && echo 1 | sudo tee /sys/module/kvm/parameters/ignore_msrs 1> /dev/null 238 | 239 | [[ $KVM_MSR_selector == "n" ]] && echo 0 | sudo tee /sys/module/kvm/parameters/ignore_msrs 1> /dev/null 240 | 241 | find_kvm_msr_default_and_status 242 | printf "\n\n Status: /sys/module/kvm/parameters/ignore_msrs = %s Current Default = %s" "$KVM_MSR_status" "$KVM_MSR_default" 243 | 244 | printf "\n\n A qqX restart may be required" 245 | 246 | printColor "\n\n [enter] to return \n\n" 247 | read -rp " > " 248 | 249 | break 250 | 251 | fi 252 | 253 | if [[ $KVM_MSR_selector == "d" ]]; then 254 | 255 | toggle_msr_defaults 256 | 257 | if [[ $Set_MSR_defaults == "b" ]]; then 258 | 259 | Set_MSR_defaults= 260 | print_kvm_status_warnings 261 | printColor "\n\n\n Make TEMPORARY setting adjustments to MSRS ?" 262 | 263 | else 264 | find_kvm_msr_default_and_status 265 | break 266 | fi 267 | 268 | elif [[ $KVM_MSR_selector == "f" ]]; then 269 | 270 | show_kvm_sudo_security_note 271 | FurtherNoteFlag=1 272 | 273 | fi 274 | 275 | done 276 | 277 | KVM_MSR_selector= 278 | KVM_MSR_selector_LoadHelp= 279 | 280 | } 281 | 282 | msrs_conflict_check_resolver() { 283 | 284 | find_kvm_msr_default_and_status 285 | 286 | # Display & Offer config settings if MSRS/OS CONFLICT exists # normal untweaked system default = N 287 | 288 | if [[ $KVM_MSR_status == "N" ]] ; then 289 | 290 | if [[ "$VM_InstanceName" == *windows* ]] || [[ "$VM_InstanceName" == *macos* ]] ; then 291 | 292 | [[ ! $ByPass_VM_Array_Selector ]] && printf "\033c" 293 | printColor "\n\n Selected: %s " "$VM_InstanceName" 294 | printf " 'ignore_msrs' is set & is recommended for Windows and Mac" 295 | 296 | select_msr_config 297 | if [[ $KVM_MSR_status == "N" ]]; then KVM_MSR_Error=1 ; else KVM_MSR_Error= ; fi 298 | 299 | fi 300 | 301 | else # Then status = Y, which is only needed for Windows & Mac 302 | 303 | if [[ "$VM_InstanceName" != *windows* ]] && [[ "$VM_InstanceName" != *macos* ]] ; then 304 | 305 | [[ ! $ByPass_VM_Array_Selector ]] && printf "\033c" 306 | printColor "\n\n Selected: %s " "$VM_InstanceName" 307 | printf "\n\n 'ignore_msrs' is set: can be okay for other OS's but is normally used only with Windows and Mac" 308 | 309 | select_msr_config 310 | if [[ $KVM_MSR_status == "Y" ]]; then KVM_MSR_Error=1 ; else KVM_MSR_Error= ; fi 311 | 312 | fi 313 | 314 | fi 315 | 316 | } 317 | 318 | -------------------------------------------------------------------------------- /qqX.main/qqX_UI_chrome_up: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ## qqX component to be located in folder 'qqX.main' 4 | 5 | ## Copyright (c) Alex Genovese https://github.com/TuxVinyards 6 | # SMALL CODE SNIPPETS eg the function printColor MAY BE USED 7 | # PERMISSIVELY in projects as MIT or similar, providing CLEAR ATTRIBUTIONS are shown. 8 | # Otherwise: 9 | # Licence GPL3 https://www.gnu.org/licenses 10 | ## qqX - quickemu quickget X terminal project 11 | 12 | ## ShellCheck global disables: 13 | # https://www.shellcheck.net/wiki/SC2242 as it clashes with use of exit traps used to keep mouse click scripts open 14 | # And SC1090,SC1091,SC2024, SC2154 for necessity of dynamic file sourcing 15 | 16 | # shellcheck disable=SC2242,SC1090,SC1091,SC2034,SC2154 17 | 18 | # GENERAL COLOR & THEMING (see settings) 19 | 20 | [[ ! $X_Shade ]] && X_Shade="3" 21 | 22 | # Yellow 3 (recommended), Blue 4, Cyan 6 (brighter blue), Red 1 23 | # https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit 24 | # https://tldp.org/HOWTO/Bash-Prompt-HOWTO/x405.html 25 | # if source without declared shade, set default shade 26 | 27 | 28 | # non-color ECHO TRAP, mainly designed to capture the technical output that quickemu doesn't send to log file 29 | printf "\n" > "/tmp/qqX_echo_output.log" 30 | 31 | echo () { 32 | 33 | if [[ $UseCustomBootArgs ]]; then 34 | command echo "$@" >> "/tmp/qqX_echo_output.log" ; 35 | else 36 | command echo "$@" | tee -a "/tmp/qqX_echo_output.log" ; 37 | fi 38 | 39 | } 40 | 41 | printColor () { 42 | tput setaf "$X_Shade" 43 | # shellcheck disable=SC2059 44 | printf "$@" 45 | tput sgr0 46 | } 47 | 48 | # avoid use of echo near the vm boot section, to avoid clashes with the log file 49 | 50 | echColor () { 51 | if [[ $2 ]] ; then 52 | if [[ "$1" == "-n" ]] ; then 53 | tput setaf "$X_Shade" 54 | echo -n "$2" 55 | tput sgr0 56 | else 57 | tput setaf "$X_Shade" 58 | echo "$@" 59 | tput sgr0 60 | fi 61 | else 62 | tput setaf "$X_Shade" 63 | echo "$1" 64 | tput sgr0 65 | fi 66 | } 67 | 68 | 69 | # General default title: 70 | qqX_title="[quickemu quickget X terminal project] - $qqX_Version - Basically Better:" 71 | 72 | ## qqX chrome up 73 | 74 | qqX_logo() { 75 | # not for use during vm boot sequences 76 | echColor " _ _ \/ " 77 | echColor " (_| (_| /\ " 78 | echColor " |. |. " 79 | } 80 | 81 | qqX_logo_plain() { 82 | # early loader 83 | echo " _ _ \/ " 84 | echo " (_| (_| /\ " 85 | echo " |. |. " 86 | } 87 | 88 | qqX_logo_printf() { 89 | # printf version (no end line feed) 90 | printColor " _ _ \/ " 91 | printColor " (_| (_| /\ " 92 | printColor " |. |. " 93 | } 94 | 95 | # Variant on http://www.figlet.org/ -f mini (-k) (c) Alex Genovese 96 | 97 | qqX_logo_title() { 98 | # not for use during vm boot sequences 99 | if [[ $1 ]]; then qqX_LogoTxt="$1" ; else qqX_LogoTxt="$qqX_title" ; fi 100 | echColor " _ _ \/ " 101 | echColor " (_| (_| /\ " 102 | echColor " |. |. $qqX_LogoTxt" 103 | } 104 | 105 | qqX_logo_printf_title() { 106 | # printf version (no end line feed & also avoids the quickemu echo trap) 107 | if [[ $1 ]]; then qqX_LogoTxt="$1" ; else qqX_LogoTxt="$qqX_title" ; fi 108 | printColor "\n _ _ \/ " 109 | printColor "\n (_| (_| /\ " 110 | printColor "\n |. |. %s" "$qqX_LogoTxt" 111 | } 112 | 113 | 114 | function exit { 115 | 116 | # trap to keep terminal open if started by mouse click -t secs 117 | # or to stop sourced quickemu quitting as 'normal' procedure (tput resets any hidden cursors) 118 | # https://unix.stackexchange.com/questions/220330/hide-and-unhide-cursor-with-tput 119 | 120 | if [[ $1 == "directly" ]]; then 121 | tput cnorm 122 | command exit 123 | 124 | elif [[ $1 ]] && [[ ! $NoExit ]]; then 125 | printf "\n\n" 126 | printColor " ERROR : [i] ignore & continue [q] quit \n\n" 127 | read -rp " > " 128 | ExitTrap= 129 | printf "\n\n Confirm: [Enter] to quit [r] to return and retry something else \n\n" 130 | read -rp " > " ExitTrap 131 | if [[ ! $ExitTrap ]]; then 132 | tput cnorm 133 | command exit "$@" 134 | else 135 | return 0 136 | fi 137 | 138 | elif [[ ! $disk_img ]]; then 139 | # don't exit on quickemu error no disk defined, this is handled by qqX REVIEW 140 | return 141 | 142 | fi 143 | 144 | # zero the optional 'exit 1' bypass flag (if it has been set) 145 | NoExit= 146 | } 147 | 148 | # Tests for these programs will have been made just previously in the read_main_settings script 149 | # but placing the message functions here allows these functions to run elsewhere 150 | # See function qcow2_filemanagement_launcher 151 | 152 | texteditor_not_found_message () { 153 | printColor "\n\n Unable to open settings or show some help sections without an editor." 154 | printf "\n\n Default or basic text editors will be tried or have been tried ..." 155 | printf "\n\n Give settings file the command name of a preferred editor." 156 | printf "\n\n Settings location: %s \n\n [enter] to return \n\n" "$qqX_SettingsFile" 157 | read -rp " > " 158 | } 159 | 160 | web_browser_not_found_message () { 161 | printColor "\n\n Add Web Browser to GENERAL SETTINGS:" 162 | printf "\n\n None of the usual or default Web Browsers can be found ..." 163 | printf "\n\n Give settings file the command name of a preferred browser." 164 | printf "\n\n Settings location: %s \n\n [enter] to return \n\n" "$qqX_SettingsFile" 165 | read -rp " > " 166 | } 167 | 168 | file_manager_not_found_message () { 169 | printColor "\n\n Add File Manager to GENERAL SETTINGS:" 170 | printf "\n\n None of the usual or default File Managers can be found ..." 171 | printf "\n\n Give settings file the command name of a preferred manager." 172 | printf "\n\n Settings location: %s \n\n [enter] to return \n\n" "$qqX_SettingsFile" 173 | read -rp " > " 174 | } 175 | 176 | show_qqX_title_bar() { 177 | 178 | if [[ ! $LatestQEMU ]] && grep -qsE ^'[0-9]' "/tmp/latest-qemu.txt"; then LatestQEMU="$(cat "/tmp/latest-qemu.txt")"; fi 179 | 180 | qqX_logo_printf_title "$qqX_title" ; printf "\n" 181 | printf "\n\n Currently wrapping: QEMU %s" "$QEMU_VER_LONG" 182 | if [[ $LatestQEMU ]]; then 183 | if [[ $QEMU_VER_LONG == "$LatestQEMU" ]]; then printf " (latest) " 184 | else printf " (latest: %s) " "$LatestQEMU" 185 | fi 186 | else printf " " 187 | fi 188 | printf "quickemu %s & quickget %s '%s'" "$QE_VerNumber" "$QG_VerNumber" "$QE_SourceVersion" 189 | 190 | [[ $VerboseArgs ]] && printColor " (Verbose Args)" 191 | 192 | if [[ $CodingHotSwap && $CodingHotSwapShow ]] || [[ $DevStart ]]; then 193 | printf "\n\n Using Code: %s " "$QE_SourcePath" 194 | [[ $CodingHotSwap ]] && CodingHotSwapShow= 195 | fi 196 | 197 | PWD_Folder="$(pwd)" 198 | HideFolderTitleBar= 199 | 200 | for TF in "${Extra_VM_Folder[@]}"; do 201 | [[ $TF == "$PWD_Folder" ]] || [[ $PWD_Folder == "$Default_VM_Folder" ]] && HideFolderTitleBar=1 202 | done 203 | 204 | if [[ ! $Current_VM_Folder ]]; then 205 | Current_VM_Folder="$Default_VM_Folder" 206 | cd "$Default_VM_Folder" || printf "\n\n ChromeUp ERROR sorting Current Folder" 207 | fi 208 | 209 | if [[ ! $HideFolderTitleBar ]]; then 210 | printf "\n\n Default Folder: %s Current Folder: %s \n" "$Default_VM_Folder" "$Current_VM_Folder" 211 | else 212 | printf "\n" 213 | fi 214 | 215 | } 216 | 217 | function show_qqX_general_help { 218 | 219 | HelpSelect= 220 | 221 | while true; do 222 | 223 | if [[ $1 ]]; then HelpSelect="$1" ; shift ; fi 224 | 225 | printf "\n\n FOR HELP with settings and user modes, SEE NOTES IN THE GENERAL SETTINGS file" 226 | printf "\n\n Also see: https://github.com/TuxVinyards/qqX/wiki/FAQs-and-Help etc " 227 | printf "\n\n For code contributions, bugs and updates, see: https://github.com/TuxVinyards/qqX" 228 | 229 | cat "$HOME/.qqX/icons/qqX.ascii.logo.8bit.25.txt" 230 | 231 | printf "\n Translations: https://github.com/TuxVinyards/qqX/wiki/Translation Release Notes: https://github.com/TuxVinyards/qqX#release-notes" 232 | printColor "\n\n [w] Online: qqX WIKI PAGES Offline: [f] FreeBird notes [c] command line & mouse notes [enter] to RETURN" 233 | printf "\n\n" 234 | [[ ! $HelpSelect ]] && read -rp " > " HelpSelect 235 | 236 | if [[ $HelpSelect == "c" ]]; then 237 | show_CLI_usage 238 | printf "\n" 239 | read -rp " > " HelpSelect 240 | 241 | elif [[ $HelpSelect == "f" ]]; then 242 | printf "\n Hot-Swappable from all main menus: [FS] FreeSpirit [FB] FreeBird [HB] [Ech] [Sys] [Cust] [Dev] \n\n" 243 | grep -m 1 -A 100 '# Branches' "$qqX_SettingsFile" | grep -m 1 -B 100 '# FreeBird or FreeSpirit are recommended'| tr '#' ' ' 244 | if [[ -e "$Default_VM_Folder/qqX.custom/QE.Custom/quickemu" ]]; then 245 | printf "\n\n Custom = Default_VM_Folder > qqX.custom > %s " "$("$Default_VM_Folder/qqX.custom/QE.Custom/quickemu" --version)" 246 | fi 247 | if [[ -e "$QE_DevPath/quickemu" ]]; then 248 | printf "\n\n Dev = %s > %s " "$QE_DevPath" "$("$QE_DevPath/quickemu" --version)" 249 | fi 250 | printf "\n\n [enter] to return \n\n" 251 | read -rp " > " HelpSelect 252 | 253 | elif [[ $HelpSelect == "w" ]]; then 254 | HelpSelect= 255 | printf "\n Opening Browser ... \n" 256 | (nohup &> /dev/null "$WebBrowser" "https://github.com/TuxVinyards/qqX/wiki/FAQs-and-Help" & ) || web_browser_not_found_message 257 | sleep 1 258 | else 259 | HelpSelect= 260 | break 261 | fi 262 | 263 | done 264 | 265 | } 266 | 267 | 268 | function multi_instance_checks { 269 | 270 | MultiQuit= 271 | KillQQ= 272 | CurrentQQ_FullLine= 273 | CurrentQQ= 274 | CurrentQQ_LastField= 275 | AnotherQQ= 276 | 277 | # Make sure that all current processes have been statted properly before moving on. 278 | # pgrep -an is not reliable and pgrep sub-shell creates another pid too. 279 | # BUT new PID always goes on end of list so check for this until no 'kworker' grep errors 280 | sleep 0.2 281 | i=0 282 | while true; do 283 | CurrentQQ_FullLine="$(pgrep -afi qqX | tail -n 1 2>/dev/null)" 284 | [[ $ShowProcesses ]] && printf "\n(i%s) %s " "$i" "$CurrentQQ_FullLine" 285 | if [[ "$CurrentQQ_FullLine" == *kworker* ]]; then sleep 0.2 ; ((i+=1)) 286 | else 287 | CurrentQQ_LastField="$(awk '{print $NF}' <<< "$CurrentQQ_FullLine")" 288 | CurrentQQ="$(awk '{print $1}' <<< "$CurrentQQ_FullLine")" 289 | break 290 | fi 291 | if [[ $i -gt 25 ]]; then 292 | printColor "\n\n ERROR statting current PID > restart required" 293 | printf "\n\n\n [enter] to close \n\n" 294 | read -rp " > " 295 | fi 296 | done 297 | 298 | # All qqX, exactly, will exclude 'qqX_copy_over' but doesn't make reading the parameters and variants too easy 299 | # Also pgrep -aAxi qqX, with x, doesn't count itself ..... but -afi does .... REVIEW 300 | # Note: needs -i ignore case, so that CLI starts using 'qqx' (lower) get counted 301 | 302 | AnotherQQ="$(pgrep -aAi qqX 2>/dev/null)" 303 | 304 | # Also Note: CL launched running of qcow2 file management happens before this script is called. See qqX start. 305 | # Running CL instances will show up with 'pgrep -aAxi qqX' as 'pid /bin/bash /usr/bin/qqX --qqf' or similar. 306 | # With pgrep -afi qqX_filemanage as: pid /bin/bash /usr/share/qqX/qqX.main/qqX_filemanage_qcow_drive --qqf 307 | 308 | if [[ $ShowProcesses ]]; then 309 | echo 310 | echo; echo "CurrentQQ $CurrentQQ"; echo; echo "LastField $CurrentQQ_LastField" 311 | echo; echo "AnotherQQ:"; echo; echo "$AnotherQQ"; echo 312 | echo; echo "QF_exit_to_Main = $QF_exit_to_Main"; echo 313 | read -rp " > " 314 | fi 315 | 316 | CurrentQQ_mode="main" 317 | if [[ ! $QF_exit_to_Main ]]; then 318 | case "$CurrentQQ_LastField" in 319 | *'.qcow2'*|*'--qqf'*|*'qqX_filemanage '*) CurrentQQ_mode="qcow" ;; 320 | esac 321 | fi 322 | 323 | if [[ $AnotherQQ ]]; then 324 | OtherQQ_MainArr=() 325 | OtherQQ_F_Arr=() 326 | mapfile -t AnotherQQ_Arr <<< "$AnotherQQ" 327 | for QQ in "${AnotherQQ_Arr[@]}"; do 328 | if [[ "$QQ" != *"$CurrentQQ"* ]]; then 329 | if [[ "$QQ" == *'--qqf'* ]] || [[ "$QQ" == *'qqX_filemanage'* ]]; then 330 | OtherQQ_F_Arr+=("$(awk '{print $1}' <<< "$QQ")") 331 | elif [[ "$QQ" != *'qqX_copy_over'* ]]; then 332 | OtherQQ_MainArr+=("$(awk '{print $1}' <<< "$QQ")") 333 | elif [[ $QF_exit_to_Main ]] && [[ "$QQ" != *'qqX_copy_over'* ]]; then 334 | OtherQQ_MainArr+=("$(awk '{print $1}' <<< "$QQ")") 335 | fi 336 | fi 337 | done 338 | # Starting Main via the .qcow2 route needs to counted too. And only 1 QF instance should exist. 339 | # .qcow2 remains when 'exit to main' but changes to 'filemanage' when switching to QF 340 | # while elif [[ "$QQ" == *'.qcow2'* ]]; gets included in [[ "$QQ" != *'qqX_copy_over'* ]] 341 | if [[ $ShowProcesses ]]; then 342 | echo; declare -p AnotherQQ_Arr; echo 343 | echo; declare -p OtherQQ_MainArr; echo 344 | echo; declare -p OtherQQ_F_Arr; echo 345 | read -rp " > " 346 | fi 347 | fi 348 | 349 | if [[ $AnotherQQ ]] && [[ $CurrentQQ_mode == "main" ]] && [[ "${#OtherQQ_MainArr[@]}" -ge 1 ]]; then 350 | 351 | tput sc 352 | qqX_logo_printf_title "CAUTION: more than one instance of qqX appears to be running " 353 | 354 | printf "\n" 355 | printf "\n\n QEMU will lock protect key files and will prevent VM's being run simultaneously ..." 356 | printf "\n\n Simple usage can be possible but be aware that minor cross-linking problems could occur" 357 | 358 | printf "\n\n\n [enter] to continue with all [k] kill other instances and continue with just this one " 359 | printColor "\n\n [q] to quit \n\n" 360 | read -rp " > " MultiQuit 361 | 362 | [[ $MultiQuit == "q" ]] && exit directly 363 | 364 | if [[ $MultiQuit == "k" ]]; then 365 | if [[ $(pgrep kvm 2>/dev/null) ]]; then printColor "\n\n CAUTION: Check for distros that may be running. Force closing can corrupt VM file systems ...." 366 | else 367 | printf "\n\n Killing desktop started instances will close the terminal windows." 368 | printf "\n\n CLI instances will terminate but the windows will remain open ..." 369 | printColor "\n\n\n Killing other qqX instances ... " 370 | fi 371 | printf "\n\n [k] yes, kill [enter] to continue with all [q] to quit \n\n" 372 | read -rp " > " KillQQ 373 | if [[ $KillQQ == "k" ]]; then 374 | for QQ in "${OtherQQ_MainArr[@]}"; do 375 | KillTarget="$(awk '{print $1}' <<< "$QQ" | tr -cd '[:digit:]' 2>/dev/null)" 376 | [[ $ShowProcesses ]] && printf "\n QQ = % > killing: %s" "$QQ" "$KillTarget" 377 | kill "$KillTarget" 2>/dev/null 378 | done 379 | printColor "\n\n Closed \n\n" ; sleep 0.7 380 | elif [[ $KillQQ == "q" ]]; then exit directly 381 | fi 382 | fi 383 | 384 | tput rc ; tput ed # clear back the last lines to sc (set cursor) 385 | 386 | fi 387 | 388 | if [[ "$(pgrep quickgui 2>/dev/null)" ]] ; then 389 | 390 | printColor "\n\n WARNING: an instance of QUICKGUI appears to be running " 391 | printf "\n\n Both this program and qqX do have an amount of cross-link protection, however note that:" 392 | printf "\n\n Stopping an instance shown as running in QuickGUI may stop an instance started in qqX. \n\n" 393 | read -rp " [q] to quit [enter] to continue > " MultiQuit 394 | 395 | [[ $MultiQuit == "q" ]] && exit directly 396 | 397 | fi 398 | 399 | printf "" > "$ConfClearanceLog" 400 | 401 | } 402 | -------------------------------------------------------------------------------- /qqX.main/qqX_VM_array_and_Select: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ## VM Selector Menu to be located in folder 'qqX.main' along with 'qqX_VM_array_routines' 4 | 5 | ## Copyright (c) Alex Genovese https://github.com/TuxVinyards 6 | # SMALL CODE SNIPPETS eg the function printColor MAY BE USED 7 | # PERMISSIVELY in projects as MIT or similar, providing CLEAR ATTRIBUTIONS are shown. 8 | # Otherwise: 9 | # Licence GPL3 https://www.gnu.org/licenses 10 | ## qqX - quickemu quickget X terminal project 11 | 12 | ## ShellCheck global disables: 13 | # https://www.shellcheck.net/wiki/SC2242 as it clashes with use of exit traps used to keep mouse click scripts open 14 | # And SC1090,SC1091,SC2024, SC2154 for necessity of dynamic file sourcing 15 | 16 | # shellcheck disable=SC2242,SC1090,SC1091,SC2034,SC2154 17 | 18 | # For notes on custom vm menus > edit > find > "Custom styles" in 'qqX.main/qqX_VM_selector_styles' 19 | 20 | 21 | VM_loader_selector_and_menu () { 22 | 23 | ShowUtilsMenu= 24 | ShowCustomBootStyle= 25 | ShowDefault_Hybrid= 26 | ForceCustomBootStyle= 27 | 28 | # LOADER 29 | 30 | if [[ $ByPass_VM_Array_Loader ]] && [[ ${VM_Array[0,0]} ]]; then 31 | # just clear the flag, may be bypassed if array present and nothing new added, or if settings not changed ... 32 | ByPass_VM_Array_Loader= 33 | [[ $GeneralLoopCheck ]] && printf "\n\n Bypassing VM loader:" && dev_loop_check 34 | else 35 | refresh_sources_and_load_VM_arrays 36 | [[ $GeneralLoopCheck ]] && printf "\n\n VM selector, post refresh_sources_and_load_VM_arrays:" && dev_loop_check 37 | fi 38 | 39 | # SELECTOR 40 | 41 | if [[ $ByPass_VM_Array_Selector ]] ; then 42 | 43 | VM_check_and_load 44 | [[ $GeneralLoopCheck ]] && printf "\n\n Bypassing VM array selector :" && dev_loop_check 45 | 46 | else 47 | 48 | VM_Selection= 49 | VM_Conf_Dir= 50 | VM_Conf_File= 51 | VM_InstanceName= 52 | MainDrive= 53 | IsoImg= 54 | Mounted_ISO= 55 | NewFileTuneUp= 56 | NewFileTuneUp_JustDone= 57 | 58 | cd "$Default_VM_Folder" || DefaultFolderError=1 59 | Current_VM_Folder="$Default_VM_Folder" 60 | 61 | while true; do 62 | 63 | [[ $GeneralLoopCheck ]] && printf "\n\n VM selector loop:" && dev_loop_check 64 | 65 | if [[ $Run_GT ]]; then Run_GT= ; VM_Selection="gt" 66 | 67 | else 68 | printf "\033c" 69 | show_qqX_title_bar 70 | 71 | if ! type -t "print_selector_array_${VM_SelectorStyle}_style" 1>/dev/null ; then VM_SelectorStyle="$Default_VM_SelectorStyle" ; fi 72 | 73 | for Style in {A..Z} ; do 74 | if type -t "print_selector_array_${Style}_style" 1>/dev/null ; then LastSelectStyleAvailable="$Style" ; else break; fi 75 | done 76 | 77 | printf "\n Total Storage: Drives %s (%s) ISO's %s (%s)" "${TotalQcow2_Sizes}G" "$TotalQcow2_SizesCount" "${Total_ISO_Sizes}G" "$Total_ISO_SizesCount" 78 | [[ $BackUpQcowPresent ]] && printf " Backups (%s)" "$BackUpQcowPresent" 79 | printf " [r] refresh [sz] show size details" 80 | printf "\n" 81 | 82 | [[ ! $SelectArrayError ]] && print_selector_array_"$VM_SelectorStyle"_style 83 | 84 | if [[ $New_VM_Conf_File ]] && [[ ! $Current_VM_Folder_Recognised ]] && [[ -f "$(pwd)/$New_VM_Conf_File" ]]; then 85 | printColor "\n\n For future display, add the NEW Current Folder to the VM array, [set] general settings ..." 86 | fi 87 | 88 | dev_loop_check 89 | 90 | [[ $DefaultFolderError ]] && printf "\n\n ERROR: DEFAULT FOLDER " 91 | [[ $ExtraFolderError ]] && printf "\n\n ERROR: VM FOLDER ARRAY '%s' " "$ExtraFolderError" 92 | [[ $SelectArrayError ]] && printf "\n\n ERROR: VM ARRAY / VM .CONF %s " "$SelectArrayError" 93 | 94 | if [[ $DefaultFolderError || $ExtraFolderError || $SelectArrayError ]]; then 95 | 96 | printf "\n\n Settings file: %s" "$qqX_SettingsFile" 97 | printf "\n\n [enter] to edit settings file [c] ignore, re-check and continue [q] to quit " 98 | 99 | else 100 | 101 | printf "\n\n [number] to select a VM " 102 | [[ ! $ShowFindPoint ]] && ShowFindPoint=32 103 | if [[ $VM_ElementCount -ge "$ShowFindPoint" ]]; then printf " [set] edit settings [fd] find distro" 104 | else printf " [set] view or edit settings file" 105 | fi 106 | printf " [A-%s] change menu style %s" "$LastSelectStyleAvailable" "$VM_SelectorStyle" 107 | 108 | if [[ $ZsyncDistrosFound || $LiveDistrosFound ]]; then 109 | printf " (" 110 | [[ $LiveDistrosFound ]] && printf "L=LiveISO" 111 | [[ $LiveDistrosFound && $ZsyncDistrosFound ]] && printf ") & (" 112 | [[ $ZsyncDistrosFound ]] && printf "Z=Zsync" 113 | printf ")" 114 | fi 115 | 116 | VM_Array_NameZero="${VM_Array[0,1]}" 117 | VM_Array_NameZero="${VM_Array_NameZero/'.conf'/}" 118 | 119 | if [[ $(cat "$qqX_Upd_VarsTmpFile" 2>/dev/null) ]]; then source "$qqX_Upd_VarsTmpFile"; fi 120 | 121 | if [[ $qqX_UpdateAvail ]]; then printf "\n\n [Enter] " ; else printColor "\n\n [Enter] " ; fi 122 | 123 | printf "%-25s" "${VM_Array_NameZero^^}" 124 | printf " [gt] quickget a new distro [qf] qcow2 file manager [h] Help [f] FreeBird [r] refresh [q] quit" 125 | 126 | [[ $qqX_UpdateAvail ]] && printColor "\n\n [dl] >> view new qqX update details: %s %s <<" "$qqX_Latest_ReleaseType" "$qqX_Latest_ReleaseNumber" 127 | 128 | if [[ $MenuStylePreviewed ]] && [[ ! $SettingsFileAdjusted ]] && [[ ! $VM_Selection_Overide ]]; then 129 | printColor "\n\n Preview only - use [set] for menu permanency, [r] to return to default " ; MenuStylePreviewed= 130 | elif [[ $SettingsFileAdjusted ]]; then printColor "\n\n ARRAYS have been RELOADED and settings REFRESHED ...."; SettingsFileAdjusted= 131 | fi 132 | 133 | fi 134 | 135 | path_diagnostics 136 | IgnoreConfigErrors= 137 | FD_Phrase= 138 | FD_Start= 139 | 140 | if [[ $DiskStorageChanges ]]; then 141 | DiskStorageChanges= 142 | VM_Selection_Overide="r" 143 | fi 144 | 145 | if [[ $VM_Selection_Overide ]];then 146 | VM_Selection="$VM_Selection_Overide" 147 | VM_Selection_Overide= 148 | else 149 | printf "\n\n" 150 | read -rp " > " VM_Selection 151 | fi 152 | fi 153 | 154 | # 155 | 156 | if [[ $DefaultFolderError || $ExtraFolderError || $SelectArrayError || $VM_Selection == "c" ]]; then 157 | 158 | ExtraFolderError= 159 | VM_InstanceName= 160 | [[ $VM_Selection != "c" ]] && qqX_edit_settings 161 | VM_Selection= 162 | return 163 | 164 | elif [[ $VM_Selection == "fd" ]]; then FD_help_notes 165 | 166 | elif [[ "$VM_Selection" == fd* ]]; then 167 | 168 | if [[ "$VM_Selection" == 'fd c '* ]]; then FD_Phrase="${VM_Selection/'fd c '/}" 169 | elif [[ "$VM_Selection" == 'fd s '* ]]; then FD_Start="${VM_Selection/'fd s '/}" 170 | else FD_help_notes 171 | fi 172 | 173 | elif [[ "$VM_Selection" == [A-Z] ]]; then 174 | 175 | # NB different to VM_selector_menu_refresh 176 | VM_SelectorStyle="$VM_Selection" 177 | ByPass_VM_Array_Loader=1 178 | MenuStylePreviewed=1 179 | ListTitleFolderOld= 180 | InvertStyleColors= 181 | VM_Conf_Dir= 182 | VM_Conf_File= 183 | VM_InstanceName= 184 | VM_Selection= 185 | 186 | # Note, for this script, 'refresh_sources_and_load_VM_arrays' gets run at start of loop on return 187 | 188 | elif [[ $VM_Selection == "r" ]]; then VM_selector_menu_refresh ; return 189 | 190 | elif [[ ${VM_Selection^^} == "FS" ]] ; then 191 | CodingHotSwap="FreeSpirit" ; SettingsFileAdjusted=1; CodingHotSwapShow=1; VM_selector_menu_refresh ; return 192 | elif [[ ${VM_Selection^^} == "FB" ]] ; then 193 | CodingHotSwap="FreeBird" ; SettingsFileAdjusted=1; CodingHotSwapShow=1; VM_selector_menu_refresh ; return 194 | elif [[ ${VM_Selection^^} == "HB" ]] ; then 195 | CodingHotSwap="HomeBird" ; SettingsFileAdjusted=1; CodingHotSwapShow=1; VM_selector_menu_refresh ; return 196 | elif [[ "${VM_Selection,,}" == ech* ]] ; then 197 | CodingHotSwap="Echo" ; SettingsFileAdjusted=1; CodingHotSwapShow=1; VM_selector_menu_refresh ; return 198 | elif [[ "${VM_Selection,,}" == sys* ]] ; then 199 | CodingHotSwap="System" ; SettingsFileAdjusted=1; CodingHotSwapShow=1; VM_selector_menu_refresh ; return 200 | elif [[ "${VM_Selection}" == cust* ]] ; then 201 | CodingHotSwap="Custom" ; SettingsFileAdjusted=1; CodingHotSwapShow=1; VM_selector_menu_refresh ; return 202 | elif [[ "${VM_Selection,,}" == dev* ]] ; then 203 | CodingHotSwap="Dev" ; SettingsFileAdjusted=1; CodingHotSwapShow=1; VM_selector_menu_refresh ; return 204 | 205 | elif [[ $VM_Selection == "q" ]]; then echo ; exit directly 206 | 207 | elif [[ $VM_Selection == "dl" ]]; then download_new_release ; return 208 | 209 | elif [[ $VM_Selection == "qf" ]]; then qcow2_filemanagement_launcher "qqf-setvars" ; qcow2_filemanagement_launcher 210 | 211 | elif [[ $VM_Selection == "set" ]];then 212 | 213 | VM_Selection= 214 | qqX_edit_settings 215 | return 216 | 217 | elif [[ $VM_Selection == "sz" ]];then 218 | 219 | VM_Selection= 220 | show_VM_Array_sizes_text 221 | 222 | elif [[ $VM_Selection == "h" || $VM_Selection == "f" ]];then 223 | 224 | if [[ $VM_Selection == "f" ]]; then show_qqX_general_help "f" ; else show_qqX_general_help ; fi 225 | VM_Selection= 226 | 227 | elif [[ $VM_Selection == "gt" || $VM_Selection == "GT" ]]; then 228 | 229 | VM_Selection= 230 | function_quick_get_wrap 231 | ByPass_VM_Array_Loader= 232 | return 233 | 234 | elif [[ $VM_Selection == [0-9] || $VM_Selection == [0-9][0-9] || $VM_Selection == [0-9][0-9][0-9] ]]; then 235 | 236 | if [[ $VM_Selection -gt $VM_Array_LastIndex ]]; then 237 | printColor "\n\n Request '%s' Not Recognised \n\n" "$VM_Selection" 238 | sleep 1 239 | # clear any pressing of the enter key occuring during error message pause 240 | read -r -t 0.1 241 | read -r -t 0.1 242 | read -r -t 0.1 243 | read -r -t 0.1 244 | VM_Conf_Dir= 245 | VM_Conf_File= 246 | VM_Selection= 247 | else 248 | VM_Conf_Dir="${VM_Array[$VM_Selection,0]}" 249 | VM_Conf_File="${VM_Array[$VM_Selection,1]}" 250 | VM_Selection= 251 | VM_check_and_load 252 | return 253 | fi 254 | 255 | elif [[ $VM_Selection ]]; then 256 | 257 | printColor "\n\n Request '%s' Not Recognised \n\n" "$VM_Selection" 258 | sleep 1 259 | # clear any pressing of the enter key occuring during error message pause 260 | read -r -t 0.1 261 | read -r -t 0.1 262 | read -r -t 0.1 263 | read -r -t 0.1 264 | VM_Conf_Dir= 265 | VM_Conf_File= 266 | VM_Selection= 267 | 268 | else 269 | 270 | sleep 0.1 # gives background scans a little more time, if using the faster [enter] selection 271 | VM_Conf_Dir="${VM_Array[0,0]}" 272 | VM_Conf_File="${VM_Array[0,1]}" 273 | VM_Selection= 274 | VM_check_and_load 275 | return 276 | 277 | fi 278 | 279 | done 280 | 281 | fi 282 | 283 | } 284 | 285 | FD_help_notes () { 286 | printColor "\n\n Find Distro - Use multi-part cmd at the standard prompt:" 287 | printf "\n\n Contains fd c search-phrase" 288 | printf "\n\n Starts with fd s [a-z] or phrase" 289 | printColor "\n\n [enter] to continue \n\n" 290 | read -rp " > " 291 | } 292 | 293 | 294 | VM_selector_menu_refresh () { 295 | ByPass_VM_Array_Loader= 296 | ByPass_VM_Array_Selector= 297 | ListTitleFolderOld= 298 | InvertStyleColors= 299 | MenuStylePreviewed= 300 | VM_Conf_Dir= 301 | VM_Conf_File= 302 | VM_InstanceName= 303 | VM_Selection= 304 | } 305 | 306 | # https://code.visualstudio.com/ (recommended) 307 | 308 | # vim:tabstop=2:shiftwidth=2:expandtab 309 | 310 | ## 311 | -------------------------------------------------------------------------------- /qqX.main/qqX_VM_selector_styles: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ## qqX component to be located in folder 'qqX.main' 4 | 5 | ## Copyright (c) Alex Genovese https://github.com/TuxVinyards 6 | # SMALL CODE SNIPPETS eg the function printColor MAY BE USED 7 | # PERMISSIVELY in projects as MIT or similar, providing CLEAR ATTRIBUTIONS are shown. 8 | # Otherwise: 9 | # Licence GPL3 https://www.gnu.org/licenses 10 | ## qqX - quickemu quickget X terminal project 11 | 12 | ## ShellCheck global disables: 13 | # https://www.shellcheck.net/wiki/SC2242 as it clashes with use of exit traps used to keep mouse click scripts open 14 | # And SC1090,SC1091,SC2024, SC2154 for necessity of dynamic file sourcing 15 | 16 | # shellcheck disable=SC2242,SC1090,SC1091,SC2034,SC2154 17 | 18 | 19 | 20 | ## Variant styles of VM display & their common function units 21 | 22 | # For notes on custom vm menus see "Custom styles" at the end of this script 23 | 24 | vm_selector_init_common () { 25 | 26 | VM_ElementCount="${#VM_Array[@]}" 27 | VM_PairsCount="$((VM_ElementCount/2))" 28 | VM_Array_LastIndex="$((VM_PairsCount-1))" 29 | 30 | ZsyncDistrosFound= 31 | LiveDistrosFound= 32 | SymLinksPresent= 33 | MSR_DistrosFound= 34 | 35 | } 36 | 37 | function_vm_folder_item_text () { 38 | 39 | ArrayConf_Item="${VM_Array[$i,0]}/${VM_Array[$i,1]}" 40 | ArrayConf_Item_Listing="${VM_Array[$i,1]}" 41 | ArrayConf_Item_Listing="${ArrayConf_Item_Listing/'.conf'/}" 42 | 43 | [[ "$ArrayConf_Item_Listing" == 'windows'* ]] || [[ "$ArrayConf_Item_Listing" == 'macos'* ]] && MSR_DistrosFound=1 44 | 45 | # item max display length 46 | [[ ${#ArrayConf_Item_Listing} -gt "22" ]] && ArrayConf_Item_Listing="${ArrayConf_Item_Listing:0:22}" 47 | 48 | ## Decide on suffix (if needed) 49 | QcowTarget= 50 | QcowTarget="$(grep -s 'disk_img' "$ArrayConf_Item" )" 51 | QcowTarget="${QcowTarget/'disk_img='/}" 52 | QcowTarget="${QcowTarget//'"'/}" 53 | 54 | # Newly downloaded VMs will have a qcow disk path in the .conf file 55 | # BUT the disk won't be be there yet, not until quickemu has done a first run against the .conf 56 | 57 | if [[ $ArrayConf_Item_Listing == "empty" ]]; then ArrayConf_Item_Listing="EMPTY FOLDER or Bad Path" 58 | 59 | elif [[ "${VM_Array[$i,1]}" == 'ubuntu'* ]] && [[ "${VM_Array[$i,1]}" == *'daily'* ]]; then 60 | 61 | # Are Live VMs only, as zsync only updates the iso file. 62 | ArrayConf_Item_Listing+=" (Z)" 63 | ZsyncDistrosFound=1 64 | 65 | elif [[ ! -e "${VM_Array[$i,0]}/$QcowTarget" ]]; then ArrayConf_Item_Listing+=" (new)" 66 | 67 | elif [[ -h "$ArrayConf_Item" ]]; then ArrayConf_Item_Listing+=" (s)" ; SymLinksPresent=1 68 | 69 | elif [[ ! $(du -b -t 1000000 "${VM_Array[$i,0]}/$QcowTarget" 2>/dev/null) ]]; then 70 | 71 | # Live VM qcow2 disks are as little as only 196k. 72 | # But set test ceiling is set at 1,000,000k (1G) to allow for meta preallocation etc REVIEW 73 | # Live VMs only, in general, running off the iso file. 74 | ArrayConf_Item_Listing+=" (L)" 75 | LiveDistrosFound=1 76 | 77 | fi 78 | 79 | if [[ $FD_Phrase && ${ArrayConf_Item_Listing,,} == *"${FD_Phrase,,}"* ]] || [[ $FD_Start && ${ArrayConf_Item_Listing,,} == "${FD_Start,,}"* ]] ; then 80 | FD_Highlight=1 81 | else FD_Highlight= 82 | fi 83 | 84 | } 85 | 86 | print_selector_array_A_style () { 87 | 88 | i=0 89 | ListTitleFolder= 90 | VM_List_LineItemCount=0 91 | 92 | vm_selector_init_common 93 | [[ ! $VM_Select_Items_style_A_PerLine ]] && VM_Select_Items_style_A_PerLine="4" 94 | 95 | while [[ ${VM_Array[$i,0]} ]] ; do 96 | 97 | if [[ $ListTitleFolder != "${VM_Array[$i,0]}" ]]; then 98 | if [[ $ListTitleFolder ]]; then 99 | # won't print first time if value has been initialized as empty ... 100 | printf " >> %s\n\n\n " "$ListTitleFolder" 101 | VM_List_LineItemCount=0 102 | else printf "\n\n " 103 | fi 104 | ListTitleFolder="${VM_Array[$i,0]}" 105 | fi 106 | 107 | function_vm_folder_item_text 108 | 109 | if [[ $FD_Highlight ]]; then 110 | if [[ $i -gt 9 ]]; then printColor "{%d} " "$i" ; else printColor " {%d} " "$i" ; fi 111 | else 112 | if [[ $i -gt 9 ]]; then printf "[%d] " "$i" ; else printf " [%d] " "$i" ; fi 113 | fi 114 | if [[ $FD_Highlight ]]; then 115 | printf "%-29s" "${ArrayConf_Item_Listing^^}" 116 | else 117 | printColor "%-29s" "$ArrayConf_Item_Listing" 118 | fi 119 | ((VM_List_LineItemCount+=1)) 120 | 121 | # Move to new line when number of items per line reached (default 4, see main settings) 122 | if [[ $VM_List_LineItemCount == "$VM_Select_Items_style_A_PerLine" ]]; then 123 | VM_List_LineItemCount=0 124 | printf "\n\n " 125 | fi 126 | ((i+=1)) 127 | 128 | done 129 | 130 | [[ $i == "$VM_PairsCount" ]] && printf " >> %s" "$ListTitleFolder" 131 | printf "\n" 132 | 133 | } 134 | 135 | print_selector_array_B_style () { 136 | 137 | i=0 138 | VM_List_LineItemCount=0 139 | ListTitleFolder="${VM_Array[0,0]}" 140 | 141 | vm_selector_init_common 142 | [[ ! $VM_Select_Items_style_B_PerLine ]] && VM_Select_Items_style_B_PerLine="1" 143 | printf "\n " 144 | 145 | while [[ ${VM_Array[$i,0]} ]] ; do 146 | 147 | function_vm_folder_item_text 148 | 149 | if [[ $FD_Highlight ]]; then 150 | if [[ $i -gt 9 ]]; then printf "{%d} " "$i" ; else printf "{%d} " "$i" ; fi 151 | else 152 | if [[ $i -gt 9 ]]; then printf "[%d] " "$i" ; else printf "[%d] " "$i" ; fi 153 | fi 154 | if [[ $FD_Highlight ]]; then 155 | printf "%-30s" "${ArrayConf_Item_Listing^^}" 156 | else 157 | printColor "%-30s" "$ArrayConf_Item_Listing" 158 | fi 159 | 160 | # Move to new line when number of items per line reached (default 4, see main settings) 161 | if [[ $VM_List_LineItemCount == "$VM_Select_Items_style_B_PerLine" ]]; then VM_List_LineItemCount=0 162 | else ((VM_List_LineItemCount+=1)) 163 | fi 164 | ListTitleFolder="${VM_Array[$i,0]}" 165 | ((i+=1)) 166 | 167 | if [[ $ListTitleFolder != "${VM_Array[$i,0]}" ]]; then 168 | if [[ $VM_List_LineItemCount == "0" ]]; then Spaces=0 169 | else Spaces="$(((VM_Select_Items_style_B_PerLine+1)-VM_List_LineItemCount))" 170 | fi 171 | Spacer=" " # 35 172 | while [[ $Spaces -gt 0 ]]; do printf "%s" "$Spacer" ; ((Spaces-=1)) ; done 173 | printf " >> %s\n" "$ListTitleFolder" 174 | [[ $i != "$VM_PairsCount" ]] && printf "\n " 175 | VM_List_LineItemCount=0 176 | else [[ $VM_List_LineItemCount == 0 ]] && printf "\n " 177 | fi 178 | 179 | done 180 | 181 | } 182 | 183 | print_selector_array_C_style () { 184 | 185 | i=0 186 | VM_List_LineItemCount=1 187 | ListTitleFolder= 188 | ListTitleFolderOld= 189 | ListTitleFolderDisplay= 190 | 191 | if [[ $1 == "invert" ]]; then InvertStyleColors=1 ; else InvertStyleColors= ; fi 192 | vm_selector_init_common 193 | [[ ! $VM_Select_Items_style_C_PerLine ]] && VM_Select_Items_style_C_PerLine="3" 194 | 195 | while [[ ${VM_Array[$i,0]} ]] ; do 196 | 197 | function_vm_folder_item_text 198 | ListTitleFolder="${VM_Array[$i,0]}" 199 | 200 | if [[ $ListTitleFolder != "$ListTitleFolderOld" ]]; then 201 | 202 | if [[ ${#ListTitleFolder} -gt 28 ]]; then 203 | ListTitleFolderDisplay="${ListTitleFolder:(-28)}" 204 | ListTitleFolderDisplay="${ListTitleFolderDisplay#*/}" 205 | [[ "$ListTitleFolderDisplay" != '/'* ]] && ListTitleFolderDisplay="/$ListTitleFolderDisplay" 206 | else ListTitleFolderDisplay="$ListTitleFolder" 207 | fi 208 | 209 | [[ $ListTitleFolderOld ]] && printf "\n" 210 | 211 | if [[ $InvertStyleColors ]]; then printColor "\n\n %-35s" "$ListTitleFolderDisplay" 212 | else printf "\n\n %-35s" "$ListTitleFolderDisplay" 213 | fi 214 | VM_List_LineItemCount=1 215 | 216 | else 217 | 218 | if [[ $VM_List_LineItemCount == "$VM_Select_Items_style_C_PerLine" ]]; then 219 | printf "\n\n " ; VM_List_LineItemCount=0 220 | else ((VM_List_LineItemCount+=1)) 221 | fi 222 | 223 | fi 224 | 225 | if [[ $InvertStyleColors ]]; then 226 | 227 | if [[ $FD_Highlight ]]; then 228 | if [[ $i -le 9 ]]; then printColor "{%d} " "$i" ; printColor "%-30s" "${ArrayConf_Item_Listing^^}" 229 | else printColor "{%d} " "$i" ; printColor "%-30s" "${ArrayConf_Item_Listing^^}" 230 | fi 231 | else 232 | if [[ $i -le 9 ]]; then printf "[%d] " "$i" ; printf "%-30s" "$ArrayConf_Item_Listing" 233 | else printf "[%d] " "$i" ; printf "%-30s" "$ArrayConf_Item_Listing" 234 | fi 235 | fi 236 | 237 | else 238 | 239 | if [[ $FD_Highlight ]]; then 240 | if [[ $i -le 9 ]]; then printf "{%d} " "$i" ; printf "%-30s" "${ArrayConf_Item_Listing^^}" 241 | else printf "{%d} " "$i" ; printf "%-30s" "${ArrayConf_Item_Listing^^}" 242 | fi 243 | else 244 | if [[ $i -le 9 ]]; then printf "[%d] " "$i" ; printColor "%-30s" "$ArrayConf_Item_Listing" 245 | else printf "[%d] " "$i" ; printColor "%-30s" "$ArrayConf_Item_Listing" 246 | fi 247 | fi 248 | 249 | fi 250 | 251 | ListTitleFolderOld="${VM_Array[$i,0]}" 252 | ((i+=1)) 253 | 254 | done 255 | 256 | printf "\n" 257 | 258 | } 259 | 260 | print_selector_array_D_style () { print_selector_array_C_style invert ; } 261 | 262 | print_selector_array_E_style () { 263 | 264 | i=0 265 | VM_List_LineItemCount=1 266 | ListTitleFolder= 267 | ListTitleFolderOld= 268 | ListTitleFolderDisplay= 269 | 270 | vm_selector_init_common 271 | [[ ! $VM_Select_Items_style_E_PerLine ]] && VM_Select_Items_style_E_PerLine="2" 272 | 273 | while [[ ${VM_Array[$i,0]} ]] ; do 274 | 275 | function_vm_folder_item_text 276 | ListTitleFolder="${VM_Array[$i,0]}" 277 | 278 | if [[ $ListTitleFolder != "$ListTitleFolderOld" ]]; then 279 | 280 | # sneak in the new folder name & nudge up the line item count before printing the VM item 281 | if [[ ${#ListTitleFolder} -gt 28 ]]; then 282 | ListTitleFolderDisplay="${ListTitleFolder:(-28)}" 283 | ListTitleFolderDisplay="${ListTitleFolderDisplay#*/}" 284 | [[ "$ListTitleFolderDisplay" != '/'* ]] && ListTitleFolderDisplay="/$ListTitleFolderDisplay" 285 | else ListTitleFolderDisplay="$ListTitleFolder" 286 | fi 287 | 288 | [[ $ListTitleFolderOld ]] && printf "\n" 289 | if [[ $FD_Highlight ]]; then 290 | printf "\n %-35s" "${ListTitleFolderDisplay^^}" 291 | else 292 | printColor "\n %-35s" "$ListTitleFolderDisplay" 293 | fi 294 | VM_List_LineItemCount=1 295 | 296 | else 297 | 298 | # print the item, but move to new line first if needed 299 | if [[ $VM_List_LineItemCount == "$VM_Select_Items_style_E_PerLine" ]]; then 300 | if [[ $FD_Highlight ]]; then printColor "\n %-35s" " " 301 | else printf "\n %-35s" " " 302 | fi 303 | VM_List_LineItemCount=1 304 | else ((VM_List_LineItemCount+=1)) 305 | fi 306 | 307 | fi 308 | 309 | if [[ $FD_Highlight ]]; then 310 | if [[ $i -le 9 ]]; then printf "{%d} " "$i" ; printColor "%-30s" "${ArrayConf_Item_Listing^^}" 311 | else printf "{%d} " "$i" ; printColor "%-30s" "${ArrayConf_Item_Listing^^}" 312 | fi 313 | else 314 | if [[ $i -le 9 ]]; then printf "[%d] " "$i" ; printf "%-30s" "$ArrayConf_Item_Listing" 315 | else printf "[%d] " "$i" ; printf "%-30s" "$ArrayConf_Item_Listing" 316 | fi 317 | fi 318 | ListTitleFolderOld="${VM_Array[$i,0]}" 319 | ((i+=1)) 320 | 321 | done 322 | printf "\n" 323 | 324 | } 325 | 326 | ## Custom styles 327 | 328 | if [[ -e "$Default_VM_Folder/qqX.custom/custom_vm_selectors" ]]; then source "$Default_VM_Folder/qqX.custom/custom_vm_selectors" ; fi 329 | 330 | # Method: 331 | # Create plain text folder & file 'custom_vm_selectors' as above. Add "#!/usr/bin/env bash" to line 1. 332 | # Use an existing style to create a template. This is the easiest way. 333 | # Copy and paste any of the style functions into the new file. 334 | # Only the needed style function has to be copied but more than one function may be added. 335 | # Use style letters from end of alphabet eg R to Z Letters must be CAPS but sequence may be non-contiguous. 336 | 337 | 338 | -------------------------------------------------------------------------------- /qqX.main/qqX_filemanage_qcow_drive: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ## qqX component to be located in folder 'qqX.main' 4 | 5 | # NB Avoid use of "#!/usr/bin/env bash" in this script 6 | # as it will mask process names & inhibit process controls. 7 | 8 | ## Copyright (c) Alex Genovese https://github.com/TuxVinyards 9 | # SMALL CODE SNIPPETS eg the function printColor MAY BE USED 10 | # PERMISSIVELY in projects as MIT or similar, providing CLEAR ATTRIBUTIONS are shown. 11 | # Otherwise: 12 | # Licence GPL3 https://www.gnu.org/licenses 13 | ## qqX - quickemu quickget X terminal project 14 | 15 | ## ShellCheck global disables: 16 | # https://www.shellcheck.net/wiki/SC2242 as it clashes with use of exit traps used to keep mouse click scripts open 17 | # And SC1090,SC1091,SC2024, SC2154 for necessity of dynamic file sourcing 18 | 19 | # shellcheck disable=SC2242,SC1090,SC1091,SC2034,SC2154,SC2009 20 | 21 | # MOUNTS and UNMOUNTS the Shared Drive, or other, if present, in the designated HOST File Manager 22 | # To be run in a separate terminal to ensure qqX main does NOT get elevated permissions 23 | 24 | # A CUSTOM VERSION for this file MAY BE CREATED, if wished, and placed next to this one 25 | # at/as "$qqX_CustomFolder/custom_filemanage_qcow_drive" which will be auto-detected 26 | # and run by function 'qcow2_filemanagement_launcher' in the main qqX program in the disk maintenance script 27 | 28 | tput civis 29 | 30 | # Avoid 'kworker' grep errors, make sure that all current processes have been statted properly before checking 31 | sleep 0.4 32 | 33 | # Sharded generic standalone running of qcow2 file management utility 34 | if [[ $1 == "--qqf" ]]; then qqf_CmdLineStart=1 ; shift 35 | else qqf_CmdLineStart= 36 | fi 37 | 38 | source "/tmp/qqX_nbd_vars" 39 | source "$qqX_MainFolder/qqX_UI_chrome_up" 40 | 41 | if [[ $Trace_InputVars ]]; then echo "Args: $*" ; echo 42 | fi 43 | 44 | if [[ -e "$1" ]]; then qqf_Input_Drivepath="$1" ; shift 45 | fi 46 | 47 | if [[ $Trace_InputVars ]]; then echo "Input Path: $qqf_Input_Drivepath" ; echo ; sleep "$Trace_InputVars" 48 | fi 49 | 50 | if [[ ! $(type -p "$FileManager") ]] ; then 51 | printf "\n\n\n ERROR: File Manager '%s' NOT found.\n" "$FileManager" ; sleep 2 52 | fi 53 | 54 | # One control window per VM. Auto-closes others if new one opened. Avoids confusion. 55 | # New PID always goes on end of list but pgrep sub-shell creates another. 56 | 57 | # Note: kde/dolphin/konsole opens two PIDs - one for the terminal and one for the script 58 | # and gnome based terminals open just one and with a 'bash' nomination. 59 | # Also see function 'multi_instance_checks' in 'UI_chrome_up' ... 60 | 61 | if pgrep -af qqX_filemanage | grep -q "$UserTerm" ; then 62 | ThisPID="$(pgrep -n "$UserTerm")" 63 | while [[ "$(pgrep -af qqX_filemanage)" ]] ; do 64 | ChkPID="$(pgrep -af qqX_filemanage | grep "$UserTerm" | cut -d' ' -f1 | head -n 1)" 65 | if [[ $ChkPID == "$ThisPID" ]] ; then break ; else kill "$ChkPID" 2>/dev/null ; fi 66 | done 67 | else 68 | ThisPID="$(pgrep -af qqX_filemanage | cut -d' ' -f1 | tail -n 2 | head -n 1)" 69 | while [[ "$(pgrep -af qqX_filemanage)" ]] ; do 70 | ChkPID="$(pgrep -af qqX_filemanage | cut -d' ' -f1 | head -n 1)" 71 | if [[ $ChkPID == "$ThisPID" ]] ; then break ; else kill "$ChkPID" 2>/dev/null ; fi 72 | done 73 | fi 74 | 75 | show_nbd_help () { 76 | printf "\n\n Drives from different VM's may be connected at the same time." 77 | printf "\n Only directly linked drives need to be disconnected before booting." 78 | 79 | printf "\n\n For drives that only have root access and that your file manager won't change," 80 | printf "\n the following commands can help give general user permissions:" 81 | printf "\n\n" 82 | # shellcheck disable=SC2016 83 | echo ' sudo chown $USER:$USER "mounted-drive-name"' 84 | echo ' chmod 775 "mounted-drive-name"' 85 | 86 | printColor "\n\n Script name and location: %s" "$0" 87 | printf "\n\n Different distro NBD and file-manager behaviours may possibly occur." 88 | printf "\n View script for notes on creating Custom versions or for details of its functions" 89 | [[ $TextEditor ]] && printf "\n\n [H] View script in Text Editor" 90 | printf "\n" 91 | } 92 | 93 | check_sudo_nbd_active () { 94 | printf "\n\n"; printf "\e[3A\r" 95 | # https://superuser.com/a/1183480/2243702 96 | if ! sudo -nv 2>/dev/null ; then sudo -v ; printf "\n" 97 | fi 98 | if ! lsmod | grep -q nbd ; then sudo modprobe nbd max_part=8 nbds_max=4 99 | fi 100 | } 101 | 102 | load_qf_defaults () { 103 | 104 | if [[ $MainDrive ]]; then MainPathName="$VM_Conf_Dir/$MainDrive" ; fi 105 | if [[ -e "$MainPathName" ]] && [[ ! $LiveBoot ]]; then MainAvailable=1 ; else MainAvailable= ; fi 106 | 107 | if [[ $SecondDrive ]]; then SecondPathName="$VM_Conf_Dir/$SecondDrive" ; fi 108 | if [[ -e "$SecondPathName" ]]; then SecondAvailable=1 109 | if [[ ! $(du -b -t 1000000 "$SecondPathName" 2>/dev/null) ]]; then Empty2nd=1 ; else Empty2nd= ; fi 110 | else SecondAvailable= 111 | fi 112 | SharedPathName="$SharedDrive" 113 | if [[ -e "$SharedPathName" ]]; then SharedAvailable=1 114 | if [[ ! $(du -b -t 1000000 "$SharedPathName" 2>/dev/null) ]]; then EmptyShared=1 ; else EmptyShared= ; fi 115 | else SharedAvailable= 116 | fi 117 | if [[ "$qqf_Input_Drivepath" ]]; then InputAvailable=1 118 | if [[ ! $(du -b -t 1000000 "$qqf_Input_Drivepath" 2>/dev/null) ]]; then EmptyInput=1 ; else EmptyInput= ; fi 119 | else InputAvailable= 120 | fi 121 | 122 | if [[ $InputAvailable ]]; then 123 | TargetDrive="$qqf_Input_Drivepath" 124 | TargetPathName="$qqf_Input_Drivepath" 125 | TargetName="Input Drive" 126 | TargetDevice="/dev/nbd0" 127 | TargetAvailable=1 128 | else 129 | # defaults 130 | TargetDrive="$SharedDrive" 131 | TargetPathName="$SharedDrive" 132 | TargetName="Shared Drive" 133 | TargetDevice="/dev/nbd3" 134 | if [[ $SharedAvailable ]]; then TargetAvailable=1 ; else TargetAvailable= ; fi 135 | fi 136 | } 137 | 138 | load_qf_defaults 139 | 140 | # LOOP 141 | 142 | while true; do 143 | 144 | printf "\033c" 145 | qqX_logo_printf_title "File Manager Utility for mounting/unmounting and for connecting QCOW2 drives" 146 | [[ "$(basename "$0")" == custom* ]] && printColor " - custom version" 147 | printf "\n" 148 | printf "\n\n This terminal window is PROCESS SEPARATED from qqX and may be LEFT OPEN, repositioned or minimized" 149 | printf "\n\n The elevated permissions required to prepare the drives will remain isolated." 150 | printf "\n\n Any opening of new qqF windows will terminate other qqF instances that are running." 151 | printf "\n" 152 | printf "\n\n Run option 'qqf' from the qqX menu if the VM is swapped. Follow with [r] to refresh" 153 | printf "\n\n Mounting of the connected drives is to be carried out as standard in the file-manager." 154 | printf "\n" 155 | 156 | if [[ ! $HideRefreshMsgs ]] && [[ $qqf_CmdLineStart ]]; then 157 | printColor "\n\n qqF - Cmd Line Start > " 158 | printf " Use option 'qqf' from a selected qqX VM menu to populate the VM drives." 159 | printf "\n\n For spot input, use a right mouse click on the disk or at a new terminal type qqX \"/path/disk_name.qcow2\"" 160 | printf "\n" 161 | fi 162 | if [[ ! $HideRefreshMsgs ]] && [[ $InputAvailable ]] && [[ ! $qqf_CmdLineStart ]]; then 163 | printColor "\n\n qqF - Spot Input > " 164 | printf " Use option 'qqf' from a selected qqX VM menu to populate the VM drives." 165 | printf "\n" 166 | fi 167 | 168 | [[ $ShowHelp ]] && show_nbd_help && ShowHelp= 169 | 170 | qf_DriveSelect= 171 | 172 | [[ $VM_InstanceName ]] && printColor "\n\n %s " "${VM_InstanceName^^}" 173 | 174 | printf "\n\n Available Drive(s): " 175 | if [[ "$MainAvailable" ]]; then printf " Main " ; fi 176 | if [[ "$SecondPathName" ]]; then printf " Second " ; fi 177 | if [[ "$SharedPathName" ]]; then printf " Shared " ; fi 178 | if [[ "$qqf_Input_Drivepath" ]]; then printf " Input " ; fi 179 | 180 | printf "\n\n Connected Drive(s): " 181 | if ps -ef | grep nbd1 | grep -q qcow; then printf " Main " ; MainConnected=1 ; else MainConnected= ; fi 182 | if ps -ef | grep nbd2 | grep -q qcow; then printf " Second " ; SecondConnected=1 ; else SecondConnected= ; fi 183 | if ps -ef | grep nbd3 | grep -q qcow; then printf " Shared " ; SharedConnected=1 ; else SharedConnected= ; fi 184 | if ps -ef | grep nbd0 | grep -q qcow; then printf " Input " ; InputConnected=1 ; else InputConnected= ; fi 185 | 186 | printf "\n\n Mounted Drive(s): " 187 | if mount -fl | grep -q '/dev/nbd1'; then printf " Main "; MainMountPoint="$(mount -fl | grep '/dev/nbd1' | awk '{print $3}')" ; fi 188 | if mount -fl | grep -q '/dev/nbd2'; then printf " Second "; SecondMountPoint="$(mount -fl | grep '/dev/nbd2' | awk '{print $3}')" ; fi 189 | if mount -fl | grep -q '/dev/nbd3'; then printf " Shared "; SharedMountPoint="$(mount -fl | grep '/dev/nbd3' | awk '{print $3}')" ; fi 190 | if mount -fl | grep -q '/dev/nbd0'; then printf " Input "; InputMountPoint="$(mount -fl | grep '/dev/nbd0' | awk '{print $3}')" ; fi 191 | 192 | if [[ $MainConnected ]]; then 193 | MainGreppedPathName="$(ps -ef | grep nbd1 | grep qcow | awk '{print $NF}')" 194 | if [[ $MainGreppedPathName ]] && [[ "$MainGreppedPathName" != "$MainPathName" ]]; then 195 | printf "\n\n NOTE: Main connects to %s" "$(awk -F "/" '{print $(NF-1)}' <<< "$MainGreppedPathName")" 196 | MainCrossConnected=1 197 | else MainCrossConnected= 198 | fi 199 | else MainCrossConnected= 200 | fi 201 | if [[ $SecondConnected ]]; then 202 | SecondGreppedPathName="$(ps -ef | grep nbd2 | grep qcow | awk '{print $NF}')" 203 | if [[ $SecondGreppedPathName ]] && [[ "$SecondGreppedPathName" != "$SecondPathName" ]]; then 204 | printf "\n\n NOTE: Second connects to %s" "$(awk -F "/" '{print $(NF-1)}' <<< "$SecondGreppedPathName")" 205 | SecondCrossConnected=1 206 | else SecondCrossConnected= 207 | fi 208 | else SecondCrossConnected= 209 | fi 210 | if [[ $qqf_Input_Drivepath ]] && [[ $TargetName != "Input Drive" ]]; then 211 | printf "\n\n Input Drive: %s" "$qqf_Input_Drivepath" 212 | fi 213 | if [[ $InputConnected ]]; then 214 | InputGreppedPathName="$(ps -ef | grep nbd0 | grep qcow | awk '{print $NF}')" 215 | if [[ $InputGreppedPathName ]] && [[ "$InputGreppedPathName" != "$qqf_Input_Drivepath" ]]; then 216 | printColor "\n\n NOTE: The current connection for Input Drive is to %s" "$(awk -F "/" '{print $(NF-1)}' <<< "$InputGreppedPathName")" 217 | InputCrossConnected=1 218 | else InputCrossConnected= 219 | fi 220 | else InputCrossConnected= 221 | fi 222 | 223 | 224 | printf "\n\n\n [1] Main Drive [2] Second Drive [3] Shared Drive [i] Input Drive [A] All Drives [a] all Non-Shared Drives " 225 | 226 | printf "\n\n See main utilities for general disk tools and for disk creation" 227 | 228 | printColor "\n\n Selected = %s " "$TargetName" 229 | 230 | if [[ $TargetName != All* ]]; then 231 | if [[ $TargetAvailable ]]; then printf " '%s'" "$TargetDrive" ; else printf " UNAVAILABLE or NOT LINKED to this VM" ; fi 232 | fi 233 | if [[ $TargetName == "Main Drive" ]]; then 234 | [[ $LiveBoot ]] && [[ -e "$MainPathName" ]] && printf " EMPTY DRIVE (live boot)" 235 | [[ $MainCrossConnected ]] && printf "\n\n CAUTION: Cross Linkage potentials > [enter] to disconnect ?" 236 | fi 237 | if [[ $TargetName == "Second Drive" ]]; then 238 | [[ $Empty2nd ]] && [[ -e "$SecondPathName" ]] && printf " EMPTY DRIVE (needs partioning?)" 239 | [[ $SecondCrossConnected ]] && printf "\n\n CAUTION: Cross Linkage potentials > [enter] to disconnect ?" 240 | fi 241 | if [[ $TargetName == "Shared Drive" ]]; then 242 | [[ $EmptyShared ]] && [[ -e "$SharedPathName" ]] && printf " EMPTY DRIVE (needs partioning?)" 243 | fi 244 | if [[ $TargetName == "Input Drive" ]]; then 245 | [[ $EmptyInput ]] && [[ -e "$qqf_Input_Drivepath" ]] && printf " EMPTY DRIVE (needs partioning?)" 246 | fi 247 | 248 | printColor "\n\n\n [enter] to change %s connections or [option] for different selection" "$TargetName" 249 | printf "\n\n [r] to refresh vars [f] open drive in file-manager [h] help [q] to quit " 250 | 251 | printf "\n\n" 252 | read -rp " > " qf_DriveSelect 253 | 254 | if [[ $qf_DriveSelect == "1" ]]; then 255 | TargetDrive="$MainDrive" ; TargetPathName="$MainPathName" ; TargetName="Main Drive" ; TargetDevice="/dev/nbd1" ; TargetAvailable="$MainAvailable" 256 | elif [[ $qf_DriveSelect == "2" ]]; then 257 | TargetDrive="$SecondDrive" ; TargetPathName="$SecondPathName" ; TargetName="Second Drive"; TargetDevice="/dev/nbd2" ; TargetAvailable="$SecondAvailable" 258 | elif [[ $qf_DriveSelect == "3" ]]; then 259 | TargetDrive="$SharedDrive" ; TargetPathName="$SharedDrive" ; TargetName="Shared Drive"; TargetDevice="/dev/nbd3" ; TargetAvailable="$SharedAvailable" 260 | elif [[ $qf_DriveSelect == "i" ]]; then 261 | TargetDrive="$qqf_Input_Drivepath" ; TargetPathName="$qqf_Input_Drivepath" 262 | TargetName="Input Drive"; TargetDevice="/dev/nbd0" ; TargetAvailable="$InputAvailable" 263 | elif [[ $qf_DriveSelect == "A" ]]; then TargetName="All Drives" 264 | elif [[ $qf_DriveSelect == "a" ]]; then TargetName="All Non-Shared" 265 | elif [[ $qf_DriveSelect == "h" ]]; then ShowHelp=1 266 | elif [[ $qf_DriveSelect == "H" ]]; then (nohup &> /dev/null "$TextEditor" "$0" & ) || texteditor_not_found_message 267 | elif [[ $qf_DriveSelect == "f" ]]; then 268 | (nohup &> /dev/null "$FileManager" "$VM_Conf_Dir" & ) || file_manager_not_found_message 269 | elif [[ $qf_DriveSelect == "r" ]]; then 270 | if [[ $qqf_Input_Drivepath ]]; then 271 | printf "\n qqf_Input_Drivepath=\"%s\" \n" "$qqf_Input_Drivepath" >> "/tmp/qqX_nbd_vars" 272 | fi 273 | source "/tmp/qqX_nbd_vars" ; load_qf_defaults ; HideRefreshMsgs=1 274 | elif [[ $qf_DriveSelect == "q" ]]; then exit directly 275 | else 276 | 277 | printf "\n\n %s: " "$TargetName" 278 | printColor "\n\n [c] connect [d] disconnect current and unmount [enter] [r] to reselect or return" 279 | printf "\n\n Connected drives may be mounted as standard in the file-manager" 280 | printf "\n\n External formats are not always readable using the ndb method ..." # REVIEW is a convertion possible ? 281 | printf "\n\n" 282 | read -rp " > " qf_DriveSelect 283 | printf "\n\n" 284 | 285 | if [[ $qf_DriveSelect ]] && [[ $qf_DriveSelect != "r" ]]; then 286 | 287 | if [[ $qf_DriveSelect == "c" || $qf_DriveSelect == "d" ]]; then 288 | check_sudo_nbd_active 289 | if [[ ! $(sudo modinfo -n nbd 2>/dev/null) ]] ; then 290 | printf "\n\n Kernel module WARNING: 'nbd' NOT found. But may be present. " 291 | printf "\n\n Try 'modinfo -n nbd' at a terminal & Check your distro repositories for 'linux-modules' ... " 292 | fi 293 | printf "checking mounts ... \n\n" 294 | if [[ "$TargetName" == All* ]]; then 295 | [[ $MainMountPoint ]] && umount "$MainMountPoint" 296 | [[ $SecondMountPoint ]] && umount "$SecondMountPoint" 297 | [[ $InputMountPoint ]] && umount "$InputMountPoint" 298 | if [[ $TargetName == "All Drives" ]]; then 299 | [[ $SharedMountPoint ]] && umount "$SharedMountPoint" 300 | fi 301 | else 302 | TargetMountPoint="$(mount -fl | grep "$TargetDevice" | awk '{print $3}')" 303 | [[ $TargetMountPoint ]] && umount "$TargetMountPoint" 304 | fi 305 | fi 306 | 307 | # Qcow2 mount is a more complex procedure > use File Manager to do it as much easier 308 | # But drive must be un-mounted in order to connect, which happily is relatively easy 309 | 310 | if [[ $qf_DriveSelect == "d" ]]; then 311 | printf "qemu-nbd disconnecting \n\n" 312 | if [[ "$TargetName" == All* ]]; then 313 | sudo qemu-nbd -d "/dev/nbd0" 314 | sudo qemu-nbd -d "/dev/nbd1" 315 | sudo qemu-nbd -d "/dev/nbd2" 316 | if [[ $TargetName == "All Drives" ]]; then 317 | sudo qemu-nbd -d "/dev/nbd3" 318 | fi 319 | else 320 | sudo qemu-nbd -d "$TargetDevice" 321 | fi 322 | 323 | elif [[ $qf_DriveSelect == "c" ]]; then 324 | printf "qemu-nbd connecting \n\n" 325 | 326 | if [[ "$TargetName" == All* ]]; then 327 | [[ $InputAvailable ]] && sudo qemu-nbd -c "/dev/nbd0" -f qcow2 "$qqf_Input_Drivepath" 328 | [[ $MainAvailable ]] && sudo qemu-nbd -c "/dev/nbd1" -f qcow2 "$MainPathName" 329 | [[ $SecondAvailable ]] && sudo qemu-nbd -c "/dev/nbd2" -f qcow2 "$SecondPathName" 330 | if [[ $TargetName == "All Drives" ]]; then 331 | [[ $SharedAvailable ]] && sudo qemu-nbd -c "/dev/nbd3" -f qcow2 "$SharedPathName" 332 | fi 333 | else 334 | [[ $TargetAvailable ]] && sudo qemu-nbd -c "$TargetDevice" -f qcow2 "$TargetPathName" 335 | fi 336 | 337 | fi 338 | 339 | printColor "\n [enter] to return \n\n" 340 | read -rp " > " 341 | 342 | fi 343 | 344 | fi 345 | 346 | done 347 | 348 | # https://stackoverflow.com/questions/17921544/get-last-field-using-awk-substr#17921589 349 | # https://unix.stackexchange.com/questions/145672/print-last-element-of-each-row 350 | # https://stackoverflow.com/questions/2961635/using-awk-to-print-all-columns-from-the-nth-to-the-last#2961994 351 | 352 | # https://code.visualstudio.com/ (recommended) 353 | # vim:tabstop=2:shiftwidth=2:expandtab 354 | 355 | -------------------------------------------------------------------------------- /qqX.main/qqX_input_and_mouse_tidyup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ## qqX component to be located in folder 'qqX.main' 4 | 5 | ## Copyright (c) Alex Genovese https://github.com/TuxVinyards 6 | # SMALL CODE SNIPPETS eg the function printColor MAY BE USED 7 | # PERMISSIVELY in projects as MIT or similar, providing CLEAR ATTRIBUTIONS are shown. 8 | # Otherwise: 9 | # Licence GPL3 https://www.gnu.org/licenses 10 | ## qqX - quickemu quickget X terminal project 11 | 12 | ## ShellCheck global disables: 13 | # https://www.shellcheck.net/wiki/SC2242 as it clashes with use of exit traps used to keep mouse click scripts open 14 | # And SC1090,SC1091,SC2024, SC2154 for necessity of dynamic file sourcing 15 | 16 | # shellcheck disable=SC2242,SC1090,SC1091,SC2034,SC2154,SC2009 17 | 18 | # Sorts out any MOUSE CLICK or COMMAND LINE parameters that have been initially read by 'qqX_initialize' 19 | # This script is then CALLED using the flag 'Initial_Input' when input is present by the qqX main script 20 | # AFTER all the other scripts, files and functions have been sourced but BEFORE starting the qqX main loop. 21 | 22 | if [[ $Trace_InputVars ]]; then echo; echo "pwd $(pwd)"; echo ; echo "1 $Initial_Input" ; sleep "$Trace_InputVars" ; fi 23 | 24 | if [[ $XDG_MimeType_is_QCOW ]]; then 25 | # should have changed to the right directory at this point (also see initialze script for .conf checks) 26 | if grep -qs "disk_img="'"'"$QCOW_Input_VM_Dir_Name/$QCOW_Input_DiskName" "$QCOW_Input_Dir.conf" ; then 27 | VM_Conf_Dir="$QCOW_Input_Conf_Dir" 28 | VM_Conf_File="$QCOW_Input_VM_Dir_Name.conf" 29 | VM_InstanceName="$QCOW_Input_VM_Dir_Name" 30 | XDG_Input_NON_qqX= 31 | XDG_Input_VM_qqX=1 32 | shift 33 | else 34 | XDG_Input_NON_qqX=1 ; XDG_Input_VM_qqX= ; shift 35 | fi 36 | fi 37 | 38 | if [[ $Trace_InputVars ]]; then 39 | echo; echo "qqX VM = $XDG_Input_VM_qqX I = $VM_InstanceName" ; echo "conf = $VM_Conf_File" ; sleep "$Trace_InputVars" 40 | echo "VM_qqX = $XDG_Input_VM_qqX NON_qqX = $XDG_Input_NON_qqX " 41 | echo "pwd $(pwd)" ; echo; sleep "$Trace_InputVars" 42 | fi 43 | 44 | input_re_route () { 45 | if [[ $xxXDG_MimeType_is_QCOW ]] && [[ "$(pgrep -af qqX_filemanage 2>/dev/null)" ]]; then 46 | printColor "\n\n Note that opening of new qqF windows will terminate other qqF instances that are running" 47 | fi 48 | printf "\n\n [enter] to run qqX " 49 | [[ $XDG_MimeType_is_QCOW ]] && printf "[qf] open qcow2 in file manager " 50 | printf "[c] to close \n\n" 51 | read -rp " > " XDG_StartVariant 52 | 53 | if [[ $XDG_StartVariant == "qf" ]]; then 54 | InputTidy="qf" 55 | XDG_StartVariant= 56 | cd "$QCOW_Input_Dir" || printf "\n\n XDG .desktop starter > Disk's start Folder not found " 57 | Current_VM_Folder="$QCOW_Input_Dir" 58 | if [[ $Trace_InputVars ]]; then 59 | echo; echo "qqX VM = $XDG_Input_VM_qqX I = $VM_InstanceName" ; echo "conf = $VM_Conf_File" ; sleep "$Trace_InputVars" 60 | echo "VM_qqX = $XDG_Input_VM_qqX NON_qqX = $XDG_Input_NON_qqX " 61 | echo "pwd $(pwd)" ; echo; sleep "$Trace_InputVars" 62 | fi 63 | elif [[ $XDG_StartVariant != "c" ]]; then 64 | VM_InstanceName= 65 | VM_Conf_Dir= 66 | VM_Conf_File= 67 | MainDrive= 68 | IsoImg= 69 | XDG_MimeType_is_QCOW= 70 | refresh_sources_and_load_VM_arrays 71 | else exit directly 72 | fi 73 | } 74 | 75 | if [[ $XDG_StartVariant ]] && [[ ! $XDG_MimeType_Recognised ]] && [[ ! $XDG_FileType_Recognised ]]; then 76 | if [[ $XDG_StartVariant ]]; then printColor "\n\n %s not understood \n\n Or non valid file type %s " "$Initial_Input" "$XDG_MimeType" 77 | else show_CLI_usage 78 | fi 79 | InputReRouted=1 80 | input_re_route 81 | fi 82 | 83 | if [[ $XDG_MimeType_is_QCOW ]]; then 84 | 85 | if ps -ef | grep -q nbd0 ; then DiskChkStatus="Okay" # as has already been checked, and where nbd0 is reserved for spot input drives 86 | else 87 | check_disk_is_okay "$QCOW_Input_Dir" "$QCOW_Input_DiskName" 88 | fi 89 | if [[ $DiskChkStatus ]] && [[ $DiskChkStatus != "Okay" ]]; then printColor "\n\n DISK.QCOW2 ERROR %s \n" "$DiskChkStatus" 90 | fi 91 | 92 | if [[ $XDG_Input_NON_qqX ]]; then 93 | if [[ $DiskChkStatus == "Okay" ]]; then printColor "\n\n QEMU qcow2 disk found BUT not configured as qqX/quickemu bootable: \n\n" 94 | else printColor "\n\n QEMU qcow2 disk found \n\n BUT not configured as qqX/quickemu bootable \n\n AND has ERRORS .... \n\n" 95 | fi 96 | else 97 | show_qqX_title_bar 98 | printColor "\n\n qqX/quickemu bootable: \n\n" 99 | if [[ $DiskChkStatus != "Okay" ]]; then 100 | printColor " BUT has errors: Try qqX [disk] utils " 101 | printf "\n\n Also see https://qemu.readthedocs.io " 102 | printf "> QEMU disk image utility notes\n\n" 103 | fi 104 | fi 105 | 106 | # show disk info: 107 | "$QEMU_IMG" info "$QCOW_Input_Dir/$QCOW_Input_DiskName" 108 | 109 | if [[ $XDG_MimeType_is_QCOW ]] && [[ "$(pgrep -af qqX_filemanage 2>/dev/null)" ]]; then 110 | printColor "\n\n Note that opening of new qqF windows will terminate other qqF instances that are running" 111 | fi 112 | 113 | if [[ $XDG_Input_NON_qqX ]]; then 114 | printColor "\n\n See https://qemu.readthedocs.io " 115 | printf "> QEMU's disk image utility or the qf/qqF drive mounter may be useful" 116 | InputReRouted=1 117 | input_re_route 118 | else 119 | printColor "\n\n [enter] to continue [qf] open qcow2 in file manager [c] to cancel and close \n\n" 120 | read -rp " > " InputTidy 121 | [[ $InputTidy == "c" ]] && exit directly 122 | fi 123 | 124 | if [[ $XDG_Input_VM_qqX ]]; then 125 | ByPass_VM_Array_Selector=1 126 | if [[ ! $VM_Conf_Dir ]]; then VM_Conf_Dir="$Current_VM_Folder" 127 | else 128 | if [[ ! -d "$VM_Conf_Dir" ]]; then 129 | printColor "\n\n Directory: %s not found " "$VM_Conf_Dir " 130 | function_conf_error " COMMAND LINE Path Instruction," 131 | else 132 | cd "$VM_Conf_Dir" || function_conf_error " COMMAND LINE Path Instruction," 133 | Current_VM_Folder="$VM_Conf_Dir" 134 | fi 135 | fi 136 | if [[ ! -f "$VM_Conf_Dir/$VM_Conf_File" ]]; then 137 | printColor "\n\n Conf: %s not found " "$VM_Conf_File" 138 | function_conf_error " COMMAND LINE .Conf Instruction," 139 | fi 140 | elif [[ $InputReRouted ]]; then true 141 | else exit directly 142 | fi 143 | 144 | if [[ $InputTidy == "qf" ]]; then 145 | qqf_Input_Drivepath="$(realpath "$QCOW_Input_Dir/$QCOW_Input_DiskName")" 146 | cd "$QCOW_Input_Dir" || printf "\n\n XDG .desktop starter > Disk's start Folder not found " 147 | Current_VM_Folder="$QCOW_Input_Dir" 148 | if [[ $Trace_InputVars ]]; then 149 | echo; echo "Tidy requested Path: $qqf_Input_Drivepath" 150 | echo "QCOW_Input_Dir: $QCOW_Input_Dir" 151 | echo "QCOW_Input_Conf_Dir: $QCOW_Input_Conf_Dir" 152 | echo "QCOW_Input_DiskName: $QCOW_Input_DiskName" 153 | echo "pwd $(pwd)" 154 | echo ; sleep "$Trace_InputVars" 155 | read -rp " [enter] to continue > " 156 | fi 157 | qcow2_filemanagement_launcher "$qqf_Input_Drivepath" 158 | sleep 2 159 | exit directly 160 | else 161 | # not qf, and not close, so re-check but with qqX_Main flag 162 | QF_exit_to_Main=1 163 | fi 164 | 165 | fi 166 | 167 | 168 | if [[ $XDG_MimeType_is_ISO || $XDG_ISO_Name ]] && [[ $XDG_MimeType_Recognised || $XDG_FileType_Recognised ]]; then 169 | refresh_sources_and_load_VM_arrays 170 | while true; do 171 | New_ISO_Start= 172 | Cancel_ISO= 173 | ISO_NewVM_Name= 174 | ISO_OvWr= 175 | ISO_NewFolder= 176 | MoveIso= 177 | printColor "\n\n Set up this ISO as a qqX/quickemu VM? " 178 | printf "\n\n %s\n" "$XDG_ISO_Name" 179 | printColor "\n\n Standard LINUX ISO's only" 180 | printf "\n\n For: batocera | freedos | haiku | kolibrios | reactos | truenas " 181 | printf "\n\n solaris | dragonflybsd | freebsd | netbsd | openbsd " 182 | printf "\n\n Follow up edit the guest_os= .conf line eg guest_os=\"batocera\"" 183 | printf "\n\n Add ' boot=\"legacy\" ' if required. \n" 184 | if [[ $XDG_FileType_Recognised ]] && [[ ! $XDG_MimeType_Recognised ]]; then 185 | printf "\n\n WARNING: The ISO formatting is a non-recognised mimetype so may not be bootable or valid. Try anyway? \n" 186 | fi 187 | printColor "\n\n [enter] continue with set up " 188 | printf "[vm] switch to qqX VM selector menu [c] cancel & close \n\n" 189 | read -rp " > " Cancel_ISO 190 | 191 | if [[ $Cancel_ISO == "c" ]]; then exit directly 192 | elif [[ $Cancel_ISO == "vm" ]]; then 193 | VM_InstanceName= 194 | VM_Conf_Dir= 195 | VM_Conf_File= 196 | XDG_MimeType_is_QCOW= 197 | break 198 | else 199 | printColor "\n\n Please give a short VM name for the folder." 200 | printColor "\n\n eg. tux-33-alpha emu-44-mate (usually recommended)" 201 | printf "\n\n [enter] to use ISO name: %s" "$(basename "$XDG_ISO_Name" .iso)" 202 | printf "\n\n [vm] switch to qqX VM selector menu [c] cancel & close \n\n" 203 | read -rp " > " ISO_NewVM_Name 204 | [[ ! $ISO_NewVM_Name ]] && ISO_NewVM_Name="$(basename "$XDG_ISO_Name" .iso)" 205 | [[ $ISO_NewVM_Name == "vm" ]] && break 206 | [[ $ISO_NewVM_Name == "c" ]] && exit directly 207 | fi 208 | 209 | # If name then start 210 | if [[ $ISO_NewVM_Name == "b" ]]; then 211 | ISO_NewVM_Name= 212 | 213 | elif [[ $ISO_NewVM_Name ]]; then 214 | 215 | QWG_Conf_Dir="$Current_VM_Folder" 216 | QGW_OverWriteAllow= 217 | QGW_OverWriteAlert= 218 | 219 | # Selector GROUP Folder 220 | while true ; do 221 | printColor "\n\n New VM Name = %s" "$ISO_NewVM_Name" 222 | printf "\n\n Preset Group Folders: \n" 223 | show_current_group_folder_list 224 | printf "\n\n" 225 | printColor " Installation target folder: %s " "$QWG_Conf_Dir" 226 | 227 | # borrow the Quick Get Wrapping routines for similar name checks etc 228 | QWG_NewVM_Name="$ISO_NewVM_Name" 229 | check_new_vm_folder_name "$ISO_NewVM_Name" 230 | 231 | if [[ $QGW_OverWriteAlert ]];then 232 | printColor "\n\n\n Installation target folder: %s" "$QWG_Conf_Dir" 233 | printf "\n\n [enter] Start the Setup " 234 | printColor "?? BACKUP ?? " 235 | else printColor "\n\n [enter] Start the Setup " 236 | fi 237 | if [[ $QGW_OverWriteAllow ]]; then printf "[number] select a DIFFERENT FOLDER " 238 | else printf "[number] select a different (preset or current) folder " 239 | fi 240 | 241 | printf " [h] help [b] go back \n\n" 242 | read -rp " > " New_ISO_Start 243 | printf "\n" 244 | 245 | case "$New_ISO_Start" in 246 | b) 247 | New_ISO_Start= ; ISO_NewVM_Name= 248 | break 249 | ;; 250 | [0-9]|[0-9][0-9]) 251 | QWG_Conf_Dir="${VM_Folder_List[New_ISO_Start]}" 252 | QGW_OverWriteAlert= 253 | Current_VM_Folder_Recognised=1 254 | ;; 255 | h) 256 | printf " Once the initial setup up has finished, " 257 | printf "\n\n the new folder and .conf file may be easily moved and/or edited before running." 258 | printf "\n\n For new preset group folders, edit the main settings.\n" 259 | show_current_group_folder_list 260 | printf "\n\n" 261 | ;; 262 | *) 263 | if [[ $QGW_OverWriteAlert ]]; then 264 | printf " RECONFIRMATION is required >> %s" "$QWG_NewVM_Name" 265 | printf "\n\n [enter] to go back" ; printColor " [yes] to confirm \n\n" 266 | read -rp " > " ISO_OvWr 267 | [[ $ISO_OvWr ]] && QGW_OverWriteAllow=1 && New_ISO_Start="start" && break 268 | # otherwise re-run loop 269 | # show_current_group_folder_list 270 | else New_ISO_Start="start" ; break 271 | fi 272 | ;; 273 | esac 274 | 275 | if [[ ! $QWG_Conf_Dir ]]; then 276 | QWG_Conf_Dir="$Default_VM_Folder" 277 | printColor " \n\n Using %s for now. \n\n Selected Group Folder not found. Reselect or Move Later ... \n\n" "$Default_VM_Folder" 278 | fi 279 | 280 | done 281 | 282 | if [[ $New_ISO_Start == "start" ]]; then 283 | 284 | if [[ ! "$Current_VM_Folder_Recognised" ]]; then 285 | printColor "\n\n Confirm installation in non-recognised folder." 286 | printf "\n\n Folder may be added to presets afterwards, via settings edit ... " 287 | printf "\n\n [enter] to go back" ; printColor " [yes] to confirm \n\n" 288 | read -rp " > " ISO_NewFolder 289 | printf "\n\n" 290 | if [[ $ISO_NewFolder ]]; then true ; else continue ; fi 291 | fi 292 | printf " Folder Creation: %s" "$QWG_Conf_Dir/$QWG_NewVM_Name" 293 | printf "\n\n Copy the ISO to the new folder, or move it? [enter] copy [m] move \n\n" 294 | read -rp " > " MoveIso 295 | 296 | mkdir -p "$QWG_Conf_Dir/$QWG_NewVM_Name" 297 | cat "$HOME/.qqX/icons/qqX.ascii.logo.8bit.25.txt" 298 | 299 | if [[ $MoveIso == "m" ]]; then 300 | printf "\n Moving the ISO .... \n\n" 301 | mv "$XDG_ISO_Name" "$QWG_Conf_Dir/$QWG_NewVM_Name/$(basename "$XDG_ISO_Name")" 302 | else 303 | printf "\n Copying the ISO ....\n\n" 304 | cp "$XDG_ISO_Name" "$QWG_Conf_Dir/$QWG_NewVM_Name/$(basename "$XDG_ISO_Name")" 305 | fi 306 | 307 | { 308 | printf '#!' ; printf "/usr/bin/qqX --vm" 309 | printf "\n\nguest_os=\"linux\"" 310 | printf "\ndisk_img=\"%s/disk.qcow2\"" "$QWG_NewVM_Name" 311 | printf "\ndisk_size=\"128G\"" 312 | printf "\niso=\"%s\"" "$QWG_NewVM_Name/$(basename "$XDG_ISO_Name")" 313 | 314 | } > "$QWG_Conf_Dir/$QWG_NewVM_Name.conf" 315 | 316 | chmod +x "$QWG_Conf_Dir/$QWG_NewVM_Name.conf" 317 | VM_Conf_Dir="$QWG_Conf_Dir" 318 | VM_Conf_File="$QWG_NewVM_Name.conf" 319 | VM_InstanceName="$QWG_NewVM_Name" 320 | cd "$QWG_Conf_Dir" || printColor "\n\n ERROR changing to New VM Location" 321 | Current_VM_Folder="$Default_VM_Folder" 322 | refresh_sources_and_load_VM_arrays 323 | break 324 | 325 | fi 326 | 327 | fi 328 | 329 | done 330 | 331 | fi 332 | 333 | 334 | if [[ -e "$VM_Conf_Dir/$VM_Conf_File" ]] && [[ $XDG_Input_VM_qqX ]]; then 335 | cd "$VM_Conf_Dir" || printColor "\n\n ERROR changing to New VM Location" 336 | Current_VM_Folder="$Default_VM_Folder" 337 | [[ $VM_Conf_File ]] && source "$VM_Conf_File" 338 | read_main_drive_and_img_iso_conf_vars 339 | else 340 | refresh_sources_and_load_VM_arrays 341 | fi 342 | 343 | [[ $GeneralLoopCheck ]] && printf "\n\n End of Api & Mouse:" && dev_loop_check 344 | 345 | -------------------------------------------------------------------------------- /qqX.main/qqX_quick_reroute_emu: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ## qqX component to be located in folder 'qqX.main' 4 | 5 | ## Copyright (c) Alex Genovese https://github.com/TuxVinyards 6 | # SMALL CODE SNIPPETS eg the function printColor MAY BE USED 7 | # PERMISSIVELY in projects as MIT or similar, providing CLEAR ATTRIBUTIONS are shown. 8 | # Otherwise: 9 | # Licence GPL3 https://www.gnu.org/licenses 10 | ## qqX - quickemu quickget X terminal project 11 | 12 | ## ShellCheck global disables: 13 | # https://www.shellcheck.net/wiki/SC2242 as it clashes with use of exit traps used to keep mouse click scripts open 14 | # And SC1090,SC1091,SC2024, SC2154 for necessity of dynamic file sourcing 15 | 16 | # shellcheck disable=SC2242,SC1090,SC1091,SC2034,SC2154 17 | 18 | 19 | function quickemu { 20 | 21 | # Any calls to quickemu become re-routed to this function instead. 22 | # Do a first check for amy qwrap internal commands REVIEW 23 | 24 | local FirstParamCount=0 25 | IFS=' ' read -ra FirstParamsArray <<< "$*" 26 | # https://unix.stackexchange.com/questions/50654/function-caller-positional-parameters?rq=1 27 | 28 | while [[ "${FirstParamsArray[$FirstParamCount]}" ]]; do 29 | for FirstParam in "${FirstParamsArray[@]}"; do 30 | case "$FirstParam" in 31 | --vm_boot) 32 | VM_ExecBoot=1 33 | unset "FirstParamsArray[$FirstParamCount]" 34 | set -- "${FirstParamsArray[@]}" 35 | # no other cases applicable, move on. 36 | break 37 | ;; 38 | --menu_bypass) 39 | # used for msr functions below 40 | unset "FirstParamsArray[$FirstParamCount]" 41 | set -- "${FirstParamsArray[@]}" 42 | shift ;; 43 | --toggle_msr_defaults) 44 | toggle_msr_defaults 45 | exit directly ;; 46 | --select_msr_config) 47 | select_msr_config 48 | exit directly ;; 49 | --msrs_conflict_check_resolver) 50 | msrs_conflict_check_resolver 51 | exit directly ;; 52 | esac 53 | ((FirstParamCount+=1)) 54 | done 55 | done 56 | 57 | ## Run the code, by sourcing the sections - first read the cased instructions, then do the actions 58 | # These bits are basically the remaining sections of the original quickemu code. 59 | # The earlier original code sections are all functions that have now been loaded, as is, or modified 60 | 61 | if [[ ! -e "/tmp/qmod-case-temp" ]] || [[ ! -e "/tmp/qmod-actions-temp" ]] || [[ ! -e "/tmp/qmod-boot-temp" ]]; then 62 | 63 | printColor "\n\n ERROR qqX restart needed \n\n" ; sleep 5 64 | 65 | else 66 | 67 | # keep running quickemu 'case' checks for matches: 68 | while [[ $1 ]]; do source "/tmp/qmod-case-temp" ; done 69 | shift $# 70 | 71 | # if no more matches, remove any leftover params not cleared due to trapped exit re-route 72 | # & if not yet given, decide on action 73 | # https://unix.stackexchange.com/questions/18981/how-to-unset-the-positional-parameters 74 | 75 | # Quickemu normally 'SOURCES-IN' the .conf file at the start of "/tmp/qmod-actions-temp" 76 | # BUT from qqX ver. 1.5 this happens at the end of qqX function 'VM_loader_selector_and_menu' 77 | # So, some setting confirmations and overides can be placed here, others go into the modded function 'vm_boot' 78 | 79 | gl="$qqX_GL_Mode" 80 | fixed_iso="$Mounted_ISO" 81 | [[ $guest_os == "macos" ]] && [[ ! $cpu_cores ]] && cpu_cores=8 82 | source "/tmp/qmod-actions-temp" 83 | 84 | if [[ $VM_ExecBoot ]]; then 85 | 86 | # Make sure quickemu doesn't run the vm boot sequence after running an 'action' unless flagged to do so 87 | # load extracted standard quickemu code that gives the okay to run main function 'vm_boot' (in this case, the modded one) 88 | 89 | Mounted_ISO_Return= 90 | 91 | if [[ $Mounted_ISO && $BootStyle == "slow" && $BootStyleMsg == "slow" ]]; then 92 | printColor " Press 'esc' when 'Start boot Option' appears ..." 93 | printf " to access the BIOS or boot an ISO, " 94 | printf "\n\n Mounted ISO: Select 'Boot manager' and select 'QEMU DVD-ROM' from the list. " 95 | printf "\n\n A 'live boot' ISO such as from https://gparted.org/download.php is normally required for disk management." 96 | printColor "\n\n Current ISO/DVD: '%s'" "$Mounted_ISO_DisplayName" 97 | printf "\n\n Use the qqX [iso] utility to select a specific bootable image file" 98 | printColor "\n\n [enter] to continue [b] to go back \n\n" 99 | read -rp " > " Mounted_ISO_Return 100 | 101 | elif [[ ! $Mounted_ISO && $BootStyle == "slow" && $BootStyleMsg == "slow" ]]; then 102 | printColor " Press 'esc' when 'Start boot Option' appears ..." 103 | printf " to access the BIOS " 104 | if [[ $img ]]; then 105 | printf "\n\n A 'live boot' ISO may be usable for disk management" 106 | printf "\n\n BUT this appears to be an 'IMG' based distro which may need special booting." 107 | else 108 | printf "\n\n A 'live boot' ISO such as from https://gparted.org/download.php is normally required for disk management." 109 | fi 110 | printf "\n\n NO ISO/DVD has been specifically mounted ... " 111 | [[ $LiveBoot ]] && printf "\n\n This is a non-installed distro. The standard Live Boot should normally load." 112 | printf "\n\n Use the qqX [iso] utility to select a specific bootable image file" 113 | printColor "\n\n [enter] to continue [b] to go back \n\n" 114 | read -rp " > " Mounted_ISO_Return 115 | 116 | elif [[ $BootStyle == "fast" ]]; then 117 | printColor " qqX Boot Manager may be session enabled in utils, if required ..." 118 | printf "\n\n Use the qqX [iso] utility to select a specific bootable image file \n\n" 119 | 120 | elif [[ $BootStyle == "slow" ]]; then 121 | printColor " Press 'esc' to access BIOS Boot Manager, when 'Start boot Option' appears" 122 | [[ ! $Mounted_ISO ]] && printf "\n\n NO ISO/DVD has been specifically mounted ... " 123 | [[ $LiveBoot ]] && printf "\n\n This is a non-installed distro. The standard Live Boot should normally load." 124 | printf "\n\n Use the qqX [iso] utility to select a specific bootable image file \n\n" 125 | 126 | fi 127 | 128 | if [[ $BootStyle == "fast" ]] && [[ $LiveBoot ]] && [[ "${VM_InstanceName,,}" == *windows* ]]; then 129 | printf " [enter] to continue \n\n" 130 | read -rp " > " 131 | fi 132 | 133 | if [[ $Mounted_ISO_Return ]]; then return 134 | else 135 | # These logs only get cleared just before running, so stay until a boot sequence is run, irrespective of 136 | # whether the run is made as [v] verbose & terminated 137 | printf "" > "/tmp/qqX_echo_output.log" 138 | printf "" > "$QemuErrorLog" 139 | [[ $GeneralLoopCheck ]] && printf "\n\n pre drives_and_snapshots :" && dev_loop_check 140 | drives_and_snapshots "info_current" 141 | printf "\n" 142 | if [[ $DiskChkStatus ]] && [[ $DiskChkStatus != "Okay" ]]; then 143 | printColor " DISK.QCOW2 ERROR %s \n\n Return and run [disk] repair utils \n\n" "$DiskChkStatus" 144 | read -rp " > " 145 | BreakAtVerboseArgs=1 146 | VM_ExecBoot= 147 | return 148 | fi 149 | [[ $GeneralLoopCheck ]] && printf "\n\n pre-sourcing qmod-boot :" && dev_loop_check && printf "\n\n" 150 | source "/tmp/qmod-boot-temp" 151 | VM_ExecBoot= 152 | if [[ $Mounted_ISO && $BootStyle == "slow" && $BootStyleMsg == "slow" ]]; then 153 | printColor "\n\n Press 'esc' when 'Start boot Option' appears" 154 | printf "\n\n Select 'Boot manager' and select 'QEMU DVD-ROM' from the list. \n\n" 155 | fi 156 | if [[ $BreakAtVerboseArgs ]]; then 157 | SpicyPID="$(pgrep spicy)" 158 | [[ $SpicyPID ]] && kill "$SpicyPID" 2>/dev/null & 159 | fi 160 | fi 161 | 162 | fi 163 | 164 | fi 165 | 166 | } 167 | 168 | 169 | -------------------------------------------------------------------------------- /qqX.main/qqX_read_main_settings: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ## qqX component to be located in folder 'qqX.main' 4 | 5 | ## Copyright (c) Alex Genovese https://github.com/TuxVinyards 6 | # SMALL CODE SNIPPETS eg the function printColor MAY BE USED 7 | # PERMISSIVELY in projects as MIT or similar, providing CLEAR ATTRIBUTIONS are shown. 8 | # Otherwise: 9 | # Licence GPL3 https://www.gnu.org/licenses 10 | ## qqX - quickemu quickget X terminal project 11 | 12 | ## ShellCheck global disables: 13 | # https://www.shellcheck.net/wiki/SC2242 as it clashes with use of exit traps used to keep mouse click scripts open 14 | # And SC1090,SC1091,SC2024, SC2154 for necessity of dynamic file sourcing 15 | 16 | # shellcheck disable=SC2242,SC1090,SC1091,SC2034,SC2154 17 | 18 | ## VM selector style 19 | Default_VM_SelectorStyle="A" 20 | [[ ! $VM_SelectorStyle ]] && VM_SelectorStyle="$Default_VM_SelectorStyle" 21 | 22 | function_create_VM_folderList() { 23 | 24 | DefaultFolderError= 25 | ExtraFolderError= 26 | 27 | # Make sure that 'Default_VM_Folder' has a value and it exists 28 | [[ ! $Default_VM_Folder ]] && Default_VM_Folder="$Current_VM_Folder" 29 | if [[ ! -d "$Default_VM_Folder" ]]; then printf "\n\n Settings: Default Folder NOT found \n\n" ; sleep 1 ; DefaultFolderError=1 ; fi 30 | 31 | # set and clear the folder array, current first but don't scan twice, and dont add if the folder is empty (from right click on non-conf files) 32 | if [[ "$Current_VM_Folder" != "$Default_VM_Folder" ]] && [[ "$(ls "$Current_VM_Folder/"*.conf 2> /dev/null)" ]] ; then 33 | declare -ga VM_Folder_List=( "$Current_VM_Folder" "$Default_VM_Folder" ) 34 | else declare -ga VM_Folder_List=("$Default_VM_Folder") 35 | fi 36 | for xF in "${Extra_VM_Folder[@]}"; do 37 | ExtraExists="$(realpath -eq "/$xF")" 38 | if [[ $ExtraExists ]] ; then VM_Folder_List+=("$xF") 39 | else 40 | ExtraFolderError="$xF Path ERROR or Folder Not Yet Created" 41 | VM_Folder_List+=("PATH ERROR: $xF") 42 | fi 43 | done 44 | } 45 | 46 | # Run the function & populate the list: 47 | function_create_VM_folderList 48 | 49 | # Record any set ExtraArgs in case of need to reset 50 | ExtraSetArgs="$ExtraArgs" 51 | 52 | # Add default values, if none supplied 53 | [[ ! $P_Kill_WaitSecs ]] && P_Kill_WaitSecs="4" 54 | P_Kill_WaitSecs_PreSet="$P_Kill_WaitSecs" 55 | 56 | # Set up any quickemu variables needed for .conf file analysis 57 | disk_img= 58 | img= 59 | iso= 60 | 61 | 62 | function find_file_manager_browser() { 63 | 64 | # Check if a default File Manager and a default Web Browser are set or are available, or if suppressed 65 | 66 | if [[ $FileManager == "no" ]]; then FileManager= 67 | else 68 | [[ $FileManager ]] && [[ ! $(type -p "$FileManager") ]] && FileManager= 69 | if [[ ! $FileManager ]] ; then 70 | if [[ $(type -p caja) ]]; then FileManager="caja" 71 | elif [[ $(type -p nautilus) ]]; then FileManager="nautilus" 72 | elif [[ $(type -p dolphin) ]]; then FileManager="dolphin" 73 | elif [[ $(type -p thunar) ]]; then FileManager="thunar" 74 | elif [[ $(type -p pcmanfm) ]]; then FileManager="pcmanfm" 75 | elif [[ $(xdg-mime query default inode/directory 2>/dev/null) ]]; then FileManager="xdg-open" 76 | else FileManager= 77 | fi 78 | fi 79 | fi 80 | if [[ $WebBrowser == "no" ]]; then WebBrowser= 81 | else 82 | [[ $WebBrowser ]] && [[ ! $(type -p "$WebBrowser") ]] && WebBrowser= 83 | if [[ ! $WebBrowser ]]; then 84 | WebBrowser="$(xdg-settings get default-web-browser 2>/dev/null)" 85 | if [[ $WebBrowser ]]; then XDG_Web=1 ; else XDG_Web= 86 | fi 87 | WebBrowser="${WebBrowser/'.desktop'/}" 88 | if [[ "$WebBrowser" == *'firefox'* ]]; then WebBrowser="firefox" 89 | elif [[ "$WebBrowser" == *'falkon'* ]]; then WebBrowser="falkon" 90 | elif [[ $(type -p 'google-chrome') ]]; then WebBrowser="google-chrome" 91 | elif [[ $(type -p falkon) ]]; then WebBrowser="falkon" 92 | elif [[ $(type -p midori) ]]; then WebBrowser="midori" 93 | elif [[ $XDG_Web ]]; then WebBrowser="xdg-open" 94 | else WebBrowser= 95 | fi 96 | fi 97 | fi 98 | } 99 | 100 | find_texteditor () { 101 | 102 | [[ $TextEditor ]] && [[ ! $(type -p "$TextEditor") ]] && texteditor_not_found_message && TextEditor= 103 | if [[ ! $TextEditor ]]; then 104 | DefaultTextEditor="$(xdg-mime query default text/plain 2>/dev/null)" 105 | EditorTest="${DefaultTextEditor/.desktop/}" 106 | EditorTest="$(tr -cd "[:alnum:]" <<< "$EditorTest")" 107 | # orggnomeTextEditor etc will fail 108 | [[ $EditorTest == "orggnomeTextEditor" ]] && EditorTest="gnome-text-editor" 109 | if [[ $DefaultTextEditor ]] && [[ $(type -p "$EditorTest") ]] ; then TextEditor="$EditorTest" 110 | elif [[ $(type -p gedit) ]] ; then TextEditor="gedit" 111 | elif [[ $(type -p gnome-text-editor) ]] ; then TextEditor="gnome-text-editor" 112 | elif [[ $(type -p pluma) ]] ; then TextEditor="pluma" 113 | elif [[ $(type -p kate) ]] ; then TextEditor="kate" 114 | elif [[ $(type -p mousepad) ]] ; then TextEditor="mousepad" 115 | elif [[ $(type -p leafpad) ]] ; then TextEditor="leafpad" 116 | elif [[ $(type -p okular) ]] ; then TextEditor="okular" 117 | fi 118 | fi 119 | } 120 | 121 | # NB Also needs calling if re-loading the settings file See refresh_sources_and_load_VM_arrays 122 | find_file_manager_browser 123 | find_texteditor 124 | 125 | ## PATHS 126 | 127 | # Set the original initial value of paths as found on start up. Check Once. 128 | OriginalPATH="$PATH" 129 | QE_SystemFilePath="$(type -p quickemu)" 130 | QG_SystemFilePath="$(type -p quickget)" 131 | 132 | if [[ ! $QE_DevPath && $DevStart ]]; then QE_DevPath="$(realpath "./qqX.builtins/freespirit")" ; fi 133 | 134 | function sort_qqX_builtin_settings { 135 | 136 | # Check custom QE & QG, plus set the 'quickget' version and set PATH for standard ancilliaries 137 | # Also standardize 'QE_SourceVersion' spelling so the version can be referenced if needed later 138 | 139 | [[ $GeneralLoopCheck ]] && printf "\n\n Sort builtins 1 :" && dev_loop_check 140 | 141 | QE_SourcePath= ; QG_SourcePath= ; FreePath= 142 | 143 | if [[ $CodingHotSwap ]]; then QE_SourceVersion="$CodingHotSwap" ; fi 144 | [[ ! $QE_SourceVersion ]] && QE_SourceVersion="freebird" 145 | 146 | # simplfy checks and standardization, make all lower case, for now 147 | QE_SourceVersion="${QE_SourceVersion,,}" 148 | 149 | if [[ $QE_SourceVersion == "custom" ]] && [[ ! -d "$Default_VM_Folder/qqX.custom/QE.Custom" ]]; then 150 | printf "\n\n ERROR - Custom Folder not found \n\n Re-check Installation &/or Installation instructions" 151 | printf "\n\n Settings File: %s \n\n" "$qqX_SettingsFile" ; sleep 4 152 | fi 153 | 154 | if [[ $QE_SourceVersion == "dev" ]] && [[ ! -d "$QE_DevPath" ]]; then 155 | printf "\n\n ERROR - Dev Folder not found \n\n Re-check Installation &/or Installation instructions" 156 | printf "\n\n Settings File: %s \n\n" "$qqX_SettingsFile" ; sleep 4 157 | fi 158 | 159 | if [[ $QE_SourceVersion == "system" ]]; then 160 | if [[ $QE_SystemFilePath && $QG_SystemFilePath ]]; then 161 | QE_SourceVersion="System" 162 | QE_SourcePath="$QE_SystemFilePath" 163 | QG_SourcePath="$QG_SystemFilePath" 164 | else 165 | QE_SourceVersion="Missing System Files" 166 | QE_SourcePath= ; QG_SourcePath= ; FreePath= 167 | fi 168 | fi 169 | 170 | if [[ $QE_SourceVersion != "System" ]]; then 171 | 172 | if [[ $QE_SourceVersion == "freebird" ]]; then 173 | QE_SourceVersion="FreeBird" 174 | FreePath="$qqX_BuiltinsFolder/freebird" 175 | elif [[ $QE_SourceVersion == "freespirit" ]]; then 176 | QE_SourceVersion="FreeSpirit" 177 | FreePath="$qqX_BuiltinsFolder/freespirit" 178 | elif [[ $QE_SourceVersion == "homebird" ]]; then 179 | QE_SourceVersion="HomeBird" 180 | FreePath="$qqX_BuiltinsFolder/homebird" 181 | elif [[ $QE_SourceVersion == "echo" ]]; then 182 | QE_SourceVersion="Echo" 183 | FreePath="$qqX_BuiltinsFolder/echo" 184 | elif [[ $QE_SourceVersion == "custom" ]]; then 185 | QE_SourceVersion="Custom" 186 | FreePath="$Default_VM_Folder/qqX.custom/QE.Custom" 187 | if [[ ! -e "$FreePath" ]]; then FreePath="$qqX_BuiltinsFolder/freebird" ; fi 188 | elif [[ $QE_SourceVersion == "dev" ]]; then 189 | QE_SourceVersion="Dev" 190 | FreePath="$QE_DevPath" 191 | if [[ ! -e "$FreePath" ]]; then 192 | QE_SourceVersion="FreeSpirit" 193 | FreePath="$qqX_BuiltinsFolder/freespirit" 194 | printf "\n\n Dev path '%s' Not Found >> Using FreeSpirit \n\n" "$QE_DevPath" 195 | sleep 2 ; read -r -t 0.1 ; read -r -t 0.1 ; read -r -t 0.1 ; read -r -t 0.1 196 | fi 197 | else 198 | printf "\n\n Source Setting '%s' Not Recognised >> Using FreeBird \n\n" "$QE_SourceVersion" 199 | sleep 3 ; read -r -t 0.1 ; read -r -t 0.1 ; read -r -t 0.1 ; read -r -t 0.1 200 | QE_SourceVersion="FreeBird" 201 | FreePath="$qqX_BuiltinsFolder/freebird" 202 | fi 203 | 204 | QE_SourcePath="$FreePath/quickemu" 205 | QG_SourcePath="$FreePath/quickget" 206 | 207 | fi 208 | 209 | if [[ $FreePath ]]; then 210 | export PATH="${FreePath}:${qqX_LibraryFolder}:${OriginalPATH}" 211 | else 212 | export PATH="${qqX_LibraryFolder}:${OriginalPATH}" 213 | fi 214 | 215 | QE_VerNumber= 216 | QG_VerNumber= 217 | 218 | if [[ $QE_SourcePath ]] && [[ ! -e "$QE_SourcePath" ]]; then 219 | printf "\n\n ERROR - quickemu not found \n\n Re-check Installation &/or Installation instructions" 220 | printf "\n\n Settings File: %s \n\n" "$qqX_SettingsFile" ; sleep 10 221 | fi 222 | if [[ $QG_SourcePath ]] && [[ ! -e "$QG_SourcePath" ]]; then 223 | printf "\n\n ERROR - quickget not found \n\n Re-check Installation &/or Installation instructions" 224 | printf "\n\n Settings File: %s \n\n" "$qqX_SettingsFile" ; sleep 10 225 | fi 226 | 227 | [[ $GeneralLoopCheck ]] && printf "\n\n Sort builtins 2 :" && dev_loop_check 228 | 229 | } 230 | 231 | sort_qqX_builtin_settings 232 | 233 | function find_quick_version_numbers { 234 | 235 | # To be run only after rerouting functions for quickemu & quickget are in place & code has been sourced. 236 | # But place early as emu and get may load initially out of sequence ... 237 | 238 | # And if rerunning after a settings change, to be run after 'find_quickemu_source_functions_and_vars' 239 | # has been, so that the right QE & QG versions have been established and loaded. 240 | # See function 'refresh_and_load_quickemu_sources' 241 | 242 | # QuickEMU also uses an internal variable '$VERSION' but this only works after the correct script has been sourced. 243 | # But use grep to avoid bad original source $QEMU_VER_SHORT routines throwing errors 244 | QE_VerNumber="$(grep -s 'VERSION=' "$QE_SourcePath" | cut -d'"' -f2)" 245 | 246 | # QuickGET as @ ver 4.9.2 'official-vanilla' actually makes a call to EMU to find the number. 247 | # But the 'quickget' call goes to a qqX capture function of the same name, 248 | # and allows re-routes to custom versions that may work differently. 249 | if [[ $QE_SourceVersion == "System" || $QE_SourceVersion == "Echo" ]]; then QG_VerNumber="$QE_VerNumber" 250 | else QG_VerNumber="$("$QG_SourcePath" --version 2>/dev/null)" 251 | fi 252 | 253 | # Release Quickemu 4.9.2 was still numbered 4.9.1 254 | # Use the changes to fedora releases 'echo 38 39' as a fixing flag: 255 | if grep -q -s 'echo 38 39' "$QG_SourcePath" ; then 256 | [[ $QG_VerNumber == "4.9.1" ]] && QG_VerNumber="4.9.2" 257 | [[ $QE_VerNumber == "4.9.1" ]] && QE_VerNumber="4.9.2" 258 | fi 259 | 260 | if [[ ! $QE_VerNumber ]] || [[ ! $QG_VerNumber ]]; then 261 | printColor "\n\n ERROR finding QuickEmu/Quickget Source Path or running Source Checks" 262 | printf "\n\n If you have just re/installed and are running an initial start up, " 263 | printf "\n\n this may be due to your Linux distro being cautious about new scripts." 264 | printf "\n\n\n A close down and a standard restart of qqX should probably solve things ...." 265 | printf "\n\n [enter] to close \n\n" 266 | read -rp " > " 267 | exit directly 268 | fi 269 | } 270 | 271 | find_quick_version_numbers 272 | 273 | 274 | ## Analyse qqX Current Version (Latest version is carried out in the updater function) 275 | 276 | # Beta version notation changes @ 24/11 with intro of 'floatversion' and improved sorting abilities 277 | # Suffixes more closely follow 'semantic versioning' x.x.xx-suffix > major.minor.point and with optional as -beta.01 etc 278 | # but minor numbers remain padded to 2dp 279 | 280 | if [[ $qqX_Version == *[[:alpha:]]* ]] ; then qqX_Current_ReleaseType="beta" 281 | else qqX_Current_ReleaseType="release" 282 | fi 283 | 284 | ## Check for Qemu & set up some of quickemu's variables, the upper case ones. (Adaption of the quickemu scripting) 285 | # Sets defaults, if not given in main settings. But also for later, so systems can be swapped out. 286 | 287 | [[ ! $QEMU_Default_SystemType ]] && QEMU_Default_SystemType="qemu-system-x86_64" 288 | [[ ! $QEMU_Default_MachineType ]] && QEMU_Default_MachineType="q35" 289 | 290 | QEMU="$(type -p "$QEMU_Default_SystemType")" 291 | QEMU_IMG="$(type -p qemu-img)" 292 | 293 | 294 | function qemu_version_check { 295 | 296 | if [[ ! $QEMU ]] || [[ ! $QEMU_IMG ]]; then 297 | printColor "\n\n QEMU not found. Check installations of qemu and qemu-img ..." 298 | printf "\n\n If using the quickemu packaging this should normally have been installed." 299 | printf "\n\n [enter] to continue and edit settings if current installation is okay. \n\n" 300 | read -rp " > " 301 | fi 302 | 303 | # Takes first two elements of version and combines into single integer 304 | # eg 8.1.0 becomes 81 and 10.2.1 becomes 102 305 | QEMU_VER_LONG="$("$QEMU" -version | head -n1 | cut -d' ' -f4 | cut -d'(' -f1 )" 306 | QEMU_VER_SHORT=$(cut -d '.' -f 1-2 <<< "$QEMU_VER_LONG") 307 | QEMU_VER_SHORT="${QEMU_VER_SHORT//./}" 308 | 309 | # Test as much as possible for error caused by change of version text or version format and/or resultant empty value. 310 | # Quickemu refuses to work in the case of values -lt 61 but moves to different routines in the cases of 70 and 81 311 | # No past versions of Qemu have ever exceeded a minor value of 2 so future cases of minor versions > 9 are only hypothetical 312 | # eg where 10.14.1 becomes 1014 313 | 314 | if [[ ! $QEMU_VER_LONG ]] || [[ "$QEMU_VER_LONG" == *[[:alpha:]]* ]]; then qvFail=1 315 | elif [[ ! $QEMU_VER_SHORT ]] || [[ "$QEMU_VER_SHORT" == *[[:alpha:]]* ]]; then qvFail=1 316 | elif [[ $QEMU_VER_SHORT -lt 61 ]]; then qvFail=1 317 | fi 318 | 319 | if [[ $QemuShortVerOveride ]]; then QEMU_VER_SHORT="123456" 320 | elif [[ $qvFail ]]; then 321 | printf "\n\n %s" "$("$QEMU" -version | head -n 1)" 322 | printf "\n\n QEMU version 8.1.0 or newer is normally required for all functions to work." 323 | printf "\n\n while QEMU version 6.1.0 is baseline for GL functions." 324 | printf "\n\n Use 'QemuShortVerOveride' in MAIN SETTINGS to adjust errors and change warnings" 325 | printf "\n\n [enter] to continue and edit settings if current version is okay. \n\n" 326 | read -rp " > " 327 | fi 328 | } 329 | 330 | qemu_version_check 331 | 332 | path_diagnostics pause 333 | 334 | # How the Tiano BIOS/UEFI loads up: 335 | 336 | function store_boot_style_defaults { 337 | [[ ! $BootStyle ]] && BootStyle="fast" 338 | [[ ! $BootStyleMsg ]] && BootStyleMsg="slow" 339 | BootStyle_Default="$BootStyle" 340 | BootStyleMsg_Default="$BootStyleMsg" 341 | } 342 | 343 | function reload_boot_style_defaults { 344 | BootStyle="$BootStyle_Default" 345 | BootStyleMsg="$BootStyleMsg_Default" 346 | } 347 | 348 | function clear_boot_style_vars { 349 | BootStyle= ; BootStyle_Default= 350 | BootStyleMsg= ; BootStyleMsg_Default= 351 | } 352 | 353 | store_boot_style_defaults 354 | 355 | -------------------------------------------------------------------------------- /qqX.main/qqX_selfcheck_and_update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ## qqX component to be located in folder 'qqX.main' 4 | 5 | ## Copyright (c) Alex Genovese https://github.com/TuxVinyards 6 | # SMALL CODE SNIPPETS eg the function printColor MAY BE USED 7 | # PERMISSIVELY in projects as MIT or similar, providing CLEAR ATTRIBUTIONS are shown. 8 | 9 | # Otherwise: 10 | 11 | # Licence GPL3 https://www.gnu.org/licenses 12 | 13 | # This program is free software: you can redistribute it and/or modify 14 | # it under the terms of the GNU General Public License as published by 15 | # the Free Software Foundation, either version 3 of the License, or 16 | # (at your option) any later version. 17 | 18 | # This program is distributed in the hope that it will be useful, 19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | # GNU General Public License for more details. 22 | 23 | # https://www.gnu.org/licenses 24 | 25 | 26 | # IF CODE IN GENERAL BECOMES USED IN ANY OTHER PROJECT, 27 | # THE GPL3 LICENCE APPLIES & YOU SHOULD SHOW CLEAR ATTRIBUTIONS. 28 | 29 | 30 | ## qqX - quickemu quickget X terminal project 31 | 32 | # This script is based on ffX - the ff(mpeg) X-terminal project - AV processing scripts for ffmpeg 33 | # Also copyright (c) Alex Genovese https://github.com/TuxVinyards https://github.com/theffxproject 34 | 35 | # Updated 2024/04 to work with changes to fedora 40 and github 36 | 37 | # Make sure shell is set during session to decimal separator of dot 38 | # If LC_ALL=C changes too much, just set the numeric. 39 | # This may upset some input/output filters .... CAUTION (see notes in the main settings) 40 | 41 | # See locale setting discussion: https://unix.stackexchange.com/a/149129 42 | # Also https://unix.stackexchange.com/questions/62316/why-is-there-no-euro-english-locale?rq=1 43 | # & http://www.unicode.org/L2/L2001/01102-POSIX15897.htm 44 | 45 | 46 | ## ShellCheck global disables: 47 | # https://www.shellcheck.net/wiki/SC2242 as it clashes with use of exit traps used to keep mouse click scripts open 48 | # And SC1090,SC1091,SC2024, SC2154 for necessity of dynamic file sourcing 49 | 50 | # shellcheck disable=SC2242,SC1090,SC1091,SC2034,SC2154 51 | 52 | 53 | # 'check_for_qqX_updates' runs once only, at qqX start-up in the background, 54 | # and a transfer file is used to move the variables to the foreground 55 | 56 | # clear foreground vars: 57 | qqX_Latest_ReleaseNumber= 58 | qqX_Latest_SecureTarBall_Addr= 59 | qqX_Latest_SecureTarSha_Addr= 60 | qqX_UpdateAvail= 61 | qqX_Upd_VarsTmpFile="/tmp/qqX.upd.chk.txt" 62 | printf "\n\n qqX_UpdateAvail=" > "$qqX_Upd_VarsTmpFile" 63 | qqX_Latest_ReleaseFile="/tmp/latest-qqX" 64 | [[ -e $qqX_Latest_ReleaseFile ]] && rm "$qqX_Latest_ReleaseFile" 2>/dev/null 65 | 66 | check_for_qqX_updates() { 67 | 68 | # Quietly do a check for new releases and return required vars if update available 69 | # API method first, general release page second ... (?) 70 | # Fix API version for consistency. Better to briefly drop to non-sha than get garbled data. 71 | # Using fv --full only detects beta releases 72 | 73 | curl --header "X-GitHub-Api-Version:2022-11-28" --disable -sLf -m 12 --retry 4 \ 74 | -o "${qqX_Latest_ReleaseFile}" "https://api.github.com/repos/TuxVinyards/qqX/releases/latest" 75 | # -m --max-time Maximum time in seconds that you allow each transfer to take. 76 | # If you enable retrying the transfer (--retry) then the maximum time counter is reset each time the transfer is retried. 77 | 78 | sleep 0.5 79 | if [[ -e "${qqX_Latest_ReleaseFile}" ]]; then 80 | if [[ $Hide_qqX_BetaUpdates ]]; then qqX_Latest_ReleaseNumber="$(fv -M "$(grep -si 'tag_name' "${qqX_Latest_ReleaseFile}")" 2>/dev/null)" 81 | else qqX_Latest_ReleaseNumber="$(fv --full -M "$(grep -si 'tag_name' "${qqX_Latest_ReleaseFile}")" 2>/dev/null)" 82 | fi 83 | fi 84 | 85 | if [[ $qqX_Latest_ReleaseNumber ]]; then 86 | # make sure that GitHub is listing a tarball download address 87 | # eg. "https://github.com/TuxVinyards/qqX/releases/download/$qqX_Latest_ReleaseNumber/qqX-$qqX_Latest_ReleaseNumber.tar.gz" 88 | if [[ "$(tr '"' '\n' < "${qqX_Latest_ReleaseFile}" | grep -s http | grep -sc download)" == "2" ]] ; then 89 | qqX_Latest_SecureTarBall_Addr="$(tr '"' '\n' < "${qqX_Latest_ReleaseFile}" | grep -s http | grep -s -m 1 download)" 90 | qqX_Latest_SecureTarSha_Addr="${qqX_Latest_SecureTarBall_Addr}.sha256" 91 | fi 92 | else 93 | # function 'download_new_release' will react to whether 'qqX_Latest_SecureTarBall_Addr' is available 94 | if [[ $Hide_qqX_BetaUpdates ]]; then 95 | qqX_Latest_ReleaseNumber="$(curl -qsf -m 12 --retry 3 "https://github.com/TuxVinyards/qqX/tags" | grep 's/tag' | fv -M )" 96 | else qqX_Latest_ReleaseNumber="$(curl -qsf -m 12 --retry 3 "https://github.com/TuxVinyards/qqX/tags" | grep 's/tag' | fv -M --full)" 97 | fi 98 | fi 99 | 100 | if [[ $qqX_Latest_ReleaseNumber ]]; then 101 | # --gt check where eg "1.12.02-beta1 1.12.01" is true 102 | if floatversion --gt "$qqX_Latest_ReleaseNumber $qqX_Version" 2>/dev/null ; then 103 | # Versioning, normally as x.x.xx-suffix > major.minor.point and with optional suffix as -beta1 etc 104 | # Flag up release type. Current Version is carried out at the start, in similar way 105 | if [[ $qqX_Latest_ReleaseNumber == *[[:alpha:]]* ]] ; then qqX_Latest_ReleaseType="beta" 106 | else qqX_Latest_ReleaseType="release" 107 | fi 108 | # If currently using beta: 109 | if [[ $qqX_Current_ReleaseType == "beta" ]]; then 110 | Hide_qqX_BetaUpdates= 111 | # flag up about beta notifications if transitioning from beta to release: 112 | [[ $qqX_Latest_ReleaseType == "release" ]] && BetaTxReleasePoint=1 113 | fi 114 | # Using TRANSFER FILE for all needed variables, as check runs in background ... NB 115 | { printf "\n\n qqX_UpdateAvail=\"1\"" 116 | printf "\n\n qqX_Latest_ReleaseNumber=\"%s\"" "$qqX_Latest_ReleaseNumber" 117 | printf "\n\n qqX_Latest_SecureTarBall_Addr=\"%s\"" "$qqX_Latest_SecureTarBall_Addr" 118 | printf "\n\n qqX_Latest_SecureTarSha_Addr=\"%s\"" "$qqX_Latest_SecureTarSha_Addr" 119 | printf "\n\n qqX_Latest_ReleaseType=\"%s\"" "$qqX_Latest_ReleaseType" 120 | } > "$qqX_Upd_VarsTmpFile" 121 | fi 122 | fi 123 | } 124 | 125 | # Runs in the background. qqX will run this once. 126 | function find_latest_qemu_version { 127 | printf "" > "/tmp/latest-qemu.txt" 128 | { floatversion --full -M "$(curl --disable -sLf --retry 3 --max-time 8 "https://github.com/qemu/qemu/tags" | grep -s 'Link--primary')" 2>/dev/null; } > "/tmp/latest-qemu.txt" 129 | } 130 | 131 | 132 | function download_new_release { 133 | 134 | printf "\033c \n\n" 135 | qqX_logo_printf_title "qqX updater" 136 | printf "\n\n\n https://github.com/TuxVinyards/qqX/releases/latest" 137 | 138 | if [[ ! $qqX_UpdateAvail ]] ; then 139 | printColor "\n\n No new release found ...." 140 | else 141 | if [[ ! $qqX_Latest_SecureTarBall_Addr ]]; then 142 | printf "\n\n Only Non-SHA checked download available (?)\n" 143 | else 144 | printf "\n\n %s \n" "$qqX_Latest_SecureTarBall_Addr" 145 | AboutRelease="$(tr ',' '\n' < "${qqX_Latest_ReleaseFile}" | grep -sA 12 'body')" 146 | AboutRelease="$(tr -cd '[:print:]' <<< "$AboutRelease")" 147 | AboutRelease="$(cut -d ':' -f2 <<< "$AboutRelease")" 148 | AboutRelease="${AboutRelease//\"/}" 149 | AboutRelease="${AboutRelease//'### '/}" 150 | AboutRelease="${AboutRelease//'## '/}" 151 | AboutRelease="${AboutRelease//'\n'/}" 152 | AboutRelease="${AboutRelease//'\r'/}" 153 | AboutRelease="${AboutRelease//'}'/}" 154 | IFS='-' read -ra AboutReleaseArray <<< "$AboutRelease" 155 | for Line in "${AboutReleaseArray[@]}" ; do if [[ "$Line" != *'+1'* ]] ; then printColor "\n\n * %s" "$Line" ; fi ; done 156 | # as long as there are no trailing colons or spaces in release list, prints a tidied up bullet-point summary 157 | fi 158 | fi 159 | 160 | printf "\n\n\n" 161 | while true ; do 162 | if [[ $qqX_UpdateAvail ]]; then 163 | printf " Neatly download the release into its own sub-folder at: " 164 | [[ $Specified_Updates_Location ]] && printf "\n\n [s] Specified folder: %s" "$Specified_Updates_Location" 165 | [[ -d "$HOME/Downloads" ]] && printf "\n\n [d] Downloads folder: %s" "$HOME/Downloads" 166 | 167 | if [[ $(pwd) == "$Default_VM_Folder" ]]; then 168 | printColor "\n\n [enter] Current/Default folder:" 169 | printf " %s " "$Default_VM_Folder" 170 | else 171 | # not to confuse with '$Current_VM_Folder' 172 | printf "\n\n [c] Current folder: %s " "$(pwd)" 173 | printColor "\n\n [enter] Default VM folder:" 174 | printf " %s" "$Default_VM_Folder" 175 | fi 176 | printf "\n\n" 177 | fi 178 | [[ $WebBrowser ]] && printf " [w] popup qqX web pages with browser" 179 | printf " or [b] to go back \n\n" 180 | read -rp " > " qqX_LatestTarBall_Folder 181 | printf "\n\n" 182 | 183 | if [[ $qqX_LatestTarBall_Folder == "s" ]]; then qqX_LatestTarBall_Folder="$Specified_Updates_Location" ; break 184 | elif [[ $qqX_LatestTarBall_Folder == "d" ]]; then qqX_LatestTarBall_Folder="$HOME/Downloads" ; break 185 | elif [[ $qqX_LatestTarBall_Folder == "c" ]]; then qqX_LatestTarBall_Folder="$(pwd)" ; break 186 | elif [[ $qqX_LatestTarBall_Folder == "w" ]]; then 187 | printf " Opening Browser ... \n\n" 188 | (nohup &> /dev/null "$WebBrowser" "https://github.com/TuxVinyards/qqX" & ) || web_browser_not_found_message 189 | sleep 1 190 | elif [[ $qqX_LatestTarBall_Folder == "b" ]]; then qqX_LatestTarBall_Folder= ; qqX_NoReleaseDL=1 ; break 191 | else qqX_LatestTarBall_Folder="$Default_VM_Folder" ; break 192 | fi 193 | done 194 | 195 | if [[ $qqX_NoReleaseDL ]]; then qqX_NoReleaseDL= 196 | else 197 | # each release to its own FOLDER makes things tidier 198 | qqX_LatestTarBall_Folder="$qqX_LatestTarBall_Folder/qqX.releases/$qqX_Latest_ReleaseNumber" 199 | # make sure each DOWNLOAD is clean 200 | [[ -d "$qqX_LatestTarBall_Folder" ]] && rm -r "$qqX_LatestTarBall_Folder" 2>/dev/null 201 | sleep 0.5; mkdir -p "$qqX_LatestTarBall_Folder" ; sleep 0.5 202 | # record where to come back to, when done and not to confuse with '$Current_VM_Folder' 203 | ReturnFolder="$(pwd)" 204 | # MOVE to download folder and keep things contained 205 | cd "$qqX_LatestTarBall_Folder" || printColor "\n\n ERROR making or changing to qqX.releases folder ??" 206 | 207 | if [[ $(pwd) != "$qqX_LatestTarBall_Folder" ]]; then 208 | # if we are not in the right folder, we need to exit 209 | printf "\n\n [enter] to return \n\n" 210 | read -rp " > " 211 | else 212 | if [[ $qqX_Latest_SecureTarBall_Addr ]]; then 213 | dlCount="1" 214 | while [[ $dlCount -le "3" ]]; do 215 | eval curl --disable -Lf -m 20 -o "$qqX_LatestTarBall_Folder/qqX-${qqX_Latest_ReleaseNumber}.tar.gz" "$qqX_Latest_SecureTarBall_Addr" 216 | echo 217 | eval curl --disable -Lf -m 20 -o "$qqX_LatestTarBall_Folder/qqX-${qqX_Latest_ReleaseNumber}.tar.gz.sha256" "$qqX_Latest_SecureTarSha_Addr" 218 | echo 219 | SHA_Check="$(sha256sum -c "$qqX_LatestTarBall_Folder/qqX-${qqX_Latest_ReleaseNumber}.tar.gz.sha256")" 220 | printColor "\n SHA256 Check: %s " "$SHA_Check" 221 | if [[ "$SHA_Check" != *'OK' ]]; then 222 | rm -r ./* 2>/dev/null 223 | printf "\n\n ERROR - RETRYING DOWNLOAD \n\n" 224 | else printf "\n\n" ; break 225 | fi 226 | ((dlCount++)) 227 | done 228 | fi 229 | if [[ ! -e "qqX-${qqX_Latest_ReleaseNumber}.tar.gz" ]]; then 230 | rm -r ./* 2>/dev/null 231 | printf "\n Problem auto-downloading via Secure Tarball" 232 | printColor "\n\n Using standard NON sha256-checked \n\n" 233 | dlCount="1" 234 | while [[ $dlCount -le "3" ]]; do 235 | qqX_Latest_NonSecure_TarBall="https://github.com/TuxVinyards/qqX/archive/refs/tags/${qqX_Latest_ReleaseNumber}.tar.gz" 236 | eval curl --disable -Lf -m 20 -o "$qqX_LatestTarBall_Folder/qqX-${qqX_Latest_ReleaseNumber}.tar.gz" "$qqX_Latest_NonSecure_TarBall" 237 | echo 238 | if [[ ! -e "qqX-${qqX_Latest_ReleaseNumber}.tar.gz" ]]; then 239 | rm -r ./* 2>/dev/null 240 | printf "\n\n ERROR - RETRYING DOWNLOAD \n\n" 241 | else printf "\n\n" ; break 242 | fi 243 | ((dlCount++)) 244 | done 245 | fi 246 | 247 | if [[ ! -e "qqX-${qqX_Latest_ReleaseNumber}.tar.gz" ]]; then 248 | printf " Problem auto-downloading Tarball" 249 | printColor "\n\n Download file '%s not found \n\n" "qqX-${qqX_Latest_ReleaseNumber}.tar.gz" 250 | fi 251 | 252 | printColor " File download target %s" "$qqX_LatestTarBall_Folder" 253 | 254 | if [[ $BetaTxReleasePoint ]]; then 255 | printf "\n\n\n Moving from beta to release ... " 256 | printf "\n\n See the general settings file for more details \n" 257 | fi 258 | 259 | printf "\n\n Use the installer script to quickly refresh and add new features to your settings file." 260 | printf "\n\n All option settings, locations and preferences will be kept." 261 | printf "\n\n\n " 262 | [[ -e "qqX-${qqX_Latest_ReleaseNumber}.tar.gz" ]] && printf "[enter] to run the installer script " 263 | printf "[r] to return to main menu \n\n" 264 | read -rp " > " qqX_UpdateExit 265 | 266 | if [[ $qqX_UpdateExit != "r" ]] && [[ ! -e "qqX-${qqX_Latest_ReleaseNumber}.tar.gz" ]]; then 267 | printColor "\n\n ERROR: Download folder/file not found ??" 268 | printf "\n\n [enter] to close and retry \n\n" 269 | read -rp " > " 270 | qqX_UpdateExit="r" 271 | else 272 | # https://stackoverflow.com/questions/651018/opening-a-tar-gz-file-with-a-single-command 273 | tar xfz "qqX-${qqX_Latest_ReleaseNumber}.tar.gz" 274 | sleep 0.5 275 | if [[ -d "qqX-${qqX_Latest_ReleaseNumber}" ]]; then 276 | # is using fall-back mode 277 | cp -r "qqX-${qqX_Latest_ReleaseNumber}"/* . 278 | fi 279 | sleep 0.5 280 | if [[ ! -e "qqX_setup_and_install" ]] ; then 281 | printColor "\n\n ERROR extracting the download files ??" 282 | printf "\n\n [enter] to close and retry \n\n" 283 | read -rp " > " 284 | qqX_UpdateExit="r" 285 | fi 286 | fi 287 | if [[ $qqX_UpdateExit != "r" ]]; then 288 | # start the SYSTEM installer (use the posix installer for initial or manual installs) 289 | exec bash -c "$qqX_LatestTarBall_Folder/qqX.system/qqX_system_install Upgrade" 290 | fi 291 | fi 292 | 293 | sleep 0.5 294 | cd "$ReturnFolder" || printColor "\n\n ERROR returning from qqX.releases folder" 295 | 296 | fi 297 | } 298 | 299 | 300 | -------------------------------------------------------------------------------- /qqX.system/Func_echo_colors: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ## qqX - quickemu quickget X terminal project 4 | 5 | # qqX colour functions 6 | 7 | # https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit 8 | 9 | # https://tldp.org/HOWTO/Bash-Prompt-HOWTO/x405.html 10 | 11 | 12 | ## Copyright (c) Alex Genovese https://github.com/TuxVinyards 13 | # SMALL CODE SNIPPETS eg the function printColor MAY BE USED 14 | # PERMISSIVELY in projects as MIT or similar, providing CLEAR ATTRIBUTIONS are shown. 15 | 16 | # Otherwise: 17 | 18 | # Licence GPL3 https://www.gnu.org/licenses 19 | 20 | # This program is free software: you can redistribute it and/or modify 21 | # it under the terms of the GNU General Public License as published by 22 | # the Free Software Foundation, either version 3 of the License, or 23 | # (at your option) any later version. 24 | 25 | # This program is distributed in the hope that it will be useful, 26 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 27 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 28 | # GNU General Public License for more details. 29 | 30 | # https://www.gnu.org/licenses 31 | 32 | # IF CODE IN GENERAL BECOMES USED IN ANY OTHER PROJECT, 33 | # THE GPL3 LICENCE APPLIES & YOU SHOULD SHOW CLEAR ATTRIBUTIONS. 34 | 35 | 36 | # If source without declared shade, set default shade 37 | [[ ! $X_Shade ]] && X_Shade=3 38 | 39 | echColor () { 40 | 41 | # for X_Shade see qqX configs 42 | 43 | if [[ $2 ]] ; then 44 | 45 | if [[ "$1" == "-n" ]] ; then 46 | tput setaf "$X_Shade" 47 | echo -n "$2" 48 | tput sgr0 49 | else 50 | tput setaf "$X_Shade" 51 | # echo "$1 $2" 52 | echo "$@" 53 | # REVIEW and $3 ? 54 | tput sgr0 55 | fi 56 | 57 | else 58 | tput setaf "$X_Shade" 59 | echo "$1" 60 | tput sgr0 61 | fi 62 | 63 | } 64 | 65 | 66 | printColor () { 67 | 68 | tput setaf "$X_Shade" 69 | # shellcheck disable=SC2059 70 | printf "$@" 71 | tput sgr0 72 | 73 | } 74 | -------------------------------------------------------------------------------- /qqX.system/icons/qqX.blu.logo.128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuxVinyards/qqX/62e8bcf6fd4df6bc1a667509e166a423b0a48435/qqX.system/icons/qqX.blu.logo.128.png -------------------------------------------------------------------------------- /qqX.system/icons/qqX.blu.logo.256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuxVinyards/qqX/62e8bcf6fd4df6bc1a667509e166a423b0a48435/qqX.system/icons/qqX.blu.logo.256.png -------------------------------------------------------------------------------- /qqX.system/icons/qqX.blu.logo.500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuxVinyards/qqX/62e8bcf6fd4df6bc1a667509e166a423b0a48435/qqX.system/icons/qqX.blu.logo.500.png -------------------------------------------------------------------------------- /qqX.system/icons/qqX.blu.logo.96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuxVinyards/qqX/62e8bcf6fd4df6bc1a667509e166a423b0a48435/qqX.system/icons/qqX.blu.logo.96.png -------------------------------------------------------------------------------- /qqX.system/icons/qqX.std.logo.128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuxVinyards/qqX/62e8bcf6fd4df6bc1a667509e166a423b0a48435/qqX.system/icons/qqX.std.logo.128.png -------------------------------------------------------------------------------- /qqX.system/icons/qqX.std.logo.256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuxVinyards/qqX/62e8bcf6fd4df6bc1a667509e166a423b0a48435/qqX.system/icons/qqX.std.logo.256.png -------------------------------------------------------------------------------- /qqX.system/icons/qqX.std.logo.500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuxVinyards/qqX/62e8bcf6fd4df6bc1a667509e166a423b0a48435/qqX.system/icons/qqX.std.logo.500.png -------------------------------------------------------------------------------- /qqX.system/icons/qqX.std.logo.96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuxVinyards/qqX/62e8bcf6fd4df6bc1a667509e166a423b0a48435/qqX.system/icons/qqX.std.logo.96.png -------------------------------------------------------------------------------- /qqX.system/qqX_add_in_boot_level_chrome: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # qqX project: early stage display add-ins 4 | 5 | ## Copyright (c) Alex Genovese https://github.com/TuxVinyards 6 | # SMALL CODE SNIPPETS eg the function printColor MAY BE USED 7 | # PERMISSIVELY in projects as MIT or similar, providing CLEAR ATTRIBUTIONS are shown. 8 | 9 | # Otherwise: 10 | 11 | # Licence GPL3 https://www.gnu.org/licenses 12 | 13 | # This program is free software: you can redistribute it and/or modify 14 | # it under the terms of the GNU General Public License as published by 15 | # the Free Software Foundation, either version 3 of the License, or 16 | # (at your option) any later version. 17 | 18 | # This program is distributed in the hope that it will be useful, 19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | # GNU General Public License for more details. 22 | 23 | # https://www.gnu.org/licenses 24 | 25 | # IF CODE IN GENERAL BECOMES USED IN ANY OTHER PROJECT, 26 | # THE GPL3 LICENCE APPLIES & YOU SHOULD SHOW CLEAR ATTRIBUTIONS. 27 | 28 | # shellcheck disable=SC1090,SC2034,SC2154 29 | 30 | X_Shade="3" 31 | 32 | 33 | source "$(pwd)/qqX.system/Func_echo_colors" 34 | 35 | 36 | # Variant on http://www.figlet.org/ -f mini (-k) (c) Alex Genovese 37 | 38 | qqX_logo() { 39 | echColor " _ _ \/ " 40 | echColor " (_| (_| /\ " 41 | echColor " |. |. " 42 | } 43 | 44 | 45 | # 46 | TITLEmain="[quickemu quickget X terminal project] - Ver: $qqX_Version " 47 | # 48 | 49 | 50 | qqX_logo_title() { 51 | 52 | if [[ $1 ]]; then 53 | 54 | LogoTitle="$1" 55 | 56 | else 57 | 58 | LogoTitle="$TITLEmain" 59 | 60 | fi 61 | 62 | echColor " _ _ \/ " 63 | echColor " (_| (_| /\ " 64 | echColor " |. |. $LogoTitle" 65 | 66 | } 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /qqX.system/qqX_copy_over: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # qqX helper file for developers: 4 | # Use to easy update desktop installed versions of qqX 5 | # with modified scripts and settings from your source folder (edit as required) 6 | 7 | # SourceFolder="$HOME/qqX" 8 | 9 | [[ ! $SourceFolder ]] && printf " \n\n A Source Folder must be given >> Dev's please edit this file at the top ... \n\n " && sleep 100 10 | 11 | cd "$SourceFolder" || echo " ERROR changing to Source Folder"; echo 12 | 13 | # need updating if there have been changes 14 | source "qqX.system/qqX_settings_update_transposer" 15 | source "qqX.system/Func_echo_colors" 16 | 17 | # VARIABLES for Non FHS OS packaging, also edit at start of main file 'qqX' ... 18 | # WiP: attention may need to be given to the qqX updater too ... 19 | # From qqX 1.6 onwards, a folder /usr/share/qqX should be created for the subfolder and files of qqX.main 20 | # And from 1.8 the subfolder qqX.builtins 21 | 22 | qqX_Std_Bin_Dir="/usr/bin" 23 | qqX_Std_Share_Path="/usr/share/qqX" 24 | 25 | function copy_files { 26 | printf "\n" 27 | # remove any old and possibly obsolete and unneeded files 28 | [[ -d "$qqX_Std_Share_Path/qqX.main" ]] && sudo rm -r "$qqX_Std_Share_Path/qqX.main" 29 | sudo cp -r qqX.main "$qqX_Std_Share_Path/" || CopyFail=1 30 | sudo cp qqX "$qqX_Std_Bin_Dir/qqX" || CopyFail=1 31 | sudo chmod a+rx -R "$qqX_Std_Share_Path/qqX.main" 32 | sudo chmod a+rx "$qqX_Std_Bin_Dir/qqX" 33 | source "qqX.system/qqX_settings_update_transposer" 34 | source "qqX.system/Func_echo_colors" 35 | printf "\n" 36 | } 37 | 38 | function copy_builtins { 39 | printf "\n" 40 | [[ -d "$qqX_Std_Share_Path/qqX.builtins" ]] && sudo rm -r "$qqX_Std_Share_Path/qqX.builtins" 41 | [[ -d "$qqX_Std_Share_Path/qqX.lib" ]] && sudo rm -r "$qqX_Std_Share_Path/qqX.lib" 42 | sudo cp -r qqX.builtins "$qqX_Std_Share_Path/" || CopyFail=1 43 | sudo chmod a+rx -R "$qqX_Std_Share_Path/qqX.builtins" 44 | sudo cp -r qqX.lib "$qqX_Std_Share_Path/" || CopyFail=1 45 | sudo ln -f -s "$qqX_Std_Share_Path/qqX.lib/floatversion" "$qqX_Std_Share_Path/qqX.lib/fv" 46 | sudo chmod a+rx -R "$qqX_Std_Share_Path/qqX.lib" 47 | source "qqX.system/qqX_settings_update_transposer" 48 | source "qqX.system/Func_echo_colors" 49 | printf "\n Builtins Copied \n" 50 | } 51 | 52 | Counter=0 53 | 54 | while true ; do 55 | 56 | if [[ $Counter == 0 ]] && [[ ! $DevSetUpdArrOutputScrn ]]; then 57 | printf "\033c" 58 | printf "\n From %s" "$SourceFolder" 59 | printColor "\n\n COPY the qqX script to usr/bin" 60 | printColor "\n\n COPY all qqX.main scripts to /usr/share/qqX \n" 61 | fi 62 | 63 | printf "\n\n Terminal may be kept open. Sudo will stay usable for a while ..." 64 | printColor "\n\n COPY > [enter] for qqX scripts" ; printf " [s] qqX SETTINGS update [b] copy /qqX.builtins to /usr/share/qqX \n\n" 65 | read -rp " > " UpdSets 66 | 67 | if [[ $UpdSets ]]; then 68 | [[ $UpdSets == "s" ]] && update_old_settings_file_with_new_options 69 | [[ $UpdSets == "b" ]] && copy_builtins 70 | ((Counter+=1)) 71 | else 72 | copy_files 73 | if [[ $CopyFail ]]; then printColor " COPY ERROR .... \n" ; else printf " Copied @ %s \n" "$(date +%H.%M.%S)" ; fi 74 | fi 75 | 76 | sleep 1 77 | 78 | ((Counter+=1)) ; [[ $Counter -ge 4 ]] && Counter=0 ; UpdSets= ; CopyFail= 79 | 80 | done 81 | 82 | -------------------------------------------------------------------------------- /qqX.system/qqX_settings_update_transposer: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ## qqX - quickemu quickget X terminal project 4 | 5 | # Helper file for qqX settings file updates 6 | 7 | ## Copyright (c) Alex Genovese https://github.com/TuxVinyards 8 | # SMALL CODE SNIPPETS eg the function printColor MAY BE USED 9 | # PERMISSIVELY in projects as MIT or similar, providing CLEAR ATTRIBUTIONS are shown. 10 | 11 | # Otherwise: 12 | 13 | # Licence GPL3 https://www.gnu.org/licenses 14 | 15 | # This program is free software: you can redistribute it and/or modify 16 | # it under the terms of the GNU General Public License as published by 17 | # the Free Software Foundation, either version 3 of the License, or 18 | # (at your option) any later version. 19 | 20 | # This program is distributed in the hope that it will be useful, 21 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | # GNU General Public License for more details. 24 | 25 | # https://www.gnu.org/licenses 26 | 27 | # IF CODE IN GENERAL BECOMES USED IN ANY OTHER PROJECT, 28 | # THE GPL3 LICENCE APPLIES & YOU SHOULD SHOW CLEAR ATTRIBUTIONS. 29 | 30 | 31 | function update_old_settings_file_with_new_options { 32 | 33 | Old_SettingsFile="$1" 34 | Template_SettingsFile="$2" 35 | Old_SettingsFileFolder="$3" 36 | 37 | ## USAGE: presumes this file to be run from a qqX release folder ... 38 | [[ ! $Template_SettingsFile ]] && Template_SettingsFile="qqX_settings" 39 | 40 | # presumes standard installation placement 41 | [[ ! $Old_SettingsFileFolder ]] && Old_SettingsFileFolder="$HOME/.qqX" 42 | [[ ! $Old_SettingsFile ]] && Old_SettingsFile="qqX_settings" 43 | 44 | # *and* presumes that a valid settings file has been found before this routine is called 45 | 46 | ## DEV Array output - Set =1 file or screen or both 47 | DevSetUpdArrOutputScrn= ; DevSetUpdArrOutputFile= 48 | 49 | ## 50 | 51 | ## 1: OLD FILE 52 | 53 | # The VM & Extra Folder lists are of unknown length & number so store as separate to add later. 54 | 55 | Grep_OLD_VM_Arrays_File="/tmp/grep.old.vm.arrays.txt" 56 | Grep_OLD_XF_Arrays_File="/tmp/grep.old.xf.arrays.txt" 57 | 58 | printf "" > "$Grep_OLD_VM_Arrays_File" 59 | printf "" > "$Grep_OLD_XF_Arrays_File" 60 | 61 | # A. Extract ALL non-commented lines 62 | mapfile -t GrepArr_OLD_OneLine_Settings <<< "$(grep '=' "$Old_SettingsFileFolder/$Old_SettingsFile" | grep -v '#')" 63 | 64 | if [[ $DevSetUpdArrOutputScrn ]]; then echo "Pre-Parse"; declare -p GrepArr_OLD_OneLine_Settings ; echo ; fi 65 | if [[ $DevSetUpdArrOutputFile ]]; then declare -p GrepArr_OLD_OneLine_Settings > "/tmp/qDevSet_OldOneLineArr_PreParse.txt" ; fi 66 | 67 | 68 | # B. Remove any existing, personalized folder array lists & keep them to one side for later. (from the non-commented above) 69 | 70 | for i in "${!GrepArr_OLD_OneLine_Settings[@]}"; do 71 | 72 | case "${GrepArr_OLD_OneLine_Settings[i]}" in 73 | 74 | *'VM_Array['*) 75 | printf "%s\n\n" "${GrepArr_OLD_OneLine_Settings[i]}" >> "$Grep_OLD_VM_Arrays_File" 76 | unset "GrepArr_OLD_OneLine_Settings[i]" 77 | ;; 78 | 79 | *'Extra_VM_Folder+='*) 80 | printf "%s\n\n" "${GrepArr_OLD_OneLine_Settings[i]}" >> "$Grep_OLD_XF_Arrays_File" 81 | unset "GrepArr_OLD_OneLine_Settings[i]" 82 | ;; 83 | 84 | *) true ;; 85 | 86 | esac 87 | 88 | done 89 | 90 | # Re-index as sections have been removed 91 | GrepArr_OLD_OneLine_Settings=("${GrepArr_OLD_OneLine_Settings[@]}") 92 | 93 | if [[ $DevSetUpdArrOutputScrn ]]; then echo "Post-Parse"; declare -p GrepArr_OLD_OneLine_Settings ; echo ; fi 94 | if [[ $DevSetUpdArrOutputFile ]]; then declare -p GrepArr_OLD_OneLine_Settings > "/tmp/qDevSet_OldOneLineArr_PostParse.txt" ; fi 95 | 96 | 97 | # C. Find & store the Base Name of each setting ie the Var= instead of the whole var & value 98 | GrepArr_OLD_BaseNameSettings=() 99 | for i in "${!GrepArr_OLD_OneLine_Settings[@]}" ; do 100 | GrepArr_OLD_BaseNameString="$(cut -d '=' -f1 <<< "${GrepArr_OLD_OneLine_Settings[i]}")" 101 | GrepArr_OLD_BaseNameString="${GrepArr_OLD_BaseNameString// /}""=" 102 | GrepArr_OLD_BaseNameSettings+=( "$GrepArr_OLD_BaseNameString" ) 103 | done 104 | 105 | if [[ $DevSetUpdArrOutputScrn ]]; then echo "Old BaseName"; declare -p GrepArr_OLD_BaseNameSettings ; echo ; fi 106 | if [[ $DevSetUpdArrOutputFile ]]; then declare -p GrepArr_OLD_BaseNameSettings > "/tmp/qDevSet_OldBaseNameArr.txt" ; fi 107 | 108 | 109 | ## 2: NEW FILE OUTPUT 110 | 111 | ## A. Make a backup to the installer folder (& check that it is a faithful copy) 112 | # Dev: move to trash to stop clutter. 113 | 114 | D_Stamp=".""$(date +%a.%d.%b.%G.%H.%M)" 115 | 116 | if [[ -d ".git" ]] && [[ $(type -p gio 2>/dev/null) ]]; then 117 | 118 | gio trash "$Old_SettingsFileFolder/$Old_SettingsFile" 119 | printColor "\n\n\n Starting Update: " 120 | printf "\n\n OLD SETTINGS file is being moved TO THE TRASH BIN (for recovery, if needed)\n\n" 121 | 122 | else 123 | 124 | cp "$Old_SettingsFileFolder/$Old_SettingsFile" "$(pwd)/qqX_settings.$D_Stamp.old.txt" 125 | 126 | ChkSumOld="$(md5sum "$Old_SettingsFileFolder/$Old_SettingsFile" 2>/dev/null | head -c 32)" 127 | ChkSumBackup="$(md5sum "$(pwd)/qqX_settings.$D_Stamp.old.txt" 2>/dev/null | head -c 32)" 128 | 129 | if [[ $ChkSumOld != "$ChkSumBackup" ]]; then 130 | printColor "\n\n ERROR: unable to make a backup of the settings file." 131 | printf "\n\n Aborting update. Please check at https://github.com/TuxVinyards/qqX/issues" 132 | printf "\n\n\n [enter] to exit ... \n\n" 133 | read -rp " > " 134 | printf "\n\n" 135 | exit 136 | else printf "\n\n A backup of the original settings file has been made & verified \n\n" 137 | fi 138 | 139 | fi 140 | 141 | ## B. create new composite file, in place of the old 142 | TargetNew="$Old_SettingsFileFolder/$Old_SettingsFile" 143 | printf "" > "$TargetNew" 144 | 145 | 146 | ## C. map ALL of template file lines to an array for outputting to the new file 147 | mapfile -t GrepArr_NEW < "$Template_SettingsFile" 148 | 149 | if [[ $DevSetUpdArrOutputScrn ]]; then echo "GrepArr_NEW Pre_MainLoop"; declare -p GrepArr_NEW ; echo ; fi 150 | if [[ $DevSetUpdArrOutputFile ]]; then declare -p GrepArr_NEW > "/tmp/qDevSet_GrepArr_NEW_Pre_MainLoop.txt" ; fi 151 | 152 | VM_Old_TxDone= 153 | XF_Old_TxDone= 154 | 155 | if [[ $DevSetUpdArrOutputFile ]]; then 156 | echo > "/tmp/qDevSet_Match_NewOld.txt" 157 | echo > "/tmp/qDevSet_No_Match.txt" 158 | echo > "/tmp/qDevSet_CopyOver.txt" 159 | echo > "/tmp/qDevSet_iVals.txt" 160 | echo > "/tmp/qDevSet_ProposedMatch.txt" 161 | fi 162 | 163 | if [[ $DevSetUpdArrOutputScrn || $DevSetUpdArrOutputFile ]]; then 164 | printf "\n\n Pre Main Loop point: \n\n" 165 | # read -rp " > " 166 | fi 167 | 168 | ## MAIN LOOP: 169 | 170 | for i in "${!GrepArr_NEW[@]}"; do 171 | 172 | MatchNewOld= 173 | SkipCopy= 174 | 175 | if [[ $DevSetUpdArrOutputScrn ]]; then echo; echo "i = $i"; fi 176 | if [[ $DevSetUpdArrOutputFile ]]; then echo "i = $i" >> "/tmp/qDevSet_iVals.txt" ; fi 177 | 178 | case "${GrepArr_NEW[i]}" in 179 | 180 | # Detect array zones and copy the *stored* customised block to the new file instead 181 | # one set only avoid accumulating more new ones (also the line spaces between the extra statements) 182 | 183 | *'Extra_VM_Folder+='*) 184 | if [[ $XF_Old_TxDone ]]; then 185 | SkipCopy=1 ; SkipLineSpace=1 186 | else 187 | if grep -q 'Extra_VM_Folder+=' "$Grep_OLD_XF_Arrays_File" ; then 188 | cat "$Grep_OLD_XF_Arrays_File" >> "$TargetNew" 189 | XF_Old_TxDone=1 190 | fi 191 | fi 192 | ;; 193 | 194 | *'# ADD here'*) 195 | 196 | if [[ ! $VM_Old_TxDone ]]; then 197 | printf "%s\n\n" "${GrepArr_NEW[i]}" >> "$TargetNew" 198 | SkipCopy=1 199 | if grep -q 'VM_Array' "$Grep_OLD_VM_Arrays_File" ; then 200 | cat "$Grep_OLD_VM_Arrays_File" >> "$TargetNew" 201 | VM_Old_TxDone=1 202 | fi 203 | fi 204 | ;; 205 | 206 | esac 207 | 208 | [[ $SkipLineSpace ]] && SkipCopy=1 && SkipLineSpace= 209 | 210 | # One Array set only, avoid accumulating 'new' ones each time 211 | [[ $XF_Old_TxDone ]] && [[ "${GrepArr_NEW[i]}" == *'xtra_VM_Folder+='* ]] && SkipCopy=1 && SkipLineSpace=1 212 | [[ $VM_Old_TxDone ]] && [[ "${GrepArr_NEW[i]}" == *'M_Array['* ]] && SkipCopy=1 && SkipLineSpace=1 213 | 214 | 215 | if [[ ! $SkipCopy ]] && [[ ! $SkipLineSpace ]] ; then 216 | 217 | if ! grep -q '#' <<< "${GrepArr_NEW[i]}" && grep -q '=' <<< "${GrepArr_NEW[i]}" ; then 218 | 219 | ProposedNew_BaseNameSetting= 220 | ProposedNew_BaseNameSetting="$(cut -d '=' -f1 <<< "${GrepArr_NEW[i]}")" 221 | ProposedNew_BaseNameSetting="${ProposedNew_BaseNameSetting// /}""=" 222 | 223 | for n in "${!GrepArr_OLD_BaseNameSettings[@]}" ; do 224 | 225 | if [[ $DevSetUpdArrOutputScrn ]]; then 226 | printf "Match Test- '%s' - '%s'\n" "$ProposedNew_BaseNameSetting" "${GrepArr_OLD_BaseNameSettings[n]}" 227 | fi 228 | if [[ $DevSetUpdArrOutputFile ]]; then 229 | printf "Match Test- '%s' - '%s'\n" "$ProposedNew_BaseNameSetting" "${GrepArr_OLD_BaseNameSettings[n]}" >> "/tmp/qDevSet_ProposedMatch.txt" 230 | fi 231 | 232 | if [[ $ProposedNew_BaseNameSetting == "${GrepArr_OLD_BaseNameSettings[n]}" ]] ; then 233 | MatchNewOld=1 234 | echo "${GrepArr_OLD_OneLine_Settings[n]}" >> "$TargetNew" 235 | if [[ $DevSetUpdArrOutputScrn ]]; then echo "MatchNewOld > Copy Old - ${GrepArr_OLD_OneLine_Settings[n]}"; fi 236 | if [[ $DevSetUpdArrOutputFile ]]; then echo "MatchNewOld > Copy Old - ${GrepArr_OLD_OneLine_Settings[n]}" >> "/tmp/qDevSet_Match_NewOld.txt" ; fi 237 | break 238 | fi 239 | done 240 | 241 | fi 242 | 243 | [[ ! $MatchNewOld ]] && echo "${GrepArr_NEW[i]}" >> "$TargetNew" 244 | 245 | if [[ $DevSetUpdArrOutputScrn ]] && [[ ! $MatchNewOld ]]; then echo "No Match > Copy New - ${GrepArr_NEW[i]}" ; fi 246 | if [[ $DevSetUpdArrOutputFile ]] && [[ ! $MatchNewOld ]]; then echo "No Match > Copy New - ${GrepArr_NEW[i]}" >> "/tmp/qDevSet_No_Match.txt" ; fi 247 | 248 | elif [[ ! $SkipCopy ]]; then 249 | 250 | if [[ ! $SkipLineSpace ]]; then echo "${GrepArr_NEW[i]}" >> "$TargetNew"; fi 251 | 252 | if [[ $DevSetUpdArrOutputScrn ]]; then echo "Copy - ${GrepArr_NEW[i]}"; fi 253 | if [[ $DevSetUpdArrOutputFile ]]; then echo "Copy - ${GrepArr_NEW[i]}" >> "/tmp/qDevSet_CopyOver.txt" ; fi 254 | 255 | fi 256 | 257 | done 258 | 259 | [[ $DevSetUpdArrOutputScrn ]] && printf "\n\n" 260 | printf " The settings file has now been updated. \n All recognised original settings have been transferred. \n\n" 261 | 262 | } 263 | 264 | # vim:tabstop=2:shiftwidth=2:expandtab 265 | 266 | ## 267 | -------------------------------------------------------------------------------- /qqX.system/qqX_termfind: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ## qqX - quickemu quickget X terminal project 4 | 5 | # Helper file for qqX settings file updates 6 | 7 | ## Copyright (c) Alex Genovese https://github.com/TuxVinyards 8 | # SMALL CODE SNIPPETS eg the function printColor MAY BE USED 9 | # PERMISSIVELY in projects as MIT or similar, providing CLEAR ATTRIBUTIONS are shown. 10 | 11 | # Otherwise: 12 | 13 | # Licence GPL3 https://www.gnu.org/licenses 14 | 15 | # This program is free software: you can redistribute it and/or modify 16 | # it under the terms of the GNU General Public License as published by 17 | # the Free Software Foundation, either version 3 of the License, or 18 | # (at your option) any later version. 19 | 20 | # This program is distributed in the hope that it will be useful, 21 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | # GNU General Public License for more details. 24 | 25 | # https://www.gnu.org/licenses 26 | 27 | # IF CODE IN GENERAL BECOMES USED IN ANY OTHER PROJECT, 28 | # THE GPL3 LICENCE APPLIES & YOU SHOULD SHOW CLEAR ATTRIBUTIONS. 29 | 30 | 31 | ## Older code from 'ffX project' (c) Alex Genovese Refactored @ 03/2024 32 | 33 | 34 | printf "\033c" 35 | printColor "\n\n To create shortcut launchers, the name of a Linux Terminal Emulator is needed \n\n" 36 | 37 | # 'UserTerm' may be supplied by a calling script or setup installer settings 38 | 39 | if [[ ! $UserTerm ]]; then 40 | 41 | echo " Checking ...."; echo 42 | 43 | ## Almost all terminal emulators have 'term' as part of the name. 44 | # Check here for the notable exception: 45 | UserTerm="$(pgrep -li "konsole" )" 46 | 47 | TermWordChk="$(pgrep -a term | grep qqX | awk '{print $2}')" 48 | if [[ $TermWordChk ]]; then 49 | printf "\n Terminal might be \'%s\' \n\n" "$(basename "$TermWordChk")" 50 | [[ ! $UserTerm ]] && UserTerm="$(basename "$TermWordChk")" 51 | else 52 | printf "\n Terminal might be: \n\n" 53 | pgrep -a term 54 | printf "\n\n" 55 | fi 56 | 57 | if [[ $UserTerm ]]; then 58 | printColor "\n\n UserTerm is %s ? " "$UserTerm" 59 | printf "\n\n Is this correct? [enter] yes [n] no, possibly not \n\n" 60 | read -rp " > " FirstTermChk 61 | echo 62 | if [[ $FirstTermChk == "n" ]]; then UserTerm=; else UserTerm_Found=1; fi 63 | fi 64 | 65 | fi 66 | 67 | if [[ $WSL_Install ]]; then 68 | 69 | # add basic suggestion 70 | [[ $(type -p "gnome-terminal") ]] && UserTerm="gnome-terminal" 71 | 72 | elif [[ ! $UserTerm ]]; then 73 | 74 | echo " Checking ...."; echo 75 | # Scan for processes with 'term' as part of the name (& ignore lines with scriptname ) 76 | # https://stackoverflow.com/questions/35006457/choosing-between-0-and-bash-source & http://mywiki.wooledge.org/BashFAQ/001 77 | 78 | TermGrep=() 79 | 80 | while IFS= read -r Line; do 81 | [[ "$Line" == *"qqX_termfind"* ]] && continue 82 | TermGrep+=("$Line") 83 | done < <(pgrep -li "term") 84 | 85 | TermCount="${#TermGrep[*]}" 86 | 87 | if [[ "$TermCount" -eq 1 ]]; then 88 | 89 | UserTerm_PID="${TermGrep[0]}" 90 | UserTerm_PID="$(echo "$UserTerm_PID" | tr -cd '[:digit:]')" 91 | 92 | elif [[ "$TermCount" -gt 1 ]]; then 93 | 94 | printf "\n %s\n\n" "${TermGrep[@]}" 95 | printf " Some lightweight terminals may not be compatible (See 'known-issues')" 96 | [[ $UserTerm == "gnome-terminal" ]] && printf "\n\n 'gnome-terminal' is also used by various desktops, such as Mint Cinnamon, Budgie and Ubuntu ..." 97 | printf "\n\n Checking the menubar for help > about, could show the name ...." 98 | printf "\n\n Enter the number of your Terminal Emulator, or [n] none of these, do more tests \n\n" 99 | read -rp " > " UserTerm_PID 100 | echo 101 | if [[ $UserTerm_PID == "n" ]]; then UserTerm_PID= ; UserTerm_Found= ; fi 102 | 103 | fi 104 | 105 | if [[ $UserTerm_PID ]]; then 106 | 107 | if [[ -d "/proc/$UserTerm_PID" ]]; then 108 | UserTerm="$(realpath /proc/"$UserTerm_PID"/exe)" 109 | UserTerm="${UserTerm##*/}" 110 | else 111 | echo " Error: PID not found"; echo 112 | UserTerm= ; UserTerm_PID= ; UserTerm_Found= 113 | fi 114 | fi 115 | 116 | if [[ $UserTerm ]] && [[ ! $(type -p "$UserTerm") ]]; then UserTerm= 117 | fi 118 | 119 | if [[ ! $UserTerm ]]; then 120 | TermWordChk="$(pgrep -a term | grep qqX | awk '{print $2}')" 121 | if [[ $TermWordChk ]]; then UserTerm="$TermWordChk" ; UserTerm_Found= 122 | fi 123 | fi 124 | 125 | fi 126 | 127 | while [[ ! $UserTerm_Found ]]; do 128 | 129 | if [[ ! $UserTerm ]]; then 130 | 131 | if [[ ! $WSL_Install ]]; then 132 | 133 | PID_Term_ProbeArr=() 134 | echo 135 | echColor " Basic tests are unable to find name of your terminal emulator"; echo 136 | 137 | # LOAD NOTES from function at start of calling script qqX installer 138 | print_terminal_notes 139 | echo 140 | echo " Further tests: " 141 | 142 | if [[ $TermFind_ReRun ]]; then 143 | echo; echo 144 | echo " SHELL is $SHELL" ; echo 145 | head "/proc/$PPID/status" 146 | echo 147 | fi 148 | 149 | UserTerm="$(realpath /proc/$PPID/exe)" 150 | UserTerm="${UserTerm##*/}" 151 | PID_Term_ProbeArr+=("$UserTerm") 152 | 153 | if [[ $TermFind_ReRun ]]; then 154 | echo " UserTerm is $UserTerm" ; echo 155 | echo " Parent PID is $PPID" ; echo 156 | echo " Process Command is $(cat "/proc/$PPID/comm" 2>/dev/null)"; echo 157 | echo 158 | fi 159 | 160 | while [[ "${GrandParentPID:-$PPID}" -gt 1 ]]; do 161 | 162 | GrandParentPID="$(grep -i -e "PPid:" "/proc/${GrandParentPID:-$PPID}/status")" 163 | GrandParentPID="$(echo "$GrandParentPID" | tr -cd '[:digit:]')" 164 | # See http://mywiki.wooledge.org/BashFAQ/073 165 | 166 | [[ $TermFind_ReRun ]] && echo " *******************************" && echo 167 | 168 | if [[ "$GrandParentPID" -gt 1 ]]; then 169 | 170 | if [[ $TermFind_ReRun ]]; then 171 | echo 172 | head "/proc/$GrandParentPID/status" 173 | echo 174 | echo " GrandParentPID is $GrandParentPID"; echo 175 | echo " Process Command is $(cat "/proc/$GrandParentPID/comm" 2>/dev/null)"; echo 176 | fi 177 | 178 | GrandParent_Name="$(realpath /proc/"$GrandParentPID"/exe)" 179 | GrandParent_Name="${GrandParent_Name##*/}" 180 | UserTerm="$GrandParent_Name" 181 | PID_Term_ProbeArr+=("$UserTerm") 182 | [[ $TermFind_ReRun ]] && echo " GrandParent_Name is $GrandParent_Name"; echo 183 | 184 | fi 185 | 186 | done 187 | 188 | TermWordChk="$(pgrep -a term | grep qqX | awk '{print $2}')" 189 | if [[ $TermWordChk ]]; then printf "\n Terminal might also be \'%s\' \n" "$(basename "$TermWordChk")" 190 | fi 191 | 192 | fi 193 | 194 | if [[ $TermFind_ReRun || $WSL_Install ]]; then 195 | 196 | if [[ $ErrorPID ]] && [[ ! $WSL_Install ]]; then echo " PID not found ...." ; echo ; ErrorPID= 197 | fi 198 | 199 | echo 200 | echo " To create a shortcut, a name is needed .... Check/Recheck the script output"; echo 201 | echo " You can enter the name here, if you know the correct command name"; echo 202 | echo " Checking the menubar for help > about, could show the name ...."; echo 203 | echo " Or use a different method & manually add 'UserTerm' "; echo 204 | echo " to the 'setup and install' script, at the start."; echo; echo 205 | 206 | [[ $WSL_Install ]] && echColor " You may need to install a terminal, if you have not done so. "; echo 207 | 208 | echColor " Enter the PID number of your Terminal Emulator, [n] if you know the name, [q] to quit "; echo 209 | read -rp " > " UserTerm_PID 210 | echo 211 | 212 | ProbTerm=" Possibly" 213 | 214 | if [[ $UserTerm_PID == "q" ]]; then exit 215 | 216 | elif [[ $UserTerm_PID == "n" ]]; then 217 | 218 | # Unlikely but with so many Linux distro & desktop combinations and customisations ... 219 | read -rp " Command Name for you terminal is: " UserTerm 220 | UserTerm_Found=1 221 | ProbTerm= 222 | echo 223 | 224 | else 225 | 226 | if [[ -d "/proc/$UserTerm_PID" ]]; then 227 | UserTerm="$(realpath /proc/"$UserTerm_PID"/exe 2>/dev/null)" 228 | UserTerm="${UserTerm##*/}" 229 | else 230 | UserTerm= 231 | UserTerm_PID= 232 | UserTerm_Found= 233 | ErrorPID=1 234 | fi 235 | 236 | fi 237 | 238 | else 239 | 240 | echo 241 | i=0 242 | for ProcessName in "${PID_Term_ProbeArr[@]}" ; do 243 | [[ $ProcessName == "bash" ]] || [[ $ProcessName == "dash" ]] || [[ $ProcessName == "sh" ]] && ProcessShellpoint="$i" && ((ProcessShellpoint+=1)) 244 | ((i+=1)) 245 | done 246 | 247 | if [[ $ProcessShellpoint ]]; then UserTerm="${PID_Term_ProbeArr[$ProcessShellpoint]}"; else UserTerm="${PID_Term_ProbeArr[0]}"; fi 248 | # Usually either the first one or the one after the first occurance of 'sh' 249 | ProbTerm=" Probably" 250 | 251 | fi 252 | 253 | TermFind_ReRun=1 254 | GrandParentPID= 255 | 256 | fi 257 | 258 | # Add any modifiers here, if needed 259 | [[ "$UserTerm" == gnome-terminal* ]] && UserTerm="gnome-terminal" 260 | 261 | if [[ $UserTerm ]] ; then 262 | echColor " $ProbTerm UserTerm is $UserTerm" ; echo 263 | [[ $UserTerm == "gnome-terminal" ]] && echo " Note: 'gnome-terminal' is also used by various desktops, such as Mint Cinnamon, Budgie and Ubuntu ..." && echo 264 | echo " Is this correct? [enter] yes, probably ... [n] no, I am still not sure " ; echo 265 | read -rp " > " 266 | echo 267 | if [[ $REPLY == "n" ]]; then UserTerm= ; UserTerm_Found= ; else UserTerm_Found=1 ; fi 268 | fi 269 | 270 | if [[ ! $UserTerm ]]; then printf "\033c"; echo; echo ; fi 271 | ProbTerm= 272 | 273 | done 274 | 275 | 276 | # LOAD NOTES from function at start of calling script qqX installer 277 | 278 | print_terminal_notes 279 | 280 | echo 281 | echo; echColor " Terminal = $UserTerm"; echo 282 | read -rp " [enter] to continue > " 283 | echo 284 | 285 | # https://code.visualstudio.com/ (recommended) 286 | # vim:tabstop=2:shiftwidth=2:expandtab 287 | -------------------------------------------------------------------------------- /qqX_setup_and_install: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # Copyright (c) Alex Genovese https://github.com/TuxVinyards 4 | 5 | # THIS SCRIPT SHOULD BE RUN FROM INSIDE THE qqX DOWNLOAD/SOURCE FOLDER, where it will expect to find the right files 6 | # OR run the target script "./qqX_system_install" from qqX.system folder directly 7 | 8 | # From version 1.10.03 A POSIX FRIENDLY .sh SCRIPT is USED AS A WRAPPER, to make sure that it double click executes properly 9 | # in Qt / KDE XFCE etc desktop environments. 10 | 11 | # IF: your file manager allows > double mouse click & 'run in terminal' or 'execute' 12 | 13 | # OR: in the script's folder > right click > 'run as program' or 'run in konsole' or 'run in terminal' 14 | 15 | # OR: in the script's folder > right click > open in terminal > type "./qqX_setup_and_install" (note prefix ./) 16 | 17 | # FOR ANY NEWER OR NON-STANDARD TERMINAL, SETTING UP A CUSTOM TERMINAL PROFILE IS RECOMMENDED 18 | # as this will cause the values to be stored for future updates. 19 | 20 | # CUSTOM terminal sizes & profile should be initially set just after the initial system checking lines, at around line 290 21 | # in the target script "./qqX_system_install" in the qqX.system folder. See notes in the target script. 22 | 23 | # shellcheck disable=SC2009,SC2034 24 | 25 | # Terminal name may also be set here, for one-time usage, if '-e' exec functions are recognised: 26 | UserTerm= 27 | 28 | printf "" > "/tmp/qqX.termlist" 29 | 30 | if [ -n "$UserTerm" ]; then printf "%s\n" "$UserTerm" > "/tmp/qqX.termlist" 31 | fi 32 | 33 | # https://stackoverflow.com/questions/35385962/arrays-in-a-posix-compliant-shell#59353917 34 | # NB 'Type' is a Bash builtin, type -p is not supported in 'dash' so command -v needed. 35 | 36 | ## Selection of terminal: 37 | 38 | # Priority is given to main stream user bases and those offering geometry parameters 39 | # 85% of users are probably covered by the first four: "gnome-terminal" "konsole" "mate-terminal" "xfce4-terminal" 40 | # Over 95% probably when "qterminal" and "alacritty" are added. 41 | 42 | # There are several popular post-install terminals with various special feature sets. 43 | # However, it is very unlikely to find these and these alone. An underlying standard desktop environment terminal 44 | # can generally be expected to be available so exhaustive checks are not needed. 45 | 46 | # Kitty is relatively stable and has more or less standard behaviour, so it has been added for good measure. 47 | 48 | # On the other hand, many of these leading edge, often cross-platform terminals are still in active development 49 | # and are liable to change. We need to view these as unstable. 50 | 51 | # But, development should be encouraged and revisits made: 52 | 53 | # Wezterm @ 10/2024, for example, has much potential but an idiosyncratic command line, 54 | # requests manual closure and has over 900 open issues with which to contend ... 55 | 56 | # At the other extreme, and with the advent of Wayland, other projects have been abandoned completely. 57 | # Termite, for example, now encourages the use of Alacritty ... 58 | 59 | # Users with Window Managers, not Desktop Envs will be familiar with terminals and settings at the cmd line 60 | # However add some well known interfaces where possible, eg. urxvt that comes with 'i3' as this helps with running of QF 61 | 62 | set -- "gnome-terminal" "konsole" "mate-terminal" "xfce4-terminal" "qterminal" "lxterminal" "roxterm" "urxvt" "uxterm" "xterm" 63 | set "$@" -- "alacritty" "kitty" "foot" 64 | 65 | while [ -n "$1" ] ; do 66 | if [ -n "$(command -v "$1")" ] ; then printf "%s\n" "$1" >> "/tmp/qqX.termlist" 67 | fi 68 | shift 69 | done 70 | 71 | # Priority listing should have placed the top entry as being the most main stream and being one with sufficient size. 72 | # At this stage we are looking for something reliable to run the actual terminal selection script. 73 | if [ -n "$(cat "/tmp/qqX.termlist")" ]; then UserTerm="$(head -n 1 "/tmp/qqX.termlist")" 74 | fi 75 | 76 | # If there is still nothing, try a barrel scrape for something running that might have 'term' in the name ... 77 | if [ -z "$UserTerm" ]; then TermGrep="$(pgrep -a term | grep qqX | tr -cd '[:print:]' | awk '{print $2}')" 78 | if [ -n "$TermGrep" ]; then UserTerm="$(basename "$TermGrep")" 79 | fi 80 | fi 81 | 82 | DefWidth=150 83 | DefHeight=45 84 | 85 | if [ "$UserTerm" = "gnome-terminal" ]; then 86 | "$UserTerm" --geometry ${DefWidth}x${DefHeight} --wait -- "./qqX.system/qqX_system_install" 87 | 88 | elif [ "$UserTerm" = "konsole" ]; then 89 | "$UserTerm" --hide-menubar --hide-tabbar --notransparency -p TerminalColumns=145 -p TerminalRows=50 -e "./qqX.system/qqX_system_install" 90 | 91 | elif [ "$UserTerm" = "mate-terminal" ]; then 92 | "$UserTerm" --geometry=145x50 --hide-menubar -e "./qqX.system/qqX_system_install" 93 | 94 | elif [ "$UserTerm" = "xfce4-terminal" ]; then 95 | "$UserTerm" --geometry=145x50 --hide-menubar --hide-toolbar -e "./qqX.system/qqX_system_install" 96 | 97 | elif [ "$UserTerm" = "qterminal" ]; then 98 | # create profile for geometry 99 | if [ -e "$HOME/.config/qterminal.org/qterminal.ini" ]; then 100 | TermConf_User="$HOME/.config/qterminal.org/qterminal.ini" 101 | TermConf_Tmp="/tmp/qq-qterminal.ini" 102 | # sed -i is not always present, use grep. 103 | # But not 'grep -i' as lower case 'size=@' is separate from other camelCase entries with *Size=@ 104 | grep -B 500 'size=@' "$TermConf_User" | head -n -1 > "$TermConf_Tmp" 105 | echo "size=@(1200 600)" >> "$TermConf_Tmp" 106 | grep -A 500 'size=@' "$TermConf_User" | tail -n +2 >> "$TermConf_Tmp" 107 | "$UserTerm" -p "$TermConf_Tmp" -e "./qqX.system/qqX_system_install" 108 | else 109 | "$UserTerm" -e "./qqX.system/qqX_system_install" 110 | fi 111 | 112 | elif [ "$UserTerm" = "alacritty" ]; then 113 | # create profile for geometry 114 | if [ -e "$HOME/.config/alacritty/alacritty.toml" ]; then 115 | TermConf_User="$HOME/.config/alacritty/alacritty.toml" 116 | TermConf_Tmp="/tmp/qq-alacritty.toml" 117 | # sed -i is not always present, use grep. 118 | grep -B 500 'window.dimensions' "$TermConf_User" > "$TermConf_Tmp" 119 | { echo "columns = 150" ; echo "lines = 45"; } >> "$TermConf_Tmp" 120 | grep -A 500 'window.dimensions' "$TermConf_User" | tail -n +4 >> "$TermConf_Tmp" 121 | "$UserTerm" --config-file "$TermConf_Tmp" -e "./qqX.system/qqX_system_install" 122 | else 123 | "$UserTerm" -e "./qqX.system/qqX_system_install" 124 | fi 125 | 126 | elif [ "$UserTerm" = "lxterminal" ] || [ "$UserTerm" = "roxterm" ]; then 127 | "$UserTerm" --geometry=145x50 -e "./qqX.system/qqX_system_install" 128 | 129 | elif [ "$UserTerm" = "urxvt" ]; then 130 | "$UserTerm" -e "./qqX.system/qqX_system_install" 131 | 132 | elif [ "$UserTerm" = "uxterm" ] || [ "$UserTerm" = "xterm" ]; then 133 | "$UserTerm" -geometry 145x50 -fa truetype -fs 12 -fg white -bg black -e "./qqX.system/qqX_system_install" 134 | 135 | # A popular (and cross-platform) choice for lightweight Wayland is Alacritty, which is higher up the 'set' list. 136 | 137 | elif [ "$UserTerm" = "kitty" ] ; then 138 | # Unlikely to be a primary terminal but added as is quite popular, especially with cross-platform users. 139 | "$UserTerm" "./qqX.system/qqX_system_install" 140 | 141 | elif [ "$UserTerm" = "foot" ] ; then 142 | # FOO Terminal is a lightweight terminal starting to appear in places for Wayland 143 | # but @ 2024 tests have shown it to throw trivial warnings when switching shells, 144 | # hence the '2>/dev/null' and placement (for now) towards the end 145 | "$UserTerm" "./qqX.system/qqX_system_install" 2>/dev/null 146 | 147 | # elif [ "$UserTerm" = "wezterm" ] ; then 148 | #"$UserTerm" start --cwd "$(pwd)" -- "./qqX.system/qqX_system_install" # REVIEW 149 | 150 | elif [ -n "$UserTerm" ]; then 151 | # Standard start for unknown possibilities, size params not used: 152 | # Terminals will normally accept '-e' even if it is not the preferred method # REVIEW 153 | "$UserTerm" -e "./qqX.system/qqX_system_install" 154 | 155 | elif [ -t 1 ] ; then 156 | # Probably 97 to 100% chance of finding the matching desktop environment default terminal. 157 | # If none found, where possible, if stdout is available, give help message. 158 | # https://stackoverflow.com/a/911213 159 | printf "\n\n TERMINAL NAME not found \n\n" 160 | # show the help notes at top of script 161 | grep -m 1 -A 10 'IF:' "$0" 162 | printf "\n\n [enter] to exit \n\n " 163 | read -r reply 164 | fi 165 | 166 | if [ -t 1 ] ; then 167 | printf "\n" 168 | printf "\n\n qqX installer: %s" "$UserTerm" 169 | printf "\n" 170 | printf "\n\n Help and info always available at https://github.com/TuxVinyards/qqX/wiki" 171 | printf "\n" 172 | printf "\n\n Your terminal or distro/terminal combination may need, or may have needed " 173 | printf "\n\n this window to stay open until qqX has completed its tasks ..." 174 | printf "\n" 175 | printf "\n\n [enter] to close \n\n " 176 | read -r reply 177 | fi 178 | 179 | # Exit the script. But also check whether it has been run with a --hold optioned terminal, as Dolphin does, for example. 180 | PidRun="$(ps -eF | grep sh | grep "$$" | grep "$0" | tr -cd '[:print:]' | awk '{print $3}')" 181 | 182 | # Needs SIGHUP in KDE or logs out of desktop session... See https://en.wikipedia.org/wiki/Signal_(IPC)#Handling_signals 183 | if [ -n "$PidRun" ]; then kill -s 1 "$PidRun" 184 | else exit 185 | fi 186 | 187 | # Licence GPL3 https://www.gnu.org/licenses 188 | 189 | # This program is free software: you can redistribute it and/or modify 190 | # it under the terms of the GNU General Public License as published by 191 | # the Free Software Foundation, either version 3 of the License, or 192 | # (at your option) any later version. 193 | 194 | # This program is distributed in the hope that it will be useful, 195 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 196 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 197 | # GNU General Public License for more details. 198 | 199 | # https://www.gnu.org/licenses 200 | 201 | # IF CODE IN GENERAL BECOMES USED IN ANY OTHER PROJECT, 202 | # THE GPL3 LICENCE APPLIES & YOU SHOULD SHOW CLEAR ATTRIBUTIONS. 203 | 204 | # But, that said, and without prejudice to the above, 205 | # SMALL CODE SNIPPETS, eg the function printColor, MAY BE USED PERMISSIVELY 206 | # in projects, as MIT or similar, providing CLEAR ATTRIBUTIONS are shown. 207 | 208 | # qqX - quickemu quickget X terminal project 209 | 210 | # https://code.visualstudio.com/ (recommended) 211 | # vim:tabstop=2:shiftwidth=2:expandtab -------------------------------------------------------------------------------- /qqX_wiki_Install.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuxVinyards/qqX/62e8bcf6fd4df6bc1a667509e166a423b0a48435/qqX_wiki_Install.pdf --------------------------------------------------------------------------------