├── .gitignore ├── README.md ├── debian ├── changelog ├── compat ├── control ├── copyright ├── rules └── source │ ├── format │ └── local-options ├── images.qrc ├── images ├── close.svg ├── icon.svg ├── info.svg ├── iso.svg ├── minimize.svg ├── pardus.svg ├── piw.ico └── usb.svg ├── pardus-imagewriter.desktop ├── pardus-imagewriter.pro ├── piw.exe.manifest ├── piw.rc ├── qml.qrc ├── qtquickcontrols2.conf ├── screenshots ├── piw_macOS.jpg ├── piw_pardus-1.png ├── piw_pardus-2.png ├── piw_pardus-3.png ├── piw_pardus-4.png ├── piw_pardus-5.png ├── piw_pardus-6.png └── piw_windows.jpg ├── src ├── common.cpp ├── common.h ├── devicehandler.cpp ├── devicehandler.h ├── helper.cpp ├── helper.h ├── imagewriter.cpp ├── imagewriter.h ├── main.cpp ├── physicaldevice.cpp ├── physicaldevice.h ├── platform.h ├── platform_lin.cpp ├── platform_mac.mm ├── platform_win.cpp ├── signalhandler.cpp ├── signalhandler.h ├── usbdevice.h ├── usbdevicemonitor.h ├── usbdevicemonitor_lin.cpp ├── usbdevicemonitor_lin_p.h ├── usbdevicemonitor_mac.mm ├── usbdevicemonitor_mac_p.h ├── usbdevicemonitor_win.cpp └── usbdevicemonitor_win_p.h ├── tr.org.pardus.pkexec.pardus-imagewriter.policy ├── translations.qrc ├── translations ├── piw_tr.qm └── piw_tr.ts └── ui ├── Burn.qml ├── File.qml ├── ProgressBarCircle.qml ├── Target.qml └── main.qml /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | *.pyc 10 | *.pyo 11 | 12 | # Packages # 13 | ############ 14 | # it's better to unpack these files and commit the raw source 15 | # git has its own built in compression methods 16 | *.7z 17 | *.dmg 18 | *.gz 19 | *.iso 20 | *.jar 21 | *.rar 22 | *.tar 23 | *.zip 24 | *.deb 25 | *.tar.xz 26 | *.tar.gz 27 | 28 | 29 | # Logs and databases # 30 | ###################### 31 | *.log 32 | *.sql 33 | *.sqlite 34 | 35 | # OS generated files # 36 | ###################### 37 | .DS_Store 38 | .DS_Store? 39 | ._* 40 | .Spotlight-V100 41 | .Trashes 42 | ehthumbs.db 43 | Thumbs.db 44 | 45 | # Temporary files left by editors # 46 | ################################### 47 | *.swp 48 | 49 | # Various signature files # 50 | ########################### 51 | *.dsc 52 | *.pro.user 53 | 54 | # Build directory# 55 | ################## 56 | build/ 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pardus Image Writer 2 | 3 | Simple Qt cpp backend, qml frontend disk image writing application. 4 | 5 | ## Screen Shots 6 | 7 | ### On Linux (Pardus 17.1 GNU/Linux) 8 | 9 | ![p1](/screenshots/piw_pardus-1.png) ![p2](/screenshots/piw_pardus-2.png) 10 | 11 | ![p3](/screenshots/piw_pardus-3.png) ![p4](/screenshots/piw_pardus-4.png) 12 | 13 | ![p5](/screenshots/piw_pardus-5.png) ![p6](/screenshots/piw_pardus-6.png) 14 | 15 | ### On Windows (10 Home) 16 | 17 | If you need a standalone `piw.exe` tested on Windows 10, 18 | [here](https://www.pardus.org.tr/pardus-usb-olusturma/) it is. Guess the 19 | install link if you are not a Turkish speaker then follow the instruction 20 | screenshots to get your ISO file burnt into your USB stick. 21 | 22 | ![piw_windows](/screenshots/piw_windows.jpg) 23 | 24 | ### On MacOS (High Sierra) 25 | 26 | ![piw_macOS](/screenshots/piw_macOS.jpg) 27 | 28 | 29 | ## How to build and run (on Linux) 30 | 31 | Clone the project 32 | 33 | ```bash 34 | git clone https://github.com/yunusem/pardus-imagewriter.git 35 | ``` 36 | Install build dependencies 37 | 38 | ```bash 39 | sudo apt-get install build-essential libc6 libgcc1 libgl1-mesa-glx libgl1 \ 40 | libqt5core5a libqt5dbus5 libqt5gui5l ibqt5network5 libqt5qml5 libqt5quick5 \ 41 | libqt5svg5-dev libqt5widgets5 libstdc++6 libudev-dev qtdeclarative5-dev 42 | ``` 43 | 44 | Build 45 | 46 | ```bash 47 | cd pardus-imagewriter 48 | mkdir build 49 | cd build 50 | export QT_SELECT=qt5 51 | qmake ../ 52 | make 53 | ``` 54 | 55 | Install Runtime dependencies 56 | 57 | ```bash 58 | sudo apt install gksu libqt5svg5 qml-module-qtquick-controls2 \ 59 | qml-module-qt-labs-folderlistmodel qml-module-qtquick2 \ 60 | qml-module-qtquick-layouts qml-module-qtgraphicaleffects \ 61 | qml-module-qtquick-dialogs qml-module-qtquick-controls \ 62 | qml-module-qtquick-templates2 qml-module-qt-labs-settings 63 | ``` 64 | 65 | Run 66 | 67 | ```bash 68 | gksudo ./piw 69 | ``` 70 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | pardus-imagewriter (0.1.9) unstable; urgency=medium 2 | 3 | [ Hikmet Baş ] 4 | * Make changes to build static standalone windows application 5 | 6 | [ Yunusemre Şentürk ] 7 | * Add drop external file support. Closes: #6 8 | * Fix label font pointsize on navigaiton buttons. Closes: #20 9 | 10 | -- Yunusemre Şentürk Fri, 21 Dec 2018 16:35:32 +0300 11 | 12 | pardus-imagewriter (0.1.8) unstable; urgency=medium 13 | 14 | * Use pkexec instead of gksu 15 | 16 | -- Yunusemre Şentürk Fri, 21 Dec 2018 10:55:28 +0300 17 | 18 | pardus-imagewriter (0.1.7) unstable; urgency=medium 19 | 20 | [ Yunusemre Şentürk ] 21 | * Include QtQml explicitly and Check QT_VERSION for 5.6 above specific delerations. Closes: #15 22 | 23 | [ Gökhan Karabulut ] 24 | * Fix GNU/Linux build and runtime instructions 25 | 26 | [ Yunusemre Şentürk ] 27 | * Support debian by default 28 | * Fix listing devices with 0 size 29 | * Fix could not get device pretty name when using external usb hub 30 | * Update icons 31 | * Fix HiDpi problem on about button and Change characteristics of window management buttons 32 | * Fix disk image name doesnt fit the layout after file dialog 33 | * Update navigation buttons and use image on window management buttons 34 | * Carry size control into platform_lin 35 | * Change cursor shape while moving the window 36 | * Update Burn page looking 37 | * Add tooltips on buttons. Closes: #8 38 | * Update Turkish translations 39 | * Use polkit for administrative privillages in linux 40 | * Add system notification support for linux via notify-send. Closes: #12 41 | * Change version in about dialog 42 | * Use native versioning 43 | 44 | -- Yunusemre Şentürk Fri, 21 Dec 2018 10:47:20 +0300 45 | 46 | pardus-imagewriter (0.1.6-1) unstable; urgency=medium 47 | 48 | * Add commandline argument support 49 | * Update Material design on some buttons and prepare for mimetype support. 50 | * Add mimetype support. Closes: #7 51 | 52 | -- Yunusemre Şentürk Thu, 04 Jan 2018 15:03:57 +0300 53 | 54 | pardus-imagewriter (0.1.5-1) unstable; urgency=medium 55 | 56 | [ Yunusemre Şentürk ] 57 | * Add refresh device list manually mechanism. 58 | * Windows schedule device workaround 59 | 60 | [ Hikmet Baş ] 61 | * Retry writing against block id failure 62 | 63 | [ Yunusemre Şentürk ] 64 | * Fix scheduling usb devices problem better for windows. Change design. Closes: #13 65 | 66 | -- Yunusemre Şentürk Wed, 03 Jan 2018 19:02:36 +0300 67 | 68 | pardus-imagewriter (0.1.4-1) unstable; urgency=medium 69 | 70 | * Add cancelling the writing progress support. Closes: #10 71 | * Add about dialog. Closes: #9 72 | 73 | -- Yunusemre Şentürk Tue, 02 Jan 2018 17:30:20 +0300 74 | 75 | pardus-imagewriter (0.1.2-3) unstable; urgency=medium 76 | 77 | * Add missing runtime dependency 78 | 79 | -- Yunusemre Şentürk Tue, 02 Jan 2018 13:02:55 +0300 80 | 81 | pardus-imagewriter (0.1.2-2) unstable; urgency=medium 82 | 83 | * Update runtime dependency 84 | 85 | -- Yunusemre Şentürk Tue, 02 Jan 2018 12:51:22 +0300 86 | 87 | pardus-imagewriter (0.1.2-1) unstable; urgency=medium 88 | 89 | * Add README.md and screenshots 90 | * Update README.md 91 | * Add build instractions in README.md 92 | * Update icon resolutions and enhance navigation from Burn. Closes: #5 93 | * Add invokable functions about sizes. Closes: #4 94 | * Update Turkish translations 95 | 96 | -- Yunusemre Şentürk Tue, 02 Jan 2018 10:45:23 +0300 97 | 98 | pardus-imagewriter (0.1.1-1) unstable; urgency=medium 99 | 100 | [ Hikmet Baş ] 101 | * Fix compile errors 102 | 103 | [ Cihangir Akturk ] 104 | * macOS specific fixes 105 | 106 | [ Yunusemre Şentürk ] 107 | * Make selected file url platform independent 108 | * Debian version 0.1.1-1 109 | 110 | -- Yunusemre Şentürk Fri, 29 Dec 2017 16:52:03 +0300 111 | 112 | pardus-imagewriter (0.1.0-1) unstable; urgency=medium 113 | 114 | * Initial release. 115 | 116 | -- Yunusemre Şentürk Fri, 13 Oct 2017 15:27:33 +0300 117 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: pardus-imagewriter 2 | Section: utils 3 | Priority: optional 4 | Maintainer: Yunusemre Şentürk 5 | Build-Depends: debhelper (>= 9), 6 | libc6, 7 | libgcc1, 8 | libgcc-6-dev, 9 | libgl1-mesa-glx | libgl1, 10 | libqt5core5a, 11 | libqt5gui5, 12 | libqt5network5, 13 | libqt5qml5, 14 | libqt5quick5, 15 | libqt5svg5-dev, 16 | libqt5widgets5, 17 | libstdc++6, 18 | libudev-dev, 19 | qtdeclarative5-dev 20 | Standards-Version: 3.9.8 21 | Homepage: http://pardus.org.tr 22 | 23 | Package: pardus-imagewriter 24 | Architecture: amd64 25 | Depends: ${shlibs:Depends}, ${misc:Depends}, 26 | libqt5svg5, 27 | policykit-1, 28 | qml-module-qtquick-controls2, 29 | qml-module-qt-labs-folderlistmodel, 30 | qml-module-qtquick2, 31 | qml-module-qtquick-layouts, 32 | qml-module-qtgraphicaleffects, 33 | qml-module-qtquick-dialogs, 34 | qml-module-qtquick-controls, 35 | qml-module-qtquick-templates2, 36 | qml-module-qt-labs-settings 37 | Description: Pardus Image Writer 38 | Simple Qt based Disk Image USB burner. 39 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: pardus-imagewriter 3 | Source: https://pardus.org.tr 4 | 5 | 6 | 7 | Files: * 8 | Copyright: 2017 Yunusemre Senturk 9 | License: GPL-3.0+ 10 | 11 | License: GPL-3.0+ 12 | This program is free software: you can redistribute it and/or modify 13 | it under the terms of the GNU General Public License as published by 14 | the Free Software Foundation, either version 3 of the License, or 15 | (at your option) any later version. 16 | . 17 | This package is distributed in the hope that it will be useful, 18 | but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | GNU General Public License for more details. 21 | . 22 | You should have received a copy of the GNU General Public License 23 | along with this program. If not, see . 24 | . 25 | On Debian systems, the complete text of the GNU General 26 | Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". 27 | 28 | 29 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | export QT_SELECT := qt5 3 | 4 | %: 5 | dh $@ 6 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /debian/source/local-options: -------------------------------------------------------------------------------- 1 | extend-diff-ignore = ".drone.yml" 2 | -------------------------------------------------------------------------------- /images.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | images/icon.svg 4 | images/iso.svg 5 | images/usb.svg 6 | images/info.svg 7 | images/pardus.svg 8 | images/minimize.svg 9 | images/close.svg 10 | 11 | 12 | -------------------------------------------------------------------------------- /images/close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 41 | 44 | 45 | 47 | 48 | 50 | image/svg+xml 51 | 53 | 54 | 55 | 56 | 57 | 62 | 73 | 76 | 79 | 82 | 85 | 88 | 91 | 94 | 97 | 100 | 103 | 106 | 109 | 112 | 115 | 118 | 122 | 125 | 135 | 145 | 146 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /images/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 28 | 30 | 50 | 53 | 62 | 69 | 78 | 85 | 92 | 97 | 98 | 103 | 108 | 113 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /images/info.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 40 | 42 | 43 | 45 | image/svg+xml 46 | 48 | 49 | 50 | 51 | 52 | 57 | 68 | 71 | 74 | 77 | 80 | 83 | 86 | 89 | 92 | 95 | 98 | 101 | 104 | 107 | 110 | 113 | 117 | 121 | 126 | 131 | 132 | 136 | 141 | 146 | 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /images/iso.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 28 | 30 | 50 | 53 | 58 | 63 | 68 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /images/minimize.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 40 | 42 | 43 | 45 | image/svg+xml 46 | 48 | 49 | 50 | 51 | 52 | 57 | 68 | 71 | 74 | 77 | 80 | 83 | 86 | 89 | 92 | 95 | 98 | 101 | 104 | 107 | 110 | 113 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /images/pardus.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | image/svg+xml -------------------------------------------------------------------------------- /images/piw.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusem/pardus-imagewriter/55d9c13585266dc9d6afbdba6335fc48646bcbad/images/piw.ico -------------------------------------------------------------------------------- /images/usb.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 28 | 30 | 50 | 59 | 66 | 75 | 84 | 93 | 102 | 109 | 116 | 121 | 122 | -------------------------------------------------------------------------------- /pardus-imagewriter.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Version=1.0 3 | Name=Pardus Image Writer 4 | Name[tr]=Pardus Disk Kalıbı Yazıcı 5 | Type=Application 6 | Comment=Simple Qt based disk image writer 7 | Comment[tr]=Basit Qt tabanlı disk kalıbı yazıcı 8 | Terminal=false 9 | Exec=pkexec /usr/bin/piw %f 10 | Icon=/usr/share/pardus/pardus-imagewriter/icon.svg 11 | MimeType=application/x-iso-image; 12 | Categories=Qt;Utility; 13 | Keywords=usb;disk;writer;burner; 14 | GenericName=Disk Image USB Burner 15 | GenericName[tr]=Disk Kalıbı USB Yakıcı 16 | -------------------------------------------------------------------------------- /pardus-imagewriter.pro: -------------------------------------------------------------------------------- 1 | QT += qml quick widgets svg 2 | 3 | CONFIG += c++11 4 | 5 | QT_PLUGINS -= qdds qicns qjp2 qmng qtga qtiff qwbmp qwebp 6 | 7 | TARGET = piw 8 | 9 | SOURCES += src/main.cpp \ 10 | src/helper.cpp \ 11 | src/common.cpp \ 12 | src/devicehandler.cpp \ 13 | src/imagewriter.cpp \ 14 | src/physicaldevice.cpp 15 | 16 | HEADERS += \ 17 | src/helper.h \ 18 | src/usbdevicemonitor.h \ 19 | src/platform.h \ 20 | src/common.h \ 21 | src/usbdevice.h \ 22 | src/devicehandler.h \ 23 | src/physicaldevice.h \ 24 | src/imagewriter.h 25 | 26 | win32 { 27 | RC_FILE = piw.rc 28 | SOURCES += src/platform_win.cpp \ 29 | src/usbdevicemonitor_win.cpp 30 | HEADERS += src/usbdevicemonitor_win_p.h 31 | QMAKE_CXXFLAGS_RELEASE -= -Zc:strictStrings 32 | } 33 | linux { 34 | SOURCES += src/platform_lin.cpp \ 35 | src/signalhandler.cpp \ 36 | src/usbdevicemonitor_lin.cpp 37 | HEADERS += src/usbdevicemonitor_lin_p.h \ 38 | src/signalhandler.h 39 | 40 | target.path = /usr/bin/ 41 | 42 | desktop_file.files = pardus-imagewriter.desktop 43 | desktop_file.path = /usr/share/applications/ 44 | 45 | icon.files = images/icon.svg 46 | icon.commands = mkdir -p /usr/share/pardus/pardus-imagewriter 47 | icon.path = /usr/share/pardus/pardus-imagewriter 48 | 49 | policy.files = tr.org.pardus.pkexec.pardus-imagewriter.policy 50 | policy.commands = mkdir -p /usr/share/polkit-1/actions 51 | policy.path = /usr/share/polkit-1/actions 52 | 53 | INSTALLS += target desktop_file icon policy 54 | 55 | } 56 | macx { 57 | OBJECTIVE_SOURCES += src/platform_mac.mm \ 58 | src/usbdevicemonitor_mac.mm 59 | HEADERS += src/usbdevicemonitor_mac_p.h 60 | } 61 | 62 | RESOURCES += qml.qrc images.qrc \ 63 | translations.qrc 64 | 65 | DEFINES += QT_DEPRECATED_WARNINGS 66 | 67 | win32 { 68 | CONFIG -= embed_manifest_dll embed_manifest_exe 69 | msvc { 70 | LIBS += Ole32.lib OleAut32.lib 71 | QMAKE_CXXFLAGS -= -Zc:strictStrings 72 | QMAKE_CXXFLAGS_RELEASE -= -Zc:strictStrings 73 | QMAKE_CFLAGS -= -Zc:strictStrings 74 | QMAKE_CFLAGS_RELEASE -= -Zc:strictStrings 75 | } 76 | mingw { 77 | QMAKE_CXXFLAGS += -std=gnu++11 78 | LIBS += -lole32 -loleaut32 -luuid 79 | } 80 | } 81 | linux:gcc { 82 | LIBS += -ldl 83 | QMAKE_LFLAGS_RELEASE -= -Wl,-z,now # Make sure weak symbols are not resolved on link-time 84 | QMAKE_LFLAGS_DEBUG -= -Wl,-z,now 85 | QMAKE_LFLAGS -= -Wl,-z,now 86 | GCCSTRVER = $$system(g++ -dumpversion) 87 | GCCVERSION = $$split(GCCSTRVER, .) 88 | GCCV_MJ = $$member(GCCVERSION, 0) 89 | GCCV_MN = $$member(GCCVERSION, 1) 90 | greaterThan(GCCV_MJ, 3) { 91 | lessThan(GCCV_MN, 7) { 92 | QMAKE_CXXFLAGS += -std=gnu++0x 93 | } 94 | greaterThan(GCCV_MN, 6) { 95 | QMAKE_CXXFLAGS += -std=gnu++11 96 | } 97 | } 98 | contains(QT_CONFIG, static) { 99 | # Static build is meant for releasing, clean up the binary 100 | QMAKE_LFLAGS += -s 101 | } 102 | } 103 | macx { 104 | QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.7 105 | QMAKE_CFLAGS = $$replace(QMAKE_CFLAGS, '-mmacosx-version-min=10.6', '-mmacosx-version-min=10.7') 106 | QMAKE_CXXFLAGS = $$replace(QMAKE_CXXFLAGS, '-mmacosx-version-min=10.6', '-mmacosx-version-min=10.7') 107 | QMAKE_LFLAGS = $$replace(QMAKE_LFLAGS, '-mmacosx-version-min=10.6', '-mmacosx-version-min=10.7') 108 | QMAKE_OBJECTIVE_CFLAGS = $$replace(QMAKE_OBJECTIVE_CFLAGS, '-mmacosx-version-min=10.6', '-mmacosx-version-min=10.7') 109 | 110 | QMAKE_CXXFLAGS += -std=c++0x -stdlib=libc++ 111 | QMAKE_OBJECTIVE_CFLAGS += -std=c++0x -stdlib=libc++ 112 | QMAKE_INCDIR += /System/Library/Frameworks/AppKit.framework/Headers /System/Library/Frameworks/Security.framework/Headers /System/Library/Frameworks/ServiceManagement.framework/Headers 113 | QMAKE_LFLAGS += -framework IOKit -framework Cocoa -framework Security 114 | # Clean up the binary after linking 115 | QMAKE_POST_LINK = strip -S -x $(TARGET) 116 | } 117 | 118 | -------------------------------------------------------------------------------- /piw.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | Simple Qt based Disk Image USB burner. 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /piw.rc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | ID_ICON ICON DISCARDABLE "images/piw.ico" 4 | CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "piw.exe.manifest" 5 | 6 | VS_VERSION_INFO VERSIONINFO 7 | PRODUCTVERSION 0,1,9,0 8 | BEGIN 9 | BLOCK "StringFileInfo" 10 | BEGIN 11 | BLOCK "040904E4" 12 | BEGIN 13 | VALUE "CompanyName", "TUBITAK ULAKBIM" 14 | VALUE "InternalName", "Pardus Image Writer" 15 | VALUE "LegalCopyright", "TUBITAK ULAKBIM" 16 | VALUE "LegalTrademarks1", "All Rights Reserved" 17 | VALUE "OriginalFilename", "piw.exe" 18 | VALUE "ProductName", "Pardus Image Writer" 19 | VALUE "ProductVersion", "0.1.9\0" 20 | END 21 | END 22 | 23 | BLOCK "VarFileInfo" 24 | BEGIN 25 | VALUE "Translation", 0x409, 1252 26 | END 27 | END 28 | -------------------------------------------------------------------------------- /qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | ui/main.qml 4 | qtquickcontrols2.conf 5 | ui/File.qml 6 | ui/Target.qml 7 | ui/Burn.qml 8 | ui/ProgressBarCircle.qml 9 | 10 | 11 | -------------------------------------------------------------------------------- /qtquickcontrols2.conf: -------------------------------------------------------------------------------- 1 | ; This file can be edited to change the style of the application 2 | ; See Styling Qt Quick Controls 2 in the documentation for details: 3 | ; http://doc.qt.io/qt-5/qtquickcontrols2-styles.html 4 | 5 | [Controls] 6 | Style=Material 7 | 8 | [Universal] 9 | Theme=Light 10 | ;Accent=Steel 11 | 12 | [Material] 13 | Theme=Dark 14 | ;Foreground=#FFCB08 15 | Background=#2c2c2c 16 | Accent=#FFCB08 17 | ;Primary=BlueGray 18 | -------------------------------------------------------------------------------- /screenshots/piw_macOS.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusem/pardus-imagewriter/55d9c13585266dc9d6afbdba6335fc48646bcbad/screenshots/piw_macOS.jpg -------------------------------------------------------------------------------- /screenshots/piw_pardus-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusem/pardus-imagewriter/55d9c13585266dc9d6afbdba6335fc48646bcbad/screenshots/piw_pardus-1.png -------------------------------------------------------------------------------- /screenshots/piw_pardus-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusem/pardus-imagewriter/55d9c13585266dc9d6afbdba6335fc48646bcbad/screenshots/piw_pardus-2.png -------------------------------------------------------------------------------- /screenshots/piw_pardus-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusem/pardus-imagewriter/55d9c13585266dc9d6afbdba6335fc48646bcbad/screenshots/piw_pardus-3.png -------------------------------------------------------------------------------- /screenshots/piw_pardus-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusem/pardus-imagewriter/55d9c13585266dc9d6afbdba6335fc48646bcbad/screenshots/piw_pardus-4.png -------------------------------------------------------------------------------- /screenshots/piw_pardus-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusem/pardus-imagewriter/55d9c13585266dc9d6afbdba6335fc48646bcbad/screenshots/piw_pardus-5.png -------------------------------------------------------------------------------- /screenshots/piw_pardus-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusem/pardus-imagewriter/55d9c13585266dc9d6afbdba6335fc48646bcbad/screenshots/piw_pardus-6.png -------------------------------------------------------------------------------- /screenshots/piw_windows.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusem/pardus-imagewriter/55d9c13585266dc9d6afbdba6335fc48646bcbad/screenshots/piw_windows.jpg -------------------------------------------------------------------------------- /src/common.cpp: -------------------------------------------------------------------------------- 1 | #include "src/common.h" 2 | 3 | #include 4 | #include 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | // Implementation of the non-template cross-platform functions from src/common.h 8 | 9 | 10 | #if defined(Q_OS_WIN32) 11 | // Converts the WinAPI and COM error code into text message 12 | // Input: 13 | // errorCode - error code (GetLastError() is used by default) 14 | // Returns: 15 | // system error message for the errorCode 16 | QString errorMessageFromCode(DWORD errorCode) 17 | { 18 | LPTSTR msgBuffer; 19 | DWORD res = FormatMessage( 20 | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 21 | NULL, 22 | errorCode, 23 | 0, 24 | reinterpret_cast(&msgBuffer), 25 | 0, 26 | NULL 27 | ); 28 | if (res) 29 | { 30 | QString ret = QString::fromWCharArray(msgBuffer); 31 | LocalFree(msgBuffer); 32 | return ret; 33 | } 34 | else 35 | return QObject::tr("Error code:") + " " + QString::number(errorCode); 36 | } 37 | 38 | // Converts the WinAPI and COM error code into text message 39 | // Input: 40 | // prefixMessage - error description 41 | // errorCode - error code (GetLastError() is used by default) 42 | // Returns: 43 | // prefixMessage followed by a newline and the system error message for the errorCode 44 | QString formatErrorMessageFromCode(QString prefixMessage, DWORD errorCode) 45 | { 46 | return prefixMessage + "\n" + errorMessageFromCode(errorCode); 47 | } 48 | 49 | // This constant is declared in wbemprov.h and defined in wbemuuid.lib. If building with MinGW, the header is available but not library, 50 | // and the constant remains unresolved. So we define it here. 51 | const CLSID CLSID_WbemAdministrativeLocator = {0xCB8555CC, 0x9128, 0x11D1, {0xAD, 0x9B, 0x00, 0xC0, 0x4F, 0xD8, 0xFD, 0xFF}}; 52 | #endif 53 | 54 | // Gets the contents of the specified file 55 | // Input: 56 | // fileName - path to the file to read 57 | // Returns: 58 | // the file contents or empty string if an error occurred 59 | QString readFileContents(const QString& fileName) 60 | { 61 | QFile f(fileName); 62 | if (!f.open(QFile::ReadOnly)) 63 | return ""; 64 | QString ret = f.readAll(); 65 | f.close(); 66 | return ret; 67 | } 68 | -------------------------------------------------------------------------------- /src/common.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_H 2 | #define COMMON_H 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | // This file contains some commonly-used constants and function declarations 6 | 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | #include "src/platform.h" 15 | 16 | class UsbDevice; 17 | 18 | 19 | // Default unit to be used when displaying file/device sizes (MB) 20 | const quint64 DEFAULT_UNIT = 1048576; 21 | 22 | // Pointer to correctly typed application instance 23 | // #define mApp (static_castqApp) 24 | 25 | // Returns the number of blocks required to contain some number of bytes 26 | // Input: 27 | // T - any integer type 28 | // val - number of bytes 29 | // factor - size of the block 30 | // Returns: 31 | // the number of blocks of size required for to fit in 32 | template T alignNumberDiv(T val, T factor) 33 | { 34 | static_assert(std::is_integral::value, "Only integer types are supported!"); 35 | return ((val + factor - 1) / factor); 36 | } 37 | 38 | // Returns the total size of blocks required to contain some number of bytes 39 | // Input: 40 | // T - any integer type 41 | // val - number of bytes 42 | // factor - size of the block 43 | // Returns: 44 | // the total size of blocks of size required for to fit in 45 | template T alignNumber(T val, T factor) 46 | { 47 | static_assert(std::is_integral::value, "Only integer types are supported!"); 48 | return alignNumberDiv(val, factor) * factor; 49 | } 50 | 51 | #if defined(Q_OS_WIN32) 52 | // Converts the WinAPI and COM error code into text message 53 | // Input: 54 | // errorCode - error code (GetLastError() is used by default) 55 | // Returns: 56 | // system error message for the errorCode 57 | QString errorMessageFromCode(DWORD errorCode = GetLastError()); 58 | 59 | // Converts the WinAPI and COM error code into text message 60 | // Input: 61 | // prefixMessage - error description 62 | // errorCode - error code (GetLastError() is used by default) 63 | // Returns: 64 | // prefixMessage followed by a newline and the system error message for the errorCode 65 | QString formatErrorMessageFromCode(QString prefixMessage, DWORD errorCode = GetLastError()); 66 | #endif 67 | 68 | // Gets the contents of the specified file 69 | // Input: 70 | // fileName - path to the file to read 71 | // Returns: 72 | // the file contents or empty string if an error occurred 73 | QString readFileContents(const QString& fileName); 74 | 75 | // Performs platform-specific enumeration of USB flash disks 76 | // function for adding these devices into the application GUI structure 77 | // Returns: 78 | // true if enumeration completed successfully, false otherwise 79 | QList platformEnumFlashDevices(); 80 | 81 | // Checks the application privileges and if they are not sufficient, restarts 82 | // itself requesting higher privileges 83 | // Input: 84 | // appPath - path to the application executable 85 | // Returns: 86 | // true if already running elevated 87 | // false if error occurs 88 | // does not return if elevation request succeeded (the current instance terminates) 89 | bool ensureElevated(); 90 | 91 | #endif // COMMON_H 92 | -------------------------------------------------------------------------------- /src/devicehandler.cpp: -------------------------------------------------------------------------------- 1 | #include "src/devicehandler.h" 2 | 3 | DeviceHandler::DeviceHandler(QObject *parent) : QObject(parent) 4 | { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /src/devicehandler.h: -------------------------------------------------------------------------------- 1 | #ifndef DEVICEHANDLER_H 2 | #define DEVICEHANDLER_H 3 | 4 | #include 5 | 6 | class DeviceHandler : public QObject 7 | { 8 | Q_OBJECT 9 | public: 10 | explicit DeviceHandler(QObject *parent = 0); 11 | 12 | signals: 13 | 14 | public slots: 15 | }; 16 | 17 | #endif // DEVICEHANDLER_H 18 | -------------------------------------------------------------------------------- /src/helper.cpp: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * Copyright (C) 2017 by Yunusemre Senturk * 3 | * * 4 | * * 5 | * This program is free software; you can redistribute it and/or modify * 6 | * it under the terms of the GNU General Public License as published by * 7 | * the Free Software Foundation; either version 2 of the License, or * 8 | * (at your option) any later version. * 9 | * * 10 | * This program is distributed in the hope that it will be useful, * 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 13 | * GNU General Public License for more details. * 14 | * * 15 | * You should have received a copy of the GNU General Public License * 16 | * along with this program; if not, write to the * 17 | * Free Software Foundation, Inc., * 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * 19 | *****************************************************************************/ 20 | #include "src/helper.h" 21 | #include "src/common.h" 22 | #include "src/usbdevicemonitor.h" 23 | #include "src/usbdevice.h" 24 | #include "src/imagewriter.h" 25 | #if defined(Q_OS_LINUX) 26 | #include "src/signalhandler.h" 27 | #include 28 | #endif 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | Helper::Helper(QObject *parent) : QObject(parent), 40 | progressValue(0), maxValue(1),comboBoxIndex(-1), b(false), retryCount(0) 41 | { 42 | 43 | #if defined(Q_OS_LINUX) 44 | SignalHandler *sh = new SignalHandler; 45 | sh->setHelper(this); 46 | #endif 47 | udm = new UsbDeviceMonitor(this); 48 | udm->startMonitoring(); 49 | 50 | scheduleEnumFlashDevices(); 51 | 52 | connect(udm, SIGNAL(deviceChanged()),this,SLOT(scheduleEnumFlashDevices())); 53 | 54 | } 55 | 56 | bool Helper::burning() const 57 | { 58 | return this->b; 59 | } 60 | 61 | int Helper::progress() const 62 | { 63 | return progressValue; 64 | } 65 | 66 | QString Helper::messageFromBackend() const 67 | { 68 | return m_messageFromBackend; 69 | } 70 | 71 | QString Helper::filePathFromArguments() const 72 | { 73 | const QStringList args = QCoreApplication::arguments(); 74 | QString path = ""; 75 | if(args.length() > 1) { 76 | path = args.at(1); 77 | QString suffix = QFileInfo(path).suffix(); 78 | if( suffix == "iso" || suffix == "bin" || suffix == "img" || 79 | suffix == "ISO" || suffix == "BIN" || suffix == "IMG") { 80 | path = QDir(path).absolutePath(); 81 | } 82 | } 83 | return path; 84 | } 85 | 86 | QString Helper::fileNameFromPath(const QString &path) const 87 | { 88 | return QFileInfo(path).fileName(); 89 | } 90 | 91 | QString Helper::downloadsFolderPath() const 92 | { 93 | QStringList downloadDirs = QStandardPaths::standardLocations(QStandardPaths::DownloadLocation); 94 | if (downloadDirs.size() > 0) 95 | return downloadDirs.at(0); 96 | else 97 | return ""; 98 | } 99 | 100 | QStringList Helper::devices() 101 | { 102 | return dl; 103 | } 104 | 105 | void Helper::scheduleEnumFlashDevices() 106 | { 107 | emit scheduleStarted(); 108 | comboBoxIndex = -1; 109 | udl = platformEnumFlashDevices(); 110 | dl.clear(); 111 | for(int i = 0; i < udl.length(); i++) { 112 | dl.append(udl.at(i).formatDisplayName()); 113 | } 114 | emit deviceListChanged(); 115 | } 116 | 117 | bool Helper::preProcessImageFile(const QString &fileUrl) 118 | { 119 | QString newImageFile = fileUrl; 120 | if(fileUrl.left(7) == "file://") { 121 | newImageFile = QUrl(fileUrl).toLocalFile(); 122 | newImageFile = QDir(newImageFile).absolutePath(); 123 | } 124 | QFile f(newImageFile); 125 | if (!f.open(QIODevice::ReadOnly)) { 126 | qDebug() << "Pre Process : " << QDir::toNativeSeparators(newImageFile) << f.errorString(); 127 | return false; 128 | } 129 | imageSize = f.size(); 130 | f.close(); 131 | imageFile = newImageFile; 132 | return true; 133 | } 134 | 135 | void Helper::writeToDevice(int index) 136 | { 137 | comboBoxIndex = index; 138 | progressValue = 0; 139 | maxValue = 1; 140 | if(dl.length() == 0 || imageFile == "") { 141 | qDebug() << "Could not find the device or the image file"; 142 | return; 143 | } 144 | 145 | UsbDevice selectedDevice = udl.at(comboBoxIndex); 146 | maxValue = alignNumberDiv(imageSize, DEFAULT_UNIT); 147 | ImageWriter *writer = new ImageWriter(imageFile, selectedDevice); 148 | QThread *writerThread = new QThread(this); 149 | 150 | connect(writerThread, SIGNAL(started()), writer, SLOT(writeImage())); 151 | connect(writer, SIGNAL(finished()), writerThread, SLOT(quit())); 152 | connect(writer, SIGNAL(finished()), writerThread, SLOT(deleteLater())); 153 | connect(writerThread, SIGNAL(finished()), writerThread, SLOT(deleteLater())); 154 | 155 | connect(this,SIGNAL(cancelWritingProcess()),writer,SLOT(cancelWriting()),Qt::DirectConnection); 156 | connect(writer, SIGNAL(blockWritten(int)), this, SLOT(updateProgressValue(int))); 157 | 158 | connect(writer, SIGNAL(error(QString)), this, SLOT(output(QString))); 159 | 160 | connect(writer,SIGNAL(success()),this,SIGNAL(burningFinished())); 161 | 162 | connect(writer,SIGNAL(cancelled()),this, SIGNAL(burningCancelled())); 163 | 164 | 165 | writer->moveToThread(writerThread); 166 | writerThread->start(); 167 | } 168 | 169 | void Helper::cancelWriting() 170 | { 171 | emit cancelWritingProcess(); 172 | } 173 | 174 | quint64 Helper::getImageSize() const 175 | { 176 | return imageSize; 177 | } 178 | 179 | quint64 Helper::getSelectedDeviceSize(const int index) const 180 | { 181 | return udl.at(index).m_Size; 182 | } 183 | 184 | void Helper::notifySystem(const QString &title, const QString &content) 185 | { 186 | qDebug() << content; 187 | #if defined(Q_OS_LINUX) 188 | QProcess p; 189 | QStringList args; 190 | args << "-u" << "normal"; 191 | args << "-t" << "17000"; 192 | args << "-i" << "/usr/share/pardus/pardus-imagewriter/icon.svg"; 193 | args << title << content; 194 | 195 | QString command = "/usr/bin/notify-send"; 196 | p.execute(command,args); 197 | #endif 198 | } 199 | 200 | int Helper::maximumProgressValue() 201 | { 202 | return maxValue; 203 | } 204 | 205 | void Helper::updateProgressValue(int increment) 206 | { 207 | progressValue += increment; 208 | emit progressChanged(); 209 | } 210 | 211 | void Helper::output(QString msg) 212 | { 213 | m_messageFromBackend = msg; 214 | if (msg.contains("control block address is invalid",Qt::CaseInsensitive)) { 215 | qDebug() << msg; 216 | qDebug() << "Retrying to write"; 217 | if(retryCount < 3) { 218 | retryCount ++; 219 | writeToDevice(comboBoxIndex); 220 | } else { 221 | emit warnUser(); 222 | emit burningCancelled(); 223 | qDebug() << msg; 224 | } 225 | 226 | } else { 227 | emit warnUser(); 228 | emit burningCancelled(); 229 | qDebug() << msg; 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /src/helper.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * Copyright (C) 2017 by Yunusemre Senturk * 3 | * * 4 | * * 5 | * This program is free software; you can redistribute it and/or modify * 6 | * it under the terms of the GNU General Public License as published by * 7 | * the Free Software Foundation; either version 2 of the License, or * 8 | * (at your option) any later version. * 9 | * * 10 | * This program is distributed in the hope that it will be useful, * 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 13 | * GNU General Public License for more details. * 14 | * * 15 | * You should have received a copy of the GNU General Public License * 16 | * along with this program; if not, write to the * 17 | * Free Software Foundation, Inc., * 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * 19 | *****************************************************************************/ 20 | #ifndef HELPER_H 21 | #define HELPER_H 22 | 23 | 24 | #include "src/usbdevice.h" 25 | #include 26 | #include 27 | #include 28 | 29 | 30 | class QStringList; 31 | class UsbDeviceMonitor; 32 | 33 | 34 | class Helper : public QObject 35 | { 36 | Q_OBJECT 37 | Q_PROPERTY(bool burning READ burning 38 | NOTIFY burningFinished 39 | NOTIFY burningCancelled 40 | NOTIFY terminateCalled) 41 | Q_PROPERTY(QStringList devices READ devices 42 | NOTIFY scheduleStarted 43 | NOTIFY deviceListChanged) 44 | Q_PROPERTY(int progress READ progress NOTIFY progressChanged) 45 | Q_PROPERTY(QString messageFromBackend READ messageFromBackend NOTIFY warnUser) 46 | 47 | public: 48 | explicit Helper(QObject *parent = 0); 49 | bool burning() const; 50 | QStringList devices(); 51 | int progress() const; 52 | QString messageFromBackend() const; 53 | Q_INVOKABLE QString filePathFromArguments() const; 54 | Q_INVOKABLE QString fileNameFromPath(const QString &path) const; 55 | Q_INVOKABLE bool preProcessImageFile(const QString &fileUrl); 56 | Q_INVOKABLE void writeToDevice(int index); 57 | Q_INVOKABLE void cancelWriting(); 58 | Q_INVOKABLE int maximumProgressValue(); 59 | Q_INVOKABLE QString downloadsFolderPath() const; 60 | Q_INVOKABLE quint64 getImageSize() const; 61 | Q_INVOKABLE quint64 getSelectedDeviceSize(const int index) const; 62 | Q_INVOKABLE void notifySystem(const QString &title, const QString &content); 63 | 64 | private: 65 | QString imageFile; 66 | quint64 imageSize; 67 | int progressValue; 68 | int maxValue; 69 | int comboBoxIndex; 70 | bool b; 71 | unsigned int retryCount; 72 | QStringList dl; 73 | UsbDeviceMonitor *udm; 74 | QList udl; 75 | QString m_messageFromBackend; 76 | signals: 77 | void progressChanged(); 78 | void scheduleStarted(); 79 | void deviceListChanged(); 80 | void cancelWritingProcess(); 81 | void burningFinished(); 82 | void burningCancelled(); 83 | void terminateCalled(); 84 | void warnUser(); 85 | private slots: 86 | void scheduleEnumFlashDevices(); 87 | void updateProgressValue(int increment); 88 | void output(QString msg); 89 | }; 90 | 91 | #endif // HELPER_H 92 | -------------------------------------------------------------------------------- /src/imagewriter.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Implementation of ImageWriter 3 | 4 | 5 | #include 6 | 7 | #include "src/common.h" 8 | #include "src/imagewriter.h" 9 | #include "src/physicaldevice.h" 10 | 11 | ImageWriter::ImageWriter(const QString& ImageFile, UsbDevice Device, QObject *parent) : 12 | QObject(parent), 13 | m_Device(Device), 14 | m_ImageFile(ImageFile), 15 | m_CancelWriting(false) 16 | { 17 | } 18 | 19 | // The main method that writes the image 20 | void ImageWriter::writeImage() 21 | { 22 | const qint64 TRANSFER_BLOCK_SIZE = 1024 * 1024; 23 | void* buffer = NULL; 24 | 25 | bool isError = false; 26 | bool cancelRequested = false; 27 | bool zeroing = (m_ImageFile == ""); 28 | 29 | // Using try-catch for processing errors 30 | // Invalid values are used for indication non-initialized objects; 31 | // after the try-catch block all the initialized objects are freed 32 | try 33 | { 34 | #if defined(Q_OS_WIN32) 35 | // Using VirtualAlloc so that the buffer was properly aligned (required for 36 | // direct access to devices and for unbuffered reading/writing) 37 | buffer = VirtualAlloc(NULL, TRANSFER_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); 38 | if (buffer == NULL) 39 | throw formatErrorMessageFromCode(tr("Failed to allocate memory for buffer:")); 40 | #elif defined(Q_OS_LINUX) || defined(Q_OS_MAC) 41 | buffer = malloc(TRANSFER_BLOCK_SIZE); 42 | if (buffer == NULL) 43 | throw tr("Failed to allocate memory for buffer."); 44 | #endif 45 | 46 | QFile imageFile; 47 | if (zeroing) 48 | { 49 | // Prepare zero-filled buffer 50 | memset(buffer, 0, TRANSFER_BLOCK_SIZE); 51 | } 52 | else 53 | { 54 | // Open the source image file for reading 55 | imageFile.setFileName(m_ImageFile); 56 | if (!imageFile.open(QIODevice::ReadOnly)) 57 | throw tr("Failed to open the image file:") + "\n" + imageFile.errorString(); 58 | } 59 | 60 | // Unmount volumes that belong to the selected target device 61 | // TODO: Check first if they are used and show warning 62 | // (problem: have to show request in the GUI thread and return reply back here) 63 | QStringList errMessages; 64 | 65 | #if defined(Q_OS_WIN32) 66 | for (int i = 0; i < m_Device.m_Volumes.size(); ++i) 67 | { 68 | DWORD bret; 69 | HANDLE volume = CreateFile( 70 | reinterpret_cast(("\\\\.\\" + m_Device.m_Volumes[i]).utf16()), 71 | GENERIC_READ | GENERIC_WRITE, 72 | FILE_SHARE_READ | FILE_SHARE_WRITE, 73 | NULL, 74 | OPEN_EXISTING, 75 | 0, 76 | NULL 77 | ); 78 | if (volume == INVALID_HANDLE_VALUE) 79 | { 80 | errMessages << formatErrorMessageFromCode(tr("Failed to open the drive") + " " + m_Device.m_Volumes[i]); 81 | continue; 82 | } 83 | // Trying to lock the volume but ignore if we failed (such call seems to be required for 84 | // dismounting the volume on WinXP) 85 | DeviceIoControl(volume, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &bret, NULL); 86 | if (!DeviceIoControl(volume, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &bret, NULL)) 87 | errMessages << formatErrorMessageFromCode(tr("Failed to unmount the drive") + " " + m_Device.m_Volumes[i]); 88 | CloseHandle(volume); 89 | volume = INVALID_HANDLE_VALUE; 90 | } 91 | #elif defined(Q_OS_MAC) 92 | struct statfs* mntEntries = NULL; 93 | int mntEntriesNum = getmntinfo(&mntEntries, MNT_WAIT); 94 | for (int i = 0; i < mntEntriesNum; ++i) 95 | { 96 | for (int j = 0; j < m_Device.m_Volumes.size(); ++j) 97 | { 98 | // Check that the mount point is either our target device itself or a partition on it 99 | if ((mntEntries[i].f_mntfromname == m_Device.m_Volumes[j]) || 100 | QString(mntEntries[i].f_mntfromname).startsWith(m_Device.m_Volumes[j] + 's')) 101 | { 102 | // Mount point is the selected device or one of its partitions - try to unmount it 103 | if (unmount(mntEntries[i].f_mntonname, MNT_FORCE) != 0) 104 | errMessages << tr("Failed to unmount the volume") + " " + m_Device.m_Volumes[i] + "\n" + strerror(errno); 105 | } 106 | } 107 | } 108 | #endif 109 | if (errMessages.size() > 0) 110 | throw errMessages.join("\n\n"); 111 | 112 | // Open the target USB device for writing and lock it 113 | PhysicalDevice deviceFile(m_Device.m_PhysicalDevice); 114 | if (!deviceFile.open()) 115 | throw tr("Failed to open the target device: ") + m_Device.formatDisplayName() + " " + deviceFile.errorString(); 116 | 117 | qint64 readBytes; 118 | qint64 writtenBytes; 119 | // Start reading/writing cycle 120 | for (;;) 121 | { 122 | if (zeroing) 123 | { 124 | readBytes = TRANSFER_BLOCK_SIZE; 125 | } 126 | else 127 | { 128 | if ((readBytes = imageFile.read(static_cast(buffer), TRANSFER_BLOCK_SIZE)) <= 0) 129 | break; 130 | } 131 | // Align the number of bytes to the sector size 132 | readBytes = alignNumber(readBytes, (qint64)m_Device.m_SectorSize); 133 | writtenBytes = deviceFile.write(static_cast(buffer), readBytes); 134 | if (writtenBytes < 0) 135 | throw tr("Failed to write to the device:") + "\n" + deviceFile.errorString(); 136 | if (writtenBytes != readBytes) 137 | throw tr("The last block was not fully written (%1 of %2 bytes)!\nAborting.").arg(writtenBytes).arg(readBytes); 138 | 139 | #if defined(Q_OS_LINUX) || defined(Q_OS_MAC) 140 | // In Linux/MacOS the USB device is opened with buffering. Using forced sync to validate progress bar. 141 | // For unknown reason, deviceFile.flush() does not work as intended here. 142 | fsync(deviceFile.handle()); 143 | #endif 144 | 145 | // Inform the GUI thread that next block was written 146 | // TODO: Make sure that when TRANSFER_BLOCK_SIZE is not a multiple of DEFAULT_UNIT 147 | // this still works or at least fails compilation 148 | emit blockWritten(TRANSFER_BLOCK_SIZE / DEFAULT_UNIT); 149 | 150 | // Check for the cancel request (using temporary variable to avoid multiple unlock calls in the code) 151 | m_Mutex.lock(); 152 | cancelRequested = m_CancelWriting; 153 | m_Mutex.unlock(); 154 | if (cancelRequested) 155 | { 156 | // The cancel request was issued 157 | emit cancelled(); 158 | break; 159 | } 160 | if (zeroing) 161 | { 162 | // In zeroing mode only write 1 block - 1 MB is enough to clear both MBR and GPT 163 | break; 164 | } 165 | } 166 | if (!zeroing) 167 | { 168 | if (readBytes < 0) 169 | throw tr("Failed to read the image file:") + "\n" + imageFile.errorString(); 170 | imageFile.close(); 171 | } 172 | deviceFile.close(); 173 | } 174 | catch (QString msg) 175 | { 176 | // Something went wrong :-( 177 | emit error(msg); 178 | isError = true; 179 | } 180 | 181 | if (buffer != NULL) 182 | #if defined(Q_OS_WIN32) 183 | VirtualFree(buffer, TRANSFER_BLOCK_SIZE, MEM_DECOMMIT | MEM_RELEASE); 184 | #elif defined(Q_OS_LINUX) || defined(Q_OS_MAC) 185 | free(buffer); 186 | #endif 187 | 188 | // If no errors occurred and user did not stop the operation, it means everything went fine 189 | if (!isError && !cancelRequested) 190 | emit success(); 191 | 192 | // In any case the operation is finished 193 | emit finished(); 194 | } 195 | 196 | // Implements reaction to the cancel request from user 197 | void ImageWriter::cancelWriting() 198 | { 199 | m_Mutex.lock(); 200 | m_CancelWriting = true; 201 | m_Mutex.unlock(); 202 | } 203 | -------------------------------------------------------------------------------- /src/imagewriter.h: -------------------------------------------------------------------------------- 1 | #ifndef IMAGEWRITER_H 2 | #define IMAGEWRITER_H 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | // ImageWriter is a class for writing image file to the USB flash disk 6 | 7 | #include "src/helper.h" 8 | #include 9 | #include 10 | 11 | #include "src/usbdevice.h" 12 | 13 | 14 | class ImageWriter : public QObject 15 | { 16 | Q_OBJECT 17 | 18 | public: 19 | ImageWriter(const QString& ImageFile, UsbDevice Device, QObject *parent = 0); 20 | 21 | protected: 22 | // Information about the selected USB flash disk 23 | UsbDevice m_Device; 24 | // Source image file (full path); if empty, zero-filled buffer of 1 MB is used 25 | QString m_ImageFile; 26 | // Flag used for cancelling the operation by user 27 | bool m_CancelWriting; 28 | // Mutex for synchronizing access to m_CancelWriting member 29 | QMutex m_Mutex; 30 | 31 | signals: 32 | // Emitted when writeImage is finished for any reason 33 | void finished(); 34 | // Emitted on successful completion 35 | void success(); 36 | // Emitted when something wrong happened, is the error message 37 | void error(QString msg); 38 | // Emitted when processed the cancel request from user 39 | void cancelled(); 40 | // Emitted each time a block is written, is a number of DEFAULT_UNIT-size blocks 41 | void blockWritten(int count); 42 | 43 | public slots: 44 | // The main method that writes the image 45 | void writeImage(); 46 | // Implements reaction to the cancel request from user 47 | void cancelWriting(); 48 | }; 49 | 50 | #endif // IMAGEWRITER_H 51 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "src/helper.h" 2 | #if defined(Q_OS_LINUX) 3 | #include "src/signalhandler.h" 4 | #include 5 | #endif 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #if !defined(Q_OS_WIN32) && !defined(Q_OS_LINUX) && !defined(Q_OS_MAC) 15 | #error Unsupported platform! 16 | #endif 17 | 18 | #if defined(Q_OS_LINUX) 19 | static int setup_unix_signal_handlers() 20 | { 21 | struct sigaction sig; 22 | sig.sa_handler = SignalHandler::handleSignals; 23 | sigemptyset(&sig.sa_mask); 24 | sig.sa_flags = 0; 25 | sig.sa_flags |= SA_RESTART; 26 | 27 | if (sigaction(SIGINT, &sig, 0)) { 28 | return 1; 29 | } 30 | 31 | if (sigaction(SIGTERM, &sig, 0)) { 32 | return 2; 33 | } 34 | 35 | if (sigaction(SIGHUP, &sig, 0)) { 36 | return 3; 37 | } 38 | 39 | return 0; 40 | } 41 | #endif 42 | 43 | int main(int argc, char *argv[]) 44 | { 45 | #if defined(Q_OS_MAC) 46 | // On Mac OS X elevated launch is treated as setuid which is forbidden by default -> enable it 47 | // TODO: Try to find a more "kosher" way, as well as get rid of deprecated AuthorizationExecuteWithPrivileges() 48 | QCoreApplication::setSetuidAllowed(true); 49 | #endif 50 | qmlRegisterType("piw.helper",1,0,"Helper"); 51 | #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) 52 | QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); 53 | #endif 54 | QGuiApplication::setWindowIcon(QIcon(":/images/icon.svg")); 55 | QGuiApplication app(argc, argv); 56 | 57 | QTranslator t; 58 | if (t.load(":/translations/piw_" + QLocale::system().name())) { 59 | app.installTranslator(&t); 60 | } else { 61 | qDebug() << "Could not load the translation"; 62 | } 63 | 64 | #if defined(Q_OS_LINUX) 65 | setup_unix_signal_handlers(); 66 | #endif 67 | 68 | if (!ensureElevated()) { 69 | return 1; 70 | } 71 | 72 | #if defined(Q_OS_WIN32) 73 | // CoInitialize() seems to be called by Qt automatically, so only set security attributes 74 | HRESULT res = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, 0); 75 | if (res != S_OK) 76 | { 77 | printf("CoInitializeSecurity failed! (Code: 0x%08lx)\n", res); 78 | return res; 79 | } 80 | #endif 81 | 82 | QQmlApplicationEngine engine; 83 | engine.load(QUrl(QLatin1String("qrc:/ui/main.qml"))); 84 | 85 | return app.exec(); 86 | } 87 | 88 | 89 | -------------------------------------------------------------------------------- /src/physicaldevice.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Implementation of PhysicalDevice 3 | 4 | 5 | #include "src/physicaldevice.h" 6 | 7 | PhysicalDevice::PhysicalDevice(const QString& name) : 8 | QFile(name) 9 | { 10 | } 11 | 12 | // Opens the selected device in WriteOnly mode 13 | bool PhysicalDevice::open() 14 | { 15 | #if defined(Q_OS_WIN32) 16 | DWORD bret; 17 | 18 | // In Windows QFile with write mode uses disposition OPEN_ALWAYS, but WinAPI 19 | // requires OPEN_EXISTING for physical devices. Therefore we have to use native API. 20 | m_fileHandle = CreateFile( 21 | reinterpret_cast(fileName().utf16()), 22 | GENERIC_WRITE, 23 | FILE_SHARE_READ | FILE_SHARE_WRITE, 24 | NULL, 25 | OPEN_EXISTING, 26 | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, 27 | NULL 28 | ); 29 | if (m_fileHandle == INVALID_HANDLE_VALUE) 30 | { 31 | setErrorString(errorMessageFromCode()); 32 | return false; 33 | } 34 | // Lock the opened device 35 | if (!DeviceIoControl(m_fileHandle, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &bret, NULL)) 36 | { 37 | setErrorString(formatErrorMessageFromCode(tr("Could not acquire lock:"))); 38 | return false; 39 | } 40 | // Construct QFile around the device handle; close() will now close the handle automatically 41 | if (QFile::open(_open_osfhandle(reinterpret_cast(m_fileHandle), 0), QIODevice::WriteOnly | QIODevice::Unbuffered, AutoCloseHandle)) 42 | return true; 43 | else 44 | { 45 | CloseHandle(m_fileHandle); 46 | return false; 47 | } 48 | #elif defined(Q_OS_LINUX) || defined(Q_OS_MAC) 49 | // Simply use QFile, it works fine in Linux 50 | // TODO: Use system call open with O_DIRECT 51 | return QFile::open(QIODevice::WriteOnly); 52 | #else 53 | return false; 54 | #endif 55 | } 56 | -------------------------------------------------------------------------------- /src/physicaldevice.h: -------------------------------------------------------------------------------- 1 | #ifndef PHYSICALDEVICE_H 2 | #define PHYSICALDEVICE_H 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | // Class implementing write-only physical device 6 | 7 | 8 | #include 9 | 10 | #include "src/common.h" 11 | 12 | class PhysicalDevice : public QFile 13 | { 14 | Q_OBJECT 15 | public: 16 | PhysicalDevice(const QString& name); 17 | 18 | // Opens the selected device in WriteOnly mode 19 | virtual bool open(); 20 | 21 | protected: 22 | #if defined(Q_OS_WIN32) 23 | HANDLE m_fileHandle; 24 | #endif 25 | }; 26 | 27 | #endif // PHYSICALDEVICE_H 28 | -------------------------------------------------------------------------------- /src/platform.h: -------------------------------------------------------------------------------- 1 | #ifndef PLATFORM_H 2 | #define PLATFORM_H 3 | 4 | #include 5 | 6 | #if defined(Q_OS_WIN32) 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #endif 14 | 15 | #if defined(Q_OS_LINUX) 16 | #include 17 | #include 18 | #include 19 | #include 20 | #endif 21 | 22 | #if defined(Q_OS_MAC) 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #endif 29 | 30 | #endif // PLATFORM_H 31 | -------------------------------------------------------------------------------- /src/platform_lin.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // This file contains Linux implementation of platform-dependent functions 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "src/usbdevice.h" 9 | 10 | QList platformEnumFlashDevices() 11 | { 12 | // Using /sys/bus/usb/devices directory contents for enumerating the USB devices 13 | // 14 | // Details: 15 | // Take the devices which have /bInterfaceClass contents set to "08" (storage device). 16 | // 17 | // 1. To get the user-friendly name we need to read the and files 18 | // of the parent device (the parent device is the one with less-specified name, e.g. "2-1" for "2-1:1.0"). 19 | // 20 | // 2. The block device name can be found by searching the contents of the following subdirectory: 21 | // /host*/target*//block/ 22 | // where * is a placeholder, and starts with the same substring that "target*" ends with. 23 | // For example, this path may look like follows: 24 | // /sys/bus/usb/devices/1-1:1.0/host4/target4:0:0/4:0:0:0/block/ 25 | // This path contains the list of block devices by their names, e.g. sdc, which gives us /dev/sdc. 26 | // 27 | // 3. And, finally, for the device size we multiply .../block/sdX/size (the number of sectors) with 28 | // .../block/sdX/queue/logical_block_size (the sector size). 29 | 30 | QList l; 31 | // Start with enumerating all the USB devices 32 | QString usbDevicesRoot = "/sys/bus/usb/devices"; 33 | QDir dirList(usbDevicesRoot); 34 | QStringList usbDevices = dirList.entryList(QDir::Dirs | QDir::NoDotAndDotDot); 35 | for (int deviceIdx = 0; deviceIdx < usbDevices.size(); ++deviceIdx) 36 | { 37 | QDir deviceDir = dirList; 38 | if (!deviceDir.cd(usbDevices[deviceIdx])) 39 | continue; 40 | 41 | // Skip devices with wrong interface class 42 | if (readFileContents(deviceDir.absoluteFilePath("bInterfaceClass")) != "08\n") 43 | continue; 44 | 45 | // Search for "host*" entries and process them 46 | QStringList hosts = deviceDir.entryList(QStringList("host*")); 47 | for (int hostIdx = 0; hostIdx < hosts.size(); ++hostIdx) 48 | { 49 | QDir hostDir = deviceDir; 50 | if (!hostDir.cd(hosts[hostIdx])) 51 | continue; 52 | 53 | // Search for "target*" entries and process them 54 | QStringList targets = hostDir.entryList(QStringList("target*")); 55 | for (int targetIdx = 0; targetIdx < targets.size(); ++targetIdx) 56 | { 57 | QDir targetDir = hostDir; 58 | if (!targetDir.cd(targets[targetIdx])) 59 | continue; 60 | 61 | // Remove the "target" part and append "*" to search for appropriate SCSI devices 62 | QStringList scsiTargets = targetDir.entryList(QStringList(targets[targetIdx].mid(6) + "*")); 63 | for (int scsiTargetIdx = 0; scsiTargetIdx < scsiTargets.size(); ++scsiTargetIdx) 64 | { 65 | QDir scsiTargetDir = targetDir; 66 | if (!scsiTargetDir.cd(scsiTargets[scsiTargetIdx] + "/block")) 67 | continue; 68 | 69 | // Read the list of block devices and process them 70 | QStringList blockDevices = scsiTargetDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); 71 | for (int blockDeviceIdx = 0; blockDeviceIdx < blockDevices.size(); ++blockDeviceIdx) 72 | { 73 | // Create the new UsbDevice object to bind information to the listbox entry 74 | UsbDevice* deviceData = new UsbDevice; 75 | 76 | // Use the block device name as both physical device and displayed volume name 77 | deviceData->m_PhysicalDevice = "/dev/" + blockDevices[blockDeviceIdx]; 78 | deviceData->m_Volumes << deviceData->m_PhysicalDevice; 79 | 80 | quint64 blocksNum = readFileContents(scsiTargetDir.absoluteFilePath(blockDevices[blockDeviceIdx] + "/size")).toULongLong(); 81 | if(!(blocksNum > 0)) 82 | continue; 83 | // The size is counted in logical blocks (tested with 4K-sector HDD) 84 | deviceData->m_SectorSize = readFileContents(scsiTargetDir.absoluteFilePath(blockDevices[blockDeviceIdx] + "/queue/logical_block_size")).toUInt(); 85 | if (deviceData->m_SectorSize == 0) 86 | deviceData->m_SectorSize = 512; 87 | 88 | deviceData->m_Size = blocksNum * deviceData->m_SectorSize; 89 | 90 | // Get the user-friendly name for the device by reading the parent device fields 91 | QString usbParentDevice = usbDevices[deviceIdx]; 92 | usbParentDevice.replace(QRegularExpression("^(\\d+-\\d+):.*$"), "\\1"); 93 | usbParentDevice.prepend(usbDevicesRoot + "/"); 94 | // TODO: Find out how to get more "friendly" name (for SATA-USB connector it shows the bridge 95 | // device name instead of the disk drive name) 96 | QDir credentialsDir(usbParentDevice); 97 | QString manufacturerPath = usbParentDevice + "/manufacturer"; 98 | QString productPath = usbParentDevice + "/product"; 99 | 100 | if(!(QFileInfo(manufacturerPath).exists() || QFileInfo(productPath).exists())){ 101 | credentialsDir.cd(credentialsDir.canonicalPath()); 102 | credentialsDir.cdUp(); 103 | } 104 | deviceData->m_VisibleName = ( 105 | readFileContents(credentialsDir.absolutePath() + "/manufacturer") 106 | .trimmed() + " " + 107 | readFileContents(credentialsDir.absolutePath() + "/product") 108 | .trimmed()).trimmed(); 109 | 110 | // The device information is now complete, append the entry 111 | l.append(*deviceData); 112 | } 113 | } 114 | } 115 | } 116 | } 117 | return l; 118 | } 119 | 120 | bool ensureElevated() 121 | { 122 | // If we already have root privileges do nothing 123 | uid_t uid = getuid(); 124 | if (uid == 0) { 125 | return true; 126 | } else { 127 | qDebug() << "Please, restart the program with root privileges."; 128 | return false; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/platform_mac.mm: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // This file contains Mac implementation of platform-dependent functions 3 | 4 | #include "src/common.h" 5 | #include "src/usbdevice.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | bool readBooleanRegKey(io_service_t device, CFStringRef key) 15 | { 16 | CFTypeRef value = IORegistryEntrySearchCFProperty( 17 | device, 18 | kIOServicePlane, 19 | key, 20 | kCFAllocatorDefault, 21 | kIORegistryIterateRecursively 22 | ); 23 | bool res = false; 24 | if (value != nil) 25 | { 26 | if (CFGetTypeID(value) == CFBooleanGetTypeID()) 27 | res = (CFBooleanGetValue((CFBooleanRef)value) ? true : false); 28 | CFRelease(value); 29 | } 30 | return res; 31 | } 32 | 33 | unsigned long long readIntegerRegKey(io_service_t device, CFStringRef key) 34 | { 35 | CFTypeRef value = IORegistryEntrySearchCFProperty( 36 | device, 37 | kIOServicePlane, 38 | key, 39 | kCFAllocatorDefault, 40 | kIORegistryIterateRecursively 41 | ); 42 | unsigned long long res = 0; 43 | if (value != nil) 44 | { 45 | CFNumberGetValue((CFNumberRef)value, kCFNumberLongLongType, &res); 46 | CFRelease(value); 47 | } 48 | return res; 49 | } 50 | 51 | 52 | CFStringRef readStringRegKey(io_service_t device, CFStringRef key) 53 | { 54 | CFTypeRef value = IORegistryEntrySearchCFProperty( 55 | device, 56 | kIOServicePlane, 57 | key, 58 | kCFAllocatorDefault, 59 | kIORegistryIterateRecursively 60 | ); 61 | CFStringRef res = nil; 62 | if (value != nil) 63 | { 64 | if (CFGetTypeID(value) == CFStringGetTypeID()) 65 | res = (CFStringRef)value; 66 | else 67 | CFRelease(value); 68 | } 69 | return res; 70 | } 71 | 72 | 73 | QList platformEnumFlashDevices() 74 | { 75 | CFMutableDictionaryRef matchingDict; 76 | io_iterator_t iter; 77 | kern_return_t kr; 78 | io_service_t device; 79 | 80 | // Set up a matching dictionary for the class 81 | matchingDict = IOServiceMatching(kIOUSBDeviceClassName); 82 | if (matchingDict == NULL) 83 | { 84 | return QList(); 85 | } 86 | 87 | // Obtain iterator 88 | kr = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &iter); 89 | if (kr != KERN_SUCCESS) 90 | { 91 | return QList(); 92 | } 93 | 94 | CFStringEncoding encodingMethod = CFStringGetSystemEncoding(); 95 | 96 | QList l; 97 | // Enumerate the devices 98 | while ((device = IOIteratorNext(iter))) 99 | { 100 | // Skip all non-removable devices 101 | if (!readBooleanRegKey(device, CFSTR(kIOMediaRemovableKey))) 102 | { 103 | IOObjectRelease(device); 104 | continue; 105 | } 106 | 107 | // Skip devices without BSD names (that is, not real disks) 108 | CFStringRef tempStr = readStringRegKey(device, CFSTR(kIOBSDNameKey)); 109 | if (tempStr == nil) 110 | { 111 | IOObjectRelease(device); 112 | continue; 113 | } 114 | 115 | // Fetch the required properties and store them in the UsbDevice object 116 | UsbDevice* deviceData = new UsbDevice; 117 | 118 | // Physical device name 119 | // Using "rdiskN" instead of BSD name "diskN" to work around an OS X bug when writing 120 | // to "diskN" is extremely slow 121 | QString bsdName = CFStringGetCStringPtr(tempStr, encodingMethod); 122 | CFRelease(tempStr); 123 | deviceData->m_PhysicalDevice = "/dev/r" + bsdName; 124 | // Volume names are very long, so display the device name instead 125 | deviceData->m_Volumes << "/dev/" + bsdName; 126 | 127 | // User-friendly device name: vendor+product 128 | tempStr = readStringRegKey(device, CFSTR(kUSBVendorString)); 129 | if (tempStr != nil) 130 | { 131 | deviceData->m_VisibleName = CFStringGetCStringPtr(tempStr, encodingMethod); 132 | deviceData->m_VisibleName = deviceData->m_VisibleName.trimmed(); 133 | CFRelease(tempStr); 134 | } 135 | tempStr = readStringRegKey(device, CFSTR(kUSBProductString)); 136 | if (tempStr != nil) 137 | { 138 | deviceData->m_VisibleName += " "; 139 | deviceData->m_VisibleName += CFStringGetCStringPtr(tempStr, encodingMethod); 140 | deviceData->m_VisibleName = deviceData->m_VisibleName.trimmed(); 141 | CFRelease(tempStr); 142 | } 143 | 144 | // Size of the flash disk 145 | deviceData->m_Size = readIntegerRegKey(device, CFSTR(kIOMediaSizeKey)); 146 | deviceData->m_SectorSize = readIntegerRegKey(device, CFSTR(kIOMediaPreferredBlockSizeKey)); 147 | 148 | // The device information is now complete, append the entry 149 | l.append(*deviceData); 150 | 151 | // Free the resources 152 | IOObjectRelease(device); 153 | } 154 | 155 | IOObjectRelease(iter); 156 | return l; 157 | } 158 | 159 | bool ensureElevated() 160 | { 161 | uid_t uid = getuid(); 162 | uid_t euid = geteuid(); 163 | if ((uid == 0) || (euid == 0)) 164 | return true; 165 | 166 | AuthorizationItem authItem = { kSMRightModifySystemDaemons, 0, NULL, 0 }; 167 | AuthorizationRights authRights = { 1, &authItem }; 168 | AuthorizationFlags flags = kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed | kAuthorizationFlagPreAuthorize | kAuthorizationFlagExtendRights; 169 | 170 | AuthorizationRef authRef = NULL; 171 | 172 | if (AuthorizationCreate(&authRights, kAuthorizationEmptyEnvironment, flags, &authRef) != errAuthorizationSuccess) 173 | return false; 174 | 175 | // Prepare list of arguments for restarting ImageWriter 176 | // We need to explicitly pass language and initial directory so that the new instance 177 | // inherited the current user's parameters rather than root's 178 | const size_t maxArgsNum = 5; 179 | // Make sure QByteArray objects live long enough, so that their data()'s were valid until execv() call 180 | QByteArray argsBA[maxArgsNum + 1]; 181 | size_t argNo = 0; 182 | // Executable is not required as first argument, so start with options 183 | // Convert arguments into char*'s and append NULL element 184 | 185 | exit(0); 186 | } 187 | 188 | void disableHideOnDeactivate(WId wid) 189 | { 190 | [[reinterpret_cast(wid) window] setHidesOnDeactivate: NO]; 191 | } 192 | -------------------------------------------------------------------------------- /src/platform_win.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // This file contains Windows implementation of platform-dependent functions 3 | 4 | #include "src/common.h" 5 | #include "src/usbdevice.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | // Several WinAPI COM specific macros for keeping the code clean 12 | 13 | // Runs the COM request specified, checks for return value and throws an exception 14 | // with descriptive error message if it's not OK 15 | #define CHECK_OK(code, msg) \ 16 | { \ 17 | HRESULT res = code; \ 18 | if (res != S_OK) \ 19 | { \ 20 | throw formatErrorMessageFromCode(msg, res); \ 21 | } \ 22 | } 23 | 24 | // Releases the COM object and nullifies the pointer 25 | #define SAFE_RELEASE(obj) \ 26 | { \ 27 | if (obj != nullptr) \ 28 | { \ 29 | obj->Release(); \ 30 | obj = nullptr; \ 31 | } \ 32 | } 33 | 34 | // Allocated a BSTR string using the specified text, checks for successful memory allocation 35 | // and throws an exception with descriptive error message if unsuccessful 36 | #define ALLOC_BSTR(name, str) \ 37 | { \ 38 | name = SysAllocString(str); \ 39 | if (name == nullptr) \ 40 | { \ 41 | throw QObject::tr("Memory allocation for %1 failed.").arg(#name); \ 42 | } \ 43 | } 44 | 45 | // Releases the BSTR string and nullifies the pointer 46 | #define FREE_BSTR(str) \ 47 | { \ 48 | SysFreeString(str); \ 49 | str = nullptr; \ 50 | } 51 | 52 | void delay( int millisecondsToWait ) 53 | { 54 | QTime dieTime = QTime::currentTime().addMSecs( millisecondsToWait ); 55 | while( QTime::currentTime() < dieTime ) 56 | { 57 | QCoreApplication::processEvents( QEventLoop::AllEvents, 100 ); 58 | } 59 | } 60 | 61 | QList platformEnumFlashDevices() 62 | { 63 | // Using WMI for enumerating the USB devices 64 | 65 | // Namespace of the WMI classes 66 | BSTR strNamespace = nullptr; 67 | // "WQL" - the query language we're gonna use (the only possible, actually) 68 | BSTR strQL = nullptr; 69 | // Query string for requesting physical devices 70 | BSTR strQueryDisks = nullptr; 71 | // Query string for requesting partitions for each of the the physical devices 72 | BSTR strQueryPartitions = nullptr; 73 | // Query string for requesting logical disks for each of the partitions 74 | BSTR strQueryLetters = nullptr; 75 | 76 | // Various COM objects for executing the queries, enumerating lists and retrieving properties 77 | IWbemLocator* pIWbemLocator = nullptr; 78 | IWbemServices* pWbemServices = nullptr; 79 | IEnumWbemClassObject* pEnumDisksObject = nullptr; 80 | IEnumWbemClassObject* pEnumPartitionsObject = nullptr; 81 | IEnumWbemClassObject* pEnumLettersObject = nullptr; 82 | IWbemClassObject* pDiskObject = nullptr; 83 | IWbemClassObject* pPartitionObject = nullptr; 84 | IWbemClassObject* pLetterObject = nullptr; 85 | 86 | // Temporary object for attaching data to the combobox entries 87 | UsbDevice* deviceData; 88 | QList l; 89 | bool loop; 90 | try 91 | { 92 | // Start with allocating the fixed strings 93 | ALLOC_BSTR(strNamespace, L"root\\cimv2"); 94 | ALLOC_BSTR(strQL, L"WQL"); 95 | ALLOC_BSTR(strQueryDisks, L"SELECT * FROM Win32_DiskDrive WHERE InterfaceType = \"USB\""); 96 | 97 | // Create the IWbemLocator and execute the first query (list of physical disks attached via USB) 98 | CHECK_OK(CoCreateInstance(CLSID_WbemAdministrativeLocator, nullptr, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, IID_IUnknown, reinterpret_cast(&pIWbemLocator)), QObject::tr("CoCreateInstance(WbemAdministrativeLocator) failed.")); 99 | loop = true; 100 | while(loop) { 101 | if(pIWbemLocator != nullptr) { 102 | HRESULT res = pIWbemLocator->ConnectServer(strNamespace, nullptr, nullptr, nullptr, 0, nullptr, nullptr, &pWbemServices); 103 | if (res == S_OK) { 104 | loop = false; 105 | } else { 106 | delay(10); 107 | } 108 | } else { 109 | loop = false; 110 | } 111 | } 112 | 113 | CHECK_OK(pWbemServices->ExecQuery(strQL, strQueryDisks, WBEM_FLAG_RETURN_IMMEDIATELY, nullptr, &pEnumDisksObject), QObject::tr("Failed to query USB flash devices.")); 114 | 115 | // Enumerate the received list of devices 116 | for (;;) 117 | { 118 | // Get the next available device or exit the loop 119 | ULONG uReturned; 120 | pEnumDisksObject->Next(WBEM_INFINITE, 1, &pDiskObject, &uReturned); 121 | if (uReturned == 0) 122 | break; 123 | 124 | VARIANT val; 125 | 126 | // Fetch the required properties and store them in the UsbDevice object 127 | deviceData = new UsbDevice; 128 | 129 | // User-friendly name of the device 130 | if (pDiskObject->Get(L"Model", 0, &val, 0, 0) == WBEM_S_NO_ERROR) 131 | { 132 | if (val.vt == VT_BSTR) 133 | { 134 | deviceData->m_VisibleName = QString::fromWCharArray(val.bstrVal); 135 | } 136 | VariantClear(&val); 137 | } 138 | 139 | // System name of the device 140 | if (pDiskObject->Get(L"DeviceID", 0, &val, 0, 0) == WBEM_S_NO_ERROR) 141 | { 142 | if (val.vt == VT_BSTR) 143 | { 144 | deviceData->m_PhysicalDevice = QString::fromWCharArray(val.bstrVal); 145 | } 146 | VariantClear(&val); 147 | } 148 | 149 | // Size of the devifce 150 | if (pDiskObject->Get(L"Size", 0, &val, 0, 0) == WBEM_S_NO_ERROR) 151 | { 152 | if (val.vt == VT_BSTR) 153 | { 154 | deviceData->m_Size = QString::fromWCharArray(val.bstrVal).toULongLong(); 155 | } 156 | VariantClear(&val); 157 | } 158 | 159 | // Sector size of the devifce 160 | if (pDiskObject->Get(L"BytesPerSector", 0, &val, 0, 0) == WBEM_S_NO_ERROR) 161 | { 162 | if (val.vt == VT_I4) 163 | { 164 | deviceData->m_SectorSize = val.intVal; 165 | } 166 | VariantClear(&val); 167 | } 168 | 169 | // The device object is no longer needed, release it 170 | SAFE_RELEASE(pDiskObject); 171 | 172 | // Construct the request for listing the partitions on the current disk 173 | QString qstrQueryPartitions = "ASSOCIATORS OF {Win32_DiskDrive.DeviceID='" + deviceData->m_PhysicalDevice + "'} WHERE AssocClass = Win32_DiskDriveToDiskPartition"; 174 | ALLOC_BSTR(strQueryPartitions, reinterpret_cast(qstrQueryPartitions.utf16())); 175 | 176 | // Execute the query 177 | CHECK_OK(pWbemServices->ExecQuery(strQL, strQueryPartitions, WBEM_FLAG_RETURN_IMMEDIATELY, nullptr, &pEnumPartitionsObject), QObject::tr("Failed to query list of partitions.")); 178 | 179 | // Enumerate the received list of partitions 180 | for (;;) 181 | { 182 | // Get the next available partition or exit the loop 183 | pEnumPartitionsObject->Next(WBEM_INFINITE, 1, &pPartitionObject, &uReturned); 184 | if (uReturned == 0) 185 | break; 186 | 187 | // Fetch the DeviceID property and store it for using in the next request 188 | QString qstrQueryLetters = ""; 189 | if (pPartitionObject->Get(L"DeviceID", 0, &val, 0, 0) == WBEM_S_NO_ERROR) 190 | { 191 | if (val.vt == VT_BSTR) 192 | { 193 | qstrQueryLetters = QString::fromWCharArray(val.bstrVal); 194 | } 195 | VariantClear(&val); 196 | } 197 | 198 | // The partition object is no longer needed, release it 199 | SAFE_RELEASE(pPartitionObject); 200 | 201 | // If DeviceID was fetched proceed to the logical disks 202 | if (qstrQueryLetters != "") 203 | { 204 | // Construct the request for listing the logical disks related to the current partition 205 | qstrQueryLetters = "ASSOCIATORS OF {Win32_DiskPartition.DeviceID='" + qstrQueryLetters + "'} WHERE AssocClass = Win32_LogicalDiskToPartition"; 206 | ALLOC_BSTR(strQueryLetters, reinterpret_cast(qstrQueryLetters.utf16())); 207 | 208 | // Execute the query 209 | CHECK_OK(pWbemServices->ExecQuery(strQL, strQueryLetters, WBEM_FLAG_RETURN_IMMEDIATELY, nullptr, &pEnumLettersObject), QObject::tr("Failed to query list of logical disks.")); 210 | 211 | // Enumerate the received list of logical disks 212 | for (;;) 213 | { 214 | // Get the next available logical disk or exit the loop 215 | pEnumLettersObject->Next(WBEM_INFINITE, 1, &pLetterObject, &uReturned); 216 | if (uReturned == 0) 217 | break; 218 | 219 | // Fetch the disk letter and add it to the list of volumes in the UsbDevice object 220 | if (pLetterObject->Get(L"Caption", 0, &val, 0, 0) == WBEM_S_NO_ERROR) 221 | { 222 | if (val.vt == VT_BSTR) 223 | { 224 | deviceData->m_Volumes << QString::fromWCharArray(val.bstrVal); 225 | } 226 | VariantClear(&val); 227 | } 228 | 229 | // The logical disk object is no longer needed, release it 230 | SAFE_RELEASE(pLetterObject); 231 | } 232 | 233 | // Release the logical disks enumerator object and the corresponding query string 234 | SAFE_RELEASE(pEnumLettersObject); 235 | FREE_BSTR(strQueryLetters); 236 | } 237 | } 238 | 239 | // Release the partitions enumerator object and the corresponding query string 240 | SAFE_RELEASE(pEnumPartitionsObject); 241 | FREE_BSTR(strQueryPartitions); 242 | 243 | // The device information is now complete, append the entry 244 | l.append(*deviceData); 245 | // The object is now under the GUI control, nullify the pointer 246 | deviceData = nullptr; 247 | } 248 | } 249 | catch (QString errMessage) 250 | { 251 | // Something bad happened 252 | qDebug() << errMessage; 253 | } 254 | 255 | // The cleanup stage 256 | if (deviceData != nullptr) 257 | delete deviceData; 258 | 259 | SAFE_RELEASE(pLetterObject); 260 | SAFE_RELEASE(pPartitionObject); 261 | SAFE_RELEASE(pDiskObject); 262 | SAFE_RELEASE(pEnumDisksObject); 263 | SAFE_RELEASE(pEnumPartitionsObject); 264 | SAFE_RELEASE(pEnumLettersObject); 265 | SAFE_RELEASE(pWbemServices); 266 | SAFE_RELEASE(pIWbemLocator); 267 | 268 | FREE_BSTR(strNamespace); 269 | FREE_BSTR(strQL); 270 | FREE_BSTR(strQueryDisks); 271 | FREE_BSTR(strQueryPartitions); 272 | FREE_BSTR(strQueryLetters); 273 | return l; 274 | } 275 | 276 | bool ensureElevated() 277 | { 278 | // In Windows the manifest already ensures elevated privileges 279 | return true; 280 | } 281 | -------------------------------------------------------------------------------- /src/signalhandler.cpp: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * Copyright (C) 2017 by Yunusemre Senturk * 3 | * * 4 | * * 5 | * This program is free software; you can redistribute it and/or modify * 6 | * it under the terms of the GNU General Public License as published by * 7 | * the Free Software Foundation; either version 2 of the License, or * 8 | * (at your option) any later version. * 9 | * * 10 | * This program is distributed in the hope that it will be useful, * 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 13 | * GNU General Public License for more details. * 14 | * * 15 | * You should have received a copy of the GNU General Public License * 16 | * along with this program; if not, write to the * 17 | * Free Software Foundation, Inc., * 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * 19 | *****************************************************************************/ 20 | #include "src/signalhandler.h" 21 | #include "src/helper.h" 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | int SignalHandler::sigFd[2]; 29 | 30 | SignalHandler::SignalHandler(QObject *parent) : QObject(parent) 31 | { 32 | if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sigFd)) { 33 | qFatal("Couldn't create socketpair"); 34 | } 35 | sig = new QSocketNotifier(sigFd[1], QSocketNotifier::Read, this); 36 | connect(sig, SIGNAL(activated(int)), this, SLOT(handleSignalSlot())); 37 | 38 | } 39 | 40 | void SignalHandler::setHelper(Helper *h) 41 | { 42 | helper = h; 43 | } 44 | 45 | void SignalHandler::handleSignals(int) 46 | { 47 | char a = 1; 48 | ::write(sigFd[0], &a, sizeof(a)); 49 | } 50 | 51 | void SignalHandler::handleSignalSlot() 52 | { 53 | sig->setEnabled(false); 54 | char tmp; 55 | ::read(sigFd[1], &tmp, sizeof(tmp)); 56 | 57 | emit helper->terminateCalled(); 58 | 59 | sig->setEnabled(true); 60 | } 61 | -------------------------------------------------------------------------------- /src/signalhandler.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * Copyright (C) 2017 by Yunusemre Senturk * 3 | * * 4 | * * 5 | * This program is free software; you can redistribute it and/or modify * 6 | * it under the terms of the GNU General Public License as published by * 7 | * the Free Software Foundation; either version 2 of the License, or * 8 | * (at your option) any later version. * 9 | * * 10 | * This program is distributed in the hope that it will be useful, * 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 13 | * GNU General Public License for more details. * 14 | * * 15 | * You should have received a copy of the GNU General Public License * 16 | * along with this program; if not, write to the * 17 | * Free Software Foundation, Inc., * 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * 19 | *****************************************************************************/ 20 | #ifndef SIGNALHANDLER_H 21 | #define SIGNALHANDLER_H 22 | 23 | #include 24 | 25 | class QSocketNotifier; 26 | class Helper; 27 | 28 | class SignalHandler : public QObject 29 | { 30 | Q_OBJECT 31 | public: 32 | explicit SignalHandler(QObject *parent = 0); 33 | 34 | static void handleSignals(int); 35 | void setHelper(Helper *h); 36 | 37 | public slots: 38 | void handleSignalSlot(); 39 | 40 | private: 41 | static int sigFd[2]; 42 | QSocketNotifier *sig; 43 | Helper *helper; 44 | }; 45 | 46 | #endif // SIGNALHANDLER_H 47 | -------------------------------------------------------------------------------- /src/usbdevice.h: -------------------------------------------------------------------------------- 1 | #ifndef USBDEVICE_H 2 | #define USBDEVICE_H 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | // Class for storing information about USB flash disk 6 | 7 | #include "src/common.h" 8 | 9 | #include 10 | 11 | class UsbDevice 12 | { 13 | public: 14 | UsbDevice() : 15 | m_VisibleName(QObject::tr("Unknown Device")), 16 | m_Volumes(), 17 | m_Size(0), 18 | m_SectorSize(512), 19 | m_PhysicalDevice("") {} 20 | 21 | // Formats the device description for GUI 22 | // The format is: " - ()" 23 | QString formatDisplayName() const { 24 | return ((m_Volumes.size() == 0) ? QObject::tr("") : m_Volumes.join(", ")) + " - " + m_VisibleName + " (" + QString::number(alignNumberDiv(m_Size, DEFAULT_UNIT)) + " " + QObject::tr("MB") + ")"; 25 | } 26 | 27 | // User-friendly name of the device 28 | QString m_VisibleName; 29 | // List of mounted volumes from this device 30 | QStringList m_Volumes; 31 | // Size of the device 32 | quint64 m_Size; 33 | // Sector size 34 | quint32 m_SectorSize; 35 | // System name of the physical disk 36 | QString m_PhysicalDevice; 37 | }; 38 | 39 | Q_DECLARE_METATYPE(UsbDevice*) 40 | 41 | 42 | #endif // USBDEVICE_H 43 | -------------------------------------------------------------------------------- /src/usbdevicemonitor.h: -------------------------------------------------------------------------------- 1 | #ifndef USBDEVICEMONITOR_H 2 | #define USBDEVICEMONITOR_H 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "src/common.h" 10 | 11 | class UsbDeviceMonitorPrivate; 12 | class UsbDeviceMonitor : public QObject, public QAbstractNativeEventFilter 13 | { 14 | Q_OBJECT 15 | 16 | protected: 17 | UsbDeviceMonitorPrivate* const d_ptr; 18 | 19 | public: 20 | explicit UsbDeviceMonitor(QObject *parent = 0); 21 | ~UsbDeviceMonitor(); 22 | 23 | // Implements QAbstractNativeEventFilter interface for processing WM_DEVICECHANGE messages (Windows) 24 | bool nativeEventFilter(const QByteArray& eventType, void* message, long* result); 25 | 26 | protected: 27 | // Closes handles and frees resources 28 | void cleanup(); 29 | 30 | signals: 31 | // Emitted when device change notification arrives 32 | void deviceChanged(); 33 | 34 | public slots: 35 | // Initializes monitoring for USB devices 36 | bool startMonitoring(); 37 | }; 38 | 39 | #endif // USBDEVICEMONITOR_H 40 | -------------------------------------------------------------------------------- /src/usbdevicemonitor_lin.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Linux implementation of UsbDeviceMonitor 3 | 4 | #include 5 | 6 | #include "src/usbdevicemonitor.h" 7 | #include "src/usbdevicemonitor_lin_p.h" 8 | 9 | // Declare required functions as weak so that they were not reported as missing at compile time. 10 | // In runtime it is required to ensure they are defined: we do it by checking that libudev is loaded. 11 | #pragma weak udev_device_unref 12 | #pragma weak udev_monitor_enable_receiving 13 | #pragma weak udev_monitor_filter_add_match_subsystem_devtype 14 | #pragma weak udev_monitor_get_fd 15 | #pragma weak udev_monitor_new_from_netlink 16 | #pragma weak udev_monitor_receive_device 17 | #pragma weak udev_monitor_unref 18 | #pragma weak udev_new 19 | #pragma weak udev_unref 20 | 21 | 22 | // Private class implementation 23 | 24 | UsbDeviceMonitorPrivate::UsbDeviceMonitorPrivate(QObject *parent) : 25 | QObject(parent) 26 | { 27 | m_udevLib = dlopen("libudev.so.1", RTLD_NOW | RTLD_GLOBAL); 28 | if (m_udevLib == NULL) 29 | m_udevLib = dlopen("libudev.so.0", RTLD_NOW | RTLD_GLOBAL); 30 | } 31 | 32 | UsbDeviceMonitorPrivate::~UsbDeviceMonitorPrivate() 33 | { 34 | if (m_udevLib != NULL) 35 | dlclose(m_udevLib); 36 | } 37 | 38 | // Processes udev socket notification 39 | void UsbDeviceMonitorPrivate::processUdevNotification(int socket) 40 | { 41 | Q_UNUSED(socket); 42 | if (m_udevLib == NULL) 43 | return; 44 | 45 | // Read the device information 46 | // We don't really need it, but we have to empty the queue 47 | struct udev_device* dev = udev_monitor_receive_device(m_udevMonitor); 48 | if (dev) 49 | { 50 | udev_device_unref(dev); 51 | emit q_ptr->deviceChanged(); 52 | } 53 | } 54 | 55 | 56 | // Main class implementation 57 | 58 | UsbDeviceMonitor::UsbDeviceMonitor(QObject *parent) : 59 | QObject(parent), 60 | d_ptr(new UsbDeviceMonitorPrivate()) 61 | { 62 | d_ptr->q_ptr = this; 63 | d_ptr->m_udev = NULL; 64 | d_ptr->m_udevMonitor = NULL; 65 | d_ptr->m_udevNotifier = NULL; 66 | } 67 | 68 | UsbDeviceMonitor::~UsbDeviceMonitor() 69 | { 70 | cleanup(); 71 | delete d_ptr; 72 | } 73 | 74 | // Closes handles and frees resources 75 | void UsbDeviceMonitor::cleanup() 76 | { 77 | if (d_ptr->m_udevLib == NULL) 78 | return; 79 | if (d_ptr->m_udevMonitor != NULL) 80 | { 81 | udev_monitor_unref(d_ptr->m_udevMonitor); 82 | d_ptr->m_udevMonitor = NULL; 83 | } 84 | if (d_ptr->m_udev != NULL) 85 | { 86 | udev_unref(d_ptr->m_udev); 87 | d_ptr->m_udev = NULL; 88 | } 89 | if (d_ptr->m_udevNotifier) 90 | { 91 | d_ptr->m_udevNotifier->setEnabled(false); 92 | delete d_ptr->m_udevNotifier; 93 | d_ptr->m_udevNotifier = NULL; 94 | } 95 | } 96 | 97 | // Implements QAbstractNativeEventFilter interface for processing WM_DEVICECHANGE messages (Windows) 98 | bool UsbDeviceMonitor::nativeEventFilter(const QByteArray& eventType, void* message, long* result) 99 | { 100 | Q_UNUSED(eventType); 101 | Q_UNUSED(message); 102 | Q_UNUSED(result); 103 | return false; 104 | } 105 | 106 | bool UsbDeviceMonitor::startMonitoring() 107 | { 108 | // In Linux we use udev monitor 109 | if (d_ptr->m_udevLib == NULL) 110 | return false; 111 | try 112 | { 113 | d_ptr->m_udev = udev_new(); 114 | if (d_ptr->m_udev == NULL) 115 | throw 1; 116 | 117 | // Initialize monitoring 118 | d_ptr->m_udevMonitor = udev_monitor_new_from_netlink(d_ptr->m_udev, "udev"); 119 | if (d_ptr->m_udevMonitor == NULL) 120 | throw 1; 121 | // Set filter to get notified only about block devices of type "disk" 122 | if (udev_monitor_filter_add_match_subsystem_devtype(d_ptr->m_udevMonitor, "block", "disk") < 0) 123 | throw 1; 124 | // Start monitoring 125 | if (udev_monitor_enable_receiving(d_ptr->m_udevMonitor) < 0) 126 | throw 1; 127 | // Get the socket file descriptor for QSocketNotifier 128 | int fd = udev_monitor_get_fd(d_ptr->m_udevMonitor); 129 | 130 | // Initialize QSocketNotifier for watching the socket 131 | d_ptr->m_udevNotifier = new QSocketNotifier(fd, QSocketNotifier::Read); 132 | connect(d_ptr->m_udevNotifier, &QSocketNotifier::activated, d_ptr, &UsbDeviceMonitorPrivate::processUdevNotification); 133 | d_ptr->m_udevNotifier->setEnabled(true); 134 | } 135 | catch (...) 136 | { 137 | // Something went wrong, destroy everything and do without monitoring udev 138 | cleanup(); 139 | return false; 140 | } 141 | 142 | return true; 143 | } 144 | -------------------------------------------------------------------------------- /src/usbdevicemonitor_lin_p.h: -------------------------------------------------------------------------------- 1 | #ifndef USBDEVICEMONITOR_LIN_P_H 2 | #define USBDEVICEMONITOR_LIN_P_H 3 | 4 | #include 5 | #include 6 | 7 | // Class with platform-specific data 8 | class UsbDeviceMonitor; 9 | class UsbDeviceMonitorPrivate : public QObject 10 | { 11 | Q_OBJECT 12 | 13 | public: 14 | explicit UsbDeviceMonitorPrivate(QObject *parent = 0); 15 | virtual ~UsbDeviceMonitorPrivate(); 16 | 17 | UsbDeviceMonitor* q_ptr; 18 | 19 | // Handle to dynamically loaded udev library 20 | void* m_udevLib; 21 | // udev library context 22 | struct udev* m_udev; 23 | // udev device monitor handle 24 | struct udev_monitor* m_udevMonitor; 25 | // Watcher for udev monitor socket 26 | QSocketNotifier* m_udevNotifier; 27 | 28 | public slots: 29 | // Processes udev socket notification 30 | void processUdevNotification(int socket); 31 | }; 32 | 33 | 34 | #endif // USBDEVICEMONITOR_LIN_P_H 35 | -------------------------------------------------------------------------------- /src/usbdevicemonitor_mac.mm: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Mac OS X implementation of UsbDeviceMonitor 3 | 4 | #include "src/usbdevicemonitor.h" 5 | #include "src/usbdevicemonitor_mac_p.h" 6 | 7 | #include 8 | #include 9 | 10 | 11 | // Private class implementation 12 | 13 | UsbDeviceMonitorPrivate::UsbDeviceMonitorPrivate() 14 | { 15 | } 16 | 17 | UsbDeviceMonitorPrivate::~UsbDeviceMonitorPrivate() 18 | { 19 | } 20 | 21 | 22 | // Supplemental callback functions 23 | 24 | void UsbDeviceAddedCallback(void *refCon, io_iterator_t iterator) 25 | { 26 | while (IOIteratorNext(iterator)) {}; // Run out the iterator or notifications won't start 27 | UsbDeviceMonitor* monitor = (UsbDeviceMonitor*)refCon; 28 | emit monitor->deviceChanged(); 29 | } 30 | 31 | void UsbDeviceRemovedCallback(void *refCon, io_iterator_t iterator) 32 | { 33 | while (IOIteratorNext(iterator)) {}; // Run out the iterator or notifications won't start 34 | UsbDeviceMonitor* monitor = (UsbDeviceMonitor*)refCon; 35 | emit monitor->deviceChanged(); 36 | } 37 | 38 | 39 | // Main class implementation 40 | 41 | UsbDeviceMonitor::UsbDeviceMonitor(QObject *parent) : 42 | QObject(parent), 43 | d_ptr(new UsbDeviceMonitorPrivate()) 44 | { 45 | } 46 | 47 | UsbDeviceMonitor::~UsbDeviceMonitor() 48 | { 49 | cleanup(); 50 | delete d_ptr; 51 | } 52 | 53 | // Closes handles and frees resources 54 | void UsbDeviceMonitor::cleanup() 55 | { 56 | } 57 | 58 | // Implements QAbstractNativeEventFilter interface for processing WM_DEVICECHANGE messages (Windows) 59 | bool UsbDeviceMonitor::nativeEventFilter(const QByteArray& eventType, void* message, long* result) 60 | { 61 | Q_UNUSED(eventType); 62 | Q_UNUSED(message); 63 | Q_UNUSED(result); 64 | return false; 65 | } 66 | 67 | bool UsbDeviceMonitor::startMonitoring() 68 | { 69 | IONotificationPortRef notificationPort = IONotificationPortCreate(kIOMasterPortDefault); 70 | CFRunLoopAddSource(CFRunLoopGetCurrent(), 71 | IONotificationPortGetRunLoopSource(notificationPort), 72 | kCFRunLoopDefaultMode); 73 | 74 | // If we monitor USB devices, notification comes too early, and the new disk device is not present yet. 75 | // So, instead we monitor for the new disks. 76 | CFMutableDictionaryRef matchingDict = IOServiceMatching("IOMediaBSDClient"); 77 | CFRetain(matchingDict); // Need to use it twice and IOServiceAddMatchingNotification() consumes a reference 78 | 79 | io_iterator_t portIterator = 0; 80 | // Register for notifications when a serial port is added to the system 81 | kern_return_t result = IOServiceAddMatchingNotification(notificationPort, 82 | kIOPublishNotification, 83 | matchingDict, 84 | UsbDeviceAddedCallback, 85 | this, 86 | &portIterator); 87 | while (IOIteratorNext(portIterator)) {}; // Run out the iterator or notifications won't start (you can also use it to iterate the available devices). 88 | 89 | // Also register for removal notifications 90 | IONotificationPortRef terminationNotificationPort = IONotificationPortCreate(kIOMasterPortDefault); 91 | CFRunLoopAddSource(CFRunLoopGetCurrent(), 92 | IONotificationPortGetRunLoopSource(terminationNotificationPort), 93 | kCFRunLoopDefaultMode); 94 | result = IOServiceAddMatchingNotification(terminationNotificationPort, 95 | kIOTerminatedNotification, 96 | matchingDict, 97 | UsbDeviceRemovedCallback, 98 | this, // refCon/contextInfo 99 | &portIterator); 100 | 101 | while (IOIteratorNext(portIterator)) {}; // Run out the iterator or notifications won't start (you can also use it to iterate the available devices). 102 | 103 | return true; 104 | } 105 | -------------------------------------------------------------------------------- /src/usbdevicemonitor_mac_p.h: -------------------------------------------------------------------------------- 1 | #ifndef USBDEVICEMONITOR_MAC_P_H 2 | #define USBDEVICEMONITOR_MAC_P_H 3 | 4 | // Class with platform-specific data 5 | class UsbDeviceMonitorPrivate 6 | { 7 | public: 8 | UsbDeviceMonitorPrivate(); 9 | ~UsbDeviceMonitorPrivate(); 10 | }; 11 | 12 | 13 | #endif // USBDEVICEMONITOR_MAC_P_H 14 | -------------------------------------------------------------------------------- /src/usbdevicemonitor_win.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Windows implementation of UsbDeviceMonitor 3 | 4 | #include 5 | #include 6 | #include "src/usbdevicemonitor.h" 7 | #include "src/usbdevicemonitor_win_p.h" 8 | 9 | UsbDeviceMonitorPrivate::UsbDeviceMonitorPrivate() 10 | { 11 | } 12 | 13 | UsbDeviceMonitorPrivate::~UsbDeviceMonitorPrivate() 14 | { 15 | } 16 | 17 | 18 | // Main class implementation 19 | 20 | UsbDeviceMonitor::UsbDeviceMonitor(QObject *parent) : 21 | QObject(parent), 22 | d_ptr(new UsbDeviceMonitorPrivate()) 23 | { 24 | } 25 | 26 | UsbDeviceMonitor::~UsbDeviceMonitor() 27 | { 28 | cleanup(); 29 | delete d_ptr; 30 | } 31 | 32 | // Closes handles and frees resources 33 | void UsbDeviceMonitor::cleanup() 34 | { 35 | } 36 | 37 | // Implements QAbstractNativeEventFilter interface for processing WM_DEVICECHANGE messages (Windows) 38 | bool UsbDeviceMonitor::nativeEventFilter(const QByteArray& eventType, void* message, long* result) 39 | { 40 | Q_UNUSED(eventType); 41 | 42 | MSG* msg = static_cast(message); 43 | 44 | if ((msg->message == WM_DEVICECHANGE) && 45 | ((msg->wParam == DBT_DEVICEARRIVAL) || 46 | (msg->wParam == DBT_DEVICEREMOVECOMPLETE) || 47 | msg->wParam == DBT_DEVNODES_CHANGED)) 48 | { 49 | // If the event was caused by adding or remiving a device, mark the WinAPI message as processed 50 | // and emit the notification signal 51 | *result = TRUE; 52 | emit deviceChanged(); 53 | return true; 54 | } 55 | 56 | return false; 57 | } 58 | 59 | bool UsbDeviceMonitor::startMonitoring() 60 | { 61 | // In Windows we use QAbstractNativeEventFilter interface implementation and process native Windows messages 62 | qApp->installNativeEventFilter(this); 63 | return true; 64 | } 65 | -------------------------------------------------------------------------------- /src/usbdevicemonitor_win_p.h: -------------------------------------------------------------------------------- 1 | #ifndef USBDEVICEMONITOR_WIN_P_H 2 | #define USBDEVICEMONITOR_WIN_P_H 3 | 4 | // Class with platform-specific data 5 | class UsbDeviceMonitorPrivate 6 | { 7 | public: 8 | UsbDeviceMonitorPrivate(); 9 | ~UsbDeviceMonitorPrivate(); 10 | }; 11 | 12 | 13 | #endif // USBDEVICEMONITOR_WIN_P_H 14 | -------------------------------------------------------------------------------- /tr.org.pardus.pkexec.pardus-imagewriter.policy: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | Pardus Developers 8 | http://www.pardus.org.tr/ 9 | 10 | 11 | Authentication is required to run Pardus Image Writer as root 12 | Pardus Disk Kalıbı Yazıcısını çalıştırmak için yetkilendirme gerekli 13 | pardus-imagewriter 14 | 15 | auth_admin 16 | auth_admin 17 | auth_admin 18 | 19 | /usr/bin/piw 20 | true 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /translations.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | translations/piw_tr.qm 4 | translations/piw_tr.ts 5 | 6 | 7 | -------------------------------------------------------------------------------- /translations/piw_tr.qm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunusem/pardus-imagewriter/55d9c13585266dc9d6afbdba6335fc48646bcbad/translations/piw_tr.qm -------------------------------------------------------------------------------- /translations/piw_tr.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Burn 6 | 7 | 8 | Click to return disk image selection 9 | Disk kalıbı seçim sayfası için tıklayın 10 | 11 | 12 | 13 | Click to return target selection 14 | Hedef seçim sayfası için tıklayın 15 | 16 | 17 | 18 | Start 19 | Başlat 20 | 21 | 22 | 23 | This operation will erase all 24 | the data in your target device. 25 | 26 | Are you sure to continue ? 27 | Bu işlem hedef cihazınızdaki 28 | bütün verileri silecektir. 29 | 30 | Devam etmek istiyor musunuz ? 31 | 32 | 33 | 34 | Writing process could not start. 35 | 36 | The disk image size is greater 37 | than the target device size. 38 | Yazma işlemi başlatılamadı. 39 | 40 | Disk kalıbı boyutu hedef 41 | cihazın boyutundan daha yüksek. 42 | 43 | 44 | 45 | Click to start burning 46 | Yakmayı başlatmak için tıklayın 47 | 48 | 49 | 50 | CANCELLING... 51 | İPTAL EDİLİYOR... 52 | 53 | 54 | 55 | Cancel 56 | İptal Et 57 | 58 | 59 | 60 | Are you sure to cancel ? 61 | İptal etmek istediğinize emin misiniz ? 62 | 63 | 64 | 65 | 66 | Pardus Image Writer 67 | Pardus Disk Kalıbı Yazıcı 68 | 69 | 70 | 71 | Writing process is Finished ! 72 | Yazma işlemi Tamamlandı ! 73 | 74 | 75 | 76 | You can now remove the device: 77 | Artık hedef cihazı çıkarabilirsiniz: 78 | 79 | 80 | 81 | File 82 | 83 | 84 | Choose the disk image file 85 | Disk kalıbı dosyasını seçiniz 86 | 87 | 88 | 89 | Click to choose disk image 90 | Disk kalıbı seçmek için tıklayın 91 | 92 | 93 | 94 | Please choose the disk image 95 | Lütfen disk kalıbını seçiniz 96 | 97 | 98 | 99 | Disk images 100 | Disk Kalıpları 101 | 102 | 103 | 104 | Target 105 | 106 | 107 | Choose the target 108 | Hedefi seçiniz 109 | 110 | 111 | Click to update USB device list 112 | USB cihazı listesini yenilemek için tıkla 113 | 114 | 115 | USB device list has been updated 116 | USB cihazı listesi güncellendi 117 | 118 | 119 | 120 | main 121 | 122 | 123 | 124 | Pardus Image Writer 125 | Pardus Disk Kalıbı Yazıcı 126 | 127 | 128 | 129 | Something is trying to kill me!!! 130 | Bir şey beni öldürmeye çalışıyor !!! 131 | 132 | 133 | 134 | NEXT 135 | İLERİ 136 | 137 | 138 | 139 | Click to move forward 140 | İlerlemek için tıklayın 141 | 142 | 143 | 144 | BACK 145 | GERİ 146 | 147 | 148 | 149 | Click to move backward 150 | Geri gitmek için tıklayın 151 | 152 | 153 | 154 | 155 | Are you sure to exit ? 156 | Çıkmak istediğinize emin misiniz ? 157 | 158 | 159 | 160 | Click to close the application 161 | Uygulamayı kapatmak için tıklayın 162 | 163 | 164 | 165 | Click to minimize 166 | Küçültmek için tıklayın 167 | 168 | 169 | 170 | About 171 | Hakkında 172 | 173 | 174 | 175 | Author 176 | Yazar 177 | 178 | 179 | 180 | Source Code 181 | Kaynak Kodu 182 | 183 | 184 | 185 | License 186 | Lisans 187 | 188 | 189 | 190 | Release 191 | Sürüm 192 | 193 | 194 | 195 | Ok 196 | Tamam 197 | 198 | 199 | 200 | Yes 201 | Evet 202 | 203 | 204 | 205 | No 206 | Hayır 207 | 208 | 209 | 210 | Writing process is ongoing. 211 | Terminating it is not recommended. 212 | 213 | Are you sure to exit ? 214 | Yazma işlemi devam ediyor. 215 | İşlemi durdurmak tavsiye edilmez. 216 | 217 | Çıkmak istediğinize emin misiniz ? 218 | 219 | 220 | 221 | -------------------------------------------------------------------------------- /ui/Burn.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.7 2 | import QtQuick.Controls 2.0 3 | import QtQuick.Layouts 1.1 4 | import QtQuick.Controls.Material 2.0 5 | 6 | Item { 7 | anchors.fill: parent 8 | 9 | signal progressValueChanged 10 | signal burningProcessFinished 11 | signal burningProcessCancelled 12 | signal startBurning 13 | signal warnUserCalled 14 | 15 | property real maxValue : 1 16 | property int previousIndex : 0 17 | 18 | Item { 19 | id: p1 20 | anchors.fill: parent 21 | opacity: isBurning ? 0.0 : 1.0 22 | 23 | Behavior on opacity { 24 | PropertyAnimation { 25 | duration: 800 26 | } 27 | } 28 | 29 | Column { 30 | anchors.centerIn: parent 31 | spacing: parent.height / 12 32 | 33 | Item { 34 | id: fileRow 35 | height: p1.height / 10 36 | width: p1.width * 5 / 6 37 | anchors.horizontalCenter: parent.horizontalCenter 38 | visible: fileName != "" 39 | 40 | MouseArea { 41 | id: fileRowMa 42 | enabled: !isBurning 43 | anchors.fill: parent 44 | hoverEnabled: true 45 | onClicked: { 46 | swipeView.currentIndex = 0 47 | } 48 | cursorShape: Qt.PointingHandCursor 49 | ToolTip.text: qsTr("Click to return disk image selection") 50 | ToolTip.delay: 1000 51 | ToolTip.visible: containsMouse 52 | ToolTip.timeout: 3000 53 | } 54 | 55 | 56 | Image { 57 | id: fileIcon 58 | height: parent.height 59 | width: height 60 | anchors.verticalCenter: parent.verticalCenter 61 | source: "../images/iso.svg" 62 | sourceSize { 63 | width: fileIcon.width 64 | height: fileIcon.height 65 | } 66 | smooth: true 67 | anchors { 68 | verticalCenter: parent.verticalCenter 69 | left: parent.left 70 | leftMargin: 5 71 | } 72 | } 73 | 74 | Label { 75 | id: fileLabel 76 | text: fileName 77 | color: fileRowMa.containsMouse ? "#ffcb08" : "#eeeeee" 78 | font.pointSize: parent.height / 4 > 0 ? parent.height / 4 : 10 79 | elide: Text.ElideMiddle 80 | verticalAlignment: Text.AlignVCenter 81 | horizontalAlignment: Text.AlignHCenter 82 | anchors { 83 | verticalCenter: parent.verticalCenter 84 | left: fileIcon.right 85 | right: parent.right 86 | rightMargin: 5 87 | } 88 | 89 | } 90 | 91 | } 92 | 93 | Item { 94 | id: targetRow 95 | height: p1.height / 10 96 | width: p1.width * 4 / 5 97 | anchors.horizontalCenter: parent.horizontalCenter 98 | visible: targetDevice != "" 99 | 100 | MouseArea { 101 | id: targetRowMa 102 | enabled: !isBurning 103 | anchors.fill: parent 104 | hoverEnabled: true 105 | onClicked: { 106 | swipeView.currentIndex = 1 107 | } 108 | cursorShape: Qt.PointingHandCursor 109 | ToolTip.text: qsTr("Click to return target selection") 110 | ToolTip.delay: 1000 111 | ToolTip.visible: containsMouse 112 | ToolTip.timeout: 3000 113 | } 114 | 115 | Image { 116 | id: targetIcon 117 | height: parent.height 118 | width: height 119 | source: "../images/usb.svg" 120 | sourceSize { 121 | width: targetIcon.width 122 | height: targetIcon.height 123 | } 124 | smooth: false 125 | anchors { 126 | verticalCenter: parent.verticalCenter 127 | left: parent.left 128 | leftMargin: 5 129 | } 130 | } 131 | 132 | Label { 133 | id: targetLabel 134 | text: targetDevice 135 | color: targetRowMa.containsMouse ? "#ffcb08" : "#eeeeee" 136 | font.pointSize: parent.height / 4 > 0 ? parent.height / 4 : 10 137 | wrapMode: Text.WordWrap 138 | verticalAlignment: Text.AlignVCenter 139 | horizontalAlignment: Text.AlignHCenter 140 | anchors { 141 | verticalCenter: parent.verticalCenter 142 | left: targetIcon.right 143 | right: parent.right 144 | margins: 5 145 | } 146 | } 147 | 148 | } 149 | 150 | Button { 151 | id: btnBurn 152 | highlighted: true 153 | Material.accent: "#2c2c2c" 154 | enabled: fileName != "" && targetDevice != "" 155 | anchors.horizontalCenter: parent.horizontalCenter 156 | text: qsTr("Start") 157 | visible: true 158 | onClicked: { 159 | if (helper.getImageSize() <= helper.getSelectedDeviceSize(target.comboBoxIndex)) { 160 | requestForBurn = true 161 | dialog.topic = qsTr("This operation will erase all\nthe data in your target device.\n\nAre you sure to continue ?") 162 | dialog.open() 163 | } else { 164 | dialog.topic = qsTr("Writing process could not start.\n\nThe disk image size is greater\nthan the target device size.") 165 | dialog.showButtons = false 166 | dialog.open() 167 | } 168 | } 169 | hoverEnabled: true 170 | ToolTip.text: qsTr("Click to start burning") 171 | ToolTip.delay: 1500 172 | ToolTip.visible: hovered 173 | ToolTip.timeout: 3000 174 | } 175 | } 176 | } 177 | 178 | Item { 179 | id: p2 180 | 181 | anchors.fill: parent 182 | opacity: p1.opacity == 0.0 ? 1.0 : 0.0 183 | 184 | Behavior on opacity { 185 | PropertyAnimation { 186 | duration: 800 187 | } 188 | } 189 | 190 | Column { 191 | anchors.centerIn: parent 192 | spacing: parent.width / 10 193 | ProgressBarCircle { 194 | id: pb 195 | anchors { 196 | horizontalCenter: parent.horizontalCenter 197 | } 198 | 199 | width: p2.width * 2 / 3 200 | height: p2.width * 2 / 3 201 | colorCircle: "#ffcb08" 202 | colorBackground: "#111111" 203 | thickness: 10 204 | } 205 | 206 | Button { 207 | id: btnCancel 208 | highlighted: true 209 | Material.accent: Material.Red 210 | Material.theme: Material.Light 211 | anchors { 212 | horizontalCenter: parent.horizontalCenter 213 | } 214 | text: requestForCancel ? qsTr("CANCELLING...") : qsTr("Cancel") 215 | visible: isBurning 216 | enabled: !requestForCancel 217 | onClicked: { 218 | requestForCancel = true 219 | dialog.topic = qsTr("Are you sure to cancel ?") 220 | dialog.open() 221 | } 222 | } 223 | } 224 | } 225 | 226 | onStartBurning: { 227 | previousIndex = target.comboBoxIndex 228 | appMain.title = "%"+ pb.value * 100 229 | helper.writeToDevice(target.comboBoxIndex) 230 | pb.value = 0 231 | pb.maximumValue = helper.maximumProgressValue() 232 | isBurning = true 233 | } 234 | 235 | onProgressValueChanged: { 236 | pb.value = helper.progress 237 | var currentVal = helper.progress * 0.01 238 | var maxVal = pb.maximumValue 239 | currentVal = currentVal * 100 / maxVal 240 | appMain.title = "%"+ (currentVal * 100).toFixed(2) 241 | } 242 | onBurningProcessFinished: { 243 | appMain.title = qsTr("Pardus Image Writer") 244 | target.comboBoxIndex = previousIndex 245 | dialog.topic = qsTr("Writing process is Finished !") 246 | dialog.showButtons = false 247 | helper.notifySystem(dialog.topic, qsTr("You can now remove the device:") 248 | + " " + targetDevice.split("(")[0]+"" ) 249 | dialog.open() 250 | } 251 | onBurningProcessCancelled: { 252 | requestForCancel = false 253 | appMain.title = qsTr("Pardus Image Writer") 254 | target.comboBoxIndex = previousIndex 255 | } 256 | 257 | onWarnUserCalled: { 258 | dialog.topic = helper.messageFromBackend 259 | dialog.showButtons = false 260 | dialog.open() 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /ui/File.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.7 2 | import QtQuick.Controls 2.0 3 | import QtQuick.Layouts 1.1 4 | import QtQuick.Dialogs 1.2 5 | 6 | Item { 7 | anchors.fill: parent 8 | 9 | ColumnLayout { 10 | anchors.centerIn: parent 11 | spacing: parent.height / 15 12 | 13 | Label { 14 | anchors.horizontalCenter: parent.horizontalCenter 15 | text: qsTr("Choose the disk image file") 16 | } 17 | 18 | Button { 19 | id: btn 20 | enabled: !isBurning 21 | anchors.horizontalCenter: parent.horizontalCenter 22 | width: btnLabel.text == "+" ? parent.width / 8 : parent.width * 3 / 2 23 | scale: 0.8 24 | Label { 25 | id: btnLabel 26 | anchors.centerIn: parent 27 | text: "+" 28 | width: parent.width - 6 29 | elide: Text.ElideMiddle 30 | verticalAlignment: Text.AlignVCenter 31 | horizontalAlignment: Text.AlignHCenter 32 | font.pointSize: parent.height / 4 > 0 ? parent.height / 4 : 10 33 | } 34 | 35 | onClicked: { 36 | fd.open() 37 | } 38 | hoverEnabled: true 39 | ToolTip.text: qsTr("Click to choose disk image") 40 | ToolTip.delay: 1000 41 | ToolTip.visible: hovered 42 | ToolTip.timeout: 3000 43 | } 44 | 45 | Image { 46 | id: diskImageIcon 47 | smooth: true 48 | mipmap: true 49 | scale: 1 50 | anchors.horizontalCenter: parent.horizontalCenter 51 | source: "../images/iso.svg" 52 | Behavior on scale { 53 | PropertyAnimation { 54 | easing.overshoot: 2 55 | easing.type: Easing.OutBack 56 | duration: 200 57 | } 58 | } 59 | } 60 | } 61 | 62 | DropArea { 63 | anchors.fill: parent 64 | onEntered: { 65 | 66 | diskImageIcon.scale = 1.2 67 | } 68 | 69 | onExited: { 70 | diskImageIcon.scale = 1.0 71 | } 72 | 73 | onDropped: { 74 | diskImageIcon.scale = 1.0 75 | if(drop.hasUrls && drop.urls.toString() !== "") { 76 | var str = drop.urls.toString(); 77 | if(evaluateExtension(str)) { 78 | processFile(str) 79 | } 80 | } else { 81 | drop.accept(Qt.IgnoreAction) 82 | } 83 | } 84 | } 85 | 86 | FileDialog { 87 | id: fd 88 | title: qsTr("Please choose the disk image") 89 | folder: helper.downloadsFolderPath() 90 | nameFilters: [qsTr("Disk images") + " (*.iso *.bin *.img *.ISO *.BIN *.IMG)"] 91 | onAccepted: { 92 | var path = fd.fileUrl.toString(); 93 | processFile(path) 94 | } 95 | onRejected: { 96 | btnLabel.text = fileName != "" ? fileName : "+" 97 | } 98 | visible: false 99 | Component.onCompleted: { 100 | fd.close() 101 | } 102 | } 103 | 104 | function evaluateExtension(filePath) { 105 | var str = filePath.toString() 106 | str = str.trim() 107 | var ext = str.slice(str.length -3, str.length) 108 | ext = ext.toLowerCase() 109 | var extArray = ["iso","bin","img"] 110 | var ret = extArray.indexOf(ext) === -1 ? false : true 111 | return ret 112 | } 113 | 114 | function processFile(fp) { 115 | if (helper.preProcessImageFile(fp)) { 116 | fileName = helper.fileNameFromPath(fp) 117 | btnLabel.text = fileName 118 | } 119 | } 120 | 121 | Component.onCompleted: { 122 | var fp = helper.filePathFromArguments() 123 | if( fp !== "" && helper.preProcessImageFile(fp)) { 124 | filePath = helper.filePathFromArguments() 125 | fileName = helper.fileNameFromPath(filePath) 126 | btnLabel.text = fileName 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /ui/ProgressBarCircle.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtQml 2.2 3 | 4 | Item { 5 | id: progressbarcircle 6 | 7 | width: 200 8 | height: 200 9 | 10 | property real value : 0 11 | property real maximumValue : 360 12 | property real arcEnd: value * 360 / maximumValue 13 | property real thickness: 20 14 | property string colorCircle: "#ffcb08" 15 | property string colorBackground: "#2c2c2c" 16 | property alias endAnimation: animationArcEnd.enabled 17 | property int animationDuration: 10 18 | 19 | onArcEndChanged: canvas.requestPaint() 20 | 21 | Behavior on arcEnd { 22 | id: animationArcEnd 23 | enabled: true 24 | NumberAnimation { 25 | duration: progressbarcircle.animationDuration 26 | easing.type: Easing.OutExpo 27 | } 28 | } 29 | 30 | Text { 31 | anchors.centerIn: parent 32 | text: value >= maximumValue ? maximumValue.toFixed(0) + " MB" : value.toFixed(0) + " MB" 33 | color: colorCircle 34 | } 35 | 36 | Canvas { 37 | id: canvas 38 | anchors.fill: parent 39 | rotation: -270 40 | 41 | onPaint: { 42 | var ctx = getContext("2d") 43 | var x = width / 2 44 | var y = height / 2 45 | var start = 0 46 | var end = Math.PI * (parent.arcEnd / 180) 47 | ctx.reset() 48 | ctx.beginPath(); 49 | ctx.arc(x, y, (width / 2) - parent.thickness / 2, 0, Math.PI * 2, false) 50 | ctx.lineWidth = parent.thickness 51 | ctx.strokeStyle = parent.colorBackground 52 | ctx.stroke() 53 | ctx.beginPath(); 54 | ctx.arc(x, y, (width / 2) - parent.thickness / 2, start, end, false) 55 | ctx.lineWidth = parent.thickness 56 | ctx.strokeStyle = parent.colorCircle 57 | ctx.stroke() 58 | 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /ui/Target.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.7 2 | import QtQuick.Controls 2.0 3 | import QtQuick.Layouts 1.1 4 | 5 | Item { 6 | anchors.fill: parent 7 | signal scheduleStarted 8 | signal dlChanged 9 | property alias targetDeviceName : cb.currentText 10 | property alias comboBoxIndex : cb.currentIndex 11 | 12 | ColumnLayout { 13 | id: clayout 14 | anchors.centerIn: parent 15 | spacing: parent.height / 15 16 | Layout.minimumWidth: appMain.width 17 | Layout.maximumWidth: appMain.width 18 | 19 | Label { 20 | id: targetTitleLabel 21 | anchors.horizontalCenter: parent.horizontalCenter 22 | text: qsTr("Choose the target") 23 | } 24 | 25 | ComboBox { 26 | id: cb 27 | enabled: !isBurning 28 | //Layout.fillWidth: true 29 | Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter 30 | scale: 0.7 31 | spacing: 6 32 | background: themeBtn.background 33 | anchors.horizontalCenter: parent.horizontalCenter 34 | model: cbItems 35 | currentIndex: 0 36 | onActivated: { 37 | targetDevice = targetDeviceName 38 | } 39 | popup: Popup { 40 | id: cbp 41 | scale: 0.6 42 | y: cb.height - 1 43 | x: cb.x //- (cb.width - cbp.width) / 2 44 | width: cb.width * 7 / 10 45 | implicitHeight: contentItem.implicitHeight 46 | padding: 1 47 | 48 | contentItem: ListView { 49 | clip: true 50 | implicitHeight: contentHeight 51 | model: cb.popup.visible ? cb.delegateModel : null 52 | currentIndex: cb.highlightedIndex 53 | ScrollIndicator.vertical: ScrollIndicator { } 54 | } 55 | 56 | background: Rectangle { 57 | color: "#2c2c2c" 58 | } 59 | } 60 | 61 | } 62 | 63 | Image { 64 | id: deviceIcon 65 | smooth: true 66 | mipmap: true 67 | scale: 1.0 68 | anchors.horizontalCenter: parent.horizontalCenter 69 | source: "../images/usb.svg" 70 | } 71 | 72 | } 73 | 74 | BusyIndicator { 75 | id:bi 76 | height: parent.height * 10 / 85 77 | width: height 78 | anchors { 79 | horizontalCenter: parent.horizontalCenter 80 | top: clayout.top 81 | topMargin: clayout.spacing + targetTitleLabel.height + cb.height + 12 82 | //bottomMargin: parent.height / 15 83 | } 84 | onRunningChanged: { 85 | if (bi.running) { 86 | if(!isBurning) { 87 | swipeView.currentIndex = 1 88 | cb.enabled = false 89 | } 90 | 91 | targetDevice = "" 92 | } else { 93 | if(!isBurning) { 94 | cb.enabled = true 95 | } 96 | } 97 | } 98 | } 99 | 100 | onScheduleStarted: { 101 | bi.running = true 102 | } 103 | 104 | onDlChanged: { 105 | bi.running = false 106 | cb.currentIndex = 0 107 | } 108 | 109 | Button { 110 | id: themeBtn 111 | visible: false 112 | } 113 | 114 | Component.onCompleted: { 115 | bi.running = false 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /ui/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.7 2 | import QtQuick.Controls 2.0 3 | import QtQuick.Layouts 1.0 4 | import QtQuick.Controls.Material 2.0 5 | import piw.helper 1.0 6 | 7 | ApplicationWindow { 8 | id: appMain 9 | visible: true 10 | maximumWidth: 300 11 | maximumHeight: 400 12 | width: 300 13 | height: 400 14 | flags: Qt.FramelessWindowHint | Qt.Window 15 | title: qsTr("Pardus Image Writer") 16 | 17 | property bool requestForBurn : false 18 | property bool requestForQuit : false 19 | property bool requestForCancel : false 20 | 21 | property bool isBurning : false 22 | property string filePath : "" 23 | property string fileName : "" 24 | property string targetDevice: "" 25 | 26 | signal dialogAccepted 27 | signal dialogRejected 28 | 29 | Helper { 30 | id: helper 31 | 32 | onTerminateCalled: { 33 | dialog.showButtons = false 34 | dialog.topic = qsTr("Something is trying to kill me!!!") 35 | dialog.open() 36 | } 37 | 38 | onScheduleStarted: { 39 | target.scheduleStarted() 40 | } 41 | 42 | onDeviceListChanged: { 43 | cbItems.clear(); 44 | for (var i = 0; i < helper.devices.length; i++) { 45 | cbItems.append({"text": helper.devices[i].split(" - ")[1]}); 46 | } 47 | target.dlChanged() 48 | targetDevice = target.targetDeviceName 49 | } 50 | 51 | onProgressChanged: { 52 | burn.progressValueChanged() 53 | } 54 | 55 | onBurningFinished: { 56 | isBurning = false 57 | burn.burningProcessFinished() 58 | } 59 | 60 | onBurningCancelled: { 61 | isBurning = false 62 | burn.burningProcessCancelled() 63 | } 64 | onWarnUser: { 65 | burn.warnUserCalled() 66 | } 67 | } 68 | 69 | ListModel { 70 | id:cbItems 71 | } 72 | 73 | SwipeView { 74 | id: swipeView 75 | 76 | anchors.fill: parent 77 | currentIndex: 0 78 | Page { 79 | width: appMain.width 80 | height: appMain.height 81 | File{ 82 | id: file 83 | } 84 | } 85 | 86 | Page { 87 | width: appMain.width 88 | height: appMain.height 89 | Target{ 90 | id: target 91 | } 92 | } 93 | 94 | Page { 95 | width: appMain.width 96 | height: appMain.height 97 | Burn { 98 | id: burn 99 | } 100 | } 101 | 102 | onCurrentIndexChanged: { 103 | if(isBurning) { 104 | currentIndex = 2 105 | } 106 | } 107 | } 108 | 109 | PageIndicator { 110 | id: indicator 111 | interactive: true 112 | count: swipeView.count 113 | currentIndex: swipeView.currentIndex 114 | anchors { 115 | bottom: swipeView.bottom 116 | bottomMargin: 3 117 | horizontalCenter: parent.horizontalCenter 118 | } 119 | onCurrentIndexChanged: { 120 | swipeView.currentIndex = indicator.currentIndex 121 | } 122 | 123 | } 124 | 125 | Button { 126 | id: nextBtn 127 | Material.background: "#2c2c2c" 128 | width: parent.width / 6 129 | height: width * 3 / 4 130 | visible: swipeView.currentIndex == 2 ? false : true 131 | anchors { 132 | bottom: parent.bottom 133 | bottomMargin: -5 134 | right: parent.right 135 | rightMargin: 1 136 | } 137 | text: qsTr("NEXT") 138 | font.pointSize: height / 5 > 0 ? height / 5 : 9 139 | onClicked: { 140 | swipeView.currentIndex = swipeView.currentIndex + 1 141 | } 142 | hoverEnabled: true 143 | ToolTip.text: qsTr("Click to move forward") 144 | ToolTip.delay: 1000 145 | ToolTip.visible: hovered 146 | ToolTip.timeout: 3000 147 | } 148 | 149 | Button { 150 | id: backBtn 151 | Material.background: "#2c2c2c" 152 | width: parent.width / 6 153 | height: width * 3 / 4 154 | enabled: ! isBurning 155 | visible: swipeView.currentIndex == 0 ? false : true 156 | anchors { 157 | bottom: parent.bottom 158 | bottomMargin: -5 159 | left: parent.left 160 | rightMargin: 1 161 | } 162 | text: qsTr("BACK") 163 | font.pointSize: height / 5 > 0 ? height / 5 : 9 164 | onClicked: { 165 | swipeView.currentIndex = swipeView.currentIndex - 1 166 | } 167 | hoverEnabled: true 168 | ToolTip.text: qsTr("Click to move backward") 169 | ToolTip.delay: 1000 170 | ToolTip.visible: hovered 171 | ToolTip.timeout: 3000 172 | } 173 | 174 | Button { 175 | id: closeBtn 176 | Material.background: "#2c2c2c" 177 | width: parent.width / 12 178 | height: width + 12 179 | enabled: ! isBurning 180 | visible: true 181 | anchors { 182 | top: parent.top 183 | topMargin: -5 184 | right: parent.right 185 | rightMargin: 1 186 | } 187 | 188 | Image { 189 | anchors.centerIn: parent 190 | source: "../images/close.svg" 191 | sourceSize{ 192 | height: parent.height - 8 193 | width: parent.width - 8 194 | } 195 | smooth: true 196 | } 197 | onClicked: { 198 | requestForQuit = true 199 | dialog.topic = qsTr("Are you sure to exit ?") 200 | dialog.open() 201 | } 202 | hoverEnabled: true 203 | ToolTip.text: qsTr("Click to close the application") 204 | ToolTip.delay: 1000 205 | ToolTip.visible: hovered 206 | ToolTip.timeout: 3000 207 | } 208 | 209 | Button { 210 | id: minimizeBtn 211 | Material.background: "#2c2c2c" 212 | width: parent.width / 12 213 | height: width + 12 214 | enabled: true 215 | visible: true 216 | anchors { 217 | top: parent.top 218 | topMargin: -5 219 | right: closeBtn.left 220 | rightMargin: 1 221 | } 222 | Image { 223 | anchors.centerIn: parent 224 | source: "../images/minimize.svg" 225 | sourceSize{ 226 | height: parent.height - 8 227 | width: parent.width - 8 228 | } 229 | smooth: true 230 | } 231 | onClicked: { 232 | appMain.showMinimized() 233 | } 234 | hoverEnabled: true 235 | ToolTip.text: qsTr("Click to minimize") 236 | ToolTip.delay: 1000 237 | ToolTip.visible: hovered 238 | ToolTip.timeout: 3000 239 | } 240 | 241 | Button { 242 | id: aboutBtn 243 | Material.background: "#2c2c2c" 244 | enabled: true 245 | visible: true 246 | width: parent.width / 12 247 | height: width + 12 248 | hoverEnabled: true 249 | anchors { 250 | top: parent.top 251 | topMargin: -5 252 | left: parent.left 253 | leftMargin: 1 254 | } 255 | Image { 256 | anchors.centerIn: parent 257 | source: "../images/info.svg" 258 | sourceSize{ 259 | height: parent.height - 6 260 | width: parent.width - 6 261 | } 262 | smooth: true 263 | } 264 | 265 | ToolTip.text: qsTr("About") 266 | ToolTip.delay: 1000 267 | ToolTip.visible: hovered 268 | ToolTip.timeout: 3000 269 | onClicked: { 270 | aboutDialog.open() 271 | } 272 | } 273 | 274 | Popup { 275 | id:aboutDialog 276 | width: appMain.width 277 | height: appMain.height * 9 / 10 278 | modal: true 279 | closePolicy: Popup.CloseOnPressOutside 280 | y: appMain.height / 10 281 | 282 | ColumnLayout { 283 | anchors { 284 | top: parent.top 285 | horizontalCenter: parent.horizontalCenter 286 | } 287 | spacing: parent.height / 30 288 | 289 | Label { 290 | anchors.horizontalCenter: parent.horizontalCenter 291 | verticalAlignment: Text.AlignVCenter 292 | text: ""+ qsTr("Pardus Image Writer") + "" 293 | } 294 | 295 | Image { 296 | anchors.horizontalCenter: parent.horizontalCenter 297 | mipmap: true 298 | source: "../images/pardus.svg" 299 | } 300 | 301 | Label { 302 | anchors.horizontalCenter: parent.horizontalCenter 303 | verticalAlignment: Text.AlignVCenter 304 | horizontalAlignment: Text.horizontalAlignment 305 | text: qsTr("Author") + " : " + "Yunusemre Şentürk" 306 | } 307 | 308 | Label { 309 | anchors.horizontalCenter: parent.horizontalCenter 310 | verticalAlignment: Text.AlignVCenter 311 | text: qsTr("Source Code") + " : " +"GitHub" 312 | onLinkActivated: Qt.openUrlExternally(link) 313 | 314 | MouseArea { 315 | anchors.fill: parent 316 | acceptedButtons: Qt.NoButton // we don't want to eat clicks on the Text 317 | cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor 318 | } 319 | } 320 | 321 | Label { 322 | anchors.horizontalCenter: parent.horizontalCenter 323 | verticalAlignment: Text.AlignVCenter 324 | text: qsTr("License") + " : " +"GPL v3" 325 | onLinkActivated: Qt.openUrlExternally(link) 326 | 327 | MouseArea { 328 | anchors.fill: parent 329 | acceptedButtons: Qt.NoButton // we don't want to eat clicks on the Text 330 | cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor 331 | } 332 | } 333 | 334 | Label { 335 | anchors.horizontalCenter: parent.horizontalCenter 336 | verticalAlignment: Text.AlignVCenter 337 | horizontalAlignment: Text.horizontalAlignment 338 | text: qsTr("Release") + " : " + "0.1.9" 339 | } 340 | 341 | } 342 | Button { 343 | id: okBtn 344 | highlighted: true 345 | Material.accent: "#2c2c2c" 346 | anchors { 347 | bottom: parent.bottom 348 | horizontalCenter: parent.horizontalCenter 349 | } 350 | visible: true 351 | enabled: true 352 | text: qsTr("Ok") 353 | onClicked: { 354 | aboutDialog.close() 355 | } 356 | } 357 | 358 | 359 | } 360 | 361 | Rectangle { 362 | id: dock 363 | color: "transparent" 364 | height: appMain.height / 10 365 | anchors { 366 | top: parent.top 367 | left: aboutBtn.right 368 | right: minimizeBtn.left 369 | } 370 | MouseArea { 371 | anchors.fill: parent 372 | property int cposx: 1 373 | property int cposy: 1 374 | onPressed: { 375 | cursorShape = Qt.SizeAllCursor 376 | var cpos = Qt.point(mouse.x,mouse.y); 377 | cposx = cpos.x 378 | cposy = cpos.y 379 | 380 | } 381 | onPositionChanged: { 382 | cursorShape = Qt.SizeAllCursor 383 | var delta = Qt.point(mouse.x - cposx, mouse.y - cposy); 384 | appMain.x += delta.x; 385 | appMain.y += delta.y; 386 | 387 | } 388 | onReleased: { 389 | cursorShape = Qt.ArrowCursor 390 | } 391 | 392 | 393 | } 394 | } 395 | 396 | Popup { 397 | id: dialog 398 | width: appMain.width 399 | height: dialog.width / 2 400 | modal: true 401 | closePolicy: Popup.CloseOnPressOutside 402 | y: appMain.height / 2 - dialog.height / 2 403 | 404 | property string topic : "" 405 | property bool showButtons : true 406 | signal accepted 407 | signal rejected 408 | 409 | Label { 410 | anchors { 411 | top: parent.top 412 | topMargin: parent.height / 10 413 | horizontalCenter: parent.horizontalCenter 414 | } 415 | verticalAlignment: Text.AlignVCenter 416 | horizontalAlignment: Text.AlignHCenter 417 | text: dialog.topic 418 | } 419 | 420 | Row { 421 | id: btnRow 422 | anchors { 423 | bottom: parent.bottom 424 | horizontalCenter: parent.horizontalCenter 425 | } 426 | spacing: parent.width / 10 427 | Button { 428 | id: yesBtn 429 | highlighted: true 430 | Material.accent: "#2c2c2c" 431 | visible: dialog.showButtons 432 | text: qsTr("Yes") 433 | onClicked: { 434 | dialog.accepted() 435 | } 436 | } 437 | 438 | Button { 439 | id: noBtn 440 | highlighted: true 441 | Material.accent: "#2c2c2c" 442 | visible: dialog.showButtons 443 | text: qsTr("No") 444 | onClicked: { 445 | dialog.rejected() 446 | } 447 | } 448 | } 449 | 450 | onAccepted: { 451 | appMain.dialogAccepted() 452 | dialog.topic = "" 453 | close() 454 | } 455 | onRejected: { 456 | appMain.dialogRejected() 457 | dialog.topic = "" 458 | close() 459 | } 460 | onClosed: { 461 | dialog.showButtons = true 462 | dialog.topic = "" 463 | } 464 | 465 | Timer { 466 | interval: 3000 467 | running: !dialog.showButtons 468 | onTriggered: { 469 | console.log(dialog.topic) 470 | dialog.close() 471 | } 472 | } 473 | 474 | } 475 | 476 | onDialogAccepted: { 477 | if (requestForQuit) { 478 | Qt.quit() 479 | } 480 | if (requestForBurn) { 481 | burn.startBurning() 482 | requestForBurn = false 483 | } 484 | if (requestForCancel) { 485 | helper.cancelWriting() 486 | } 487 | 488 | } 489 | onDialogRejected: { 490 | requestForQuit = false 491 | requestForBurn = false 492 | requestForCancel = false 493 | } 494 | 495 | Component.onCompleted: { 496 | for (var i = 0; i < helper.devices.length; i++) { 497 | cbItems.append({"text": helper.devices[i].split(" - ")[1]}); 498 | } 499 | targetDevice = target.targetDeviceName 500 | } 501 | onClosing: { 502 | close.accepted = false 503 | raise() 504 | showNormal() 505 | if (isBurning) { 506 | dialog.topic = qsTr("Writing process is ongoing.\nTerminating it is not recommended.\n\nAre you sure to exit ?") 507 | } else { 508 | dialog.topic = qsTr("Are you sure to exit ?") 509 | } 510 | requestForQuit = true 511 | dialog.open() 512 | } 513 | } 514 | --------------------------------------------------------------------------------