From b80aabb11363e0aa0352717ed5810aa607dd7eca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E7=82=9C?= Date: Thu, 2 Jun 2022 16:31:24 +0800 Subject: [PATCH] Import Upstream version 3.1.1.34update1 --- BiometricAuth/BiometricAuth.pri | 9 + BiometricAuth/BiometricAuth.pro | 37 + BiometricAuth/CMakeLists.txt | 28 + BiometricAuth/biometricauthwidget.cpp | 344 ++ BiometricAuth/biometricauthwidget.h | 94 + BiometricAuth/biometricdeviceinfo.cpp | 248 ++ BiometricAuth/biometricdeviceinfo.h | 272 ++ BiometricAuth/biometricdeviceswidget.cpp | 280 ++ BiometricAuth/biometricdeviceswidget.h | 79 + BiometricAuth/biometricproxy.cpp | 212 ++ BiometricAuth/biometricproxy.h | 139 + BiometricAuth/giodbus.cpp | 54 + BiometricAuth/giodbus.h | 6 + BiometricAuth/main.cpp | 45 + CMakeLists.txt | 67 + Common/CMakeLists.txt | 22 + Common/autoresize.cpp | 32 + Common/autoresize.h | 41 + Common/checkbutton.cpp | 186 ++ Common/checkbutton.h | 73 + Common/commonfunc.cpp | 77 + Common/commonfunc.h | 39 + KylinNM/CMakeLists.txt | 95 + KylinNM/README.md | 15 + KylinNM/hot-spot/dlghotspotcreate.cpp | 155 + KylinNM/hot-spot/dlghotspotcreate.h | 71 + KylinNM/hot-spot/dlghotspotcreate.ui | 169 + KylinNM/kylin-nm.pri | 70 + KylinNM/kylin-nm.pro | 129 + KylinNM/nmqrc.qrc | 102 + KylinNM/qss/style.qss | 10 + KylinNM/res.qrc | 1 + KylinNM/res/g/checkbox-checked.svg | 6 + KylinNM/res/g/checkbox-unchecked.svg | 3 + KylinNM/res/g/close_black.png | Bin 0 -> 205 bytes KylinNM/res/g/close_white.png | Bin 0 -> 152 bytes KylinNM/res/g/down_arrow.png | Bin 0 -> 113 bytes KylinNM/res/h/hide-pwd.png | Bin 0 -> 1452 bytes KylinNM/res/h/no-pwd-wifi.png | Bin 0 -> 1643 bytes KylinNM/res/h/right-pwd.png | Bin 0 -> 1398 bytes KylinNM/res/h/show-pwd.png | Bin 0 -> 1289 bytes KylinNM/res/l/network-offline.png | Bin 0 -> 506 bytes KylinNM/res/l/network-offline.svg | 23 + KylinNM/res/l/network-online.png | Bin 0 -> 406 bytes KylinNM/res/l/network-online.svg | 1 + KylinNM/res/l/pb-network-info.png | Bin 0 -> 1129 bytes KylinNM/res/l/pb-network-offline.png | Bin 0 -> 1714 bytes KylinNM/res/l/pb-network-online.png | Bin 0 -> 1807 bytes KylinNM/res/l/pb-top-network-offline.png | Bin 0 -> 1241 bytes KylinNM/res/s/conning-a/1.png | Bin 0 -> 351 bytes KylinNM/res/s/conning-a/2.png | Bin 0 -> 3170 bytes KylinNM/res/s/conning-a/3.png | Bin 0 -> 349 bytes KylinNM/res/s/conning-a/4.png | Bin 0 -> 3180 bytes KylinNM/res/s/conning-a/5.png | Bin 0 -> 355 bytes KylinNM/res/s/conning-a/6.png | Bin 0 -> 3173 bytes KylinNM/res/s/conning-a/7.png | Bin 0 -> 359 bytes KylinNM/res/s/conning-a/8.png | Bin 0 -> 3201 bytes KylinNM/res/s/conning-b/1.png | Bin 0 -> 2800 bytes KylinNM/res/s/conning-b/10.png | Bin 0 -> 3483 bytes KylinNM/res/s/conning-b/11.png | Bin 0 -> 3459 bytes KylinNM/res/s/conning-b/12.png | Bin 0 -> 3465 bytes KylinNM/res/s/conning-b/2.png | Bin 0 -> 3459 bytes KylinNM/res/s/conning-b/3.png | Bin 0 -> 3471 bytes KylinNM/res/s/conning-b/4.png | Bin 0 -> 3511 bytes KylinNM/res/s/conning-b/5.png | Bin 0 -> 3519 bytes KylinNM/res/s/conning-b/6.png | Bin 0 -> 3496 bytes KylinNM/res/s/conning-b/7.png | Bin 0 -> 2942 bytes KylinNM/res/s/conning-b/8.png | Bin 0 -> 3478 bytes KylinNM/res/s/conning-b/9.png | Bin 0 -> 3484 bytes KylinNM/res/s/conning-s/1.png | Bin 0 -> 652 bytes KylinNM/res/s/conning-s/10.png | Bin 0 -> 627 bytes KylinNM/res/s/conning-s/11.png | Bin 0 -> 646 bytes KylinNM/res/s/conning-s/12.png | Bin 0 -> 623 bytes KylinNM/res/s/conning-s/1x/1.png | Bin 0 -> 354 bytes KylinNM/res/s/conning-s/1x/10.png | Bin 0 -> 355 bytes KylinNM/res/s/conning-s/1x/11.png | Bin 0 -> 345 bytes KylinNM/res/s/conning-s/1x/12.png | Bin 0 -> 364 bytes KylinNM/res/s/conning-s/1x/2.png | Bin 0 -> 376 bytes KylinNM/res/s/conning-s/1x/3.png | Bin 0 -> 367 bytes KylinNM/res/s/conning-s/1x/4.png | Bin 0 -> 405 bytes KylinNM/res/s/conning-s/1x/5.png | Bin 0 -> 411 bytes KylinNM/res/s/conning-s/1x/6.png | Bin 0 -> 406 bytes KylinNM/res/s/conning-s/1x/7.png | Bin 0 -> 381 bytes KylinNM/res/s/conning-s/1x/8.png | Bin 0 -> 362 bytes KylinNM/res/s/conning-s/1x/9.png | Bin 0 -> 354 bytes KylinNM/res/s/conning-s/2.png | Bin 0 -> 709 bytes KylinNM/res/s/conning-s/3.png | Bin 0 -> 686 bytes KylinNM/res/s/conning-s/4.png | Bin 0 -> 769 bytes KylinNM/res/s/conning-s/5.png | Bin 0 -> 787 bytes KylinNM/res/s/conning-s/6.png | Bin 0 -> 673 bytes KylinNM/res/s/conning-s/7.png | Bin 0 -> 736 bytes KylinNM/res/s/conning-s/8.png | Bin 0 -> 664 bytes KylinNM/res/s/conning-s/9.png | Bin 0 -> 613 bytes KylinNM/res/s/rescan/1.png | Bin 0 -> 15281 bytes KylinNM/res/s/rescan/10.png | Bin 0 -> 15390 bytes KylinNM/res/s/rescan/11.png | Bin 0 -> 15383 bytes KylinNM/res/s/rescan/12.png | Bin 0 -> 15316 bytes KylinNM/res/s/rescan/2.png | Bin 0 -> 15386 bytes KylinNM/res/s/rescan/3.png | Bin 0 -> 15391 bytes KylinNM/res/s/rescan/4.png | Bin 0 -> 15397 bytes KylinNM/res/s/rescan/5.png | Bin 0 -> 15381 bytes KylinNM/res/s/rescan/6.png | Bin 0 -> 15393 bytes KylinNM/res/s/rescan/7.png | Bin 0 -> 15271 bytes KylinNM/res/s/rescan/8.png | Bin 0 -> 15309 bytes KylinNM/res/s/rescan/9.png | Bin 0 -> 15391 bytes KylinNM/res/w/pb-all-wifi-offline.png | Bin 0 -> 4084 bytes KylinNM/res/w/pb-top-wifi-offline.png | Bin 0 -> 1136 bytes KylinNM/res/w/wifi-full-off.png | Bin 0 -> 1707 bytes KylinNM/res/w/wifi-full-pwd-off.png | Bin 0 -> 1747 bytes KylinNM/res/w/wifi-full-pwd.png | Bin 0 -> 1828 bytes KylinNM/res/w/wifi-full.png | Bin 0 -> 1770 bytes KylinNM/res/w/wifi-high-off.png | Bin 0 -> 1731 bytes KylinNM/res/w/wifi-high-pwd-off.png | Bin 0 -> 1795 bytes KylinNM/res/w/wifi-high-pwd.png | Bin 0 -> 1894 bytes KylinNM/res/w/wifi-high.png | Bin 0 -> 1820 bytes KylinNM/res/w/wifi-low-off.png | Bin 0 -> 1665 bytes KylinNM/res/w/wifi-low-pwd-off.png | Bin 0 -> 1735 bytes KylinNM/res/w/wifi-low-pwd.png | Bin 0 -> 1841 bytes KylinNM/res/w/wifi-low.png | Bin 0 -> 1777 bytes KylinNM/res/w/wifi-medium-off.png | Bin 0 -> 1696 bytes KylinNM/res/w/wifi-medium-pwd-off.png | Bin 0 -> 1754 bytes KylinNM/res/w/wifi-medium-pwd.png | Bin 0 -> 1867 bytes KylinNM/res/w/wifi-medium.png | Bin 0 -> 1812 bytes KylinNM/res/w/wifi-none-off.png | Bin 0 -> 1157 bytes KylinNM/res/w/wifi-none-pwd-off.png | Bin 0 -> 1330 bytes KylinNM/res/w/wifi-none-pwd.png | Bin 0 -> 1433 bytes KylinNM/res/w/wifi-none.png | Bin 0 -> 1229 bytes KylinNM/res/x/fly-mode-off.svg | 19 + KylinNM/res/x/fly-mode-on.svg | 14 + KylinNM/res/x/hot-spot-off.svg | 22 + KylinNM/res/x/hot-spot-on.svg | 17 + KylinNM/res/x/load-down.png | Bin 0 -> 192 bytes KylinNM/res/x/load-up.png | Bin 0 -> 194 bytes KylinNM/res/x/net-list-bg.svg | 16 + KylinNM/res/x/pb-close.png | Bin 0 -> 620 bytes KylinNM/res/x/pb-conn-dis.png | Bin 0 -> 667 bytes KylinNM/res/x/pb-newConn.png | Bin 0 -> 274 bytes KylinNM/res/x/pb-wifi-n.png | Bin 0 -> 1707 bytes KylinNM/res/x/pb-wifi-y.png | Bin 0 -> 1770 bytes KylinNM/res/x/setup.png | Bin 0 -> 2152 bytes KylinNM/res/x/wifi-list-bg.svg | 15 + KylinNM/src/backthread.cpp | 461 +++ KylinNM/src/backthread.h | 82 + KylinNM/src/confform.cpp | 552 ++++ KylinNM/src/confform.h | 102 + KylinNM/src/confform.ui | 434 +++ KylinNM/src/ksimplenm.cpp | 79 + KylinNM/src/ksimplenm.h | 53 + KylinNM/src/kylin-dbus-interface.cpp | 1105 +++++++ KylinNM/src/kylin-dbus-interface.h | 135 + KylinNM/src/kylin-network-interface.c | 997 ++++++ KylinNM/src/kylin-network-interface.h | 267 ++ KylinNM/src/kylinnm.cpp | 2801 +++++++++++++++++ KylinNM/src/kylinnm.h | 353 +++ KylinNM/src/kylinnm.ui | 296 ++ KylinNM/src/loadingdiv.cpp | 75 + KylinNM/src/loadingdiv.h | 51 + KylinNM/src/main.cpp | 76 + KylinNM/src/oneconnform.cpp | 939 ++++++ KylinNM/src/oneconnform.h | 151 + KylinNM/src/oneconnform.ui | 306 ++ KylinNM/src/onelancform.cpp | 482 +++ KylinNM/src/onelancform.h | 117 + KylinNM/src/onelancform.ui | 242 ++ KylinNM/src/swipegesturerecognizer.cpp | 154 + KylinNM/src/swipegesturerecognizer.h | 46 + KylinNM/src/switchbutton.cpp | 92 + KylinNM/src/switchbutton.h | 32 + KylinNM/src/utils.cpp | 267 ++ KylinNM/src/utils.h | 138 + KylinNM/wireless-security/dlgconnhidwifi.cpp | 379 +++ KylinNM/wireless-security/dlgconnhidwifi.h | 86 + KylinNM/wireless-security/dlgconnhidwifi.ui | 238 ++ .../wireless-security/dlgconnhidwifileap.cpp | 290 ++ .../wireless-security/dlgconnhidwifileap.h | 75 + .../wireless-security/dlgconnhidwifileap.ui | 319 ++ .../dlgconnhidwifisecfast.cpp | 403 +++ .../wireless-security/dlgconnhidwifisecfast.h | 81 + .../dlgconnhidwifisecfast.ui | 505 +++ .../dlgconnhidwifisecleap.cpp | 343 ++ .../wireless-security/dlgconnhidwifisecleap.h | 77 + .../dlgconnhidwifisecleap.ui | 354 +++ .../dlgconnhidwifisecpeap.cpp | 461 +++ .../wireless-security/dlgconnhidwifisecpeap.h | 84 + .../dlgconnhidwifisecpeap.ui | 614 ++++ .../dlgconnhidwifisecpwd.cpp | 291 ++ .../wireless-security/dlgconnhidwifisecpwd.h | 72 + .../wireless-security/dlgconnhidwifisecpwd.ui | 354 +++ .../dlgconnhidwifisectls.cpp | 628 ++++ .../wireless-security/dlgconnhidwifisectls.h | 88 + .../wireless-security/dlgconnhidwifisectls.ui | 617 ++++ .../dlgconnhidwifisectunneltls.cpp | 457 +++ .../dlgconnhidwifisectunneltls.h | 84 + .../dlgconnhidwifisectunneltls.ui | 579 ++++ .../wireless-security/dlgconnhidwifiwep.cpp | 239 ++ KylinNM/wireless-security/dlgconnhidwifiwep.h | 69 + .../wireless-security/dlgconnhidwifiwep.ui | 360 +++ .../wireless-security/dlgconnhidwifiwpa.cpp | 411 +++ KylinNM/wireless-security/dlgconnhidwifiwpa.h | 90 + .../wireless-security/dlgconnhidwifiwpa.ui | 284 ++ KylinNM/wireless-security/kylinheadfile.cpp | 44 + KylinNM/wireless-security/kylinheadfile.h | 49 + README.md | 66 + VirtualKeyboard/CMakeLists.txt | 24 + VirtualKeyboard/README.md | 1 + VirtualKeyboard/VirtualKeyboard.pri | 19 + VirtualKeyboard/VirtualKeyboard.pro | 32 + VirtualKeyboard/src/cursormonitor.cpp | 70 + VirtualKeyboard/src/cursormonitor.h | 36 + VirtualKeyboard/src/images/backspace.svg | 13 + .../src/images/backspace_click.svg | 13 + VirtualKeyboard/src/images/capslock.svg | 10 + VirtualKeyboard/src/images/capslock_click.svg | 9 + VirtualKeyboard/src/images/capslock_hl.svg | 7 + .../src/images/capslock_hl_click.svg | 7 + VirtualKeyboard/src/images/close.svg | 22 + VirtualKeyboard/src/images/close_click.svg | 22 + VirtualKeyboard/src/images/down.svg | 9 + VirtualKeyboard/src/images/down_click.svg | 6 + VirtualKeyboard/src/images/enter.svg | 11 + VirtualKeyboard/src/images/enter_click.svg | 9 + VirtualKeyboard/src/images/left.svg | 7 + VirtualKeyboard/src/images/left_click.svg | 7 + VirtualKeyboard/src/images/right.svg | 6 + VirtualKeyboard/src/images/right_click.svg | 6 + VirtualKeyboard/src/images/super.svg | 1 + VirtualKeyboard/src/images/super_click.svg | 1 + VirtualKeyboard/src/images/up.svg | 9 + VirtualKeyboard/src/images/up_click.svg | 6 + VirtualKeyboard/src/keyboard.qrc | 27 + VirtualKeyboard/src/keyboard.qss | 37 + VirtualKeyboard/src/keyboardwidget.cpp | 459 +++ VirtualKeyboard/src/keyboardwidget.h | 77 + VirtualKeyboard/src/keyboardwidget.ui | 1203 +++++++ VirtualKeyboard/src/main.cpp | 32 + VirtualKeyboard/src/virtualkeyboard.cpp | 79 + VirtualKeyboard/src/virtualkeyboard.h | 47 + VirtualKeyboard/src/x11keyboard.cpp | 208 ++ VirtualKeyboard/src/x11keyboard.h | 116 + config.h.in | 9 + data/CMakeLists.txt | 6 + data/org.ukui.screensaver.gschema.xml | 169 + data/screensaver-startup.sh | 5 + data/ukui-screensaver-qt | 14 + data/ukui-screensaver.desktop | 19 + data/ukui-screensaver.directory | 180 ++ data/ukui-screensavers.menu | 20 + i18n_ts/CMakeLists.txt | 12 + i18n_ts/bo.ts | 2088 ++++++++++++ i18n_ts/bo_CN.ts | 1679 ++++++++++ i18n_ts/es.ts | 1736 ++++++++++ i18n_ts/fr.ts | 1736 ++++++++++ i18n_ts/pt.ts | 1736 ++++++++++ i18n_ts/ru.ts | 1736 ++++++++++ i18n_ts/tr.ts | 2222 +++++++++++++ i18n_ts/zh_CN.ts | 2506 +++++++++++++++ man/ukui-screensaver-backend.1 | 62 + man/ukui-screensaver-command.1 | 60 + man/ukui-screensaver-dialog.1 | 62 + screensaver-focus-helper/CMakeLists.txt | 27 + screensaver-focus-helper/blackwindow.cpp | 93 + screensaver-focus-helper/blackwindow.h | 25 + screensaver-focus-helper/main.cpp | 20 + screensaver/CMakeLists.txt | 58 + screensaver/assets/default-intel.qss | 86 + screensaver/assets/default.qss | 90 + screensaver/assets/download.svg | 1 + screensaver/assets/logo-kylin.svg | 57 + screensaver/assets/logo.svg | 117 + screensaver/assets/message.png | Bin 0 -> 573 bytes screensaver/assets/next.svg | 1 + screensaver/assets/prev.svg | 1 + screensaver/assets/setBackground.svg | 1 + screensaver/assets/settings.svg | 1 + screensaver/assets/wallpaper.svg | 1 + screensaver/assets/weather-icon/.DS_Store | Bin 0 -> 14340 bytes screensaver/assets/weather-icon/._.DS_Store | Bin 0 -> 4096 bytes .../._511-中度霾-Moderate haze.svg | Bin 0 -> 4096 bytes screensaver/assets/weather-icon/100.svg | 1 + screensaver/assets/weather-icon/101.svg | 1 + screensaver/assets/weather-icon/102.svg | 1 + screensaver/assets/weather-icon/103.svg | 1 + screensaver/assets/weather-icon/104.svg | 1 + screensaver/assets/weather-icon/200.svg | 1 + screensaver/assets/weather-icon/201.svg | 1 + screensaver/assets/weather-icon/202.svg | 1 + screensaver/assets/weather-icon/203.svg | 1 + screensaver/assets/weather-icon/204.svg | 1 + screensaver/assets/weather-icon/205.svg | 1 + screensaver/assets/weather-icon/206.svg | 1 + screensaver/assets/weather-icon/207.svg | 1 + screensaver/assets/weather-icon/208.svg | 1 + screensaver/assets/weather-icon/209.svg | 1 + screensaver/assets/weather-icon/210.svg | 1 + screensaver/assets/weather-icon/211.svg | 1 + screensaver/assets/weather-icon/212.svg | 1 + screensaver/assets/weather-icon/213.svg | 1 + screensaver/assets/weather-icon/300.svg | 1 + screensaver/assets/weather-icon/301.svg | 1 + screensaver/assets/weather-icon/302.svg | 1 + screensaver/assets/weather-icon/303.svg | 1 + screensaver/assets/weather-icon/304.svg | 1 + screensaver/assets/weather-icon/305.svg | 1 + screensaver/assets/weather-icon/306.svg | 1 + screensaver/assets/weather-icon/307.svg | 1 + screensaver/assets/weather-icon/308.svg | 1 + screensaver/assets/weather-icon/309.svg | 1 + screensaver/assets/weather-icon/310.svg | 1 + screensaver/assets/weather-icon/311.svg | 1 + screensaver/assets/weather-icon/312.svg | 1 + screensaver/assets/weather-icon/313.svg | 1 + screensaver/assets/weather-icon/314.svg | 1 + screensaver/assets/weather-icon/315.svg | 1 + screensaver/assets/weather-icon/316.svg | 1 + screensaver/assets/weather-icon/317.svg | 1 + screensaver/assets/weather-icon/318.svg | 1 + screensaver/assets/weather-icon/399.svg | 1 + screensaver/assets/weather-icon/400.svg | 1 + screensaver/assets/weather-icon/401.svg | 1 + screensaver/assets/weather-icon/402.svg | 1 + screensaver/assets/weather-icon/403.svg | 1 + screensaver/assets/weather-icon/404.svg | 1 + screensaver/assets/weather-icon/405.svg | 1 + screensaver/assets/weather-icon/406.svg | 1 + screensaver/assets/weather-icon/407.svg | 1 + screensaver/assets/weather-icon/408.svg | 1 + screensaver/assets/weather-icon/409.svg | 1 + screensaver/assets/weather-icon/410.svg | 1 + screensaver/assets/weather-icon/499.svg | 1 + screensaver/assets/weather-icon/500.svg | 1 + screensaver/assets/weather-icon/501.svg | 1 + screensaver/assets/weather-icon/502.svg | 1 + screensaver/assets/weather-icon/503.svg | 1 + screensaver/assets/weather-icon/504.svg | 1 + screensaver/assets/weather-icon/507.svg | 1 + screensaver/assets/weather-icon/508.svg | 1 + screensaver/assets/weather-icon/509.svg | 1 + screensaver/assets/weather-icon/510.svg | 1 + screensaver/assets/weather-icon/511.svg | 1 + screensaver/assets/weather-icon/512.svg | 1 + screensaver/assets/weather-icon/513.svg | 1 + screensaver/assets/weather-icon/514.svg | 1 + screensaver/assets/weather-icon/515.svg | 1 + screensaver/assets/weather-icon/900.svg | 1 + screensaver/assets/weather-icon/901.svg | 1 + screensaver/assets/weather-icon/999.svg | 16 + screensaver/chinesedate.cpp | 162 + screensaver/chinesedate.h | 73 + screensaver/cyclelabel.cpp | 98 + screensaver/cyclelabel.h | 64 + screensaver/default.qrc | 87 + screensaver/language/screensaver-en_US.ini | 130 + screensaver/language/screensaver-jd.ini | 161 + screensaver/language/screensaver-zh_CN.ini | 161 + screensaver/main.cpp | 135 + screensaver/mbackground.cpp | 123 + screensaver/mbackground.h | 45 + screensaver/scconfiguration.cpp | 278 ++ screensaver/scconfiguration.h | 86 + screensaver/screensaver.cpp | 1406 +++++++++ screensaver/screensaver.h | 184 ++ screensaver/sleeptime.cpp | 119 + screensaver/sleeptime.h | 50 + screensaver/ukui-screensaver-default.pro | 49 + screensaver/weathermanager.cpp | 375 +++ screensaver/weathermanager.h | 122 + set4kScale/CMakeLists.txt | 25 + set4kScale/main.cpp | 208 ++ src/CMakeLists.txt | 199 ++ src/PhysicalDeviceSet/brightnessdeviceset.cpp | 104 + src/PhysicalDeviceSet/brightnessdeviceset.h | 40 + src/PhysicalDeviceSet/flightmodeset.cpp | 20 + src/PhysicalDeviceSet/flightmodeset.h | 28 + src/PhysicalDeviceSet/physical-device-set.pri | 11 + src/PhysicalDeviceSet/sounddeviceset.cpp | 221 ++ src/PhysicalDeviceSet/sounddeviceset.h | 48 + src/PhysicalDeviceSet/touchscreenset.cpp | 30 + src/PhysicalDeviceSet/touchscreenset.h | 18 + src/accountsinterface.cpp | 110 + src/accountsinterface.h | 41 + src/assets.qrc | 129 + src/assets/01-default-commercial.png | Bin 0 -> 68347 bytes src/assets/01-default-community.png | Bin 0 -> 63677 bytes src/assets/authdialog.qss | 270 ++ src/assets/bio-login.png | Bin 0 -> 183 bytes src/assets/capslock.png | Bin 0 -> 292 bytes src/assets/capslock.svg | 1 + src/assets/combobox_down.png | Bin 0 -> 181 bytes src/assets/hibernate.png | Bin 0 -> 902 bytes src/assets/hide-password.png | Bin 0 -> 335 bytes src/assets/iconFace.png | Bin 0 -> 11643 bytes src/assets/iconFace.svg | 1 + src/assets/intel/back.png | Bin 0 -> 299 bytes src/assets/intel/cancel.png | Bin 0 -> 299 bytes src/assets/intel/delete.svg | 6 + src/assets/intel/icon-backspace.png | Bin 0 -> 307 bytes src/assets/intel/icon-no-signal.png | Bin 0 -> 483 bytes src/assets/intel/icon-wechat-noqrcode.png | Bin 0 -> 3358 bytes src/assets/intel/icon-wifi.png | Bin 0 -> 657 bytes src/assets/intel/icon-wired.png | Bin 0 -> 485 bytes src/assets/intel/keyboard.png | Bin 0 -> 301 bytes src/assets/intel/lock.png | Bin 0 -> 692 bytes src/assets/intel/logout.png | Bin 0 -> 515 bytes src/assets/intel/message.png | Bin 0 -> 573 bytes src/assets/intel/more.png | Bin 0 -> 596 bytes src/assets/intel/phone.png | Bin 0 -> 165 bytes src/assets/intel/pin.png | Bin 0 -> 293 bytes src/assets/intel/powerManager.png | Bin 0 -> 574 bytes src/assets/intel/qr-code.png | Bin 0 -> 865 bytes src/assets/intel/qr-reload.png | Bin 0 -> 18271 bytes src/assets/intel/reboot.png | Bin 0 -> 1047 bytes src/assets/intel/shutdown.png | Bin 0 -> 1065 bytes src/assets/intel/slide_unlock.png | Bin 0 -> 408 bytes src/assets/intel/sms.png | Bin 0 -> 323 bytes src/assets/intel/wechat.png | Bin 0 -> 402 bytes src/assets/intel/wifi.png | Bin 0 -> 657 bytes src/assets/keyboard.png | Bin 0 -> 171 bytes src/assets/keyboard.svg | 1 + src/assets/lock.png | Bin 0 -> 830 bytes src/assets/login-button-hover.svg | 1 + src/assets/login-button.svg | 1 + src/assets/logout.png | Bin 0 -> 562 bytes src/assets/password-login.png | Bin 0 -> 188 bytes src/assets/powerManager.png | Bin 0 -> 501 bytes src/assets/reboot.png | Bin 0 -> 1106 bytes src/assets/scrollbar_down.png | Bin 0 -> 126 bytes src/assets/scrollbar_down_clicked.png | Bin 0 -> 127 bytes src/assets/scrollbar_down_hover.png | Bin 0 -> 126 bytes src/assets/scrollbar_up.png | Bin 0 -> 115 bytes src/assets/scrollbar_up_clicked.png | Bin 0 -> 115 bytes src/assets/scrollbar_up_hover.png | Bin 0 -> 115 bytes src/assets/selected.svg | 1 + src/assets/show-password.png | Bin 0 -> 582 bytes src/assets/shutdown.png | Bin 0 -> 1157 bytes src/assets/suspend.png | Bin 0 -> 817 bytes src/assets/switchGreeter.png | Bin 0 -> 1062 bytes src/assets/switchUser.png | Bin 0 -> 424 bytes src/assets/ukui-loginopt-face.svg | 1 + src/assets/ukui-loginopt-finger.svg | 1 + src/assets/ukui-loginopt-fingervein.svg | 1 + src/assets/ukui-loginopt-iris.svg | 1 + src/assets/ukui-loginopt-lose.svg | 1 + src/assets/ukui-loginopt-password.svg | 1 + src/assets/ukui-loginopt-qrcode.svg | 1 + src/assets/ukui-loginopt-smile.svg | 1 + src/assets/ukui-loginopt-voice.svg | 1 + src/assets/ukui-qrcode-null.svg | 1 + src/assets/unlock-button-hover.png | Bin 0 -> 548 bytes src/assets/unlock-button-pressed.png | Bin 0 -> 546 bytes src/assets/unlock-button.png | Bin 0 -> 538 bytes src/assets/waiting.png | Bin 0 -> 417 bytes src/assets/weather/0.png | Bin 0 -> 16703 bytes src/assets/weather/1.png | Bin 0 -> 17559 bytes src/assets/weather/10.png | Bin 0 -> 17496 bytes src/assets/weather/11.png | Bin 0 -> 17496 bytes src/assets/weather/13.png | Bin 0 -> 18639 bytes src/assets/weather/14.png | Bin 0 -> 11700 bytes src/assets/weather/15.png | Bin 0 -> 15916 bytes src/assets/weather/16.png | Bin 0 -> 19757 bytes src/assets/weather/17.png | Bin 0 -> 19757 bytes src/assets/weather/18.png | Bin 0 -> 12940 bytes src/assets/weather/19.png | Bin 0 -> 18130 bytes src/assets/weather/2.png | Bin 0 -> 12940 bytes src/assets/weather/20.png | Bin 0 -> 15156 bytes src/assets/weather/21.png | Bin 0 -> 19637 bytes src/assets/weather/22.png | Bin 0 -> 13851 bytes src/assets/weather/24.png | Bin 0 -> 21211 bytes src/assets/weather/27.png | Bin 0 -> 15916 bytes src/assets/weather/28.png | Bin 0 -> 19757 bytes src/assets/weather/29.png | Bin 0 -> 14927 bytes src/assets/weather/3.png | Bin 0 -> 22099 bytes src/assets/weather/30.png | Bin 0 -> 20621 bytes src/assets/weather/31.png | Bin 0 -> 27684 bytes src/assets/weather/33.png | Bin 0 -> 14324 bytes src/assets/weather/35.png | Bin 0 -> 14008 bytes src/assets/weather/36.png | Bin 0 -> 19448 bytes src/assets/weather/38.png | Bin 0 -> 19908 bytes src/assets/weather/39.png | Bin 0 -> 20592 bytes src/assets/weather/4.png | Bin 0 -> 18118 bytes src/assets/weather/40.png | Bin 0 -> 15092 bytes src/assets/weather/41.png | Bin 0 -> 14595 bytes src/assets/weather/43.png | Bin 0 -> 18639 bytes src/assets/weather/45.png | Bin 0 -> 23225 bytes src/assets/weather/47.png | Bin 0 -> 20265 bytes src/assets/weather/48.png | Bin 0 -> 20389 bytes src/assets/weather/49.png | Bin 0 -> 20488 bytes src/assets/weather/5.png | Bin 0 -> 18135 bytes src/assets/weather/51.png | Bin 0 -> 22200 bytes src/assets/weather/52.png | Bin 0 -> 16620 bytes src/assets/weather/53.png | Bin 0 -> 22716 bytes src/assets/weather/55.png | Bin 0 -> 16391 bytes src/assets/weather/56.png | Bin 0 -> 18135 bytes src/assets/weather/58.png | Bin 0 -> 15156 bytes src/assets/weather/59.png | Bin 0 -> 23187 bytes src/assets/weather/6.png | Bin 0 -> 18639 bytes src/assets/weather/60.png | Bin 0 -> 18135 bytes src/assets/weather/62.png | Bin 0 -> 21622 bytes src/assets/weather/63.png | Bin 0 -> 22200 bytes src/assets/weather/66.png | Bin 0 -> 27684 bytes src/assets/weather/67.png | Bin 0 -> 15474 bytes src/assets/weather/68.png | Bin 0 -> 22681 bytes src/assets/weather/7.png | Bin 0 -> 10363 bytes src/assets/weather/8.png | Bin 0 -> 12982 bytes src/assets/weather/9.png | Bin 0 -> 17671 bytes src/assets/weather/icon.htm | 29 + src/auth-pam.cpp | 352 +++ src/auth-pam.h | 59 + src/auth.h | 62 + src/authdialog.cpp | 1078 +++++++ src/authdialog.h | 176 ++ src/common.h | 61 + src/configuration.cpp | 216 ++ src/configuration.h | 66 + src/digitalauthdialog.cpp | 834 +++++ src/digitalauthdialog.h | 176 ++ src/digitalkeyboard.cpp | 129 + src/digitalkeyboard.h | 31 + src/displaymanager.cpp | 161 + src/displaymanager.h | 55 + src/eduplatforminterface.cpp | 375 +++ src/eduplatforminterface.h | 66 + src/fullbackgroundwidget.cpp | 1154 +++++++ src/fullbackgroundwidget.h | 131 + src/generalauthwidget.cpp | 23 + src/generalauthwidget.h | 34 + src/gestureauthdialog.cpp | 402 +++ src/gestureauthdialog.h | 85 + src/grab-x11.cpp | 83 + src/grab-x11.h | 25 + src/hoverwidget.cpp | 60 + src/hoverwidget.h | 50 + src/iconedit.cpp | 309 ++ src/iconedit.h | 80 + src/image.qrc | 7 + src/imageutil.cpp | 81 + src/imageutil.h | 35 + src/interface.cpp | 271 ++ src/interface.h | 74 + src/lockwidget.cpp | 987 ++++++ src/lockwidget.h | 124 + src/lockwidget.ui | 118 + src/logind.cpp | 61 + src/logind.h | 42 + src/loginoptionswidget.cpp | 680 ++++ src/loginoptionswidget.h | 120 + src/monitorwatcher.cpp | 127 + src/monitorwatcher.h | 56 + src/networkwatcher.cpp | 93 + src/networkwatcher.h | 42 + src/org.ukui.ScreenSaver.xml | 24 + src/pam-tally.c | 478 +++ src/pam-tally.cpp | 104 + src/pam-tally.h | 53 + src/pam.cpp | 126 + src/pam.h | 42 + src/permissioncheck.cpp | 401 +++ src/permissioncheck.h | 108 + src/powermanager.cpp | 801 +++++ src/powermanager.h | 135 + src/screensaver.cpp | 143 + src/screensaver.h | 77 + src/screensaverwidget.cpp | 178 ++ src/screensaverwidget.h | 51 + src/sessionwatcher.cpp | 190 ++ src/sessionwatcher.h | 62 + src/surewindow.cpp | 32 + src/surewindow.h | 28 + src/surewindow.ui | 163 + src/switchbuttongroup.cpp | 135 + src/switchbuttongroup.h | 50 + src/tabletlockwidget.cpp | 634 ++++ src/tabletlockwidget.h | 127 + src/test-accounts.cpp | 31 + src/types.h | 75 + src/ukui-screensaver-backend.cpp | 149 + src/ukui-screensaver-checkpass.cpp | 166 + src/ukui-screensaver-command.cpp | 96 + src/ukui-screensaver-dialog.cpp | 318 ++ src/ukui-screensaver.pro | 80 + src/unixsignallistener.cpp | 63 + src/unixsignallistener.h | 53 + src/users.cpp | 209 ++ src/users.h | 64 + src/utils.cpp | 20 + src/utils.h | 11 + src/verificationwidget.cpp | 674 ++++ src/verificationwidget.h | 155 + src/verticalVerificationwidget.cpp | 87 + src/verticalVerificationwidget.h | 34 + src/weathermanager.cpp | 323 ++ src/weathermanager.h | 112 + src/wechatauthdialog.cpp | 364 +++ src/wechatauthdialog.h | 116 + src/xeventmonitor.cpp | 208 ++ src/xeventmonitor.h | 58 + 595 files changed, 65000 insertions(+) create mode 100644 BiometricAuth/BiometricAuth.pri create mode 100644 BiometricAuth/BiometricAuth.pro create mode 100644 BiometricAuth/CMakeLists.txt create mode 100644 BiometricAuth/biometricauthwidget.cpp create mode 100644 BiometricAuth/biometricauthwidget.h create mode 100644 BiometricAuth/biometricdeviceinfo.cpp create mode 100644 BiometricAuth/biometricdeviceinfo.h create mode 100644 BiometricAuth/biometricdeviceswidget.cpp create mode 100644 BiometricAuth/biometricdeviceswidget.h create mode 100644 BiometricAuth/biometricproxy.cpp create mode 100644 BiometricAuth/biometricproxy.h create mode 100644 BiometricAuth/giodbus.cpp create mode 100644 BiometricAuth/giodbus.h create mode 100644 BiometricAuth/main.cpp create mode 100644 CMakeLists.txt create mode 100644 Common/CMakeLists.txt create mode 100644 Common/autoresize.cpp create mode 100644 Common/autoresize.h create mode 100644 Common/checkbutton.cpp create mode 100644 Common/checkbutton.h create mode 100644 Common/commonfunc.cpp create mode 100644 Common/commonfunc.h create mode 100644 KylinNM/CMakeLists.txt create mode 100644 KylinNM/README.md create mode 100644 KylinNM/hot-spot/dlghotspotcreate.cpp create mode 100644 KylinNM/hot-spot/dlghotspotcreate.h create mode 100644 KylinNM/hot-spot/dlghotspotcreate.ui create mode 100644 KylinNM/kylin-nm.pri create mode 100644 KylinNM/kylin-nm.pro create mode 100644 KylinNM/nmqrc.qrc create mode 100644 KylinNM/qss/style.qss create mode 100644 KylinNM/res.qrc create mode 100644 KylinNM/res/g/checkbox-checked.svg create mode 100644 KylinNM/res/g/checkbox-unchecked.svg create mode 100644 KylinNM/res/g/close_black.png create mode 100644 KylinNM/res/g/close_white.png create mode 100644 KylinNM/res/g/down_arrow.png create mode 100644 KylinNM/res/h/hide-pwd.png create mode 100644 KylinNM/res/h/no-pwd-wifi.png create mode 100644 KylinNM/res/h/right-pwd.png create mode 100644 KylinNM/res/h/show-pwd.png create mode 100644 KylinNM/res/l/network-offline.png create mode 100644 KylinNM/res/l/network-offline.svg create mode 100644 KylinNM/res/l/network-online.png create mode 100644 KylinNM/res/l/network-online.svg create mode 100644 KylinNM/res/l/pb-network-info.png create mode 100644 KylinNM/res/l/pb-network-offline.png create mode 100644 KylinNM/res/l/pb-network-online.png create mode 100644 KylinNM/res/l/pb-top-network-offline.png create mode 100644 KylinNM/res/s/conning-a/1.png create mode 100644 KylinNM/res/s/conning-a/2.png create mode 100644 KylinNM/res/s/conning-a/3.png create mode 100644 KylinNM/res/s/conning-a/4.png create mode 100644 KylinNM/res/s/conning-a/5.png create mode 100644 KylinNM/res/s/conning-a/6.png create mode 100644 KylinNM/res/s/conning-a/7.png create mode 100644 KylinNM/res/s/conning-a/8.png create mode 100644 KylinNM/res/s/conning-b/1.png create mode 100644 KylinNM/res/s/conning-b/10.png create mode 100644 KylinNM/res/s/conning-b/11.png create mode 100644 KylinNM/res/s/conning-b/12.png create mode 100644 KylinNM/res/s/conning-b/2.png create mode 100644 KylinNM/res/s/conning-b/3.png create mode 100644 KylinNM/res/s/conning-b/4.png create mode 100644 KylinNM/res/s/conning-b/5.png create mode 100644 KylinNM/res/s/conning-b/6.png create mode 100644 KylinNM/res/s/conning-b/7.png create mode 100644 KylinNM/res/s/conning-b/8.png create mode 100644 KylinNM/res/s/conning-b/9.png create mode 100644 KylinNM/res/s/conning-s/1.png create mode 100644 KylinNM/res/s/conning-s/10.png create mode 100644 KylinNM/res/s/conning-s/11.png create mode 100644 KylinNM/res/s/conning-s/12.png create mode 100644 KylinNM/res/s/conning-s/1x/1.png create mode 100644 KylinNM/res/s/conning-s/1x/10.png create mode 100644 KylinNM/res/s/conning-s/1x/11.png create mode 100644 KylinNM/res/s/conning-s/1x/12.png create mode 100644 KylinNM/res/s/conning-s/1x/2.png create mode 100644 KylinNM/res/s/conning-s/1x/3.png create mode 100644 KylinNM/res/s/conning-s/1x/4.png create mode 100644 KylinNM/res/s/conning-s/1x/5.png create mode 100644 KylinNM/res/s/conning-s/1x/6.png create mode 100644 KylinNM/res/s/conning-s/1x/7.png create mode 100644 KylinNM/res/s/conning-s/1x/8.png create mode 100644 KylinNM/res/s/conning-s/1x/9.png create mode 100644 KylinNM/res/s/conning-s/2.png create mode 100644 KylinNM/res/s/conning-s/3.png create mode 100644 KylinNM/res/s/conning-s/4.png create mode 100644 KylinNM/res/s/conning-s/5.png create mode 100644 KylinNM/res/s/conning-s/6.png create mode 100644 KylinNM/res/s/conning-s/7.png create mode 100644 KylinNM/res/s/conning-s/8.png create mode 100644 KylinNM/res/s/conning-s/9.png create mode 100644 KylinNM/res/s/rescan/1.png create mode 100644 KylinNM/res/s/rescan/10.png create mode 100644 KylinNM/res/s/rescan/11.png create mode 100644 KylinNM/res/s/rescan/12.png create mode 100644 KylinNM/res/s/rescan/2.png create mode 100644 KylinNM/res/s/rescan/3.png create mode 100644 KylinNM/res/s/rescan/4.png create mode 100644 KylinNM/res/s/rescan/5.png create mode 100644 KylinNM/res/s/rescan/6.png create mode 100644 KylinNM/res/s/rescan/7.png create mode 100644 KylinNM/res/s/rescan/8.png create mode 100644 KylinNM/res/s/rescan/9.png create mode 100644 KylinNM/res/w/pb-all-wifi-offline.png create mode 100644 KylinNM/res/w/pb-top-wifi-offline.png create mode 100644 KylinNM/res/w/wifi-full-off.png create mode 100644 KylinNM/res/w/wifi-full-pwd-off.png create mode 100644 KylinNM/res/w/wifi-full-pwd.png create mode 100644 KylinNM/res/w/wifi-full.png create mode 100644 KylinNM/res/w/wifi-high-off.png create mode 100644 KylinNM/res/w/wifi-high-pwd-off.png create mode 100644 KylinNM/res/w/wifi-high-pwd.png create mode 100644 KylinNM/res/w/wifi-high.png create mode 100644 KylinNM/res/w/wifi-low-off.png create mode 100644 KylinNM/res/w/wifi-low-pwd-off.png create mode 100644 KylinNM/res/w/wifi-low-pwd.png create mode 100644 KylinNM/res/w/wifi-low.png create mode 100644 KylinNM/res/w/wifi-medium-off.png create mode 100644 KylinNM/res/w/wifi-medium-pwd-off.png create mode 100644 KylinNM/res/w/wifi-medium-pwd.png create mode 100644 KylinNM/res/w/wifi-medium.png create mode 100644 KylinNM/res/w/wifi-none-off.png create mode 100644 KylinNM/res/w/wifi-none-pwd-off.png create mode 100644 KylinNM/res/w/wifi-none-pwd.png create mode 100644 KylinNM/res/w/wifi-none.png create mode 100644 KylinNM/res/x/fly-mode-off.svg create mode 100644 KylinNM/res/x/fly-mode-on.svg create mode 100644 KylinNM/res/x/hot-spot-off.svg create mode 100644 KylinNM/res/x/hot-spot-on.svg create mode 100644 KylinNM/res/x/load-down.png create mode 100644 KylinNM/res/x/load-up.png create mode 100644 KylinNM/res/x/net-list-bg.svg create mode 100644 KylinNM/res/x/pb-close.png create mode 100644 KylinNM/res/x/pb-conn-dis.png create mode 100644 KylinNM/res/x/pb-newConn.png create mode 100644 KylinNM/res/x/pb-wifi-n.png create mode 100644 KylinNM/res/x/pb-wifi-y.png create mode 100644 KylinNM/res/x/setup.png create mode 100644 KylinNM/res/x/wifi-list-bg.svg create mode 100644 KylinNM/src/backthread.cpp create mode 100644 KylinNM/src/backthread.h create mode 100644 KylinNM/src/confform.cpp create mode 100644 KylinNM/src/confform.h create mode 100644 KylinNM/src/confform.ui create mode 100644 KylinNM/src/ksimplenm.cpp create mode 100644 KylinNM/src/ksimplenm.h create mode 100644 KylinNM/src/kylin-dbus-interface.cpp create mode 100644 KylinNM/src/kylin-dbus-interface.h create mode 100644 KylinNM/src/kylin-network-interface.c create mode 100644 KylinNM/src/kylin-network-interface.h create mode 100644 KylinNM/src/kylinnm.cpp create mode 100644 KylinNM/src/kylinnm.h create mode 100644 KylinNM/src/kylinnm.ui create mode 100644 KylinNM/src/loadingdiv.cpp create mode 100644 KylinNM/src/loadingdiv.h create mode 100644 KylinNM/src/main.cpp create mode 100644 KylinNM/src/oneconnform.cpp create mode 100644 KylinNM/src/oneconnform.h create mode 100644 KylinNM/src/oneconnform.ui create mode 100644 KylinNM/src/onelancform.cpp create mode 100644 KylinNM/src/onelancform.h create mode 100644 KylinNM/src/onelancform.ui create mode 100644 KylinNM/src/swipegesturerecognizer.cpp create mode 100644 KylinNM/src/swipegesturerecognizer.h create mode 100644 KylinNM/src/switchbutton.cpp create mode 100644 KylinNM/src/switchbutton.h create mode 100644 KylinNM/src/utils.cpp create mode 100644 KylinNM/src/utils.h create mode 100644 KylinNM/wireless-security/dlgconnhidwifi.cpp create mode 100644 KylinNM/wireless-security/dlgconnhidwifi.h create mode 100644 KylinNM/wireless-security/dlgconnhidwifi.ui create mode 100644 KylinNM/wireless-security/dlgconnhidwifileap.cpp create mode 100644 KylinNM/wireless-security/dlgconnhidwifileap.h create mode 100644 KylinNM/wireless-security/dlgconnhidwifileap.ui create mode 100644 KylinNM/wireless-security/dlgconnhidwifisecfast.cpp create mode 100644 KylinNM/wireless-security/dlgconnhidwifisecfast.h create mode 100644 KylinNM/wireless-security/dlgconnhidwifisecfast.ui create mode 100644 KylinNM/wireless-security/dlgconnhidwifisecleap.cpp create mode 100644 KylinNM/wireless-security/dlgconnhidwifisecleap.h create mode 100644 KylinNM/wireless-security/dlgconnhidwifisecleap.ui create mode 100644 KylinNM/wireless-security/dlgconnhidwifisecpeap.cpp create mode 100644 KylinNM/wireless-security/dlgconnhidwifisecpeap.h create mode 100644 KylinNM/wireless-security/dlgconnhidwifisecpeap.ui create mode 100644 KylinNM/wireless-security/dlgconnhidwifisecpwd.cpp create mode 100644 KylinNM/wireless-security/dlgconnhidwifisecpwd.h create mode 100644 KylinNM/wireless-security/dlgconnhidwifisecpwd.ui create mode 100644 KylinNM/wireless-security/dlgconnhidwifisectls.cpp create mode 100644 KylinNM/wireless-security/dlgconnhidwifisectls.h create mode 100644 KylinNM/wireless-security/dlgconnhidwifisectls.ui create mode 100644 KylinNM/wireless-security/dlgconnhidwifisectunneltls.cpp create mode 100644 KylinNM/wireless-security/dlgconnhidwifisectunneltls.h create mode 100644 KylinNM/wireless-security/dlgconnhidwifisectunneltls.ui create mode 100644 KylinNM/wireless-security/dlgconnhidwifiwep.cpp create mode 100644 KylinNM/wireless-security/dlgconnhidwifiwep.h create mode 100644 KylinNM/wireless-security/dlgconnhidwifiwep.ui create mode 100644 KylinNM/wireless-security/dlgconnhidwifiwpa.cpp create mode 100644 KylinNM/wireless-security/dlgconnhidwifiwpa.h create mode 100644 KylinNM/wireless-security/dlgconnhidwifiwpa.ui create mode 100644 KylinNM/wireless-security/kylinheadfile.cpp create mode 100644 KylinNM/wireless-security/kylinheadfile.h create mode 100644 README.md create mode 100644 VirtualKeyboard/CMakeLists.txt create mode 100644 VirtualKeyboard/README.md create mode 100644 VirtualKeyboard/VirtualKeyboard.pri create mode 100644 VirtualKeyboard/VirtualKeyboard.pro create mode 100644 VirtualKeyboard/src/cursormonitor.cpp create mode 100644 VirtualKeyboard/src/cursormonitor.h create mode 100644 VirtualKeyboard/src/images/backspace.svg create mode 100644 VirtualKeyboard/src/images/backspace_click.svg create mode 100644 VirtualKeyboard/src/images/capslock.svg create mode 100644 VirtualKeyboard/src/images/capslock_click.svg create mode 100644 VirtualKeyboard/src/images/capslock_hl.svg create mode 100644 VirtualKeyboard/src/images/capslock_hl_click.svg create mode 100644 VirtualKeyboard/src/images/close.svg create mode 100644 VirtualKeyboard/src/images/close_click.svg create mode 100644 VirtualKeyboard/src/images/down.svg create mode 100644 VirtualKeyboard/src/images/down_click.svg create mode 100644 VirtualKeyboard/src/images/enter.svg create mode 100644 VirtualKeyboard/src/images/enter_click.svg create mode 100644 VirtualKeyboard/src/images/left.svg create mode 100644 VirtualKeyboard/src/images/left_click.svg create mode 100644 VirtualKeyboard/src/images/right.svg create mode 100644 VirtualKeyboard/src/images/right_click.svg create mode 100644 VirtualKeyboard/src/images/super.svg create mode 100644 VirtualKeyboard/src/images/super_click.svg create mode 100644 VirtualKeyboard/src/images/up.svg create mode 100644 VirtualKeyboard/src/images/up_click.svg create mode 100644 VirtualKeyboard/src/keyboard.qrc create mode 100644 VirtualKeyboard/src/keyboard.qss create mode 100644 VirtualKeyboard/src/keyboardwidget.cpp create mode 100644 VirtualKeyboard/src/keyboardwidget.h create mode 100644 VirtualKeyboard/src/keyboardwidget.ui create mode 100644 VirtualKeyboard/src/main.cpp create mode 100644 VirtualKeyboard/src/virtualkeyboard.cpp create mode 100644 VirtualKeyboard/src/virtualkeyboard.h create mode 100644 VirtualKeyboard/src/x11keyboard.cpp create mode 100644 VirtualKeyboard/src/x11keyboard.h create mode 100644 config.h.in create mode 100644 data/CMakeLists.txt create mode 100644 data/org.ukui.screensaver.gschema.xml create mode 100755 data/screensaver-startup.sh create mode 100644 data/ukui-screensaver-qt create mode 100644 data/ukui-screensaver.desktop create mode 100644 data/ukui-screensaver.directory create mode 100644 data/ukui-screensavers.menu create mode 100644 i18n_ts/CMakeLists.txt create mode 100644 i18n_ts/bo.ts create mode 100644 i18n_ts/bo_CN.ts create mode 100644 i18n_ts/es.ts create mode 100644 i18n_ts/fr.ts create mode 100644 i18n_ts/pt.ts create mode 100644 i18n_ts/ru.ts create mode 100644 i18n_ts/tr.ts create mode 100644 i18n_ts/zh_CN.ts create mode 100644 man/ukui-screensaver-backend.1 create mode 100644 man/ukui-screensaver-command.1 create mode 100644 man/ukui-screensaver-dialog.1 create mode 100644 screensaver-focus-helper/CMakeLists.txt create mode 100644 screensaver-focus-helper/blackwindow.cpp create mode 100644 screensaver-focus-helper/blackwindow.h create mode 100644 screensaver-focus-helper/main.cpp create mode 100644 screensaver/CMakeLists.txt create mode 100644 screensaver/assets/default-intel.qss create mode 100644 screensaver/assets/default.qss create mode 100644 screensaver/assets/download.svg create mode 100644 screensaver/assets/logo-kylin.svg create mode 100644 screensaver/assets/logo.svg create mode 100644 screensaver/assets/message.png create mode 100644 screensaver/assets/next.svg create mode 100644 screensaver/assets/prev.svg create mode 100644 screensaver/assets/setBackground.svg create mode 100644 screensaver/assets/settings.svg create mode 100644 screensaver/assets/wallpaper.svg create mode 100644 screensaver/assets/weather-icon/.DS_Store create mode 100644 screensaver/assets/weather-icon/._.DS_Store create mode 100644 screensaver/assets/weather-icon/._511-中度霾-Moderate haze.svg create mode 100644 screensaver/assets/weather-icon/100.svg create mode 100644 screensaver/assets/weather-icon/101.svg create mode 100644 screensaver/assets/weather-icon/102.svg create mode 100644 screensaver/assets/weather-icon/103.svg create mode 100644 screensaver/assets/weather-icon/104.svg create mode 100644 screensaver/assets/weather-icon/200.svg create mode 100644 screensaver/assets/weather-icon/201.svg create mode 100644 screensaver/assets/weather-icon/202.svg create mode 100644 screensaver/assets/weather-icon/203.svg create mode 100644 screensaver/assets/weather-icon/204.svg create mode 100644 screensaver/assets/weather-icon/205.svg create mode 100644 screensaver/assets/weather-icon/206.svg create mode 100644 screensaver/assets/weather-icon/207.svg create mode 100644 screensaver/assets/weather-icon/208.svg create mode 100644 screensaver/assets/weather-icon/209.svg create mode 100644 screensaver/assets/weather-icon/210.svg create mode 100644 screensaver/assets/weather-icon/211.svg create mode 100644 screensaver/assets/weather-icon/212.svg create mode 100644 screensaver/assets/weather-icon/213.svg create mode 100644 screensaver/assets/weather-icon/300.svg create mode 100644 screensaver/assets/weather-icon/301.svg create mode 100644 screensaver/assets/weather-icon/302.svg create mode 100644 screensaver/assets/weather-icon/303.svg create mode 100644 screensaver/assets/weather-icon/304.svg create mode 100644 screensaver/assets/weather-icon/305.svg create mode 100644 screensaver/assets/weather-icon/306.svg create mode 100644 screensaver/assets/weather-icon/307.svg create mode 100644 screensaver/assets/weather-icon/308.svg create mode 100644 screensaver/assets/weather-icon/309.svg create mode 100644 screensaver/assets/weather-icon/310.svg create mode 100644 screensaver/assets/weather-icon/311.svg create mode 100644 screensaver/assets/weather-icon/312.svg create mode 100644 screensaver/assets/weather-icon/313.svg create mode 100644 screensaver/assets/weather-icon/314.svg create mode 100644 screensaver/assets/weather-icon/315.svg create mode 100644 screensaver/assets/weather-icon/316.svg create mode 100644 screensaver/assets/weather-icon/317.svg create mode 100644 screensaver/assets/weather-icon/318.svg create mode 100644 screensaver/assets/weather-icon/399.svg create mode 100644 screensaver/assets/weather-icon/400.svg create mode 100644 screensaver/assets/weather-icon/401.svg create mode 100644 screensaver/assets/weather-icon/402.svg create mode 100644 screensaver/assets/weather-icon/403.svg create mode 100644 screensaver/assets/weather-icon/404.svg create mode 100644 screensaver/assets/weather-icon/405.svg create mode 100644 screensaver/assets/weather-icon/406.svg create mode 100644 screensaver/assets/weather-icon/407.svg create mode 100644 screensaver/assets/weather-icon/408.svg create mode 100644 screensaver/assets/weather-icon/409.svg create mode 100644 screensaver/assets/weather-icon/410.svg create mode 100644 screensaver/assets/weather-icon/499.svg create mode 100644 screensaver/assets/weather-icon/500.svg create mode 100644 screensaver/assets/weather-icon/501.svg create mode 100644 screensaver/assets/weather-icon/502.svg create mode 100644 screensaver/assets/weather-icon/503.svg create mode 100644 screensaver/assets/weather-icon/504.svg create mode 100644 screensaver/assets/weather-icon/507.svg create mode 100644 screensaver/assets/weather-icon/508.svg create mode 100644 screensaver/assets/weather-icon/509.svg create mode 100644 screensaver/assets/weather-icon/510.svg create mode 100644 screensaver/assets/weather-icon/511.svg create mode 100644 screensaver/assets/weather-icon/512.svg create mode 100644 screensaver/assets/weather-icon/513.svg create mode 100644 screensaver/assets/weather-icon/514.svg create mode 100644 screensaver/assets/weather-icon/515.svg create mode 100644 screensaver/assets/weather-icon/900.svg create mode 100644 screensaver/assets/weather-icon/901.svg create mode 100644 screensaver/assets/weather-icon/999.svg create mode 100644 screensaver/chinesedate.cpp create mode 100644 screensaver/chinesedate.h create mode 100644 screensaver/cyclelabel.cpp create mode 100644 screensaver/cyclelabel.h create mode 100644 screensaver/default.qrc create mode 100644 screensaver/language/screensaver-en_US.ini create mode 100644 screensaver/language/screensaver-jd.ini create mode 100644 screensaver/language/screensaver-zh_CN.ini create mode 100644 screensaver/main.cpp create mode 100644 screensaver/mbackground.cpp create mode 100644 screensaver/mbackground.h create mode 100644 screensaver/scconfiguration.cpp create mode 100644 screensaver/scconfiguration.h create mode 100644 screensaver/screensaver.cpp create mode 100644 screensaver/screensaver.h create mode 100644 screensaver/sleeptime.cpp create mode 100644 screensaver/sleeptime.h create mode 100644 screensaver/ukui-screensaver-default.pro create mode 100644 screensaver/weathermanager.cpp create mode 100644 screensaver/weathermanager.h create mode 100644 set4kScale/CMakeLists.txt create mode 100644 set4kScale/main.cpp create mode 100644 src/CMakeLists.txt create mode 100644 src/PhysicalDeviceSet/brightnessdeviceset.cpp create mode 100644 src/PhysicalDeviceSet/brightnessdeviceset.h create mode 100644 src/PhysicalDeviceSet/flightmodeset.cpp create mode 100644 src/PhysicalDeviceSet/flightmodeset.h create mode 100644 src/PhysicalDeviceSet/physical-device-set.pri create mode 100644 src/PhysicalDeviceSet/sounddeviceset.cpp create mode 100644 src/PhysicalDeviceSet/sounddeviceset.h create mode 100644 src/PhysicalDeviceSet/touchscreenset.cpp create mode 100644 src/PhysicalDeviceSet/touchscreenset.h create mode 100644 src/accountsinterface.cpp create mode 100644 src/accountsinterface.h create mode 100644 src/assets.qrc create mode 100644 src/assets/01-default-commercial.png create mode 100644 src/assets/01-default-community.png create mode 100644 src/assets/authdialog.qss create mode 100644 src/assets/bio-login.png create mode 100644 src/assets/capslock.png create mode 100644 src/assets/capslock.svg create mode 100644 src/assets/combobox_down.png create mode 100644 src/assets/hibernate.png create mode 100644 src/assets/hide-password.png create mode 100644 src/assets/iconFace.png create mode 100644 src/assets/iconFace.svg create mode 100644 src/assets/intel/back.png create mode 100644 src/assets/intel/cancel.png create mode 100644 src/assets/intel/delete.svg create mode 100644 src/assets/intel/icon-backspace.png create mode 100644 src/assets/intel/icon-no-signal.png create mode 100644 src/assets/intel/icon-wechat-noqrcode.png create mode 100644 src/assets/intel/icon-wifi.png create mode 100644 src/assets/intel/icon-wired.png create mode 100755 src/assets/intel/keyboard.png create mode 100644 src/assets/intel/lock.png create mode 100644 src/assets/intel/logout.png create mode 100644 src/assets/intel/message.png create mode 100644 src/assets/intel/more.png create mode 100755 src/assets/intel/phone.png create mode 100644 src/assets/intel/pin.png create mode 100644 src/assets/intel/powerManager.png create mode 100644 src/assets/intel/qr-code.png create mode 100644 src/assets/intel/qr-reload.png create mode 100644 src/assets/intel/reboot.png create mode 100644 src/assets/intel/shutdown.png create mode 100644 src/assets/intel/slide_unlock.png create mode 100755 src/assets/intel/sms.png create mode 100644 src/assets/intel/wechat.png create mode 100755 src/assets/intel/wifi.png create mode 100644 src/assets/keyboard.png create mode 100644 src/assets/keyboard.svg create mode 100644 src/assets/lock.png create mode 100644 src/assets/login-button-hover.svg create mode 100644 src/assets/login-button.svg create mode 100644 src/assets/logout.png create mode 100644 src/assets/password-login.png create mode 100644 src/assets/powerManager.png create mode 100644 src/assets/reboot.png create mode 100644 src/assets/scrollbar_down.png create mode 100644 src/assets/scrollbar_down_clicked.png create mode 100644 src/assets/scrollbar_down_hover.png create mode 100644 src/assets/scrollbar_up.png create mode 100644 src/assets/scrollbar_up_clicked.png create mode 100644 src/assets/scrollbar_up_hover.png create mode 100644 src/assets/selected.svg create mode 100644 src/assets/show-password.png create mode 100644 src/assets/shutdown.png create mode 100644 src/assets/suspend.png create mode 100644 src/assets/switchGreeter.png create mode 100644 src/assets/switchUser.png create mode 100644 src/assets/ukui-loginopt-face.svg create mode 100644 src/assets/ukui-loginopt-finger.svg create mode 100644 src/assets/ukui-loginopt-fingervein.svg create mode 100644 src/assets/ukui-loginopt-iris.svg create mode 100644 src/assets/ukui-loginopt-lose.svg create mode 100644 src/assets/ukui-loginopt-password.svg create mode 100644 src/assets/ukui-loginopt-qrcode.svg create mode 100644 src/assets/ukui-loginopt-smile.svg create mode 100644 src/assets/ukui-loginopt-voice.svg create mode 100644 src/assets/ukui-qrcode-null.svg create mode 100644 src/assets/unlock-button-hover.png create mode 100644 src/assets/unlock-button-pressed.png create mode 100644 src/assets/unlock-button.png create mode 100644 src/assets/waiting.png create mode 100644 src/assets/weather/0.png create mode 100644 src/assets/weather/1.png create mode 100644 src/assets/weather/10.png create mode 100644 src/assets/weather/11.png create mode 100644 src/assets/weather/13.png create mode 100644 src/assets/weather/14.png create mode 100644 src/assets/weather/15.png create mode 100644 src/assets/weather/16.png create mode 100644 src/assets/weather/17.png create mode 100644 src/assets/weather/18.png create mode 100644 src/assets/weather/19.png create mode 100644 src/assets/weather/2.png create mode 100644 src/assets/weather/20.png create mode 100644 src/assets/weather/21.png create mode 100644 src/assets/weather/22.png create mode 100644 src/assets/weather/24.png create mode 100644 src/assets/weather/27.png create mode 100644 src/assets/weather/28.png create mode 100644 src/assets/weather/29.png create mode 100644 src/assets/weather/3.png create mode 100644 src/assets/weather/30.png create mode 100644 src/assets/weather/31.png create mode 100644 src/assets/weather/33.png create mode 100644 src/assets/weather/35.png create mode 100644 src/assets/weather/36.png create mode 100644 src/assets/weather/38.png create mode 100644 src/assets/weather/39.png create mode 100644 src/assets/weather/4.png create mode 100644 src/assets/weather/40.png create mode 100644 src/assets/weather/41.png create mode 100644 src/assets/weather/43.png create mode 100644 src/assets/weather/45.png create mode 100644 src/assets/weather/47.png create mode 100644 src/assets/weather/48.png create mode 100644 src/assets/weather/49.png create mode 100644 src/assets/weather/5.png create mode 100644 src/assets/weather/51.png create mode 100644 src/assets/weather/52.png create mode 100644 src/assets/weather/53.png create mode 100644 src/assets/weather/55.png create mode 100644 src/assets/weather/56.png create mode 100644 src/assets/weather/58.png create mode 100644 src/assets/weather/59.png create mode 100644 src/assets/weather/6.png create mode 100644 src/assets/weather/60.png create mode 100644 src/assets/weather/62.png create mode 100644 src/assets/weather/63.png create mode 100644 src/assets/weather/66.png create mode 100644 src/assets/weather/67.png create mode 100644 src/assets/weather/68.png create mode 100644 src/assets/weather/7.png create mode 100644 src/assets/weather/8.png create mode 100644 src/assets/weather/9.png create mode 100644 src/assets/weather/icon.htm create mode 100644 src/auth-pam.cpp create mode 100644 src/auth-pam.h create mode 100644 src/auth.h create mode 100644 src/authdialog.cpp create mode 100644 src/authdialog.h create mode 100644 src/common.h create mode 100644 src/configuration.cpp create mode 100644 src/configuration.h create mode 100644 src/digitalauthdialog.cpp create mode 100644 src/digitalauthdialog.h create mode 100644 src/digitalkeyboard.cpp create mode 100644 src/digitalkeyboard.h create mode 100644 src/displaymanager.cpp create mode 100644 src/displaymanager.h create mode 100644 src/eduplatforminterface.cpp create mode 100644 src/eduplatforminterface.h create mode 100644 src/fullbackgroundwidget.cpp create mode 100644 src/fullbackgroundwidget.h create mode 100644 src/generalauthwidget.cpp create mode 100644 src/generalauthwidget.h create mode 100644 src/gestureauthdialog.cpp create mode 100644 src/gestureauthdialog.h create mode 100644 src/grab-x11.cpp create mode 100644 src/grab-x11.h create mode 100644 src/hoverwidget.cpp create mode 100644 src/hoverwidget.h create mode 100644 src/iconedit.cpp create mode 100644 src/iconedit.h create mode 100644 src/image.qrc create mode 100644 src/imageutil.cpp create mode 100644 src/imageutil.h create mode 100644 src/interface.cpp create mode 100644 src/interface.h create mode 100644 src/lockwidget.cpp create mode 100644 src/lockwidget.h create mode 100644 src/lockwidget.ui create mode 100644 src/logind.cpp create mode 100644 src/logind.h create mode 100644 src/loginoptionswidget.cpp create mode 100644 src/loginoptionswidget.h create mode 100644 src/monitorwatcher.cpp create mode 100644 src/monitorwatcher.h create mode 100644 src/networkwatcher.cpp create mode 100644 src/networkwatcher.h create mode 100644 src/org.ukui.ScreenSaver.xml create mode 100644 src/pam-tally.c create mode 100644 src/pam-tally.cpp create mode 100644 src/pam-tally.h create mode 100644 src/pam.cpp create mode 100644 src/pam.h create mode 100644 src/permissioncheck.cpp create mode 100644 src/permissioncheck.h create mode 100644 src/powermanager.cpp create mode 100644 src/powermanager.h create mode 100644 src/screensaver.cpp create mode 100644 src/screensaver.h create mode 100644 src/screensaverwidget.cpp create mode 100644 src/screensaverwidget.h create mode 100644 src/sessionwatcher.cpp create mode 100644 src/sessionwatcher.h create mode 100644 src/surewindow.cpp create mode 100644 src/surewindow.h create mode 100644 src/surewindow.ui create mode 100644 src/switchbuttongroup.cpp create mode 100644 src/switchbuttongroup.h create mode 100644 src/tabletlockwidget.cpp create mode 100644 src/tabletlockwidget.h create mode 100644 src/test-accounts.cpp create mode 100644 src/types.h create mode 100644 src/ukui-screensaver-backend.cpp create mode 100644 src/ukui-screensaver-checkpass.cpp create mode 100644 src/ukui-screensaver-command.cpp create mode 100644 src/ukui-screensaver-dialog.cpp create mode 100644 src/ukui-screensaver.pro create mode 100644 src/unixsignallistener.cpp create mode 100644 src/unixsignallistener.h create mode 100644 src/users.cpp create mode 100644 src/users.h create mode 100644 src/utils.cpp create mode 100644 src/utils.h create mode 100644 src/verificationwidget.cpp create mode 100644 src/verificationwidget.h create mode 100644 src/verticalVerificationwidget.cpp create mode 100644 src/verticalVerificationwidget.h create mode 100644 src/weathermanager.cpp create mode 100644 src/weathermanager.h create mode 100644 src/wechatauthdialog.cpp create mode 100644 src/wechatauthdialog.h create mode 100644 src/xeventmonitor.cpp create mode 100644 src/xeventmonitor.h diff --git a/BiometricAuth/BiometricAuth.pri b/BiometricAuth/BiometricAuth.pri new file mode 100644 index 0000000..cbb2ea1 --- /dev/null +++ b/BiometricAuth/BiometricAuth.pri @@ -0,0 +1,9 @@ +SOURCES += \ + $$PWD/biometricproxy.cpp \ + $$PWD/biometricauthwidget.cpp \ + $$PWD/biometricdeviceswidget.cpp + +HEADERS += \ + $$PWD/biometricproxy.h \ + $$PWD/biometricauthwidget.h \ + $$PWD/biometricdeviceswidget.h diff --git a/BiometricAuth/BiometricAuth.pro b/BiometricAuth/BiometricAuth.pro new file mode 100644 index 0000000..00bc543 --- /dev/null +++ b/BiometricAuth/BiometricAuth.pro @@ -0,0 +1,37 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2018-12-06T09:17:41 +# +#------------------------------------------------- + +QT += core gui dbus + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = BiometricAuth +TEMPLATE = app + +# The following define makes your compiler emit warnings if you use +# any feature of Qt which has been marked as deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if you use deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + + +SOURCES += \ + main.cpp \ + biometricauthwidget.cpp \ + biometricproxy.cpp \ + biometricdeviceswidget.cpp + +HEADERS += \ + biometricauthwidget.h \ + biometricproxy.h \ + biometricdeviceswidget.h + +FORMS += diff --git a/BiometricAuth/CMakeLists.txt b/BiometricAuth/CMakeLists.txt new file mode 100644 index 0000000..9ba7d2c --- /dev/null +++ b/BiometricAuth/CMakeLists.txt @@ -0,0 +1,28 @@ +qt5_wrap_cpp(BiometricAuth_SRC + biometricdeviceinfo.h + biometricproxy.h + biometricauthwidget.h + biometricdeviceswidget.h + giodbus.h + ) + +set(BiometricAuth_SRC + ${BiometricAuth_SRC} + biometricdeviceinfo.cpp + biometricproxy.cpp + biometricauthwidget.cpp + biometricdeviceswidget.cpp + giodbus.cpp + ) + +include_directories( + ${Qt5Core_INCLUDE_DIRS} + ${Qt5Widgets_INCLUDE_DIRS} + ${Qt5DBus_INCLUDE_DIRS} + ${OpenCV_INCLUDE_DIRS} + ${GLIB2_INCLUDE_DIRS} + ) + + +add_library(BiometricAuth STATIC ${BiometricAuth_SRC}) +target_link_libraries(BiometricAuth Qt5::Core Qt5::DBus Qt5::Widgets ${OpenCV_LIBS} ${GIOUNIX2_LIBRARIES}) diff --git a/BiometricAuth/biometricauthwidget.cpp b/BiometricAuth/biometricauthwidget.cpp new file mode 100644 index 0000000..a7b552d --- /dev/null +++ b/BiometricAuth/biometricauthwidget.cpp @@ -0,0 +1,344 @@ +/** + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301, USA. +**/ +#include "biometricauthwidget.h" +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "giodbus.h" + +BiometricAuthWidget::BiometricAuthWidget(BiometricProxy *proxy, QWidget *parent) : + QWidget(parent), + proxy(proxy), + isInAuth(false), + movieTimer(nullptr), + failedCount(0), + timeoutCount(0), + beStopped(false), + retrytimer(nullptr), + usebind(false) +{ + usebind = getAuthDouble(); + initUI(); + resize(400, 260); + + if(this->proxy) + { + connect(this->proxy, &BiometricProxy::StatusChanged, + this, &BiometricAuthWidget::onStatusChanged); + + connect(this->proxy, &BiometricProxy::FrameWritten, + this, &BiometricAuthWidget::onFrameWritten); + } + +} + +void BiometricAuthWidget::initUI() +{ + //显示提示信息 + lblNotify = new QLabel(this); + lblNotify->setWordWrap(true); + lblNotify->setAlignment(Qt::AlignHCenter | Qt::AlignBottom); + + //显示当前设备 + lblDevice = new QLabel(this); + lblDevice->setWordWrap(true); + lblDevice->setAlignment(Qt::AlignCenter); + + //显示图片 + lblImage = new QLabel(this); + lblImage->setFixedSize(100, 100); + +} + + +void BiometricAuthWidget::resizeEvent(QResizeEvent */*event*/) +{ + lblNotify->setGeometry(0, 0, width(), 45); + lblDevice->setGeometry(0, lblNotify->geometry().bottom()+5, width(), 30); + lblImage->setGeometry((width() - lblImage->width()) / 2, + lblDevice->geometry().bottom() + 10, + lblImage->width(), lblImage->height()); + //qDebug() +} + +void BiometricAuthWidget::startAuth(DeviceInfoPtr device, int uid) +{ + if(!proxy) + { + qWarning() << "BiometricProxy doesn't exist."; + return; + } + + if(isInAuth) + { + qDebug() << "Identification is currently under way, stop it"; + stopAuth(); + } + + this->device = device; + this->uid = uid; + this->userName = getpwuid(uid)->pw_name; + this->failedCount = 0; + this->timeoutCount = 0; + this->beStopped = false; + proxy->StopOps(device->id); + startAuth_(); + + if(device->deviceType != DeviceType::Type::Face){ + updateImage(1); + } + +} + +void BiometricAuthWidget::startAuth_() +{ + lblDevice->setText(tr("Current device: ") + device->shortName); + + //qDebug().noquote() << QString("Identify:[drvid: %1, uid: %2]").arg(1).arg(2); + + isInAuth = true; + dup_fd = -1; + + QDBusPendingCall call = proxy->Identify(device->id, uid); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, this); + connect(watcher, &QDBusPendingCallWatcher::finished, + this, &BiometricAuthWidget::onIdentifyComplete); + +} + +void BiometricAuthWidget::stopAuth() +{ + beStopped = true; + if(!isInAuth) + { + return; + } + + proxy->StopOps(device->id); + if(retrytimer&&retrytimer->isActive()){ + retrytimer->stop(); + delete retrytimer; + retrytimer = nullptr; + } + isInAuth = false; + updateImage(0); +} + +void BiometricAuthWidget::onIdentifyComplete(QDBusPendingCallWatcher *watcher) +{ + QDBusPendingReply reply = *watcher; + if(reply.isError()) + { + qWarning() << "Identify error: " << reply.error().message(); + Q_EMIT authComplete(false); + updateImage(0); + return; + } + int result = reply.argumentAt(0).toInt(); + int authUid = reply.argumentAt(1).toInt(); + + // 特征识别成功,而且用户id匹配 + if(result == DBUS_RESULT_SUCCESS && authUid == uid) + { + qDebug() << "Identify success"; + Q_EMIT authComplete(true); + } + // 特征识别不匹配 + else if(result == DBUS_RESULT_NOTMATCH) + { + if(usebind){ + Q_EMIT authComplete(false); + return; + } + qDebug() << "Identify failed"; + failedCount++; + if(failedCount >= GetMaxFailedAutoRetry(userName)) + { + Q_EMIT authComplete(false); + } + else + { + lblNotify->setText(tr("Identify failed, Please retry.")); + if(!beStopped){ + // QTimer::singleShot(1000, this, &BiometricAuthWidget::startAuth_); + if(!retrytimer){ + retrytimer = new QTimer(this); + retrytimer->setSingleShot(true); + connect(retrytimer, &QTimer::timeout, this, &BiometricAuthWidget::startAuth_); + } + retrytimer->start(1000); + } + } + } + //识别发生错误 + else if(result == DBUS_RESULT_ERROR) + { + if(usebind){ + Q_EMIT authComplete(false); + return; + } + StatusReslut ret = proxy->UpdateStatus(device->id); + //识别操作超时 + if(ret.result == 0 && ret.opsStatus == OPS_IDENTIFY_TIMEOUT) + { + timeoutCount++; + if(timeoutCount >= GetMaxTimeoutAutoRetry(userName)) + { + Q_EMIT authComplete(false); + } + else + { + QTimer::singleShot(1000, [&]{ + if(!beStopped) + { + startAuth_(); + } + }); + } + }else{ + Q_EMIT authComplete(false); + } + }else{ + Q_EMIT authComplete(false); + } + updateImage(0); +} + +void BiometricAuthWidget::onFrameWritten(int drvid) +{ + + if(dup_fd == -1){ + dup_fd = get_server_gvariant_stdout(drvid); + } + + if(dup_fd <= 0) + return ; + + cv::Mat img; + lseek(dup_fd, 0, SEEK_SET); + char base64_bufferData[1024*1024]; + int rc = read(dup_fd, base64_bufferData, 1024*1024); + printf("rc = %d\n", rc); + + cv::Mat mat2(1, sizeof(base64_bufferData), CV_8U, base64_bufferData); + img = cv::imdecode(mat2, cv::IMREAD_COLOR); + cv::cvtColor(img,img,cv::COLOR_BGR2RGB); + + QImage srcQImage = QImage((uchar*)(img.data), img.cols, img.rows, QImage::Format_RGB888); + lblImage->setFixedSize(160,160); + lblImage->setGeometry((width() - lblImage->width()) / 2, + lblDevice->geometry().bottom() + 10, + lblImage->width(), lblImage->height()); + lblImage->setPixmap(QPixmap::fromImage(srcQImage).scaled(lblImage->size())); + +} + +void BiometricAuthWidget::onStatusChanged(int drvid, int status) +{ + if(!isInAuth) + { + return; + } + if(drvid != device->id) + { + return; + } + + // 显示来自服务的提示信息 + if(status == STATUS_NOTIFY) + { + QString notifyMsg = proxy->GetNotifyMesg(drvid); + lblNotify->setText(notifyMsg); + } +} + +static int count = 0; +void BiometricAuthWidget::updateImage(int type) +{ + if(device->deviceType == DeviceType::Type::Face) + return ; + + if(type == 0) + { + if(movieTimer && movieTimer->isActive()) + { + movieTimer->stop(); + } + + QString imagePath = QString(UKUI_BIOMETRIC_IMAGES_PATH "%1/01.png") + .arg(DeviceType::getDeviceType(device->deviceType)); + setImage(imagePath); + } + else + { + if(!movieTimer) + { + movieTimer = new QTimer(this); + movieTimer->setInterval(100); + connect(movieTimer, &QTimer::timeout, + this, &BiometricAuthWidget::onMoviePixmapUpdate); + } + count = 0; + movieTimer->start(); + } +} + +void BiometricAuthWidget::onMoviePixmapUpdate() +{ + if(count >= 18) + { + count = 0; + } + count++; + QString fileName = (count < 10 ? "0" : "") + QString::number(count); + QString imagePath = QString(UKUI_BIOMETRIC_IMAGES_PATH "%1/%2.png") + .arg(DeviceType::getDeviceType(device->deviceType)) + .arg(fileName); + setImage(imagePath); +} + +void BiometricAuthWidget::setImage(const QString &path) +{ + QPixmap image(path); + image = image.scaled(lblImage->width(), lblImage->height(), + Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + + lblImage->setPixmap(image); +} + +bool BiometricAuthWidget::getAuthDouble() +{ + QSettings settings("/etc/biometric-auth/ukui-biometric.conf", QSettings::IniFormat); + bool distribId = settings.value("DoubleAuth").toBool(); + return distribId; +} + +void BiometricAuthWidget::setMinImage(float val) +{ + resize(400,100+100*val); + lblImage->setFixedSize(100*val, 100*val); +} diff --git a/BiometricAuth/biometricauthwidget.h b/BiometricAuth/biometricauthwidget.h new file mode 100644 index 0000000..e380abf --- /dev/null +++ b/BiometricAuth/biometricauthwidget.h @@ -0,0 +1,94 @@ +/** + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301, USA. +**/ +#ifndef BIOMETRICAUTHWIDGET_H +#define BIOMETRICAUTHWIDGET_H + +#include +#include "biometricproxy.h" + +class QLabel; +//class QDBusPendingCallWatcher; +//class BiometricProxy; +//class DeviceIdentityPtr; +class BiometricAuthWidget : public QWidget +{ + Q_OBJECT +public: + explicit BiometricAuthWidget(BiometricProxy *proxy, QWidget *parent = 0); + + /** + * @brief 进行生物识别认证 + * @param deviceInfo 使用的设备 + * @param uid 待认证的用户id + */ + void startAuth(DeviceInfoPtr device, int uid); + + /** + * @brief 终止生物识别认证 + */ + void stopAuth(); + + bool isAuthenticating() { return isInAuth; } + + void setMinImage(float val); + +protected: + void resizeEvent(QResizeEvent *event); + +Q_SIGNALS: + /** + * @brief 认证完成 + * @param result 认证结果 + */ + void authComplete(bool result); + +private Q_SLOTS: + void onIdentifyComplete(QDBusPendingCallWatcher *watcher); + void onStatusChanged(int drvid, int status); + void onFrameWritten(int drvid); + void onMoviePixmapUpdate(); + void startAuth_(); + +private: + void initUI(); + void updateImage(int type = 0); + void setImage(const QString &path); + bool getAuthDouble(); + +private: + QLabel *lblNotify; + QLabel *lblDevice; + QLabel *lblImage; + + BiometricProxy *proxy; + int uid; + QString userName; + DeviceInfoPtr device; + bool isInAuth; + QTimer *movieTimer; + int failedCount; + int timeoutCount; + bool beStopped; + QTimer *retrytimer; + bool usebind; + int fd = -1; + int dup_fd = -1; +}; + +#endif // BIOMETRICAUTHWIDGET_H diff --git a/BiometricAuth/biometricdeviceinfo.cpp b/BiometricAuth/biometricdeviceinfo.cpp new file mode 100644 index 0000000..368b1d0 --- /dev/null +++ b/BiometricAuth/biometricdeviceinfo.cpp @@ -0,0 +1,248 @@ +/** + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301, USA. +**/ +#include "biometricdeviceinfo.h" + +#include +#include +#include +//#include + +QString DeviceType::getDeviceType(int deviceType) +{ + if(deviceType >= __MAX_NR_TYPES) + { + return ""; + } + QMetaEnum meta = QMetaEnum::fromType(); + const char *typeString = meta.valueToKey(deviceType); + return QString(typeString); +} + +QString DeviceType::getDeviceType_tr(int deviceType) +{ + switch(deviceType) + { + case FingerPrint: + return tr("FingerPrint"); + case FingerVein: + return tr("FingerVein"); + case Iris: + return tr("Iris"); + case Face: + return tr("Face"); + case VoicePrint: + return tr("VoicePrint"); + case REMOTE_QRCODE_TYPE: + return tr("QRCode"); + default: + return ""; + } +} + +QDebug operator <<(QDebug stream, const DeviceInfo &deviceInfo) +{ + stream << "[" + << deviceInfo.id + << deviceInfo.shortName + << deviceInfo.fullName + << deviceInfo.deviceType + << deviceInfo.driverEnable + << deviceInfo.deviceNum + << "]"; + return stream; +} + +QDBusArgument &operator <<(QDBusArgument &arg, const DeviceInfo &deviceInfo) +{ + arg.beginStructure(); + arg << deviceInfo.id + << deviceInfo.shortName + << deviceInfo.fullName + << deviceInfo.driverEnable + << deviceInfo.deviceNum + << deviceInfo.deviceType + << deviceInfo.storageType + << deviceInfo.eigType + << deviceInfo.verifyType + << deviceInfo.identifyType + << deviceInfo.busType + << deviceInfo.deviceStatus + << deviceInfo.OpsStatus; + arg.endStructure(); + return arg; +} +const QDBusArgument &operator >>(const QDBusArgument &arg, DeviceInfo &deviceInfo) +{ + arg.beginStructure(); + arg >> deviceInfo.id + >> deviceInfo.shortName + >> deviceInfo.fullName + >> deviceInfo.driverEnable + >> deviceInfo.deviceNum + >> deviceInfo.deviceType + >> deviceInfo.storageType + >> deviceInfo.eigType + >> deviceInfo.verifyType + >> deviceInfo.identifyType + >> deviceInfo.busType + >> deviceInfo.deviceStatus + >> deviceInfo.OpsStatus; + arg.endStructure(); + return arg; +} + +void registerMetaType() +{ + qRegisterMetaType("DeviceInfo"); + qDBusRegisterMetaType(); +} + + +QString GetDefaultDevice(const QString &userName) +{ + //QString configPath = QString("/home/%1/" UKUI_BIOMETRIC_CONFIG_PATH).arg(userName); + QString configPath = QDir::homePath() + "/" + UKUI_BIOMETRIC_CONFIG_PATH; + QSettings settings(configPath, QSettings::IniFormat); + qDebug() << "configure path: " << settings.fileName(); + + QString defaultDevice = settings.value("DefaultDevice").toString(); + if(defaultDevice.isEmpty()) + { + QSettings sysSettings(UKUI_BIOMETRIC_SYS_CONFIG_PATH, QSettings::IniFormat); + defaultDevice = sysSettings.value("DefaultDevice").toString(); + } + + return defaultDevice; +} + +int GetLastDevice(const QString &userName) +{ + int nLastDevId = -1; + QSettings sysSettings(QString(SHARE_BIOMETRIC_CONFIG_PATH).arg(userName), QSettings::IniFormat); + sysSettings.beginGroup("Common"); + if (sysSettings.allKeys().contains("LastDeviceId")) { + nLastDevId = sysSettings.value("LastDeviceId").toInt(); + } + sysSettings.endGroup(); + return nLastDevId; +} + +void SetLastDevice(const QString &userName, int drvid) +{ + if (drvid < 0) { + return; + } + QString desConfPath = QString(SHARE_BIOMETRIC_CONFIG_PATH).arg(userName); + QFile fileConf(desConfPath); + if (fileConf.exists()) { + QSettings sysSettings(desConfPath, QSettings::IniFormat); + sysSettings.beginGroup("Common"); + sysSettings.setValue("LastDeviceId", drvid); + sysSettings.endGroup(); + } else { + QSettings sysSettings(desConfPath, QSettings::IniFormat); + sysSettings.beginGroup("Common"); + sysSettings.setValue("LastDeviceId", drvid); + sysSettings.endGroup(); + sysSettings.sync(); + QFile file(desConfPath); + file.setPermissions(QFile::WriteUser | QFile::ReadUser | QFile::WriteOther | QFile::ReadOther); + } +} + +static int getValueFromSettings(const QString &userName, const QString &key, int defaultValue = 3) +{ + //从家目录下的配置文件中获取 + //QString configPath = QString("/home/%1/" UKUI_BIOMETRIC_CONFIG_PATH).arg(userName); + QString configPath = QDir::homePath() + "/" + UKUI_BIOMETRIC_CONFIG_PATH; + QSettings settings(configPath, QSettings::IniFormat); + QString valueStr = settings.value(key).toString(); + + //如果没有获取到,则从系统配置文件中获取 + if(valueStr.isEmpty()) + { + QSettings sysSettings(UKUI_BIOMETRIC_SYS_CONFIG_PATH, QSettings::IniFormat); + valueStr = sysSettings.value(key).toString(); + } + + bool ok; + int value = valueStr.toInt(&ok); + if( (value == 0 && !ok) || valueStr.isEmpty() ) + { + value = defaultValue; + } + return value; +} + +bool GetHiddenSwitchButton() +{ + QSettings sysSettings(UKUI_BIOMETRIC_SYS_CONFIG_PATH, QSettings::IniFormat); + if(sysSettings.contains("HiddenSwitchButton")) + return sysSettings.value("HiddenSwitchButton").toBool(); + else + return false; +} + +int GetFailedTimes() +{ + QSettings sysSettings(UKUI_BIOMETRIC_SYS_CONFIG_PATH, QSettings::IniFormat); + if(sysSettings.contains("MaxFailedTimes")) + return sysSettings.value("MaxFailedTimes").toInt(); + else + return 3; +} + +bool GetAuthEnable() +{ + bool isEnable = false; + QSettings sysSettings(UKUI_BIOMETRIC_SYS_CONFIG_PATH, QSettings::IniFormat); + if(sysSettings.contains("EnableAuth")) + isEnable = sysSettings.value("EnableAuth").toBool(); + else + isEnable = false; + + if(isEnable && sysSettings.allKeys().contains("EnableAuthApp")){ + int isEnableApp = sysSettings.value("EnableAuthApp").toInt(); + isEnable = isEnableApp & (1<<1); + } + + return isEnable; +} + +bool GetQRCodeEnable() +{ + bool isEnable = false; + QSettings sysSettings(UKUI_BIOMETRIC_SYS_CONFIG_PATH, QSettings::IniFormat); + sysSettings.beginGroup("Functions"); + if (sysSettings.allKeys().contains("EnableQRCode")) { + isEnable = sysSettings.value("EnableQRCode").toBool(); + } + sysSettings.endGroup(); + return isEnable; +} + +int GetMaxFailedAutoRetry(const QString &userName) +{ + return getValueFromSettings(userName, "MaxFailedAutoRetry"); +} + +int GetMaxTimeoutAutoRetry(const QString &userName) +{ + return getValueFromSettings(userName, "MaxTimeoutAutoRetry"); +} diff --git a/BiometricAuth/biometricdeviceinfo.h b/BiometricAuth/biometricdeviceinfo.h new file mode 100644 index 0000000..c9604be --- /dev/null +++ b/BiometricAuth/biometricdeviceinfo.h @@ -0,0 +1,272 @@ +/** + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301, USA. +**/ +#ifndef BIOMETRICDEVICEINFO_H +#define BIOMETRICDEVICEINFO_H + +#include +#include + +#define BIOMETRIC_DBUS_SERVICE "org.ukui.Biometric" +#define BIOMETRIC_DBUS_PATH "/org/ukui/Biometric" +#define BIOMETRIC_DBUS_INTERFACE "org.ukui.Biometric" + +#define UKUI_BIOMETRIC_IMAGES_PATH "/usr/share/ukui-biometric/images/" +#define UKUI_BIOMETRIC_CONFIG_PATH ".biometric_auth/ukui_biometric.conf" +#define UKUI_BIOMETRIC_SYS_CONFIG_PATH "/etc/biometric-auth/ukui-biometric.conf" +#define SHARE_BIOMETRIC_CONFIG_PATH "/var/lib/lightdm-data/%1/ukui-biometric.conf" //greeter、screensaver、polkit share conf + +#define BIOMETRIC_PAM_DOUBLE "BIOMETRIC_PAM_DOUBLE" +#define BIOMETRIC_PAM "BIOMETRIC_PAM" +#define BIOMETRIC_PAM_QRCODE "BIOMETRIC_PAM_QRCODE" +#define BIOMETRIC_IGNORE "BIOMETRIC_IGNORE" +#define BIOMETRIC_SUCCESS "BIOMETRIC_SUCCESS" + +#define REMOTE_QRCODE_TYPE (8) + +/** + * @brief 设备类型 + */ +class DeviceType : public QObject +{ + Q_OBJECT +public: + DeviceType(); + enum Type { + FingerPrint, + FingerVein, + Iris, + Face, + VoicePrint, + __MAX_NR_TYPES + }; + Q_ENUM(Type) + /** + * @brief 获取设备类型的字符串表现形式 + * @param deviceType 设备类型 + * @return + */ + static QString getDeviceType(int deviceType); + + /** + * @brief 获取设备类型的国际化字符串 + * @param deviceType 设备类型 + * @return + */ + static QString getDeviceType_tr(int deviceType); +}; + +/** + * @brief StatusChanged D-Bus 信号触发时的状态变化类型 + */ +enum StatusType { + STATUS_DEVICE, + STATUS_OPERATION, + STATUS_NOTIFY +}; + +/** + * @brief 识别、终止操作等DBus调用的结果,即返回值里的 result + */ +enum DBusResult { + DBUS_RESULT_SUCCESS = 0, + DBUS_RESULT_NOTMATCH = -1, + DBUS_RESULT_ERROR = -2, + DBUS_RESULT_DEVICEBUSY = -3, + DBUS_RESULT_NOSUCHDEVICE = -4, + DBUS_RESULT_PERMISSIONDENIED = -5 +}; + +/** + * @brief 识别操作(Identify)的ops状态 + */ +/* 定义操作类型 */ +typedef enum { + OPS_TYPE_COMM = 0, + OPS_TYPE_OPEN, + OPS_TYPE_ENROLL, + OPS_TYPE_VERIFY, + OPS_TYPE_IDENTIFY, + OPS_TYPE_CAPTURE, + OPS_TYPE_SEARCH, + OPS_TYPE_CLEAN, + OPS_TYPE_GET_FLIST, + OPS_TYPE_RENAME, + OPS_TYPE_CLOSE, +}BioOpsType; + +/* + * 定义各种操作结果 + */ +typedef enum { + OPS_COMM_SUCCESS = OPS_TYPE_COMM * 100, /** 空闲状态 **/ + OPS_COMM_FAIL, /** 操作失败 **/ + OPS_COMM_NO_MATCH = OPS_COMM_FAIL, /** 不匹配 **/ + OPS_COMM_ERROR, /** 通用操作错误 **/ + OPS_COMM_STOP_BY_USER, /** 用户取消 **/ + OPS_COMM_TIMEOUT, /** 操作超时 **/ + OPS_COMM_OUT_OF_MEM, /** 无法分配内存 **/ + OPS_COMM_MAX, + + OPS_OPEN_SUCCESS = OPS_TYPE_OPEN * 100, /** 打开设备完成 **/ + OPS_OPEN_FAIL, /** 打开设备失败 **/ + OPS_OPEN_ERROR, /** 打开设备遇到错误 **/ + OPS_OPEN_MAX, + + OPS_ENROLL_SUCCESS = OPS_TYPE_ENROLL * 100, /** 录入信息成功 **/ + OPS_ENROLL_FAIL, /** 录入失败 **/ + OPS_ENROLL_ERROR, /** 录入过程中遇到错误 **/ + OPS_ENROLL_STOP_BY_USER, /** 录入被用户中断 **/ + OPS_ENROLL_TIMEOUT, /** 操作超时 **/ + OPS_ENROLL_MAX, + + OPS_VERIFY_MATCH = OPS_TYPE_VERIFY * 100, /** 认证匹配 **/ + OPS_VERIFY_NO_MATCH, /** 认证不匹配 **/ + OPS_VERIFY_ERROR, /** 认证过程中遇到错误 **/ + OPS_VERIFY_STOP_BY_USER, /** 认证被用户中断 **/ + OPS_VERIFY_TIMEOUT, /** 操作超时 **/ + OPS_VERIFY_MAX, + + OPS_IDENTIFY_MATCH = OPS_TYPE_IDENTIFY * 100, /** 识别到指定特征 **/ + OPS_IDENTIFY_NO_MATCH, /** 未识别出指定特征 **/ + OPS_IDENTIFY_ERROR, /** 识别过程中遇到错误 **/ + OPS_IDENTIFY_STOP_BY_USER, /** 识别被用户中断 **/ + OPS_IDENTIFY_TIMEOUT, /** 操作超时 **/ + OPS_IDENTIFY_MAX, + + OPS_CAPTURE_SUCCESS = OPS_TYPE_CAPTURE * 100, /** 捕获成功 **/ + OPS_CAPTURE_FAIL, /** 捕获失败 **/ + OPS_CAPTURE_ERROR, /** 捕获过程中遇到错误 **/ + OPS_CAPTURE_STOP_BY_USER, /** 捕获被用户中断 **/ + OPS_CAPTURE_TIMEOUT, /** 操作超时 **/ + OPS_CAPTURE_MAX, + + OPS_SEARCH_MATCH = OPS_TYPE_SEARCH * 100, /** 搜索到指定特征 **/ + OPS_SEARCH_NO_MATCH, /** 未搜索到指定特征 **/ + OPS_SEARCH_ERROR, /** 搜索过程中遇到错误 **/ + OPS_SEARCH_STOP_BY_USER, /** 搜索被用户中断 **/ + OPS_SEARCH_TIMEOUT, /** 操作超时 **/ + OPS_SEARCH_MAX, + + OPS_CLEAN_SUCCESS = OPS_TYPE_CLEAN * 100, /** 清理特征成功 **/ + OPS_CLEAN_FAIL, /** 清理失败 **/ + OPS_CLEAN_ERROR, /** 清理过程中遇到错误 **/ + OPS_CLEAN_STOP_BY_USER, /** 清理被用户中断 **/ + OPS_CLEAN_TIMEOUT, /** 操作超时 **/ + OPS_CLEAN_MAX, + + OPS_GET_FLIST_SUCCESS = OPS_TYPE_GET_FLIST * 100, /** 获取特征列表完成 **/ + OPS_GET_FLIST_FAIL, /** 获取特征列表失败 **/ + OPS_GET_FLIST_ERROR, /** 获取特征列表过程中遇到错误 **/ + OPS_GET_FLIST_STOP_BY_USER, /** 获取特征列表被用户中断 **/ + OPS_GET_FLIST_TIMEOUT, /** 获取特征列表超时 **/ + OPS_GET_FLIST_MAX, + + OPS_RENAME_SUCCESS = OPS_TYPE_RENAME * 100, /** 重命名特征完成 **/ + OPS_RENAME_FAIL, /** 重命名特征失败 **/ + OPS_RENAME_ERROR, /** 重命名特征过程中遇到错误 **/ + OPS_RENAME_STOP_BY_USER, /** 重命名特征被用户中断 **/ + OPS_RENAME_TIMEOUT, /** 重命名特征超时 **/ + OPS_RENAME_MAX, + + OPS_CLOSE_SUCCESS = OPS_TYPE_CLOSE * 100, /** 关闭设备完成 **/ + OPS_CLOSE_FAIL, /** 关闭设备失败 **/ + OPS_CLOSE_ERROR, /** 关闭设备过程中遇到错误 **/ + OPS_CLOSE_MAX, +}OpsResult; + +/** + * @brief 设备的信息 + */ +struct DeviceInfo +{ + int id; + QString shortName; + QString fullName; + int driverEnable; + int deviceNum; + int deviceType; + int storageType; + int eigType; + int verifyType; + int identifyType; + int busType; + int deviceStatus; + int OpsStatus; +}; + +class QDBusArgument; + +QDBusArgument &operator <<(QDBusArgument &arg, const DeviceInfo &deviceInfo); +const QDBusArgument &operator >>(const QDBusArgument &arg, DeviceInfo &deviceInfo); + +void registerMetaType(); + +typedef std::shared_ptr DeviceInfoPtr; +typedef QList DeviceList; +typedef QMap DeviceMap; + +QDebug operator <<(QDebug stream, const DeviceInfo &deviceInfo); + +Q_DECLARE_METATYPE(DeviceInfo) + +/** + * @brief 获取默认设备 + * @return + */ +QString GetDefaultDevice(const QString &userName); + +/** + * @brief 获取上次选择的设备 + * @return + */ +int GetLastDevice(const QString &userName); + +void SetLastDevice(const QString &userName, int drvid); + +/** + * @brief 获取失败后自动重新开始的最大次数 + * @param userName + * @return + */ +int GetMaxFailedAutoRetry(const QString &userName); + +/** + * @brief 获取超时后自动重新开始的最大次数 + * @param userName + * @return + */ +int GetMaxTimeoutAutoRetry(const QString &userName); +bool GetHiddenSwitchButton(); +int GetFailedTimes(); +bool GetAuthEnable(); +bool GetQRCodeEnable(); + +enum LOGINOPT_TYPE { + LOGINOPT_TYPE_PASSWORD = 0, // 密码 + LOGINOPT_TYPE_FACE, // 人脸 + LOGINOPT_TYPE_FINGERPRINT, // 指纹 + LOGINOPT_TYPE_IRIS, // 虹膜 + LOGINOPT_TYPE_VOICEPRINT, // 声纹 + LOGINOPT_TYPE_FINGERVEIN, // 指静脉 + LOGINOPT_TYPE_QRCODE, // 二维码 + LOGINOPT_TYPE_OTHERS, // 其他 + LOGINOPT_TYPE_COUNT +}; + +#endif // BIOMETRICDEVICEINFO_H diff --git a/BiometricAuth/biometricdeviceswidget.cpp b/BiometricAuth/biometricdeviceswidget.cpp new file mode 100644 index 0000000..856b750 --- /dev/null +++ b/BiometricAuth/biometricdeviceswidget.cpp @@ -0,0 +1,280 @@ +/** + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301, USA. +**/ +#include "biometricdeviceswidget.h" +#include +#include +#include +#include +#include +#include + + +BiometricDevicesWidget::BiometricDevicesWidget(BiometricProxy *proxy, int uid,QWidget *parent) + : QWidget(parent), + proxy(proxy), + m_uid(uid) +{ + initUI(); + if(proxy && proxy->isValid()) + { + connect(proxy, &BiometricProxy::USBDeviceHotPlug, + this, &BiometricDevicesWidget::onUSBDeviceHotPlug); + updateDevice(); + } + + resize(500, 500); +} + +void BiometricDevicesWidget::initUI() +{ + lblPrompt = new QLabel(this); + lblPrompt->setObjectName(QStringLiteral("lblBioetricDevicesPrompt")); + lblPrompt->setText(tr("Please select the biometric device")); + lblPrompt->setAlignment(Qt::AlignHCenter); + + lblDeviceType = new QLabel(this); + lblDeviceType->setObjectName(QStringLiteral("lblDeviceType")); + lblDeviceType->setText(tr("Device type:")); + lblDeviceType->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); + + QStyledItemDelegate* itemDelegate = new QStyledItemDelegate(); + + cmbDeviceType = new QComboBox(this); + cmbDeviceType->view()->parentWidget()->setWindowFlags(Qt::Popup|Qt::FramelessWindowHint); + cmbDeviceType->view()->parentWidget()->setAttribute(Qt::WA_TranslucentBackground); + cmbDeviceType->setObjectName(QStringLiteral("cmbDeviceType")); + cmbDeviceType->setMaxVisibleItems(5); + cmbDeviceType->setItemDelegate(itemDelegate); + connect(cmbDeviceType, SIGNAL(currentIndexChanged(int)), + this, SLOT(onCmbDeviceTypeCurrentIndexChanged(int))); + + lblDeviceName = new QLabel(this); + lblDeviceName->setObjectName(QStringLiteral("lblDeviceName")); + lblDeviceName->setText(tr("Device name:")); + lblDeviceName->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); + + cmbDeviceName = new QComboBox(this); + cmbDeviceName->view()->parentWidget()->setWindowFlags(Qt::Popup|Qt::FramelessWindowHint); + cmbDeviceName->view()->parentWidget()->setAttribute(Qt::WA_TranslucentBackground); + cmbDeviceName->setObjectName(QStringLiteral("cmbDeviceName")); + cmbDeviceName->setMaxVisibleItems(5); + cmbDeviceName->setItemDelegate(itemDelegate); + + btnOK = new QPushButton(tr("OK"), this); + btnOK->setObjectName(QStringLiteral("OKButton")); + btnOK->setCursor(Qt::PointingHandCursor); + connect(btnOK, &QPushButton::clicked, + this, &BiometricDevicesWidget::onOKButtonClicked); +} + +void BiometricDevicesWidget::setUser(int user) +{ + m_uid = user; +} + +void BiometricDevicesWidget::resizeEvent(QResizeEvent */*event*/) +{ + lblPrompt->setGeometry(0, 0, width(), 40); + lblDeviceType->setGeometry(100, lblPrompt->geometry().bottom() + 40, + 120, 20); + cmbDeviceType->setGeometry(100, lblDeviceType->geometry().bottom() + 15, + 300, 40); + lblDeviceName->setGeometry(100, cmbDeviceType->geometry().bottom() + 80, + 120, 20); + cmbDeviceName->setGeometry(100, lblDeviceName->geometry().bottom() + 15, + 300, 40); + btnOK->setGeometry(100, cmbDeviceName->geometry().bottom() + 80, 140, 38); + +} + +void BiometricDevicesWidget::updateDevice() +{ + deviceMap.clear(); + DeviceList deviceList = proxy->GetDevList(); + for(auto pDeviceInfo : deviceList) + { + qDebug() << *pDeviceInfo; + if(proxy->GetUserDevFeatureCount(m_uid,pDeviceInfo->id) > 0) + deviceMap[pDeviceInfo->deviceType].push_back(pDeviceInfo); + } + cmbDeviceType->clear(); + for(int type : deviceMap.keys()) + { + QString iconPath = QString(UKUI_BIOMETRIC_IMAGES_PATH"icon/%1.png") + .arg(DeviceType::getDeviceType(type)); + qDebug() << iconPath; + cmbDeviceType->addItem(QIcon(iconPath), DeviceType::getDeviceType_tr(type), type); + } + if(deviceMap.size() > 0) + { + int index = deviceMap.keys().at(0); + setCurrentDevice(deviceMap[index].at(0)); + } + +} + + +void BiometricDevicesWidget::setCurrentDevice(int drvid) +{ + DeviceInfoPtr pDeviceInfo = findDeviceById(drvid); + if(pDeviceInfo) + { + setCurrentDevice(pDeviceInfo); + } +} + +void BiometricDevicesWidget::setCurrentDevice(const QString &deviceName) +{ + DeviceInfoPtr pDeviceInfo = findDeviceByName(deviceName); + if(pDeviceInfo) + { + setCurrentDevice(pDeviceInfo); + } +} + +void BiometricDevicesWidget::setCurrentDevice(const DeviceInfoPtr &pDeviceInfo) +{ + this->currentDevice = pDeviceInfo; + cmbDeviceType->setCurrentText(DeviceType::getDeviceType_tr(pDeviceInfo->deviceType)); + cmbDeviceName->setCurrentText(pDeviceInfo->shortName); +} + +bool BiometricDevicesWidget::deviceExists(int drvid) +{ + return (findDeviceById(drvid) != nullptr); +} + +bool BiometricDevicesWidget::deviceExists(const QString &deviceName) +{ + return (findDeviceByName(deviceName) != nullptr); +} + +DeviceInfoPtr BiometricDevicesWidget::findDeviceById(int drvid) +{ + for(int type : deviceMap.keys()) + { + DeviceList &deviceList = deviceMap[type]; + auto iter = std::find_if(deviceList.begin(), deviceList.end(), + [&](DeviceInfoPtr ptr){ + return ptr->id == drvid; + }); + if(iter != deviceList.end()) + { + return *iter; + } + } + return DeviceInfoPtr(); +} + +DeviceInfoPtr BiometricDevicesWidget::findDeviceByName(const QString &name) +{ + for(int type : deviceMap.keys()) + { + DeviceList &deviceList = deviceMap[type]; + auto iter = std::find_if(deviceList.begin(), deviceList.end(), + [&](DeviceInfoPtr ptr){ + return ptr->shortName == name; + }); + if(iter != deviceList.end()) + { + return *iter; + } + } + return DeviceInfoPtr(); +} + +void BiometricDevicesWidget::onCmbDeviceTypeCurrentIndexChanged(int index) +{ + if(index < 0 || index >= deviceMap.keys().size()) + { + return; + } + int type = cmbDeviceType->itemData(index).toInt(); + cmbDeviceName->clear(); + for(auto &deviceInfo : deviceMap.value(type)) + { + cmbDeviceName->addItem(deviceInfo->shortName); + } +} + +void BiometricDevicesWidget::onOKButtonClicked() +{ + int type = cmbDeviceType->currentData().toInt(); + int index = cmbDeviceName->currentIndex(); + qDebug() << type << index; + DeviceInfoPtr deviceInfo = deviceMap.value(type).at(index); + Q_EMIT deviceChanged(deviceInfo); + hide(); +} + +void BiometricDevicesWidget::onUSBDeviceHotPlug(int drvid, int action, int /*devNum*/) +{ + int savedDeviceId = currentDevice->id; + int savedCount = 0; + for(int type : deviceMap.keys()) + savedCount += deviceMap.value(type).count(); + + switch(action) + { + case ACTION_ATTACHED: + { + //插入设备后,需要更新设备列表 + deviceMap.clear(); + updateDevice(); + setCurrentDevice(savedDeviceId); + break; + } + case ACTION_DETACHED: + { + DeviceInfoPtr pDeviceInfo = findDeviceById(drvid); + if(pDeviceInfo) + { + int type = pDeviceInfo->deviceType; + deviceMap[type].removeOne(pDeviceInfo); + int index = cmbDeviceName->findText(pDeviceInfo->shortName); + cmbDeviceName->removeItem(index); + + //如果该类型的设备全被移除,删除该类型相关的列表 + if(deviceMap[type].isEmpty()) + { + deviceMap.remove(type); + index = cmbDeviceType->findData(type); + cmbDeviceType->removeItem(index); + } + } + if(savedDeviceId != drvid) + { + setCurrentDevice(savedDeviceId); + } + break; + } + } + + int count = 0; + for(int type : deviceMap.keys()) + count += deviceMap.value(type).count(); + + //设备数量发生了变化 + if(count != savedCount) + { + Q_EMIT deviceCountChanged(count); + } +} + + diff --git a/BiometricAuth/biometricdeviceswidget.h b/BiometricAuth/biometricdeviceswidget.h new file mode 100644 index 0000000..d606aef --- /dev/null +++ b/BiometricAuth/biometricdeviceswidget.h @@ -0,0 +1,79 @@ +/** + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301, USA. +**/ +#ifndef BIOMETRICDEVICESWIDGET_H +#define BIOMETRICDEVICESWIDGET_H + +#include +#include "biometricproxy.h" + +class QLabel; +class QPushButton; +class QComboBox; + + +class BiometricDevicesWidget : public QWidget +{ + Q_OBJECT +public: + explicit BiometricDevicesWidget(BiometricProxy *proxy, int uid,QWidget *parent = nullptr); + void setCurrentDevice(int drvid); + void setCurrentDevice(const QString &deviceName); + void setCurrentDevice(const DeviceInfoPtr &pDeviceInfo); + DeviceInfoPtr findDeviceById(int drvid); + DeviceInfoPtr findDeviceByName(const QString &name); + bool deviceExists(int drvid); + bool deviceExists(const QString &deviceName); + void setUser(int user); + +protected: + void resizeEvent(QResizeEvent *event); + +Q_SIGNALS: + void deviceChanged(const DeviceInfoPtr &pDeviceInfo); + void deviceCountChanged(int newCount); + +public Q_SLOTS: + void onOKButtonClicked(); + +private Q_SLOTS: + void onCmbDeviceTypeCurrentIndexChanged(int index); + void onUSBDeviceHotPlug(int drvid, int action, int devNum); + +private: + void initUI(); + void updateDevice(); + +private: + typedef QMap QButtonMap; + + QLabel *lblPrompt; + QLabel *lblDeviceType; + QLabel *lblDeviceName; + QComboBox *cmbDeviceType; + QComboBox *cmbDeviceName; + QPushButton *btnOK; + QPushButton *btnCancel; + + BiometricProxy *proxy; + DeviceMap deviceMap; + DeviceInfoPtr currentDevice; + int m_uid; +}; + +#endif // BIOMETRICDEVICESWIDGET_H diff --git a/BiometricAuth/biometricproxy.cpp b/BiometricAuth/biometricproxy.cpp new file mode 100644 index 0000000..85637a4 --- /dev/null +++ b/BiometricAuth/biometricproxy.cpp @@ -0,0 +1,212 @@ +/** + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301, USA. +**/ +#include "biometricproxy.h" +#include + +BiometricProxy::BiometricProxy(QObject *parent) + : QDBusAbstractInterface(BIOMETRIC_DBUS_SERVICE, + BIOMETRIC_DBUS_PATH, + BIOMETRIC_DBUS_INTERFACE, + QDBusConnection::systemBus(), + parent) +{ + registerMetaType(); + setTimeout(2147483647); +} + +QDBusPendingCall BiometricProxy::Identify(int drvid, int uid, int indexStart, int indexEnd) +{ + QList argList; + argList << drvid << uid << indexStart << indexEnd; + return asyncCallWithArgumentList(QStringLiteral("Identify"), argList); +} + +int BiometricProxy::GetFeatureCount(int uid, int indexStart, int indexEnd) +{ + QDBusMessage result = call(QStringLiteral("GetDevList")); + if(result.type() == QDBusMessage::ErrorMessage) + { + qWarning() << "GetDevList error:" << result.errorMessage(); + return 0; + } + auto dbusArg = result.arguments().at(1).value(); + QList variantList; + dbusArg >> variantList; + int res = 0; + for(int i = 0; i < variantList.size(); i++) + { + + DeviceInfoPtr pDeviceInfo = std::make_shared(); + + auto arg = variantList.at(i).value(); + arg >> *pDeviceInfo; + + QDBusMessage FeatureResult = call(QStringLiteral("GetFeatureList"),pDeviceInfo->id,uid,indexStart,indexEnd); + if(FeatureResult.type() == QDBusMessage::ErrorMessage) + { + qWarning() << "GetFeatureList error:" << FeatureResult.errorMessage(); + return 0; + } + res += FeatureResult.arguments().takeFirst().toInt(); + } + return res; +} + +int BiometricProxy::StopOps(int drvid, int waiting) +{ + QDBusReply reply = call(QStringLiteral("StopOps"), drvid, waiting); + if(!reply.isValid()) + { + qWarning() << "StopOps error:" << reply.error(); + return -1; + } + return reply.value(); +} + +int BiometricProxy::GetUserDevCount(int uid) +{ + QDBusMessage result = call(QStringLiteral("GetDevList")); + if(result.type() == QDBusMessage::ErrorMessage) + { + qWarning() << "GetDevList error:" << result.errorMessage(); + return 0; + } + auto dbusArg = result.arguments().at(1).value(); + QList variantList; + DeviceList deviceList; + dbusArg >> variantList; + for(int i = 0; i < variantList.size(); i++) + { + DeviceInfoPtr pDeviceInfo = std::make_shared(); + + auto arg = variantList.at(i).value(); + arg >> *pDeviceInfo; + + int count = GetUserDevFeatureCount(uid,pDeviceInfo->id); + + if(count>0) + deviceList.push_back(pDeviceInfo); + } + + return deviceList.count(); +} + +int BiometricProxy::GetUserDevFeatureCount(int uid,int drvid) +{ + StopOps(drvid); + QDBusMessage FeatureResult = call(QStringLiteral("GetFeatureList"),drvid,uid,0,-1); + if(FeatureResult.type() == QDBusMessage::ErrorMessage) + { + qWarning() << "GetFeatureList error:" << FeatureResult.errorMessage(); + return 0; + } + return FeatureResult.arguments().takeFirst().toInt(); +} + +DeviceList BiometricProxy::GetDevList() +{ + QDBusMessage result = call(QStringLiteral("GetDevList")); + if(result.type() == QDBusMessage::ErrorMessage) + { + qWarning() << "GetDevList error:" << result.errorMessage(); + return DeviceList(); + } + auto dbusArg = result.arguments().at(1).value(); + QList variantList; + DeviceList deviceList; + dbusArg >> variantList; + for(int i = 0; i < variantList.size(); i++) + { + DeviceInfoPtr pDeviceInfo = std::make_shared(); + + auto arg = variantList.at(i).value(); + arg >> *pDeviceInfo; + + deviceList.push_back(pDeviceInfo); + + } + + return deviceList; +} + +int BiometricProxy::GetDevCount() +{ + QDBusMessage result = call(QStringLiteral("GetDevList")); + if(result.type() == QDBusMessage::ErrorMessage) + { + qWarning() << "GetDevList error:" << result.errorMessage(); + return 0; + } + int count = result.arguments().at(0).value(); + return count; +} + +QString BiometricProxy::GetDevMesg(int drvid) +{ + QDBusMessage result = call(QStringLiteral("GetDevMesg"), drvid); + if(result.type() == QDBusMessage::ErrorMessage) + { + qWarning() << "GetDevMesg error:" << result.errorMessage(); + return ""; + } + return result.arguments().at(0).toString(); +} + +QString BiometricProxy::GetNotifyMesg(int drvid) +{ + QDBusMessage result = call(QStringLiteral("GetNotifyMesg"), drvid); + if(result.type() == QDBusMessage::ErrorMessage) + { + qWarning() << "GetNotifyMesg error:" << result.errorMessage(); + return ""; + } + return result.arguments().at(0).toString(); +} + +QString BiometricProxy::GetOpsMesg(int drvid) +{ + QDBusMessage result = call(QStringLiteral("GetOpsMesg"), drvid); + if(result.type() == QDBusMessage::ErrorMessage) + { + qWarning() << "GetOpsMesg error:" << result.errorMessage(); + return ""; + } + return result.arguments().at(0).toString(); +} + +StatusReslut BiometricProxy::UpdateStatus(int drvid) +{ + StatusReslut status; + QDBusMessage result = call(QStringLiteral("UpdateStatus"), drvid); + if(result.type() == QDBusMessage::ErrorMessage) + { + qWarning() << "UpdateStatus error:" << result.errorMessage(); + status.result = -1; + return status; + } + + status.result = result.arguments().at(0).toInt(); + status.enable = result.arguments().at(1).toInt(); + status.devNum = result.arguments().at(2).toInt(); + status.devStatus = result.arguments().at(3).toInt(); + status.opsStatus = result.arguments().at(4).toInt(); + status.notifyMessageId = result.arguments().at(5).toInt(); + + return status; +} diff --git a/BiometricAuth/biometricproxy.h b/BiometricAuth/biometricproxy.h new file mode 100644 index 0000000..ac03881 --- /dev/null +++ b/BiometricAuth/biometricproxy.h @@ -0,0 +1,139 @@ +/** + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301, USA. +**/ +#ifndef BIOMETRICPROXY_H +#define BIOMETRICPROXY_H + +#include +#include +#include +#include "biometricdeviceinfo.h" + +/** + * @brief UpdateStauts调用返回的结果 + */ +struct StatusReslut +{ + int result; + int enable; + int devNum; + int devStatus; + int opsStatus; + int notifyMessageId; +}; + +/** + * @brief USB设备插拔动作 + */ +enum USBDeviceAction +{ + ACTION_ATTACHED = 1, + ACTION_DETACHED = -1 +}; + +/** + * @brief DBus代理类,负责调用对应的DBus接口 + */ +class BiometricProxy : public QDBusAbstractInterface +{ + Q_OBJECT +public: + explicit BiometricProxy(QObject *parent = nullptr); + +public Q_SLOTS: + /** + * @brief 使用指定id的设备进行用户认证 + * @param drvid 驱动(设备)id + * @param uid 用户id + * @param indexStart 用于认证的特征索引范围 + * @param indexEnd + * @return 结果: (结果,用户id) + */ + QDBusPendingCall Identify(int drvid, int uid, int indexStart = 0, int indexEnd = -1); + /** + * @brief 终止设备上正在进行的操作 + * @param drvid 设备id + * @param waiting 等待时间(秒) + * @return + */ + int StopOps(int drvid, int waiting = 3000); + /** + * @brief 获取当前用户已连接设备对应特征数目 + * @param uid 用户id + * @param indexStart 用于认证的特征索引范围 + * @param indexEnd + * @return + */ + int GetFeatureCount(int uid, int indexStart = 0, int indexEnd = -1); + /** + * @brief 获取已连接的设备列表 + * @return + */ + DeviceList GetDevList(); + /** + * @brief 获取设备数量 + * @return + */ + int GetDevCount(); + /** + * @brief 获取设备消息 + * @param drvid 驱动id + * @return + */ + QString GetDevMesg(int drvid); + /** + * @brief GetNotifyMesg 获取通知消息 + * @param drvid 驱动id + * @return + */ + QString GetNotifyMesg(int drvid); + /** + * @brief GetOpsMesg 获取操作消息 + * @param drvid 驱动id + * @return + */ + QString GetOpsMesg(int drvid); + /** + * @brief UpdateStatus 获取更新的设备状态 + * @param drvid 驱动id + * @return 结果: + */ + StatusReslut UpdateStatus(int drvid); + int GetUserDevCount(int uid); + int GetUserDevFeatureCount(int uid,int drvid); + + +Q_SIGNALS: + /** + * @brief 设备状态发生变化 + * @param drvid 设备id + * @param status 设备状态 + */ + void StatusChanged(int drvid, int status); + /** + * @brief USB设备热插拔 + * @param drvid 设备id + * @param action 插拔动作(1:插入,-1:拔出) + * @param deviceNum 插拔动作后该驱动拥有的设备数量 + */ + void USBDeviceHotPlug(int drvid, int action, int deviceNum); + void FrameWritten(int drvid); +}; + +#endif // BIOMETRICPROXY_H diff --git a/BiometricAuth/giodbus.cpp b/BiometricAuth/giodbus.cpp new file mode 100644 index 0000000..9e0490e --- /dev/null +++ b/BiometricAuth/giodbus.cpp @@ -0,0 +1,54 @@ +#include "giodbus.h" +#include +#include +#include + +int get_server_gvariant_stdout (int drvid) +{ + GDBusMessage *method_call_message; + GDBusMessage *method_reply_message; + GUnixFDList *fd_list; + GError **error = NULL; + + gint fd,dup_fd; + const gchar * response; + fd = -1; + dup_fd = -1; + method_call_message = NULL; + method_reply_message = NULL; + GDBusConnection *con = g_bus_get_sync(G_BUS_TYPE_SYSTEM,NULL,NULL); + method_call_message = g_dbus_message_new_method_call ("org.ukui.Biometric", + "/org/ukui/Biometric", + "org.ukui.Biometric", + "GetFrameFd"); + + g_dbus_message_set_body (method_call_message, g_variant_new ("(i)", drvid)); + method_reply_message = g_dbus_connection_send_message_with_reply_sync (con, + method_call_message, + G_DBUS_SEND_MESSAGE_FLAGS_NONE, + -1, + NULL, /* out_serial */ + NULL, /* cancellable */ + error); + if (method_reply_message == NULL) + goto out; + + if (g_dbus_message_get_message_type (method_reply_message) == G_DBUS_MESSAGE_TYPE_ERROR) + { + g_dbus_message_to_gerror (method_reply_message, error); + goto out; + } + g_print("%s",g_dbus_message_print(method_reply_message,0)); + + fd_list = g_dbus_message_get_unix_fd_list(method_reply_message); + fd = g_unix_fd_list_get(fd_list,0,error); + g_print("get fd : %d\n", fd); + dup_fd = dup(fd); + g_print("dup fd : %d\n", dup_fd); + +out: + g_object_unref (method_call_message); + g_object_unref (method_reply_message); + return dup_fd; + +} diff --git a/BiometricAuth/giodbus.h b/BiometricAuth/giodbus.h new file mode 100644 index 0000000..13e0880 --- /dev/null +++ b/BiometricAuth/giodbus.h @@ -0,0 +1,6 @@ +#ifndef GIODBUS_H +#define GIODBUS_H + +int get_server_gvariant_stdout(int drvid); + +#endif diff --git a/BiometricAuth/main.cpp b/BiometricAuth/main.cpp new file mode 100644 index 0000000..6215ae1 --- /dev/null +++ b/BiometricAuth/main.cpp @@ -0,0 +1,45 @@ +/** + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301, USA. +**/ +#include +#include +#include "biometricproxy.h" +#include "biometricauthwidget.h" +#include "biometricdeviceswidget.h" + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + + BiometricProxy proxy; + BiometricAuthWidget biometricAuthWidget(&proxy); + biometricAuthWidget.hide(); + BiometricDevicesWidget biometricDeviceWidget(&proxy); + QObject::connect(&biometricDeviceWidget, &BiometricDevicesWidget::deviceChanged, + &a, [&](const DeviceInfoPtr &pDeviceInfo){ + biometricAuthWidget.startAuth(pDeviceInfo, 1000); + biometricAuthWidget.show(); + }); + QObject::connect(&biometricDeviceWidget, &BiometricDevicesWidget::deviceCountChanged, + &a, [&](int count){ + qDebug() << "device count changed: " << count; + }); + biometricDeviceWidget.show(); + + return a.exec(); +} diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..9e63621 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,67 @@ +cmake_minimum_required(VERSION 2.6) +project(ukui-screensaver) + +find_package(Qt5 COMPONENTS Core Widgets DBus X11Extras Xml Network Svg) +find_package(PkgConfig REQUIRED) +find_package(OpenCV REQUIRED) +find_package(PkgConfig) +pkg_check_modules(GIOUNIX2 REQUIRED gio-unix-2.0) +pkg_check_modules(GLIB2 REQUIRED glib-2.0 gio-2.0) + +# 是否是intel项目 +option (USE_INTEL "intel项目" OFF) +# 用于测试替换用的变量 +set(TEST_ON 1) +set(TEST_OF 0) +set(VAR "VAR_NEW") + +# 加入一个配置头文件,用于处理 CMake 对源码的设置 +configure_file ( + "${CMAKE_CURRENT_SOURCE_DIR}/config.h.in" + "${CMAKE_CURRENT_BINARY_DIR}/config.h" +) + +set(TS_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/i18n_ts/zh_CN.ts + ${CMAKE_CURRENT_SOURCE_DIR}/i18n_ts/es.ts + ${CMAKE_CURRENT_SOURCE_DIR}/i18n_ts/fr.ts + ${CMAKE_CURRENT_SOURCE_DIR}/i18n_ts/bo_CN.ts + ${CMAKE_CURRENT_SOURCE_DIR}/i18n_ts/pt.ts + ${CMAKE_CURRENT_SOURCE_DIR}/i18n_ts/ru.ts + ${CMAKE_CURRENT_SOURCE_DIR}/i18n_ts/tr.ts + ) +add_custom_command( + OUTPUT ${TS_FILES} + COMMAND lupdate src/ screensaver/ -ts ${TS_FILES} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) +add_custom_target( + i18n_ts + DEPENDS ${TS_FILES} + ) +add_compile_options(-fPIC) + +#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror") +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_INSTALL_PREFIX /usr) + +set(Debug ON) +if(Debug) + set(CMAKE_BUILD_TYPE "Debug") +endif() + +#add_subdirectory(BioAuth) +add_subdirectory(BiometricAuth) +add_subdirectory(VirtualKeyboard) +add_subdirectory(src) +add_subdirectory(i18n_ts) +add_subdirectory(set4kScale) +add_subdirectory(data) +add_subdirectory(screensaver) +add_subdirectory(screensaver-focus-helper) +add_subdirectory(Common) +add_subdirectory(KylinNM) + +add_dependencies(ukui-screensaver-dialog BiometricAuth VirtualKeyboard Common) +add_dependencies(ukui-screensaver-default Common) diff --git a/Common/CMakeLists.txt b/Common/CMakeLists.txt new file mode 100644 index 0000000..5390d95 --- /dev/null +++ b/Common/CMakeLists.txt @@ -0,0 +1,22 @@ +qt5_wrap_cpp(Common_SRC + autoresize.h + checkbutton.h + commonfunc.h + ) + +set(Common_SRC + ${Common_SRC} + autoresize.cpp + checkbutton.cpp + commonfunc.cpp + ) + +include_directories( + ${Qt5Core_INCLUDE_DIRS} + ${Qt5Widgets_INCLUDE_DIRS} + ${Qt5DBus_INCLUDE_DIRS} + ) + + +add_library(Common STATIC ${Common_SRC}) +target_link_libraries(Common Qt5::Core Qt5::DBus Qt5::Widgets) diff --git a/Common/autoresize.cpp b/Common/autoresize.cpp new file mode 100644 index 0000000..c5301e9 --- /dev/null +++ b/Common/autoresize.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ + +#include "autoresize.h" + +AutoResize::AutoResize(QWidget* obj,int baseWidth,int baseHeight): + obj(obj), + baseWidth(baseWidth), + baseHeight(baseHeight) +{ + +} + +AutoResize::~AutoResize(void) +{ +} + diff --git a/Common/autoresize.h b/Common/autoresize.h new file mode 100644 index 0000000..610a2f9 --- /dev/null +++ b/Common/autoresize.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ + +#ifndef AUTORESIZE_H +#define AUTORESIZE_H + +#include + +struct AutoResizeOriginalData +{ + QRect data_rect; + QFont data_font; +}; +class AutoResize +{ +public: + AutoResize(QWidget* obj,int baseWidth,int baseHeight); + ~AutoResize(void); +private: + QWidget *obj; + int baseWidth; + int baseHeight; + +}; + +#endif diff --git a/Common/checkbutton.cpp b/Common/checkbutton.cpp new file mode 100644 index 0000000..14ae327 --- /dev/null +++ b/Common/checkbutton.cpp @@ -0,0 +1,186 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * Copyright (C) 2019 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "checkbutton.h" + +checkButton::checkButton(QWidget *parent) + :QFrame(parent) +{ + this->setFixedSize(QSize(52, 24)); + checked = false; + + borderColorOff = QColor("#cccccc"); + + bgColorOff = QColor("#ffffff"); + bgColorOn = QColor("#0078d7"); + + sliderColorOff = QColor("#cccccc"); + sliderColorOn = QColor("#ffffff"); + + space = 4; + + + step = width() / 50; + startX = 0; + endX= 0; + + timer = new QTimer(this); + timer->setInterval(5); + connect(timer, SIGNAL(timeout()), this, SLOT(updatevalue())); + setWindowFlags(Qt::FramelessWindowHint); + setAttribute(Qt::WA_TranslucentBackground, true); + + +} + +void checkButton::paintEvent(QPaintEvent *){ + //启用反锯齿 + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing, true); + + drawBg(&painter); + drawSlider(&painter); +} + + +void checkButton::drawBg(QPainter *painter){ + painter->save(); + + if (!checked){ + painter->setPen(QColor(255,255,255,30)); + painter->setBrush(QColor(255,255,255,30)); + } + else{ + painter->setPen(Qt::NoPen); + painter->setBrush(bgColorOn); + } + + //circle in + QRect rect(0, 0, width(), height()); + //半径为高度的一半 + int radius = rect.height() / 2; + //圆的宽度为高度 + int circleWidth = rect.height(); + + QPainterPath path; + path.moveTo(radius, rect.left()); + path.arcTo(QRectF(rect.left(), rect.top(), circleWidth, circleWidth), 90, 180); + path.lineTo(rect.width() - radius, rect.height()); + path.arcTo(QRectF(rect.width() - rect.height(), rect.top(), circleWidth, circleWidth), 270, 180); + path.lineTo(radius, rect.top()); + + painter->drawPath(path); + + painter->restore(); + +} + + +void checkButton::drawSlider(QPainter *painter){ + painter->save(); + painter->setPen(Qt::NoPen); + + if (!checked){ + painter->setBrush(sliderColorOff); + } + else + painter->setBrush(sliderColorOn); + + //circle in + QRect rect(0, 0, width(), height()); + int sliderWidth = rect.height() - space * 2; + QRect sliderRect(startX + space, space, sliderWidth, sliderWidth); + painter->drawEllipse(sliderRect); + + painter->restore(); +} + +void checkButton::mousePressEvent(QMouseEvent *){ + checked = !checked; + emit checkedChanged(checked); + + step = width() / 50; + + if (checked){ + + endX = width() - height(); + } + else{ + endX = 0; + } + timer->start(); +} + +void checkButton::resizeEvent(QResizeEvent *){ + step = width() / 50; + + if (checked){ + startX = width() - height(); + } + else + startX = 0; + + update(); +} + +void checkButton::updatevalue(){ + if (checked) + if (startX < endX){ + startX = startX + step; + } + else{ + startX = endX; + timer->stop(); + } + else{ + if (startX > endX){ + startX = startX - step; + } + else{ + startX = endX; + timer->stop(); + } + } + update(); +} + + +void checkButton::setChecked(bool checked){ + if (this->checked != checked){ + this->checked = checked; + emit checkedChanged(checked); + update(); + } + + step = width() / 50; + + if (checked){ + endX = width() - height(); + } + else{ + endX = 0; + } + timer->start(); +} + +bool checkButton::isChecked(){ + return this->checked; +} + diff --git a/Common/checkbutton.h b/Common/checkbutton.h new file mode 100644 index 0000000..d6167c9 --- /dev/null +++ b/Common/checkbutton.h @@ -0,0 +1,73 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * Copyright (C) 2019 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef CHECKBUTTON_H +#define CHECKBUTTON_H +#include +#include +#include +#include +#include +#include + +class checkButton : public QFrame +{ + Q_OBJECT +public: + checkButton(QWidget *parent = 0); + void setChecked(bool checked); + + bool isChecked(); + +protected: + void mousePressEvent(QMouseEvent *); + void resizeEvent(QResizeEvent *); + void paintEvent(QPaintEvent *); + +private: + bool checked; + + QColor borderColorOff; + QColor bgColorOff; + QColor bgColorOn; + QColor sliderColorOff; + QColor sliderColorOn; + int space; //滑块离背景间隔 + int rectRadius; //圆角角度 + int step; //移动步长 + int startX; + int endX; + QTimer * timer; + + void drawBg(QPainter *painter); + void drawSlider(QPainter *painter); + + +private Q_SLOTS: + void updatevalue(); + + +Q_SIGNALS: + void checkedChanged(bool checked); + + +}; + +#endif // CHECKBUTTON_H diff --git a/Common/commonfunc.cpp b/Common/commonfunc.cpp new file mode 100644 index 0000000..150bd5c --- /dev/null +++ b/Common/commonfunc.cpp @@ -0,0 +1,77 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * Copyright (C) 2019 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include "commonfunc.h" + +bool ispicture(QString filepath) +{ + QFileInfo file(filepath); + if(file.exists() == false) + return false; + + QMimeDatabase db; + QMimeType mime = db.mimeTypeForFile(filepath); + return mime.name().startsWith("image/"); +} + +QString getSystemVersion() +{ + QSettings settings("/etc/lsb-release", QSettings::IniFormat); + QString release = settings.value("DISTRIB_RELEASE").toString(); + QString description = settings.value("DISTRIB_DESCRIPTION").toString(); + if(description.right(3) == "LTS") + release = release + " LTS"; + return release; +} + +QString getSystemDistrib() +{ + QSettings settings("/etc/lsb-release", QSettings::IniFormat); + QString distribId = settings.value("DISTRIB_ID").toString(); + return distribId; +} + +bool getUseFirstDevice() +{ + QSettings settings("/etc/biometric-auth/ukui-biometric.conf", QSettings::IniFormat); + return settings.value("UseFirstDevice").toBool(); +} + +commonFunc::commonFunc() +{ + +} + +QString ElideText(QFont font,int width,QString strInfo) +{ + QFontMetrics fontMetrics(font); + //如果当前字体下,字符串长度大于指定宽度 + if(fontMetrics.width(strInfo) > width) + { + strInfo= QFontMetrics(font).elidedText(strInfo, Qt::ElideRight, width); + } + return strInfo; +} + diff --git a/Common/commonfunc.h b/Common/commonfunc.h new file mode 100644 index 0000000..3e23bb3 --- /dev/null +++ b/Common/commonfunc.h @@ -0,0 +1,39 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * Copyright (C) 2019 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef COMMONFUNC_H +#define COMMONFUNC_H + +#include +#include + +bool ispicture(QString filepath); +QString getSystemVersion(); +QString getSystemDistrib(); +bool getUseFirstDevice(); +QString ElideText(QFont font,int width,QString strInfo); + +class commonFunc +{ +public: + commonFunc(); +}; + +#endif // COMMONFUNC_H diff --git a/KylinNM/CMakeLists.txt b/KylinNM/CMakeLists.txt new file mode 100644 index 0000000..f9577ea --- /dev/null +++ b/KylinNM/CMakeLists.txt @@ -0,0 +1,95 @@ +find_package(Qt5 COMPONENTS Core Widgets REQUIRED) +find_package(X11 REQUIRED) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) +#pkg_check_modules(X11 REQUIRED x11) + +qt5_wrap_ui(Kylin_NM_SRC + src/confform.ui + src/kylinnm.ui + src/oneconnform.ui + src/onelancform.ui + wireless-security/dlgconnhidwifi.ui + wireless-security/dlgconnhidwifisecfast.ui + wireless-security/dlgconnhidwifisectunneltls.ui + wireless-security/dlgconnhidwifisecpeap.ui + wireless-security/dlgconnhidwifisectls.ui + wireless-security/dlgconnhidwifisecleap.ui + wireless-security/dlgconnhidwifisecpwd.ui + wireless-security/dlgconnhidwifiwep.ui + wireless-security/dlgconnhidwifileap.ui + wireless-security/dlgconnhidwifiwpa.ui + hot-spot/dlghotspotcreate.ui + ) + +#qt5_wrap_cpp(Kylin_NM_SRC +# src/backthread.h +# src/confform.h +# src/ksimplenm.h +# src/kylin-dbus-interface.h +# src/kylin-network-interface.h +# src/loadingdiv.h +# src/kylinnm.h +# src/oneconnform.h +# src/onelancform.h +# src/switchbutton.h +# src/utils.h +# wireless-security/dlgconnhidwifi.h +# wireless-security/dlgconnhidwifisecfast.h +# wireless-security/dlgconnhidwifisectunneltls.h +# wireless-security/dlgconnhidwifisecpeap.h +# wireless-security/dlgconnhidwifisectls.h +# wireless-security/dlgconnhidwifisecleap.h +# wireless-security/dlgconnhidwifisecpwd.h +# wireless-security/dlgconnhidwifiwep.h +# wireless-security/dlgconnhidwifileap.h +# wireless-security/dlgconnhidwifiwpa.h +# wireless-security/kylinheadfile.h +# hot-spot/dlghotspotcreate.h +# ) + +qt5_add_resources(Kylin_NM_SRC + nmqrc.qrc + res.qrc) + +set(Kylin_NM_SRC + ${Kylin_NM_SRC} + src/backthread.cpp + src/confform.cpp + src/ksimplenm.cpp + src/kylin-dbus-interface.cpp + src/kylin-network-interface.c + src/loadingdiv.cpp + src/kylinnm.cpp + src/oneconnform.cpp + src/onelancform.cpp + src/switchbutton.cpp + src/utils.cpp + src/swipegesturerecognizer.cpp + wireless-security/dlgconnhidwifi.cpp + wireless-security/dlgconnhidwifisecfast.cpp + wireless-security/dlgconnhidwifisectunneltls.cpp + wireless-security/dlgconnhidwifisecpeap.cpp + wireless-security/dlgconnhidwifisectls.cpp + wireless-security/dlgconnhidwifisecleap.cpp + wireless-security/dlgconnhidwifisecpwd.cpp + wireless-security/dlgconnhidwifiwep.cpp + wireless-security/dlgconnhidwifileap.cpp + wireless-security/dlgconnhidwifiwpa.cpp + wireless-security/kylinheadfile.cpp + hot-spot/dlghotspotcreate.cpp + nmqrc.qrc + ) + +include_directories( + ${Qt5Core_INCLUDE_DIRS} + ${Qt5Widgets_INCLUDE_DIRS} + ${Qt5DBus_INCLUDE_DIRS} + ) + +find_package(KF5WindowSystem) + +add_library(Kylin-nm STATIC ${Kylin_NM_SRC}) +target_link_libraries(Kylin-nm Qt5::Core Qt5::Widgets KF5::WindowSystem Qt5::DBus Qt5::X11Extras) diff --git a/KylinNM/README.md b/KylinNM/README.md new file mode 100644 index 0000000..a59ec5e --- /dev/null +++ b/KylinNM/README.md @@ -0,0 +1,15 @@ +# NAME + kylin-nm - kylin network monitor used in ubuntu-kylin operation system + +# DESCRIPTION + kylin-nm is a Qt based applet and uses some interface provided by NetworkManager. + It provides a GUI for users to connect or disconnect wired or wireless network which managed by NetworkManager. + Users can also create new wired network and configure a old network. + By click button at left bottom in the main window, a network configure window of NetworkManager will show in the screen. + Users can get some information about network directly by clicking one item in the network list, these information shown in extension area. + +# BUILD KYLIN-NM + down the source sode + install dependency packages(see cntrol files in the debian directory) + execute debuild command in the root directory of project + execute sudo dpkg -i packagename.deb to install diff --git a/KylinNM/hot-spot/dlghotspotcreate.cpp b/KylinNM/hot-spot/dlghotspotcreate.cpp new file mode 100644 index 0000000..a64e249 --- /dev/null +++ b/KylinNM/hot-spot/dlghotspotcreate.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see setupUi(this); + + this->setWindowFlags(Qt::FramelessWindowHint); + this->setStyleSheet("background-color:white;"); + + ui->lbLeftup->setStyleSheet("QLabel{background-color:#266ab5;}"); + ui->lbLeftupIcon->setStyleSheet("QLabel{background-image:url(:/res/h/no-pwd-wifi.png);background-color:transparent;}"); + ui->lbLeftupTitle->setStyleSheet("QLabel{font-size:12px;color:#ffffff;background-color:transparent;}"); + ui->btnCancel->setStyleSheet("QPushButton{border:1px solid #aaaaaa;background-color:#f5f5f5;}" + "QPushButton:Hover{border:2px solid #629ee8;background-color:#eeeeee;}" + "QPushButton:Pressed{border:1px solid #aaaaaa;background-color:#d8d8d8;}"); + ui->btnOk->setStyleSheet("QPushButton{border:1px solid #aaaaaa;background-color:#f5f5f5;}" + "QPushButton:Hover{border:2px solid #629ee8;background-color:#eeeeee;}" + "QPushButton:Pressed{border:1px solid #aaaaaa;background-color:#d8d8d8;}"); + ui->checkBoxPwd->setStyleSheet("QCheckBox::indicator {width: 18px; height: 9px;}" + "QCheckBox::indicator:checked {image: url(:/res/h/show-pwd.png);}" + "QCheckBox::indicator:unchecked {image: url(:/res/h/hide-pwd.png);}"); + + ui->lbLeftupTitle->setText(tr("Create Hotspot")); //创建个人热点 + ui->lbNetName->setText(tr("Network name")); //网络名称: + ui->lbSecurity->setText(tr("Wi-Fi security")); //Wi-Fi 安全性: + ui->lbPassword->setText(tr("Password")); //密码: + ui->btnCancel->setText(tr("Cancel")); //取消 + ui->btnOk->setText(tr("Ok")); //确定 + ui->btnOk->setEnabled(false); + + ui->cbxSecurity->addItem(tr("None")); //无 + ui->cbxSecurity->addItem(tr("WPA & WPA2 Personal")); //WPA 及 WPA2 个人 + ui->cbxSecurity->setCurrentIndex(1); + connect(ui->cbxSecurity,SIGNAL(currentIndexChanged(QString)),this,SLOT(changeDialog())); +} + +DlgHotspotCreate::~DlgHotspotCreate() +{ + delete ui; +} + +void DlgHotspotCreate::mousePressEvent(QMouseEvent *event){ + if(event->button() == Qt::LeftButton){ + this->isPress = true; + this->winPos = this->pos(); + this->dragPos = event->globalPos(); + event->accept(); + } +} +void DlgHotspotCreate::mouseReleaseEvent(QMouseEvent *event){ + this->isPress = false; + this->setWindowOpacity(1); +} +void DlgHotspotCreate::mouseMoveEvent(QMouseEvent *event){ + if(this->isPress){ + this->move(this->winPos - (this->dragPos - event->globalPos())); + this->setWindowOpacity(0.9); + event->accept(); + } +} + +void DlgHotspotCreate::on_btnCancel_clicked() +{ + this->close(); + emit btnHotspotState(); +} + +void DlgHotspotCreate::on_btnOk_clicked() +{ + //nmcli device wifi hotspot [ifname ifname] [con-name name] [ssid SSID] [band {a | bg}] [channel channel] [password password] + //example: nmcli device wifi hotspot ifname wlan0 con-name MyHostspot ssid MyHostspotSSID password 12345678 + QString str; + if(ui->cbxSecurity->currentIndex() == 0 ){ + str = "nmcli device wifi hotspot ifname " + wirelessCardName + " con-name " + ui->leNetName->text() + " ssid " + ui->leNetName->text() + "SSID"; + }else{ + str = "nmcli device wifi hotspot ifname " + wirelessCardName + " con-name " + ui->leNetName->text() + " ssid " + ui->leNetName->text() + " password " + ui->lePassword->text(); + } + Utils::m_system(str.toUtf8().data()); +// int status = system(str.toUtf8().data()); +// if (status != 0){ syslog(LOG_ERR, "execute 'nmcli device wifi hotspot' in function 'on_btnOk_clicked' failed");} + + this->close(); + emit updateHotspotList(); +} + +void DlgHotspotCreate::on_checkBoxPwd_stateChanged(int arg1) +{ + if (arg1 == 0) { + ui->lePassword ->setEchoMode(QLineEdit::Password); + } else { + ui->lePassword->setEchoMode(QLineEdit::Normal); + } +} + +void DlgHotspotCreate::on_leNetName_textEdited(const QString &arg1) +{ + if(ui->cbxSecurity->currentIndex() == 0 ){ + if (ui->leNetName->text() == ""){ + ui->btnOk->setEnabled(false); + } else { + ui->btnOk->setEnabled(true); + } + }else{ + if (ui->leNetName->text() == "" || ui->lePassword->text().size() < 5){ + ui->btnOk->setEnabled(false); + } else { + ui->btnOk->setEnabled(true); + } + } +} + +void DlgHotspotCreate::on_lePassword_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == "" || ui->lePassword->text().size() < 5){ + ui->btnOk->setEnabled(false); + } else { + ui->btnOk->setEnabled(true); + } +} + +void DlgHotspotCreate::changeDialog() +{ + if(ui->cbxSecurity->currentIndex()==0){ + ui->lbPassword->setEnabled(false); + ui->lePassword->setEnabled(false); + ui->checkBoxPwd->setEnabled(false); + } else { + ui->lbPassword->setEnabled(true); + ui->lePassword->setEnabled(true); + ui->checkBoxPwd->setEnabled(true); + } +} diff --git a/KylinNM/hot-spot/dlghotspotcreate.h b/KylinNM/hot-spot/dlghotspotcreate.h new file mode 100644 index 0000000..11a884d --- /dev/null +++ b/KylinNM/hot-spot/dlghotspotcreate.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see + +#include +#include +#include + +namespace Ui { +class DlgHotspotCreate; +} + +class DlgHotspotCreate : public QDialog +{ + Q_OBJECT + +public: + explicit DlgHotspotCreate(QString wiFiCardName, QWidget *parent = nullptr); + ~DlgHotspotCreate(); + +public Q_SLOTS: + void changeDialog(); + +private Q_SLOTS: + void on_btnCancel_clicked(); + + void on_btnOk_clicked(); + + void on_checkBoxPwd_stateChanged(int arg1); + + void on_leNetName_textEdited(const QString &arg1); + + void on_lePassword_textEdited(const QString &arg1); + +private: + Ui::DlgHotspotCreate *ui; + + QString wirelessCardName; + + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + bool isPress; + QPoint winPos; + QPoint dragPos; + +Q_SIGNALS: + void updateHotspotList(); + void btnHotspotState(); +}; + +#endif // DLGHOTSPOTCREATE_H diff --git a/KylinNM/hot-spot/dlghotspotcreate.ui b/KylinNM/hot-spot/dlghotspotcreate.ui new file mode 100644 index 0000000..989108f --- /dev/null +++ b/KylinNM/hot-spot/dlghotspotcreate.ui @@ -0,0 +1,169 @@ + + + DlgHotspotCreate + + + + 0 + 0 + 432 + 250 + + + + Dialog + + + + + 0 + 0 + 120 + 32 + + + + + + + + + + 9 + 9 + 16 + 16 + + + + + + + + + + 34 + 6 + 80 + 20 + + + + + + + + + + 215 + 210 + 90 + 30 + + + + + + + + + + 315 + 210 + 90 + 30 + + + + + + + + + + 76 + 60 + 90 + 20 + + + + + + + + + + 175 + 55 + 200 + 32 + + + + + + + 76 + 105 + 90 + 20 + + + + + + + + + + 175 + 100 + 200 + 32 + + + + + + + 76 + 150 + 90 + 20 + + + + + + + + + + 175 + 145 + 200 + 32 + + + + QLineEdit::Password + + + + + + 350 + 157 + 18 + 9 + + + + + + + + + + diff --git a/KylinNM/kylin-nm.pri b/KylinNM/kylin-nm.pri new file mode 100644 index 0000000..e124796 --- /dev/null +++ b/KylinNM/kylin-nm.pri @@ -0,0 +1,70 @@ +SOURCES += \ + $$PWD/src/backthread.cpp \ + $$PWD/src/confform.cpp \ + $$PWD/src/ksimplenm.cpp \ + $$PWD/src/kylin-dbus-interface.cpp \ + $$PWD/src/kylin-network-interface.c \ + $$PWD/src/loadingdiv.cpp \ + $$PWD/src/kylinnm.cpp \ + $$PWD/src/oneconnform.cpp \ + $$PWD/src/onelancform.cpp \ + $$PWD/src/switchbutton.cpp \ + $$PWD/src/utils.cpp \ + $$PWD/wireless-security/dlgconnhidwifi.cpp \ + $$PWD/wireless-security/dlgconnhidwifisecfast.cpp \ + $$PWD/wireless-security/dlgconnhidwifisectunneltls.cpp \ + $$PWD/wireless-security/dlgconnhidwifisecpeap.cpp \ + $$PWD/wireless-security/dlgconnhidwifisectls.cpp \ + $$PWD/wireless-security/dlgconnhidwifisecleap.cpp \ + $$PWD/wireless-security/dlgconnhidwifisecpwd.cpp \ + $$PWD/wireless-security/dlgconnhidwifiwep.cpp \ + $$PWD/wireless-security/dlgconnhidwifileap.cpp \ + $$PWD/wireless-security/dlgconnhidwifiwpa.cpp \ + $$PWD/wireless-security/kylinheadfile.cpp \ + $$PWD/hot-spot/dlghotspotcreate.cpp + +HEADERS += \ + $$PWD/src/backthread.h \ + $$PWD/src/confform.h \ + $$PWD/src/ksimplenm.h \ + $$PWD/src/kylin-dbus-interface.h \ + $$PWD/src/kylin-network-interface.h \ + $$PWD/src/loadingdiv.h \ + $$PWD/src/kylinnm.h \ + $$PWD/src/oneconnform.h \ + $$PWD/src/onelancform.h \ + $$PWD/src/switchbutton.h \ + $$PWD/src/utils.h \ + $$PWD/wireless-security/dlgconnhidwifi.h \ + $$PWD/wireless-security/dlgconnhidwifisecfast.h \ + $$PWD/wireless-security/dlgconnhidwifisectunneltls.h \ + $$PWD/wireless-security/dlgconnhidwifisecpeap.h \ + $$PWD/wireless-security/dlgconnhidwifisectls.h \ + $$PWD/wireless-security/dlgconnhidwifisecleap.h \ + $$PWD/wireless-security/dlgconnhidwifisecpwd.h \ + $$PWD/wireless-security/dlgconnhidwifiwep.h \ + $$PWD/wireless-security/dlgconnhidwifileap.h \ + $$PWD/wireless-security/dlgconnhidwifiwpa.h \ + $$PWD/wireless-security/kylinheadfile.h \ + $$PWD/hot-spot/dlghotspotcreate.h + +FORMS += \ + $$PWD/src/confform.ui \ + $$PWD/src/kylinnm.ui \ + $$PWD/src/oneconnform.ui \ + $$PWD/src/onelancform.ui \ + $$PWD/wireless-security/dlgconnhidwifi.ui \ + $$PWD/wireless-security/dlgconnhidwifisecfast.ui \ + $$PWD/wireless-security/dlgconnhidwifisectunneltls.ui \ + $$PWD/wireless-security/dlgconnhidwifisecpeap.ui \ + $$PWD/wireless-security/dlgconnhidwifisectls.ui \ + $$PWD/wireless-security/dlgconnhidwifisecleap.ui \ + $$PWD/wireless-security/dlgconnhidwifisecpwd.ui \ + $$PWD/wireless-security/dlgconnhidwifiwep.ui \ + $$PWD/wireless-security/dlgconnhidwifileap.ui \ + $$PWD/wireless-security/dlgconnhidwifiwpa.ui \ + $$PWD/hot-spot/dlghotspotcreate.ui + +RESOURCES += \ + $$PWD/nmqrc.qrc + diff --git a/KylinNM/kylin-nm.pro b/KylinNM/kylin-nm.pro new file mode 100644 index 0000000..b8af2ba --- /dev/null +++ b/KylinNM/kylin-nm.pro @@ -0,0 +1,129 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2018-10-19T15:29:47 +# +#------------------------------------------------- + +QT += core gui x11extras dbus + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = kylin-nm +TEMPLATE = app +LANGUAGE = C++ + +CONFIG += c++11 + +# CONFIG += link_pkgconfig +# PKGCONFIG += gsettings-qt +LIBS += -L/usr/lib/ -lgsettings-qt -lX11 + +target.path = /usr/bin +target.source += $$TARGET +desktop.path = /etc/xdg/autostart/ +desktop.files = kylin-nm.desktop + +INSTALLS += target \ + desktop + +# The following define makes your compiler emit warnings if you use +# any feature of Qt which has been marked as deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if you use deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +# QMAKE_CXXFLAGS += -Wno-unused-parameter +QMAKE_CPPFLAGS *= $(shell dpkg-buildflags --get CPPFLAGS) +QMAKE_CFLAGS *= $(shell dpkg-buildflags --get CFLAGS) +QMAKE_CXXFLAGS *= $(shell dpkg-buildflags --get CXXFLAGS) +QMAKE_LFLAGS *= $(shell dpkg-buildflags --get LDFLAGS) + + +SOURCES += \ + src/backthread.cpp \ + src/confform.cpp \ + src/ksimplenm.cpp \ + src/kylin-dbus-interface.cpp \ + src/kylin-network-interface.c \ + src/loadingdiv.cpp \ + src/main.cpp \ + src/kylinnm.cpp \ + src/oneconnform.cpp \ + src/onelancform.cpp \ + src/switchbutton.cpp \ + src/utils.cpp \ + wireless-security/dlgconnhidwifi.cpp \ + wireless-security/dlgconnhidwifisecfast.cpp \ + wireless-security/dlgconnhidwifisectunneltls.cpp \ + wireless-security/dlgconnhidwifisecpeap.cpp \ + wireless-security/dlgconnhidwifisectls.cpp \ + wireless-security/dlgconnhidwifisecleap.cpp \ + wireless-security/dlgconnhidwifisecpwd.cpp \ + wireless-security/dlgconnhidwifiwep.cpp \ + wireless-security/dlgconnhidwifileap.cpp \ + wireless-security/dlgconnhidwifiwpa.cpp \ + wireless-security/kylinheadfile.cpp \ + hot-spot/dlghotspotcreate.cpp + + +HEADERS += \ + src/backthread.h \ + src/confform.h \ + src/ksimplenm.h \ + src/kylin-dbus-interface.h \ + src/kylin-network-interface.h \ + src/loadingdiv.h \ + src/kylinnm.h \ + src/oneconnform.h \ + src/onelancform.h \ + src/switchbutton.h \ + src/utils.h \ + wireless-security/dlgconnhidwifi.h \ + wireless-security/dlgconnhidwifisecfast.h \ + wireless-security/dlgconnhidwifisectunneltls.h \ + wireless-security/dlgconnhidwifisecpeap.h \ + wireless-security/dlgconnhidwifisectls.h \ + wireless-security/dlgconnhidwifisecleap.h \ + wireless-security/dlgconnhidwifisecpwd.h \ + wireless-security/dlgconnhidwifiwep.h \ + wireless-security/dlgconnhidwifileap.h \ + wireless-security/dlgconnhidwifiwpa.h \ + wireless-security/kylinheadfile.h \ + hot-spot/dlghotspotcreate.h + +FORMS += \ + src/confform.ui \ + src/kylinnm.ui \ + src/oneconnform.ui \ + src/onelancform.ui \ + wireless-security/dlgconnhidwifi.ui \ + wireless-security/dlgconnhidwifisecfast.ui \ + wireless-security/dlgconnhidwifisectunneltls.ui \ + wireless-security/dlgconnhidwifisecpeap.ui \ + wireless-security/dlgconnhidwifisectls.ui \ + wireless-security/dlgconnhidwifisecleap.ui \ + wireless-security/dlgconnhidwifisecpwd.ui \ + wireless-security/dlgconnhidwifiwep.ui \ + wireless-security/dlgconnhidwifileap.ui \ + wireless-security/dlgconnhidwifiwpa.ui \ + hot-spot/dlghotspotcreate.ui + +RESOURCES += \ + nmqrc.qrc + +unix { + UI_DIR = .ui + MOC_DIR = .moc + OBJECTS_DIR = .obj +} + +TRANSLATIONS = translations/kylin-nm_zh_CN.ts \ + translations/kylin-nm_tr.ts \ + translations/kylin-nm_bo.ts + +DISTFILES += diff --git a/KylinNM/nmqrc.qrc b/KylinNM/nmqrc.qrc new file mode 100644 index 0000000..f2349b6 --- /dev/null +++ b/KylinNM/nmqrc.qrc @@ -0,0 +1,102 @@ + + + res/s/conning-b/1.png + res/s/conning-b/2.png + res/s/conning-b/3.png + res/s/conning-b/4.png + res/s/conning-b/5.png + res/s/conning-b/6.png + res/s/conning-b/7.png + res/s/conning-b/8.png + res/s/conning-b/9.png + res/s/conning-b/10.png + res/s/conning-b/11.png + res/s/conning-b/12.png + res/g/down_arrow.png + res/g/checkbox-checked.svg + res/g/checkbox-unchecked.svg + res/s/rescan/1.png + res/s/rescan/2.png + res/s/rescan/3.png + res/s/rescan/4.png + res/s/rescan/5.png + res/s/rescan/6.png + res/s/rescan/7.png + res/s/rescan/8.png + res/s/rescan/9.png + res/s/rescan/10.png + res/s/rescan/11.png + res/s/rescan/12.png + res/h/hide-pwd.png + res/h/right-pwd.png + res/h/show-pwd.png + res/h/no-pwd-wifi.png + res/x/fly-mode-off.svg + res/x/fly-mode-on.svg + res/x/hot-spot-off.svg + res/x/hot-spot-on.svg + res/x/net-list-bg.svg + res/x/wifi-list-bg.svg + res/x/load-down.png + res/x/load-up.png + res/l/network-offline.png + res/l/network-offline.svg + res/l/network-online.png + res/l/network-online.svg + res/w/wifi-full.png + res/w/wifi-full-pwd.png + res/w/wifi-high.png + res/w/wifi-high-pwd.png + res/w/wifi-low.png + res/w/wifi-low-pwd.png + res/w/wifi-medium.png + res/w/wifi-medium-pwd.png + res/w/wifi-none.png + res/w/wifi-none-pwd.png + res/s/conning-a/1.png + res/s/conning-a/2.png + res/s/conning-a/3.png + res/s/conning-a/4.png + res/s/conning-a/5.png + res/s/conning-a/6.png + res/s/conning-a/7.png + res/s/conning-a/8.png + qss/style.qss + res/g/close_black.png + res/g/close_white.png + res/s/conning-s/1.png + res/s/conning-s/2.png + res/s/conning-s/3.png + res/s/conning-s/4.png + res/s/conning-s/5.png + res/s/conning-s/6.png + res/s/conning-s/7.png + res/s/conning-s/8.png + res/s/conning-s/9.png + res/s/conning-s/10.png + res/s/conning-s/11.png + res/s/conning-s/12.png + res/x/setup.png + res/x/pb-wifi-n.png + res/x/pb-wifi-y.png + res/x/pb-conn-dis.png + res/l/pb-network-online.png + res/l/pb-network-info.png + res/l/pb-network-offline.png + res/x/pb-newConn.png + res/x/pb-close.png + res/l/pb-top-network-offline.png + res/w/pb-all-wifi-offline.png + res/w/pb-top-wifi-offline.png + res/w/wifi-full-off.png + res/w/wifi-high-off.png + res/w/wifi-low-off.png + res/w/wifi-medium-off.png + res/w/wifi-low-pwd-off.png + res/w/wifi-full-pwd-off.png + res/w/wifi-high-pwd-off.png + res/w/wifi-medium-pwd-off.png + res/w/wifi-none-off.png + res/w/wifi-none-pwd-off.png + + diff --git a/KylinNM/qss/style.qss b/KylinNM/qss/style.qss new file mode 100644 index 0000000..741e632 --- /dev/null +++ b/KylinNM/qss/style.qss @@ -0,0 +1,10 @@ +QScrollBar:vertical{margin:0px 2px 0px 2px;width:10px;background:rgba(48,48,51,0);border-radius:6px;} +QScrollBar::up-arrow:vertical{height:0px;} +QScrollBar::sub-line:vertical{border:0px solid;height:0px} +QScrollBar::sub-page:vertical{background:transparent;} +QScrollBar::handle:vertical{width:6px;background:rgba(72,72,76,1);border-radius:3px;} +QScrollBar::handle:vertical:hover{width:6px;background:rgba(97,97,102,1);border-radius:3px;} +QScrollBar::handle:vertical:pressed{width:6px;background:rgba(133,133,140,1);border-radius:3px;} +QScrollBar::add-page:vertical{background:transparent;} +QScrollBar::add-line:vertical{border:0px solid;height:0px} +QScrollBar::down-arrow:vertical{height:0px;} diff --git a/KylinNM/res.qrc b/KylinNM/res.qrc new file mode 100644 index 0000000..7646d2b --- /dev/null +++ b/KylinNM/res.qrc @@ -0,0 +1 @@ + diff --git a/KylinNM/res/g/checkbox-checked.svg b/KylinNM/res/g/checkbox-checked.svg new file mode 100644 index 0000000..b9ee316 --- /dev/null +++ b/KylinNM/res/g/checkbox-checked.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/KylinNM/res/g/checkbox-unchecked.svg b/KylinNM/res/g/checkbox-unchecked.svg new file mode 100644 index 0000000..0df442d --- /dev/null +++ b/KylinNM/res/g/checkbox-unchecked.svg @@ -0,0 +1,3 @@ + + + diff --git a/KylinNM/res/g/close_black.png b/KylinNM/res/g/close_black.png new file mode 100644 index 0000000000000000000000000000000000000000..237e555213079ed8a44c34bd702e94bf8999d310 GIT binary patch literal 205 zcmeAS@N?(olHy`uVBq!ia0vp^av;pX1|+Qw)-3{3Q#@T9Lp(a)PCm$aK!L|~|6JX7 ziRzoHU*w4$Z02oKd$3fMziQ>d^he@-k1hVUc1Q^>y(SeFAOG6)7Nbm?=Uj%O9>b5V zANBiZ?R^wdhxDIGZJktT*?6`2Rt#2l<1Kr2q>FVdQ&MBb@ E0JbGncmMzZ literal 0 HcmV?d00001 diff --git a/KylinNM/res/g/close_white.png b/KylinNM/res/g/close_white.png new file mode 100644 index 0000000000000000000000000000000000000000..0943a4bb87ef3488385d4707b140c5a951fc95c1 GIT binary patch literal 152 zcmeAS@N?(olHy`uVBq!ia0vp^av;pX1|+Qw)-3{3@t!V@As(G?FFSHI81T4UJUF@P zzjseZNRQ$LS0}HT>~|wx=r43~Nr|tSqawJby8k7!1Uvi3^7TqGN6&6w^}+s$UXbL! zV|QPq%l-`axn}!n+VQL3tX%_z{rS5))-WDm4HDBb@?X&K7iblOr>mdKI;Vst0D5XS AbpQYW literal 0 HcmV?d00001 diff --git a/KylinNM/res/g/down_arrow.png b/KylinNM/res/g/down_arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..c7a343b36b90adec3fa8dbba3bc48ff71c181568 GIT binary patch literal 113 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)Q!3HEdXMMW?q%1sL978x}#`bvfF(~jbrT_YB zC&Pa&;|WvZ=G}{Du`o=%s41GDE*_})V9h7C2Gh3ZiCs)rik}|Wxx80o=2z)yeM}Q)7{6;vDQqZnMg~r~97{yydLQ)bE))v1LL)_jHC9NF>)m^BLVM@+T#IoT z=v>fbMx9xdY*|hC!;iR#II=_|Y}N_?xJ5UH#7RQ3B`#U~SR%{Rxwk-_e;D87a_{rp z^Lw7(^K}QB8td|Mi*gYJ$@ABHo8fyId~z&i1VQVk8csl=Q1^xO&5ESQc?}@dq7nv} zKf-r_X26R*J10N|f*3!NgCRW>Xy61TV&&5|)en#^?}lckPWv3mcGnbHyry?Kat)i@vSrJl;oKjF z0m*#v@w-ipEBlB05@`PR?AGjK!R&$eEMgnd^4Ht-Gw48Y$>68oPWCkzicaT<*U^VM zcIEq2O3Y5x{rl`mK~`$z@t#DY@X%`aV{`x9^i*h*Y2D4NmWf+;@5(zAC0=|gQR(R& zLRQ^)^Oyyl%{@Hf$tv%iykxljyXOv?YVG~IFn=RDv^4l@4tB%D-LsDi>YC~g&b%== zg6>@V}O-D&9mpkeiS{PeFmAlJ zZ%i#Pg}1lY7a2>`b>m3;9cpaWe9cE8Kb7SUO2|-Qeth=9^tR;LAB^{cnAx{!4YFcv W=xE!cJr~pePrt9x`&CW*-v0n!l=BGy literal 0 HcmV?d00001 diff --git a/KylinNM/res/h/no-pwd-wifi.png b/KylinNM/res/h/no-pwd-wifi.png new file mode 100644 index 0000000000000000000000000000000000000000..dbec54a3af0844bb65373f4cf2204601d77d81ea GIT binary patch literal 1643 zcmV-x29)`UP) zaB^>EX>4U6ba`-PAZ2)IW&i+q+P#)xlG`8*h5zdmJpz&d!EvxLGrd8N--k_{#L1@X zWT$3gu!1G=K0!jupWlss;S$G*e6SRAj1HI2K1;zMx$W9|c7DB&2X`6V*=hd*Zk?jn z?J+K6et|t)Uob!H)4iQ+9)8kr#Jl75(jK{w>qv4qQrYZ0o+*zPO1xRl_{com4(G>b zZxEvf_){TSl+~?w*&->j(5wahb{FXu#WwYF@lD?SqBiepiirK}@NoFJ#JuSGP&Ctz%A z%V^0JH-lQmQYUf~9x@9QZ|u2p;l|6_&pabVFN+v@ky&)LB{j)1uTXq%+{6fEswS|S zK~7j0Mb!AI=3gc2%* z?c+lTF<3|;hY~gV7-EbT`#Dui&0DZ&X34UZq{*j{VzQJ{P9;lL9AklU%$8HmxfBE~ zzQ9+(`~t%ylTLmLQ=Du{Q=ZC<@|pb{<~ZA&<~)}wRV$7FsK)i5|+5w zl9s%bhFWXddP5TxaI8D5(ZPmZ8I`+=+ppUsyupo!yn(m)r>Ip5-Rb$Wcf46Xd9)yCL_; z?FqG7*5g)JKz$cNQ=E{B?S~0VDw0b7U3#mhcVGOzqc)vNlAiiVr}JtweJalA=|zxq zX1Vke%j)jwvt?bw(mS-CWliT{Ki7_ah8e`Z@&6>N?~|j?!9Uc|H^9HDqj&4(_!Yff zh6lIwUip1pM;|TgADi|?J~!?zR(fBR<6e7RhEHGmDx>FS@^kM<0dOCdq zeYk0V;#cI~+5FGL|9jayX!OAuaC*~zEjIq`WcmwyL(y25*2Dn-00D$)LqkwWLqi~N za&Km7Y-IodD3N`UJxIeq9K~N#OI0ckb`WvMP@OD@Uv$(e6rn<>6nNgNw7S4z7YA_yOYL>ZIr*CH}7|w21NGxF7HCJ?`ECLcPp1t7{z4blXfN z;$kMdDh6NC0|5a{BPKJ;n3JR=Jjd5Pe0;r&@+|Lje~vyiXEDGh63;Tjw23!}XEtqv z^FDEy6=jw9oOsNn3lcwaU2*x1bD_xs&kURC)I4#RSS)m~(!s1~YQ$5-5mnPEU&y$u za^B*sm8-0EPyWJCPG4E(I?Z7uu!to{5TT%o63Va9w{N zIRg5(fs5<5rtATiJHX(RA)9hj^3xRZdEotwz9|a~+ycF8Zf~u9oIU_)>MD5y92^3p z1VyY4_ zG}IO#3mAduf=4IUTd`Pl%Eww>c8PtZW;EhbD_W8cMi5u-Ale;FtFtvVvvs^09$L9w zt5{N&6eWE~IvLIj39#EzkergG=wSQac5RVNZUU3@hi3_jAEEg=Kz?;A9N!Jh=4bLF ple9dP_YlGl&`J7rwY>V9^#`z*R8XiZJfHvo002ovPDHLkV1n_*0C)fZ literal 0 HcmV?d00001 diff --git a/KylinNM/res/h/right-pwd.png b/KylinNM/res/h/right-pwd.png new file mode 100644 index 0000000000000000000000000000000000000000..1bbc00d18e3edae496bed96128b6fef1e1ff046f GIT binary patch literal 1398 zcmaJ>ZD<>19KWV9%OdHz&ds)?dR(Vwy34)fa=p7X_VU&)YTB3vTSQCmRDDz3%)O{#BIPQ6# z`~Cj^-~a779_#LMI+`5_f;c0GLUH)5tGsp_g4CpXTDst-$qe_Ky?WBjO9nvvsxAXG zl97f$97yW;=$D`!L26%5C;H9)=re+%XWUW+<1S>fkc}Yi9fho<3@0j?{@ z1ct6b%wZq4E~-BoLxZ{jP_LWC70Sb*ZM>WE@*K?^KxvX<36du$1}7?&5cz!Go%gtPV~C)5o+n6}plKW;@Z7j&N(EfYwN@>JKu$5z zSu?F`XvLx=>tm)5gG$#!$Yi6@O~YDlBT+DAL_x|Dl$#_nnMz!1=$sh`|I64ColA^o z0TBl|eaui`J(8_eGR)n*9aVr(jnHGHVNs-!kgkknfM!NQJ`DbGt7%nWNS;(Bki->` zq;X1OXk6ks1y8bV3=i65hGp2QovrwRFdN`J92cfonxev-KM)RdvSF6>!fG%cf0Y~2 za;Bsypz1pfeQ$8v?#UH`29QkMNa*@VwF_b?-PCg_J&Oi|m8!R)uD-OU>iOLMN}1N9 z4gn+m5>Pu0J%g_0Sx9f8!OO~On?&-stgsvoJdA{MBuT>flO8}OWs>od*aly{D}U>K z3Qu4h*{U2tp-9?Gl3rYqR1T*oz~MYE%Q(+a6zgSaPNEfzfD(yH>)h2!)g#!SmF4DO zz|H1p0S(TR0SD*GyYsu@?3N-Sf1>b{e49^QM63_ah}QZ=i?zNrcG`Jq-g3NbId`9k z6ho&brUaY)kD*E9(Mh%VaXmWGIQRZPh}^Vio~}a&maNxK*Nj?AyDa6Wi>po%S-Ra` zb6paVTJ*VOvHA1jzR`x#H^o)qQX`Lkf9$D!fk(uv6ogIfu333iT(Fg2B)>R1<+QE5 zEG`^VnPn%c_@Do_HL~n)`N1}Qplf=tRzBidY*ZI^x^B$Ab`Tju?LQ+C%_38%CQc(i z-u%jXvQS$-I5D%g^jPh=$Lr5q?Bq;edIfwtvV7fY%9Izm+mh&iJ$EGE&43_ zsiUmL_I|gt-F5j5+pkZYkkH?6*yN6se8DCz-D)UZEUsdSzaBdAX0!N8-5J-zrH?GP zT3ko!T=8PRqjV?mnK%$rCp&v~B(m6*j@ib)?Pbrz^3KRE`-ev@+uyS7n8aF6jWo|) z{$<;WCl&dVVg}9(?r(S?{z2?*@%q1X{T<5+5!0FAHC38(AQw*dwpfD9)yhp633rFi I1_mep0|fTVc>n+a literal 0 HcmV?d00001 diff --git a/KylinNM/res/h/show-pwd.png b/KylinNM/res/h/show-pwd.png new file mode 100644 index 0000000000000000000000000000000000000000..08c4de5105349165d8f21927d596272a8fbdbdee GIT binary patch literal 1289 zcmaJ>Z)h837{4^3?NV#wl;PG1FV{LO>~i;B?k;z?X1SWAHnOxOOIZ;+dVk(DM|*ea z-E~QevbOF+gyJ8DP{%$DWatN(AN0dO#7*--ncq}!Q$#Wt4Ag?)5G>3$ZBqB4?BKZf zdG7f=&+qy7zBxJmQg8T3m>`JW>{vRFzkT>U(A`N8flIZ~n^-vPXQutrPSLNZE+R$@ zM?)ZMsdFfgRAYJZTQp1%!3$<#+Mmvyl6A+5s!f|{)hc5&K@2CWWmTU?KG4vdX(!07 z&%Y#rX(Y%qe2&SLQ>bK)t+?p)%6LIvnb#$QOr8Y8RT(?5kgtNOwP1U4H9_w9mGQZ$ zrb)14;?F0@T~gDzNsw|}1o)^x=`bdOxD}hKDQrP`%eJppDcc)pdq^Wscg?bI zIyPu}R5fSGPmq}DZVFa8m)mn}d#ys@lF?PQOv5NcTUIl#9c$0eqyK5_YwZ=5%ZSb+ z&slPH+>hcw8;ooB_&`lV%toGYP23c9A?@f(7P9?pIzi$;QNuK3j*%EcMMX+SMV5jp z$5N^!>QqsPa}tVcoWKcfKl|ZD$i#VxWiwDq57rroU5$TiO) zW7Ksluv2H*+=qi?@Tz1Wfr{}Oq7dR@lon@oN{I1Nj2BeB$i>MP-gu;byK{;)IFE9h zj;uq5}_1dyW5{lj})qRwBNV3=7{xwPdzb7M3fEXsebT!DB2NGz`J*qI|pmatN-*U z1pJB%EFK>l>M2~6BM&g=)A z8?S-4h{k)$W#S-l`@;kE8RgZ!`uWxCPu5P1G{6TJ*RF?Z$snX74@25H@Mqxu<(~Q< z%E!Hb9sOYqT)emLpY3YA49*hu@04GF4h3>b}^-3@?r{{Y!v z$O0q;zDQ|1-1TXqtIf(=>KJ&QT@_#&=qMOM_#V+y5G4u-RFVQk{-fzDTRKNWe$ViC~6KcEngXjg#rH;trzY@X%fYf@h59JgX{y%zv zXoRBjiE{ujloWsz$WQ@faS=(`co6;O`QwGi;=82z;`x{u6+o7;G5U|8HJJaRjkx02KfE#74ndNnQhK7A66zqycgO)pG%vj%WZ<^>~yt wvMpHEz(~tt78(-uXa(H`sO1|#9Do1=00VlOW>+6u+W-In07*qoM6N<$f(T{U5dZ)H literal 0 HcmV?d00001 diff --git a/KylinNM/res/l/network-offline.svg b/KylinNM/res/l/network-offline.svg new file mode 100644 index 0000000..ca9dcea --- /dev/null +++ b/KylinNM/res/l/network-offline.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + diff --git a/KylinNM/res/l/network-online.png b/KylinNM/res/l/network-online.png new file mode 100644 index 0000000000000000000000000000000000000000..b8c5fe4a9e8f615f3797004ea6ac57d7e04df539 GIT binary patch literal 406 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?3oArNM~bhqvg0|TRn zr;B4qL~HV&|Nrd|J1{L{+*)g3(Qxs^&ASiml`ku@xw*ORx&@T1KlcCsn+ORB36_6F zpEk$!&OGes|9k87C4Ulrev3;m;q9?XxWc%YPong|3T8K1gI5dz%`J^TkE@4m{hBK8 zdG*YR69Q$fITMR&-d*upTEWiolOv!9(acFqW=6l^PM)OaqMsi z+-$RP-E)SHdG|veIjoa(u{?0&sB=SOqax##Iu|kViBniR4fj<3t6-cpjh}~yhx^>S zn;#pK*d2814*-Gxg{3PPrYsZTuw;6}5D}2XJpbNav5JDni|pEkjG6oGxDU_%YTsGb z*wpsV)lM;R4j+T7EccYV4c-^;@2^(@`f=L-s$cyN|4LsH`VADZ0*b^v2zJhq;z&qH w&`>y8#@5D}{{77j2ak&x&r1?yfmSjwFjeGE;MZZD01RjbPgg&ebxsLQ0K}84g#Z8m literal 0 HcmV?d00001 diff --git a/KylinNM/res/l/network-online.svg b/KylinNM/res/l/network-online.svg new file mode 100644 index 0000000..7f10bfd --- /dev/null +++ b/KylinNM/res/l/network-online.svg @@ -0,0 +1 @@ +gnome-dev-ethernet32 \ No newline at end of file diff --git a/KylinNM/res/l/pb-network-info.png b/KylinNM/res/l/pb-network-info.png new file mode 100644 index 0000000000000000000000000000000000000000..23fa16c5a1d9dfc785265d0738f484006e6097d9 GIT binary patch literal 1129 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 literal 0 HcmV?d00001 diff --git a/KylinNM/res/l/pb-top-network-offline.png b/KylinNM/res/l/pb-top-network-offline.png new file mode 100644 index 0000000000000000000000000000000000000000..b5822b9d75e41eeb2f54c426e0409d00b58a988a GIT binary patch literal 1241 zcmV;~1Sb25P)V{JiGe;?*kymencwI4 zXJ+1S2s`|t9d19a0Bm;zOGUsKGhwZLhKOENN||m(&(6-?)@rpLB%=EO+yG!VGw&iI zX69c3tbT+L-$YUL$>`|lg(~2^0GaV`yUB=Xg@_JFDYg4;jJXcL1T#O$%zHu%BBJvE z=A$T@9vd6`Jw&%%)(em%iSyLWYf26Rz#(S73&24D*YrAh=9EPMuM^QpDW&@!^mqZZ z))K%OW*%sRaB1Py4YCAS7#J9MYJ7ZrE<~@C5ylwB%%|HSLMACI>*@u-2`S}N_T8&m z3Lwk(ErG}^o)JPkE~Wg-M^GAIJ0O%&$`=VQfY$m^A~I4+SG;cJ*|JCw(Q&2JiJ~YJ z0vKZ+veupjpoI`_`P@~5N{YIknR6>ZOQpz{l6*d59t!J6JUV7Aj1-A;~ zOdQ7td;niD^F4t8TLtk&9LEp%04_4~OUF04hxc4$Ez)# zwP!;L&Ckz|EH5wrSTR5)5VczE_C}-eT_M2gOj@5~<~#mR5JYrYDYduLro7YW6f?iH z2>?AuLI`opwOdL7%+1a1O4IZLGp}27i-3seZvd0ETJ0kz*J`yM1K=ESRTsU`)oDox zaif&-k5T|x8XaTi<7J~jL@y|%UR$>jX#G4hznU{f2*jy4j-T!}PSH#KkhRto^X@Vb zQ4|f=>-FE-Wt+|B^=X>Au5{kxg~<9s2bEGE1OYhVnVFd}Yppw8d;3Q{0+gzW&aSw3 z73xTXQmNEK3ay*Q*LTXg;`=-AC%m3@-q%{Y`+)HZi@4iR-gE+RF^=Prpa?i&W6ago z+7EpYWopGEQwSl1I4q@{4hC=vE)vEV*Vb?;<&9jgFhu0i@?oV z1RLTCdu3(iowBP_0EoLkdqPV2X@E{RK$cQN%zPBUb0M2uj$umx-WNhlb=#u3)X(Xgu*B67Eo3jls$ z<}Yit+Gm}oN}s3m{;v$6R|ERhy8_U!2m96E@e%k3Y9UHqu;+uZ00000NkvXXu0mjf Dl7Tl+ literal 0 HcmV?d00001 diff --git a/KylinNM/res/s/conning-a/1.png b/KylinNM/res/s/conning-a/1.png new file mode 100644 index 0000000000000000000000000000000000000000..cd0fafa97f8b8adf20fafd14b3cba792d1097270 GIT binary patch literal 351 zcmV-l0igbgP)k*&3LlzRM;| zVGsIxa;SY{#pP4002ovPDHLkV1hWclYRgI literal 0 HcmV?d00001 diff --git a/KylinNM/res/s/conning-a/2.png b/KylinNM/res/s/conning-a/2.png new file mode 100644 index 0000000000000000000000000000000000000000..38b549a378e574fa0daaf90a0c049b80075a7a12 GIT binary patch literal 3170 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 IM6N<$f>E6dbN~PV literal 0 HcmV?d00001 diff --git a/KylinNM/res/s/conning-a/3.png b/KylinNM/res/s/conning-a/3.png new file mode 100644 index 0000000000000000000000000000000000000000..c002abd0edea76da5e0341be55a1e2f86bca5550 GIT binary patch literal 349 zcmV-j0iyniP)7+J|tQE5Fz16C93sCnNRunsJy9tPULw(t1h z8n6!>0<8qB3!DKbz*Y>L08^k392B4qI|WvN2jDseegNYDZCdUQxVP_4fb9ZrGsXSo zV6`L-B^^r|MDlx*9wl8$>W29k=vLFPMo!M5o!HsbIZO^WhdV$oIPe6V7a$kFX0Ufv z%PFoJ*}q>oufus8ow>4L>@7QN!<)hBUpn-$ci`Ffmu;2mupVGHz?bC~s&u%NgOv)u v!&l(e-sN`{EWEOtpO>mo4E!xVld6Cpx2UO~b0A*x00000NkvXXu0mjfv}lvu literal 0 HcmV?d00001 diff --git a/KylinNM/res/s/conning-a/4.png b/KylinNM/res/s/conning-a/4.png new file mode 100644 index 0000000000000000000000000000000000000000..ec398d051033ea4f75b7897ef0c74ff1ab0d33c0 GIT binary patch literal 3180 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 S?1W_i0000 literal 0 HcmV?d00001 diff --git a/KylinNM/res/s/conning-a/5.png b/KylinNM/res/s/conning-a/5.png new file mode 100644 index 0000000000000000000000000000000000000000..df58230e81f2fb0c4cb45211685c418c97754a4c GIT binary patch literal 355 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 LNkvXXu0mjfo@*3G literal 0 HcmV?d00001 diff --git a/KylinNM/res/s/conning-a/7.png b/KylinNM/res/s/conning-a/7.png new file mode 100644 index 0000000000000000000000000000000000000000..1dcfb6d0d6df2054840e31524d523b4f739ae713 GIT binary patch literal 359 zcmV-t0hs=YP)oTKI)Oh^}+2C_i_hk-R<8JPMn zP+_aUDDVcnyO*N-tw4s;(X7s=X(5--M z1GABLpGrAP_>~&?E2lP`1okY=SPh_^=oO}{r~U|7wEM%TzX9w5TfhO(FWXRI#|FCv zPR-x9Qe6Ufz#Py6#!|ougDn_r2Y9yLl9lS!HVdK6BH(%gR-}Feo&TNDl&$I_(h-sI zz{~5zD)yTqvLYh2+zUhb2F^>iD$+Y}9X7oG@{Q}T13$aca<;M~{M`Tm002ovPDHLk FV1jhAnP>n2 literal 0 HcmV?d00001 diff --git a/KylinNM/res/s/conning-a/8.png b/KylinNM/res/s/conning-a/8.png new file mode 100644 index 0000000000000000000000000000000000000000..01785950998ff739ce9eb250988eae6dad778749 GIT binary patch literal 3201 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 literal 0 HcmV?d00001 diff --git a/KylinNM/res/s/conning-b/1.png b/KylinNM/res/s/conning-b/1.png new file mode 100644 index 0000000000000000000000000000000000000000..6255abf92b5f5e7d58007c5913dc03f0cb4ab144 GIT binary patch literal 2800 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 literal 0 HcmV?d00001 diff --git a/KylinNM/res/s/conning-b/11.png b/KylinNM/res/s/conning-b/11.png new file mode 100644 index 0000000000000000000000000000000000000000..d4f8d0e97c738895abed4bada41d5bc50e7b2ce9 GIT binary patch literal 3459 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 CTiee7 literal 0 HcmV?d00001 diff --git a/KylinNM/res/s/conning-b/12.png b/KylinNM/res/s/conning-b/12.png new file mode 100644 index 0000000000000000000000000000000000000000..1fa952b655d7aa515c54c172df4b018322a20445 GIT binary patch literal 3465 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 Nxx07~8k|B>{|`W|<~RTV literal 0 HcmV?d00001 diff --git a/KylinNM/res/s/conning-b/2.png b/KylinNM/res/s/conning-b/2.png new file mode 100644 index 0000000000000000000000000000000000000000..a226b696a9d62c6628a9724228ecf417127f8ff6 GIT binary patch literal 3459 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 QW%-wiM)4*$kfJjF2czHBzyJUM literal 0 HcmV?d00001 diff --git a/KylinNM/res/s/conning-b/6.png b/KylinNM/res/s/conning-b/6.png new file mode 100644 index 0000000000000000000000000000000000000000..a97049009f678ec9932e01e305def13d7a58e860 GIT binary patch literal 3496 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 literal 0 HcmV?d00001 diff --git a/KylinNM/res/s/conning-b/7.png b/KylinNM/res/s/conning-b/7.png new file mode 100644 index 0000000000000000000000000000000000000000..c58bde11c5c2bc1b2a84f2965fd55ac9f51815be GIT binary patch literal 2942 zcmaJ@dpuNm8y_0?yo6YcjKMH+%*|Y5a+z_W!+dA`r>b52fJh`*t}l|BRl zF$@f#N2u4hg*o>5TgNpt0@%21~_Is2D8#>xEEz zlko-A2)f@_U+R-5B0-^$Qqkz-JCusV1D?r1?#&4wB(6RL~C*-Dr25=Ed`5lHt$ zsNcBpg?uWJh$C@83Lh-7yXi7TKG1^h)j|AcoZ`cQnaei%Qx z4;_nTkbST|42n07LLyKwByWcIA~sMgS8&8UaM8C=?fVOs;rFjts;>;>C?qnLM6z|U z3&Ij43W+>HB8B_xWacV-L4d`Yq#wNR!nQPV-0FbU-Q$s{89=R8w||G-58kq%YB1$lTpj|&L+ zTpobMf@F0z$?gD|LK1Me6=s*#0#b>cd~71&Y=4BvTI# zxgxU)0?{c5q{R z0ei}>8rQAYJ~U7JWQI&-Q|ADW0G;~Kr)-7*I)p9s*8ZdKs zoz94>o^wmbCi6G>HaGWnI%+yMHod5?3pKSa1F^gAWGGFFZL{xdUOH9wl!=?S+Hgoo~<UflJ+P-n0oyR8sf~37S#9Y>&D+ucoz;t0 zwx?i9laRsHrW+ehPi@mh?F{%hw<_Wxw|M@z`Ru2HiK61DY^H~ZRqNdF&0K@upLxZo zJig6xXWHEpJ7e1b-2?lZ56D8>E<3WPI<~u(cih;T3>gFcK0f`Hn6Xl?)_d(+RQAW% zw*3*m?u+j5gn|!hb4D0f0(If0O~`vRFDKGu7V-JYFqTy>*2Gljme;8Br*B=Ko1*?O zoPPJsTH^*p%Zp)K<@14`Izr1!+qCMci_V-lUO{iMLG`|yT!NBo*ZiinGTZ*yA55p+TEP<4eQy6}BQPth?{N3lPNbQA=p^?7hgKQ_Pjptf+z;Pk(G)R@@Qo z_1G((4U25@vPL3GTJnD-Tij%}TqU**!NQwDZovAIt}!mz8x&W%W%th&Nai#KR!hs> zJGas%qKahxB_GxVd3KJ_RMDLiV)g-aXM;{5QC{Jn?5%?4`J6J8$$F*MBAXt;{ZXh? zY!vaScx2-B3ga_wAxG_`w=%xZ)nI+x-mV-5rWKO(zYC ziAg~#B3p@J?;&KWpj$CjQe=7G#z6<=>le;ZGqAU%ke=z=!M$~IlQY*nzH{GQAFwG} z_ZhYJyEJoQmsZU-lQ3QxqhXJhw0&rc3VQmwLTG{?tC+a`%e0}N0eQ$ldFE8&Eq~1^ zn)F%rVaSEqk0tSsJ0X3i|JdEGm32=X01%G8&xZ6o)UW(_Qco1yX~&Bn+kC`o+G|EV za;p9qqbO8{v?ZldKU1?WLkH?wUbB4LyL?2uG%gpCvA%$@ii5OS`{@|@X-(_3?9rtf zi2@RRr()T_IGIYZ_U?!FwavhO4~v3N&1oEj&OhuPzj+sD)K(+WPcAD7e>fSY@cz8}_!Awk0$CNepVB8+i>+{SWuiNlV0TuCmXZUe9R? zTwj{lS$5mAC+Xzj8&8Nma{{RDa@f*1o5~O%%wV6XO8ueC$As#f{VwoF)YClkt~%668cJdpx3;i)*AeM? z)B4E5*MN6$RZC1D;B~#bP%A&X)GYrXbSH9fvNTVyV&2KBn-mvLG^dyMDm)`xE<8tMjySAhcF=th`5i=DbNUy!qbG^g1 zG0TiwmTQ?GILa_?$f9ltz4k*!Ol;iGT%8|1);8&1WgeU}Gd&cx|BO6o*d%>^htYxN zl_oIZQFSHUNhu7){gpvy@1%ZOu2-@H`TmWaW}s)U2+=$r?65&@tt#rdX)zXdf_=!L zw(1D!N`({U@TIcDF7q2g%^w^)m`2g7e`JO#tEmDACk`cjaw#%sy6LnlKZ+>6-L~bl z*@VTVSjbHE(g)X1vMoo7E`+4~;M6m9F^h+u%)6B}o7iBk7xoGOCR{G&OrTnY>!Rg3 zkgOHP;Zv-41m`l*8&{Hs{y}5w%O2q#TD>-$xU6iHCaYjU73gT^mj zPo2)=ivgZ(&;V%%F9te1xpyPS&m_3X%@zenPFrZ%h>m&D6?T?7Gs30SIn&;38`*cT!bt| zq!LjL*;{nEEnF%~rE-znEbq9t?)(06KkxZ`&iVb$?>yh<`99z0dHy&@J>4DUWYuLs zAdnoziR>*|L)UNVjgsg1={US(Q4`n)3VgU>f;cLl39?~uX-t5^riL@UnN&tXbQjYS z1lo|n@(mOOy15eRTsD%rZi9?x^CV~x$Z~f)k4itl6aX}4IEzDqeQdr116T|aECB0< zcH`MGBUny{_)MQe?!NRx2j~O_Y_}C)8BdfrU^4|&Af6q?i6O?5V1M}~O7`o=C>ZdU ziQoVU_BW|OH&4Kh%Vz>uBo0A0!QcUN0@4Iaz?+)w0!+~+I24+IGBHD-O^9d$5p4o| zyI_)Nd`1}2o9yr{mgJ2Dix3ESL=-A6E)E%oL2~)wC=&vKfI^$1Oid9I3q(u;M?j57 zaAI`7dmuAo=zJDWz~XX%b&pgUH&#G`Ntphf0-NXN_K#yu%r~JVlA+?MJd_C%jbgLc z^ZLs=M&Ql-FO7e-j`2<4F;U*k7;Y?|E~!VD?su?6yZ;|(-B7}Y=)q@6ib9PdbLp{c zCPzRalVFlBNCt~R#NkZMsZ2B)fybI+5oo*_0YM96Fc4TA-pt&b7Dm8@F~0lxH@uk* z8f!yF6CB94WD^s6ybZ>LOd#81Z18q?j2#~R9ZTWF2&f!7^LuQTB=$Ef{{Lc$c6=sP zz~%dLxl!M{z%zm?;KoF7d4QeWdezMVg8&wX!HtVCTrbn#S(BN3)BcI))yG#-MSqZ+OQ4==XP@N=_ts@V?U_ zhS8XG42DiagfVDzgoz0gFHsY3hQOQSm;?enjDV(@!cY>5sP)$QpH}*QBkJG@g;*}b|;Gu0?AZR$Tq(5&)$b@iqh~?IQKHRzfLnhBjX`NzW9eyKpL?2 zePnSY#~MhF_(l5BSp~l%`qn^(0-(@BNvkN%RCl(PR#4a@4JIqN>vX_xzA!nS{I1#~ z@#fsp#At1@E@|dj^ufh}uc=|Hy=%?;iam2PMCGcKY)^(t3PcNRsIVl>7ePgD$gmx6 z;YUCQjYl7Nk2R_&)Vn@%dA$PyapvmR=X{hiy#*y{7~T`5d5N}ZzjG62&xYE-s534-Pi!wUwOHM%e8oPHW3j)@>J5p8pw{bnot-UhPO5>%#6u zX1t86_ol9j9pI~ruNX}Cup5*gb&H#{AZ9*G_d;X^{V$&iv!1$Ft9S2LOUkaoy{;9e zp?jm8Qq^3lLY=LzKbq^w%WZ@KTd*y{^8u`!izTi#GM|L3ilCaHD|pjt-(r?~#Zx;6 z?{KWn#UP=(cVAh9?|8wA>}s-Wx;nJ6MmbAk-*eDPepV>Wm7p2EF%i5;*70PduK1P+ ztI}++dJ>p!!mWMS+3z%ItW|ZUrJQ)w8I3^7J;$ z2D!v#c}uJN5eOtjcz|7kx2daw1ohoh@lbEtFfih06mr7wiBGUg743j{M5G(xpCR|f zeYT0VE%hg(rzlJJv9)CLjxBDlD+tbYY9%*+6AeKz8jYK8EgR!*?>ezLvhrTkcG5CK z{8`jk5q0{cBQ(`$AM@j9-GoyQ$@Uh5veOr;MjdCZUxyc{qP%ZU-AK-YU+O3_RGe!f z6y2~6I%VWeLkTTwfq}(ga_jH8X}F_7Y(;aa;Dwaj z-9gdEXyG!KUC|hGL#yi9meBqWfDLc(-R$DPAB%Huv9jYZjR3f~MaA4_Y1C)7bBBTU zAKByZyqxhr4_ofu6WvJ5E5RK{PHu2`L<#Cy6}_7VsL!?id(B#R7A(s{Qy~_kaw0i` zRE|$??8Ad!8qa&{wjJuo`pOoMSnZqDO8nh*lib>|iHYsP8g0XVpQOtX<)dfbMhG=5 zq>>!83)7`1jc_Vn512}Wej&uWY$(@0Wyn3hLB{UIoaUE|AI5^=&em1Y?~iMxHq+vs zG3GliDCdn2rzzLRvXne1_*tuZou2ZiK_$-j5q>(eC;h*!8o>Cw{eIoJpyDapgr4+%rT(X}WQRS< z3RafF`9D+?(?*WJ@H%hR>!Ayt>0JOed?_h)Q7BA-+bA;Yi`CSrls1lKtKoD(f*x3B zTCc<_x81(xAd(|~wTp74cOv>{MQ|%OV)WNB&sCSl*GP?ltz*&j66yYIkZ)0L0{Lzw zYVMTnj~wD}h0*?yKv%Op(|dEj2I-3RBf!tomy%=qSz@>WA)n{*cLV-s}$%pcitXUro!) z1U#+WaBHeb^jdJyxv|CGXZetox6oEj64ytH3HCB^0p)D@9PSf?oXuTH~erV26ibxJ5tklI@TU^Xw9v+GD@d?&)HJh&-t2t zG1YlrpFqJ0bWBF})SKI*EHg)nix)7%%Yhi**EVffDPHPw@roK@zp&orl0;6AwN+I- ztM9awuQyB}WL}!w_DcS@@^=Pct~ANnl;=!L*MBz#7-E zZf}7<9sr-LKL8Kh39%o&G<;O~f--c-uwNigV!;*|8Yr(01}f#Vgilr|R}I zhV8ZXn}d!GS>@?}is|skm`YB>idFM6~~*;_8bIA*|OT zI2{h}bk6nHvP(U@-2bw^KJ>LS5q@cPU-z$cXj+xWvFx(E&zF{_L$@YhzR!@pa*dF2 zZSVptdKrS_84c{5Tbj#mMPC!IHh73voLh3wjO$ySXA9N?%RS1 z6MX2+ZTzyh@6pmOoW_;QHnAU`6W!;X_2;`=EYzLrkfrh2;yYteH(&HGt>`&Hn@mME z+n$E51Im({5IN3e*VCoYgZt3jqp}k{XT~B5w zs^He8vxW2R4`7D*^f_Y<+Yq@B`M_JH>5%ZUOU$FC2l7K&;5(uIkB#6L+r|pGQ`ce5 zcRY=PW8Q7eeJVm(y26)BDdH@Rjp{EiI@R4Mp7b+HwazSxyxa3(qfA|W;N^;%fU}WP zqEbb5QmTUXEGwzwGA^tfd7)0WY*weq#_5%h@G0dNE9P(&fb-%mWIfD*3Se$b!vs2G$>&Xwx=PoV40Ai}DefYW9q>oHNEy=r6 zDXc}tAgAc(Bh^2i>Gqm#A;ThfP+&k#?w0T&-S`iVzf;!A41O@el5-D^z6xYLcia>QB002;Q zuqV1n)_~1hUQY5HIv0VHENVO(AD%lakQYJb&;gb-7KILW2qOp4-RNXm)X8?bDFCn~ zk>TOP^Ko_}P+4JyH`DIaH7ur-TA2OAq=U{oX;2gc$Jk;Zr&%IF{%g+OB92s|8V1VbPR2s{CS1b@9C z5^o$@Ai<4j`_-4^WCjW5@z?}7JR%~(FamAJ;sn8wcsw4CK*3Qcm_!4{jbif1kuWA# z`A<5 zmo}H@M*lC3f7RxCM6u~`H#(OU&Y?=`5vctQEXm#fceJS}VMB1?FeF7GhZ0%T@Gv@) z=Rh=rNZuII7&HO~gTj*O2m}mgj53BHa7K6-C6Gpg8DnrpSS%$Fj|rrGv-59wBWp_} z0)@4)C0Y@YNE@7`5ejK-j4-xEA*~Qr2-G*M1Cz@mGpY1%z8MnVU$MmhizQfd=wu#? z1+X4Q8>y*4CR<$Aa~|8B7{0f~&t-roW>m(m9M%beb)P6$bt*&jiN5 zaKNJx#&{G41EZnvXjmZH2m_;FFvc(n9)qW%seuSI0t5Mqr~Oa<{_azWL=p%48y!L* zg-%7IsT5csjY5SXk#w9Sn>ZsF4vV4V@zg*(f`Wp;B^2SCt@A&v^i3q`&&};WgCRNm zGg@?}WS%&Z!I8O5P67ZFDjbNG9+AI)^kbyEd+t0G>)ydj%5qL}rmMVAv6M^ENC@dJ z)Vq*b*sAPgR7^=fvPF6(1FUc&UDx)7x$Q8hs`;8wquepQ@Mn9Wl&Z>EY1HX^Ji?{d z{K)>&(((J3dM>Z_U0i@3_g4#C=`-tpG+I!7X}3KTG;%~iZ%)c!X9Ca*C=GCwPA&sq z6_<(?A}foewyV^WK5b1boTdPGfIPDgKAR%4;R#B+|A{yOE$y@Vw?f1^XqN2HN65eV{QMXRDe>& z{)SopX|@Z_APeuHTpu3}upe`7>|hz3Dm2uUl3H38bXS+k&S(wlNaKD{n!S5F0;I7H zYh5b(UM8_HCXM9yMrW^_T7^pex&W**N+UUg#uWB4m6elb9sNM^&p#dRvMJkAUOAi7 z{?wvOziNjx@4^pxJLrI#1U;#u%J-vCEvSv~u<;hUSx~s$&7TAAD#+&s-x%+{HF`7IJWac%`$K=ccvY?UXy{b(NBjjCkTtmYM0fF&)t*V_V~B>-1DfDd-c;1(#s@%b z$w+scWziLbJ@o<8{LFivgOtj$6gzz<87=pjs<+fS&tEiB+15*H>*Fa~Z=i0joe(|S z8L0QGRTcf&5^>PaXH`jL-saB=wsh&0Dc?}(d@BcspF6Hz$S4Q#fBNO`?6M6 z`FDwm`sVWacdf|k(y#si?z5V?{;o>7_q-_bM{d{PpMMT4Ea-=Nzd1g#;AK%2_DCM_ zpakbVh!HtV&pcCo-E_2+p4=EC!n+Fg(3E}O`AA9MXWj$3GU^w5z`=%%nXzXZ4*)MJ zuFxjKg1`&Gi+9vIc|^o9pw-i?NqGcvs8)TwPOffUjfT-J-O&)M9>bAslLyN=WMpce zw{6_jKJ{wEDX6G1a%rW^&$TWuqbOPFmrMGnw!OMi(b9dGRyjaaWwKDxv zdH1zr=^||X=<$*}zgz6NY2+-F-H4V_uLYh@y4cwJ>L_V)sM>K5eAVOwu-~NMuu$ji zysqBcZRN+dJQR0Qe~zzUOujJ`w$EL{H1j4^=re>NUG3rFj`y?^s zAU>Qa*;Tq?52_d3*Uam-m^}QQNtXA?S_iRB8i;Mso2KnlkC`2_96wm8#Q4mhx#T$7 zi=a~OVo~o(MD|AFcJf-P59TZlQz8q?KEE2|_H+on>h^>g(wjW7?1q-fa}|=S(0i`n zH!_x7m*aCUo7l98QdL}2=FX}^~h+mRbJrpjLlte=l&+%VGT|y-KWq zHO|J$#ZBf;OFD6C<&UQ_MDeb^67fMBp}pWDXlv5SIl%M88z)sApa+Wv8n^7eJoIWl zZq1;HyzC@wUT96+DfL^j6tP{zGVib?d3`#O1+g%&e!(>l%8q!}H9~^ozKoQ1$68(I z`DP2J*q2CsK3_Oi^$1gbd1e{TR@nXe)0LCAv^TmHNcrt!wI-rDUOGU3A~f+mdjav{ zOvmWQTbfs}^6#{D9RLU7b{q_@=*- zR(0}7p!!*zUjEARnAmBbO1%lL!g}Bxky9RpG*ili#Jgi_D|s0i@&oRK9K_kmBcx@5 z{5_(qMw7{vD62GV&~G#Gv-qjwshyovgs#>RGu4KdT5rIbt>&_=yCGG9*W-VR3cE4n zRay2Dzf`9kc}2-xM)|Gylu;;BfZ3OaIbCxk4w786L0(%unf>mDKw9b~8QZKs{xfZ# zU?4qMQ~lSFTRr?lz+_gMSUaj~a#sW2W_}y-PadzDy(Q}N#0ev|tX%NNyb9>zgzL2B zp+h;Qd(FZh-Lcf{dGJ%-U3*~Au}*^;g_cetc7#fz`nuH^o{u}z_zCz#zou|HZ8{cx zIddF>P8)U1JeE;B;xTW^DT7Mym)UOcws?Af?=eu1x~srjS2~#%aeKU}Yo2X){k`UG z2wyvY+;;<6*;#(~rpHp7@WAtE5=wdzjEpmvQInf`5Na8qDKxz})!u?zP;*@iSX$w$ zKKEKu82`YRo80TTh-zS>cBO!fR|ifVB{NJrOoh1m2iU5sU+NCE_#Yg6r{=BaTg+CJ zA9ZeeHn8e!AF{IDDc@iqJ!t#RivH1;e)`f2ox`cQN`3PAP4RKK52u!JFTKa4bUS)! z2R+4p4{{ASPmJD^lv|H!ESt^VP1_p+@mK>Bhlgzh}6>) z->W{6(5%nBR9vF~Ht=?U6Mr00fAd4ssiKFKhI`LOa(}$>IUM$z$G}Ah$%&FXdeo{; z25SE>^9U;&H{@P?PA1sv^ZF1-^$RJd28*9Fs!qv1e}O6R5@hV+6qMwDqJvPR9eAH( z5rkKgFX(K!6EG&lW!c!P<)lePO)JT9*@h`=zwDXxyxILW%5>e&LmmEkgz^68k-Pa( zSD%VzCqn(87G+g65YrzDCXcdKh> e80vn3*8?8!&aJw?5OZzwf62k-FtNfaAmKmLdFt-~ literal 0 HcmV?d00001 diff --git a/KylinNM/res/s/conning-s/1.png b/KylinNM/res/s/conning-s/1.png new file mode 100644 index 0000000000000000000000000000000000000000..69747259f43ba4fcf59b7f9f99030b79195ce56a GIT binary patch literal 652 zcmV;70(1R|P)~~~Ie>;OS>h2uC!UN)nF!G1D*zYjvrN`C-Wo62SBM@0 zI7+C~2GIIRkyHZo`VK%@c_Wj&ZgQLB%Or19lX16cYJ4L=;}HO#1Do+=JhU;KGM6ZF zLq|k`>m-RnVt_V2;sC47w1$MwIcsSoiYXm;^D-`&&!+D|Q0f7wQ;B*Il)Ca={`XYh zD|UFz%eVpOy5`(we_%y`(wp7h%+q*+M3b)<+^8!B&iQ=Iy5THI3>i@{DtKIb6E0m> z#uZxp*$~@>N?5()5u3z_v1=9kzdJn2kVaFvJ3C=x#>lP zzI8TqM8b)K{|piel;cm}FLsJ`8XeT1O~`jV)^LK;yp0o! z6vqzPP76GWANa?mtJpVSkrMIs%wv}Nk{}$zC@qO^VX|R|&juN~^xh=CrOjnUG@ni1 zf}kWzu`g?*E0!FM`>Y4#*^VdUW#-itQA<@U5eMXHJW>^xNy(ozA!|2@h!a)2^|nO+tGly$(B*=_I||_xAhl1eW3lVHdy!fHMGnK3bLl3IHDf z;yg=NHk$Vm@Pkl*ZU9`{lBFyF%=0Ww8WEtycK~kHXHoV}YZGH7*9y@Afb)`fgvj(a zkJJ*N)z1KWO36!}rNut8xxYv9#%r=~ROgKf0fJ8ebh&YyXX(-gxJH+FWJp8A15afV zogLt^@S!QV2=_ivmr3F+*?53)jL0@2TdPdA5qcvj>(m5>j9Ed;4+g$VI`~N%lQ83B zfs2?Nxa04lP0X8!$JiU|Ut9wcPkPIh^aoF6W@20CEx^UlM^=PL-(znFJDe-vKKns= zew=nxw{sc$M}n^y<|lq%dtktqokOFg%sC}zQen)4HGva92;cDCwJseJsEEPs{B7Ie zWkbNYMSlGty!V5!(zg$2y$Jz4#(sUmVZXj%nMhK`jyRZVSZV?jwLrd29f3n7ejx-t z)gLRKUKS3)!BdmShw*@Pz*kcyE|EF-ObB8x&(hdotm{M%zSU97kV(EZ_>M+^r8ng3 z%nZH?0rr7GMMHb=3Fl6s$9|m$WL8i7AdGawH~_dAMkAf*bp&EAlnqfEZ*XsR=rNSn*)UavL1o{Njixgp+>$NqW_;0rS`+kVImv6o#rA@K*v3RUkoCHACK#;d&_xS&o-n4sLhny`9~inN7hGxqQI~Pz7)cpvXrf zBHH}BJ6RdNjiUak8_rV1IF~Op^|ut){+<6HY>%2Ncnq&# zU`do*;4!uf21%0}0VZtziBd@*6BVH1dg%Hz&~}Mlm!nlT$SpFA&l?g-F)=X3+ z?ALm=JgH9LnoAjWV^x-`ftS0OC?h_N)Ya!xu4W?s<*2LKf!WwXh~MI7eUtztK@=PgB;u$-6+%!ot*R-eC_H#ne*g+qRfXaQ zRN=vc!cbL1J-ysHRDlFl)x2i*@^Zbx2JUhdeaYqc*nKlQ`|)OTu#8HzRs_%ha1Ef& zN5_iK8-PXNdwZMAM+sD_H34b^XxWlwOaV*+-&>tTfFte#=%}A1*$aTBd^V$GTOoP_ z;4Y%h7{KT^MPdnX)K37)YBZ8$eQen5UnY5@n)G{&Q{x8#f)4={*>Mo~-qHp*CYLDE z=Rib(UX(;G(m{i-*uiQs9UxI;&Uze)d`!cOq`)P1+s&uzmgWF%sl=`8mS*z1{O_o? zT{c)J1#W`5ddzLs2SNgr-mLaPlHd^%1AgrgGgP3>_w>{SXGvno9Tj3aoO`ofMrnbo zFc>Ytc6hvk2aDA{KPQM`c0r?{g`S$j!T1@}|002ov JPDHLkV1i$K3jY8A literal 0 HcmV?d00001 diff --git a/KylinNM/res/s/conning-s/1x/1.png b/KylinNM/res/s/conning-s/1x/1.png new file mode 100644 index 0000000000000000000000000000000000000000..258bd7041c124bb6bc2d6a23017dda7ea6f8de73 GIT binary patch literal 354 zcmV-o0iFJdP)TldWz7K@i7(97$_vGV5@6x!{Y!fad|g_Zd)Cs47$y zZ_z#g9u(#tfL!hzijM?U)eM>25DH;wm}IiMng4&jx5k;oN!kYbfSygWu7M1gl|`PN zGQ5L0NgKes5x;?XS>!9TwSXQlFh`TJ$PeCukv}bxH(toD3Zj{fJT6Ys57kSgD~>!q zvYJ&Vp$-h&R&HIyy#Y_ai-hyes^gnhGY^QW`_Qsy7TN53cVw`(v#T3hHE|GHie>s`4V*=`+p4-@p`zGKxpjSs zxFs+YaUH$uoUhd15OHIRK33dgKGRv0@b3)#0(JUnF;aq^?f?J)07*qoM6N<$g1B#) ABme*a literal 0 HcmV?d00001 diff --git a/KylinNM/res/s/conning-s/1x/10.png b/KylinNM/res/s/conning-s/1x/10.png new file mode 100644 index 0000000000000000000000000000000000000000..88398b807950bbadcf6b8c4bb526f11dbcc647f3 GIT binary patch literal 355 zcmV-p0i6DcP)DlRZwuP!NPaz!D9`bLTEmlmp}d^mH^-R8({{R8+30 zs5n3lK$QcePeXTch>zYtkWj;(i>udw< zi^QCDZjUNp!B)G*-a$p;-s%0+-8Tn-CFl~69t#!7I<+H1Lf>1&EE$c}?xW&-|BohU zd;`mYabl@7iH(8BY7U?xV-^3b9NAq0z5%%?920V5p}iQ2*f}xA%F^ml-_E*Z2TlQB-iKoCVAhSJ5lf^Jt!4nRed^c;X3AXlJDl`2&# zPLczpPnX6A0L$(|1xTr=&`94a2#HuCMq1g{{QqaX^XH7c@FCs+cYr;wl-&X~un%KKEx83ins*UUE4e*YZ7%4a8jd^jZfQ(faYHK2sWQDNi%2nUNwH%fkhOKQc&h5*&Y6Fa&t8VT6{q6PR r$OV?pRWnU)X1M=ZroAfp>kNDXG|gQVyJ$F~00000NkvXXu0mjftxc7M literal 0 HcmV?d00001 diff --git a/KylinNM/res/s/conning-s/1x/12.png b/KylinNM/res/s/conning-s/1x/12.png new file mode 100644 index 0000000000000000000000000000000000000000..60b88e98108d9c79760a34ca42ec6db4f5f0ebbe GIT binary patch literal 364 zcmV-y0h9iTP)bQ>$(QK@>eKq6Trt;bte ztji(~|05!Sb$$TgT?4-WY|0|v>D@hmM*tHgT9!qAj076I(oFI4Om(Lutn4k~tn(lA zb3q+M5ua(#^kpi6Vn>YaiYE>oWYF$o4N`q1e{S*SDzw=8aif_Cl#h5NixM!mFrBk zO+S^DV#KoT(ASl00Zc{Am}l;}qyK@3S*Vm3#9ikxwX&o?X8?bB_;gc~N$O4j0000< KMNUMnLSTYvQJQ-I literal 0 HcmV?d00001 diff --git a/KylinNM/res/s/conning-s/1x/2.png b/KylinNM/res/s/conning-s/1x/2.png new file mode 100644 index 0000000000000000000000000000000000000000..45eedb2bd3bfb08d5774729b017cb52df375dfd3 GIT binary patch literal 376 zcmV-;0f+vHP)TQ!P&eK@ffIS;TA*oZHO?RaJ$;^8@$;^)Hq=)KxjvLKH&AM~dMm#jp@D9{@%vhGLrtA!6PD+><|`zt-z9u+C2aT+qTyn{+Ie zv`c~flpSbJK0JWC6BKApv7k9kCSv$;zOVCrq!_+L%sU0X0o;k0XCCLCsuK~zWX{1_ zB;${W8F5ro3@^I`L<}c!Dq^lg%qt7FO`Nl%)_EQ&N!vi1T;!~I|8tpoJ`can0Db|j Wpo6xiH*HS<0000LQ>#wIKomU;0#k9v)y-tG3bhKge({Up8{ijEDAfJ} z4ux7(g;|H`2au+9RXmbaC~}%RFr5I|!bzr`*E#n&2gu-jm;+b>;J?(<9zYGC>e{AG z86yGb!v%m-0QUg4UE4hA{tUnZz)FQ~y0+Qz%w!H!(PAVMoOf+=3*cP0*3O3_wOl0g zne$Hpb!)#>uITR$w)1jQ#Lcy-{QQ05(?|YBYGrkBEH)a3*3;IDdP6 zdCzr3EZ@zQolac@c N002ovPDHLkV1hZpoH+mh literal 0 HcmV?d00001 diff --git a/KylinNM/res/s/conning-s/1x/4.png b/KylinNM/res/s/conning-s/1x/4.png new file mode 100644 index 0000000000000000000000000000000000000000..c8a9a98291b8672a790d8d7c08c04c36f58cdb31 GIT binary patch literal 405 zcmV;G0c!qDlc7$-P!xtgx+NIIt*dL(pl0X;%<}+KQ@j8MHHDg* z;uS#c8K!2yfawD)H}|?J+9p*fa>nV>m1b=GNtd?weE&VC`|kxkij#B(WI&{=TObEk z-r2lO4EqdJ8}ICtAH!>449w#s6@!I$wwAR(3$EfMowwi@=^#$hT?gJ-F?$B4y8a~0 z-T;`jxAx9T%MU=^d0%^HA9bW1GmMio(|MSFR_;z=LW9c8<00000NkvXXu0mjf78|bf literal 0 HcmV?d00001 diff --git a/KylinNM/res/s/conning-s/1x/5.png b/KylinNM/res/s/conning-s/1x/5.png new file mode 100644 index 0000000000000000000000000000000000000000..0595e6b9e934e664cb7f718f3bf1352a70b080e5 GIT binary patch literal 411 zcmV;M0c8G(P)Dlc7!nK@f&Nt^!jr3+CKi4itt{C=3dPDh3P>03KC^ zLiH8EgF;ndz)*7!KsLATT-&4yMdmVXu5c-|lWcO?`@i{jHvew1qLMUw0lGj16u>Z) zzGxDi`A3pwT@lw8amjQtu0Hh3yZFS-9WnY8xg=v$$n%p^uU@%K=^YZ4E-{tCnrm=E`DQ=v}8KoEVjBpAf3qOMJIo_+ux{3$T-0hlTj>eQ+G z1zZ&$fJbqr4s&T*R8egcFsH~%XPa$vgySY@(w%wl&FC~0XzUm06YW89b5q56Tl*jV&<1sS$whJIpmdsudA|nvH%;* zv8KnBa)$0yUQD0k)UPJz0q^t80B!)JUE6GT33P3plARzYp+HcT#b$4shH?V$fX<_!KpjT$?7u|i)SepV;(+Gl

-T zFdrG?@9xCT$@QwT;0^fE#x7`No=Fg!lWT{#Dlc7$;Koms}g#@#rtEii%;MotrgI|S$UqG!|g<8dD zpzy3;1*RVWlNnbBBq$WQ!%Hd6rs;B%rp?SdcV0U8?a`Afs~K<(=v@G-82lqYdnYmY zmoCv7xU!l63j-g3O}E&L@3d~FpRIwx+HAU;UAeNl29AJk}@HjBZl13f7i%7rT{Dlf7-jFc5{G3777pjs)QWJb?QQ;3{2KaFxncDpy${ zU8)S=-UE05gCIMl_W;2coCSvYQIrD+3DkY>oyfbHn(!g6B;88l+1b}#(yrumZu18L zAL2sNgMqJ-j#t2kxUAmETa=voLBKYj%xPb8y0lJ}5qacq$#b@kb6;}Wjlfj5G$RLlP_)pK@`Tn!pe#_VAerM55NKH*VUjnKotsAg{rDT zp?ZKSeDTzCfMzn|#sLWjXg>0_q1z^%Nnf(r$-Mc!|L+Pd84O2b01W{CYiSdJ9e_;? zes@WH85j&l*8m;_t^sUg@bA*y0JxRm`xyM`n*qT~d3w5h@RwB5({qZ!FLEG7)WqOd zm2&1cEr6Fgzg5a>T@dyRz)A&fI`Y2F%{c}?kHNo+8Ph=hS?v-LQ+GY6zo`ev>+bF<@NzsWIV=I#KT*aZTQx}WF38oUXBMfZ5vCo|V$YjTTe<~VR1yq6!# zl;N3E%Nbxh&ndJYJS(l=mWg3=8_zIx|66AOA0_^3E_g0SPyhe`07*qoM6N<$f=s`Y AGynhq literal 0 HcmV?d00001 diff --git a/KylinNM/res/s/conning-s/2.png b/KylinNM/res/s/conning-s/2.png new file mode 100644 index 0000000000000000000000000000000000000000..eb6a4a41cdbe9f7f8e85e5bac32a7e7b8031d080 GIT binary patch literal 709 zcmV;$0y_PPP)z9Km&aOFmxqLTmm?Yqwuj70V+N!1XhJ} z0Jr)%SIM<`R^At~^PNOWGg3-`s!srPH&J8RtKHZluXfWo3hyfkF!&b$J`GRfD7r(iH*+QD>BTsPATBzy|mDMt~%qvNX9CF$-ov4g4%k zu9{?mBz|yX?yFFZwMM`)c+Aq|Ld5(6po@PuA&~nNFzP(v4T}0YFhC&fgP(@ z3Ac_Ffu<#Q6WFmlYeN(#oWF_HzVoaGw9R9f$669t@XOmR5|}ab=N-O}3;yG?>(tX0 zJRXTl`Wv_9SWA4bKd2mJ^HXrnzeA2uZ9pfh<6eL8^(CLncC1ZE#P}RQ0`}_XCL}~m zhnr2i-%!M8)4S7Vv_^michE~h0#s;=jn6%FQoJqPdyjbRvTLEXQQ00000NkvXXu0mjf-p|SCy zp`xLop`oGj7KJ1zBq1H?d^QW3?YoW)?f%|V_Sdr=C5?T1ByD2-?~`OnPe%w;x7R-g zFa~e}paWo3Xt@Ni2CxD!OXBDur1DmPZm&N8a1LM+3QOGpxK85eelGwTzbo`qip|;E zd*4fr&VbLp=HD9RbJd8{0bCFOG`vdUXkFJE)mEnHQjMCl`%pF-FRF^##S{ z!)czSKSKcWEPcqcbZ)I%aa5$vnYC_FtWgQTLUTe4s|svszqZ!h0ywRLs0Ap4$g}iY zC;;34w$@D*#Hm2Z_fG)2z5Ymygt`Jkra^{wx(h%7;*1clq^A6Kguie67cofU=*_MM zp^{Dl_{7KG%1deZ&pAJ5O___5I9hU9ZafXwuwO1;n*=C$OvKx8dj&U^pmqKn(ZlZt zW5c~2+~L}i_c}3Z+g!E?;JrE;p!U!>u)NpEuL6V~pNEk4GL1R~Xam6I*ow55QS&iq z3qZg48lV`lnw0li8)i5#$+RW!wK8h|A27)@Dth=Sm>Dh!e?VNeFaqA=M!fwhxaQ}C zM`n{6YtP3(A`waDzmZNXyQq#i(R4f#Pq}U&2Ik8HG8e1X$NyVywPq7mz zLXe;X%Ubu6Df;ofvex~Qdq&x_bvL79E}0AU^*&d#C9h_s$Nm3%IV}M>{Pi5bA6X14 Ut#fweQUCw|07*qoM6N<$g2muF-2eap literal 0 HcmV?d00001 diff --git a/KylinNM/res/s/conning-s/4.png b/KylinNM/res/s/conning-s/4.png new file mode 100644 index 0000000000000000000000000000000000000000..e8645dc4411745b205930e6cb1bb599deeeaf8c2 GIT binary patch literal 769 zcmV+c1OEJpP)2^(qANQz1l?pgLwN@`mQYDx8?@1+;VaU99cCq;?lyZhdK@7;Uv-k~I# zt#${12jCOGz0AxRfCYd#fN2y4%Q7n;V!&?zjk4nb+yWR!VX&ymfbRgl@$njfue)rv z+9y=x48VCtoe2%eRpbx@ghW(0;es~>V;eN~f`5q${U{9l?AZ4?lRabHOJiI=P2y+$ zUw#|o#sF5aV2u06es3d9;$@n|_r|z$sv-t`F~)sJllaLV>tx9RjA(r}$Pk5r_|>Nh z-F>P>VIYC|!2Kc))=^C+A_vh~JWPhY?GS8LmAb*9vPF7~@`3F7emT0(qa( zB)&_N_XAOR$$;2X00kf4h`Ur7;wsm`?|?3DA%cD<@efrp9AIc93DNC zJ=YaMxx95Es^jSGg3aX|JiB6KQ}Iz?b2+v@j<|8r{^K2Ego z*@*!B!?KGcY*e6H}58zVf7~?*sN$h9d3*ifZU)j4s?!|;s4y;nW_nKwhoP_ME zY;oJVnBYx`JLz5le5QO|W8980?nh(X)vmO2rmLPA;WFnXP2y7h#ont~&NZtH7Eu^n zRbqf;i0o4OM`_{2sBFFv-^%Ba{iSBu-2m_k{G}Ep_)*3U00000NkvXXu0mjfYeHky literal 0 HcmV?d00001 diff --git a/KylinNM/res/s/conning-s/5.png b/KylinNM/res/s/conning-s/5.png new file mode 100644 index 0000000000000000000000000000000000000000..089e6710a7ff22718fba95979002c4822652018f GIT binary patch literal 787 zcmV+u1MK{XP)OY zc%jtljUj-FwR+Cq48S<>y=4*0|3cskfNIW^M*w$$?=AA|9OVF1l+s;DucP^&j&@c=**z-s`b6asGmv_&8I-h=4l zD4fPoI8;iHNW>+{wo-Z)N8#^vtc?&@;9@{P`+@IG(>w%V$l-Zf%xm>Vlim?;iM|{7 z-rSz!t2A&C0ves0F-P=Qv?VqN#7HCw0l#R#_|3^ScPA^Qe*g$&*Js68-YccQ z0XU;4&Ll);{*h(uij&0c*G1AuV#>Iuf~SyZlf-cxg-@b$`1SJgg7f=Db9fh_%|4fO zOeoAAxa9LOE9QG5G3EIn@_t2tvG4HZU0L9k7!2`3MLf{ri}|)wYac=klZq`NaLU1` zY;a4S`1jPYa(KRV+5W`rv+DmMV6d9WNmxeeWr>aD?7VuFCvUbZQA^KXq@o~Mz%K;zI_QJmQ*N@^G zR z{CN*x7DwT#jO9WCuIC2;&H(iFSsaC@DeImRj~;|f9EFQK`v*C|vf~Q?=jO2(yDi`> zYXTDPyPkh1I$0h`^#FH00?;wL5hKeS!fOwRcU;f!ki2?L^oD8P*djpi34j(A$8i+S z3wHg3fO*yhaLtW~2L?$JP7b&%yetErCDgs7EwMQv#x?=Qd}P1}Wlu6?!Ost&je#LC zBGy_qA#gqaiU)4VomT-b6oO6}j=MMtZ#EvFTlBP#>juZSC&s)z*HCB(&Uk(JOUz4< zm@}g`m=0Z|=~9&Ab%QCLOT>a zXPy5dpjh2aA=*XiWr@XdDr?s)$x_^iSS-iHBWQ|%!J1nF-3AFL)=~((@aCjJ0*dt_ z1hl~n*vafvh@l~vIZO$CL0mMjd&Q6!-%7LKp4UFxsGSIH_bukWe0{vXy|&wjk{U;H zK-%rY$B*~7&pJsk^~UUqq#;?qdu$=PWjEAe@0MB@WB@TkNEPr2Z7=O>KUs>0>|TQ$ z52-e~SL^2NPo?Ycvb82)Lf#qmB(x z_0cS0bZa=xAz%m8Qp4$Q#`ASI8%Lo`JjpfkzpN`ao&)#-t`Pv^GBc1y00000NkvXX Hu0mjfMl2^Y literal 0 HcmV?d00001 diff --git a/KylinNM/res/s/conning-s/7.png b/KylinNM/res/s/conning-s/7.png new file mode 100644 index 0000000000000000000000000000000000000000..914deea16cfb64dd665e48013b8f988b18ac3673 GIT binary patch literal 736 zcmV<60w4W}P)a6qDS<8|vsg~c2Y+1hB_q4aWcY8(^;kaIx zh=xSeC!!uM#v%#>zp9-q-W(d>xZXJtT@%r$rr28|nnz(UZ6p9jnD|OPUa?VrYrc=d zU|leMD3{}U?}=zqG&Wa&<9h7gJ0d#AqbZC$m%WWbU)^6p$T$jvRd%3NK>h%7CiJ5) zxUWIZ7~p#X8*J+Vwu=QQ@-Ezc6NSN|2C>A<#&b~^d}AB1)y)%Osfk^&t z|IHwRZTL#W9k}*9@FnB+Cgd7(xmO}&Ij-fuGkSIXBcAzn-Ch-r>kY8PjL#fb;(<^2 zp0-6!FP>N+qe#49VLhbCOxhw>5RMUZTPkQ)HB+q-z#;WuXsMzxKO)Z|2v^_Io5~3V z2v<)F*ejDQVxB@>Pf;o6kqC4O9- zP=IhPE!j5NE!fLj1vq30W)@OnXM<=N!oeHgTCzZI5Us@mZ0~G*9sWp@cx{Z$squjW z#2EW~n#6zA#E~)fqcQfMG>LyTBxn2gI2AobVeo^W>q3-0K%NKiUR_S~}ME?NJ)c+OB SYwphg0000v1Y zx`0+c(i*`Wz@#klY8T-~p13Y7i+tLs0LjMy#3~^il|{ajRgOF{;)TOoH$H+t$d~+J zZ*tgmoSAc$eG~`3|@xF-SBxB})GQT;n-@uS$SlKWVg& zx*(6@q|dBaF@WVd;&fkKp(`-pb>4x`6-%b%lNv#X2N1tH37*7e`b;xjsdK1qUh1-!oBH;&$vm=w@URV7g_fl zPIcq6ZTSbj~fs5A0ypLDIHCG344EJ!~z-bcH?Dw6dN)e>UVp zw=LgxFp>VSDvNxvY2Jf4WDIG*RkXae<9Z)X@q2FQ{c$wQSY&M0Oifc-!>$U5FMUh4 y*Kw}#bnHN4Lb_Q!h+4?12$__23Et-Z0>BqNPvgI!5!>$o0000XOOS6Hh&O@cjd-P$*PY6$*u_ zFA7t9@vZM<5*NkCOcjb*=51$5KS)Eb!<*@3n#8zFkoN>1Vmcd>)+nLUi--@9LfU}Z! zuJbINY-Ipv-2>1xvl--BT5Ot)=`Z~{T(aA(3?TdtK)^NaZyMmQ`e8r`KBN08ZKk?9 zH1L4%6QA%fim#52jz2%%-(KavGi_b2dEpU$Lrz!UIW{C_xA#55m633{7aln0bL3;g z8Tgzb4CWR%uDx0|@Br@`__cVLO&7v;j8!nPREqO0)l|4-MKUxywj0dtqGu0q}N$$kNe0FSqJqPdw=9%GA|M~)d00000NkvXXu0mjf>-rQ~ literal 0 HcmV?d00001 diff --git a/KylinNM/res/s/rescan/1.png b/KylinNM/res/s/rescan/1.png new file mode 100644 index 0000000000000000000000000000000000000000..19c4f777ef4a38fc914b83636580f3e5390fd713 GIT binary patch literal 15281 zcmeI3duUr#9LH~MowIgDYIP3~?xGtGy}6GhH@Vj|UDmW~(X_Rz*nzs<$4PTplAGn; zENR7At03DH!9SdvQ|99jL9L1)ihuYhf}k+z5JW|sf;tr^qoVZ{&&~5RO?LW+f1W~` zU(WgdzQ1!n=W*{p_weZO?hWl*+9`_KFf4IIp57-`!uQ?%o&sAksgak-pA)xYv}CDYMpwN%O~(}Nt@ypm>lH^(NEY@1-xZ1g&c< zNPr})V?hRdLjdWxX`5EuY*Mn?U4+=Eux-Se0Cakqrh&ZCB+cn5r4`pyX{v(|N;E2u zWDL0cl|;nUvd(o4MMAK{tsA-w6h-8 z(8q?GAY&W^`as4^7B@^<$ymTyygjbU z>aFguGS%YpmyOk9(j^fRivdRAyc{F&UWHLLPGn+06}=wK<5vTGkX@Bt8xt%LEOqO( zxhfq#umN6mb!Ewt2r71Z@eI#{0IbTv=>r}h#k{)8`Mi}{h*_;x1r7pW_0kzM zEZfXXBOT5Vor7f$IIyy{(vdLM$gxTjo&mC=IY(B=Qs7)T!$#U_LoQ`Fg-U`3Q?RyW zu`0cJja(e0LhuX^FucGB(l|W8eID84zmW^b99J1?QPteUGGlg5$$;<_+(iXAeN$`oMJsi`1aYebZ_z1(Le%nTeB2zi2R zqh=(_xj#53I$TIfam&CHWmQ|fe!@A*64|2OFL3aW<(>cKRJ6N3b??Pf(eC=xTITI3 z!ww1MYIV&>mU9!6i!k=U2R9HBeLjyb$ksR4XN($okeG-V@ZM%QhX^m0bLu-9Chl#P zh;)<_OB)4w#47HiQY(CbgU^m^_2F^l7OS3I|8r2gqW{?#wIfAP8rYcN!XiQi1Q$vJ z8xveuM5ut^LTO-Qf(wfX6%bq~4QxzsVG*GMf(xaAjR`I+B2++dp){~D!G%SH3J5Ng z1~w+Ru!v9r!G+Sm#sn7@5h@_KP#V~n;KCw81q2sL0~-@uSVX9R;6iC&V}c8d2o(@q zC=F~(aA6Ul0)h*rfsF|+EFx4uaG^A?F~NmJgbD~Qlm<2?xUh&&0l|gRz{Ug@77;2S zxKJ9{nBc-9LIngDN&_1cTv$Y?fZ#%DU}J&{iwG4ETqq4}d{JEOwbwC03ciDxgD+cN z*yPE=7d2^Za4bquGutR?_5q4oF2L_|6qV&D>c>fnlAoZcjpk$fJ{W-Sf?7k7@Ob`n z^^ZcKccD-yY^K{{CjQle-{e-zK6PJVKdas{S+2o zKGCunHowz(IN7OP{}1fk4lPO^b6+gO-}~X`(Fe91a{7c1JO6rrcHO^)0{h@gr_REb zkK^t0@M~Yk!to#AE2z-*!PDXPos-@DXN6NQg^%;EcYQ&>Hnwf-jpt6cog3-+>gT0j zq{6$)?u8>=8y>%zy864x-ho_;^yjf7e_pb9Y;OHuXOxcFw!RzQdS>U*b32x&FDsfp zzj$+JOUsq(zJ6r)HIHb6Cl~JR?jC;blNV0*ZR_d`FI^wGu;byM7u;R<37-Zp^lm?M z;;Hk>H5<>rsUE$2aWS#=<5Zix`|VxXBiqDR4n6%%=f;!#ts(kudW5H{~BQp=d Q>7b~g{^7{{uF1Lo0Gc|_(EtDd literal 0 HcmV?d00001 diff --git a/KylinNM/res/s/rescan/10.png b/KylinNM/res/s/rescan/10.png new file mode 100644 index 0000000000000000000000000000000000000000..2b4c501f86f64ae1b247947c41411f28f28de99b GIT binary patch literal 15390 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 H9i9F^PmSz+ literal 0 HcmV?d00001 diff --git a/KylinNM/res/s/rescan/11.png b/KylinNM/res/s/rescan/11.png new file mode 100644 index 0000000000000000000000000000000000000000..c43ee8ce4a388a9ed1d32841e4eb34fda3028eba GIT binary patch literal 15383 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 literal 0 HcmV?d00001 diff --git a/KylinNM/res/s/rescan/2.png b/KylinNM/res/s/rescan/2.png new file mode 100644 index 0000000000000000000000000000000000000000..406289221559917c2bdba0eadc889f98d08e257b GIT binary patch literal 15386 zcmeI3e{37&8ON`rNt@7A>5qwa>$Z~%TekLcKm6l-7sny7lg1kELh258V_MF4uai@4 zpXa;OPGKT#fC23otB|M=gEou~bqr-c1eJyq(-O1^q=NWog>}FxO;EN`1`=bc=Dp{i zZydXC{@~B|B-@YQ_xn80d!P4t-@AX_`$u>0>g@Pp2Srhxp^@Mi{Oz(oYuhMl)#NAV zKZSqV(`W&~p!(2EW0F(O2!3@}QaCbyKxL=htN58+rmy=Psr=}p}1 zo|0R!rb<)ogHXa@`P&&CmcNn+8tRmNU!kBMcJyevCIdzB@T%9#XaevsV%)_uu^7-8 z(W&tb1}dPUbq@WK@pjl;u09I<`NmjSk`_2pyIBo8DO2aLkGI7Z-I3KLT~4-*G5kISh# z#h91(vCG_RV}c2MrEaY@SEbDdHo&{CR+cPz;2epX#>7=O&+t6(!mJ#eKHvmW+@-}h zx2uv130BKhfr9{8UOFSXX&ISmq{AMfy|L^8KU~^c=}71+cVz%g^mFiet0QO3k0~w&e!iX3~VkNgz?@}9AEgA?a7O0Zuxd`upfMq9F zAzckpQr5)?8=4-UUJ|3hY}qjoWql0zbk`J1Sy2>Ji^S1XIc{WB3sRY5QR`ynP5VeEk~ZouzxyPa+yTi;yoF{kimp#p*nrGd2xE-WHcKyaZnur|Sk zMT80nE|dn=Cb+POPyxY((!kmT7ZwpJAh=K(SexL&B0>cO7fJ(b6I@tCsDR)?X<%)F z3yTO95L_q?tW9uX5upNt3#Eaz2`(%mR6uZ{G_W?og++u42riTc)+V^Hh)@B+h0?&< z*TmIPJB|rb@C;@a9=3dPm)}0=Nvk7cVTw9<6GhF;Qq*S!`1?LZO>q=;d7Pr;;}ms+ zaqI_+!|*KVUqZowNbb$p)k2}arKM%|#_bQkXdm`06wYd;-^2WKPL9jLV-AUX+P5j= z9l{^#HNd2jFUZoP5ak6su*_ki>6 zcJcHht%0?@zwQ6RrH|h`cXY>KiybC${$%HM16{4JJud|^$2z*Hjhj2xJowmCt7ZD+ z+K&8qm+R1zEvx_i-7_--FP`vR*mm^l$H)5LSYzJ)3O&1FxA$*{nE2LpR|YTd{qlTh z!>#ZBGn+d8i~FMQKKNYpJ5PV~s(AZ+cYbT<<#jLrS^cs5RP=v$nD?!JV%we4F9R1> z&vmao_x77Ieevrz=dbVDxb6Wu)HV5)nTV2~z4@IhIj(zW+x^?moH`xqJazi?@s}3 zTzF~Mv;TRf?fBeJKDc<|Q2T%H`iJjV8~-|g?)}t5Z9hHo&TmFSGp@z2{j2ZshfW;b zw0_H_9TTHRPJZjk@6Sqa4<43>-&-_#+qT~E%tzl3ey}k0C2G}cPqfZHa_k;^B11#F KgU=3*AO1hkHSXyE literal 0 HcmV?d00001 diff --git a/KylinNM/res/s/rescan/3.png b/KylinNM/res/s/rescan/3.png new file mode 100644 index 0000000000000000000000000000000000000000..d6a64bc5cd82df44ed1a54a1bf9b24467f8b5659 GIT binary patch literal 15391 zcmeI3eQXow9mmg2$w&YdhSCHYG|v=DTh4dq^Xtuamc-7I6;mr@5G1DgeD~PCVf&2l z96PAeAcR8Oy+{=+X_++2w63jGRFJlIV(LOivQ1QUXwujw)NKf^u0byzfsUU9txWW)hsYOtWH|uc(>W*v2qTN+h*Ttwy~U(VLc3I zr&&&5Jp#*_D<6x$6Gq;w91}(ZLzQyyKc6L`>luMz^7*_q&s)`OoZ&nk55w9Rn~jDF zTANJkVu4O;8_OnDp8(LLtdh|cHElM0#i*LoeHM#RsP?H`S1J<<)k>wc3O&e?DTo<{ zv$9Mo#Wc&*^ifbBWYbJ-Y%&9wQJ|^0tOTHZV{@b61RP!EYL{RXoB(OPsTG{FI~z5t zFH5zYOfH)&ftDo(BtZ(+(I5k^DS%8u)m1H_wkTQaE**Q8I1v2g8 zWiINHqGHtM@-i)L>Z__!s!0jk0PF1U_w=*Zrh-fhs{-cBz2MP zNLtgyv;-<6SK!D+9)6^lOgrpuJ8iewowS>^1KI(vMgqLY$=hsIYb1S#fcZJb@CqY~?+V<76Z^Ahih zx;ZbiD7`)=SioEEmg{p>K73#Uyz1J@5+Zv!Fi?x1Ya!(+F435cA-mag%VFx9n2VvYOW(thz2tYi%zj#B?0Uex{mOZD>sK zGRD*;m4!1`Ze2K`)zz_h0T?@77|a3@E{ri%O)lkf8dK`ys=I-JsDm15jveEi0?P~b z8tIxzS=rzbQe`DJxxk~zY#APive5(H-DOc1m*j$K5g%C+Vrn)e>V739#sQN_$GuFw z5mDCn@}Qbjvv63z&wH6>%}ADUe=tsTxRAusmVqbAqP9l;xN(#vb0xjY#=;N78GkFO zXm?}k?mMTV-HoaB%&(;t-4BoB(m)%Uku2jTCX`_8fDdlK?{qkLhnHz=Zp;WP1&|yM zDDd8<8HWhxtmHIyG)>&yDiP_ZB$hV{@`zR1N99)d00*BPncBnS!Yx)ixjy5denp?L zF=|JOpfs>C!G%SH3J5Ng1~w+Ru!v9r!G+Sm#sn7@5h@_KP#V~n;KCw81q2sL0~-@u zSVX9R;6iC&V}c8d2o(@qC=F~(aA6Ul0)h*rfsF|+EFx4uaG^A?F~NmJgbD~Qlm<2? zxUh&&0l|gRz{Ug@77;2SxKJ9{nBc-9LIngDN&_1cTv$Y?fZ#%DU}J&{iwG4ETqq4} zOmJZlp#p*nrGbqJE-WHcKyaZnura}fMT80nE|dl~Cb+POPyxY((!j<97ZwpJAh=K( z*!YgPI_s}vf;4;wGY?<3JT-eg24B=POT(ifirT-4q7EFQsQ(q=?+uE|u@v=wgrbBQ zit1LM{>I!8d>8a@BZ2<0!k?p`6pMXrZEc4v8;_kgUiK^&-;DgG987U%*Y)(g0N-=i zdbr~ku}EY%9*MlTtR$NH`$5xuu~=LI-+p*Zjzn&~Jv%%5)-Bj_WaY}0{|Ybr152-g z(f8r^BS31@mVoZ;$-FYG2JG5=Mc)}-K-4Zq#1KjhwGaYw{tEVsL+fN0TZ;qYXuqU=+ zBD?d8_eK+KQyUU5A3VCOcjm?Ad(VEZ@3r5)dC9ev-^6ZvxBIxQ`|1bt?R~xMLkGe7 zOF#d0$15)%9X~nuqvO|}+}*WC^R9g;{=M8s*QSNTrl0-k`cK-vc{6k2ZpNOt?c27G zx#wH+H^RZ^*`s%TaAfM4x$f1|T~DPhI@f%m|M&BAmYJLPB~Nt!*Rk@ommW1I&WwNO z%6ps70qZAQ-uduYNAay^`6K`Si&GB=@B86<*NzVTkv;#?xBq%z*Uig=zFy}29__Kn z@z0NOzfya?{&@QScQ;J-vlsHoPj4SNc{n%yz$0DPXDkn|J00Bl@xFg}K0NtE*E6@C zbnkikc6ZO*+2e(C{F&K%-my#{3jg%H^9^~^G4K6_^;b;PiS{4gef3Kd9R~f8;P$}j IfymVV0e}wc{Qv*} literal 0 HcmV?d00001 diff --git a/KylinNM/res/s/rescan/4.png b/KylinNM/res/s/rescan/4.png new file mode 100644 index 0000000000000000000000000000000000000000..f92092f017eece76d62846c8888f7cc8689cbce6 GIT binary patch literal 15397 zcmeI3Yiu0V701UojuKafJZNhO3abf`VkP65*~je6u-?@1`UTnGI16#$1D?lSd&ut2 zWM}N1SS<-IrKtjqLTQC4Ehts1L9cg#Zp8G%N-1*(d%$NJMp&k8gt+%#P6xB935F3WStGv&O4xOppeS;&aofj4iue zbRn+{ZO_fpzJkQr+eLSK1$`s&AzzP~1-?+Hz-W9x^o6BPJ|v0$;1-{s;{}$JSU$*b zyv#{5$NNeb9c_hHB&#Ro;aFd(9Q@Zsk2{VfvurMx>&yi@&FmPd3hDpoN&5m+ACDKO81qv;_*tUj9sD!IkK)|v3w`T zrqgV_NZT0()lSxpv=jR+zzzf3oXBbb$~V+c)H?}#SGwCJB7o!6RSx*Ya%v=eQR*1T8k0cp%bMxJ zg0YmW4ZO+QMVDu9WrdZf7N);!Ebfyo36NM4FbWss7(X9W7**p0CJ9s_7|;S?RpcY= zqVQ^;V1Y=nS+9;&ar1!=c-NJgB})Pvqp+?sNiD=PJP$-zm4m|v0zgUzb(ISR%e9bX z)mmjZ2!O>?XTY!>GrJ#Y@Ve-2EW5zIh2C;Q%2*=D3Qf2N$cpA|Sy5YoW8rlh@m0HA zjPME-1$Bl1C`*|Zi^3b%$c=+c6z<_7!}}S(lzPNh-EHzQkhy5J9Sl;_Cg`&;1 zp?j_$iZ<7VRwM7t7*5n*s#fDhqP&NgT!66)zPN#?5DEoC5w_M{8!=?KAT<&*;Iqy4 zHW6MZ#nd*`4cyZx5NRj{78eTgid9%gMK64TgYS-P<>hhV607W7pR!TCqfa?8nvN7f zX<%)F3yTO95L_q?tW9uX5upNt3#Eaz2`(%mR6uZ{G_W?og++u42riTc)+V^Hh)@B+ zh0?&<1Q!+&Dj>K}8d#g)!XiQi1Q$vJYZF{pM5ut^LTO-af(wfX6%bq~4XjOYVG*GM zf(xaAwFxdPB2++dp){~I!G%SH3J5Ng2G%CHu!v9r!G+Sm+5{IC5h@_KP#Rd9;KCw8 z1q2sL18WmpSVX9R;6iC&ZGsDn2o(@qC=IMlaA6Ul0)h*rfwc)PEFx4uaG^A?_H}W! zR*z$X3_OFGgNH4jyKDX`JgDi@28QDlHMNnV4t|-UuI1tH-zaK=qo~WH6eT}RQJ*)T zd~mi8o(27IFxH)Lf3MEx^IMzYb<>8YPJ4$v^LfuTBR4mni9dbFJ4c$&Pv70${kiQI z`R3*x0s50g-So<{@bH5Lk3am;3pub6Hs6^zyy}BIjECMUUub!9^!~5IV-lC(=Sou{ zl|FZ}?Vyipc`H2qjrNCxW9NUf`uJ^U?p?9*m64t8o7WtB_kVXkoOyUqwEooBzkb`9 z%X863*Zv{6Y|HTrs}5~)FSdByNzBc(p5EF1=D+5S{^IzBO;2t8(G4Ac*>OisdF98; z-qo~|zdW8&gQvb_Uv_`~;k9=@`O-trpI);&a@R=nh1X9VVV1XyYezPG_w5(%{6Pmj zb?MK~pIUog2i@m>u%$ufBYh{&v_^zS?v2HuXD)+upoo{(-kewoN4Z1K{fu7s+flzb zeAL8(e;mlo*)z9XogUkAbT)jy+rRuhecu}E2>p(G_^&tqTYTgEx^qLP z#g(tka#7`y`QGeg+qt=e{oCLF_#yFteyD%_y7TX^o@_et*ohMd4}TP(td?WLGu$U|T2Rzr?~d3@ I+eWAV561ELkN^Mx literal 0 HcmV?d00001 diff --git a/KylinNM/res/s/rescan/5.png b/KylinNM/res/s/rescan/5.png new file mode 100644 index 0000000000000000000000000000000000000000..279fd345a487b9ae71b1280c822a3ec59aedd5f7 GIT binary patch literal 15381 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 K(1F&z&Hn?*+|`-@ literal 0 HcmV?d00001 diff --git a/KylinNM/res/s/rescan/8.png b/KylinNM/res/s/rescan/8.png new file mode 100644 index 0000000000000000000000000000000000000000..3930bef02fcfe8cf6679dbc7642a7645a41a945c GIT binary patch literal 15309 zcmeI3TWl0n7{><^jnop+VgMt$EMmAgJGZ^fu-&$FDXp?t+X}5WI+wHUgx#HWXG#}% zKxrUcLt=P9qrr*^m>87@6+{I=h#CwahD1R@(Fk552#T8EdSyZD|g*!T;mz?=3e|)TrJoN3X&kV-n3>CPm#^V}D0c zFT6OBqQu>LN2k>po-e6J%q44v0$izB0;(x0P?t)`>Jnf%6wsr`Yn{LE-RE@ZTCH=D zC(MQuji6U=UY-Q)%NKO0%a^F4=Bx`=1yT|;hyhD>q+-#yDWz(i8D9y;b~EF2WF*#- zT4#e@(9s!gbu=1D;PAM-w8{y7hfj2I9?{RcA8_z2=Ve%t;oLOKNvtTboFn&f2CHBc zNNN$OE!31N2Y=N%do3#=F-(7dzpGzx8Oa`o6Gf3>d4}g{sG!YdaZ66oadT?cB<~Xf zrkd0fmTtrywy&%heO9g0X%{Mda@Q40gu{hWaWh8`a%56+g5g{&6N@qBGEJ)u6bD&0 z)9hH508AS&jlQG`pnPh1qq$4r=yF#(0;9Q0LEI{91?TMgM&;^jY9S}lmyBjWYbpbx zAO`E0kO5Z~K%&>M471m$P_odSfmp7vrAI0Nw0kP1fs|e$&F;yjW!6-7ssj*8I4nJ! z)Zy}%qaj1>v#+Zq6oefvP1htK%U(|P`)Q2_UfLINbF`uWjrIu|r}$JwR(M~4sc2JN zRk>1SN(do=_cV$PY=d9mxVfTVmCVeSSsZIkzDCWj1e(qz|(Wfuz0^XiZ5Ymf0`~IcWlW@zy%? ztGBSja#St<;<3Yk_^)=Ni#;<E;8XlufEkuv91|=M$aZVR zxyl|sumN6mVP#387tWDS)98rm;b@Knepr=-(+30~M%9)xGOpXPX)7dzkq?%^fgJB#&8EL>q(tBk6$iD^WvemMymdf@g8W^Wp; z`7^}P*l6D*e2(dh^tfj9n=|sRL(?kTtG#l(2WUYipO|kbO$jje)J657GnZ{0I-&X1 zF?<2oJ6&o_0vRrh4#S9!cO7fJ&g6I@tCsDR)?X<%c53yTO95L_q?Y*}5P0rdM1`r_bD#ZT_X<KBgN5dR8Bk3;p* zvFA?z3(E^Hta|(W!t--3bZe`#xz@44+Xq_5hmN12M%T={xYIqbf5#hZC-#m$SU+mZ zN39>P`|jxb?8|4qnZ1{Ja_5OVk9F5R_#xMHcJ-83XCB$peqibLZTwrOsTDUr_T`Rq zHQPG>*l^FZ_s%gZXWcWUD|BjeyL5QU^!2OQUt6Y6o4KiJ`-JhY*MB~k9}fS%ClP?cWK{uFP+`^)bX=xf4lAb>h?g}0>@xTfzG*YWnNHLLf1Yf}IIFvB^S?|R4v&@y*H=#4qutNsV~BJ0!u literal 0 HcmV?d00001 diff --git a/KylinNM/res/s/rescan/9.png b/KylinNM/res/s/rescan/9.png new file mode 100644 index 0000000000000000000000000000000000000000..bd22fa1b32131cd241824416ecebb698f036e0d2 GIT binary patch literal 15391 zcmeI3eQXC|s;Rpx&=3DNn}4esD5@^@pVvQv zKUSx@hINWsv)=rxqnSdImBArnD0IIdsR^4Xs}W$!BvMdKQNEp-lqih?!x8}_ zO0vy*`SdGRiz2sKcRNFDDAfUCO4qms2F7~^rSVb8D_eK^8+{o88YF-rS~7`vQWr99 z)|{^ZW3!pDT5=L&w9VRX7PJh7`Yau)1}sjSi zOQoFDm=@20mL&$nK?2s%Ap@=|fK*I1R6VBFC|T;xL9ABTP@**enmsksKt`#NX7=RM za%(C-)jkL%6cTzh1ulOv9#Ew*^SZhNe%N7?6s%eH&$92fL@909k-(;n#HT;71Yv$JTAq%N`T zPU?o3lt5wR3LH6Smw^}scG~H11KJ53Jne}(B>2Tg?6N&7+vU>8k@O|j0iY>+fZVC6 ziQI-siJA_~#oJ~ruHMoPD^SfXe?eI~CfVzP#G-%}SqDq=oI|7|66>O)AmVb^CA&M~ z;e5=J^zxWs0bjmbF3(l|@PQ5Rs!J^Xs3?%*V4EN9+PxIbEcz`>3fpdS6^$09m9BNKg+eBBR zMq1QB`v}~A{`^g&uXu)7Iy%gogxj)xx000Aw0?Kdb#Yp4dnqO+M}X{SiiyRB$`l`C zPF-ACJahTh#S>aw9ZMI0xzmLX4Tx}I4615;IhRwJQXW^?4Fp63lt^>zAm0+>{C#K)8y z5oLKVcc^hygTn%TyN{{XjAWVj2lGUS3rWmv8F->BX{*!^nMYZCET?z#Ec`H>`CUjw zyDL+NZ=8yDSEiOTzmil8KVPU;(Trr7H!&dxV*oz50l&-Xv^#xFWpialpOOLbp@0JK zZMu1gaIQj5Wk=P-;aZ7EM;!Us6`?8uZJ9v5%1(#iEH2jwgJl#Nk4 zQUs-ejR`I+B2++dp){~D!G%SH3J5Ng1~w+Ru!v9r!G+Sm#sn7@5h@_KP#V~n;KCw8 z1q2sL0~-@uSVX9R;6iC&V}c8d2o(@qC=F~(aA6Ul0)h*rfsF|+EFx4uaG^A?F~NmJ zgbD~Qlm<2?xUh&&0l|gRz{Ug@77;2SxKJ9{nBc-9LIngDN&_1cTv$Y?fZ#%DU}J&{ ziwG4ETqq4}OmJZlp#p*nrGbqJE-WHcKyaZnura}fMT80nE|dl~Cb+POPyxY((!jv+(~#iW*}n>Qb1Zgh`5O zQGf8zTqk@N^g?%_eK7M@gN8W2^Qb|2{wJJMz)_O}|b3;*&3U-hX@Z_LB#x1TwU#IUG5TxeoV+1;Cf0m--jb<%XZD`cD~69uHk|tF^z3x( zmk)wOxWDh!Tm7#b>h*p6NcQMe-}~3D99;J;?o31Pg^LTl&+V=|Fny)@ogZzN9=&hj z8^MjvYuoC-elED?lk<}&cHMt&?cLv%r(51zNS%J`%D*-}{^jY<{riKto}>JVhWUqA z9@%zaQ|Dx4v;D4_jq^{vK6d<1&1>rJY`Hr1JK7a_;YoR9-Iakg|dmqOUg$yq6JHPR>JG1WH_5MxI+Kghjd)qm;f z)VAle;0-t9QRJIXA*u_^7!Fld?yD9F#Oy*0yc81)#mL{iE-fF-KQc1?^W%sqisK#!(53W``YJh2~15(#yUrKu?yLDl}a;A(RWd zoPUXq0`Zdk?ROrJ9*tJJ7A*~#6WT(MR_jl$Xb%*ME%x6Za}75Uh< zShktnJa z_2?MfXnVlwPp>F1azr`dKWTx2HTG>5ytxzVT~km?>pL77dvT1Odz-*O%|1`lkf<&p?||xf4qtI;$8>PJj^E*pXH@ zrW@l0=SRR+r45RW7HubF)yx1yXU{^Mh)~i1IW1q|7|+EdByw|OWu@`K#+Syd&u7%u zfe5gXpAanBq=GFpX2$X&&*sY7+BEOo2+sp;Aj&TG)|wd^-iNrE(~W@KKc0H zaoL_I_~M+oSexe1^4##Xtpf$Bhc+~`!0$ok!tl+Xt6wp&Cv7H4k4Z7)D`hbAlYcV; za|2PX^3Bc7*|fOSWm9QHz7nb>5g8PRnO}%f{#J64m~julI^Y_$=veYv%(BmjOhxrY zw_ql*6yF{hBDCNMuWqpr*p?cRIk<6|z;3`WMEGh4nIpXS{>yWvCARJCL)k33#+xHD z#%5^*v&@JW^mEJ0%m2F82EmY_T*!mSLyib+|DA`gqsT|83+B^xR?HKExrXy#hXe+9 zB>?e}NLV^IoXRcHGed4*0_8t8Hd>$6*Zbcptb1a(5jbNzyI(;^jO3j{pv_lt1Gj9B z6Z^a;wY$@)f7pj%3qA+qzd!T3Y1p57Whgchvz&$ZWu=o&-Id}Hxob)YLT)cfJATBu zjla5g6s787z&bZHG^GDC==eR}SII=zL5Ml)LZ}aUSz!K5oyTm&Cng85qp{Sq5k(HT zfaET`c0tf)G;SqHy=#rRbMA$MaD{scSPr!0M;?>NzqxPq>7d=O5{mus_^_xtv0k&d z9=7UN8){zK}IYT|Gn|y3;Bcx#(a@gz}I5`HSm%}a#L|t{#IuY zcO9j#TD<*{SutG~>W$qMsjf-`LYU6o=DwKaqYpePeu+s~`$8#nG}$dEDCl;M-2HQs zr^%xjI-Hc0)N7;CuWESyIQwFl%^8H5MrNyo+oSGzZ~gRCH7Jd&gJ${&azKoom=0Q7 zxL+%7>zWgdtQ0Nb++b8m6up3uqv)VUIaJIII;(Q_BiTCCs(4Enz~Gg=qj2@Gzr3|C;1XM~gbdnq~L zY?dQi{$Vo+k55;Gi7F~8MHX-TE_X9H3d=O|ITdjf9HC^H?W{|REs~)PHl5K^^8Op& zlQxI+p5Ni(fE#H0X&W4|Ov&`C7TsAHQ!C+xYT!o{27}3Dp6;4F6aPlO^J%6FvB>RO z+p6DxTU&43zrVl&;p5|L<^=;r8!@&~(1LlA$V*n_eLudXno6+KToH))^HiHqnKLyn z%Ky&cPHFX?x_(WSxx{AYsJ-AE8sPM6)>63P1V_c1^|wBa%~mgwL%Ypmt(#UnrUd#^ zunuM}8`smVMiS|gtS~fFV|m{pE-&?LH?n8}ZthsnvU(TI)YFeSW` z{gqer2|ePO?su7x{52kjon#Y_?^7v~g^@1Cava0wUvrO-&cI2O;&H(Q>Z@$mijL=b z^H6wT({V8cAx;v6+K`2WMLU?jK;}q}|GbieC58%NMhIWcz+B3yP@{5Fv|;WiJx1jA zg-&b&(`&B_Tto;7Hn{6~zA#xiFbOFTcrPbNAZ%i6tQPYlY^(snjK0`Ze#iuHmw#5; zqPXC8LXyhH_#+iv-N7AEgIz%gO6fyz%NNSKlp}bR95**S>$zxo`|ijKU8^fj1KIKV zT&na~V&(L1b1>OimCLTl0oKtaw)KJ{?Ihvhu$GVpn9JciH;bi zl}x^6dOf7d0bFICARG|e4r7*`Yi)!}b%Rx<)`W%|2mmu;}#t+*SSc!`ybZSduo zD;u<3|59Lnak3Ihe*0v3b(Pmy=DlR>;v}f98Xg|LXJ==}25Cp?$E~Gt!_7*8WE-t? zoLgRBx0uL`mJ7ZBNgUhe`dd|X*|P4Swc&QrlwwAE`!uXigd)K4m|Q{EKAb>3k|zZi zARR!p@C`!9;ji^J)`0~NR|Rn>)Mi<$*(P(5*h_6ofqt-YpQBKlyefEI+~lCf1PelNj@Z*)u@O^T;!a|sVWVbUB& zdxNP1>j#3-iE>w6v(k*=k^NC=R}*&4KXq%tlvpv7-1_ahsW z_{F|Iq1nw*>sh?Sw~;*&wWjf#h-t_E%ZE9qX%hb+&l*dlhja4eH~17oQ3@G+UAph_ z_T7XV{eN><1uz?cNIm=hTdASzB?7oAHMq+vf{Ct_tkvK~ty;tVGoLqps=oL)Hq~_m zYVD;PpwVKvBq`ETtj0HD!k}!T+Qmr&_Xrisc>wx-5|djk@5y+-&h;G>y*#|byAx|P zRo5b-cFmSB#)uByXxa#lJbvNyK2~U9V4bFXoVgP>RsAaX&h4+~7HjBG<_V9tLi6Oh zvWuk8=zG+Qlir@Y#Q%VADP7;r=u#d6Tzxs0VSFRfK6(|MJUZdYjC2L0l8G% z?Bn_H%xVffHUN7M258-qa%N17n>Z6YW}YR1x>Ybw@t`o!z%m18@A$XR8a@8C`8~xqnY9{XCA|}zZU51CSDIZYb;0ZZM-4}T<|2;!B5Ct97Gz+ zIH56{GqXmmeHxR$)1Q~cHprivAPfo>YPT9?xVm$|_xo>~K~Zs}C1`tUlB8OuJe(BF zlkWjxKBql!zHrmoQLH*JVyi!;R+jiyBR~Lzh)co#6FL!Bnx>=StON>g$x3riH7iXD z0D8f1!k}`{B`&F0qfuo>;0GD$)iJUyM_%r`Ak8&xo-6P}6htN^4qml9n-!bFZ4Xf% zkzv#I5iC@@u#N+f!5y0%!-4;aW_!YQM$FTGeV$7=MRT?SN_=2zkW>w|VCRjlqS18! z1)ryr0!&WJ)NAe+L4gyu{_I_C`V~^s+*-y2HkmtHfcEG;1Y5-ode6S*30&l2d{p?O zhwtcvkh5%P1{7aIC?_(9r%v=VL$3qJ|fBQhJfdGF) zs5A&sVDgIE`c_ZNPE}fV*0EO~l1UL*rmY3~cA$L2>(B^utueLm`4|H-Z)Ib{a%Ot^ zjc`JLF&f_`iAzY1xxak&kV~N_Qge7$!P06c3D>PtB8mCy{kg`aZ*d^!jb>E58oMK+ z)jXc~hDi%=QNA_<0pfSL?iD1|(z+j$Yr(&MurLyKu49eFQa2A;PXh5rPpCNKm!tf~ z#^q}52=<+bY5#4%8gI^*g^HNJFz~y%>8IG9j?^E2L0j1i$7QSi!o;`NZ62>R?L*ry zmx<`AD&j+WuyNgK4lJcZ6Q0+6n5@A!nZez0_F-F{sNlxZ(npWmcVwm}Jg-aupI_ps zy}q;i{OCee7b7~P)i=7lrNKPKjOSlpP4!+UnkObDB~hqUYRKwgk_#6cc>)7;B?Dh{ zI+vcg!-+Q?4@UQ0YkL_y`Ew1c>00iiqP=T8rkQOvvH%Ve5&w_ez*OUdB|*HeiLyO2 z4`Woso#H($nbWvvUq4h^V+F)&60b?hJQk5mqxN@xL?eX8)mS~Hy)&0UAZ+a zP6wUi(uj+ToBTh!<;{egR0{M=OvO^i>n7W%ZxBQst>&=fKT>&rZsQs7&ulm`=aS!1 z-XM>AQyKuOknic!r%bf+&55rGhkM_cAuwSi>OBrA*qy*MBv3J$-EBUOA**-P-yVw^ z{35HfJ4y4(S7WS^hZ6{dPU6s)iIdZ%Ad-&h9g#!QECykS9=h4&OX2(=VB(RaC$;y3q6pWUTG1 zu~~vqt4Q-;G+1mp`V)B?{G32foP!e^CEEVZ!IX|=Kh!cnt;_o=6ayf7;VeGu5&s{s azlx!?%V$nj#fY@uJkV6VjW1WW{`Y^J`f%6) literal 0 HcmV?d00001 diff --git a/KylinNM/res/w/pb-top-wifi-offline.png b/KylinNM/res/w/pb-top-wifi-offline.png new file mode 100644 index 0000000000000000000000000000000000000000..89310de7e968f80df1320359b4e0538cd970941f GIT binary patch literal 1136 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 BH^=}0 literal 0 HcmV?d00001 diff --git a/KylinNM/res/w/wifi-full-pwd-off.png b/KylinNM/res/w/wifi-full-pwd-off.png new file mode 100644 index 0000000000000000000000000000000000000000..79490900c72599a6fd8a0d967bf0fe204e7f18c6 GIT binary patch literal 1747 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 ZzXAWozE3t09x4C;002ovPDHLkV1kU69^3!` literal 0 HcmV?d00001 diff --git a/KylinNM/res/w/wifi-high-pwd-off.png b/KylinNM/res/w/wifi-high-pwd-off.png new file mode 100644 index 0000000000000000000000000000000000000000..2ca3038c434139a8bd62a930a1992db910c53440 GIT binary patch literal 1795 zcmV+e2mJVnP)j_c9MJfbv!2Q8WNFE_jq%C4!SMQpD>$;*`E)NhtwK3+#;NakO*L5qK zu^EW~r$?bs*lmp2r?pOPW`oVjBH~Ic7Mq`(oV*?-%{C;E$z*mJWA-EBT?O7IDl0;W zxzndl|F@G9LJ7F8+gqtr-ffH-+{laUDI$7P2(bhJtF>C~Rw|Y9mzS5<^E#DES+yG1 zu+`)HelI_&*UW|v6hf5K>GVHb*Ij9cqpbnQadrd1BZw%%z!Q-{#GA=v^2W%>$l~F{ zhc{J(=gyt8E?v6RFN7E}#yr@m0dUb9V`fXG(tkP@alNy=lZYM)18!NCPeiYz(`ha^ zf1~I`dCz2KW@flC3=1Jb8-rz8ul4oy&AP5@8g`ozVDL*zOON@!|3KSNA(Cy|^W}2+ z)w5^M*1~|ft{Y!jSsC(u|E&PP^@Bo)*8$*-;o;#M$BrFa3)4Mvl2xTWx)R=FFMI+}zwJi0DIzcnAPSh$tQc zl8IkN#ESs%{J_A#1=n?NwaRk+mCNP*M8rTI;VO;unZ$SCmv~(`gm~;1>Y!L#5OUt?%>s{5yQ#=ZM^~;_>+G)YR0qMg)2m z7Z*nWAkoY%M2f{?S6L{ioylalMka{p@Kz>y8%$5f z3)Vo-!otD*?c&9Y#{pohLyB}0h^7uAa)T%h4i0`Rm|D49 z?g8KTAFFr8HAKAJNax!M<>=9)iJqRGhY<0`)YR0Q4a-u>JsfHx`fw+Ni1;!9%pl@5 zV+=>`x@B30h`259u6MU5h-f$v=|2F#PlXVFC!$%NBmAzl-p0np`q$Rh_6`gTyc8t2 z;UF0u9o=88R-Xrey&*L+i-^A@qGyy+m!pIrm&?6ZYyB}q{G;c2e~O|LCex8XHk;jN zjF};#c7=2k5sx1_bm;76huBsC-_Zy<8Iw}}iikebrhBMFBH>O>PL{V5OgFYVMnFpW z5dgSAL`gQzMTUq+JkR@ayUlp``BF-tL_BWpK z&8}Llz8L2DHg(4-_`cS3>WV-vmwQ}m{Tm|M=#s;CU%o5|MEtc<>QfD1nM`Kf81wA_ zP)^aO;_>*;ZQItXtE*okqVEDgtRW+W_>Sj!JWQLbD*{r=YedAut##c(#Agwa&o_TQ zc<|tBSFT*KDwWDG5hXm&d$FM)rMyf;2O7T-aZD*S6*TBL&L{vhpYa4%O7W3rYXmZx z%vX#tKM3Y71GjBEbL!Nomm<~`Px`ksWP5vihfbV0aWkkpIy(BcYPEVj@NB76D)nud z02^=|=W77)ef}R2^Gc~=6yOaR$8m1^E>tR&!fuBh<+@6t0Eo}=*`-t}Jr@;xeYX6J zi1tO*xy|E<{|103l~R0$>i)x4*yi=i(dUWiNg{ewDK#9tOGqhSj@o&`_TbsTcDJ2C zKA#_|)oM=zz~hb0TT??ysktk4Q6xSHLZ$P%8+>aV`M>A0wKpl|XCmcT1#U zce@F2UmxGG1Q@uK@;DK>VJUP^N1y>ZlgWJ381p^;y`xenm^xBQKIBdASOQEYo6UZ* zSS(%$65A0e)Ty)F(`__LOT4ECSwsSSP5lV~?Az=>Lqz_l%SVYX-nFAd6cXDhv#o<{ lJAp;Db)P^~Pu(rK{{cPwD_-QQroI3G002ovPDHLkV1hOEYcc=; literal 0 HcmV?d00001 diff --git a/KylinNM/res/w/wifi-high-pwd.png b/KylinNM/res/w/wifi-high-pwd.png new file mode 100644 index 0000000000000000000000000000000000000000..3b1f477b5ab9acbc61bf2d004f08a1c8190e43f8 GIT binary patch literal 1894 zcmV-s2buVZP)?FG;{?(WwFCia>|OGwNvr-qeV_*QS1VCaOGQl8 zBDJcj_E*)1`uvd!go?B4O-vD3JQ^j!_F9xaoI>KnwLNR+;n>*m&fNP|vtc*u^{jXN zh#E_oU%NBsocn#}eCOOd!?eaPUgP-bCV~HV1#ed1W(8K5fmM?b0N$1ut&moQmwi_} zLg+eWOb?Nt6s9fmcudUsO|@>$9C$Q5!^~JA_N@D|33LFWyL-557J85^S;;Jd3aq&S zC0LUHE_;B>nzv$dvgflnWste(xxY$3kB zz>A66L_qkkH`X|@SQ1JSXpN7oQ>1+Yq;4sfMb-$yQd64doram2wY=g=Kvh_#kXc=O zl%~yMM${cW4zk+(uR%S#h{KGqg~zk{!m$P%!@ckMWMk7-h!Q*fb+3%;q$spAKlW{aQi zO~N-y>Aj@rGa&3FPzw=x9h1Op5|APx{w9DogMm=8XE$Fdwzd0@gxDe#YMkN}lE@Nh zd3C%IbJkss;62)K@1D93ibq;Y+-N4o6N>0d1gdr;m4*7{OT=_o)5ULkKUddR{QSP| zvD-5lY;sJ7G6(zj-8PaX@ZcM#{gcz47Lex4YfB@Lsh{fKS2t1^V0*&2BLloZ%m)Z4 zJBHm8UcRH2V!a#>`m0--*;dU*ggiK&^2W~oqNcl|o@{{8J})x}(}{eU*u zl|DqkGo>Wr9;~w^Gg~0~`7MFa_X?%e8Xu`ug6_4aB9ol+*C2I#=T$k+%i!MRDPNj` z8~vt8zq7AyDz{AQvGE3J%EKV~>|!99=?uVc38oQZ9sy{S8492=Ry&-YDP3A84SEmG2(AT&CH zXC5S?KQr@RQthFXn<}(+Pkd6D>JbHTVjxy~(v43kn?(t1?>^sTn&w*oEodZW8fW4| zceaOq^%5`dB)Jmc`3f!;B$6;*0kChu^kDHb&8K(tK2klo8X#5@W6=sk;^Pkz$s~v> zvJGQ58S~c%+C#r{S~UAo^XmD#rKNTdVG|UpnK{G6XNlAhApTiAW$ZW)ygj~Q`jUEz zi5>&!&Z}$De?W9(eUA@nn0Q z-LUy9^2Ws~nwfhVpeG5m_^Fi{em^x79>{F)QpwlK?kCL9g!Zr#30DN1m`9FIY$s{G zxLC&L`7wVu5Djn1-Lqx$(Ixp_smwc6ZzxcueX3Ya%OcQvG`-7`>KI}1mmDUR$nG@| z%+!^N)<<)|niIzJ1U#3YCiXdr=~)l)pDeDBkTYK)rXK+CLjE2zr&7_deOSE4sZ0+$K5j{*zorPkF4QhFcX~a(Z=Kvzb=pnL=D0CMg!MUF zp#?(yZ5zf5eYba|>oZD?7Qmezh=n(-n1CIixyv|6pkLU(1=Cag?ctxg0iKsod~Du# zp@CSvqZ*pa*ey#!IS^Zu=`TR3+5Tw#5x3y&y#*xyNJLF;eTwYPF=>ydVs$`V z;>FTvjvLaRO3Tdt$8M|!V&UvZfK?^X95;>=VMigjg#_knf4M|*aaWZy9;0pu+|V1DD+2awD$Cyk+_dx!jTJM_5wtH~x1)p`602#msvFsA0&^Sd gCIPpZx?Xnw18^cQUS@^?JOBUy07*qoM6N<$f>L&mZvX%Q literal 0 HcmV?d00001 diff --git a/KylinNM/res/w/wifi-high.png b/KylinNM/res/w/wifi-high.png new file mode 100644 index 0000000000000000000000000000000000000000..f6ab167a6fc0f4617e3739f6e37799c09d335b80 GIT binary patch literal 1820 zcmV+%2jlpOP)~p^FnU1SOuH$=M z4Wyi3mcRG%Jn!W>-(mWMKl};DPiqAJ-yK}*z*+|`vjTM+AppE1I^~v@+sUpahY-3> z8Ph@3KnmwAayU$+8%(t>ZI0dMUtnfjCibj*%>*KV7#^NzT7>RpOExkKuL5h8fD)`p z0P_xDUURzV*3WzAp1PZ5CBFGKRTJ1VkhSTY* zd^IsEB4CflO+zWaDdYyS$Wu*%YHf9*1*!Cjkvm$aDjBn~1U`R!u6Z%Du$lPE0?#LE z69M5r@o?t{rHW9LKwxlkog!@uNL^j7imVZYrOs)Z^E}MVqU97z9_6-7A+vMsP@1-g z1x?pn4S-sw7#?X!FSz^;>YAE^HRC1Gb z%}3QP;en&jCcG1k8B_Ct^)>}rQ~>OycQj!93KfYiL`aYC2?3}nmoP{yL&4DY!c@d++9+^%|cVe@{Q5{j?}W_0YDp% zB@YqslVT%MIoQIM%xp2yZyP_vU6O36!=1x38%)!D8K6~*#7rqBKJ@9J@A0R& zwwt7VqaBE)zM%-eBA|WMk~5!mI$U2kwXf++F`Okk&>c0N1#su;z~T|jrAOjlYM!g4 z;^l1iypp==!kl^#pa)h91HUj9_V2BYz+mcDA~^&i_r+Oe3(Vgd3;LcY6fpZy^Zd+K zX{lXA*aU^Fyt3d~2LG3Y_K%Ui<~NGnGJB$_g9`G`Dzy%67(IfiGc02J!JVnYAw` zLT#~{cVOAECzjgbAeuiFY_k`3<>`L$!zQ|5J^)hRAyC;{YpyRd&lBh|r`vt_)L!pQ z?wKtdP@-uh)D|x{eq~jtSb*+hAM7M)9W7h&vIuyB1szQE`~P0fw9t7?1OmsBH(OF2 zCzSrkVY;YJAe8=AW?B*&g+}|@U#ONuO$53Jjk5*c3rtG{8el<3p!C;o2(?VxfX1UU z?V5(|QptOW=q>^^tOCk3n+W<_EBTDPWE87qY}@xlliyT?KjumvGY>c&t{;@rY}+^R zx7m#{Z9W2E|0O_#_--QT`+c=|YU+TUq$_6JN1#90zXj8eMuYx`t4ZD+HSP!KQ6dPz z;(MbZ-=o#|FWc5kAlr$U@f8Bh(NOzywItsjOWsaQjU)ZOzt-Y!`&<@*LP_oG!Lvig zL4d!`z&2#xfBYs9Ke;8?_GGQsj$#DsN?`Z!Ip2lM!qY^!xrpTb6Qu7S3%8!Fwc`1H zbtTX>XuL**U8TS=(dk6E{WBj+0zFaVAqD)RG&p{EEbRZ`$C7{zsyk*Jfqou%f4;nuP;c(W5`IloM|3mc#f5kem4Aj1g zgW#(5Jy8kax~fnm3F~dMMxfpmtv23T2db@Dz3taJQ16OX8}EMwIM+`(B{r)70000< KMNUMnLSTYxlXzkP literal 0 HcmV?d00001 diff --git a/KylinNM/res/w/wifi-low-off.png b/KylinNM/res/w/wifi-low-off.png new file mode 100644 index 0000000000000000000000000000000000000000..60e238a281ed85a16aab25d03384cefb691d70df GIT binary patch literal 1665 zcmV-{27dX8P) zj-PEi@VYa&oq_EPY%&6EDl*;E|baRZrr$GH= z35TPBNF=hGG4?(Hpc{B01P6d8e!qWlaBwht;>3xjn(+Gd>(b23Ob8MCIOn_S5dbwk z=X`c*YU**_CN49}yF!S)Zonl;vV;(S1%p9qILj+LjeI9(Zr;2}#Bcx+U5P=Gr2jfQ zJ7;xW=M}r_86fa0D=SAW%j$JZ6@ZXsd46DE;L)j5r%X3sWQQ@PGREX$v8a%o&*vj~ za@MFmyw=9alP8rxAh4e?_BIiR3y!5wD0H`CXHBRa`6EJzT~11fxQvK%iA17M`<--` zmX^Y@EQf5{4oQ-|0W&dtp|bK-yMzg#YNNC*-3a$?PaXf)bSHSZ#v zNhXsIYAH?E^__)6p`UZUyVZG6F2_Y#mKWmj`19KHM@B{tT9!o_+3={UIvbD2=PMcT zWwY5q##l$Sq^hcO@pwE_{Z7*~KVyvg#4%fq)Vz}n=lp3;PtU_zwu!}Jdkn*PuUZEY zjbJeNi>~X%>K^bdFE4)#0Dei5xT>nZPfScasVGuejE;^TuxRuTh9`3a)>u8x3O8{^?l}f!Jy_XtgXlSUz=kx6?7K=}&r>ECPI=xpEC>los;b&c`;-uZ2Ak7Eo9k^S z;;0s`>wvE7j=fPfU;@CSWHLG5$j;*8JZPHs79t*W3J}%05aQQLxj2$cy;N$NcFc*& z03Z_zg&wtQkm_rfKeTz>`g<)3@Dl*3H1!*jTq|n#XM0URx>3JI`!7onCN@ z=;-L^4$HEF#AtM(Dfa*kvXU&z`FfWbT450Jg9=H|d4^%!xq9{LQcDhy5^bZ5v15!e z8sB$QsnoMtW3&p~x^=7HvaG!gG$=Lqzdb!Y^G+G0pp{Cc52^QkKHr^*i3wNm8(7HA z%b}s6U^pDMYB{<*`u8E?o@R#`EokM{!2hF8oowb-NibG&mJS`R(Gxi_6XEHC#fqM$er)_o)-@5fIM# z5+Xh}4CDEgD_7PA+1ayaRaI3x8Do8nvEB+PRNJM!D3M6~)GQ^pR6cxH9Ij~^?fi#H$F}Xc>FMd0mc~YH zwUmL1@e3C&>@o~PcwM~Rz??aAChYh7r9>jJ*eKvyxlK9XbZGf<^raBuD*W|BQ%Vv{LIWPhzhKRDX{|A~TG!PwMFa zz}<8@O_R1QPg^q(iA26(jE#FZkW8o3!)-yonLl^y47qQG5W3^k1ZXSxmIs+v^fXQT znsferwHzW&rc$XfFWqLpx3z?-506HpF(Jg{T9g33OQ+LRUTuH2%mG3gi9|l9d%@Jy z)Q@dPUN*R;4r~?ZYP;JGyoxB-rRCQRV6*3Mm(XS-xLN%-J_G*)=Lp+R3TRA000000 LNkvXXu0mjfn2II6 literal 0 HcmV?d00001 diff --git a/KylinNM/res/w/wifi-low-pwd-off.png b/KylinNM/res/w/wifi-low-pwd-off.png new file mode 100644 index 0000000000000000000000000000000000000000..1c6860d5d540ac09096a37d348e407123385d9d3 GIT binary patch literal 1735 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 literal 0 HcmV?d00001 diff --git a/KylinNM/res/w/wifi-low.png b/KylinNM/res/w/wifi-low.png new file mode 100644 index 0000000000000000000000000000000000000000..363150568f6f62f50d8df1b7b43d13a1d132c5d6 GIT binary patch literal 1777 zcmVB7n5h z%zgKrb3fRHBhf%g-<*#S~?@;ai* za_R0`XKR z6*es%Ar$SJR7C->o8Iuz{M1X)bk!zKCX?*#Q$K%7TPj`&I43Tdwmu7n=NBgi2W747 z)brjZ^ZCAeF)7(0g;-G-IM7mJIyRq7qTcrVAOkk|(c?eskwk5dsp5i2(E^XP-yxsU!oerQm~frDhIE>h0r#Tl+7 zhN9QE{gepU;4h#0>GPHa?apHp}1JfG@qX)QXIULCP>`+*hf#?Os7m zIZbLExmbNB_mBG*&!69=|91ZYV^1qhtxkA$k|+`Ae>WRbmVDO9m}!}sem#DxT+#-v zWcID*AjU~XtEIhK330(C*O)OREM7g=zEN&_VtnbaY0^Q*plWM==G=jW5`lp$w|#3l zqmO`lcN%-S=jPN!CBq z%>BX0M-zj%^`HlA5*vB{F{R0mjB7ykpP8XpcKe_`%ubG^4_QD5>XE-jh#(iliiK2S z?oYRq$>f7Z?H^fa(+qlSRYXulUn|W{p6M>wcRVDcua4dDW*`(7u%4Y7I`Uwo+t0IB zphq$mt_F;8+ybrF(2}O`28)F)%e3wNKEtE6^e9kX&@8Nxd{s+}^m@yR`N_mj4sUJS z^OSu2J_Rev@ceQ=&|?G0VB5+QUR|Qq%=x)LRcI{(L%Le?PI32 z!k~U`TO^Qx45>bT{|E7vh6vb)>}_<*%pbLt$t%3qCtnHQFL(CkDsVq$ffd)PdV`{1 zMP{jcEiJJHrwrOb%Y*TP9W)~l{P_KsLKVU9QlUyLPmbLPNfngk8f}k$*45puHFmoF zxUoNCknshGL_RNHO-^6C>t|V?a2jsq7;CCfWl}343#^`65wA55N*NKxC)3}1a;=uF z>n5(`u=j7b_oK)A?P}|`8!&cx2Fdg9e-7{e!p*t5OQ_sw?`ZmrlkC|*v{EYqDIMXl zjj>muG{}bD0Z&FPwrGwKAl^n=?fTB(MDoK#?5bNyZm5KumOpz#^lVx;t7*1P#-1&? zP5(3HYr7we8`QhoS%yd7`rvHWT{o8>8(Ho`u#VVgyj=^_7qPJ)uo@m@ZX$DLd?T`H=KlgfFIJGe4+zBf=NhQ>uZ6&|k@O@0ovKC} z0j|#`I$m%Sm>T|?}xz`XBLkbHmX$+=8pe{;?6T zFaOSH`YZu?XNEi8t_MsbHjkA+BY@nV*(2aKV$HVS%RsXu>ek<02HZxh+4lbf*V*__ TJcx@700000NkvXXu0mjfs624h literal 0 HcmV?d00001 diff --git a/KylinNM/res/w/wifi-medium-off.png b/KylinNM/res/w/wifi-medium-off.png new file mode 100644 index 0000000000000000000000000000000000000000..c823649b50e91b3a406c1dd17b7fc402822242e3 GIT binary patch literal 1696 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 FV1gish(iDX literal 0 HcmV?d00001 diff --git a/KylinNM/res/w/wifi-medium.png b/KylinNM/res/w/wifi-medium.png new file mode 100644 index 0000000000000000000000000000000000000000..82cdf4009766b883c8e1836d519d73135e994901 GIT binary patch literal 1812 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 literal 0 HcmV?d00001 diff --git a/KylinNM/res/w/wifi-none-pwd.png b/KylinNM/res/w/wifi-none-pwd.png new file mode 100644 index 0000000000000000000000000000000000000000..c3fd701649758f4c5eba7f4825a0d2be07298ffc GIT binary patch literal 1433 zcmV;K1!nq*P)nfF!0G0ka~ti(+()(xFsKpfcE_brZ!}(OyfAUqcxC(-G)C{X zM6;EVp^L+0bJmdD6#@2lMoT%(Shk|p;eo)YSqM;qCiYF0a!p%SOb$urKm-f zHBmM2oIQewR3!Ged%*-eL~LkivSEdkI@ClxBCC^x+z2R!ToBQsiD;3Vs~6f9>lQBb zqcGx|Zj(2GQx`+Fuo7)igt=LuPzp(!?!%A-6Ew?Bmg!r)I~MbaX^4O>k7I)&yUIj6 zXi%F^f_!ZifQXRFLlfcO<3)@#>kv)Lr1)fe+Cqh7f`rNH^*^00!fllvK zjRd}zKyJ&oilwwyxLhECzxv#*kxX$j5;%S_XpbpkA4z1T1XE}%ebJjNG6oZ_QZooUaA}@)zOi&k4(i0bX^;>+*>vu z0b=G-B`9kgF4gL=ymPa^eI>aD^leihcLB+VnuNlcY7JvxxW75PVHWBbnyF<%xIi#d zSu2_|KmXImQE%TluFU>ZEorevDz*k0Rqcd;oJh0T6#sDd8u$$a z;&M7aYYkh;sR>zD63DnTDRQF5{PVTmrEq!`jBfM?`C6)sN|53hDZ$ql7+nuA0dyw9-tR$(AcDT5pMr40k`aS_t5* zCvp165ft9N=E zzsdMLSJm|It}n+L&|K9~%y6dBC-9lVg@DiCEK>k)}F-jj($&ig=u;OT+! zZOdpnR455EQ>Jde*fg4mr^{yfp7>$2%5Ts*!gy^1Ka?&f!vyqzC(lo8U#+b=NK)iI zW+R8YBfTx51Tg17@I4@UH@UbGRX}*h1o*jz)q+Ii6+rlai0*Iai0=pb?C0`Y1L>8} zZ)mHr$RYh;F(SXU(?-U)qxE~=QB)H?u7RMRh@43RfN%TV_778Ix-JLbB1y@2i0JqC z*$)>*KzBZNb#aFpSw3il1agI#iTi!eZeK`E?DPhg0LYqn$9Qe?NUtr~A^rW+muDR@ zDW~uIVL0Hn+lnTj2k7)pHA;>LNM!GXehKlpTYpLKmeV`4?!QpL?a0{CTz1A~kU=jU zU(7Uf3*S#d+cb_9V}a{^yZ%i|#~bP*5T5}+yI~2gH=ID=FZta~!*O*M7$oVY-=CvQ z?%yoV-rbJxE*!wveeKWuWpwA+ET(=o`1>4>mW05XR>vHwv^<$9;Ygq3+tmL%PH#{E zP~N-P`q(G~ZhQP-ExH4Vz@1k@Ekf1yrIYZxTjH%wJn5P)Q~`-KQ!)ZNki!@MZY5?~ zcQvgj0y+t&cWx(wx;IhTiX)%{KR!6$78SAhR9TS(;tP9dzFm#Q_EanZoy4)LlRIwr zw9Qw{A~v$4Jha=W5%9W=Yv3U~$c%!I6Yz$*0L7<7*q;A^h7yq`fcVQL8zu4qUk=-n n9%SVJm@DQMfm{_T_xJw*FG@8}Fqd8;00000NkvXXu0mjfB%H7r literal 0 HcmV?d00001 diff --git a/KylinNM/res/w/wifi-none.png b/KylinNM/res/w/wifi-none.png new file mode 100644 index 0000000000000000000000000000000000000000..b4927dc8fdad980b8b0e8febfd644cdb95374945 GIT binary patch literal 1229 zcmV;;1Ty=HP)$G;*E`XBfY_~3&& zQ$;9Ks3VS}GF1bi);i*}^q2eyq!eh7n|pVUv(aE^lia;&AYq0)Ws;&CY&2yn{at#W~|1(0d8kP!0(i95aO*&q-RI{K$8xK-f-_o_~*rjewn6XGEP z?^{IIO*A~Fs5H#+F5e0OU@@X-3m}~w8i>6qIDUczStbN9cn5)h0~X%_Kx+iT+W+~) zQD7VqmvqD>=Bik1TB=$6ya#w8KuQ4lAP~rhfMU21=jkyNkW-E^rhBF@b=NI9nM)a9 zqao6wH9}Un(C&<$_D-v$nJvK43yZZso~T(!PnIj4iV=y-m6sq$@OmT^n9moHoB-{8 zQP(6n>Xi57>N`Fg?Omj_gfhLg-As}ia&qk-4@d%^(tE!lo3P<;NZ|)01a!eiuklm zsnZFBIWczT@u?KRvEfP2y@cx!LF(EP5JH$0h8P{`Y5seyh*R4pupmL<2x7C-Nn_Nk zWf&bg;ZLm9Kzh|%2WP72Di0D$btjS7EC_~ZRaa}#%sa_L=RnusOx0ZuL5qvBf?yi6 zJV_Y;;Y|H<=Hg~O$Ycw>yle$IfL0EXkj-Q(FBy_~JYi@kHzt1fKg7B4A;JcYr% zd^vJ_&!TcGkv8lch}W3HXjAR;i@n7HMS)4BsV5c+>P9mM1;3cw2`+bQW^<=f8-*TrK$-9)j4LBO&3Enm*hU~d!At!& zcO^hSu+b2J1a*sF`RR?OxSjZi&H=jtM?bq=yISSmkAknY*>+>(|EiH7lzhMh|H$qV zW4n#!6D4nGtI3rE2+p1*RUw={*(j$_Ce-1tm+H)w#Qt)CCSLSQ7Pti#m7i&th@1Q`nLyP zI{@Hf^pvm5Zr(=*kPcorKj}Ax=UF>v>j2!^#8_ATROW9If(!965crPy8_q^LAnQxS zBke-DM7Cd;YviW2PX(fa=zorcynki0b^3%~lnA7S9PGUnK&t1xn(AC!`}yqd+bW$>y9PUXPp(+)@#yYEyIo>9}(s-emCP)9Z4T;^f$Cft#wrRP8nd zAXV6d@n$ob+>;WbksHyIjW<+9IkDR?04d_|xjS`Sr{JWwaUx#YZYY3LF@@M~0~E4q r#T>I8fnu&^A#-g + + + +画板 1 + + + + + diff --git a/KylinNM/res/x/fly-mode-on.svg b/KylinNM/res/x/fly-mode-on.svg new file mode 100644 index 0000000..9f5e543 --- /dev/null +++ b/KylinNM/res/x/fly-mode-on.svg @@ -0,0 +1,14 @@ + + + + +画板 1 + + diff --git a/KylinNM/res/x/hot-spot-off.svg b/KylinNM/res/x/hot-spot-off.svg new file mode 100644 index 0000000..cf39cad --- /dev/null +++ b/KylinNM/res/x/hot-spot-off.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + diff --git a/KylinNM/res/x/hot-spot-on.svg b/KylinNM/res/x/hot-spot-on.svg new file mode 100644 index 0000000..2041db9 --- /dev/null +++ b/KylinNM/res/x/hot-spot-on.svg @@ -0,0 +1,17 @@ + + + + + + + + + + diff --git a/KylinNM/res/x/load-down.png b/KylinNM/res/x/load-down.png new file mode 100644 index 0000000000000000000000000000000000000000..a96298ace4380a4cc8ab5eea0ce6c086df416f6f GIT binary patch literal 192 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xl001;Ln>}1B}il(=}1B}iO(5bkuNM&Q`;HqQQMEDEg00?fh=bU1Hj zoM6?jmUCiT!d2ELHU?8}hLyh^bQ;vYGIoeIw$&&+Y5f~vV(f7AoA6WC72f}pKR22g pPZH@k(iF(NBH_RR<^~34hB`MXgMW5E4+8CD@O1TaS?83{1OUkBLnHtI literal 0 HcmV?d00001 diff --git a/KylinNM/res/x/net-list-bg.svg b/KylinNM/res/x/net-list-bg.svg new file mode 100644 index 0000000..c17c1fd --- /dev/null +++ b/KylinNM/res/x/net-list-bg.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + diff --git a/KylinNM/res/x/pb-close.png b/KylinNM/res/x/pb-close.png new file mode 100644 index 0000000000000000000000000000000000000000..c5cc8ad4e13e92e7debddfe547f927792206fa22 GIT binary patch literal 620 zcmV-y0+aoTP)S5P)ZBMf^dLwjiEt8?Qnde-45Nkz5t^1$-}`z~V(cNI|Fvduh-&s0VLK6+sV* z%}Uflt!dpPJIfL$$C6Cun{PU^LHK7E{xe=+;ItcO!)!QZ72uXmnb2Q2Y?uwFtODH9 zDHF;=i(8soI%O5$mQI;a+9~b2?!^6l`x*e6mQ~uXR;zDu3g|e_i0|V8pjav7b4#^a z?Ili}uxFIE^f&O004M;$=|Z`*8x=Z$?Fcvo0o#b!o}QYVuh;9(Mgj*(RVu5C0^ZMs zfIK3eSmolLEOaj{06t_hu<3bT&@sv`rG~p%#evoV8{|g<9M!ZgSW;kFXq~X+z<)vO zhSdT*2)fm3iS9I}8c^hGY1k<39c01E0s$QW*wP8B&2ZTC{`!q(^$aUK*^~}@Y4D$Y z_u&iyHa>9?FRXIWmEF@Sma#VAp92b>5QQ%U&O|+&B!$)j95#)8KhueOB1-~I4m@o2 z^bfK)XYZ)+q`)$JRyQ;muncVI^p5$JC2N`lI0~$8Xyw3hV0A((2evCqM?#35xQB|q zPs`)$D{o7-U1jc53hX#ezT?9k06r%9c<+JEjgO7aHX4mP85o&+lmZ7YwC&~fY!+^s z&E~C40)y_c?X|f~=C$Z~-erCTQM9FTbtk=9wuA_Ye5j~ z60|TFJ3&xHP&5cuCPqBN&XZYl>;0Mi6FC_Nw%RxIe)D}ZZ)S*$P0HnM`1Jm?*1>s# zM*-OjzKWjlfj_`m-eNj3KZtz%A9k>n3Mr8n!X40A|+GkOv|%{5OfaK*%NL zc)_Md7l4fCkBQ)s2=@0Pd2Y+%Z5X{SW=ty4p#wFa`{t zfZVMCFPOEbPzm_)zwIP<xV(^rD!9IqMOL`F zls3--r`Pfb!sWvxf^hjXYdhmzJx)9C2mk7M{tGMt@`?*Mc&`8e002ovPDHLkV1m5C BD}4X} literal 0 HcmV?d00001 diff --git a/KylinNM/res/x/pb-newConn.png b/KylinNM/res/x/pb-newConn.png new file mode 100644 index 0000000000000000000000000000000000000000..86a18e0729d5652128aa69d9bda1beba37559463 GIT binary patch literal 274 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpUto1QL?AsNnZXE^d5GT>pE{hqP> zdFM&yQ{S7aRyTNq+0Tw;5f=# zkifR#`@Vidz2m=wPTf7TPU+3|PbTb9*^g_I)|x(DGsU<6Q09S|<}x!Hm7H8ocmj1U zIJEsj<)N|3W*(#TkhIes z0)k3TN{r9*r+#SpI&JDV!D{Y+>lYim0Fgz${>2V5leYmLKRz22WQ%mvv4F FO#s8EbJzd? literal 0 HcmV?d00001 diff --git a/KylinNM/res/x/pb-wifi-n.png b/KylinNM/res/x/pb-wifi-n.png new file mode 100644 index 0000000000000000000000000000000000000000..54e83d6b963a7b3ce09873214c01cef4b90c026e GIT binary patch literal 1707 zcmV;c22}ZpP)~W_I31rA5m47-WMt$H zW{xuR{OKGpU0ovj5`gIF=y<-fv(qbQUzUJj7}o&k25?R}XZ3uph%ob7*LB}6<=2-$ zJRUzYlgT^{;OYv1WdVEy;B!sW_Av8z07i-Ekfv$ud0sOSH3Mj6=Cf25iRfZxZY<}U zi25{5yEmOq?Afi72ybfSnAQ0$FCX?!S#bVp`?2(a?h0J^_ zfLoYZ5%iNoM0CFp;+2v`Y}Ij`nQ&f-yR9Pknd<)l`a+@54%@cJ3Vr74FAd;& zAF}VXwY4o(!I;Q^Wm$JiDWCIEP6;96M8x^eBaz6tp6A^|M9Z03P26b<5e)-)6~J>s zh))Y0SeCU>O1aL*@qMoAZk-?yi9}A7QtoEv)2Hqhkz{7eb=}SR4lK($gPEUX=FVCs zc@a==G&VLq+|$$ZP5v{(FjfLk-B8cN_KU9T;Z!O$I8_6Rgy(th0Z{wQn6B%$r_<>- zCXIS~d-eYQ{tW=SDrtYyPKSyH(Kr#MT3TAx*|x1}ZbA&hxFMIR*+ewh(9m#YPfyRc z|0rVhBOZ^RoXuvVM6}IuoR23Rn5H>TIbh~RrGOF9_W*Vi(a%!Kp8@=m(^vNMi0Bk% zz63y98G`-6VDQ%N?(Th)dXY$Efs}GpFc@5)NF;uk%0{_J#pCg|OeWI@;FMyzM6`#9 z)E@Ju5aP3P{?v2JvgS)Emk`l1W^VWKyIiZQCUaYWO!#g60btcrj#%59_D0jMFg)OFomh0bEJ z*c>$znAv3J)-oyCM?`KY6xw3j_GqDQBoetoO8F)L6||d#5bu`qS9YDw72SnIG+MY3 zs?ncI`>HBlZbmgtd$y^mDN(qZsKz0s45d=3Ps-iTiU<^Q5{*VL@;tA%f{2&Hpfa*d z2=RV7#iHG+8lz}5y3q5you$_8oZrbes;MdJbDkQU*Qe8IHAtr=Rb{|1jGuhw!Wa>4 zCnB}qd>jske^qD(1_o4%JYPzw-lG=-Xe!d#4myr=f?t7GUBtx{FbMH2Lb`B zRL@qvy1Kef8yg!Fg=;$zJt%}wJI}PFstn}1lUlN#QU4QBObDUY--^YutOuC+DP~sR zjaCaGo~qb5PqAtO%10y;x!Lo)Y`GV2KVX(+T`Q$*ab0&?HQ*MqBOfI^yE#e>z(iAvZW zQv#I$s8wf%K&=t1)qW-ewMMX3`X8o^ra{{oq#qEB)IRz&~+002ovPDHLkV1gLe BH^=}0 literal 0 HcmV?d00001 diff --git a/KylinNM/res/x/pb-wifi-y.png b/KylinNM/res/x/pb-wifi-y.png new file mode 100644 index 0000000000000000000000000000000000000000..a85824079396eb4b5c1f5eb5e3f15e1e5765ebfd GIT binary patch literal 1770 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 eTdC%d{ literal 0 HcmV?d00001 diff --git a/KylinNM/res/x/wifi-list-bg.svg b/KylinNM/res/x/wifi-list-bg.svg new file mode 100644 index 0000000..43f1866 --- /dev/null +++ b/KylinNM/res/x/wifi-list-bg.svg @@ -0,0 +1,15 @@ + + + + +画板 1 + + + + + + + diff --git a/KylinNM/src/backthread.cpp b/KylinNM/src/backthread.cpp new file mode 100644 index 0000000..5747fc8 --- /dev/null +++ b/KylinNM/src/backthread.cpp @@ -0,0 +1,461 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include +#include +#include +#include + +#include +#include +#include + +#include "kylinnm.h" //逼不得已,引入kylinnm头文件 + +BackThread::BackThread(QObject *parent) : QObject(parent) +{ + cmdConnWifi = new QProcess(this); + connect(cmdConnWifi , SIGNAL(readyReadStandardOutput()) , this , SLOT(on_readoutput())); + connect(cmdConnWifi , SIGNAL(readyReadStandardError()) , this , SLOT(on_readerror())); + cmdConnWifi->start("bash"); + cmdConnWifi->waitForStarted(); +} + +BackThread::~BackThread() +{ + cmdConnWifi->close(); +} + +//get the connection state of wired and wireles network +IFace* BackThread::execGetIface() +{ + IFace *iface = new IFace(); + + QString tmpPath = "/tmp/kylin-nm-iface-" + QDir::home().dirName(); + QString cmd = "export LANG='en_US.UTF-8';export LANGUAGE='en_US';nmcli -f TYPE,DEVICE,STATE device > " + tmpPath; + Utils::m_system(cmd.toUtf8().data()); + + // int status = system(cmd.toUtf8().data()); + // if (status != 0){ syslog(LOG_ERR, "execute 'nmcli device' in function 'execGetIface' failed");} + + QFile file(tmpPath); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + // print information if can not open file ~/.config/kylin-nm-iface + syslog(LOG_ERR, "Can't open the file ~/.config/kylin-nm-iface!"); + qDebug()<<"Can't open the file ~/.config/kylin-nm-iface!"; + } + QString txt = file.readAll(); + QStringList txtList = txt.split("\n"); + file.close(); + + iface->lstate = 2; + iface->wstate = 2; + + for (int i = 1; i < txtList.size(); i ++) { + QString line = txtList.at(i); + if (line != "") { + int index1 = line.indexOf(" "); + QString type = line.left(index1); + QString lastStr = line.mid(index1).trimmed(); + int index2 = lastStr.indexOf(" "); + QString iname = lastStr.left(index2); + QString istateStr = lastStr.mid(index2).trimmed(); + + if (type == "ethernet" && iface->lstate != 0) { + // if type is wired network + iface->lname = iname; + + if (istateStr == "unmanaged") { + iface->lstate = 2; //switch of wired device is off + } + if (istateStr == "disconnected" || istateStr == "unavailable") { + iface->lstate = 1; //wired network is disconnected + } + if (istateStr == "connected") { + iface->lstate = 0; //wired network is connected + } + } + if (type == "wifi" && iface->wstate != 0) { + // if type is wireless network + iface->wname = iname; + + if (istateStr == "unmanaged" || istateStr == "unavailable") { + iface->wstate = 2; //switch of wireless device is off + } + if (istateStr == "disconnected") { + iface->wstate = 1; //wireless network is disconnected + } + if (istateStr == "connected") { + iface->wstate = 0; //wireless network is connected + } + } + } + } + + return iface; +} + +//turn on the switch of network +void BackThread::execEnNet() +{ + char *chr = "nmcli networking on"; + Utils::m_system(chr); + + // int status = system("nmcli networking on"); + // if (status != 0){ syslog(LOG_ERR, "execute 'nmcli networking on' in function 'execEnNet' failed");} + while (1) { + if (execGetIface()->lstate != 2) { + sleep(3); + emit enNetDone(); + emit btFinish(); + break; + } + sleep(1); + } +} + +//turn off the switch of wireless network first, then turn off the switch of network +void BackThread::execDisNet() +{ + if (execGetIface()->wstate != 2) { + char *chr = "nmcli radio wifi off"; + Utils::m_system(chr); + + // int status = system("nmcli radio wifi off"); + // if (status != 0){ syslog(LOG_ERR, "execute 'nmcli radio wifi off' in function 'execDisNet' failed");} + while (1) { + if (execGetIface()->wstate == 2) { + emit disWifiDone(); + emit btFinish(); + break; + } + sleep(1); + } + } + + char *chr1 = "nmcli networking off"; + Utils::m_system(chr1); + // int status1 = system("nmcli networking off"); + // if (status1 != 0){ syslog(LOG_ERR, "execute 'nmcli networking off' in function 'execDisNet' failed");} + while (1) { + if (execGetIface()->lstate == 2) { + emit disNetDone(); + emit btFinish(); + break; + } + sleep(1); + } +} + +//turn on the switch of wireless network +void BackThread::execEnWifi() +{ + //if (execGetIface()->lstate == 2){ + // char *chr = "nmcli networking on"; + // Utils::m_system(chr); + // //int status = system("nmcli networking on"); + // //if (status != 0){ syslog(LOG_ERR, "execute 'nmcli networking on' in function 'execEnWifi' failed");} + // while(1){ + // if (execGetIface()->lstate != 2){ + // emit launchLanDone(); + // break; + // } + // sleep(1); + // } + //} + + char *chr1 = "nmcli radio wifi on"; + Utils::m_system(chr1); + //int status1 = system("nmcli radio wifi on"); + //if (status1 != 0){ syslog(LOG_ERR, "execute 'nmcli radio wifi on' in function 'execEnWifi' failed");} + while (1) { + if (execGetIface()->wstate != 2) { + KylinDBus objKyDbus; + while (1) { + if (objKyDbus.getAccessPointsNumber() > 0) { + // objKyDbus.getAccessPointsNumber()>0 standard can get wireless accesspoints now + KylinNM::reflashWifiUi(); + emit enWifiDone(); + emit btFinish(); + break; + } + sleep(2); + } + break; + } + sleep(1); + } +} + +//turn off the switch of wireless network +void BackThread::execDisWifi() +{ + char *chr = "nmcli radio wifi off"; + Utils::m_system(chr); + // int status = system("nmcli radio wifi off"); + // if (status != 0){ syslog(LOG_ERR, "execute 'nmcli radio wifi off' in function 'execDisWifi' failed");} + while (1) { + if (execGetIface()->wstate == 2) { + KylinNM::reflashWifiUi(); + emit disWifiDone(); + emit btFinish(); + break; + } + sleep(1); + } +} + +//to connect wired network +void BackThread::execConnLan(QString connName) +{ + disConnLanOrWifi("ethernet"); + + KylinDBus objKyDbus; + if (objKyDbus.isWiredCableOn) { + // only if wired cable is plug in, can connect wired network + QString cmd = "export LANG='en_US.UTF-8';export LANGUAGE='en_US';nmcli connection up \"" + connName.replace("\"","\\\"") + "\""; + Utils::m_system(cmd.toUtf8().data()); + // int status = system(cmd.toUtf8().data()); + // if (status != 0){ syslog(LOG_ERR, "execute 'nmcli connection up' in function 'execConnLan' failed");} + qDebug()<<"debug: in function execConnLan, wired net state is: "<lstate); + syslog(LOG_DEBUG, "In function execConnLan, wired net state is: %d", execGetIface()->lstate); + emit connDone(0); + } else { + qDebug()<<"connect wired network failed for without wired cable plug in."; + syslog(LOG_DEBUG, "connect wired network failed for without wired cable plug in."); + emit connDone(1); + } + + emit btFinish(); +} + +//to connected wireless network need a password +void BackThread::execConnWifiPWD(QString connName, QString password) +{ + //disConnLanOrWifi("wifi"); + QString tmpPath = "/tmp/kylin-nm-btoutput-" + QDir::home().dirName(); + QString cmdStr = "export LANG='en_US.UTF-8';export LANGUAGE='en_US';nmcli device wifi connect \"" + connName.replace("\"","\\\"") + "\" password \"" + password.replace("\"","\\\"") + "\" > " + tmpPath; + Utils::m_system(cmdStr.toUtf8().data()); + // int status = system(cmdStr.toUtf8().data()); + // if (status != 0){ syslog(LOG_ERR, "execute 'nmcli device wifi connect' in function 'execConnWifiPWD' failed");} + QFile file(tmpPath); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + syslog(LOG_DEBUG, "Can't open the file /tmp/kylin-nm-btoutput !"); + qDebug()<<"Can't open the file /tmp/kylin-nm-btoutput !"<wstate); + syslog(LOG_DEBUG, "In function execConnWifiPWD, wireless net state is: %d", execGetIface()->wstate); + } else { + emit connDone(1); + } + emit btFinish(); +} + +//to connected wireless network driectly do not need a password +void BackThread::execConnWifi(QString connName) +{ + //disConnLanOrWifi("wifi"); + + QString cmdStr = "export LANG='en_US.UTF-8';export LANGUAGE='en_US';nmcli connection up \"" + connName.replace("\"","\\\"") + "\"\n"; + cmdConnWifi->write(cmdStr.toUtf8().data()); +} + +void BackThread::on_readoutput() +{ + QString str = cmdConnWifi->readAllStandardOutput(); + cmdConnWifi->close(); + qDebug()<<"on_readoutput: "<< str; + if (str.indexOf("successfully") != -1) { + emit connDone(0); //send this signal if connect net successfully + qDebug()<<"debug: in function on_readoutput, wireless net state is: "<wstate); + syslog(LOG_DEBUG, "In function on_readoutput, wireless net state is: %d", execGetIface()->wstate); + } else if(str.indexOf("unknown") != -1) { + emit connDone(2); + } else { + emit connDone(1); + } + + emit btFinish(); +} +void BackThread::on_readerror() +{ + QString str = cmdConnWifi->readAllStandardError(); + cmdConnWifi->close(); + qDebug()<<"on_readerror: "<< str; + if (str.indexOf("successfully") != -1) { + emit connDone(0); + } else if(str.indexOf("unknown") != -1 || str.indexOf("not exist") != -1) { + emit connDone(2); //send this signal if the network we want to connect has not a configuration file + } else { + emit connDone(1); //send this signal if connect net failed + } + + emit btFinish(); +} + +//get property of connected network +QString BackThread::getConnProp(QString connName) +{ + QString tmpPath = "/tmp/kylin-nm-connprop-" + QDir::home().dirName(); + QString cmd = "export LANG='zh_CN.UTF-8';export LANGUAGE='zh_CN';nmcli connection show \"" + connName.replace("\"","\\\"") + "\" > " + tmpPath; + Utils::m_system(cmd.toUtf8().data()); + // int status = system(cmd.toUtf8().data()); + // if (status != 0){ syslog(LOG_ERR, "execute 'nmcli connection show' in function 'getConnProp' failed");} + + QFile file(tmpPath); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + syslog(LOG_ERR, "Can't open the file /tmp/kylin-nm-connprop!"); + qDebug()<<"Can't open the file /tmp/kylin-nm-connprop!"< " + tmpPath; + Utils::m_system(cmd.toUtf8().data()); + // int status = system(cmd.toUtf8().data()); + // if (status != 0){ syslog(LOG_ERR, "execute 'ethtool' in function 'execChkLanWidth' failed");} + + QFile file(tmpPath); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + syslog(LOG_ERR, "Can't open the file /tmp/kylin-nm-bandwidth!"); + qDebug()<<"Can't open the file /tmp/kylin-nm-bandwidth!"< +#include +#include +#include +#include +#include +#include + +class IFace{ +public: + QString lname; + QString wname; + int lstate; // 0已连接 1未连接 2已关闭 + int wstate; // 0已连接 1未连接 2已关闭 +}; + +class BackThread : public QObject +{ + Q_OBJECT +public: + explicit BackThread(QObject *parent = nullptr); + ~BackThread(); + + IFace* execGetIface(); + QString getConnProp(QString connName); + QString execChkLanWidth(QString ethName); + QProcess *cmdConnWifi = nullptr; + +public Q_SLOTS: + void execEnNet(); + void execDisNet(); + void execEnWifi(); + void execDisWifi(); + void execConnLan(QString connName); + void execConnWifi(QString connName); + void execConnWifiPWD(QString connName, QString password); + + void disConnSparedNetSlot(QString type); + void disConnLanOrWifi(QString type); + + void on_readoutput(); + void on_readerror(); + +Q_SIGNALS: + void enNetDone(); + void disNetDone(); + void enWifiDone(); + void launchLanDone(); + void disWifiDone(); + + void connDone(int connFlag); + + void btFinish(); + void disFinish(); + void ttFinish(); +}; + +#endif // BACKTHREAD_H diff --git a/KylinNM/src/confform.cpp b/KylinNM/src/confform.cpp new file mode 100644 index 0000000..493c50a --- /dev/null +++ b/KylinNM/src/confform.cpp @@ -0,0 +1,552 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include +#include + +extern QString llname, lwname; + +ConfForm::ConfForm(QWidget *parent) : + QWidget(parent), + ui(new Ui::ConfForm) +{ + ui->setupUi(this); + this->setWindowFlags(Qt::FramelessWindowHint); //Qt::WindowStaysOnTopHint + this->setWindowTitle(tr("edit network"));//"网络设置" + this->setAttribute(Qt::WA_TranslucentBackground); + //需要添加 void paintEvent(QPaintEvent *event) 函数 + + QPainterPath path; + auto rect = this->rect(); + rect.adjust(1, 1, -1, -1); + path.addRoundedRect(rect, 6, 6); + setProperty("blurRegion", QRegion(path.toFillPolygon().toPolygon())); + + this->setStyleSheet("QWidget{border-radius:16px;border:none;}"); +// KylinDBus mkylindbus; + + QString strTrans; + strTrans = QString::number(1, 10, 2); + QString sty = "#centralWidget{border:1px solid rgba(255,255,255,0.05);border-radius:16px;background:rgba(255,255,255," + strTrans + ");}"; + ui->centralWidget->setStyleSheet(sty); + + labelQss = "QLabel{border:0px;color:rgba(38,38,38,1);background-color:transparent;}"; + cbxQss = "QComboBox{padding-left:20px;font-size:13px;color:rgba(38,38,38,1);" + "border:1px solid rgba(255, 255, 255, 0.05);border-radius:4px;background:rgba(255,255,255,0.08);}" + "QComboBox::drop-down{border:0px;width:30px;}" + "QComboBox::down-arrow{image:url(:/res/g/down_arrow.png);}" + "QComboBox QAbstractItemView {margin:0px 0px 0px 0px;padding: 0px 0px;border-radius:0px;background-color:#48484C;outline:0px;}" + "QComboBox QAbstractItemView::item{padding-left:17px;border-radius:0px;font-size:13px;color:rgba(255,255,255,0.91);height: 32px;background-color:#48484C;outline:0px;}" + "QComboBox QAbstractItemView::item:hover{padding-left:17px;border-radius:0px;font-size:13px;color:rgba(255,255,255,0.91);background-color:#3D6BE5;outline:0px;}"; + leQss = "QLineEdit{padding-left:20px;color:rgba(38,38,38,1);background:rgba(245,245,245,1);}"; + btnOffQss = "QPushButton{border:1px solid rgba(44,44,44,1);border-radius:4px;background-color:rgba(255,255,255,0.12);color:rgba(44,44,44,1);font-size:14px;}"; + // "QPushButton:Hover{border:0px solid rgba(255,255,255,0.2);border-radius:4px;background-color:rgba(255,255,255,0.2);}" + // "QPushButton:Pressed{border-radius:4px;background-color:rgba(255,255,255,0.08);}"; + btnOnQss = "QPushButton{border:0px;border-radius:4px;background-color:rgba(61,107,229,1);color:white;font-size:14px;}" + "QPushButton:Hover{border:0px solid rgba(255,255,255,0.2);border-radius:4px;background-color:rgba(107,142,235,1);}" + "QPushButton:Pressed{border-radius:4px;background-color:rgba(50,87,202,1);}"; + lineQss = "background:rgba(255,255,255,0.08);"; + + // ui->centralWidget->setStyleSheet("#centralWidget{border:1px solid #297a97;background-color:#ffffff;}"); + ui->wdHead->setStyleSheet("#wdHead{border:none}"); + ui->wgManual->setStyleSheet("#wgManual{border:none}"); + ui->wdBottom->setStyleSheet("#wdBottom{border:none}"); + ui->lbLeftupTitle->setStyleSheet("QLabel{border:0px;font-size:14px;color:rgba(38,38,38,1);background-color:transparent;}"); + ui->lbName->setStyleSheet(labelQss); + ui->lbTxt1->setStyleSheet(labelQss); + ui->lbTxt2->setStyleSheet(labelQss); + ui->lbTxt3->setStyleSheet(labelQss); + ui->lbTxt4->setStyleSheet(labelQss); + ui->lbTxt5->setStyleSheet(labelQss); + ui->lbTxt6->setStyleSheet(labelQss); + + //YYF + ui->btnClose->setStyleSheet("border-image:url(:/res/x/pb-close.png);"); + connect(ui->btnClose,&QPushButton::clicked,ui->btnCancel,&QPushButton::clicked); + ui->isDHCP->setStyleSheet("QCheckBox::indicator {width: 16px; height: 16px;}" + "QCheckBox::indicator:checked {image: url(:/res/g/checkbox-checked.svg);}" + "QCheckBox::indicator:unchecked {image: url(:/res/g/checkbox-unchecked.svg);}"); + ui->notDHCP->setStyleSheet("QCheckBox::indicator {width: 16px; height: 16px;}" + "QCheckBox::indicator:checked {image: url(:/res/g/checkbox-checked.svg);}" + "QCheckBox::indicator:unchecked {image: url(:/res/g/checkbox-unchecked.svg);}"); + + ui->cbType->setStyleSheet(cbxQss); + ui->cbType->setView(new QListView()); + ui->cbType->hide(); + ui->cbMask->setStyleSheet(cbxQss); + ui->cbMask->setView(new QListView()); + + ui->leName->setStyleSheet(leQss); + ui->leAddr->setStyleSheet(leQss); + ui->leGateway->setStyleSheet(leQss); + ui->leDns->setStyleSheet(leQss); + ui->leDns2->setStyleSheet(leQss); + + ui->btnCancel->setStyleSheet(btnOffQss); + ui->btnOk->setStyleSheet(btnOnQss); + ui->btnCreate->setStyleSheet(btnOnQss); + ui->lineUp->setStyleSheet(lineQss); + ui->lineDown->setStyleSheet(lineQss); + ui->lineUp->hide(); + ui->lineDown->hide(); + + ui->lbName->setText(tr("LAN name: "));//"网络名称:" + ui->lbTxt1->setText(tr("Method: "));//"编辑IP设置:" + ui->lbTxt2->setText(tr("Address: "));//"IP地址:" + ui->lbTxt3->setText(tr("Netmask: "));//"子网掩码:" + ui->lbTxt4->setText(tr("Gateway: "));//"默认网关:" + ui->lbTxt5->setText(tr("DNS 1: "));//"首选DNS:" + ui->lbTxt6->setText(tr("DNS 2: "));//"备选DNS:" + + ui->lbLeftupTitle->setText(tr("Edit Conn"));//"网络设置" + ui->cbType->addItem(tr("Auto(DHCP)"));//"自动(DHCP)" + ui->cbType->addItem(tr("Manual"));//"手动" + ui->isDHCP->setText(tr("Auto(DHCP)")); + ui->notDHCP->setText(tr("Manual")); + + connect(ui->cbType, SIGNAL(currentIndexChanged(int)), this, SLOT(cbTypeChanged(int))); + + ui->cbMask->addItem("255.255.255.0"); //24 + ui->cbMask->addItem("255.255.254.0"); //23 + ui->cbMask->addItem("255.255.252.0"); //22 + ui->cbMask->addItem("255.255.0.0"); //16 + ui->cbMask->addItem("255.0.0.0"); //8 +// ui->cbMask->setWindowFlags(Qt::X11BypassWindowManagerHint); + ui->cbMask->view()->setWindowFlags(Qt::Popup | Qt::X11BypassWindowManagerHint); + ui->cbMask->view()->setParent(this); + ui->cbMask->view()->hide(); + + ui->cbMask->installEventFilter(this); + ui->cbMask->view()->installEventFilter(this); + + connect(ui->cbMask->view(), &QAbstractItemView::pressed, this, [=](QModelIndex index){ +// qDebug() << "-----------------------pressed:" << index.row(); +// qDebug() << "-----------------------itemText:" << ui->cbMask->itemText(index.row()); + Q_EMIT ui->cbMask->setCurrentIndex(index.row()); + ui->cbMask->view()->hide(); + }); + +// connect(ui->cbMask->view(), &QAbstractItemView::entered, this, [=](QModelIndex index){ +// qDebug() << "-----------------------entered:" << index.row(); +// }); + + ui->cbMask->view()->setGeometry(QRect(ui->cbMask->geometry().left(), ui->wgManual->geometry().top() + ui->cbMask->geometry().bottom(), ui->cbMask->view()->width(), ui->cbMask->view()->height())); + + ui->btnCancel->setText(tr("Cancel"));//"取消" + ui->btnOk->setText(tr("Save"));//"保存" + ui->btnCreate->setText(tr("Ok"));//"确定" + + ui->btnCancel->setFocusPolicy(Qt::NoFocus); + ui->btnOk->setFocusPolicy(Qt::NoFocus); + ui->btnCreate->setFocusPolicy(Qt::NoFocus); + + // IP的正则格式限制 + QRegExp rx("\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\b"); + ui->leAddr->setValidator(new QRegExpValidator(rx, this)); + ui->leGateway->setValidator(new QRegExpValidator(rx, this)); + ui->leDns->setValidator(new QRegExpValidator(rx, this)); + ui->leDns2->setValidator(new QRegExpValidator(rx, this)); + +} + +void ConfForm::setMainWindow(KylinNM *mainwindow) +{ + this->mw = mainwindow; +} + +ConfForm::~ConfForm() +{ + delete ui; +} + +bool ConfForm::eventFilter(QObject *obj, QEvent *ev) +{ + if(obj == ui->cbMask) + { + if(ev->type() == QEvent::MouseButtonPress) + { + ui->cbMask->view()->setVisible(!ui->cbMask->view()->isVisible()); + if(ui->cbMask->view()->isVisible()) + ui->cbMask->view()->setFocus(); + } + } else if (obj != ui->cbMask->view()) + { + ui->cbMask->view()->hide(); + } + return false; +} + +void ConfForm::mousePressEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) { + this->isPress = true; + this->winPos = this->pos(); + this->dragPos = event->globalPos(); + event->accept(); + } +} +void ConfForm::mouseReleaseEvent(QMouseEvent *event) +{ + this->isPress = false; +} +void ConfForm::mouseMoveEvent(QMouseEvent *event) +{ + if (this->isPress) { + this->move(this->winPos - (this->dragPos - event->globalPos())); + event->accept(); + } +} + +void ConfForm::tabletStyle(bool isTablet) +{ + isTabletStyle = isTablet; + + ui->leName->resize(290+194*isTabletStyle,48); + ui->leName->resize(290+194*isTabletStyle,48); + ui->btnOk->move(312+194*isTabletStyle,25); + ui->btnCreate->move(312+194*isTabletStyle,25); + ui->btnCancel->move(180+194*isTabletStyle,25); + ui->btnClose->move(408+194*isTabletStyle,8); + ui->isDHCP->move(180+194*isTabletStyle,130); + ui->notDHCP->move(356+194*isTabletStyle,130); + + ui->wdBottom->setFixedWidth(446+194*isTabletStyle); + ui->wdHead->setFixedWidth(446+194*isTabletStyle); + ui->wgManual->setFixedWidth(446+194*isTabletStyle); + ui->upTitle->setFixedWidth(446+194*isTabletStyle); +} + +//网络配置参数设置界面的显示内容 +void ConfForm::setProp(QString connName, QString v4method, QString addr, QString mask, QString gateway, QString dns, bool isActConf) +{ + this->isActConf = isActConf; + ui->leName->setText(connName); + + if (v4method == "auto" || v4method == "") { + ui->isDHCP->setChecked(true); + ui->notDHCP->setChecked(false); + ui->cbType->setCurrentIndex(0); + cbTypeChanged(0); + } else { + ui->isDHCP->setChecked(false); + ui->notDHCP->setChecked(true); + ui->cbType->setCurrentIndex(1); + cbTypeChanged(1); + } + + ui->leAddr->setText(addr); + ui->leGateway->setText(gateway); + + // 配置中有多个DNS,只处理前两个 + if (dns.indexOf(",") != -1) { + QStringList dnss = dns.split(","); + ui->leDns->setText(dnss.at(0)); + ui->leDns2->setText(dnss.at(1)); + } else { + ui->leDns->setText(dns); + ui->leDns2->setText(""); + } + + if (mask == "24") { + ui->cbMask->setCurrentIndex(0); + } else if(mask == "23") { + ui->cbMask->setCurrentIndex(1); + } else if(mask == "22") { + ui->cbMask->setCurrentIndex(2); + } else if(mask == "16") { + ui->cbMask->setCurrentIndex(3); + } else if(mask == "8") { + ui->cbMask->setCurrentIndex(4); + } else { + ui->cbMask->setCurrentIndex(0); + } +} + +//点击了创建新的网络的按钮 +void ConfForm::on_btnCreate_clicked() +{ + if (mw->lanNameList.contains(ui->leName->text())) { + qWarning() << "Warn : 已有同名LAN存在:" << ui->leName->text(); + // ui->tipLabel->show(); + return; + } + KylinDBus kylindbus; + kylindbus.getWiredCardName(); + QString mIfname = kylindbus.dbusLanCardName; + + if (mIfname == "") { + QString tip(tr("Can not create new wired network for without wired card")); +// kylindbus.showDesktopNotify(tip); + this->hide(); + return; + } + + QString name = ui->leName->text(); + QString cmdStr = "nmcli connection add con-name \"" + name.replace("\"","\\\"") + "\" type ethernet"; //由于样机存在veth虚拟网卡,故不指定有线设备,将自动选择 + Utils::m_system(cmdStr.toUtf8().data()); + //int status = system(cmdStr.toUtf8().data()); + //if (status != 0){ syslog(LOG_ERR, "execute 'nmcli connection add con-name' in function 'on_btnCreate_clicked' failed");} + + if (ui->cbType->currentIndex() == 1) { + //config the ipv4 and netmask and gateway if select Manual + this->isCreateNewNet = true; + this->on_btnOk_clicked(); + } else { + QString txt(tr("New network already created")); +// kylindbus.showDesktopNotify(txt); + } + + this->hide(); +} + +//点击了保存更改网络设置的按钮 +void ConfForm::on_btnOk_clicked() +{ + QString mask = ""; + if (ui->cbMask->currentIndex() == 0) { + mask = "24"; + } else if(ui->cbMask->currentIndex() == 1) { + mask = "23"; + } else if(ui->cbMask->currentIndex() == 2) { + mask = "22"; + } else if(ui->cbMask->currentIndex() == 3) { + mask = "16"; + } else if(ui->cbMask->currentIndex() == 4) { + mask = "8"; + } else { + mask = "24"; + } + + if (ui->cbType->currentIndex() == 0) { + QString name = ui->leName->text(); + name.replace("\"","\\\""); + kylin_network_set_automethod(name.toUtf8().data()); + } else { + QString dnss = ui->leDns->text(); + if (ui->leDns2->text() != "") { + dnss.append(","); + dnss.append(ui->leDns2->text()); + } + QString name = ui->leName->text(); + name.replace("\"","\\\""); + kylin_network_set_manualall(name.toUtf8().data(), ui->leAddr->text().toUtf8().data(), mask.toUtf8().data(), ui->leGateway->text().toUtf8().data(), dnss.toUtf8().data()); + } + + KylinDBus kylindbus; + + this->hide(); + + QString txt(tr("New network settings already finished")); + kylindbus.showDesktopNotify(txt); + + if (!this->isCreateNewNet) { + if (this->isActConf == true) { + // 如果是修改当前连接的网络,则修改设置后简略重连网络 + //QString cmd = "/usr/share/kylin-nm/shell/connup.sh '" + ui->leName->text() + "'"; + kylindbus.connectWiredNet(ui->leName->text()); //reconnect this wired network + + QString m_txt(tr("New settings already effective")); + kylindbus.showDesktopNotify(m_txt); //show desktop notify + } + + //需要更新一下有线网界面 + qDebug()<<"debug: request refresh Lan list"; + emit requestRefreshLanList(0); + } + this->isCreateNewNet = false; +} + +//点击取消按钮 +void ConfForm::on_btnCancel_clicked() +{ + this->hide(); +} + +//根据需要设置的种类(自动或手动等)显示界面内容 +void ConfForm::cbTypeChanged(int index) +{ + if (isShowSaveBtn) { + ui->leName->setEnabled(false); + ui->btnOk->show(); //显示保存按钮 + ui->btnCreate->hide(); //隐藏创建按钮 + ui->lbLeftupTitle->setText(tr("Edit Network")); + } + + if (index == 0) { + ui->lineUp->hide(); + ui->lineDown->hide(); + ui->wgManual->hide(); + ui->centralWidget->resize(446+194*isTabletStyle, 443); + ui->wdBottom->move(0, 347); + + this->setEnableOfBtn(); + + this->resize(446+194*isTabletStyle,443); + } + if (index == 1) { + ui->lineUp->show(); + ui->lineDown->show(); + ui->wgManual->show(); + ui->centralWidget->resize(446+194*isTabletStyle, 576); + ui->wdBottom->move(0, 480); + + this->setEnableOfBtn(); + + this->resize(446+194*isTabletStyle, 576); + } + if (index == 3) { + ui->isDHCP->setChecked(true); + ui->notDHCP->setChecked(false); + ui->btnOk->setStyleSheet(btnOffQss); + ui->btnOk->setEnabled(false); + ui->btnCreate->setStyleSheet(btnOffQss); + ui->btnCreate->setEnabled(false); + + ui->leName->setEnabled(true); + ui->btnOk->hide(); + ui->btnCreate->show(); + ui->lbLeftupTitle->setText(tr("Add Wired Network")); + isShowSaveBtn = false; + + ui->lineUp->hide(); + ui->lineDown->hide(); + ui->wgManual->hide(); + ui->centralWidget->resize(446+194*isTabletStyle, 443); + ui->wdBottom->move(0, 347); + this->resize(446+194*isTabletStyle, 443); + } +} + +//编辑网络名称 +void ConfForm::on_leName_textEdited(const QString &arg1) +{ + this->setEnableOfBtn(); +} + +//编辑网络ip +void ConfForm::on_leAddr_textEdited(const QString &arg1) +{ + this->setEnableOfBtn(); +} + +//编辑网络网关 +void ConfForm::on_leGateway_textEdited(const QString &arg1) +{ + this->setEnableOfBtn(); +} + +//编辑网络DNS +void ConfForm::on_leDns_textEdited(const QString &arg1) +{ + this->setEnableOfBtn(); +} + +//编辑网络备用DNS +void ConfForm::on_leDns2_textEdited(const QString &arg1) +{ + // this->setEnableOfBtn(); +} + +//设置界面按钮是否可点击 +void ConfForm::setEnableOfBtn() +{ + if (ui->leName->text().size() == 0 ) { + this->setBtnEnableFalse(); + return; + } + + if (ui->cbType->currentIndex() == 1) { + if (!this->getTextEditState(ui->leAddr->text()) ) { + this->setBtnEnableFalse(); + return; + } + + if (!this->getTextEditState(ui->leGateway->text()) ) { + this->setBtnEnableFalse(); + return; + } + + if (!this->getTextEditState(ui->leDns->text()) ) { + this->setBtnEnableFalse(); + return; + } + } + + ui->btnOk->setStyleSheet(btnOnQss); + ui->btnOk->setEnabled(true); + + ui->btnCreate->setStyleSheet(btnOnQss); + ui->btnCreate->setEnabled(true); +} + +//文本的输入要符合ip的格式要求 +bool ConfForm::getTextEditState(QString text) +{ + QRegExp rx("\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\b"); + + bool match = false; + match = rx.exactMatch(text); + // qDebug()<<"the match result is: " << match; + + return match; +} + +//设置创建或保存按钮不可点击 +void ConfForm::setBtnEnableFalse() +{ + ui->btnOk->setStyleSheet(btnOffQss); + ui->btnOk->setEnabled(false); + + ui->btnCreate->setStyleSheet(btnOffQss); + ui->btnCreate->setEnabled(false); +} + +void ConfForm::paintEvent(QPaintEvent *event) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); + QWidget::paintEvent(event); +} + +void ConfForm::on_isDHCP_clicked() +{ + ui->isDHCP->setEnabled(false); + ui->notDHCP->setEnabled(true); + ui->notDHCP->setChecked(false); + ui->cbType->setCurrentIndex(0); + cbTypeChanged(0); +} + +void ConfForm::on_notDHCP_clicked() +{ + ui->notDHCP->setEnabled(false); + ui->isDHCP->setEnabled(true); + ui->isDHCP->setChecked(false); + ui->cbType->setCurrentIndex(1); + cbTypeChanged(1); +} diff --git a/KylinNM/src/confform.h b/KylinNM/src/confform.h new file mode 100644 index 0000000..73999d4 --- /dev/null +++ b/KylinNM/src/confform.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include +#include +#include +#include +#include +#include + +namespace Ui { +class ConfForm; +} +class KylinNM; + +class ConfForm : public QWidget +{ + Q_OBJECT + +public: + explicit ConfForm(QWidget *parent = 0); + ~ConfForm(); + + void setProp(QString connName, QString v4method, QString addr, QString mask, QString gateway, QString dns, bool isActConf ); + void setMainWindow(KylinNM *mainwindow); + +public Q_SLOTS: + void tabletStyle(bool isTablet);//YYF 平板桌面模式特有设置 + void cbTypeChanged(int index); + +protected: + void paintEvent(QPaintEvent *event); + bool eventFilter(QObject *obj, QEvent *ev) override; + +private Q_SLOTS: + void on_btnOk_clicked(); + void on_btnCancel_clicked(); + + void on_btnCreate_clicked(); + + void on_leName_textEdited(const QString &arg1); + + void on_leAddr_textEdited(const QString &arg1); + + void on_leGateway_textEdited(const QString &arg1); + + void on_leDns_textEdited(const QString &arg1); + + void on_leDns2_textEdited(const QString &arg1); + + void setEnableOfBtn(); + bool getTextEditState(QString text); + void setBtnEnableFalse(); + + void on_isDHCP_clicked(); + + void on_notDHCP_clicked(); + +private: + Ui::ConfForm *ui; + + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + + bool isPress; + QPoint winPos; + QPoint dragPos; + bool isActConf; //是否对已经连接的网络进行的更改 + bool isCreateNewNet = false; //是否是创建的新网络 + bool isShowSaveBtn = true; //是否显示保存按钮,即是否是编辑网络界面 + + bool isTabletStyle=false;//YYF 平板桌面模式特有设置 + + QString labelQss, cbxQss, leQss, lineQss, btnOnQss, btnOffQss; + + KylinNM *mw = nullptr; + +Q_SIGNALS: + void requestRefreshLanList(int updateType); +}; + +#endif // CONFFORM_H diff --git a/KylinNM/src/confform.ui b/KylinNM/src/confform.ui new file mode 100644 index 0000000..08ff5b4 --- /dev/null +++ b/KylinNM/src/confform.ui @@ -0,0 +1,434 @@ + + + ConfForm + + + + 0 + 0 + 446 + 576 + + + + + + + + + 0 + 0 + 446 + 581 + + + + + + 0 + 200 + 446 + 280 + + + + + + 175 + 15 + 250 + 32 + + + + + 10 + + + + + + + 175 + 60 + 250 + 32 + + + + + 10 + + + + + + + 175 + 105 + 250 + 32 + + + + + 10 + + + + + + + 175 + 150 + 250 + 32 + + + + + 10 + + + + + + + 20 + 20 + 80 + 20 + + + + + Noto Sans CJK SC + 11 + + + + + + + + + + 20 + 65 + 80 + 20 + + + + + Noto Sans CJK SC + 11 + + + + + + + + + + 20 + 110 + 80 + 20 + + + + + Noto Sans CJK SC + 11 + + + + + + + + + + 20 + 155 + 80 + 20 + + + + + Noto Sans CJK SC + 11 + + + + + + + + + + 175 + 195 + 250 + 32 + + + + + 10 + + + + + + + 20 + 200 + 80 + 20 + + + + + Noto Sans CJK SC + 11 + + + + + + + + + + + 0 + 0 + 446 + 200 + + + + + + 170 + 60 + 182 + 32 + + + + + 10 + + + + + + + 20 + 135 + 80 + 20 + + + + + Noto Sans CJK SC + 11 + + + + + + + + + + 20 + 90 + 80 + 20 + + + + + Noto Sans CJK SC + 11 + + + + + + + + + + 135 + 75 + 290 + 48 + + + + + 10 + + + + + + + 0 + 0 + 446 + 48 + + + + + + 408 + 8 + 24 + 24 + + + + + + + + + + 24 + 10 + 240 + 22 + + + + + + + + + + + 180 + 130 + 111 + 28 + + + + + + + + + + 356 + 130 + 50 + 28 + + + + + + + + + + + 0 + 480 + 446 + 96 + + + + + + 312 + 25 + 112 + 48 + + + + + 10 + + + + + + + + + + 180 + 25 + 112 + 48 + + + + + 10 + + + + + + + + + + 312 + 25 + 112 + 48 + + + + + + + + + + + 10 + 200 + 426 + 1 + + + + Qt::Horizontal + + + + + + 10 + 480 + 426 + 1 + + + + Qt::Horizontal + + + + + + + diff --git a/KylinNM/src/ksimplenm.cpp b/KylinNM/src/ksimplenm.cpp new file mode 100644 index 0000000..d5be6e3 --- /dev/null +++ b/KylinNM/src/ksimplenm.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include +#include + +#define MAX_LEN 2048 +#define MAX_PATH 1024 + +KSimpleNM::KSimpleNM(QObject *parent) : QObject(parent) +{ + runShellProcess = new QProcess(this); + + connect(runShellProcess, &QProcess::readyRead, this, &KSimpleNM::readProcess); + connect(runShellProcess, SIGNAL(finished(int)), this, SLOT(finishedProcess(int))); +} + +//获取有线网络列表数据 +void KSimpleNM::execGetLanList() +{ + if (isExecutingGetWifiList) { + syslog(LOG_DEBUG, "It is running getting wifi list when getting lan list"); + qDebug()<<"debug: it is running getting wifi list when getting lan list"; + isUseOldLanSlist = true; + } + isExecutingGetLanList = true; + shellOutput = ""; + type = 0; + qDebug() << "runShellProcess->start ---1"; + runShellProcess->start("nmcli -f type,device,name connection show"); + qDebug() << "runShellProcess->start ---2"; +} + +//获取无线网络列表数据 +void KSimpleNM::execGetWifiList() +{ + isExecutingGetWifiList = true; + shellOutput = ""; + type = 1; + runShellProcess->start("nmcli -f in-use,signal,security,freq,bssid,ssid,dbus-path device wifi"); +} + +//读取获取到的结果 +void KSimpleNM::readProcess() +{ + QString output = runShellProcess->readAll(); + shellOutput += output; +} + +//读取完所有列表数据后发信号,将数据发往mainwindow用于显示网络列表 +void KSimpleNM::finishedProcess(int msg) +{ + QStringList slist = shellOutput.split("\n"); + if (type == 0) { + emit getLanListFinished(slist); + isExecutingGetLanList = false; + } else { + emit getWifiListFinished(slist); + isExecutingGetWifiList = false; + } +} diff --git a/KylinNM/src/ksimplenm.h b/KylinNM/src/ksimplenm.h new file mode 100644 index 0000000..fd1a427 --- /dev/null +++ b/KylinNM/src/ksimplenm.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see + +#include +#include +#include + +class KSimpleNM : public QObject +{ + Q_OBJECT +public: + explicit KSimpleNM(QObject *parent = nullptr); + + QProcess *runShellProcess = nullptr; + QString shellOutput; + int type; + bool isExecutingGetLanList = false; //是否正在执行获取有线网列表 + bool isExecutingGetWifiList = false; //是否正在执行获取无线网列表 + bool isUseOldLanSlist = false; //是否应该要用上一次获取的有线列表 + + void execGetLanList(); + void execGetWifiList(); + +Q_SIGNALS: + void getLanListFinished(QStringList slist); + void getWifiListFinished(QStringList slist); + +public Q_SLOTS: + void readProcess(); + void finishedProcess(int msg); +}; + +#endif // KSIMPLENM_H diff --git a/KylinNM/src/kylin-dbus-interface.cpp b/KylinNM/src/kylin-dbus-interface.cpp new file mode 100644 index 0000000..ddaba02 --- /dev/null +++ b/KylinNM/src/kylin-dbus-interface.cpp @@ -0,0 +1,1105 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see + +#include +#include + + +namespace { + +void quitThread(QThread *thread) +{ + Q_ASSERT(thread); + if (thread) { + thread->quit(); + if (!thread->wait(2000)) { + thread->terminate(); + thread->wait(); + } + } +} + +} // namespace + +KylinDBus::KylinDBus(KylinNM *mainWindow, QObject *parent) :QObject(parent) +{ + this->mw = mainWindow; + + getObjectPath(); //获取dbus中 lan 与 WiFi 的device路径 + getPhysicalCarrierState(0); //初始化获取网线插入状态 + getLanHwAddressState(); //获取有线网Mac地址 + getWiredCardName(); //获取有线网卡名称 + //initTaskbarGsetting(); //初始化taskbar的GSetting方法 + getWifiSwitchState(); //初始化wifi开关GSetting通信方法 + initTransparentState(); //初始化窗口透明度的GSetting方法 + + QDBusConnection::systemBus().connect(QString("org.freedesktop.NetworkManager"), + QString("/org/freedesktop/NetworkManager"), + QString("org.freedesktop.NetworkManager"), + QString("PropertiesChanged"), this, SLOT(onPropertiesChanged(QVariantMap) ) ); + + QDBusConnection::systemBus().connect(QString("org.freedesktop.NetworkManager"), + QString("/org/freedesktop/NetworkManager"), + QString("org.freedesktop.NetworkManager"), + QString("DeviceAdded"), mw, SLOT(onNetworkDeviceAdded(QDBusObjectPath) ) ); + + QDBusConnection::systemBus().connect(QString("org.freedesktop.NetworkManager"), + QString("/org/freedesktop/NetworkManager"), + QString("org.freedesktop.NetworkManager"), + QString("DeviceRemoved"), mw, SLOT(onNetworkDeviceRemoved(QDBusObjectPath) ) ); + + QDBusConnection::systemBus().connect(QString("org.freedesktop.NetworkManager"), + QString("/org/freedesktop/NetworkManager/Settings"), + QString("org.freedesktop.NetworkManager.Settings"), + QString("NewConnection"), this, SLOT(onNewConnection(QDBusObjectPath) ) ); + + QDBusConnection::systemBus().connect(QString("org.freedesktop.NetworkManager"), + QString("/org/freedesktop/NetworkManager/Settings"), + QString("org.freedesktop.NetworkManager.Settings"), + QString("ConnectionRemoved"), this, SLOT(onConnectionRemoved(QDBusObjectPath) ) ); + + if (wiredPath.path() != "") { + QDBusConnection::systemBus().connect(QString("org.freedesktop.NetworkManager"), + QString(wiredPath.path()), + QString("org.freedesktop.NetworkManager.Device.Wired"), + QString("PropertiesChanged"), this, SLOT(onLanPropertyChanged(QVariantMap) ) ); + } else { + qDebug()<<"Can not find wired device object path when using dbus."; + } + + if (wirelessPath.path() != "") { + QDBusConnection::systemBus().connect(QString("org.freedesktop.NetworkManager"), + QString(wirelessPath.path()), + QString("org.freedesktop.NetworkManager.Device.Wireless"), + QString("PropertiesChanged"), this, SLOT(onWifiPropertyChanged(QVariantMap) ) ); + + QDBusConnection::systemBus().connect(QString("org.freedesktop.NetworkManager"), + QString(wirelessPath.path()), + QString("org.freedesktop.NetworkManager.Device.Wireless"), + QString("AccessPointAdded"), this, SLOT(onAccessPointAdded(QDBusObjectPath) ) ); + getWirelessCardName();//获取无线网卡名称 + } else { + qDebug()<<"Can not find wireless device object path when using dbus."; + } + + time = new QTimer(this); + time->setTimerType(Qt::PreciseTimer); + QObject::connect(time, SIGNAL(timeout()), this, SLOT(slot_timeout())); + + QObject::connect(this, SIGNAL(updateWiredList(int)), mw, SLOT(onBtnNetListClicked(int))); + + mUtils = new Utils(); + mUtilsThread = new QThread(this); + mUtils->moveToThread(mUtilsThread); + connect(mUtilsThread, &QThread::finished, mUtils, &Utils::deleteLater); + connect(this, SIGNAL(requestSendDesktopNotify(QString)), mUtils, SLOT(onRequestSendDesktopNotify(QString)), Qt::QueuedConnection); + QTimer::singleShot(1, this, [=] { + mUtilsThread->start(); + }); +} + +KylinDBus::~KylinDBus() +{ + quitThread(mUtilsThread); +} + + +/////////////////////////////////////////////////////////////////////////////// +//下方使用Dbus 进程通信方法 + +//获取dbus中 lan 与 WiFi 的device路径 +void KylinDBus::getObjectPath() +{ + foreach (QDBusObjectPath mPath, multiWiredPaths) { + multiWiredPaths.removeOne(mPath); + } + + QDBusInterface m_interface( "org.freedesktop.NetworkManager", + "/org/freedesktop/NetworkManager", + "org.freedesktop.NetworkManager", + QDBusConnection::systemBus() ); + + //先获取所有的网络设备的设备路径 + QDBusReply> obj_reply = m_interface.call("GetAllDevices"); + if (!obj_reply.isValid()) { + qDebug()<<"execute dbus method 'GetAllDevices' is invalid in func getObjectPath()"; + } + + QList obj_paths = obj_reply.value(); + + //再判断有无有线设备和无线设备的路径 + foreach (QDBusObjectPath obj_path, obj_paths) { + QDBusInterface interface( "org.freedesktop.NetworkManager", + obj_path.path(), + "org.freedesktop.DBus.Introspectable", + QDBusConnection::systemBus() ); + + QDBusReply reply = interface.call("Introspect"); + if (!reply.isValid()) { + qDebug()<<"execute dbus method 'Introspect' is invalid in func getObjectPath()"; + } + + if(reply.value().indexOf("org.freedesktop.NetworkManager.Device.Wired") != -1) { + //表明有有线网设备 + wiredPath = obj_path; + multiWiredPaths.append(obj_path); + } else if (reply.value().indexOf("org.freedesktop.NetworkManager.Device.Wireless") != -1) { + //表明有wifi设备 + wirelessPath = obj_path; + isWirelessCardOn = true; + } + } +} + +//获取是否连接有线网网线 +void KylinDBus::getPhysicalCarrierState(int n) +{ + multiWiredCableState.clear(); + + foreach (QDBusObjectPath localPath, multiWiredPaths) { + QDBusInterface interface( "org.freedesktop.NetworkManager", + localPath.path(), + "org.freedesktop.DBus.Properties", + QDBusConnection::systemBus() ); + + //Carrier值为true,插了网线;为false,未插网线 + QDBusReply reply = interface.call("Get", "org.freedesktop.NetworkManager.Device.Wired", "Carrier"); + + try { + if (reply.value().toString() == "true") { + //isWiredCableOn = true; + QDBusReply managed = interface.call("Get", "org.freedesktop.NetworkManager.Device", "Managed"); + //针对intel特殊环境的二次判断,当设备未托管时,即时carrier为true,仍认为有线设备未连接 + if (managed.value().toString() == "true") { + //isWiredCableOn = true; + multiWiredCableState.append("true"); + } else { + multiWiredCableState.append("false"); + } + } else if (reply.value().toString() == "false") { + //isWiredCableOn = false; + multiWiredCableState.append("false"); + //if (n == 1){ this->mw->onPhysicalCarrierChanged(isWiredCableOn);} + } else { + throw -1; //出现异常 + } + } catch(...) { + syslog(LOG_ERR, "Error occurred when get the property 'Carrier' of Wired"); + qDebug()<<"Error occurred when get the property 'Carrier' of Wired"; + } + } + + isWiredCableOn = false; + foreach (QString state, multiWiredCableState) { + if (state == "true") { + isWiredCableOn = true; + } + } + + if (n == 1) { this->mw->onPhysicalCarrierChanged(isWiredCableOn);} +} + +//获取有线网Mac地址 +void KylinDBus::getLanHwAddressState() +{ + QDBusInterface lanInterface( "org.freedesktop.NetworkManager", + wiredPath.path(), + "org.freedesktop.DBus.Properties", + QDBusConnection::systemBus() ); + + QDBusReply lanReply = lanInterface.call("Get", "org.freedesktop.NetworkManager.Device.Wired", "HwAddress"); + if (!lanReply.isValid()) { + qDebug()<<"can not get the attribute 'HwAddress' in func getLanHwAddressState()"; + } else { + dbusLanMac = lanReply.value().toString(); + } +} + +//获取有线网卡名称 +void KylinDBus::getWiredCardName() +{ + if (wiredPath.path() == "") { + dbusLanCardName = ""; + } else { + QDBusInterface lanInterface( "org.freedesktop.NetworkManager", + wiredPath.path(), + "org.freedesktop.DBus.Properties", + QDBusConnection::systemBus() ); + + QDBusReply lanReply = lanInterface.call("Get", "org.freedesktop.NetworkManager.Device", "Interface"); + if (!lanReply.isValid()) { + qDebug()<<"can not get the attribute 'Interface' in func getWiredCardName()"; + dbusLanCardName = ""; + } else { + dbusLanCardName = lanReply.value().toString(); + } + } +} + +//获取无线网卡名称 +void KylinDBus::getWirelessCardName() +{ + QDBusInterface lanInterface( "org.freedesktop.NetworkManager", + wirelessPath.path(), + "org.freedesktop.DBus.Properties", + QDBusConnection::systemBus() ); + + QDBusReply lanReply = lanInterface.call("Get", "org.freedesktop.NetworkManager.Device", "Interface"); + if (!lanReply.isValid()) { + qDebug()<<"can not get the attribute 'Interface' in func getWirelessCardName()"; + } else { + dbusWiFiCardName = lanReply.value().toString(); + } +} + +//获取没有连接的有线网ip +void KylinDBus::getLanIp(QString netName) +{ + QDBusInterface m_interface("org.freedesktop.NetworkManager", + "/org/freedesktop/NetworkManager/Settings", + "org.freedesktop.NetworkManager.Settings", + QDBusConnection::systemBus() ); + QDBusReply> m_reply = m_interface.call("ListConnections"); + + QList m_objNets = m_reply.value(); + foreach (QDBusObjectPath objNet, m_objNets) { + QDBusInterface m_interface("org.freedesktop.NetworkManager", + objNet.path(), + "org.freedesktop.NetworkManager.Settings.Connection", + QDBusConnection::systemBus()); + QDBusMessage result = m_interface.call("GetSettings"); //get information of this network + + const QDBusArgument &dbusArg1st = result.arguments().at( 0 ).value(); + //DBus type : a{sa{sv}}, a map with a key of QString, which maps to another map of QString,QVariant + QMap> map; + dbusArg1st >> map; + + for (QString outside_key : map.keys() ) { + QMap outsideMap = map.value(outside_key); + if (outside_key == "connection") { + for (QString search_key : outsideMap.keys()) { + if (search_key == "id") { + //const QDBusArgument &dbusArg2nd = innerMap.value(inner_key).value(); + if (netName == outsideMap.value(search_key).toString()) { + // qDebug()<<"aaaaaa"< innerMap = map.value(key); + //qDebug() << "Key: " << key; + if (key == "ipv4") { + for (QString inner_key : innerMap.keys()) { + if (inner_key == "address-data") { + const QDBusArgument &dbusArg2nd = innerMap.value(inner_key).value(); + QMap m_map; + + dbusArg2nd.beginArray(); + while (!dbusArg2nd.atEnd()) { + dbusArg2nd >> m_map;// append map to a vector here if you want to keep it + } + dbusArg2nd.endArray(); + + //qDebug()<<" " << m_map.value("address").toString(); + dbusLanIpv4 = m_map.value("address").toString(); + } + } + } + + if (key == "ipv6") { + for (QString inner_key : innerMap.keys()) { + if (inner_key == "address-data"){ + const QDBusArgument &dbusArg2nd = innerMap.value(inner_key).value(); + QMap m_map; + + dbusArg2nd.beginArray(); + while (!dbusArg2nd.atEnd()) { + dbusArg2nd >> m_map;// append map to a vector here if you want to keep it + } + dbusArg2nd.endArray(); + + //qDebug()<<" " << m_map.value("address").toString(); + dbusLanIpv6 = m_map.value("address").toString(); + } + } + } + } //end for(QString key : map.keys() ) + + } + } + } //end for (QString search_key : outsideMap.keys()) + } + } // end for(QString outside_key : map.keys() ) + + } //end foreach (QDBusObjectPath objNet, m_objNets) +} + +//获取已经连接的有线网ip +void KylinDBus::getConnectNetIp() +{ + QDBusInterface interface( "org.freedesktop.NetworkManager", + "/org/freedesktop/NetworkManager", + "org.freedesktop.DBus.Properties", + QDBusConnection::systemBus() ); + + //获取已经连接了那些网络,及这些网络对应的网络类型(ethernet or wifi) + QDBusMessage result = interface.call("Get", "org.freedesktop.NetworkManager", "ActiveConnections"); + QList outArgs = result.arguments(); + QVariant first = outArgs.at(0); + QDBusVariant dbvFirst = first.value(); + QVariant vFirst = dbvFirst.variant(); + QDBusArgument dbusArgs = vFirst.value(); + + QDBusObjectPath objPath; + dbusArgs.beginArray(); + while (!dbusArgs.atEnd()) { + dbusArgs >> objPath; + + QDBusInterface interfacePro( "org.freedesktop.NetworkManager", + objPath.path(), + "org.freedesktop.DBus.Properties", + QDBusConnection::systemBus() ); + + QDBusReply reply = interfacePro.call("Get", "org.freedesktop.NetworkManager.Connection.Active", "Type"); + if (reply.value().toString() == "ethernet" || reply.value().toString() == "802-3-ethernet") { + //ipv4的路径信息和ip信息 + QDBusInterface interfaceIp4( "org.freedesktop.NetworkManager", + objPath.path(), + "org.freedesktop.DBus.Properties", + QDBusConnection::systemBus() ); + QDBusMessage replyIp4 = interfaceIp4.call("Get", "org.freedesktop.NetworkManager.Connection.Active", "Ip4Config"); + QList outArgsIp4 = replyIp4.arguments(); + QVariant firstIp4 = outArgsIp4.at(0); + QDBusVariant dbvFirstIp4 = firstIp4.value(); + QVariant vFirstIp4 = dbvFirstIp4.variant(); + QDBusObjectPath dbusPathIp4 = vFirstIp4.value(); + + QDBusInterface interfaceIpv4( "org.freedesktop.NetworkManager", + dbusPathIp4.path(), + "org.freedesktop.DBus.Properties", + QDBusConnection::systemBus() ); + QDBusMessage replyIpv4 = interfaceIpv4.call("Get", "org.freedesktop.NetworkManager.IP4Config", "AddressData"); + + QList outArgsIpv4 = replyIpv4.arguments(); + if(outArgsIp4.count() >= 0){ + QVariant firstIpv4 = outArgsIpv4.at(0); + QDBusVariant dbvFirstIpv4 = firstIpv4.value(); + QVariant vFirstIpv4 = dbvFirstIpv4.variant(); + + const QDBusArgument &dbusArgIpv4 = vFirstIpv4.value(); + QList mDatasIpv4; + dbusArgIpv4 >> mDatasIpv4; + + foreach (QVariantMap mDataIpv4, mDatasIpv4) { + dbusActiveLanIpv4 = mDataIpv4.value("address").toString(); + } + } + //ipv6的路径信息和ip信息 + QDBusInterface interfaceIp6( "org.freedesktop.NetworkManager", + objPath.path(), + "org.freedesktop.DBus.Properties", + QDBusConnection::systemBus() ); + QDBusMessage replyIp6 = interfaceIp6.call("Get", "org.freedesktop.NetworkManager.Connection.Active", "Ip6Config"); + QList outArgsIp6 = replyIp6.arguments(); + QVariant firstIp6 = outArgsIp6.at(0); + QDBusVariant dbvFirstIp6 = firstIp6.value(); + QVariant vFirstIp6 = dbvFirstIp6.variant(); + QDBusObjectPath dbusPathIp6 = vFirstIp6.value(); + + QDBusInterface interfaceIpv6( "org.freedesktop.NetworkManager", + dbusPathIp6.path(), + "org.freedesktop.DBus.Properties", + QDBusConnection::systemBus() ); + QDBusMessage replyIpv6 = interfaceIpv6.call("Get", "org.freedesktop.NetworkManager.IP6Config", "AddressData"); + + QList outArgsIpv6 = replyIpv6.arguments(); + if(outArgsIp6.count() >= 0){ + QVariant firstIpv6 = outArgsIpv6.at(0); + QDBusVariant dbvFirstIpv6 = firstIpv6.value(); + QVariant vFirstIpv6 = dbvFirstIpv6.variant(); + + const QDBusArgument &dbusArgIpv6 = vFirstIpv6.value(); + QList mDatasIpv6; + dbusArgIpv6 >> mDatasIpv6; + + foreach (QVariantMap mDataIpv6, mDatasIpv6) { + dbusActiveLanIpv6 = mDataIpv6.value("address").toString(); + } + } + } + } + dbusArgs.endArray(); +} + +//获取wifi的mac地址 +void KylinDBus::getWifiMac(QString netName) +{ + dbusWifiMac = ""; + + QDBusInterface interface( "org.freedesktop.NetworkManager", + wirelessPath.path(), + "org.freedesktop.NetworkManager.Device.Wireless", + QDBusConnection::systemBus() ); + + QDBusReply> reply = interface.call("GetAllAccessPoints"); //get accesspoint for each wifi + QList objPaths = reply.value(); + + foreach (QDBusObjectPath objPath, objPaths) { + QDBusInterface ssid_interface( "org.freedesktop.NetworkManager", + objPath.path(), + "org.freedesktop.DBus.Properties", + QDBusConnection::systemBus() ); + + QDBusReply ssid_replys = ssid_interface.call("Get", "org.freedesktop.NetworkManager.AccessPoint", "Ssid"); + QString str_name = ssid_replys.value().toString(); + + if (str_name == netName) { + //if the ssid of the accesspoint is just the netName, we can get this hardware address + QDBusInterface path_interface( "org.freedesktop.NetworkManager", + objPath.path(), + "org.freedesktop.DBus.Properties", + QDBusConnection::systemBus() ); + + QDBusReply path_reply = path_interface.call("Get", "org.freedesktop.NetworkManager.AccessPoint", "HwAddress"); + dbusWifiMac = path_reply.value().toString(); + } + } +} + +//获取当前能获取到的wifi的数量, 每个accesspoint 代表一个wifi +int KylinDBus::getAccessPointsNumber() +{ + QDBusInterface interface( "org.freedesktop.NetworkManager", + wirelessPath.path(), + "org.freedesktop.NetworkManager.Device.Wireless", + QDBusConnection::systemBus() ); + + QDBusReply> reply = interface.call("GetAllAccessPoints"); + QList objPaths = reply.value(); + + return objPaths.size(); +} + +//新增了一个网络,伴随着多了一个网络配置文件 +void KylinDBus::onNewConnection(QDBusObjectPath objPath) +{ + QDBusInterface m_interface("org.freedesktop.NetworkManager", + objPath.path(), + "org.freedesktop.NetworkManager.Settings.Connection", + QDBusConnection::systemBus()); + QDBusMessage result = m_interface.call("GetSettings"); + + const QDBusArgument &dbusArg1st = result.arguments().at( 0 ).value(); + QMap> map; + dbusArg1st >> map; + + for(QString key : map.keys() ) { + if (key == "802-3-ethernet") { + emit this->updateWiredList(0); //send this signal to update wired network list + syslog(LOG_DEBUG, "A new wired network was created."); + qDebug()<<"A new wired network was created."; + } + } +} + +//减少了一个网络,伴随着减少了一个网络配置文件 +void KylinDBus::onConnectionRemoved(QDBusObjectPath objPath) +{ + syslog(LOG_DEBUG, "An old network was removed from configure directory."); + qDebug()<<"An old network was removed from configure directory."; + + if (mw->is_btnNetList_clicked == 1) { + emit this->updateWiredList(0); //send this signal to update wired network list + } +} + +//应用启动时,获取已经连接的网络信息,包括该网络的路径和类型 +void KylinDBus::initConnectionInfo() +{ + QDBusInterface interface( "org.freedesktop.NetworkManager", + "/org/freedesktop/NetworkManager", + "org.freedesktop.DBus.Properties", + QDBusConnection::systemBus() ); + + //获取已经连接了那些网络,及这些网络对应的网络类型(ethernet or wifi) + QDBusMessage result = interface.call("Get", "org.freedesktop.NetworkManager", "ActiveConnections"); + QList outArgs = result.arguments(); + QVariant first = outArgs.at(0); + QDBusVariant dbvFirst = first.value(); + QVariant vFirst = dbvFirst.variant(); + QDBusArgument dbusArgs = vFirst.value(); + + QDBusObjectPath objPath; + // qDebug()<<" "; + dbusArgs.beginArray(); + while (!dbusArgs.atEnd()) { + dbusArgs >> objPath; + oldPaths.append(objPath); + // qDebug() <<"debug: *****path is: "<< objPath.path(); + + QDBusInterface interface( "org.freedesktop.NetworkManager", + objPath.path(), + "org.freedesktop.DBus.Properties", + QDBusConnection::systemBus() ); + + QDBusReply reply = interface.call("Get", "org.freedesktop.NetworkManager.Connection.Active", "Type"); + // qDebug()<<"debug: *****connection type is: "< m_result = interface.call("Get", "org.freedesktop.NetworkManager", "WirelessEnabled"); + qDebug()<<"debug: *****初始的无线网络开关状态是: "<(); + QList newPaths; + dbusArg >> newPaths; + QStringList newPathInfo; + qDebug()<<" "; + foreach (QDBusObjectPath objPath, newPaths) { + qDebug()<<"dbug: bbbbb "< reply = interface.call("Get", "org.freedesktop.NetworkManager.Connection.Active", "Type"); + qDebug()<<"dbug: ccccc "<onExternalConnectionChange(oldPathInfo.at(i)); + } else { + for (int j=0; jonExternalConnectionChange(oldPathInfo.at(i)); + } + } + } + } + } + + // 第三步 同上一次获取的已连接网络相比较,处理相比于上次增加的网络连接 + for (int i=0; ionExternalConnectionChange(newPathInfo.at(i)); + } else { + for (int j=0; jonExternalConnectionChange(newPathInfo.at(i)); + } + } + } + } + + bool isChangeOldPathInfo = true; + for (int k=0; konExternalWifiSwitchChange(true); + setBtnWifiGsetting(true); + } + if (oldWifiSwitchState == true && newWifiSwitchState == false) { + qDebug()<<"debug: wifi开关已经关闭"; + is_wificonnected = false; + mw->onExternalWifiSwitchChange(false); + setBtnWifiGsetting(false); + } + + oldWifiSwitchState = newWifiSwitchState; //更新状态用于下一次 + } + } +} + +//有线网属性变化时,执行该函数。由于可能在短时间收到几条相同属性变化信息,所以在短时间内,执行一次 +void KylinDBus::onLanPropertyChanged(QVariantMap qvm) +{ + if (!isRunningFunction) { + syslog(LOG_DEBUG, "kylin-nm receive a signal 'Device.Wired PropertiesChanged' about interface."); + qDebug()<<"kylin-nm receive a signal 'Device.Wired PropertiesChanged' about interface."; + isRunningFunction = true; //function onLanPropertyChanged is running + time->start(3000); + + QString str = qvm.value("Carrier").toString(); + if (str == "false" || str == "true") { + getPhysicalCarrierState(1); + } + } else { a = 0; } +} +void KylinDBus::slot_timeout() +{ + isRunningFunction = false; + time->stop(); +} + +//无线网属性变化时,执行该函数 +void KylinDBus::onWifiPropertyChanged(QVariantMap qvm) +{ + //qDebug()<<"debug: *************"<> m_reply = m_interface.call("ListConnections"); + + QDBusObjectPath active_connection; + active_connection.setPath("/"); + + QList m_objNets = m_reply.value(); + foreach (QDBusObjectPath objNet, m_objNets) { + QDBusInterface m_interface("org.freedesktop.NetworkManager", + objNet.path(), + "org.freedesktop.NetworkManager.Settings.Connection", + QDBusConnection::systemBus()); + QDBusMessage result = m_interface.call("GetSettings"); + + const QDBusArgument &dbusArg1st = result.arguments().at( 0 ).value(); + QMap> map; + dbusArg1st >> map; + + for (QString outside_key : map.keys() ) { + QMap outsideMap = map.value(outside_key); + if (outside_key == "connection") { + for (QString search_key : outsideMap.keys()) { + if (search_key == "id") { + if (netName == outsideMap.value(search_key).toString()) { + + QDBusInterface m_interface("org.freedesktop.NetworkManager", + "/org/freedesktop/NetworkManager", + "org.freedesktop.NetworkManager", + QDBusConnection::systemBus() ); + QDBusReply connectionReply = m_interface.call("ActivateConnection", + QVariant::fromValue(objNet), + QVariant::fromValue(wiredPath), + QVariant::fromValue(active_connection)); + } + } + } + } + } // end for(QString outside_key : map.keys() ) + + } //end foreach (QDBusObjectPath objNet, m_objNets) +} + +//显示桌面通知 +void KylinDBus::showDesktopNotify(QString message) +{ + //锁屏不发送该通知 +// emit requestSendDesktopNotify(message); +} + +//获取任务栏位置,上下左右 +int KylinDBus::getTaskBarPos(QString str) +{ + QDBusInterface interface( "com.ukui.panel.desktop", + "/", + "com.ukui.panel.desktop", + QDBusConnection::sessionBus() ); + + QDBusReply reply = interface.call("GetPanelPosition", str); + if (reply.isValid()) { + return reply; + } else { + return 0; //如果没有成功获取,返回一个默认值 + } +} + +//获取任务栏高度 +int KylinDBus::getTaskBarHeight(QString str) +{ + QDBusInterface interface( "com.ukui.panel.desktop", + "/", + "com.ukui.panel.desktop", + QDBusConnection::sessionBus() ); + + QDBusReply reply = interface.call("GetPanelSize", str); + if (reply.isValid()) { + return reply; + } else { + return 46; //如果没有成功获取,返回一个默认值 + } +} + +/////////////////////////////////////////////////////////////////////////////// +//下方使用Gsetting 进程通信方法 + +//创建获取任务栏信息的GSetting对象 +void KylinDBus::initTaskbarGsetting() +{ + if (QGSettings::isSchemaInstalled("org.ukui.panel.settings")) { + m_tastbar_gsettings = new QGSettings("org.ukui.panel.settings"); + } + if (QGSettings::isSchemaInstalled("org.kylinnm.settings")) { + m_nm_gsettings = new QGSettings("org.kylinnm.settings"); + if (m_nm_gsettings->keys().contains("wifiswitch")) { + m_nm_gsettings->set("wifiswitch",oldWifiSwitchState); + } + } +} + +//使用GSetting方法获取任务栏高度 +int KylinDBus::getTaskbarHeight() +{ + if (!m_tastbar_gsettings) { + return 46; + } + + QStringList keys = m_tastbar_gsettings->keys(); + if (keys.contains("panelsize")) { + int hh = m_tastbar_gsettings->get("panelsize").toInt(); + return hh; + } else { + return 46; + } +} + +//使用GSetting方法写入WIFW信号强度 +void KylinDBus::setWifiSignal(int wifiSignal, QString wifiname) +{ if(!m_tastbar_gsettings || !m_nm_gsettings) + return; + + if (m_tastbar_gsettings->keys().contains("wifisignal")) { + m_tastbar_gsettings->set("wifisignal",wifiSignal); + } + if (m_nm_gsettings->keys().contains("wifisignal")) { + m_nm_gsettings->set("wifisignal",wifiSignal); + } + if (m_nm_gsettings->keys().contains("wifiname")) { + m_nm_gsettings->set("wifiname",wifiname); + } +} +//使用GSetting方法获取任务栏位置,上下左右 +int KylinDBus::getTaskbarPos() +{ + if (!m_tastbar_gsettings) { + return 0; + } + + QStringList keys = m_tastbar_gsettings->keys(); + if (keys.contains("panelposition")) { + int pp = m_tastbar_gsettings->get("panelposition").toInt(); + return pp; + } else { + return 0; + } +} + +//创建监听wifi打开或关闭信息的GSetting对象 +void KylinDBus::getWifiSwitchState() +{ + if (QGSettings::isSchemaInstalled("org.ukui.control-center.wifi.switch")) { + + m_gsettings = new QGSettings("org.ukui.control-center.wifi.switch"); + + // 监听key的value是否发生了变化 + connect(m_gsettings, &QGSettings::changed, this, [=] (const QString &key) { + + if (key == "switch") { + if (isWirelessCardOn) { + bool judge = getSwitchStatus(key); + if (judge) { + mw->onBtnWifiClicked(2); //打开wifi开关 + qDebug()<<"receive a signal to turn on wifi switch from control-center"; + syslog(LOG_DEBUG, "receive a signal to turn on wifi switch from control-center"); + } else { + if (!is_wificonnected) { + return; + } + mw->onBtnWifiClicked(3); //关闭wifi开关 + qDebug()<<"receive a signal to turn off wifi switch from control-center"; + syslog(LOG_DEBUG, "receive a signal to turn off wifi switch from control-center"); + } + } + } + }); + } +} + +//收到控制面板发来的消息后,获取wifi开关信息 +bool KylinDBus::getSwitchStatus(QString key) { + if (!m_gsettings) { + return true; + } + const QStringList list = m_gsettings->keys(); + if (!list.contains(key)) { + return true; + } + bool res = m_gsettings->get(key).toBool(); + return res; +} + +//通知控制面板wifi开关的信息 +void KylinDBus::setWifiSwitchState(bool signal) +{ + if (!m_gsettings) { + return ; + } + + const QStringList list = m_gsettings->keys(); + + if (!list.contains("switchor")) { + return ; + } + m_gsettings->set("switchor",signal); + + if (!m_nm_gsettings) { + return ; + } + if (m_nm_gsettings->keys().contains("wifiswitch")) { + m_nm_gsettings->set("wifiswitch",signal); + } +} + +void KylinDBus::setWifiConnStatus(bool is_wifi_connected) +{ +// qDebug()<<"WIFI连接状态:"<keys().contains("wificonn")) { + m_nm_gsettings->set("wificonn",is_wifi_connected); + } +} + +void KylinDBus::setLanConnStatus(bool is_lan_connected) +{ +// qDebug()<<"LAN连接状态:"<keys().contains("lanconn")) { + m_nm_gsettings->set("lanconn",is_lan_connected); + } +} + +//通知控制面板无线网卡的状态信息 +void KylinDBus::setWifiCardState(bool signal) +{ + if (!m_gsettings) { + return ; + } + + const QStringList list = m_gsettings->keys(); + + if (!list.contains("wificard")) { + return ; + } + m_gsettings->set("wificard",signal); +} + +//创建获取窗口透明度信息的GSetting的对象 +void KylinDBus::initTransparentState() +{ + if (QGSettings::isSchemaInstalled("org.ukui.control-center.personalise")) { + m_transparency_gsettings = new QGSettings("org.ukui.control-center.personalise"); + } +} + +//创建WIFI开关状态的GSetting的对象 +void KylinDBus::initBtnWifiGsetting() +{ + if (QGSettings::isSchemaInstalled("org.ukui.control-center.wifi.switch")) { + m_btnWifi_gsettings = new QGSettings("org.ukui.control-center.wifi.switch"); + } +} +void KylinDBus::setBtnWifiGsetting(bool bl) +{ + if(m_btnWifi_gsettings->get("switch").toBool()!=bl) + { + m_btnWifi_gsettings->set("switch",bl); + } +} + +//根据Dbus路径获取wifi的ssid(排除空格干扰) +QString KylinDBus::getWifiSsid(QString accessPointPath) +{ + QDBusInterface interface( "org.freedesktop.NetworkManager", + accessPointPath, + "org.freedesktop.DBus.Properties", + QDBusConnection::systemBus() ); + QDBusMessage result = interface.call("Get", "org.freedesktop.NetworkManager.AccessPoint", "Ssid"); + if (result.arguments().isEmpty()) return ""; + return result.arguments().at(0).value().variant().toString(); +} + +//获取已经连接无线网络的ssid和uuid +QList KylinDBus::getAtiveWifiBSsidUuid(QStringList wifilist) +{ + QList strBSsidUuid; + + QDBusInterface interface( "org.freedesktop.NetworkManager", + "/org/freedesktop/NetworkManager", + "org.freedesktop.DBus.Properties", + QDBusConnection::systemBus() ); + //获取已经连接了那些网络,及这些网络对应的网络类型(ethernet or wifi) + QDBusMessage result = interface.call("Get", "org.freedesktop.NetworkManager", "ActiveConnections"); + QList outArgs = result.arguments(); + QVariant first = outArgs.at(0); + QDBusVariant dbvFirst = first.value(); + QVariant vFirst = dbvFirst.variant(); + QDBusArgument dbusArgs = vFirst.value(); + + QDBusObjectPath objPath; + dbusArgs.beginArray(); + while (!dbusArgs.atEnd()) { + dbusArgs >> objPath; + + QDBusInterface interfaceType( "org.freedesktop.NetworkManager", + objPath.path(), + "org.freedesktop.DBus.Properties", + QDBusConnection::systemBus() ); + QDBusReply reply = interfaceType.call("Get", "org.freedesktop.NetworkManager.Connection.Active", "Type"); + + if (reply.value().toString() == "wifi" || reply.value().toString() == "802-11-wireless") { + QDBusInterface interfaceInfo( "org.freedesktop.NetworkManager", + objPath.path(), + "org.freedesktop.DBus.Properties", + QDBusConnection::systemBus() ); + + //获取已经连接wifi的uuid + QDBusReply replyUuid = interfaceInfo.call("Get", "org.freedesktop.NetworkManager.Connection.Active", "Uuid"); + //qDebug() << "wifi uuid : "<< replyUuid.value().toString(); + strBSsidUuid.append(replyUuid.value().toString()); + + + //再获取bssid + QDBusMessage resultConnection = interfaceInfo.call("Get", "org.freedesktop.NetworkManager.Connection.Active", "Connection"); + + QList outArgsConnection = resultConnection.arguments(); + QVariant firstConnection = outArgsConnection.at(0); + QDBusVariant dbvFirstConnection = firstConnection.value(); + QVariant vFirstConnection = dbvFirstConnection.variant(); + QDBusObjectPath dbusArgsConnection = vFirstConnection.value(); + + QDBusInterface interfaceSet("org.freedesktop.NetworkManager", + dbusArgsConnection.path(), + "org.freedesktop.NetworkManager.Settings.Connection", + QDBusConnection::systemBus()); + QDBusMessage resultSet = interfaceSet.call("GetSettings"); + + const QDBusArgument &dbusArg1stSet = resultSet.arguments().at( 0 ).value(); + QMap> mapSet; + dbusArg1stSet >> mapSet; + + for (QString setKey : mapSet.keys() ) { + QMap subSetMap = mapSet.value(setKey); + if (setKey == "802-11-wireless") { + for (QString searchKey : subSetMap.keys()) { + if (searchKey == "seen-bssids") { + //qDebug() << "wifi bssid : "<keys(); + if (keys.contains("transparency")) { + double tp = m_transparency_gsettings->get("transparency").toDouble(); + return tp; + } else { + return 0.7; + } +} diff --git a/KylinNM/src/kylin-dbus-interface.h b/KylinNM/src/kylin-dbus-interface.h new file mode 100644 index 0000000..b62c4c5 --- /dev/null +++ b/KylinNM/src/kylin-dbus-interface.h @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class KylinNM; +class Utils; + +class KylinDBus : public QObject +{ + Q_OBJECT +public: + explicit KylinDBus(KylinNM *mw = 0, QObject *parent = nullptr); + ~KylinDBus(); + + void getObjectPath(); + int getAccessPointsNumber(); + void showDesktopNotify(QString message); + void initConnectionInfo(); + void connectWiredNet(QString netName); + void getConnectNetIp(); + + int getTaskBarPos(QString str); + int getTaskBarHeight(QString str); + void initTaskbarGsetting(); + int getTaskbarHeight(); + int getTaskbarPos(); + + void getWifiSwitchState(); + bool getSwitchStatus(QString key); + void setWifiSwitchState(bool signal); + void setWifiCardState(bool signal); + + void initTransparentState(); + double getTransparentData(); + + void initBtnWifiGsetting(); + void setBtnWifiGsetting(bool bl); + QString getWifiSsid(QString accessPointPath); + QList getAtiveWifiBSsidUuid(QStringList wifilist); + + QDBusObjectPath wiredPath; //有线设备的路径 + QDBusObjectPath wirelessPath; //无线设备的路径 + QList multiWiredPaths; //已连接网络的对象路径列表 + + QList multiWiredCableState; + bool isWiredCableOn = false; //是否插入了网线 + bool isWirelessCardOn = false; //是否插入了无线网卡 + + QString dbusLanCardName = ""; + QString dbusLanIpv4 = ""; + QString dbusLanIpv6 = ""; + QString dbusActiveLanIpv4 = ""; + QString dbusActiveLanIpv6 = ""; + QString dbusLanGateway = ""; + QString dbusLanMac = ""; + QString dbusWiFiCardName = ""; + QString dbusWifiMac = ""; + +public Q_SLOTS: + void onNewConnection(QDBusObjectPath objPath); + void onConnectionRemoved(QDBusObjectPath objPath); + void onPropertiesChanged(QVariantMap qvm); + void onLanPropertyChanged(QVariantMap qvm); + void onWifiPropertyChanged(QVariantMap qvm); + void onAccessPointAdded(QDBusObjectPath objPath); + void getPhysicalCarrierState(int n); + void getLanHwAddressState(); + void getWiredCardName(); + void getWirelessCardName(); + void getLanIp(QString netName); + void getWifiMac(QString netName); + void slot_timeout(); + void setWifiSignal(int wifiSignal, QString wifiname); + void setWifiConnStatus(bool is_wifi_connected); + void setLanConnStatus(bool is_lan_connected); + +private: + KylinNM *mw; + Utils *mUtils; + QThread *mUtilsThread; + + int a = 0; + bool isRunningFunction = false; + QTimer *time = nullptr; + QList oldPaths; //已连接网络的对象路径列表 + QStringList oldPathInfo; //某个已连接网络对象路径对应的网络类型(ethernet or wifi) + bool oldWifiSwitchState; //上一次获取到的wifi开关状态 + + QGSettings *m_tastbar_gsettings = nullptr; + QGSettings *m_nm_gsettings = nullptr; + QGSettings *m_gsettings = nullptr; + QGSettings *m_transparency_gsettings = nullptr; + QGSettings *m_btnWifi_gsettings = nullptr; + + bool is_wificonnected = false; + +Q_SIGNALS: + void updateWiredList(int n); + void requestSendDesktopNotify(QString message); +}; + +#endif // KYLINDBUSINTERFACE_H diff --git a/KylinNM/src/kylin-network-interface.c b/KylinNM/src/kylin-network-interface.c new file mode 100644 index 0000000..623f457 --- /dev/null +++ b/KylinNM/src/kylin-network-interface.c @@ -0,0 +1,997 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +//获取网络接口名 +ifname *kylin_network_get_ifacename() +{ + int i = 0; + int sockfd; + struct ifconf ifconf; + struct ifreq *ifreq; + unsigned char buf[1024]; + + //初始化ifconf + ifconf.ifc_len = 1024; + //字符串指针ifconf.ifc_buf指向buf + ifconf.ifc_buf = buf; + + if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) + { + perror("socket error"); + return 0; + } + + //获取所有接口信息 + ioctl(sockfd, SIOCGIFCONF, &ifconf); + // printf("%s\n",ifconf.ifc_buf); + + //逐个获取Ip地址 + //结构体指针ifreq指向buf,即ifconf.ifc_buf + ifreq = (struct ifreq*)buf; + int number=ifconf.ifc_len/sizeof(struct ifreq); + // printf("%d\n",number); + + ifname *ifn=(ifname *)malloc(sizeof(ifname)*(number+1)); + + for(i = number; i>0; i--) + { + // printf("name = [%s] \n",ifreq->ifr_name); + + int j=number-i; + ifn[j].if_name=(char *)malloc(sizeof(char)*10); + strcpy(ifn[j].if_name,ifreq->ifr_name); + // ifn[j].if_name=ifreq->ifr_name; + // printf("if_name[%d]:%s\n",j,if_name[j]); + + ifreq++; + } + ifn[number].if_name=NULL; + + + return ifn; +} + +//获取所有网络连接 +conlist *kylin_network_get_conlist_info() +{ + int status = system("nmcli connection show > /tmp/conlist.txt"); + if (status != 0){ syslog(LOG_ERR, "execute 'nmcli connection show' in function 'kylin_network_get_conlist_info' failed");} + char *filename="/tmp/conlist.txt"; + + FILE *confp; + int connum=0; + char ConStrLine[1024]; + if((confp=fopen(filename,"r"))==NULL) + { + printf("error!"); + + } + fgets(ConStrLine,1024,confp); + while(!feof(confp)) + { + fgets(ConStrLine,1024,confp); + connum++; + } + // printf("%d\n",connum); + fclose(confp); + conlist *clist=(conlist *)malloc(sizeof(conlist)*connum); + + int count=0; + FILE *fp; + char StrLine[1024]; + if((fp=fopen(filename,"r"))==NULL) + { + printf("error!"); + + } + fgets(StrLine,1024,fp); + while(!feof(fp)) + { + if(count==connum-1)break; + + fgets(StrLine,1024,fp); + + char *index=StrLine; + char conname[100]; + + //截取连接名称 + int num=0; + for(index;*index!='\n';index++) + { + if(*index==' ') + { + if(*(index+1)==' ') + break; + } + num++; + } + + // printf("连接名称长度:%d\n",num); + clist[count].con_name=(char *)malloc(sizeof(char)*(num+1)); + strncpy(conname,StrLine,num+1); + conname[num]='\0'; + strncpy(clist[count].con_name,conname,num+1); + // printf("%s\n",clist[count].con_name); + + //截取连接类型 + char type[100]; + for(index;*index!='\n';index++) + { + if(*index==' ') + { + if(*(index+1)=' ') + if(*(index+2)!=' ') + break; + + } + } + char *index1=index+2; + for(index1;*index1!='\n';index1++) + { + if(*index1==' ') + { + if(*(index1+1)==' ') + break; + } + } + int num1=0; + char *index2=index1+2; + for(index2;*index2!='\n';index2++) + { + if(*index2==' ')break; + num1++; + } + clist[count].type=(char *)malloc(sizeof(char)*(num1+1)); + strncpy(type,index1+2,num1+1); + type[num1]='\0'; + strncpy(clist[count].type,type,num1+1); + // printf("%s\n",clist[count].type); + count++; + } + fclose(fp); + + clist[count].con_name=NULL; + clist[count].type=NULL; + + return clist; +} + +//获取当前活动网络连接 +activecon *kylin_network_get_activecon_info() +{ + struct passwd *pwd; + pwd = getpwuid(getuid()); + char *name = pwd->pw_name; + char *tmpPrefix = "/tmp/kylin-nm-activecon-"; + char *chr = "nmcli connection show -active > "; + + char *cmd; + asprintf(&cmd, "%s%s%s", chr, tmpPrefix, name); + char *path; + asprintf(&path, "%s%s", tmpPrefix, name); + int status = system(cmd); + //int status = system("nmcli connection show -active > /tmp/activecon.txt"); + if (status != 0) + syslog(LOG_ERR, "execute 'nmcli connection show -active' in function 'kylin_network_get_activecon_info' failed"); + free(cmd); + + char *filename = path; + + FILE *activefp; + int activenum=0; + char activeStrLine[1024]; + if((activefp=fopen(filename,"r"))==NULL) + { + printf("error!"); + } + fgets(activeStrLine,1024,activefp); + while(!feof(activefp)) + { + fgets(activeStrLine,1024,activefp); + activenum++; + } + // printf("%d\n",activenum); + fclose(activefp); + activecon *activelist=(activecon *)malloc(sizeof(activecon)*activenum); + + int count=0; + FILE *fp; + char StrLine[1024]; + if((fp=fopen(filename,"r"))==NULL) + { + printf("error!"); + + } + free(path); + + fgets(StrLine,1024,fp); + while(!feof(fp)) + { + if(count==activenum-1)break; + + fgets(StrLine,1024,fp); + + char *index=StrLine; + char conname[100]; + + //截取连接名称 + int num=0; + for(index;*index!='\n';index++) + { + if(*index==' ') + { + if(*(index+1)==' ') + break; + } + num++; + } + + // printf("连接名称长度:%d\n",num); + activelist[count].con_name=(char *)malloc(sizeof(char)*(num+1)); + strncpy(conname,StrLine,num+1); + conname[num]='\0'; + strncpy(activelist[count].con_name,conname,num+1); + // printf("%s\n",activelist[count].con_name); + + //截取连接类型 + char type[100]; + for(index;*index!='\n';index++) + { + if(*index==' ') + { + if(*(index+1)=' ') + if(*(index+2)!=' ') + break; + + } + } + char *index1=index+2; + for(index1;*index1!='\n';index1++) + { + if(*index1==' ') + { + if(*(index1+1)==' ') + break; + } + } + int num1=0; + char *index2=index1+2; + for(index2;*index2!='\n';index2++) + { + if(*index2==' ')break; + num1++; + } + activelist[count].type=(char *)malloc(sizeof(char)*(num1+1)); + strncpy(type,index1+2,num1+1); + type[num1]='\0'; + strncpy(activelist[count].type,type,num1+1); + // printf("%s\n",activelist[count].type); + + //截取连接所属设备 + char *index3=index2; + char dev[100]; + for(index3;*index3!='\n';index3++) + { + if(*index3==' ') + { + if(*(index3+1)!=' ') + break; + } + } + int num2=0; + char *index4=index3+1; + for(index4;*index4!='\n';index4++) + { + if(*index4==' ')break; + num2++; + } + activelist[count].dev=(char *)malloc(sizeof(char)*(num2+1)); + strncpy(dev,index3+1,num2+1); + dev[num2]='\0'; + strncpy(activelist[count].dev,dev,num2+1); + // printf("%s\n",activelist[count].dev); + count++; + } + fclose(fp); + + activelist[count].con_name=NULL; + activelist[count].type=NULL; + activelist[count].dev=NULL; + + return activelist; +} + +//创建新的以太网连接 +void kylin_network_create_new_ethernet(char *con_name,char *if_name) +{ + char str[100]; + char *net_type="ethernet"; + sprintf(str,"nmcli connection add con-name \"%s\" ifname %s type %s",con_name,if_name,net_type); + int status = system(str); + if (status != 0){ syslog(LOG_ERR, "execute 'nmcli connection add con-name' in function 'kylin_network_create_new_ethernet' failed");} +} + +// 创建新的wifi连接配置 +void kylin_network_create_new_wifi(char *con_name, char *if_name) +{ + char str[200]; + sprintf(str, "nmcli connection add con-name \"%s\" ifname \"%s\" type wifi ssid \"%s\"", + con_name, if_name, con_name); + int status = system(str); + if (status != 0){ syslog(LOG_ERR, "execute 'nmcli connection add con-name' in function 'kylin_network_create_new_wifi' failed");} +} + +//删除以太网连接 +void kylin_network_del_ethernet_con(char *con_name) +{ + char str[100]; + sprintf(str,"nmcli connection delete \"%s\"",con_name); + int status = system(str); + if (status != 0){ syslog(LOG_ERR, "execute 'nmcli connection delete' in function 'kylin_network_del_ethernet_con' failed");} +} + +//设置动态分配ip +void kylin_network_set_automethod(char *con_name) +{ + char str[100]; + char *automethod="auto"; + sprintf(str,"nmcli connection modify \"%s\" ipv4.method %s ipv4.address '' ipv4.gateway ''",con_name,automethod); + int status = system(str); + if (status != 0){ syslog(LOG_ERR, "execute 'nmcli connection modify' in function 'kylin_network_set_automethod' failed");} +} + +//设置手动分配ip +void kylin_network_set_manualmethod(char *con_name,char *ip) +{ + char str[100]; + char *method="manual"; + sprintf(str,"nmcli connection modify \"%s\" ipv4.method %s ipv4.address %s",con_name,method,ip); + int status = system(str); + if (status != 0){ syslog(LOG_ERR, "execute 'nmcli connection modify' in function 'kylin_network_set_manualmethod' failed");} +} + +// 设置手动分配all +void kylin_network_set_manualall(char *con_name, char *addr, char *mask, char *gateway, char *dns){ + char str[200]; + sprintf(str, "nmcli connection modify \"%s\" ipv4.method manual ipv4.address %s/%s ipv4.gateway %s ipv4.dns %s", + con_name, addr, mask, gateway, dns); + int status = system(str); + if (status != 0){ syslog(LOG_ERR, "execute 'nmcli connection modify' in function 'kylin_network_set_manualall' failed");} +} + +//设置是否自动连接 +void kylin_network_set_autoconnect(char *con_name,bool autocon) +{ + char str[200]; + if(autocon==false) + { + char *ac="no"; + sprintf(str,"export LANG='en_US.UTF-8';export LANGUAGE='en_US';nmcli connection modify \"%s\" connection.autoconnect %s",con_name,ac); + } + else{ + char *ac="yes"; + sprintf(str,"export LANG='en_US.UTF-8';export LANGUAGE='en_US';nmcli connection modify \"%s\" connection.autoconnect %s",con_name,ac); + } + int status = system(str); + if (status != 0){ syslog(LOG_ERR, "execute 'nmcli connection modify' in function 'kylin_network_set_autoconnect' failed");} +} + +//修改ip +void kylin_network_mod_ip(char *con_name,char *ip) +{ + char str[100]; + sprintf(str,"nmcli connection modify \"%s\" ipv4.address %s",con_name,ip); + int status = system(str); + if (status != 0){ syslog(LOG_ERR, "execute 'nmcli connection modify' in function 'kylin_network_mod_ip' failed");} +} + +//修改网关 +void kylin_network_mod_gateway(char *con_name,char *gw) +{ + char str[100]; + sprintf(str,"nmcli connection modify \"%s\" ipv4.gateway %s",con_name,gw); + int status = system(str); + if (status != 0){ syslog(LOG_ERR, "execute 'nmcli connection modify' in function 'kylin_network_mod_gateway' failed");} +} + +//修改dns +void kylin_network_mod_dns(char *con_name,char *dns) +{ + char str[100]; + sprintf(str,"nmcli connection modify \"%s\" ipv4.dns %s",con_name,dns); + int status = system(str); + if (status != 0){ syslog(LOG_ERR, "execute 'nmcli connection modify' in function 'kylin_network_mod_dns' failed");} +} + +//连接以太网 +void kylin_network_set_con_up(char *con_name) +{ + char str[100]; + sprintf(str,"nmcli connection up \"%s\"",con_name); + int status = system(str); + if (status != 0){ syslog(LOG_ERR, "execute 'nmcli connection up' in function 'kylin_network_set_con_up' failed");} +} + +//断开以太网 +void kylin_network_set_con_down(char *con_name) +{ + char str[100]; + sprintf(str,"nmcli connection down \"%s\"",con_name); + int status = system(str); + if (status != 0){ syslog(LOG_ERR, "execute 'nmcli connection down' in function 'kylin_network_set_con_down' failed");} +} + +//连接wifi +void kylin_network_set_wifi_up(char *con_name,char *passwd) +{ + char str[100]; + sprintf(str,"export LANG='en_US.UTF-8';export LANGUAGE='en_US';nmcli device wifi connect \"%s\" password \"%s\"", con_name,passwd); + int status = system(str); + if (status != 0){ syslog(LOG_ERR, "execute 'nmcli device wifi connect' in function 'kylin_network_set_wifi_up' failed");} +} + +//断开wifi连接 +void kylin_network_set_wifi_down(char *if_name) +{ + char str[100]; + sprintf(str,"nmcli device disconnect \"%s\"",if_name); + int status = system(str); + if (status != 0){ syslog(LOG_ERR, "execute 'nmcli device disconnect' in function 'kylin_network_set_wifi_down' failed");} +} + +//获取wifi列表信息 +wifilist *kylin_network_get_wifilist_info() +{ + int status = system("nmcli device wifi > /tmp/wflist.txt"); + if (status != 0){ syslog(LOG_ERR, "execute 'nmcli device wifi' in function 'kylin_network_get_wifilist_info' failed");} + char *filename="/tmp/wflist.txt"; + + FILE *wffp; + int wfnum=0; + char WfStrLine[1024]; + if((wffp=fopen(filename,"r"))==NULL) + { + printf("error!"); + + } + fgets(WfStrLine,1024,wffp); + while(!feof(wffp)) + { + fgets(WfStrLine,1024,wffp); + wfnum++; + } + // printf("wifi数量:%d\n",wfnum); + fclose(wffp); + //wifi实际数量是wfnum-1 + wifilist *wflist=(wifilist *)malloc(sizeof(wifilist)*wfnum); + + int count=0; + FILE *fp; + char StrLine[1024]; + if((fp=fopen(filename,"r"))==NULL) + { + printf("error!"); + + } + fgets(StrLine,1024,fp); + while(!feof(fp)) + { + if(count==wfnum-1)break; + + fgets(StrLine,1024,fp); + + // printf("%s\n",StrLine+3); + char *index=StrLine+3; + + //截取ssid + char *str1="Infra"; + int num=0; + for(index;strcmp(index+5,"\n")!=0;index++) + { + int result=strncmp(index,str1,5); + if(result==0) + { + // printf("%s\n",index); + break; + + } + num++; + + } + char ssid[100]; + char *ssidindex=index-1; + int ssidnum=0; + for(ssidindex;*ssidindex==' ';ssidindex--)ssidnum++; + // printf("空格数量:%d\n",ssidnum); + // if(ssidnum==1) + strncpy(ssid,StrLine+3,num-1); + ssid[num-ssidnum]='\0'; + // printf("-6666--%s---\n",ssid); + + wflist[count].ssid=(char *)malloc(sizeof(char)*(num-ssidnum)); + strncpy(wflist[count].ssid,ssid,num-ssidnum+1); + // printf("第%d个:%s ",count,wflist[count].ssid); + + + //截取信号强度 + char *str2="Mbit/s"; + for(index;strcmp(index+6,"\n")!=0;index++) + { + int result=strncmp(index,str2,6); + if(result==0) + { + // printf("%s\n",index); + break; + } + } + + char signal[10]; + char *signalindex=index+8; + int signalnum=0; + for(signalindex;*signalindex!=' ';signalindex++)signalnum++; + strncpy(signal,index+8,signalnum); + signal[signalnum]='\0'; + // printf("-7777--%s---\n",signal); + + wflist[count].signal=atoi(signal); + // printf("%d ",wflist[count].signal); + + //截取安全性 + char *str3="WPA"; + for(index;strcmp(index+3,"\n")!=0;index++) + { + int result=strncmp(index,str3,3); + if(result==0) + { + // printf("%s\n",index); + break; + } + } + + char safety[20]; + char *safetyindex=index; + int safetynum=0; + for(safetyindex;strcmp(safetyindex+2,"\n")!=0;safetyindex++) + { + int result=strncmp(safetyindex," ",2); + if(result==0)break; + safetynum++; + } + strncpy(safety,index,safetynum+1); + safety[safetynum+1]='\0'; + // printf("-8888--%s---\n",safety); + + wflist[count].safety=(char *)malloc(sizeof(char)*(safetynum+1)); + strncpy(wflist[count].safety,safety,safetynum+2); + // printf("%s\n",wflist[count].safety); + + count++; + + } + + fclose(fp); + + wflist[count].ssid=NULL; + wflist[count].signal=0; + wflist[count].safety=NULL; + + return wflist; +} + +//启用联网 +void kylin_network_enable_networking() +{ + int status = system("nmcli networking on"); + if (status != 0){ syslog(LOG_ERR, "execute 'nmcli networking on' in function 'kylin_network_enable_networking' failed");} +} + +//禁用联网 +void kylin_network_disable_networking() +{ + int status = system("nmcli networking off"); + if (status != 0){ syslog(LOG_ERR, "execute 'nmcli networking off' in function 'kylin_network_disable_networking' failed");} +} + +//启用wifi +void kylin_network_enable_wifi() +{ + int status = system("nmcli radio wifi on;sleep 3"); + if (status != 0){ syslog(LOG_ERR, "execute 'nmcli radio wifi on' in function 'kylin_network_enable_wifi' failed");} +} + +//禁用wifi +void kylin_network_disable_wifi() +{ + int status = system("nmcli radio wifi off;sleep 2"); + if (status != 0){ syslog(LOG_ERR, "execute 'nmcli radio wifi off' in function 'kylin_network_disable_wifi' failed");} +} + +//获取ip地址 +int kylin_network_get_ipaddr(char *if_name,char *ipaddr) +{ + int sock_ip; + struct sockaddr_in sin_ip; + struct ifreq ifr_ip; + if((sock_ip=socket(AF_INET,SOCK_STREAM,0))<0) + { + perror("sockket error"); + return -1; + } + + strcpy(ifr_ip.ifr_name,if_name); + + if(ioctl(sock_ip,SIOCGIFADDR,&ifr_ip)<0)//直接获取ip地址 + { + perror("Not setup interface\n"); + return -1; + } + + memcpy(&sin_ip,&ifr_ip.ifr_addr,sizeof(sin_ip)); + strcpy(ipaddr,inet_ntoa(sin_ip.sin_addr));//#include + close(sock_ip); + return 0; + +} + +//获取广播地址 +int kylin_network_get_brdaddr(char *if_name,char *brdaddr) +{ + int sock_brdaddr; + struct sockaddr_in sin_brd; + struct ifreq ifr_brd; + if((sock_brdaddr=socket(AF_INET,SOCK_STREAM,0))<0) + { + perror("sockket error"); + return -1; + } + + strcpy(ifr_brd.ifr_name,if_name); + + if(ioctl(sock_brdaddr,SIOCGIFBRDADDR,&ifr_brd)<0)//直接获取广播地址 + { + perror("Not setup interface\n"); + return -1; + } + + memcpy(&sin_brd,&ifr_brd.ifr_broadaddr,sizeof(sin_brd)); + strcpy(brdaddr,inet_ntoa(sin_brd.sin_addr)); + close(sock_brdaddr); + return 0; + +} + +//获取子网掩码 +int kylin_network_get_netmask(char *if_name,char *netmask) +{ + int sock_mask; + struct sockaddr_in sin_netmask; + struct ifreq ifr_mask; + if((sock_mask=socket(AF_INET,SOCK_STREAM,0))<0) + { + perror("sockket error"); + return -1; + } + + strcpy(ifr_mask.ifr_name,if_name); + + if(ioctl(sock_mask,SIOCGIFNETMASK,&ifr_mask)<0)//直接获取子网掩码 + { + perror("Not setup interface\n"); + return -1; + } + + memcpy(&sin_netmask,&ifr_mask.ifr_netmask,sizeof(sin_netmask)); + strcpy(netmask,inet_ntoa(sin_netmask.sin_addr)); + close(sock_mask); + return 0; + +} + +//获取MAC +int kylin_network_get_mac(char *if_name,char *macaddr) +{ + int sock_mac; + struct ifreq ifr_mac; + struct sockaddr_in sin_mac; + struct ether_addr ethaddr;//#include + + sock_mac=socket(AF_INET,SOCK_STREAM,0); + + strcpy(ifr_mac.ifr_name,if_name); + + if(-1==ioctl(sock_mac,SIOCGIFHWADDR,&ifr_mac)) + { + perror("Not setup interface\n"); + return -1; + } + memcpy(ðaddr,&ifr_mac.ifr_hwaddr.sa_data,sizeof(ethaddr)); + strcpy(macaddr,ether_ntoa(ðaddr));//#include + + // strcpy(macaddr,ether_ntoa((struct ether_addr*)ifr_mac.ifr_hwaddr.sa_data)); + + close(sock_mac); + return 0; + +} + +//获取MTU +int kylin_network_get_mtu(char *if_name) +{ + int sock_mtu; +// struct sockaddr_in sin_ip; + struct ifreq ifr_MTU; + if((sock_mtu=socket(AF_INET,SOCK_STREAM,0))<0) + { + perror("sockket error"); + return -1; + } + + strcpy(ifr_MTU.ifr_name,if_name); + + if(ioctl(sock_mtu,SIOCGIFMTU,&ifr_MTU)) + { + perror("Not setup interface\n"); + return -1; + } + + int mtu=ifr_MTU.ifr_mtu; + // printf("%d\n",ifr_MTU.ifr_mtu); + // printf("%d\n",mtu); + + close(sock_mtu); + return mtu; + +} + +static void getBuf(char *bufstr) +{ + //O_RDONLY以只读方式打开文件 + int fd=open("/proc/net/dev", O_RDONLY); + if(-1==fd) + { + printf("/proc/net/dev not exists!\n"); + } + + char buf[1024*2]; + //将读写位置移到文件开头 + lseek(fd,0,SEEK_SET); + int nBytes=read(fd,buf,sizeof(buf)-1); + if(-1==nBytes) + { + perror("read error"); + close(fd); + } + + buf[nBytes]='\0'; + strcpy(bufstr,buf); +} + +//总计上传下载数据量 +long *kylin_network_get_bytes(char *if_name) +{ + char buf[1024*2]; + getBuf(buf); + + //返回第一次指向if_name位置的指针 + char *pDev=strstr(buf,if_name); + if(NULL==pDev) + { + printf("don't find dev %s\n",if_name); + return NULL; + } + + char *p; + char *value; + int i=0; + static long rtbyt[2]; + /*去除空格,制表符,换行符等不需要的字段*/ + for (p = strtok(pDev, " \t\r\n"); p; p = strtok(NULL, " \t\r\n")) + { + i++; + value = (char*)malloc(20); + strcpy(value, p); + /*得到的字符串中的第二个字段是接收流量*/ + if(i == 2) + { + rtbyt[0] = atol(value); + } + /*得到的字符串中的第十个字段是发送流量*/ + if(i == 10) + { + rtbyt[1] = atol(value); + break; + } + free(value); + } + return rtbyt; +} + +//总计上传下载数据包 +long *kylin_network_get_packets(char *if_name) +{ + char buf[1024*2]; + getBuf(buf); + + //返回第一次指向if_name位置的指针 + char *pDev=strstr(buf,if_name); + if(NULL==pDev) + { + printf("don't find dev %s\n",if_name); + return NULL; + } + + char *p; + char *value; + int i=0; + static long rtpkt[2]; + /*去除空格,制表符,换行符等不需要的字段*/ + for (p = strtok(pDev, " \t\r\n"); p; p = strtok(NULL, " \t\r\n")) + { + i++; + value = (char*)malloc(20); + strcpy(value, p); + /*得到的字符串中的第三个字段是接收流量*/ + if(i == 3) + { + rtpkt[0] = atol(value); + } + /*得到的字符串中的第十一个字段是发送流量*/ + if(i == 11) + { + rtpkt[1] = atol(value); + break; + } + free(value); + } + return rtpkt; +} + +//总计上传下载错误数据包数量 +long *kylin_network_get_errs(char *if_name) +{ + char buf[1024*2]; + getBuf(buf); + + //返回第一次指向if_name位置的指针 + char *pDev=strstr(buf,if_name); + if(NULL==pDev) + { + printf("don't find dev %s\n",if_name); + return NULL; + } + + char *p; + char *value; + int i=0; + static long rterrs[2]; + /*去除空格,制表符,换行符等不需要的字段*/ + for (p = strtok(pDev, " \t\r\n"); p; p = strtok(NULL, " \t\r\n")) + { + i++; + value = (char*)malloc(20); + strcpy(value, p); + /*得到的字符串中的第四个字段是接收流量*/ + if(i == 4) + { + rterrs[0] = atol(value); + } + /*得到的字符串中的第十二个字段是发送流量*/ + if(i == 12) + { + rterrs[1] = atol(value); + break; + } + free(value); + } + return rterrs; +} + +//总计上传下载丢弃数据包数量 +long *kylin_network_get_drop(char *if_name) +{ + char buf[1024*2]; + getBuf(buf); + + //返回第一次指向if_name位置的指针 + char *pDev=strstr(buf,if_name); + if(NULL==pDev) + { + printf("don't find dev %s\n",if_name); + return NULL; + } + + char *p; + char *value; + int i=0; + static long rtdrop[2]; + /*去除空格,制表符,换行符等不需要的字段*/ + for (p = strtok(pDev, " \t\r\n"); p; p = strtok(NULL, " \t\r\n")) + { + i++; + value = (char*)malloc(20); + strcpy(value, p); + /*得到的字符串中的第五个字段是接收流量*/ + if(i == 5) + { + rtdrop[0] = atol(value); + } + /*得到的字符串中的第十三个字段是发送流量*/ + if(i == 13) + { + rtdrop[1] = atol(value); + break; + } + free(value); + } + return rtdrop; +} + +//总计上传下载过载数据包数量 +long *kylin_network_get_fifo(char *if_name) +{ + char buf[1024*2]; + getBuf(buf); + + //返回第一次指向if_name位置的指针 + char *pDev=strstr(buf,if_name); + if(NULL==pDev) + { + printf("don't find dev %s\n",if_name); + return NULL; + } + + char *p; + char *value; + int i=0; + static long rtfifo[2]; + /*去除空格,制表符,换行符等不需要的字段*/ + for (p = strtok(pDev, " \t\r\n"); p; p = strtok(NULL, " \t\r\n")) + { + i++; + value = (char*)malloc(20); + strcpy(value, p); + /*得到的字符串中的第六个字段是接收流量*/ + if(i == 6) + { + rtfifo[0] = atol(value); + } + /*得到的字符串中的第十四个字段是发送流量*/ + if(i == 14) + { + rtfifo[1] = atol(value); + break; + } + free(value); + } + return rtfifo; +} diff --git a/KylinNM/src/kylin-network-interface.h b/KylinNM/src/kylin-network-interface.h new file mode 100644 index 0000000..1b44dab --- /dev/null +++ b/KylinNM/src/kylin-network-interface.h @@ -0,0 +1,267 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C"{ +#endif + +typedef struct +{ + char *if_name; +}ifname; + +typedef struct +{ + char *ssid;//wifi名称 + int signal;//信号强度 + char *safety;//安全性 + +}wifilist;//存放wifi信息的结构体 + +typedef struct +{ + char *con_name;//网络连接名称 + char *type;//网络连接类型 +}conlist;//存放所有网络连接 + +typedef struct +{ + char *con_name;//活动网络连接名称 + char *type;//活动网络连接类型 + char *dev;//活动网络所属设备 +}activecon;//存放当前活动网络连接 + +/* + * Get the network interface name. + * return the struct pointer. + */ +ifname *kylin_network_get_ifacename(); + +/* + * Get the all network connection. + * return the struct pointer. + */ +conlist *kylin_network_get_conlist_info(); + +/* + * Get the active network connection. + * return the struct pointer. + */ +activecon *kylin_network_get_activecon_info(); + +/* + * Create a new Ethernet connection. + * @con_name is the connection name. + * @if_name is the interface name. + */ +void kylin_network_create_new_ethernet(char *con_name,char *if_name); + +/* + * Create a new Wifi connection. + * @con_name is the connection name. + * @if_name is the interface name. + */ +void kylin_network_create_new_wifi(char *con_name, char *if_name); + +/* + * Delete ethernet connection. + * @con_name is the connection name. + */ +void kylin_network_del_ethernet_con(char *con_name); + +/* + * Set up dynamic IP allocation. + * @con_name is the connection name. + */ +void kylin_network_set_automethod(char *con_name); + +/* + * Set up manual IP assignment. + * @con_name is the connection name. + * @ip is the ip address(for example,"192.168.68.160/16") + */ +void kylin_network_set_manualmethod(char *con_name,char *ip); + +/* + * Set up manual all prop. + */ +void kylin_network_set_manualall(char *con_name, char *addr, char *mask, char *gateway, char *dns); + +/* + * Sets whether the connection is automatic. + * @con_name is the connection name. + * @autocon is the automatic connection option. + */ +void kylin_network_set_autoconnect(char *con_name,bool autocon); + +/* + * Modify the ip address. + * @con_name is the connection name. + * @ip is the ip address(for example,"192.168.68.160/16") + */ +void kylin_network_mod_ip(char *con_name,char *ip); + +/* + * Modify the gateway. + * @con_name is the connection name. + * @gw is the gateway address. + */ +void kylin_network_mod_gateway(char *con_name,char *gw); + +/* + * Modify the dns address. + * @con_name is the connection name. + * @dns is the dns address. + */ +void kylin_network_mod_dns(char *con_name,char *dns); + +/* + * Connect the ethernet. + * @con_name is the connection name. + */ +void kylin_network_set_con_up(char *con_name); + +/* + * Disconnect the ethernet. + * @con_name is the connection name. + */ +void kylin_network_set_con_down(char *con_name); + +/* + * Connect the wifi. + * @con_name is the wifi name. + * @passwd is the wifi password. + */ +void kylin_network_set_wifi_up(char *con_name,char *passwd); + +/* + * Disconnect the wifi. + * @if_name is the network interface name. + */ +void kylin_network_set_wifi_down(char *if_name); + +/* + * Get wifi list information. + * Return the struct pointer. + */ +wifilist *kylin_network_get_wifilist_info(); + +/* + * Enable networking. + */ +void kylin_network_enable_networking(); + +/* + * Disable networking. + */ +void kylin_network_disable_networking(); + +/* + * Enable wifi. + */ +void kylin_network_enable_wifi(); + +/* + * Disable wifi. + */ +void kylin_network_disable_wifi(); + +/* Get the ip address. + * @if_name is the interface name. + * @ipaddr is used to save the ip address. + */ +int kylin_network_get_ipaddr(char *if_name,char *ipaddr); + +/* + * Get the broadcast address. + * @if_name is the interface name. + * @brdaddr is used to save the broadcast address. + */ +int kylin_network_get_brdaddr(char *if_name,char *brdaddr); + +/* + * Get the subnet mask. + * @if_name is the interface name. + * @netmask is used to save the subnet mask. + */ +int kylin_network_get_netmask(char *if_name,char *netmask); + +/* + * Get MAC address. + * @if_name is the interface name. + * @macaddr is used to save the MAC address. + */ +int kylin_network_get_mac(char *if_name,char *macaddr); + +/* Get the MTU. + * @if_name is the interface name. + * return the MTU value. + */ +int kylin_network_get_mtu(char *if_name); + +/* + * Total upload and download data volume. + * @if_name is the network interface name. + * return the address of the first element of a one-dimensional long integer array. + */ +long *kylin_network_get_bytes(char *if_name); + +/* + * Total upload and download data packets. + * @if_name is the network interface name. + * return the address of the first element of a one-dimensional long integer array. + */ +long *kylin_network_get_packets(char *if_name); + +/* + * Total wrong data packets number of uploading and downloading. + * @if_name is the network interface name. + * return the address of the first element of a one-dimensional long integer array. + */ +long *kylin_network_get_errs(char *if_name); + +/* + * Total discarded data packets number of uploading and downloading. + * @if_name is the network interface name. + * return the address of the first element of a one-dimensional long integer array. + */ +long *kylin_network_get_drop(char *if_name); + +/* + * Total overloaded data packets number of uploading and downloading. + * @if_name is the network interface name. + * return the address of the first element of a one-dimensional long integer array. + */ +long *kylin_network_get_fifo(char *if_name); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/KylinNM/src/kylinnm.cpp b/KylinNM/src/kylinnm.cpp new file mode 100644 index 0000000..3446f2e --- /dev/null +++ b/KylinNM/src/kylinnm.cpp @@ -0,0 +1,2801 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include +#include +#include + +#include "kylinnm.h" +#include "ui_kylinnm.h" +#include "swipegesturerecognizer.h" + +QString llname, lwname, hideWiFiConn; +int currentActWifiSignalLv, count_loop; + +bool KylinNM::m_is_reflashWifiUi = false; +QMutex mutexIsReflashWifi; + +KylinNM::KylinNM(QWidget *parent) : + QWidget(parent), + ui(new Ui::KylinNM) +{ + ui->setupUi(this); + qDebug() << "99999999999999999999999999--------------9"; + //checkSingle(); + + syslog(LOG_DEBUG, "Using the icon theme named 'ukui-icon-theme-default'"); + //QIcon::setThemeName("ukui-icon-theme-default"); + + // 如果使用Qt::Popup 任务栏不显示且保留X事件如XCB_FOCUS_OUT, 但如果indicator点击鼠标右键触发,XCB_FOCUS_OUT事件依然会失效 + // 如果使用Qt::ToolTip, Qt::Tool + Qt::WindowStaysOnTopHint, Qt::X11BypassWindowManagerHint等flag则会导致X事件失效 + // this->setWindowFlags(Qt::FramelessWindowHint | Qt::Tool | Qt::WindowStaysOnTopHint); + this->setWindowFlags(Qt::FramelessWindowHint);//QTool + this->setAttribute(Qt::WA_TranslucentBackground);//设置窗口背景透明 + + //UseQssFile::setStyle("style.qss"); + + QPainterPath path; + auto rect = this->rect(); + rect.adjust(1, 1, -1, -1); + path.addRoundedRect(rect, 6, 6); + setProperty("blurRegion", QRegion(path.toFillPolygon().toPolygon())); + + this->setStyleSheet("#centralWidget{border:none;border-radius:16px;}"); + + ui->centralWidget->setStyleSheet("#centralWidget{border:none;border-radius:16px;background:rgba(255,255,255,1);}"); + + editQssString(); //编辑部分控件QSS + createTopLanUI(); //创建顶部有线网item + createTopWifiUI(); //创建顶部无线网item + createOtherUI(); //创建上传下载控件,列表区无item时的说明控件 + createListAreaUI(); //创建列表区域的控件 + createLeftAreaUI(); //创建左侧区域控件 + + lname = "-1"; + wname = "-1"; + llname = "-1"; + lwname = "-1"; + hideWiFiConn = "Connect to Hidden Wi-Fi Network"; + currentActWifiSignalLv = -1; + count_loop = 0; + + createTrayIcon(); + connect(trayIcon, &QSystemTrayIcon::activated, this, &KylinNM::iconActivated); + connect(mShowWindow,SIGNAL(triggered()),this,SLOT(on_showWindowAction())); + connect(mAdvConf,SIGNAL(triggered()),this,SLOT(on_btnAdvConf_clicked())); + trayIcon->show(); + + objKyDBus = new KylinDBus(this); + objKyDBus->initBtnWifiGsetting(); + objKyDBus->initConnectionInfo(); + + objKyDBus->initTaskbarGsetting(); +// objKyDBus->setWifiSignal(-1, ""); + + objNetSpeed = new NetworkSpeed(); + + this->confForm = new ConfForm(this->parentWidget()); + this->confForm->setMainWindow(this); + this->confForm->hide(); + + this->ksnm = new KSimpleNM(); + connect(ksnm, SIGNAL(getLanListFinished(QStringList)), this, SLOT(getLanListDone(QStringList))); + connect(ksnm, SIGNAL(getWifiListFinished(QStringList)), this, SLOT(getWifiListDone(QStringList))); + + loading = new LoadingDiv(this); + connect(loading, SIGNAL(toStopLoading() ), this, SLOT(on_checkOverTime() )); + + checkIsWirelessDeviceOn(); //检测无线网卡是否插入 + getInitLanSlist(); //初始化有线网列表 + initNetwork(); //初始化网络 + initTimer(); //初始化定时器 + + connect(ui->btnNetList, &QPushButton::clicked, this, &KylinNM::onBtnNetListClicked); + connect(btnWireless, &SwitchButton::clicked,this, &KylinNM::onBtnWifiClicked); + + //auto app = static_cast(QCoreApplication::instance()); + //app->setStyle(new CustomStyle()); //设置自定义主题 + + ui->btnNetList->setAttribute(Qt::WA_Hover,true); + ui->btnNetList->installEventFilter(this); + ui->btnWifiList->setAttribute(Qt::WA_Hover,true); + ui->btnWifiList->installEventFilter(this); + + SwipeGestureRecognizer *fftRecognizer = new SwipeGestureRecognizer(this); + Qt::GestureType fftType = QGestureRecognizer::registerRecognizer(fftRecognizer); +// grabGesture(fftType); + connect(fftRecognizer, &SwipeGestureRecognizer::onSwipeGesture, this, &KylinNM::onSwipeGesture); +} + +KylinNM::~KylinNM() +{ + trayIcon->deleteLater(); + trayIconMenu->deleteLater(); + delete ui; +} + +void KylinNM::onSwipeGesture(int dx, int dy) +{ + // qDebug() << "info: [KylinNM] onSwipeGesture dy=" << dy; + if(scrollAreal->isVisible()) { + // qDebug() << "info: [KylinNM] onSwipeGesture before moved " << scrollAreal->verticalScrollBar()->value(); + scrollAreal->verticalScrollBar()->setValue(scrollAreal->verticalScrollBar()->value() - dy/5); + // qDebug() << "info: [KylinNM] onSwipeGesture after moved " << scrollAreal->verticalScrollBar()->value(); + } else if (scrollAreaw->isVisible()) { + // qDebug() << "info: [KylinNM] onSwipeGesture before moved " << scrollAreaw->verticalScrollBar()->value(); + scrollAreaw->verticalScrollBar()->setValue(scrollAreaw->verticalScrollBar()->value() - dy/5); + // qDebug() << "info: [KylinNM] onSwipeGesture after moved " << scrollAreaw->verticalScrollBar()->value(); + } +} + +void KylinNM::checkSingle() +{ + int fd = 0; + try { + QStringList homePath = QStandardPaths::standardLocations(QStandardPaths::HomeLocation); + QString lockPath = homePath.at(0) + "/.config/kylin-nm-lock"; + fd = open(lockPath.toUtf8().data(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + + if (fd < 0) { + throw -1; + } + } catch(...) { + fd = open("/tmp/kylin-nm-lock", O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (fd < 0) { + exit(0); + } + } + + + if (lockf(fd, F_TLOCK, 0)) { + syslog(LOG_ERR, "Can't lock single file, kylin-network-manager is already running!"); + qDebug()<<"Can't lock single file, kylin-network-manager is already running!"; + exit(0); + } +} + +bool KylinNM::nativeEvent(const QByteArray &eventType, void *message, long *result) +{ + Q_UNUSED(result); + if (eventType != "xcb_generic_event_t") { + return false; + } + + xcb_generic_event_t *event = (xcb_generic_event_t*)message; + + switch (event->response_type & ~0x80) { + case XCB_FOCUS_OUT: + //this->hide(); + break; + } + + return false; +} + +bool KylinNM::eventFilter(QObject *obj, QEvent *event) +{ + if (obj == ui->btnNetList) { + if (event->type() == QEvent::HoverEnter) { + if (!is_btnNetList_clicked) { + ui->lbNetListBG->setStyleSheet(btnBgHoverQss); + } + return true; + } else if(event->type() == QEvent::HoverLeave) { + if (!is_btnNetList_clicked) { + ui->lbNetListBG->setStyleSheet(btnBgLeaveQss); + } + return true; + } + } + + if (obj == ui->btnWifiList) { + if (event->type() == QEvent::HoverEnter) { + if (!is_btnWifiList_clicked) { + ui->lbWifiListBG->setStyleSheet(btnBgHoverQss); + } + return true; + } else if(event->type() == QEvent::HoverLeave) { + if (!is_btnWifiList_clicked) { + ui->lbWifiListBG->setStyleSheet(btnBgLeaveQss); + } + return true; + } + } + + return QWidget::eventFilter(obj,event); +} + +/////////////////////////////////////////////////////////////////////////////// + +void KylinNM::tabletStyle()//平板桌面模式特有设置 +{ + this->setFixedWidth(446+194*isTabletStyle); + ui->centralWidget->setFixedWidth(446+194*isTabletStyle); + ui->btnNetList->setFixedWidth(223+97*isTabletStyle); + ui->btnWifiList->setFixedWidth(223+97*isTabletStyle); + ui->btnWifiList->move(223+97*isTabletStyle,0); + ui->lbWifiListBG->move(223+97*isTabletStyle,0); + ui->lbNetListImg->move(16+95*isTabletStyle,17); + ui->lbWifiListImg->move(288+144*isTabletStyle,17); + scrollAreal->resize(W_SCROLL_AREA+194*isTabletStyle, H_SCROLL_AREA); + scrollAreaw->resize(W_SCROLL_AREA+194*isTabletStyle, H_SCROLL_AREA); + lbNetListText->move(98+48*isTabletStyle,2); + lbWifiListText->move(98+48*isTabletStyle,2); + confForm->tabletStyle(isTabletStyle); + btnWireless->move(385+194*isTabletStyle,73); + lbNoItemTip->move(this->width()/2 - lbNoItemTip->width()/2, this->height()/2+30); + if(isTabletStyle) + { + btnOffQss = "QLabel{min-width: 320px; min-height: 56px;max-width:320px; max-height: 56px;border-radius: 16px; background-color:rgba(255,255,255,0);}"; + btnOnQss = "QLabel{min-width: 320px; min-height: 56px;max-width:320px; max-height: 56px;border-radius: 16px; background-color:rgba(61,107,229,1);}"; + } + else + { + btnOffQss = "QLabel{min-width: 221px; min-height: 56px;max-width:221px; max-height: 56px;border-radius: 16px; background-color:rgba(255,255,255,0)}"; + btnOnQss = "QLabel{min-width: 221px; min-height: 56px;max-width:221px; max-height: 56px;border-radius: 16px; background-color:rgba(61,107,229,1);}"; + } + if(scrollAreal->isHidden())//首次运行初始化大按钮状态 + { + ui->btnNetList->setStyleSheet(btnOffQss); + ui->btnWifiList->setStyleSheet(btnOnQss); + ui->lbNetListBG->setStyleSheet(btnOffQss); + ui->lbWifiListBG->setStyleSheet(btnOnQss); + ui->lbNetListImg->setStyleSheet("QLabel{border-image:url(:/res/l/pb-network-offline.png);background-position:center;background-repeat:no-repeat;}"); + ui->lbWifiListImg->setStyleSheet("QLabel{border-image:url(:/res/x/pb-wifi-y.png);background-position:center;background-repeat:no-repeat;}"); + lbNetListText->setStyleSheet("QLabel{color:rgba(38, 38, 38, 0.75);background-color:transparent;}"); + lbWifiListText->setStyleSheet("QLabel{color:rgba(47, 179, 232, 1);background-color:transparent;}"); + } +} + +// 初始化控件、网络、定时器 + +// 初始化界面各控件 +void KylinNM::editQssString() +{ + btnOffQss = "QLabel{min-width: 221px; min-height: 56px;max-width:221px; max-height: 56px;border-radius: 16px; background-color:rgba(38,38,38,0.15)}"; + btnOnQss = "QLabel{min-width: 221px; min-height: 56px;max-width:221px; max-height: 56px;border-radius: 16px; background-color:rgba(255,255,255,0.2)}"; + btnBgOffQss = "QLabel{min-width: 48px; min-height: 22px;max-width:48px; max-height: 22px;border-radius: 16px; background-color:rgba(38,38,38,0.15)}"; + btnBgOnQss = "QLabel{min-width: 48px; min-height: 22px;max-width:48px; max-height: 22px;border-radius: 16px; background-color:rgba(255,255,255,0.2);}"; + btnBgHoverQss = "QLabel{border-radius: 16px; background-color:rgba(38,38,38,0.2)}"; + btnBgLeaveQss = "QLabel{border-radius: 16px; background-color:rgba(38,38,38,0.15)}"; + leftBtnQss = "QPushButton{border:0px;border-radius:4px;background-color:rgba(255,255,255,0);}" + "QPushButton:Hover{border:0px solid rgba(255,255,255,0.2);border-radius:4px;background-color:rgba(255,255,255,0.12);}" + "QPushButton:Pressed{border-radius:4px;background-color:rgba(255,255,255,0.12);}"; +// funcBtnQss = "QPushButton{border:0px;border-radius:4px;background-color:rgba(255,255,255,0);color:rgba(107,142,235,0.97);font-size:14px;}" +// "QPushButton:Hover{border:0px;border-radius:4px;background-color:rgba(255,255,255,0);color:rgba(151,175,241,0.97);font-size:14px;}" +// "QPushButton:Pressed{border-radius:4px;background-color:rgba(255,255,255,0);color:rgba(61,107,229,0.97);font-size:14px;}"; + funcBtnQss="QPushButton{border:0px;border-radius:4px;background-color:rgba(255,255,255,0);color:rgba(38,38,38,1);font-size:14px;text-align:left;}"; +} + +void KylinNM::createTopLanUI() +{ + topLanListWidget = new QWidget(ui->centralWidget); + topLanListWidget->move(W_LEFT_AREA, Y_TOP_ITEM); + topLanListWidget->resize(W_TOP_LIST_WIDGET, H_NORMAL_ITEM + H_GAP_UP + X_ITEM); + /*顶部的一个item*/ + lbTopLanList = new QLabel(topLanListWidget); + lbTopLanList->setText(tr("Inactivated LAN"));//"可用网络列表" + lbTopLanList->resize(W_MIDDLE_WORD, H_MIDDLE_WORD); + lbTopLanList->move(X_MIDDLE_WORD, H_NORMAL_ITEM + H_GAP_UP); + lbTopLanList->setStyleSheet("QLabel{font-size:14px;color:rgba(38, 38, 38, 0.45);}"); + lbTopLanList->show(); + /*新建有线网按钮*/ +// btnCreateNet = new QPushButton(ui->centralWidget); +// btnCreateNet->resize(W_BTN_FUN, H_BTN_FUN); +// btnCreateNet->move(X_BTN_FUN, Y_BTN_FUN); +// btnCreateNet->setText(" "+tr("New LAN"));//"新建网络" +// btnCreateNet->setIcon(QIcon(":/res/x/pb-newConn.png")); +// btnCreateNet->setStyleSheet(funcBtnQss); +// btnCreateNet->setFocusPolicy(Qt::NoFocus); +// btnCreateNet->show(); +// connect(btnCreateNet,SIGNAL(clicked()),this,SLOT(onBtnCreateNetClicked())); +} + +void KylinNM::createTopWifiUI() +{ + topWifiListWidget = new QWidget(ui->centralWidget); + topWifiListWidget->move(W_LEFT_AREA, Y_TOP_ITEM); + topWifiListWidget->resize(W_TOP_LIST_WIDGET+194*isTabletStyle, H_NORMAL_ITEM + H_GAP_UP + X_ITEM); + /*顶部的一个item*/ + lbTopWifiList = new QLabel(topWifiListWidget); + lbTopWifiList->setText(tr("Other WLAN"));//"可用网络列表" + lbTopWifiList->resize(W_MIDDLE_WORD, H_MIDDLE_WORD); + lbTopWifiList->move(X_MIDDLE_WORD, H_NORMAL_ITEM + H_GAP_UP); + lbTopWifiList->setStyleSheet("QLabel{font-size:14px;color:rgba(38, 38, 38, 0.45);}"); + lbTopWifiList->show(); + +// /*新建无线网按钮*/ +// if(!btnAddNet){ +// btnAddNet = new QPushButton(ui->centralWidget); +// connect(btnAddNet,SIGNAL(clicked()),this,SLOT(onBtnAddNetClicked())); +// } +// btnAddNet->resize(W_BTN_FUN, H_BTN_FUN); +// btnAddNet->move(X_BTN_FUN, Y_BTN_FUN); +// btnAddNet->setText(" "+tr("Hide WiFi"));//"加入网络" +// btnAddNet->setIcon(QIcon(":/res/x/pb-newConn.png")); +// btnAddNet->setStyleSheet(funcBtnQss); +// btnAddNet->setFocusPolicy(Qt::NoFocus); +} + +void KylinNM::createOtherUI() +{ + lbLoadDown = new QLabel(ui->centralWidget); + lbLoadDown->move(X_ITEM + 129, Y_TOP_ITEM + 32); + lbLoadDown->resize(65, 20); + lbLoadDownImg = new QLabel(ui->centralWidget); + lbLoadDownImg->move(X_ITEM + 112, Y_TOP_ITEM + 35); + lbLoadDownImg->resize(16, 16); + + lbLoadUp = new QLabel(ui->centralWidget); + lbLoadUp->move(X_ITEM + 187, Y_TOP_ITEM + 32); + lbLoadUp->resize(65, 20); + lbLoadUpImg = new QLabel(ui->centralWidget); + lbLoadUpImg->move(X_ITEM + 170, Y_TOP_ITEM + 35); + lbLoadUpImg->resize(16, 16); + +//YYF lbLoadDownImg->setStyleSheet("QLabel{background-image:url(:/res/x/load-down.png);}"); +//YYF lbLoadUpImg->setStyleSheet("QLabel{background-image:url(:/res/x/load-up.png);}"); + + lbNoItemTip = new QLabel(ui->centralWidget); + lbNoItemTip->resize(W_NO_ITEM_TIP, H_NO_ITEM_TIP); + lbNoItemTip->move(this->width()/2 - W_NO_ITEM_TIP/2 + W_LEFT_AREA/2, this->height()/2); + lbNoItemTip->setStyleSheet("QLabel{border:none;background:transparent;font-size:14px;color:rgba(38, 38, 38, 0.45);}"); + lbNoItemTip->setText(tr("No usable network in the list"));//列表暂无可连接网络 + lbNoItemTip->setAlignment(Qt::AlignCenter); + lbNoItemTip->hide(); +} + +void KylinNM::createListAreaUI() +{ + scrollAreal = new QScrollArea(ui->centralWidget); + scrollAreal->move(W_LEFT_AREA, Y_TOP_ITEM + H_NORMAL_ITEM + H_GAP_UP + X_ITEM + H_GAP_DOWN); + scrollAreal->resize(W_SCROLL_AREA, H_SCROLL_AREA); + scrollAreal->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + scrollAreal->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + + scrollAreaw = new QScrollArea(ui->centralWidget); + scrollAreaw->move(W_LEFT_AREA, Y_TOP_ITEM + H_NORMAL_ITEM + H_GAP_UP + X_ITEM + H_GAP_DOWN); + scrollAreaw->resize(W_SCROLL_AREA, H_SCROLL_AREA); + scrollAreaw->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + scrollAreaw->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + + lanListWidget = new QWidget(scrollAreal); + wifiListWidget = new QWidget(scrollAreaw); + lbLanList = new QLabel(lanListWidget); + lbWifiList = new QLabel(wifiListWidget); + + ui->lbNetwork->setStyleSheet("QLabel{font-size:14px;color:rgba(38, 38, 38, 0.45);}"); + ui->lbNetwork->show(); + + topLanListWidget->setStyleSheet("QWidget{border:none;}"); + topLanListWidget->setStyleSheet("background-color:transparent;"); + + topWifiListWidget->setStyleSheet("QWidget{border:none;}"); + topWifiListWidget->setStyleSheet("background-color:transparent;"); + + lbLoadUp->setStyleSheet("QLabel{font-size:14px;color:rgba(38, 38, 38, 0.45);}"); + lbLoadDown->setStyleSheet("QLabel{font-size:14px;color:rgba(38, 38, 38, 0.45);}"); +//YYF lbLoadUp->setText("0KB/s"); +//YYF lbLoadDown->setText("0KB/s "); +//YYF this->on_setNetSpeed(); + scrollAreal->setStyleSheet("QScrollArea{border:none;}"); + scrollAreal->viewport()->setStyleSheet("background-color:transparent;"); + //scrollAreal->verticalScrollBar()->setStyleSheet(scrollBarQss); + scrollAreal->verticalScrollBar()->setContextMenuPolicy(Qt::NoContextMenu); + + scrollAreaw->setStyleSheet("QScrollArea{border:none;}"); + scrollAreaw->viewport()->setStyleSheet("background-color:transparent;"); + //scrollAreaw->verticalScrollBar()->setStyleSheet(scrollBarQss); + scrollAreaw->verticalScrollBar()->setContextMenuPolicy(Qt::NoContextMenu); +} + +void KylinNM::createLeftAreaUI() +{ + btnWireless = new SwitchButton(this); + // btnWireless->setStyleSheet("SwitchButton{border:none;background-color:rgba(255,255,255,0.12);}"); + ui->btnNetList->setFocusPolicy(Qt::NoFocus); + QString txtEthernet(tr("LAN")); + ui->lbNetListBG->setStyleSheet(btnOffQss); + lbNetListText =new QLabel(ui->lbNetListBG); + lbNetListText->setFocusPolicy(Qt::NoFocus); + lbNetListText->setText(tr("LAN")); + lbNetListText->move(98,2); + lbNetListText->setStyleSheet("QLabel{color:rgba(47, 179, 232, 1);background-color:transparent;}"); + ui->lbNetListImg->setStyleSheet("QLabel{border-image:url(:/res/l/pb-network-online.png);background-position:center;background-repeat:no-repeat;}"); + + ui->btnWifiList->setFocusPolicy(Qt::NoFocus); + QString txtWifi(tr("WLAN")); + ui->lbWifiListBG->setStyleSheet(btnOffQss); + lbWifiListText =new QLabel(ui->lbWifiListBG); + lbWifiListText->setFocusPolicy(Qt::NoFocus); + lbWifiListText->setText(tr("WLAN")); + lbWifiListText->move(98,2); + lbWifiListText->setStyleSheet("QLabel{color:rgba(38, 38, 38, 0.75);background-color:transparent;}"); + ui->lbWifiListImg->setStyleSheet("QLabel{border-image:url(:/res/x/pb-wifi-n.png);background-position:center;background-repeat:no-repeat;}"); + + ui->btnNet->hide(); + + btnWireless->move(385,73); + + ui->btnHotspot->setStyleSheet(leftBtnQss); + ui->btnHotspot->setFocusPolicy(Qt::NoFocus); + QString txtHotSpot(tr("HotSpot")); + ui->btnHotspot->hide(); + ui->lbHotImg->hide(); + ui->lbHotImg->setStyleSheet("QLabel{background-image:url(:/res/x/hot-spot-off.svg);}"); + ui->lbHotBG->hide(); + ui->lbHotBG->setStyleSheet(btnOffQss); + + ui->btnFlyMode->setStyleSheet(leftBtnQss); + ui->btnFlyMode->setFocusPolicy(Qt::NoFocus); + QString txtFlyMode(tr("FlyMode")); + ui->btnFlyMode->hide(); + ui->lbFlyImg->hide(); + ui->lbFlyImg->setStyleSheet("QLabel{background-image:url(:/res/x/fly-mode-off.svg);}"); + ui->lbFlyBG->hide(); + ui->lbFlyBG->setStyleSheet(btnOffQss); + + ui->btnAdvConf->setStyleSheet(leftBtnQss); + ui->btnAdvConf->setFocusPolicy(Qt::NoFocus); + QString txtAdvanced(tr("Advanced")); + //ui->lbBtnConfImg->setStyleSheet("QLabel{background-image:url(:/res/x/setup.png);}"); + ui->btnConfImg->setStyleSheet("QPushButton{background-image:url(:/res/x/setup.png);}"); + //ui->btnConfImg->setIcon(QIcon::fromTheme("settings-app-symbolic.svg", QIcon(":/res/x/setup.png")) ); +} + +// 初始化有线网列表 +void KylinNM::getInitLanSlist() +{ + const int BUF_SIZE = 1024; + char buf[BUF_SIZE]; + + FILE * p_file = NULL; + + p_file = popen("export LANG='en_US.UTF-8';export LANGUAGE='en_US';nmcli -f type,device,name connection show", "r"); + if (!p_file) { + syslog(LOG_ERR, "Error occurred when popen cmd 'nmcli connection show'"); + qDebug()<<"Error occurred when popen cmd 'nmcli connection show"; + } + while (fgets(buf, BUF_SIZE, p_file) != NULL) { + QString strSlist = ""; + QString line(buf); + strSlist = line.trimmed(); + if (strSlist.indexOf("UUID") != -1 || strSlist.indexOf("NAME") != -1) { + oldLanSlist.append(strSlist); + } + if (strSlist.indexOf("802-3-ethernet") != -1 || strSlist.indexOf("ethernet") != -1) { + oldLanSlist.append(strSlist); + } + } + pclose(p_file); +} + +// 初始化网络 +void KylinNM::initNetwork() +{ + BackThread *bt = new BackThread(); + IFace *iface = bt->execGetIface(); + + wname = iface->wname; + lwname = iface->wname; + lname = iface->lname; + llname = iface->lname; + + mwBandWidth = bt->execChkLanWidth(lname); + + // 开关状态 + qDebug()<<"==="; + qDebug()<<"state of network: '0' is connected, '1' is disconnected, '2' is net device switch off"; + syslog(LOG_DEBUG, "state of network: '0' is connected, '1' is disconnected, '2' is net device switch off"); + qDebug()<<"current network state: lan state ="<lstate<<", wifi state ="<wstate ; + syslog(LOG_DEBUG, "current network state: wired state =%d, wifi state =%d", iface->lstate, iface->wstate); + qDebug()<<"==="; + + //ui->lbBtnNetBG->setStyleSheet(btnOnQss); + if (iface->wstate == 0 || iface->wstate == 1 || iface->wstate == 3) { + // ui->lbBtnWifiBG->setStyleSheet(btnBgOnQss); + //ui->lbBtnWifiBall->move(X_RIGHT_WIFI_BALL, Y_WIFI_BALL); + btnWireless->setSwitchStatus(true); + } else { + btnWireless->setSwitchStatus(false); + //ui->lbBtnWifiBG->setStyleSheet(btnBgOffQss); + //ui->lbBtnWifiBall->move(X_LEFT_WIFI_BALL, Y_WIFI_BALL); + } + + // 初始化网络列表 + if (iface->wstate != 2) { + if (iface->wstate == 0) { + connWifiDone(3); + } else { + if (iface->lstate == 0) { + connLanDone(3); + } + } + on_btnWifiList_clicked(); + + ui->btnNetList->setStyleSheet("QPushButton{border:0px solid rgba(255,255,255,0);background-color:rgba(255,255,255,0);}"); + ui->btnWifiList->setStyleSheet("QPushButton{border:none;}"); + } else { + objKyDBus->setWifiSwitchState(false); //通知控制面板wifi未开启 + if (iface->lstate != 2) { + if (iface->lstate == 0) { + connLanDone(3); + } + onBtnNetListClicked(); + + ui->btnNetList->setStyleSheet("QPushButton{border:0px solid rgba(255,255,255,0);background-color:rgba(255,255,255,0);}"); + ui->btnWifiList->setStyleSheet("QPushButton{border:none;}"); + } else { + /*没看懂这段断开连接是什么意思,暂时关闭这段操作,会导致页面卡顿、某些情景还会自动断开网络 +// BackThread *m_bt = new BackThread(); +// IFace *m_iface = m_bt->execGetIface(); + +// m_bt->disConnLanOrWifi("ethernet"); +// sleep(1); +// m_bt->disConnLanOrWifi("ethernet"); +// sleep(1); +// m_bt->disConnLanOrWifi("ethernet"); + +// delete m_iface; +// m_bt->deleteLater(); + */ + + char *chr = "nmcli networking on"; + Utils::m_system(chr); + } + } + //第一次加载时,加载完lan列表会继续加载WIFI列表,先加载lan后加载WIFI是为了保证在加载WIFI时打开的计时器不会被直接关闭 + onBtnNetListClicked(); + //平板上默认展示wifi界面 +// on_btnWifiList_clicked(); +} + +// 初始化定时器 +void KylinNM::initTimer() +{ + //循环检测wifi列表的变化,可用于更新wifi列表 + checkWifiListChanged = new QTimer(this); + checkWifiListChanged->setTimerType(Qt::PreciseTimer); + QObject::connect(checkWifiListChanged, SIGNAL(timeout()), this, SLOT(on_checkWifiListChanged())); + checkWifiListChanged->start(7000); + + //网线插入时定时执行 + wiredCableUpTimer = new QTimer(this); + wiredCableUpTimer->setTimerType(Qt::PreciseTimer); + QObject::connect(wiredCableUpTimer, SIGNAL(timeout()), this, SLOT(onCarrierUpHandle())); + + //网线拔出时定时执行 + wiredCableDownTimer = new QTimer(this); + wiredCableDownTimer->setTimerType(Qt::PreciseTimer); + QObject::connect(wiredCableDownTimer, SIGNAL(timeout()), this, SLOT(onCarrierDownHandle())); + + //定时处理异常网络,即当点击Lan列表按钮时,若lstate=2,但任然有有线网连接的情况 + deleteLanTimer = new QTimer(this); + deleteLanTimer->setTimerType(Qt::PreciseTimer); + QObject::connect(deleteLanTimer, SIGNAL(timeout()), this, SLOT(onDeleteLan())); + + //定时获取网速 + setNetSpeed = new QTimer(this); + setNetSpeed->setTimerType(Qt::PreciseTimer); +//YYF QObject::connect(setNetSpeed, SIGNAL(timeout()), this, SLOT(on_setNetSpeed())); + setNetSpeed->start(3000); +} + + +/////////////////////////////////////////////////////////////////////////////// +// 任务栏托盘管理、托盘图标处理 + +void KylinNM::createTrayIcon() +{ + trayIcon = new QSystemTrayIcon(); + + trayIconMenu = new QMenu(); + + mShowWindow = new QAction(tr("Show KylinNM"),this); + mAdvConf = new QAction(tr("Advanced"),this); + mAdvConf->setIcon(QIcon::fromTheme("document-page-setup", QIcon(":/res/x/setup.png")) ); + + trayIconMenu->addAction(mShowWindow); + //trayIconMenu->addSeparator(); + trayIconMenu->addAction(mAdvConf); + //trayIconMenu->setAttribute(Qt::WA_TranslucentBackground);//设置窗口背景透明 + //trayIconMenu->setWindowOpacity(0.8); + trayIcon->setContextMenu(trayIconMenu); + + // 初始化托盘所有Icon + iconLanOnline = QIcon::fromTheme("network-wired-symbolic"); + iconLanOffline = QIcon::fromTheme("network-wired-offline-symbolic"); + iconWifiFull = QIcon::fromTheme("network-wireless-signal-excellent-symbolic"); + iconWifiHigh = QIcon::fromTheme("network-wireless-signal-good-symbolic"); + iconWifiMedium = QIcon::fromTheme("network-wireless-signal-ok"); + iconWifiLow = QIcon::fromTheme("network-wireless-signal-low"); + + loadIcons.append(QIcon::fromTheme("kylin-network-1")); + loadIcons.append(QIcon::fromTheme("kylin-network-2")); + loadIcons.append(QIcon::fromTheme("kylin-network-3")); + loadIcons.append(QIcon::fromTheme("kylin-network-4")); + loadIcons.append(QIcon::fromTheme("kylin-network-5")); + loadIcons.append(QIcon::fromTheme("kylin-network-6")); + loadIcons.append(QIcon::fromTheme("kylin-network-7")); + loadIcons.append(QIcon::fromTheme("kylin-network-8")); + loadIcons.append(QIcon::fromTheme("kylin-network-9")); + loadIcons.append(QIcon::fromTheme("kylin-network-10")); + loadIcons.append(QIcon::fromTheme("kylin-network-11")); + loadIcons.append(QIcon::fromTheme("kylin-network-12")); + + iconTimer = new QTimer(this); + connect(iconTimer, SIGNAL(timeout()), this, SLOT(iconStep())); + + setTrayIcon(iconLanOnline); +} + +void KylinNM::iconStep() +{ + if (currentIconIndex < 0) { + currentIconIndex = 11; + } + setTrayIcon(loadIcons.at(currentIconIndex)); + currentIconIndex --; +} + +void KylinNM::setTrayIcon(QIcon icon) +{ + trayIcon->setIcon(icon); +} + +void KylinNM::setTrayLoading(bool isLoading) +{ + if (isLoading) { + currentIconIndex = 11; + iconTimer->start(60); + } else { + iconTimer->stop(); + } +} + +void KylinNM::updateNetList() +{ + QString strTrans; + strTrans = QString::number(1, 10, 2); + //QString sty = "#centralWidget{background:rgba(19,19,20," + strTrans + ");}"; //YYF + QString sty = "#centralWidget{background:rgba(255,255,255," + strTrans + ");}"; + ui->centralWidget->setStyleSheet(sty); + + this->showNormal(); + if (is_btnNetList_clicked == 1) { + onBtnNetListClicked(0); + } + is_stop_check_net_state = 1; + if (is_btnWifiList_clicked == 1) { + BackThread *loop_bt = new BackThread(); + IFace *loop_iface = loop_bt->execGetIface(); + + if (loop_iface->wstate != 2) { + is_update_wifi_list = 1; + this->ksnm->execGetWifiList(); //更新wifi列表 + } + + delete loop_iface; + loop_bt->deleteLater(); + } + is_stop_check_net_state = 0; +} + +void KylinNM::updateWifiList() +{ + //每次展示都要显示无线 + on_btnWifiList_clicked(); +} + +void KylinNM::iconActivated(QSystemTrayIcon::ActivationReason reason) +{ + switch (reason) { + case QSystemTrayIcon::Trigger: + case QSystemTrayIcon::MiddleClick: + + handleIconClicked(); + + if (this->isHidden()) { + QString strTrans; + strTrans = QString::number(1, 10, 2); + //QString sty = "#centralWidget{background:rgba(19,19,20," + strTrans + ");}"; //YYF + QString sty = "#centralWidget{background:rgba(255,255,255," + strTrans + ");}"; + ui->centralWidget->setStyleSheet(sty); + + this->showNormal(); + if (is_btnNetList_clicked == 1) { + onBtnNetListClicked(0); + } + is_stop_check_net_state = 1; + if (is_btnWifiList_clicked == 1) { + BackThread *loop_bt = new BackThread(); + IFace *loop_iface = loop_bt->execGetIface(); + + if (loop_iface->wstate != 2) { + is_update_wifi_list = 1; + this->ksnm->execGetWifiList(); //更新wifi列表 + } + + delete loop_iface; + loop_bt->deleteLater(); + } + is_stop_check_net_state = 0; + } else { + //this->hide(); + } + break; + case QSystemTrayIcon::DoubleClick: + //this->hide(); + break; + case QSystemTrayIcon::Context: + //右键点击托盘图标弹出菜单 + showTrayIconMenu(); + break; + default: + break; + } +} + +void KylinNM::handleIconClicked() +{ + tabletStyle(); + if(isTabletStyle)//平板桌面模式 + { + //在屏幕中央显示 + QRect availableGeometry = qApp->primaryScreen()->availableGeometry(); + this->move((availableGeometry.width() - this->width())/2, (availableGeometry.height() - this->height())/2); + return; + } + + QRect availableGeometry = qApp->primaryScreen()->availableGeometry(); + QRect screenGeometry = qApp->primaryScreen()->geometry(); + + QDesktopWidget* desktopWidget = QApplication::desktop(); + QRect deskMainRect = desktopWidget->availableGeometry(0);//获取可用桌面大小 + QRect screenMainRect = desktopWidget->screenGeometry(0);//获取设备屏幕大小 + QRect deskDupRect = desktopWidget->availableGeometry(1);//获取可用桌面大小 + QRect screenDupRect = desktopWidget->screenGeometry(1);//获取设备屏幕大小 + + int n = objKyDBus->getTaskBarPos("position"); + int m = objKyDBus->getTaskBarHeight("height"); + int d = 2; //窗口边沿到任务栏距离 + + if (screenGeometry.width() == availableGeometry.width() && screenGeometry.height() == availableGeometry.height()) { + if (n == 0) { + //任务栏在下侧 + this->move(availableGeometry.x() + availableGeometry.width() - this->width(), screenMainRect.y() + availableGeometry.height() - this->height() - m - d); + } else if(n == 1) { + //任务栏在上侧 + this->move(availableGeometry.x() + availableGeometry.width() - this->width(), screenMainRect.y() + screenGeometry.height() - availableGeometry.height() + m + d); + } else if (n == 2) { + //任务栏在左侧 + if (screenGeometry.x() == 0) {//主屏在左侧 + this->move(m + d, screenMainRect.y() + screenMainRect.height() - this->height()); + } else {//主屏在右侧 + this->move(screenMainRect.x() + m + d, screenMainRect.y() + screenMainRect.height() - this->height()); + } + } else if (n == 3) { + //任务栏在右侧 + if (screenGeometry.x() == 0) {//主屏在左侧 + this->move(screenMainRect.width() - this->width() - m - d, screenMainRect.y() + screenMainRect.height() - this->height()); + } else {//主屏在右侧 + this->move(screenMainRect.x() + screenMainRect.width() - this->width() - m - d, screenMainRect.y() + screenMainRect.height() - this->height()); + } + } + } else if(screenGeometry.width() == availableGeometry.width() ) { + if (trayIcon->geometry().y() > availableGeometry.height()/2) { + //任务栏在下侧 + this->move(availableGeometry.x() + availableGeometry.width() - this->width(), screenMainRect.y() + availableGeometry.height() - this->height() - d); + } else { + //任务栏在上侧 + this->move(availableGeometry.x() + availableGeometry.width() - this->width(), screenMainRect.y() + screenGeometry.height() - availableGeometry.height() + d); + } + } else if (screenGeometry.height() == availableGeometry.height()) { + if (trayIcon->geometry().x() > availableGeometry.width()/2) { + //任务栏在右侧 + this->move(availableGeometry.x() + availableGeometry.width() - this->width() - d, screenMainRect.y() + screenGeometry.height() - this->height()); + } else { + //任务栏在左侧 + this->move(screenGeometry.width() - availableGeometry.width() + d, screenMainRect.y() + screenGeometry.height() - this->height()); + } + } +} + +void KylinNM::showTrayIconMenu() +{ + QRect availableGeometry = qApp->primaryScreen()->availableGeometry(); + QRect screenGeometry = qApp->primaryScreen()->geometry(); + + QDesktopWidget* desktopWidget = QApplication::desktop(); + // QRect deskMainRect = desktopWidget->availableGeometry(0);//获取可用桌面大小 + QRect screenMainRect = desktopWidget->screenGeometry(0);//获取设备屏幕大小 + // QRect deskDupRect = desktopWidget->availableGeometry(1);//获取可用桌面大小 + QRect screenDupRect = desktopWidget->screenGeometry(1);//获取设备屏幕大小 + + QPoint cursorPoint = QCursor::pos();//返回相对显示器的全局坐标 + int cursor_x = cursorPoint.x(); + int cursor_y = cursorPoint.y(); + + int n = objKyDBus->getTaskBarPos("position"); + int m = objKyDBus->getTaskBarHeight("height"); + int d = 0; //窗口边沿到任务栏距离 + int s = 80; //窗口边沿到屏幕边沿距离 + + if (screenGeometry.width() == availableGeometry.width() && screenGeometry.height() == availableGeometry.height()) { + if (n == 0) { //任务栏在下侧 + trayIconMenu->move(availableGeometry.x() + cursor_x - trayIconMenu->width()/2, screenMainRect.y() + availableGeometry.height() - trayIconMenu->height() - m - d); + } else if(n == 1) { //任务栏在上侧 + trayIconMenu->move(availableGeometry.x() + cursor_x - trayIconMenu->width()/2, screenMainRect.y() + screenGeometry.height() - availableGeometry.height() + m + d); + } else if (n == 2) { //任务栏在左侧 + trayIconMenu->move(m + d, cursor_y - trayIconMenu->height()/2); + //if (screenGeometry.x() == 0){//主屏在左侧 + // trayIconMenu->move(screenGeometry.width() - availableGeometry.width() + m + d, screenMainRect.y() + screenMainRect.height() - trayIconMenu->height() - s); + //}else{//主屏在右侧 + // trayIconMenu->move(screenGeometry.width() - availableGeometry.width() + m + d,screenDupRect.y() + screenDupRect.height() - trayIconMenu->height() - s); + //} + } else if (n == 3) { //任务栏在右侧 + trayIconMenu->move(screenMainRect.width() - trayIconMenu->width() - m - d, cursor_y - trayIconMenu->height()/2); + //if (screenGeometry.x() == 0){//主屏在左侧 + // trayIconMenu->move(screenMainRect.width() + screenDupRect.width() - trayIconMenu->width() - m - d, screenDupRect.y() + screenDupRect.height() - trayIconMenu->height() - s); + //}else{//主屏在右侧 + // trayIconMenu->move(availableGeometry.x() + availableGeometry.width() - trayIconMenu->width() - m - d, screenMainRect.y() + screenMainRect.height() - trayIconMenu->height() - s); + //} + } + } else if(screenGeometry.width() == availableGeometry.width() ) { + if (trayIcon->geometry().y() > availableGeometry.height()/2) { //任务栏在下侧 + trayIconMenu->move(availableGeometry.x() + cursor_x - trayIconMenu->width()/2, screenMainRect.y() + availableGeometry.height() - trayIconMenu->height() - d); + } else { //任务栏在上侧 + trayIconMenu->move(availableGeometry.x() + cursor_x - trayIconMenu->width()/2, screenMainRect.y() + screenGeometry.height() - availableGeometry.height() + d); + } + } else if (screenGeometry.height() == availableGeometry.height()) { + if (trayIcon->geometry().x() > availableGeometry.width()/2) { //任务栏在右侧 + trayIconMenu->move(availableGeometry.x() + availableGeometry.width() - trayIconMenu->width() - d, cursor_y - trayIconMenu->height()/2); + } else { //任务栏在左侧 + trayIconMenu->move(screenGeometry.width() - availableGeometry.width() + d, cursor_y - trayIconMenu->height()/2); + } + } +} + +void KylinNM::on_showWindowAction() +{ + handleIconClicked(); + this->showNormal(); +} + + +/////////////////////////////////////////////////////////////////////////////// +//加载动画,获取当前连接的网络和状态并设置图标 + +void KylinNM::startLoading() +{ +// loading->startLoading(); + setTrayLoading(true); +} + +void KylinNM::stopLoading() +{ + loading->stopLoading(); + setTrayLoading(false); + getActiveInfo(); +} + +void KylinNM::on_checkOverTime() +{ + QString cmd = "kill -9 $(pidof nmcli)"; //杀掉当前正在进行的有关nmcli命令的进程 + int status = system(cmd.toUtf8().data()); + if (status != 0) { + qDebug()<<"execute 'kill -9 $(pidof nmcli)' in function 'on_checkOverTime' failed"; + syslog(LOG_ERR, "execute 'kill -9 $(pidof nmcli)' in function 'on_checkOverTime' failed"); + } + this->stopLoading(); //超时停止等待动画 + is_stop_check_net_state = 0; +} + +void KylinNM::getActiveInfo() +{ + QString actLanName = "--"; + QString actWifiName = "--"; + + activecon *act = kylin_network_get_activecon_info(); + int index = 0; + while (act[index].con_name != NULL) { + if (QString(act[index].type) == "ethernet" || QString(act[index].type) == "802-3-ethernet") { + actLanName = QString(act[index].con_name); + } + if (QString(act[index].type) == "wifi" || QString(act[index].type) == "802-11-wireless") { + actWifiName = QString(act[index].con_name); + } + index ++; + } + + + //ukui3.0中获取currentActWifiSignalLv的值 + if (activeWifiSignalLv > 75) { + currentActWifiSignalLv = 1; + } else if(activeWifiSignalLv > 55 && activeWifiSignalLv <= 75) { + currentActWifiSignalLv = 2; + } else if(activeWifiSignalLv > 35 && activeWifiSignalLv <= 55) { + currentActWifiSignalLv = 3; + } else if( activeWifiSignalLv <= 35) { + currentActWifiSignalLv = 4; + } + + // 设置图标 + if (actLanName != "--") { + setTrayIcon(iconLanOnline); + } else if (actWifiName != "--") { + switch (currentActWifiSignalLv) { + case 1: + setTrayIcon(iconWifiFull); + break; + case 2: + setTrayIcon(iconWifiHigh); + break; + case 3: + setTrayIcon(iconWifiMedium); + break; + case 4: + setTrayIcon(iconWifiLow); + break; + default: + setTrayIcon(iconWifiFull); + break; + } + } else { + setTrayIcon(iconLanOffline); + } +} + + +/////////////////////////////////////////////////////////////////////////////// +//网络设备管理 + +//网线插拔处理,由kylin-dbus-interface.cpp调用 +void KylinNM::onPhysicalCarrierChanged(bool flag) +{ + this->startLoading(); + if (flag) { + is_stop_check_net_state = 1; + qDebug()<<"插入了有线网的网线"; + syslog(LOG_DEBUG,"wired physical cable is already plug in"); + wiredCableUpTimer->start(2000); + } else { + qDebug()<<"拔出了有线网的网线"; + syslog(LOG_DEBUG,"wired physical cable is already plug out"); + + BackThread *bt = new BackThread(); + IFace *iface = bt->execGetIface(); + if (iface->lstate != 0) { + is_stop_check_net_state = 1; + wiredCableDownTimer->start(2000); + } + delete iface; + bt->deleteLater(); + } +} + +void KylinNM::onCarrierUpHandle() +{ + wiredCableUpTimer->stop(); + //BackThread *up_bt = new BackThread(); + //up_bt->disConnLanOrWifi("ethernet"); + //sleep(1); + //up_bt->disConnLanOrWifi("ethernet"); + //sleep(1); + //up_bt->disConnLanOrWifi("ethernet"); + //up_bt->deleteLater(); + + this->stopLoading(); + onBtnNetListClicked(1); + is_stop_check_net_state = 0; +} + +void KylinNM::onCarrierDownHandle() +{ + wiredCableDownTimer->stop(); + this->stopLoading(); + onBtnNetListClicked(0); + is_stop_check_net_state = 0; +} + +void KylinNM::onDeleteLan() +{ + deleteLanTimer->stop(); + BackThread *btn_bt = new BackThread(); + btn_bt->disConnLanOrWifi("ethernet"); + sleep(1); + btn_bt->disConnLanOrWifi("ethernet"); + sleep(1); + btn_bt->disConnLanOrWifi("ethernet"); + btn_bt->deleteLater(); + + this->stopLoading(); + onBtnNetListClicked(0); + is_stop_check_net_state = 0; +} + +//无线网卡插拔处理 +void KylinNM::onNetworkDeviceAdded(QDBusObjectPath objPath) +{ + //仅处理无线网卡插入情况 + objKyDBus->isWirelessCardOn = false; + objKyDBus->getObjectPath(); + + if (objKyDBus->wirelessPath.path() == objPath.path()) { //证明添加的是无线网卡 + is_wireless_adapter_ready = 0; + if (objKyDBus->isWirelessCardOn) { + syslog(LOG_DEBUG,"wireless device is already plug in"); + qDebug()<<"wireless device is already plug in"; + is_wireless_adapter_ready = 1; + onBtnWifiClicked(4); + } + } +} + +void KylinNM::onNetworkDeviceRemoved(QDBusObjectPath objPath) +{ + //仅处理无线网卡拔出情况 + if (objKyDBus->wirelessPath.path() == objPath.path()) { + objKyDBus->isWirelessCardOn = false; + objKyDBus->getObjectPath(); //检查是不是还有无线网卡 + if (!objKyDBus->isWirelessCardOn) { + syslog(LOG_DEBUG,"wireless device is already plug out"); + qDebug()<<"wireless device is already plug out"; + is_wireless_adapter_ready = 0; + onBtnWifiClicked(5); + } else { + syslog(LOG_DEBUG,"wireless device is already plug out, but one more wireless exist"); + qDebug()<<"wireless device is already plug out, but one more wireless exist"; + } + } +} + +void KylinNM::checkIsWirelessDeviceOn() +{ + //启动时判断是否有无线网卡 + //KylinDBus kDBus3; + if (objKyDBus->isWirelessCardOn) { + is_wireless_adapter_ready = 1; + } else { + is_wireless_adapter_ready = 0; + } +} + +void KylinNM::getLanBandWidth() +{ + BackThread *bt = new BackThread(); + IFace *iface = bt->execGetIface(); + + lname = iface->lname; + + mwBandWidth = bt->execChkLanWidth(lname); +} + +//检测网络设备状态 +bool KylinNM::checkLanOn() +{ + BackThread *bt = new BackThread(); + IFace *iface = bt->execGetIface(); + + if (iface->lstate == 2) { + return false; + } else { + return true; + } + + delete iface; + bt->deleteLater(); +} + +bool KylinNM::checkWlOn() +{ + BackThread *bt = new BackThread(); + IFace *iface = bt->execGetIface(); + + bool ret = true; + if (iface->wstate == 2) { + ret = false; + } + + delete iface; + bt->deleteLater(); + return ret; +} + + +/////////////////////////////////////////////////////////////////////////////// +//有线网与无线网按钮响应 + +void KylinNM::on_btnNet_clicked() +{ + if (checkLanOn()) { + QThread *t = new QThread(); + BackThread *bt = new BackThread(); + bt->moveToThread(t); + connect(t, SIGNAL(finished()), t, SLOT(deleteLater())); + connect(t, SIGNAL(started()), bt, SLOT(execDisNet())); + connect(bt, SIGNAL(disNetDone()), this, SLOT(disNetDone())); + connect(bt, SIGNAL(btFinish()), t, SLOT(quit())); + t->start(); + + } else { + is_stop_check_net_state = 1; + QThread *t = new QThread(); + BackThread *bt = new BackThread(); + bt->moveToThread(t); + connect(t, SIGNAL(finished()), t, SLOT(deleteLater())); + connect(t, SIGNAL(started()), bt, SLOT(execEnNet())); + connect(bt, SIGNAL(enNetDone()), this, SLOT(enNetDone())); + connect(bt, SIGNAL(btFinish()), t, SLOT(quit())); + t->start(); + } + + this->startLoading(); +} + +void KylinNM::onBtnWifiClicked(int flag) +{ + qDebug()<<"Value of flag passed into function 'onBtnWifiClicked' is: "<setWifiSwitchState(false); + lbTopWifiList->hide(); +// btnAddNet->hide(); + + QThread *t = new QThread(); + BackThread *bt = new BackThread(); + bt->moveToThread(t); + btnWireless->setSwitchStatus(true); + connect(t, SIGNAL(finished()), t, SLOT(deleteLater())); + connect(t, SIGNAL(started()), bt, SLOT(execDisWifi())); + connect(bt, SIGNAL(disWifiDone()), this, SLOT(disWifiDone())); + connect(bt, SIGNAL(btFinish()), t, SLOT(quit())); + + /**锁屏多线程信号槽消息传递异常,暂时以定时器方式检测,需调整pam验证的fork方式 + */ + QTimer *timer = new QTimer(this); + connect(timer, &QTimer::timeout, this, [=]{ + if(m_is_reflashWifiUi) { + disWifiDone(); + mutexIsReflashWifi.lock(); + m_is_reflashWifiUi = false; + mutexIsReflashWifi.unlock(); + timer->stop(); + } + }); + timer->start(200); + QTimer::singleShot(8*1000, this, [=]{ + if(timer->isActive()) + timer->stop(); + }); + /**锁屏多线程信号槽消息传递异常,暂时以定时器方式检测,需调整pam验证的fork方式 + */ + t->start(); + this->startLoading(); + } + } else { + if (is_fly_mode_on == 0) { + //on_btnWifiList_clicked(); + is_stop_check_net_state = 1; + qDebug() << "aaa222"; + objKyDBus->setWifiCardState(true); + objKyDBus->setWifiSwitchState(true); + //lbTopWifiList->show(); + //btnAddNet->show(); + + QThread *t = new QThread(); + BackThread *bt = new BackThread(); + bt->moveToThread(t); + btnWireless->setSwitchStatus(true); + connect(t, SIGNAL(finished()), t, SLOT(deleteLater())); + connect(t, SIGNAL(started()), bt, SLOT(execEnWifi())); + connect(bt, SIGNAL(enWifiDone()), this, SLOT(enWifiDone())); + connect(bt, SIGNAL(launchLanDone()), this, SLOT(launchLanDone())); + connect(bt, SIGNAL(btFinish()), t, SLOT(quit())); + + /**锁屏多线程信号槽消息传递异常,暂时以定时器方式检测,需调整pam验证的fork方式 + */ + QTimer *timer = new QTimer(this); + connect(timer, &QTimer::timeout, this, [=]{ + if(m_is_reflashWifiUi) { + enWifiDone(); + mutexIsReflashWifi.lock(); + m_is_reflashWifiUi = false; + mutexIsReflashWifi.unlock(); + timer->stop(); + } + }); + timer->start(200); + QTimer::singleShot(8*1000, this, [=]{ + if(timer->isActive()) + timer->stop(); + }); + t->start(); + this->startLoading(); + } + } + } else if(flag == 2) { + if (is_fly_mode_on == 0) { + //on_btnWifiList_clicked(); + is_stop_check_net_state = 1; + lbTopWifiList->show(); +// btnAddNet->show(); + + QThread *t = new QThread(); + BackThread *bt = new BackThread(); + btnWireless->setSwitchStatus(true); + bt->moveToThread(t); + connect(t, SIGNAL(finished()), t, SLOT(deleteLater())); + connect(t, SIGNAL(started()), bt, SLOT(execEnWifi())); + connect(bt, SIGNAL(enWifiDone()), this, SLOT(enWifiDone())); + connect(bt, SIGNAL(launchLanDone()), this, SLOT(launchLanDone())); + connect(bt, SIGNAL(btFinish()), t, SLOT(quit())); + t->start(); + this->startLoading(); + } + } else if(flag == 3) { + is_stop_check_net_state = 1; + lbTopWifiList->hide(); +// btnAddNet->hide(); + + QThread *t = new QThread(); + BackThread *bt = new BackThread(); + btnWireless->setSwitchStatus(true); + bt->moveToThread(t); + connect(t, SIGNAL(finished()), t, SLOT(deleteLater())); + connect(t, SIGNAL(started()), bt, SLOT(execDisWifi())); + connect(bt, SIGNAL(disWifiDone()), this, SLOT(disWifiDone())); + connect(bt, SIGNAL(btFinish()), t, SLOT(quit())); + t->start(); + this->startLoading(); + } else { + qDebug()<<"receive an invalid value in function onBtnWifiClicked"; + syslog(LOG_DEBUG, "receive an invalid value in function onBtnWifiClicked"); + } + + } else { + lbTopWifiList->hide(); +// btnAddNet->hide(); + + if (flag == 0) { + objKyDBus->setWifiSwitchState(false); + objKyDBus->setWifiCardState(false); + } + + QString txt(tr("No wireless card detected")); //未检测到无线网卡 + objKyDBus->showDesktopNotify(txt); + qDebug()<<"No wireless card detected"; + syslog(LOG_DEBUG, "No wireless card detected"); + //QString cmd = "export LANG='en_US.UTF-8';export LANGUAGE='en_US';notify-send '" + txt + "' -t 3800"; + //int status = system(cmd.toUtf8().data()); + //if (status != 0){ syslog(LOG_ERR, "execute 'notify-send' in function 'onBtnWifiClicked' failed");} + + disWifiStateKeep(); + } + +} + +void KylinNM::onBtnNetListClicked(int flag) +{ + this->is_btnNetList_clicked = 1; + this->is_btnWifiList_clicked = 0; + + ui->lbNetListBG->setStyleSheet(btnOnQss); + ui->lbWifiListBG->setStyleSheet(btnOffQss); + + BackThread *bt = new BackThread(); + IFace *iface = bt->execGetIface(); + + lbLoadDown->show(); + lbLoadUp->show(); + lbLoadDownImg->show(); + lbLoadUpImg->show(); + if (iface->lstate != 0) { + lbLoadDown->hide(); + lbLoadUp->hide(); + lbLoadDownImg->hide(); + lbLoadUpImg->hide(); + } + + lbNoItemTip->hide(); + + ui->lbNetwork->setText(tr("Activated LAN")); + btnWireless->hide(); + //ui->lbBtnWifiBG->hide(); + //ui->lbBtnWifiBall->hide(); + + // 强行设置为打开 + if (flag == 1) { + this->startLoading(); + this->ksnm->execGetLanList(); + this->scrollAreal->show(); + this->topLanListWidget->show(); + this->scrollAreaw->hide(); + this->topWifiListWidget->hide(); + on_btnNetList_pressed(); + return; + } + + if (iface->lstate != 2) { + this->startLoading(); + this->ksnm->execGetLanList(); + } else { + this->startLoading(); + this->ksnm->execGetLanList(); + } + +// btnCreateNet->show(); +// btnAddNet->hide(); + this->scrollAreal->show(); + this->topLanListWidget->show(); + this->scrollAreaw->hide(); + this->topWifiListWidget->hide(); + on_btnNetList_pressed(); + //YYF + ui->lbNetListImg->setStyleSheet("QLabel{border-image:url(:/res/l/pb-network-online.png);background-position:center;background-repeat:no-repeat;}"); + ui->lbWifiListImg->setStyleSheet("QLabel{border-image:url(:/res/x/pb-wifi-n.png);background-position:center;background-repeat:no-repeat;}"); + lbNetListText->setStyleSheet("QLabel{color:rgba(47, 179, 232, 1);background-color:transparent;}"); + lbWifiListText->setStyleSheet("QLabel{color:rgba(38, 38, 38, 0.75);background-color:transparent;}"); + + + delete iface; + bt->deleteLater(); +} + +// 当点击wifi标题的时候执行 +void KylinNM::on_btnWifiList_clicked() +{ + this->is_btnWifiList_clicked = 1; + this->is_btnNetList_clicked = 0; + + BackThread *bt = new BackThread(); + IFace *iface = bt->execGetIface(); + + lbLoadDown->show(); + lbLoadUp->show(); + lbLoadDownImg->show(); + lbLoadUpImg->show(); + if (iface->wstate != 0) { + lbLoadDown->hide(); + lbLoadUp->hide(); + lbLoadDownImg->hide(); + lbLoadUpImg->hide(); + } + + ui->lbNetListBG->setStyleSheet(btnOffQss); + ui->lbWifiListBG->setStyleSheet(btnOnQss); + + lbNoItemTip->hide(); + ui->lbNetwork->setText(tr("Activated WLAN")); + btnWireless->show(); + //ui->lbBtnWifiBG->show(); + //ui->lbBtnWifiBall->show(); + if (iface->wstate == 0 || iface->wstate == 1 || iface->wstate == 3) { + //ui->lbBtnWifiBG->setStyleSheet(btnBgOnQss); + //ui->lbBtnWifiBall->move(X_RIGHT_WIFI_BALL, Y_WIFI_BALL); + btnWireless->setSwitchStatus(true); + } else { + //ui->lbBtnWifiBG->setStyleSheet(btnBgOffQss); + //ui->lbBtnWifiBall->move(X_LEFT_WIFI_BALL, Y_WIFI_BALL); + btnWireless->setSwitchStatus(false); + } + + if (iface->wstate != 2) { + //ui->lbBtnWifiBG->setStyleSheet(btnBgOnQss); + //ui->lbBtnWifiBall->move(X_RIGHT_WIFI_BALL, Y_WIFI_BALL); + btnWireless->setSwitchStatus(true); + lbTopWifiList->show(); +// btnAddNet->show(); + + this->startLoading(); + this->ksnm->execGetWifiList(); + } else { + //ui->lbBtnWifiBG->setStyleSheet(btnBgOffQss); + //ui->lbBtnWifiBall->move(X_LEFT_WIFI_BALL, Y_WIFI_BALL); + btnWireless->setSwitchStatus(false); + delete topWifiListWidget; //清空top列表 + createTopWifiUI(); //创建顶部无线网item + lbTopWifiList->hide(); + ui->lbNetListImg->setStyleSheet("QLabel{border-image:url(:/res/l/pb-network-offline.png);background-position:center;background-repeat:no-repeat;}"); + ui->lbWifiListImg->setStyleSheet("QLabel{border-image:url(:/res/x/pb-wifi-y.png);background-position:center;background-repeat:no-repeat;}"); + lbNetListText->setStyleSheet("QLabel{color:rgba(38, 38, 38, 0.75);background-color:transparent;}"); + lbWifiListText->setStyleSheet("QLabel{color:rgba(47, 179, 232, 1);background-color:transparent;}"); + + // 清空wifi列表 + wifiListWidget = new QWidget(scrollAreaw); + wifiListWidget->resize(W_LIST_WIDGET+173*isTabletStyle, H_WIFI_ITEM_BIG_EXTEND); + scrollAreaw->setWidget(wifiListWidget); + scrollAreaw->move(W_LEFT_AREA, Y_SCROLL_AREA); + + + // 当前连接的wifi + OneConnForm *ccf = new OneConnForm(topWifiListWidget, this, confForm, ksnm); + ccf->setName(tr("Not connected"));//"当前未连接任何 Wifi" + ccf->setSignal("0", "--"); + ccf->setRate("0"); + ccf->setConnedString(1, tr("Disconnected"), "");//"未连接" + ccf->isConnected = false; + ccf->setTopItem(false); + ccf->setAct(true); + ccf->move(L_VERTICAL_LINE_TO_ITEM, 0); + ccf->show(); + + this->lanListWidget->hide(); + this->wifiListWidget->show(); + + getActiveInfo(); + is_stop_check_net_state = 0; + } + +// btnCreateNet->hide(); +// if(is_wireless_adapter_ready == 1) +// { +// btnAddNet->show(); +// } + + this->scrollAreal->hide(); + this->topLanListWidget->hide(); + this->scrollAreaw->show(); + this->topWifiListWidget->show(); + on_btnWifiList_pressed(); + + delete iface; + bt->deleteLater(); +} + +void KylinNM::on_btnNetList_pressed() +{ + //ui->btnNetList->setStyleSheet("#btnNetList{font-size:12px;color:white;border:1px solid rgba(255,255,255,0.1);border:1px solid rgba(255,255,255,0.5);background:transparent;background-color:rgba(255,255,255,0.1);}"); + //ui->btnWifiList->setStyleSheet("#btnWifiList{font-size:12px;color:white;border:1px solid rgba(255,255,255,0.1);background:transparent;background-color:rgba(0,0,0,0.2);}" + // "#btnWifiList:Pressed{border:1px solid rgba(255,255,255,0.5);background:transparent;background-color:rgba(255,255,255,0.1);}"); +} + +void KylinNM::on_btnWifiList_pressed() +{ + //YYF 复制自tabletStyle方法,后期考虑合并 + ui->btnNetList->setStyleSheet(btnOffQss); + ui->btnWifiList->setStyleSheet(btnOnQss); + ui->lbNetListBG->setStyleSheet(btnOffQss); + ui->lbWifiListBG->setStyleSheet(btnOnQss); + ui->lbNetListImg->setStyleSheet("QLabel{border-image:url(:/res/l/pb-network-offline.png);background-position:center;background-repeat:no-repeat;}"); + ui->lbWifiListImg->setStyleSheet("QLabel{border-image:url(:/res/x/pb-wifi-y.png);background-position:center;background-repeat:no-repeat;}"); + lbNetListText->setStyleSheet("QLabel{color:rgba(38, 38, 38, 0.75);background-color:transparent;}"); + lbWifiListText->setStyleSheet("QLabel{color:rgba(47, 179, 232, 1);background-color:transparent;}"); + + //ui->btnWifiList->setStyleSheet("#btnWifiList{font-size:12px;color:white;border:1px solid rgba(255,255,255,0.1);border:1px solid rgba(255,255,255,0.5);background:transparent;background-color:rgba(255,255,255,0.1);}"); + //ui->btnNetList->setStyleSheet("#btnNetList{font-size:12px;color:white;border:1px solid rgba(255,255,255,0.1);background:transparent;background-color:rgba(0,0,0,0.2);}" + // "#btnNetList:Pressed{border:1px solid rgba(255,255,255,0.5);background:transparent;background-color:rgba(255,255,255,0.1);}"); +} + + +/////////////////////////////////////////////////////////////////////////////// +//网络列表加载与更新 + +// 获取lan列表回调 +void KylinNM::getLanListDone(QStringList slist) +{ + if (this->ksnm->isUseOldLanSlist) { + slist = oldLanSlist; + this->ksnm->isUseOldLanSlist = false; + } + + delete topLanListWidget; // 清空top列表 + + createTopLanUI(); //创建顶部有线网item + + // 清空lan列表 + lanListWidget = new QWidget(scrollAreal); + lanListWidget->resize(W_LIST_WIDGET, H_NORMAL_ITEM + H_LAN_ITEM_EXTEND); + scrollAreal->setWidget(lanListWidget); + scrollAreal->move(W_LEFT_AREA, Y_SCROLL_AREA); + lanNameList.clear(); + + // 获取当前连接的lan name + QString actLanName = "--"; + activecon *act = kylin_network_get_activecon_info(); + + int index = 0; + while (act[index].con_name != NULL) { + if (QString(act[index].type) == "ethernet" || QString(act[index].type) == "802-3-ethernet") { + actLanName = QString(act[index].con_name); + break; + } + index ++; + } + + // 若当前lan name为"--",设置OneConnForm + OneLancForm *ccf = new OneLancForm(topLanListWidget, this, confForm, ksnm); + topLanListWidget->resize(W_TOP_LIST_WIDGET+173*isTabletStyle, H_NORMAL_ITEM + H_GAP_UP + X_ITEM); + ccf->setFixedWidth(414+194*isTabletStyle); + if (actLanName == "--") { + ccf->setName(tr("Not connected"), "");//"当前未连接任何 以太网" + ccf->setIcon(false); + ccf->setConnedString(1, tr("Disconnected"));//"未连接" + ccf->isConnected = false; + ifLanConnected = false; + lbLoadDown->hide(); + lbLoadUp->hide(); + lbLoadDownImg->hide(); + lbLoadUpImg->hide(); + ccf->setTopItem(false); + ccf->setAct(false); + } + else + ccf->setAct(true); + ccf->move(L_VERTICAL_LINE_TO_ITEM, 0); + ccf->show(); + // 填充可用网络列表 + QString headLine; + if(slist.count() > 0) + headLine = slist.at(0); + else + headLine = ""; + int indexDevice, indexName; + headLine = headLine.trimmed(); + + bool isChineseExist = headLine.contains(QRegExp("[\\x4e00-\\x9fa5]+")); + if (isChineseExist) { + indexDevice = headLine.indexOf("设备") + 2; + indexName = headLine.indexOf("名称") + 4; + } else { + indexDevice = headLine.indexOf("DEVICE"); + indexName = headLine.indexOf("NAME"); + } + + QString order = "a"; //为避免同名情况,这里给每一个有线网设定一个唯一标志 + for(int i = 1, j = 0; i < slist.size(); i ++) { + QString line = slist.at(i); + QString ltype = line.mid(0, indexDevice).trimmed(); + QString nname = line.mid(indexName).trimmed(); + + if (ltype != "802-11-wireless" && ltype != "wifi" && ltype != "" && ltype != "--") { + lanNameList.append(nname); + // 当前连接的lan + if (nname == actLanName) { + //objKyDBus->getConnectNetIp(); + actLanName = "--"; + if (mwBandWidth == "Unknown!") { getLanBandWidth(); } + + connect(ccf, SIGNAL(selectedOneLanForm(QString, QString)), this, SLOT(oneTopLanFormSelected(QString, QString))); + connect(ccf, SIGNAL(disconnActiveLan()), this, SLOT(activeLanDisconn())); + ccf->setName(nname, nname + order); + ccf->setIcon(true); + ccf->setLanInfo(objKyDBus->dbusActiveLanIpv4, objKyDBus->dbusActiveLanIpv6, mwBandWidth, objKyDBus->dbusLanMac); + ccf->setConnedString(1, tr("NetOn,"));//"已连接" + ccf->isConnected = true; + ifLanConnected = true; + lbLoadDown->show(); + lbLoadUp->show(); + lbLoadDownImg->show(); + lbLoadUpImg->show(); + ccf->setTopItem(false); + currSelNetName = ""; + objKyDBus->dbusActiveLanIpv4 = ""; + objKyDBus->dbusActiveLanIpv6 = ""; + syslog(LOG_DEBUG, "already insert an active lannet in the top of lan list"); + } else { + objKyDBus->getLanIp(nname); + OneLancForm *ocf = new OneLancForm(lanListWidget, this, confForm, ksnm); +// connect(ocf, SIGNAL(selectedOneLanForm(QString, QString)), this, SLOT(oneLanFormSelected(QString, QString))); + ocf->setName(nname, nname + order); + ocf->setIcon(false); //YYF + ocf->setLine(true); + ocf->setLanInfo(objKyDBus->dbusLanIpv4, objKyDBus->dbusLanIpv6, tr("Disconnected"), objKyDBus->dbusLanMac); + ocf->setConnedString(0, tr("Disconnected"));//"未连接" + ocf->move(L_VERTICAL_LINE_TO_ITEM, j * H_NORMAL_ITEM); + lanListWidget->resize(W_LIST_WIDGET+194*isTabletStyle, lanListWidget->height() + H_NORMAL_ITEM); + ocf->setFixedWidth(W_LIST_WIDGET+194*isTabletStyle); //YYF + ocf->setSelected(false, false); + ocf->show(); + j ++; + } + order += "a"; + } + } + + QList itemList = lanListWidget->findChildren(); + int n = itemList.size(); + if (n >= 1) { + OneLancForm *lastItem = itemList.at(n-1); + lastItem->setLine(false); + lbNoItemTip->hide(); + } else { + if (!ifLanConnected) { + lbNoItemTip->hide(); +// lbTopLanList->hide(); +// btnCreateNet->hide(); + } else { + lbNoItemTip->show(); + lbNoItemTip->setText(tr("No Other Wired Network Scheme")); + } + } + + this->lanListWidget->show(); + this->topLanListWidget->show(); + this->wifiListWidget->hide(); + this->topWifiListWidget->hide(); + + this->stopLoading(); + oldLanSlist = slist; + is_stop_check_net_state = 0; +} + +// 获取wifi列表回调 +void KylinNM::getWifiListDone(QStringList slist) +{ + qDebug()<<"debug: oldWifiSlist.size()="< 1) { + actWifiuuid = currConnWifiBSsidUuid.at(0); + for (int i=1; isetName(tr("Not connected"));//"当前未连接任何 Wifi" + ccf->setSignal("0", "--"); + activeWifiSignalLv = 0; + ccf->setConnedString(1, tr("Disconnected"), "");//"未连接" + ccf->isConnected = false; + ifWLanConnected = false; + lbLoadDown->hide(); + lbLoadUp->hide(); + lbLoadDownImg->hide(); + lbLoadUpImg->hide(); + ccf->setTopItem(false); + } + ccf->setAct(true); + ccf->move(L_VERTICAL_LINE_TO_ITEM, 0); + ccf->show(); + // 填充可用网络列表 + QString headLine = slist.at(0); + int indexSignal,indexSecu, indexFreq, indexBSsid, indexName, indexPath; + headLine = headLine.trimmed(); + + bool isChineseExist = headLine.contains(QRegExp("[\\x4e00-\\x9fa5]+")); + if (isChineseExist) { + indexSignal = headLine.indexOf("SIGNAL"); + indexSecu = headLine.indexOf("安全性"); + indexFreq = headLine.indexOf("频率") + 4; + indexBSsid = headLine.indexOf("BSSID") + 6; + indexName = indexBSsid + 19; + indexPath = headLine.indexOf("DBUS-PATH"); + } else { + indexSignal = headLine.indexOf("SIGNAL"); + indexSecu = headLine.indexOf("SECURITY"); + indexFreq = headLine.indexOf("FREQ"); + indexBSsid = headLine.indexOf("BSSID"); + indexName = indexBSsid + 19; + indexPath = headLine.indexOf("DBUS-PATH"); + } + QStringList wnames; + int count = 0; + QString actWifiBssid = " "; + for (int i = 1; i < slist.size(); i ++) { + QString line = slist.at(i); + QString wbssid = line.mid(indexBSsid, 17).trimmed(); + QString wname = line.mid(indexName, indexPath - indexName).trimmed(); + + if (actWifiBssidList.contains(wbssid)) { + actWifiName = wname; + } + if ("*" == line.mid(0,indexSignal).trimmed()){ + actWifiBssid = wbssid; + } + } + + if (actWifiBssidList.size()==1 && actWifiBssidList.at(0)=="--") { + actWifiId = actWifiName; + actWifiName = "--"; + } + for (int i = 1, j = 0; i < slist.size(); i ++) { + QString line = slist.at(i); + //QString wsignal = line.mid(0, indexRate).trimmed(); + //QString wrate = line.mid(indexRate, indexSecu - indexRate).trimmed(); + QString wsignal = line.mid(indexSignal, 3).trimmed(); + QString wsecu = line.mid(indexSecu, indexFreq - indexSecu).trimmed(); + QString wbssid = line.mid(indexBSsid, 17).trimmed(); + QString wname = line.mid(indexName, indexPath - indexName).trimmed(); + + bool isContinue = false; + foreach (QString addName, wnames) { + // 重复的网络名称,跳过不处理 + if(addName == wname){ isContinue = true; } + } + if(isContinue){ continue; } + + if (actWifiName != "--" && actWifiName == wname) { + if (!actWifiBssidList.contains(wbssid)) { + continue; //若当前热点ssid名称和已经连接的wifi的ssid名称相同,但bssid不同,则跳过 + } + } + + if (wname != "" && wname != "--") { + // 当前连接的wifi + if (wname == actWifiName) { + connect(ccf, SIGNAL(selectedOneWifiForm(QString,int)), this, SLOT(oneTopWifiFormSelected(QString,int))); + connect(ccf, SIGNAL(disconnActiveWifi()), this, SLOT(activeWifiDisconn())); + QString path = line.mid(indexPath).trimmed(); + QString m_name = this->objKyDBus->getWifiSsid(QString("/org/freedesktop/NetworkManager/AccessPoint/%1").arg(path.mid(path.lastIndexOf("/") + 1))); +// ccf->setName(m_name); + if (m_name.isEmpty() || m_name == "") { + ccf->setName(wname); + } else { + ccf->setName(m_name); + } + //ccf->setRate(wrate); + int signal = wsignal.toInt() + 11; + ccf->setSignal(QString::number(signal), wsecu); + activeWifiSignalLv = wsignal.toInt(); + objKyDBus->getWifiMac(wname); + ccf->setWifiInfo(wsecu, wsignal, objKyDBus->dbusWifiMac); + ccf->setConnedString(1, tr("NetOn,"), wsecu);//"已连接" + ccf->isConnected = true; + ifWLanConnected = true; + lbLoadDown->show(); + lbLoadUp->show(); + lbLoadDownImg->show(); + lbLoadUpImg->show(); + ccf->setTopItem(false); + currSelNetName = ""; + + syslog(LOG_DEBUG, "already insert an active wifi in the top of wifi list"); + } else { + wifiListWidget->resize(W_LIST_WIDGET+173*isTabletStyle, wifiListWidget->height() + H_NORMAL_ITEM); + + OneConnForm *ocf = new OneConnForm(wifiListWidget, this, confForm, ksnm); + connect(ocf, SIGNAL(selectedOneWifiForm(QString,int)), this, SLOT(oneWifiFormSelected(QString,int))); + connect(ocf,&OneConnForm::onLineEditClicked, this, &KylinNM::onLineEditClicked); + QString path = line.mid(indexPath).trimmed(); + QString m_name = this->objKyDBus->getWifiSsid(QString("/org/freedesktop/NetworkManager/AccessPoint/%1").arg(path.mid(path.lastIndexOf("/") + 1))); +// ocf->setName(m_name); + if (m_name.isEmpty() || m_name == "") { + ocf->setName(wname); + } else { + ocf->setName(m_name); + } + //ocf->setRate(wrate); + ocf->setLine(true); + ocf->setSignal(wsignal, wsecu); + objKyDBus->getWifiMac(wname); + ocf->setWifiInfo(wsecu, wsignal, objKyDBus->dbusWifiMac); + ocf->setConnedString(0, tr("Disconnected"), wsecu); + ocf->move(L_VERTICAL_LINE_TO_ITEM, j * H_NORMAL_ITEM); + ocf->setSelected(false, false); + ocf->show(); + + j ++; + count ++; + } + + wnames.append(wname); + } + } + QList itemList = wifiListWidget->findChildren(); + int n = itemList.size(); + if (n >= 1) { + OneConnForm *lastItem = itemList.at(n-1); + lastItem->setLine(false); + lbNoItemTip->hide(); + } else { + if (ifWLanConnected) { + lbNoItemTip->show(); + lbNoItemTip->setText(tr("No Other Wireless Network Scheme")); + } else { + lbNoItemTip->hide(); + lbTopWifiList->hide(); +// btnAddNet->hide(); + } + } + + this->lanListWidget->hide(); + this->topLanListWidget->hide(); + this->wifiListWidget->show(); + this->topWifiListWidget->show(); + + this->stopLoading(); + is_stop_check_net_state = 0; + + actWifiBssidList.clear(); + wnames.clear(); +} + +// 更新wifi列表 +void KylinNM::updateWifiListDone(QStringList slist) +{ + if (this->ksnm->isExecutingGetLanList){ return;} + + //获取表头信息 + QString lastHeadLine = oldWifiSlist.at(0); + //int lastIndexName = lastHeadLine.indexOf("SSID"); + int lastIndexName, lastIndexPath; + lastHeadLine = lastHeadLine.trimmed(); + bool isChineseInIt = lastHeadLine.contains(QRegExp("[\\x4e00-\\x9fa5]+")); + if (isChineseInIt) { + lastIndexName = lastHeadLine.indexOf("BSSID") + 6 + 19; + } else { + lastIndexName = lastHeadLine.indexOf("BSSID") + 19; + } + lastIndexPath = lastHeadLine.indexOf("DBUS-PATH"); + + QString headLine = slist.at(0); + int indexSecu, indexFreq, indexBSsid, indexName, indexPath; + headLine = headLine.trimmed(); + bool isChineseExist = headLine.contains(QRegExp("[\\x4e00-\\x9fa5]+")); + if (isChineseExist) { + indexSecu = headLine.indexOf("安全性"); + indexFreq = headLine.indexOf("频率") + 4; + indexBSsid = headLine.indexOf("BSSID") + 6; + //indexName = headLine.indexOf("SSID") + 6; + indexName = indexBSsid + 19; + indexPath = headLine.indexOf("DBUS-PATH"); + } else { + indexSecu = headLine.indexOf("SECURITY"); + indexFreq = headLine.indexOf("FREQ"); + indexBSsid = headLine.indexOf("BSSID"); + indexName = indexBSsid + 19; + indexPath = headLine.indexOf("DBUS-PATH"); + } + + //列表中去除已经减少的wifi + for (int i=1; i wifiList = wifiListWidget->findChildren(); + for (int pos = 0; pos < wifiList.size(); pos ++) { + OneConnForm *ocf = wifiList.at(pos); + if (ocf->getName() == lastWname) { + if (ocf->isActive == true){break; + } else { + delete ocf; + //删除元素下面的的所有元素上移 + for (int after_pos = pos+1; after_pos < wifiList.size(); after_pos ++) { + OneConnForm *after_ocf = wifiList.at(after_pos); + if (lastWname == currSelNetName) {after_ocf->move(L_VERTICAL_LINE_TO_ITEM, after_ocf->y() - H_NORMAL_ITEM - H_WIFI_ITEM_BIG_EXTEND);} + else {after_ocf->move(L_VERTICAL_LINE_TO_ITEM, after_ocf->y() - H_NORMAL_ITEM);} + } + wifiListWidget->resize(W_LIST_WIDGET+173*isTabletStyle, wifiListWidget->height() - H_NORMAL_ITEM); + break; + } + } + } + + } //end if (j == slist.size()-1) + } //end (int j=1; j wifiList = wifiListWidget->findChildren(); + int n = wifiList.size(); + int posY = 0; + if (n >= 1) { + OneConnForm *lastOcf = wifiList.at(n-1); + lastOcf->setLine(true); + if (lastOcf->wifiName == currSelNetName) { + posY = lastOcf->y()+H_NORMAL_ITEM + H_WIFI_ITEM_BIG_EXTEND; + } else { + posY = lastOcf->y()+H_NORMAL_ITEM; + } + } + + wifiListWidget->resize(W_LIST_WIDGET+173*isTabletStyle, wifiListWidget->height() + H_NORMAL_ITEM); + OneConnForm *addItem = new OneConnForm(wifiListWidget, this, confForm, ksnm); + connect(addItem, SIGNAL(selectedOneWifiForm(QString,int)), this, SLOT(oneWifiFormSelected(QString,int))); + connect(addItem,&OneConnForm::onLineEditClicked, this, &KylinNM::onLineEditClicked); + QString path = slist.at(i).mid(indexPath).trimmed(); + QString m_name = this->objKyDBus->getWifiSsid(QString("/org/freedesktop/NetworkManager/AccessPoint/%1").arg(path.mid(path.lastIndexOf("/") + 1))); + if (m_name.isEmpty() || m_name == "") { + addItem->setName(wname); + } else { + addItem->setName(m_name); + } + addItem->setLine(false); + addItem->setSignal(wsignal, wsecu); + objKyDBus->getWifiMac(wname); + addItem->setWifiInfo(wsecu, wsignal, objKyDBus->dbusWifiMac); + addItem->setConnedString(0, tr("Disconnected"), wsecu);//"未连接" + addItem->move(L_VERTICAL_LINE_TO_ITEM, posY); + addItem->setSelected(false, false); + addItem->show(); + + count += 1; + } + } + } + + this->lanListWidget->hide(); + this->topLanListWidget->hide(); + this->wifiListWidget->show(); + this->topWifiListWidget->show(); + this->stopLoading(); +} + + +/////////////////////////////////////////////////////////////////////////////// +//主窗口其他按钮点击响应 + +void KylinNM::on_btnAdvConf_clicked() +{ + QProcess *qprocess = new QProcess(this); + qprocess->start("nm-connection-editor &"); + // int status = system("nm-connection-editor &"); + // if (status != 0){ syslog(LOG_ERR, "execute 'nm-connection-editor &' in function 'on_btnAdvConf_clicked' failed");} +} + +void KylinNM::on_btnAdvConf_pressed() +{ + //ui->lbBtnConfBG->setStyleSheet(btnOnQss); +} + +void KylinNM::on_btnAdvConf_released() +{ + //ui->lbBtnConfBG->setStyleSheet(btnOffQss); +} + +void KylinNM::on_btnFlyMode_clicked() +{ + if (is_fly_mode_on == 0) { + ui->lbFlyImg->setStyleSheet("QLabel{background-image:url(:/res/x/fly-mode-on.svg);}"); + ui->lbFlyBG->setStyleSheet(btnOnQss); + is_fly_mode_on = 1; + + onBtnWifiClicked(0); + on_btnWifiList_clicked(); + } else { + ui->lbFlyImg->setStyleSheet("QLabel{background-image:url(:/res/x/fly-mode-off.svg);}"); + ui->lbFlyBG->setStyleSheet(btnOffQss); + is_fly_mode_on = 0; + } +} + +void KylinNM::on_btnHotspot_clicked() +{ + if (is_wireless_adapter_ready == 1) { + if (is_hot_sopt_on == 0) { + ui->lbHotImg->setStyleSheet("QLabel{background-image:url(:/res/x/hot-spot-on.svg);}"); + ui->lbHotBG->setStyleSheet(btnOnQss); + is_hot_sopt_on = 1; + + QApplication::setQuitOnLastWindowClosed(false); + DlgHotspotCreate *hotCreate = new DlgHotspotCreate(objKyDBus->dbusWiFiCardName); + connect(hotCreate,SIGNAL(updateHotspotList()),this,SLOT(on_btnWifiList_clicked() )); + connect(hotCreate,SIGNAL(btnHotspotState()),this,SLOT(on_btnHotspotState() )); + hotCreate->show(); + } else { + on_btnHotspotState(); + + BackThread objBT; + objBT.disConnLanOrWifi("wifi"); + + sleep(2); + on_btnWifiList_clicked(); + } + } +} + +void KylinNM::onBtnAddNetClicked() +{ +// QApplication::setQuitOnLastWindowClosed(false); + DlgConnHidWifi *connHidWifi = new DlgConnHidWifi(0, this, this->parentWidget()); + connect(connHidWifi, SIGNAL(reSetWifiList() ), this, SLOT(on_btnWifiList_clicked()) ); + connHidWifi->show(); +} + +void KylinNM::onBtnCreateNetClicked() +{ + QPoint pos = QCursor::pos(); + QRect primaryGeometry; + for (QScreen *screen : qApp->screens()) { + if (screen->geometry().contains(pos)) { + primaryGeometry = screen->geometry(); + } + } + + if (primaryGeometry.isEmpty()) { + primaryGeometry = qApp->primaryScreen()->geometry(); + } + + ConfForm *m_cf = new ConfForm(this->parentWidget()); + m_cf->setMainWindow(this); + m_cf->cbTypeChanged(3); + m_cf->move(primaryGeometry.width() / 2 - m_cf->width() / 2, primaryGeometry.height() / 2 - m_cf->height() / 2); + m_cf->show(); +} + + +/////////////////////////////////////////////////////////////////////////////// +//处理窗口变化、网络状态变化 + +//列表中item的扩展与收缩 +void KylinNM::oneLanFormSelected(QString lanName, QString uniqueName) +{ + QList topLanList = topLanListWidget->findChildren(); + QList lanList = lanListWidget->findChildren(); + + //**********************先处理下方列表********************// + // 下方所有元素回到原位 + for (int i = 0, j = 0;i < lanList.size(); i ++) { + OneLancForm *ocf = lanList.at(i); + if (ocf->isActive == true) { + ocf->move(L_VERTICAL_LINE_TO_ITEM, 0); + } + if (ocf->isActive == false) { + ocf->move(L_VERTICAL_LINE_TO_ITEM, j * H_NORMAL_ITEM); + j ++; + } + } + + //是否与上一次选中同一个网络框 + if (currSelNetName == uniqueName) { + // 缩小所有选项卡 + for (int i = 0;i < lanList.size(); i ++) { + OneLancForm *ocf = lanList.at(i); + if (ocf->uniqueName == uniqueName) { + ocf->setSelected(false, true); + } else { + ocf->setSelected(false, false); + } + } + + currSelNetName = ""; + } else { + int selectY = 0; + for (int i = 0;i < lanList.size(); i ++) { + OneLancForm *ocf = lanList.at(i); + if (ocf->uniqueName == uniqueName) { + selectY = ocf->y(); //获取选中item的y坐标 + break; + } + } + + // 选中元素下面的所有元素下移 H_LAN_ITEM_EXTEND + for (int i = 0;i < lanList.size(); i ++) { + OneLancForm *ocf = lanList.at(i); + if (ocf->y() > selectY) { + ocf->move(L_VERTICAL_LINE_TO_ITEM, ocf->y() + H_LAN_ITEM_EXTEND); + } + } + + for (int i = 0;i < lanList.size(); i ++) { + OneLancForm *ocf = lanList.at(i); + if (ocf->uniqueName == uniqueName) { + ocf->setSelected(true, false); + selectY = ocf->y(); + } else { + ocf->setSelected(false, false); + } + } + + currSelNetName = uniqueName; + } + + QList itemList = lanListWidget->findChildren(); + int n = itemList.size(); + if (n >= 1) { + OneLancForm *lastItem = itemList.at(n-1); + lastItem->setLine(false); + } + + //**********************处理上方列表-界面所有控件回原位********************// + topLanListWidget->resize(W_TOP_LIST_WIDGET, H_NORMAL_ITEM + H_GAP_UP + X_ITEM); // 顶部的item缩小 + lbTopLanList->move(X_MIDDLE_WORD, H_NORMAL_ITEM + H_GAP_UP); +// btnCreateNet->move(X_BTN_FUN, Y_BTN_FUN); + scrollAreal->move(W_LEFT_AREA, Y_SCROLL_AREA); + lbNoItemTip->move(this->width()/2 - W_NO_ITEM_TIP/2 + W_LEFT_AREA/2, this->height()/2); + + OneLancForm *ocf = topLanList.at(0); + ocf->setTopItem(false); +} +void KylinNM::oneTopLanFormSelected(QString lanName, QString uniqueName) +{ + currSelNetName = uniqueName; +} + +void KylinNM::oneWifiFormSelected(QString wifiName, int extendLength) +{ + QListtopWifiList = topWifiListWidget->findChildren(); + QList wifiList = wifiListWidget->findChildren(); + + //******************先处理下方列表****************// + // 下方所有元素回到原位 + for (int i = 0, j = 0;i < wifiList.size(); i ++) { + OneConnForm *ocf = wifiList.at(i); + if (ocf->isActive == true) { + ocf->move(L_VERTICAL_LINE_TO_ITEM, 0); + } + if (ocf->isActive == false) { + ocf->move(L_VERTICAL_LINE_TO_ITEM, j * H_NORMAL_ITEM); + j ++; + } + } + + //是否与上一次选中同一个网络框 + if (currSelNetName == wifiName) { + // 缩小所有选项卡 + for (int i = 0;i < wifiList.size(); i ++) { + OneConnForm *ocf = wifiList.at(i); + if (ocf->wifiName == wifiName) { + if (ocf->wifiName == hideWiFiConn) { + ocf->setHideItem(true, true); + } else { + ocf->setSelected(false, true); + } + } else { + if (ocf->wifiName == hideWiFiConn) { + ocf->setHideItem(true, true); + } else { + ocf->setSelected(false, false); + } + } + + } + currSelNetName = ""; + } else { + int selectY = 0; + for (int i = 0;i < wifiList.size(); i ++) { + OneConnForm *ocf = wifiList.at(i); + if (ocf->wifiName == wifiName) { + selectY = ocf->y(); //获取选中item的y坐标 + this->scrollAreaw->verticalScrollBar()->setValue(selectY); + break; + } + } + + // 选中元素下面的所有元素下移 H_WIFI_ITEM_BIG_EXTEND + for (int i = 0;i < wifiList.size(); i ++) { + OneConnForm *ocf = wifiList.at(i); + if (ocf->y() > selectY) { + ocf->move(L_VERTICAL_LINE_TO_ITEM, ocf->y() + extendLength); + } + } + + for (int i = 0;i < wifiList.size(); i ++) { + OneConnForm *ocf = wifiList.at(i); + if (ocf->wifiName == wifiName) { + if (ocf->wifiName == hideWiFiConn) { + ocf->setHideItem(true, true); + } else { + ocf->setSelected(true, false); + } + } else { + if (ocf->wifiName == hideWiFiConn) { + ocf->setHideItem(true, true); + } else { + ocf->setSelected(false, false); + } + } + } + + currSelNetName = wifiName; + } + + //最后一个item没有下划线 + QList itemList = wifiListWidget->findChildren(); + int n = itemList.size(); + if (n >= 1) { + OneConnForm *lastItem = itemList.at(n-1); + lastItem->setLine(false); + } + + //********************处理上方列表-界面所有控件回原位******************// + // 顶部的item缩小 + topWifiListWidget->resize(W_TOP_LIST_WIDGET+194*isTabletStyle, H_NORMAL_ITEM + H_GAP_UP + X_ITEM); + lbTopWifiList->move(X_MIDDLE_WORD, H_NORMAL_ITEM + H_GAP_UP); +// btnAddNet->move(X_BTN_FUN, Y_BTN_FUN); + scrollAreaw->move(W_LEFT_AREA, Y_SCROLL_AREA); + lbNoItemTip->move(this->width()/2 - W_NO_ITEM_TIP/2 + W_LEFT_AREA/2, this->height()/2); + + OneConnForm *ocf = topWifiList.at(0); + ocf->setTopItem(false); +} +void KylinNM::oneTopWifiFormSelected(QString wifiName, int extendLength) +{ + QListtopWifiList = topWifiListWidget->findChildren(); + QList wifiList = wifiListWidget->findChildren(); + + if (currSelNetName == wifiName) { + // 与上一次选中同一个网络框,缩小当前选项卡 + topWifiListWidget->resize(W_TOP_LIST_WIDGET+194*isTabletStyle, H_NORMAL_ITEM + H_GAP_UP + X_ITEM); + lbTopWifiList->move(X_MIDDLE_WORD, H_NORMAL_ITEM + H_GAP_UP); +// btnAddNet->move(X_BTN_FUN, Y_BTN_FUN); + scrollAreaw->move(W_LEFT_AREA, Y_SCROLL_AREA); + lbNoItemTip->move(this->width()/2 - W_NO_ITEM_TIP/2 + W_LEFT_AREA/2, this->height()/2); + + OneConnForm *ocf = topWifiList.at(0); + ocf->setTopItem(false); + + currSelNetName = ""; + } else { + // 没有与上一次选中同一个网络框,放大当前选项卡 + + for(int i = 0;i < wifiList.size(); i ++) { + // 所有元素回到原位 + OneConnForm *ocf = wifiList.at(i); + ocf->setSelected(false, false); + ocf->move(L_VERTICAL_LINE_TO_ITEM, i * H_NORMAL_ITEM); + } + + topWifiListWidget->resize(W_TOP_LIST_WIDGET+194*isTabletStyle, H_NORMAL_ITEM + H_WIFI_ITEM_BIG_EXTEND + H_GAP_UP + X_ITEM); + lbTopWifiList->move(X_MIDDLE_WORD, H_NORMAL_ITEM + H_WIFI_ITEM_BIG_EXTEND + H_GAP_UP); +// btnAddNet->move(X_BTN_FUN, Y_BTN_FUN + H_WIFI_ITEM_BIG_EXTEND); + scrollAreaw->move(W_LEFT_AREA, Y_SCROLL_AREA + H_WIFI_ITEM_BIG_EXTEND); + lbNoItemTip->move(this->width()/2 - W_NO_ITEM_TIP/2 + W_LEFT_AREA/2, this->height()/2 + 65); + + OneConnForm *ocf = topWifiList.at(0); + ocf->setTopItem(true); + + currSelNetName = wifiName; + } +} + +//断开网络处理 +void KylinNM::activeLanDisconn() +{ + syslog(LOG_DEBUG, "Wired net is disconnected"); + + QString txt(tr("Wired net is disconnected")); + objKyDBus->showDesktopNotify(txt); + //QString cmd = "export LANG='en_US.UTF-8';export LANGUAGE='en_US';notify-send '" + txt + "...' -t 3800"; + //int status1 = system(cmd.toUtf8().data()); + //if (status1 != 0){ syslog(LOG_ERR, "execute 'notify-send' in function 'execConnWifiPWD' failed");} + + currSelNetName = ""; + //this->startLoading(); + emit this->waitLanStop(); + this->ksnm->execGetLanList(); + QTimer::singleShot(200, this, &KylinNM::onConnectChanged); +} + +void KylinNM::activeWifiDisconn() +{ + QThread *tt = new QThread(); + BackThread *btt = new BackThread(); + btt->moveToThread(tt); + connect(tt, SIGNAL(finished()), tt, SLOT(deleteLater())); + connect(this, SIGNAL(disConnSparedNet(QString)), btt, SLOT(disConnSparedNetSlot(QString)),Qt::DirectConnection); + connect(btt, SIGNAL(disFinish()), this, SLOT(activeGetWifiList()), Qt::DirectConnection); + connect(btt, SIGNAL(ttFinish()), tt, SLOT(quit()),Qt::DirectConnection); + + tt->start(); + activeStartLoading(); + QTimer::singleShot(200, this, &KylinNM::onConnectChanged); +} + +void KylinNM::reflashWifiUi() +{ + mutexIsReflashWifi.lock(); + m_is_reflashWifiUi = true; + mutexIsReflashWifi.unlock(); +} + +void KylinNM::activeStartLoading() +{ + syslog(LOG_DEBUG, "Wi-Fi is disconnected"); + emit this->disConnSparedNet("wifi"); +} +void KylinNM::activeGetWifiList() +{ + emit this->waitWifiStop(); + this->ksnm->execGetWifiList(); +} + +//网络开关处理,打开与关闭网络 +void KylinNM::enNetDone() +{ + BackThread *bt = new BackThread(); + mwBandWidth = bt->execChkLanWidth(lname); + + ui->lbBtnNetBG->setStyleSheet(btnOnQss); + + // 打开网络开关时如果Wifi开关是打开的,设置其样式 + if (checkWlOn()) { + btnWireless->setSwitchStatus(true); + //ui->lbBtnWifiBG->setStyleSheet(btnBgOnQss); + //ui->lbBtnWifiBall->move(X_RIGHT_WIFI_BALL, Y_WIFI_BALL); + } + + onBtnNetListClicked(1); + is_stop_check_net_state = 0; + + qDebug()<<"debug: already turn on the switch of lan network"; + syslog(LOG_DEBUG, "Already turn on the switch of lan network"); +} +void KylinNM::disNetDone() +{ + this->is_btnNetList_clicked = 1; + this->is_btnWifiList_clicked = 0; + + ui->lbNetListBG->setStyleSheet(btnOnQss); + ui->lbWifiListBG->setStyleSheet(btnOffQss); + + ui->lbNetwork->setText("Activated LAN"); + btnWireless->hide(); + //ui->lbBtnWifiBG->hide(); + //ui->lbBtnWifiBall->hide(); + + delete topLanListWidget; // 清空top列表 + createTopLanUI(); //创建顶部有线网item + + // 清空lan列表 + lanListWidget = new QWidget(scrollAreal); + lanListWidget->resize(W_LIST_WIDGET, H_NORMAL_ITEM + H_LAN_ITEM_EXTEND); + scrollAreal->setWidget(lanListWidget); + scrollAreal->move(W_LEFT_AREA, Y_SCROLL_AREA); + + // 当前连接的lan + OneLancForm *ccf = new OneLancForm(topLanListWidget, this, confForm, ksnm); + ccf->setName(tr("Not connected"), "");//"当前未连接任何 以太网" + ccf->setIcon(false); + ccf->setConnedString(1, tr("Disconnected"));//"未连接" + ccf->isConnected = false; + ccf->setTopItem(false); + ccf->setAct(true); + ccf->move(L_VERTICAL_LINE_TO_ITEM, 0); + ccf->show(); + + ui->lbBtnNetBG->setStyleSheet(btnOffQss); + + btnWireless->setSwitchStatus(false); + + this->lanListWidget->show(); + this->wifiListWidget->hide(); + this->scrollAreal->show(); + this->topLanListWidget->show(); + this->scrollAreaw->hide(); + this->topWifiListWidget->hide(); + + on_btnNetList_pressed(); + + qDebug()<<"debug: already turn off the switch of lan network"; + syslog(LOG_DEBUG, "Already turn off the switch of lan network"); + + this->stopLoading(); + QTimer::singleShot(200, this, &KylinNM::onConnectChanged); +} +void KylinNM::launchLanDone() +{ + ui->lbBtnNetBG->setStyleSheet(btnOnQss); +} + +void KylinNM::enWifiDone() +{ + //ui->lbBtnWifiBG->setStyleSheet(btnBgOnQss); + //ui->lbBtnWifiBall->move(X_RIGHT_WIFI_BALL, Y_WIFI_BALL); + + is_update_wifi_list = 0; + if (is_btnWifiList_clicked) { + this->ksnm->execGetWifiList(); + } else { + on_btnWifiList_clicked(); + } + + qDebug()<<"debug: already turn on the switch of wifi network"; + syslog(LOG_DEBUG, "Already turn on the switch of wifi network"); +} +void KylinNM::disWifiDone() +{ + disWifiDoneChangeUI(); + + on_btnWifiList_pressed(); + + qDebug()<<"debug: already turn off the switch of wifi network"; + syslog(LOG_DEBUG, "Already turn off the switch of wifi network"); + + this->stopLoading(); + is_stop_check_net_state = 0; + QTimer::singleShot(200, this, &KylinNM::onConnectChanged); +} +void KylinNM::disWifiStateKeep() +{ + if (this->is_btnNetList_clicked == 1) { + btnWireless->setSwitchStatus(false); + //ui->lbBtnWifiBG->setStyleSheet(btnBgOffQss); + //ui->lbBtnWifiBall->move(X_LEFT_WIFI_BALL, Y_WIFI_BALL); + } + if (this->is_btnWifiList_clicked== 1) { + disWifiDoneChangeUI(); + + // this->stopLoading(); + getActiveInfo(); + } +} +void KylinNM::disWifiDoneChangeUI() +{ + wifiListWidget = new QWidget(scrollAreaw); + wifiListWidget->resize(W_LIST_WIDGET+173*isTabletStyle, H_WIFI_ITEM_BIG_EXTEND); + scrollAreaw->setWidget(wifiListWidget); + scrollAreaw->move(W_LEFT_AREA, Y_SCROLL_AREA); + + lbTopWifiList->move(X_MIDDLE_WORD, H_NORMAL_ITEM + H_GAP_UP); +// btnAddNet->move(X_BTN_FUN, Y_BTN_FUN); + topWifiListWidget->resize(W_TOP_LIST_WIDGET+194*isTabletStyle, H_NORMAL_ITEM + H_GAP_UP + X_ITEM); + + QList wifiList = topWifiListWidget->findChildren(); + for (int i = 0; i < wifiList.size(); i ++) { + OneConnForm *ocf = wifiList.at(i); + if (ocf->isActive == true) { + ocf->setSelected(false, false); + ocf->setName(tr("Not connected"));//"当前未连接任何 Wifi" + ocf->setSignal("0", "--"); + ocf->setConnedString(1, tr("Disconnected"), "");//"未连接" + lbLoadDown->hide(); + lbLoadUp->hide(); + lbLoadDownImg->hide(); + lbLoadUpImg->hide(); + ocf->isConnected = false; + ocf->setTopItem(false); + disconnect(ocf, SIGNAL(selectedOneWifiForm(QString,int)), this, SLOT(oneTopWifiFormSelected(QString,int))); + } else { + ocf->deleteLater(); + } + } + + btnWireless->setSwitchStatus(false); + //ui->lbBtnWifiBG->setStyleSheet(btnBgOffQss); + //ui->lbBtnWifiBall->move(X_LEFT_WIFI_BALL, Y_WIFI_BALL); + + this->lanListWidget->hide(); + this->topLanListWidget->hide(); + this->wifiListWidget->show(); + this->topWifiListWidget->show(); + this->scrollAreal->hide(); + this->scrollAreaw->show(); +} + +void KylinNM::on_btnHotspotState() +{ + ui->lbHotImg->setStyleSheet("QLabel{background-image:url(:/res/x/hot-spot-off.svg);}"); + ui->lbHotBG->setStyleSheet(btnOffQss); + is_hot_sopt_on = 0; +} + +//处理外界对网络的连接与断开 +void KylinNM::onExternalConnectionChange(QString type) +{ + qDebug() <is_btnWifiList_clicked==1 && this->isVisible()) { + BackThread *loop_bt = new BackThread(); + IFace *loop_iface = loop_bt->execGetIface(); + + if (loop_iface->wstate != 2) { + is_update_wifi_list = 1; + this->ksnm->execGetWifiList(); //更新wifi列表 + } + + delete loop_iface; + loop_bt->deleteLater(); + } +} + +void KylinNM::on_setNetSpeed() +{ + if (this->isVisible() && is_stop_check_net_state==0) { + if (is_btnWifiList_clicked == 1) { + if ( objNetSpeed->getCurrentDownloadRates(objKyDBus->dbusWiFiCardName.toUtf8().data(), &start_rcv_rates, &start_tx_rates) == -1) { + start_rcv_rates = end_rcv_rates; + } + } else if(is_btnNetList_clicked == 1) { + if ( objNetSpeed->getCurrentDownloadRates(objKyDBus->dbusLanCardName.toUtf8().data(), &start_rcv_rates, &start_tx_rates) == -1) { + start_tx_rates = end_tx_rates; + } + } + + long int delta_rcv = (start_rcv_rates - end_rcv_rates)/800; + long int delta_tx = (start_tx_rates - end_tx_rates)/8; + if (delta_rcv>=10000 || delta_rcv<0){delta_rcv = 0;} + if (delta_tx>=10000 || delta_tx<0){delta_tx = 0;} + + int rcv_num = delta_rcv/3; + int tx_num = delta_tx/3; + + QString str_rcv; + QString str_tx; + + if (rcv_num < 1000) { + str_rcv = QString::number(rcv_num) + "KB/s."; + } else { + int remainder; + if (rcv_num%1000 < 100) { + remainder = 0; + } else { + remainder = (rcv_num%1000)/100; + } + str_rcv = QString::number(rcv_num/1000) + "." + QString::number(remainder) + "MB/s."; + } + + if (tx_num < 1000) { + str_tx = QString::number(tx_num) + "KB/s"; + } else { + int remainder; + if (tx_num%1000 < 100) { + remainder = 0; + } else { + remainder = (tx_num%1000)/100; + } + str_tx = QString::number(tx_num/1000) + "." + QString::number(remainder) + "MB/s"; + } + + lbLoadDown->setText(str_rcv); + lbLoadUp->setText(str_tx); + + switch (str_rcv.size()) { + case 6: + lbLoadUp->move(X_ITEM + 187, Y_TOP_ITEM + 32); + lbLoadUpImg->move(X_ITEM + 170, Y_TOP_ITEM + 35); + break; + case 7: + lbLoadUp->move(X_ITEM + 194, Y_TOP_ITEM + 32); + lbLoadUpImg->move(X_ITEM + 176, Y_TOP_ITEM + 35); + break; + case 8: + lbLoadUp->move(X_ITEM + 199, Y_TOP_ITEM + 32); + lbLoadUpImg->move(X_ITEM + 186, Y_TOP_ITEM + 35); + break; + default: + break; + } + + end_rcv_rates = start_rcv_rates; + end_tx_rates = start_tx_rates; + } +} + +void KylinNM::connLanDone(int connFlag) +{ + emit this->waitLanStop(); //停止加载动画 + + // Lan连接结果,0点击连接成功 1失败 3开机启动网络工具时已经连接 + if (connFlag == 0) { + syslog(LOG_DEBUG, "Wired net already connected by clicking button"); + this->is_wired_line_ready = 1; + this->is_by_click_connect = 1; + this->ksnm->execGetLanList(); + + QString txt(tr("Conn Ethernet Success")); + objKyDBus->showDesktopNotify(txt); + //QString cmd = "export LANG='en_US.UTF-8';export LANGUAGE='en_US';notify-send '" + txt + "' -t 3800"; + //int status = system(cmd.toUtf8().data()); + //if (status != 0){ syslog(LOG_ERR, "execute 'notify-send' in function 'connLanDone' failed");} + } + + if (connFlag == 1) { + qDebug()<<"without net line connect to computer"; + this->is_wired_line_ready = 0; //without net line connect to computer + is_stop_check_net_state = 0; + + QString txt(tr("Conn Ethernet Fail")); + objKyDBus->showDesktopNotify(txt); + //QString cmd = "export LANG='en_US.UTF-8';export LANGUAGE='en_US';notify-send '" + txt + "' -t 3800"; + //int status = system(cmd.toUtf8().data()); + //if (status != 0){ syslog(LOG_ERR, "execute 'notify-send' in function 'connLanDone' failed");} + } + + if (connFlag == 3) { + syslog(LOG_DEBUG, "Launch kylin-nm, Lan already connected"); + this->is_wired_line_ready = 1; + } + + this->stopLoading(); + QTimer::singleShot(200, this, &KylinNM::onConnectChanged); +} + +void KylinNM::connWifiDone(int connFlag) +{ + emit this->waitWifiStop(); //停止加载动画 + // Wifi连接结果,0点击连接成功 1失败 2没有配置文件 3开机启动网络工具时已经连接 + if (connFlag == 0) { + syslog(LOG_DEBUG, "Wi-Fi already connected by clicking button"); + this->is_by_click_connect = 1; + this->ksnm->execGetWifiList(); + + QString txt(tr("Conn Wifi Success")); + objKyDBus->showDesktopNotify(txt); + //QString cmd = "export LANG='en_US.UTF-8';export LANGUAGE='en_US';notify-send '" + txt + "' -t 3800"; + //int status = system(cmd.toUtf8().data()); + //if (status != 0){ syslog(LOG_ERR, "execute 'notify-send' in function 'connWifiDone' failed");} + } else if (connFlag == 1) { + is_stop_check_net_state = 0; + is_connect_wifi_failed = 1; + + QString txt(tr("Confirm your Wi-Fi password or usable of wireless card")); + objKyDBus->showDesktopNotify(txt); + //QString cmd = "export LANG='en_US.UTF-8';export LANGUAGE='en_US';notify-send '" + txt + "...' -t 3800"; + //int status1 = system(cmd.toUtf8().data()); + //if (status1 != 0){ syslog(LOG_ERR, "execute 'notify-send' in function 'execConnWifiPWD' failed");} + } else if (connFlag == 3) { + syslog(LOG_DEBUG, "Launch kylin-nm, Wi-Fi already connected"); + } + QTimer::singleShot(200, this, &KylinNM::onConnectChanged); +} + +int KylinNM::getConnectStatus() +{ + int ret = -1; + QString actLanName = "--"; + QString actWifiName = "--"; + + activecon *act = kylin_network_get_activecon_info(); + int index = 0; + while (act[index].con_name != NULL) { + if (QString(act[index].type) == "ethernet" || QString(act[index].type) == "802-3-ethernet") { + actLanName = QString(act[index].con_name); + } + if (QString(act[index].type) == "wifi" || QString(act[index].type) == "802-11-wireless") { + actWifiName = QString(act[index].con_name); + } + index ++; + } + + //ukui3.0中获取currentActWifiSignalLv的值 + if (activeWifiSignalLv > 75) { + currentActWifiSignalLv = 1; + } else if(activeWifiSignalLv > 55 && activeWifiSignalLv <= 75) { + currentActWifiSignalLv = 2; + } else if(activeWifiSignalLv > 35 && activeWifiSignalLv <= 55) { + currentActWifiSignalLv = 3; + } else if( activeWifiSignalLv <= 35) { + currentActWifiSignalLv = 4; + } + + // 设置图标 + if (actLanName != "--") { + ret = 0; + } else if (actWifiName != "--") { + ret = 1; + } else { + ret = -1; + } + + return ret; +} diff --git a/KylinNM/src/kylinnm.h b/KylinNM/src/kylinnm.h new file mode 100644 index 0000000..3ed9ce6 --- /dev/null +++ b/KylinNM/src/kylinnm.h @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "oneconnform.h" +#include "onelancform.h" +#include "hot-spot/dlghotspotcreate.h" +#include "wireless-security/dlgconnhidwifi.h" + +#define W_LEFT_AREA 16 +//#define W_VERTICAL_LINE 1 //左边竖线宽度 +#define W_RIGHT_AREA 438 //41 + 1 + 438 = 480 +#define L_VERTICAL_LINE_TO_ITEM 4 //竖线到item左侧的距离 + +#define X_LEFT_WIFI_BALL 416 //白色小球在左边 +#define X_RIGHT_WIFI_BALL 440 //白色小球在右边 +#define Y_WIFI_BALL 23 //白色小球y坐标 +#define X_ITEM 46 //item到窗口左侧的距离 41 + 1 + 4 = 46 +#define W_ITEM 424 + +#define Y_TOP_ITEM 100 //顶部item、topLanListWidget、topWifiListWidget的y坐标 +#define H_NORMAL_ITEM 56 +#define H_GAP_UP -5 +#define H_MIDDLE_WORD 46 //"显示‘可用网络列表’的label" +#define H_GAP_DOWN 5 //57 + 60 + 10 + 46 + 5 = 178 +#define X_MIDDLE_WORD 0 +#define W_MIDDLE_WORD 260 + +#define H_LAN_ITEM_EXTEND 102 //162 - 60 +#define H_WIFI_ITEM_BIG_EXTEND 90 //150 - 60 +#define H_WIFI_ITEM_SMALL_EXTEND 100 //156 - 56 + +#define Y_SCROLL_AREA 192//列表纵坐标 +#define W_SCROLL_AREA 414 +//#define H_SCROLL_AREA 200 +#define H_SCROLL_AREA H_NORMAL_ITEM*4 //平板中去掉了添加新网络按钮,设置为4倍的item,防止最后一栏被遮挡 + +#define W_TOP_LIST_WIDGET 435 +#define W_LIST_WIDGET 414 + +#define W_BTN_FUN 128 +#define H_BTN_FUN 24 + +#define X_BTN_FUN 36 +#define Y_BTN_FUN 400 //新建网络,加入网络按钮的宽高、x坐标、y坐标 + +#define W_NO_ITEM_TIP 220 +#define H_NO_ITEM_TIP 20 + +class OneConnForm; +class ConfForm; + +namespace Ui { +class KylinNM; +} + +class KylinNM : public QWidget +{ + Q_OBJECT + +public Q_SLOTS: + void onPhysicalCarrierChanged(bool flag); + void onCarrierUpHandle(); + void onCarrierDownHandle(); + void onDeleteLan(); + void onNetworkDeviceAdded(QDBusObjectPath objPath); + void onNetworkDeviceRemoved(QDBusObjectPath objPath); + void getLanBandWidth(); + + void onExternalConnectionChange(QString type); + void onExternalLanChange(); + void onExternalWifiChange(); + void onExternalWifiSwitchChange(bool wifiEnabled); + + void oneLanFormSelected(QString lanName, QString uniqueName); + void oneTopLanFormSelected(QString lanName, QString uniqueName); + void oneWifiFormSelected(QString wifiName, int extendLength); + void oneTopWifiFormSelected(QString wifiName, int extendLength); + + void on_btnHotspot_clicked(); + void on_btnHotspotState(); + + //flag =0或1为普通点击、2为收到打开信息、3为收到关闭信息、4为无线网卡插入、5为无线网卡拔出 + void onBtnWifiClicked(int flag = 0); + + void connLanDone(int connFlag); + void connWifiDone(int connFlag); + + void activeGetWifiList(); + +protected: + bool eventFilter(QObject *obj, QEvent *event); + +public: + explicit KylinNM(QWidget *parent = 0); + ~KylinNM(); + + void onSwipeGesture(int dx, int dy); + void editQssString(); + void createTopLanUI(); + void createTopWifiUI(); + void createOtherUI(); + void createListAreaUI(); + void createLeftAreaUI(); + + void startLoading(); + void stopLoading(); + + void setTrayIcon(QIcon icon); + void setTrayLoading(bool isLoading); + void getActiveInfo(); + + void initTimer(); + void checkIsWirelessDeviceOn(); + void updateNetList(); + void updateWifiList(); + + int getConnectStatus();//获取网络连接状态,返回值 -1 无连接; 0 有线连接; 1无线连接; 2有线和无线都已连接 + + QIcon iconLanOnline, iconLanOffline; + QIcon iconWifiFull, iconWifiHigh, iconWifiMedium, iconWifiLow; + QIcon iconConnecting; + QList loadIcons; + QString mwBandWidth; + KylinDBus *objKyDBus = nullptr; + NetworkSpeed *objNetSpeed = nullptr; + SwitchButton *btnWireless; + + QStringList lanNameList; + + //状态设置,0为假,1为真 + int is_update_wifi_list = 0; //是否是update wifi列表,而不是load wifi列表 + int is_by_click_connect = 0; //是否是通过点击连接按钮进行的连接 + int is_btnNetList_clicked = 1; //是否处于有线网界面 + int is_btnWifiList_clicked = 0; //是否处于无线网界面 + int is_wired_line_ready = 1; //主机是否连接网线 + int is_wireless_adapter_ready = 1; //主机是否插入无线网卡 + int is_keep_wifi_turn_on_state = 1; //是否要执行wifi开关变为打开样式 + int is_stop_check_net_state = 0; //是否要在进行其他操作时停止检查网络状态 + int is_connect_wifi_failed = 0; //刚才是否连接wifi失败 + int is_fly_mode_on = 0; //是否已经打开飞行模式 + int is_hot_sopt_on = 0; //是否已经打开热点 + + QString currSelNetName = ""; //当前ScrollArea中选中的网络名称 + int currSelNetNum = 0; //当前选中的item序号 + + bool isTabletStyle=false;//YYF 平板桌面模式 + + static void reflashWifiUi(); + static bool m_is_reflashWifiUi; +private: + void tabletStyle();//YYF 平板桌面模式特有设置 + + void checkSingle(); + void initNetwork(); + void createTrayIcon(); + void handleIconClicked(); + void showTrayIconMenu(); + bool checkLanOn(); + bool checkWlOn(); + void getLanList(); + void getWifiList(); + void getInitLanSlist(); + + Ui::KylinNM *ui; + + LoadingDiv *loading = nullptr; + + QDesktopWidget desktop; + KSimpleNM *ksnm = nullptr; + ConfForm *confForm = nullptr; + QWidget *topLanListWidget = nullptr; + QWidget *topWifiListWidget = nullptr; + QWidget *lanListWidget = nullptr; + QWidget *wifiListWidget = nullptr; + QWidget *optWifiWidget = nullptr; + + QLabel *lbLoadDown = nullptr; + QLabel *lbLoadDownImg = nullptr; + QLabel *lbLoadUp = nullptr; + QLabel *lbLoadUpImg = nullptr; + + QLabel *lbNoItemTip = nullptr; + bool ifLanConnected; + bool ifWLanConnected; + + QScrollArea *scrollAreal = nullptr; + QScrollArea *scrollAreaw = nullptr; + QLabel *lbTopLanList = nullptr; + QLabel *lbTopWifiList = nullptr; + QLabel *lbLanList = nullptr; + QLabel *lbWifiList = nullptr; + QPushButton *btnAddNet = nullptr; + QPushButton *btnCreateNet = nullptr; + + QSystemTrayIcon *trayIcon = nullptr; + QMenu *trayIconMenu = nullptr; + QAction *mShowWindow = nullptr; + QAction *mAdvConf = nullptr; + QWidget *widShowWindow = nullptr; + QWidget *widAdvConf = nullptr; + + QString lname, wname; // 以太网卡和无线网卡名称 + + QString btnOffQss, btnOnQss, btnBgOffQss, btnBgOnQss, btnBgHoverQss, btnBgLeaveQss; // 主界面按钮底色 + QString scrollBarQss, leftBtnQss, funcBtnQss; + + QStringList oldLanSlist; //上一次获取Lan列表 + + QStringList oldWifiSlist; //上一次获取wifi列表 + + //平板 + QLabel *lbNetListText=nullptr;//有线网络 + QLabel *lbWifiListText=nullptr;//无线网络 + + + //循环检测网络连接状态 + QTimer *iconTimer = nullptr; + QTimer *wiredCableUpTimer = nullptr; + QTimer *wiredCableDownTimer = nullptr; + QTimer *deleteLanTimer = nullptr; + QTimer *checkWifiListChanged = nullptr; + QTimer *checkIfLanConnect = nullptr; + QTimer *checkIfWifiConnect = nullptr; + QTimer *checkIfNetworkOn = nullptr; + QTimer *setNetSpeed = nullptr; + + int currentIconIndex; + int activeWifiSignalLv; + + long int start_rcv_rates = 0; //保存开始时的流量计数 + long int end_rcv_rates = 0; //保存结束时的流量计数 + long int start_tx_rates = 0; //保存开始时的流量计数 + long int end_tx_rates = 0; //保存结束时的流量计数 + + QString actWifissid = "--";//当前连接wifi的ssid + QString actWifiuuid = "--";//当前连接wifi的uuid + QStringList actWifiBssidList; //当前连接wifi的bssid + +private Q_SLOTS: + void iconActivated(QSystemTrayIcon::ActivationReason reason); + + bool nativeEvent(const QByteArray &eventType, void *message, long *result); + + void on_btnNet_clicked(); + void on_btnWifiList_clicked(); + void onBtnNetListClicked(int flag=0); + + void getLanListDone(QStringList slist); + void getWifiListDone(QStringList slist); + void loadWifiListDone(QStringList slist); + void updateWifiListDone(QStringList slist); + + void on_showWindowAction(); + void on_btnAdvConf_clicked(); + void on_btnNetList_pressed(); + void on_btnWifiList_pressed(); + + void activeLanDisconn(); + void activeWifiDisconn(); + void activeStartLoading(); + void on_btnAdvConf_pressed(); + void on_btnAdvConf_released(); + void on_checkWifiListChanged(); + void on_setNetSpeed(); + void on_checkOverTime(); + + // 后台回调 + void enNetDone(); + void disNetDone(); + void enWifiDone(); + void launchLanDone(); + void disWifiDone(); + void disWifiStateKeep(); + void disWifiDoneChangeUI(); + + void iconStep(); + void on_btnFlyMode_clicked(); + + void onBtnAddNetClicked(); + void onBtnCreateNetClicked(); + +Q_SIGNALS: + void disConnSparedNet(QString type); + + void waitWifiStop(); + void waitLanStop(); + void onLineEditClicked(); + void onConnectChanged(); // 网络连接变化时发送该信号 +}; + +#endif diff --git a/KylinNM/src/kylinnm.ui b/KylinNM/src/kylinnm.ui new file mode 100644 index 0000000..a3aee92 --- /dev/null +++ b/KylinNM/src/kylinnm.ui @@ -0,0 +1,296 @@ + + + KylinNM + + + + 0 + 0 + 441 + 443 + + + + kylin-nm + + + + + 0 + 0 + 442 + 437 + + + + + + 1 + 200 + 37 + 37 + + + + + + + + + + 2 + 200 + 37 + 37 + + + + + + + + + + 385 + 73 + 50 + 24 + + + + + + + + + + 400 + 400 + 37 + 37 + + + + + + + + + + 2 + 348 + 37 + 37 + + + + + + + + + + 2 + 395 + 37 + 37 + + + + + + + + + + 16 + 72 + 171 + 20 + + + + + + + + + + 413 + 75 + 20 + 20 + + + + + + + + + + 11 + 359 + 19 + 19 + + + + + + + + + + 11 + 405 + 19 + 19 + + + + + + + + + + 0 + 0 + 37 + 37 + + + + + + + + + + 221 + 0 + 37 + 37 + + + + + + + + + + 63 + 17 + 24 + 24 + + + + + + + + + + 288 + 16 + 24 + 24 + + + + + + + + + + 2 + 348 + 37 + 37 + + + + + + + + + + 2 + 395 + 37 + 37 + + + + + + + + + + 0 + 0 + 221 + 56 + + + + + + + + + + 221 + 0 + 221 + 56 + + + + + + + + + + 409 + 409 + 19 + 19 + + + + + + + btnConfImg + lbFlyBG + lbHotBG + lbFlyImg + lbHotImg + lbBtnNetBG + btnNet + lbBtnWifiBG + btnAdvConf + btnHotspot + btnFlyMode + lbBtnWifiBall + lbNetListBG + lbWifiListBG + lbWifiListImg + lbNetListImg + btnNetList + btnWifiList + lbNetwork + + + + + + diff --git a/KylinNM/src/loadingdiv.cpp b/KylinNM/src/loadingdiv.cpp new file mode 100644 index 0000000..bf1b0c4 --- /dev/null +++ b/KylinNM/src/loadingdiv.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see resize(480, 538); + + this->loadingGif = new QLabel(this); + this->loadingGif->resize(96, 96); + this->loadingGif->move(this->width()/2 - 96/2 + 41/2, this->height()/2 + 20); + this->loadingGif->show(); + + this->switchTimer = new QTimer(this); //QTimer对象,控制等待动画播放 + connect(switchTimer, SIGNAL(timeout()), this, SLOT(switchAnimStep())); + + this->raise(); + this->hide(); +} + +//加载动画控件'loadingGif' +void LoadingDiv::switchAnimStep() +{ + QString qpmQss = "QLabel{background-image:url(':/res/s/conning-b/"; + qpmQss.append(QString::number(this->currentPage)); + qpmQss.append(".png');}"); + loadingGif->setStyleSheet(qpmQss); + + this->currentPage --; + + if (this->currentPage < 1) { + this->currentPage = 12; //循环播放 + } + + this->countCurrentTime += FRAMESPEED; + if (this->countCurrentTime >= ALLTIME) { + emit this->toStopLoading(); //发出信号停止主界面和托盘区的等待动画 + } +} + +//开始播放动画 +void LoadingDiv::startLoading() +{ + this->currentPage = 12; + this->countCurrentTime = 0; + this->switchTimer->start(FRAMESPEED); + this->show(); +} + +//结束播放动画 +void LoadingDiv::stopLoading() +{ + this->switchTimer->stop(); + this->hide(); +} diff --git a/KylinNM/src/loadingdiv.h b/KylinNM/src/loadingdiv.h new file mode 100644 index 0000000..f8ee9e3 --- /dev/null +++ b/KylinNM/src/loadingdiv.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include +#include +#include +#include +#include + +class LoadingDiv : public QWidget +{ + Q_OBJECT +public: + explicit LoadingDiv(QWidget *parent = nullptr); + +Q_SIGNALS: + void toStopLoading(); + +public Q_SLOTS: + void switchAnimStep(); + void startLoading(); + void stopLoading(); + +private: + QLabel *loadingGif = nullptr; + QTimer *switchTimer = nullptr; + + int currentPage; + int countCurrentTime; +}; + +#endif // LOADINGDIV_H diff --git a/KylinNM/src/main.cpp b/KylinNM/src/main.cpp new file mode 100644 index 0000000..0929685 --- /dev/null +++ b/KylinNM/src/main.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include +#include +#include +#include +#include + +#define LOG_IDENT "ukui_kylin_nm" + + +int main(int argc, char *argv[]) +{ + //QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + Display *disp = XOpenDisplay(NULL); + Screen *scrn = DefaultScreenOfDisplay(disp); + if (NULL == scrn) { + return 0; + } + int width = scrn->width; + + if (width > 2560) { + #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) + QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); + #endif + } + if (NULL != disp) { + XCloseDisplay(disp); + } + + QApplication a(argc, argv); + + + openlog(LOG_IDENT, LOG_NDELAY | LOG_NOWAIT | LOG_PID, LOG_USER); + + syslog(LOG_DEBUG, "Kylin Network Manager Is Already Launched"); + + // Internationalization + QString locale = QLocale::system().name(); + QTranslator trans_global; + if (locale == "zh_CN") { + trans_global.load(":/translations/kylin-nm_zh_CN.qm"); + //trans_global.load(":/translations/kylin-nm_bo.qm"); + a.installTranslator(&trans_global); + } + if (locale == "tr_TR") { + trans_global.load(":/translations/kylin-nm_tr.qm"); + a.installTranslator(&trans_global); + } + + KylinNM w; + return a.exec(); +} diff --git a/KylinNM/src/oneconnform.cpp b/KylinNM/src/oneconnform.cpp new file mode 100644 index 0000000..9a82926 --- /dev/null +++ b/KylinNM/src/oneconnform.cpp @@ -0,0 +1,939 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include +#include +#include +#include +#include +#include + +extern int currentActWifiSignalLv; + +OneConnForm::OneConnForm(QWidget *parent, KylinNM *mainWindow, ConfForm *confForm, KSimpleNM *ksnm) : + QWidget(parent), + ui(new Ui::OneConnForm) +{ + ui->setupUi(this); + + ui->btnConnSub->setText(tr("Connect"));//"设置" + ui->btnConn->setText(tr("Connect"));//"连接" + ui->btnConnPWD->setText(tr("Connect"));//"连接" + ui->btnDisConn->setText(tr("Disconnect"));//"断开连接" + ui->btnHideConn->setText(tr("Connect"));//"连接" + ui->lePassword->setPlaceholderText(tr("Input Password..."));//"输入密码..." + ui->lePassword->setContextMenuPolicy(Qt::NoContextMenu); + ui->lePassword->setTextMargins(0,0,20,0); + + ui->lbConned->setAlignment(Qt::AlignLeft); + ui->lePassword->setEchoMode(QLineEdit::Normal); + ui->btnConnPWD->setEnabled(false); + + leQssLow = "QLineEdit{border:none;background:transparent;font-size:14px;color:rgba(255,255,255,0.57);font-family:Noto Sans CJK SC;}"; + leQssHigh = "QLineEdit{border:none;background:transparent;font-size:14px;color:rgba(255,255,255,0.91);font-family:Noto Sans CJK SC;}"; + + ui->leInfo_1->setStyleSheet(leQssLow); + ui->leInfo_2->setStyleSheet(leQssLow); + ui->leInfo_3->setStyleSheet(leQssLow); + ui->leInfo_1->setEnabled(false); + ui->leInfo_2->setEnabled(false); + ui->leInfo_3->setEnabled(false); + ui->btnInfo->setStyleSheet("QPushButton{border:none;background:transparent;}"); + ui->wbg->setStyleSheet("#wbg{border-radius:8px;background-color:rgba(255,255,255,0);}"); + ui->wbg_2->setStyleSheet("#wbg_2{border-radius:8px;background-color:rgba(38, 38, 38, 0.1)}"); + ui->wbg_3->setStyleSheet("#wbg_3{border-radius:4px;background-color:rgba(255,255,255,0.1);}"); + ui->lbName->setStyleSheet("QLabel{font-size:14px;color:rgba(38,38,38,1);}"); + ui->lbConned->setStyleSheet("QLabel{font-size:14px;color:rgba(255,255,255,0.57);}"); + ui->lePassword->setStyleSheet("QLineEdit{border:2px solid rgba(47, 179, 232, 1);border-radius:4px;" + "background:rgba(255,255,255,1);color:rgba(38, 38, 38, 1);font-size:14px;}"); + ui->checkBoxPwd->setStyleSheet("QCheckBox::indicator {width: 18px; height: 9px;}" + "QCheckBox::indicator:checked {image: url(:/res/h/show-pwd.png);}" + "QCheckBox::indicator:unchecked {image: url(:/res/h/hide-pwd.png);}"); + ui->btnConnSub->setStyleSheet("QPushButton{border:0px;border-radius:4px;background-color:rgba(61,107,229,1);color:white;font-size:14px;}" + "QPushButton:Hover{border:0px solid rgba(255,255,255,0.2);border-radius:4px;background-color:rgba(107,142,235,1);}" + "QPushButton:Pressed{border-radius:4px;background-color:rgba(50,87,202,1);}"); + ui->btnConn->setStyleSheet("QPushButton{border:0px;border-radius:4px;background-color:rgba(61,107,229,1);color:white;font-size:14px;}" + "QPushButton:Hover{border:0px solid rgba(255,255,255,0.2);border-radius:4px;background-color:rgba(107,142,235,1);}" + "QPushButton:Pressed{border-radius:4px;background-color:rgba(50,87,202,1);}"); + ui->btnConnPWD->setStyleSheet("QPushButton{border:0px;border-radius:4px;background-color:rgba(47, 179, 232, 1);color:rgba(255, 255, 255, 1);font-size:14px;}" + "QPushButton:Hover{border:0px solid rgba(255,255,255,0.2);border-radius:4px;background-color:rgba(70, 193, 243, 1);}" + "QPushButton:Pressed{border-radius:4px;background-color:rgba(27, 160, 213, 1);}" + "QPushButton:Disabled{border-radius:4px;background-color:rgba(168, 168, 168, 1)}"); + ui->btnDisConn->setStyleSheet("#btnDisConn{font-family: NotoSansCJKsc-Regular, NotoSansCJKsc;" + "font-weight: 400;color: rgba(38,38,38,0.75);line-height: 20px;font-size:14px;" + "border:0px;background-color:rgba(38,38,38,0.1);border-radius:8px;}" + "#btnDisConn:Hover{border:0px solid rgba(255,255,255,0.2);border-radius:8px;background-color:rgba(38,38,38,0.2);}" + "#btnDisConn:Pressed{border-radius:8px;background-color:rgba(38,38,38,0.06);}");//断开链接按钮 + ui->btnInfo_2->setStyleSheet("border:0px;border-radius:8px;border-image:url(:/res/l/pb-network-info.png);"); + ui->btnHideConn->setStyleSheet("QPushButton{border:0px;border-radius:4px;background-color:rgba(61,107,229,1);color:white;font-size:14px;}" + "QPushButton:Hover{border:0px solid rgba(255,255,255,0.2);border-radius:4px;background-color:rgba(107,142,235,1);}" + "QPushButton:Pressed{border-radius:4px;background-color:rgba(50,87,202,1);}"); + ui->lbWaiting->setStyleSheet("QLabel{border:0px;border-radius:10px;background-color:rgba(61,107,229,1);}"); + ui->lbWaitingIcon->setStyleSheet("QLabel{border:0px;background-color:transparent;}"); + ui->autoConn->setStyleSheet("QCheckBox::indicator {width: 16px; height: 16px;}" + "QCheckBox::indicator:checked {image: url(:/res/g/checkbox-checked.svg);}" + "QCheckBox::indicator:unchecked {image: url(:/res/g/checkbox-unchecked.svg);}"); + + ui->btnInfo->setCursor(QCursor(Qt::PointingHandCursor)); + ui->btnInfo->setFocusPolicy(Qt::NoFocus); + ui->checkBoxPwd->setFocusPolicy(Qt::NoFocus); + ui->btnConnSub->setFocusPolicy(Qt::NoFocus); + ui->btnConn->setFocusPolicy(Qt::NoFocus); + ui->btnConnPWD->setFocusPolicy(Qt::NoFocus); + ui->btnDisConn->setFocusPolicy(Qt::NoFocus); + ui->btnHideConn->setFocusPolicy(Qt::NoFocus); + + ui->wbg->show(); + ui->wbg_2->hide(); + ui->wbg_3->hide(); + ui->lbName->show(); + ui->leInfo_1->hide(); + ui->leInfo_2->hide(); + ui->leInfo_3->hide(); + ui->lePassword->hide(); + ui->checkBoxPwd->hide(); + ui->checkBoxPwd->setChecked(false); + ui->lePassword ->setEchoMode(QLineEdit::Password); + ui->btnConnSub->hide(); + ui->btnConn->hide(); + ui->btnDisConn->hide(); + ui->btnConnPWD->hide(); + ui->btnHideConn->hide(); + ui->line->show(); + ui->autoConn->hide(); + ui->lbWaiting->hide(); + ui->lbWaitingIcon->hide(); + ui->btnInfo->hide(); + + this->mw = mainWindow; + this->cf = confForm; + this->ks = ksnm; + + this->isSelected = false; + this->isActive = false; + + this->setAttribute(Qt::WA_Hover,true); + this->installEventFilter(this); + ui->lePassword->setAttribute(Qt::WA_Hover,true); + ui->lePassword->installEventFilter(this); + ui->btnInfo->setAttribute(Qt::WA_Hover,true); + ui->btnInfo->installEventFilter(this); + + //m_notify = new NotifySend(); + + connect(ui->lePassword, SIGNAL(returnPressed()), this, SLOT(on_btnConnPWD_clicked())); + ui->btnConn->setShortcut(Qt::Key_Return);//将字母区回车键与登录按钮绑定在一起 + + this->waitTimer = new QTimer(this); + connect(waitTimer, SIGNAL(timeout()), this, SLOT(waitAnimStep())); + + connect(mw, SIGNAL(waitWifiStop()), this, SLOT(stopWaiting())); + connect(ui->btnInfo_2,&QPushButton::clicked,this,&OneConnForm::on_btnInfo_2_clicked); + ui->btnInfo_2->hide(); + // connect(ui->autoConn,&QCheckBox::toggled,this,&OneConnForm::on_autoConn_toggled); + + ui->autoConn->setText(tr("Automatically join the network")); +// ui->autoConn->setText(tr("自动连接")); + + ui->lbWaitingIcon->move(316+194*mw->isTabletStyle,20);// YYF + tabletStyle();//平板模式PC桌面样式 +} + +void OneConnForm::tabletStyle()//平板桌面模式特有设置 +{ + this->setFixedWidth(414+194*mw->isTabletStyle); + ui->wbg->setFixedWidth(414+194*mw->isTabletStyle); + ui->wbg_2->setFixedWidth(414+194*mw->isTabletStyle); + ui->wbg_3->setFixedWidth(414+194*mw->isTabletStyle); + ui->lbName->setFixedWidth(190+194*mw->isTabletStyle); + ui->btnInfo->setFixedWidth(230+194*mw->isTabletStyle); + ui->leInfo_1->setFixedWidth(230+194*mw->isTabletStyle); + ui->leInfo_2->setFixedWidth(230+194*mw->isTabletStyle); + ui->leInfo_3->setFixedWidth(230+194*mw->isTabletStyle); + ui->lePassword->setFixedWidth(254+172*mw->isTabletStyle); + ui->checkBoxPwd->move(286+172*mw->isTabletStyle,75); + ui->btnConnPWD->move(318+172*mw->isTabletStyle,56); + ui->btnDisConn->move(316+194*mw->isTabletStyle,8 + 18*mw->isTabletStyle); + ui->btnInfo_2->move(364+194*mw->isTabletStyle,18); + ui->lbName->move(63,18); +} + +OneConnForm::~OneConnForm() +{ + delete ui; +} + +void OneConnForm::mousePressEvent(QMouseEvent *event) +{ + if (event->button() != Qt::LeftButton) { + event->ignore(); + return; + } + // emit selectedOneWifiForm(wifiName, H_WIFI_ITEM_BIG_EXTEND); + ui->wbg_2->setStyleSheet("#wbg_2{border-radius:8px;background-color:rgba(38,38,38,0.1);}"); +} + +void OneConnForm::mouseReleaseEvent(QMouseEvent *event) +{ + if (event->button() != Qt::LeftButton || !rect().contains(event->pos())) { + event->ignore(); + return; + } + if(ui->lePassword->isVisible()){ + Q_EMIT selectedOneWifiForm(wifiName, H_WIFI_ITEM_BIG_EXTEND); + }else{ + if (!this->isTopItem) { + if (!is_connecting) { + toConnectWirelessNetwork(); + } + } + } + ui->wbg_2->setStyleSheet("#wbg_2{border-radius:8px;background-color:rgba(255,255,255,0);}"); +} + +//事件过滤器 +bool OneConnForm::eventFilter(QObject *obj, QEvent *event) +{ + if (obj == ui->btnInfo) { + if (event->type() == QEvent::HoverEnter) { + ui->leInfo_1->setStyleSheet(leQssHigh); + ui->leInfo_2->setStyleSheet(leQssHigh); + ui->leInfo_3->setStyleSheet(leQssHigh); + return true; + } else if(event->type() == QEvent::HoverLeave) { + ui->leInfo_1->setStyleSheet(leQssLow); + ui->leInfo_2->setStyleSheet(leQssLow); + ui->leInfo_3->setStyleSheet(leQssLow); + return true; + } + } else if (obj == this) { + if(event->type() == QEvent::HoverEnter) { + if (!this->isTopItem) { + if (!this->isSelected) { + //ui->btnConn->show(); + ui->wbg->setStyleSheet("#wbg{border-radius:8px;background-color:rgba(38,38,38,0.05);}"); + ui->wbg->show(); + } + } + return true; + } else if (event->type() == QEvent::HoverLeave) { + ui->btnConn->hide(); + ui->wbg->setStyleSheet("#wbg{border-radius:8px;background-color:rgba(255,255,255,0);}"); + ui->wbg->hide(); + return true; + } + } + + if (obj == ui->lePassword) { + if (event->type() == QEvent::MouseButtonPress) { + Q_EMIT onLineEditClicked(); + } + } + + return QWidget::eventFilter(obj,event); +} + +void OneConnForm::on_autoConn_toggled(bool val) +{ +// kylin_network_set_autoconnect(wifiName.toLatin1().data(),val); +} + +// 是否当前连接的网络,字体设置不同 +void OneConnForm::setAct(bool isAct) +{ + if (isAct) { + ui->lbConned->hide(); + } else { + ui->lbConned->hide(); + } + ui->lbName->move(63,18); + isActive = isAct; +} + +//点击窗口最上面的item时 +void OneConnForm::setTopItem(bool isSelected) +{ + if (isSelected) { + resize(W_ITEM+194*mw->isTabletStyle, H_ITEM_BIG); + ui->wbg_3->show(); + ui->leInfo_1->show(); + ui->leInfo_2->show(); + ui->leInfo_3->show(); + + this->isSelected = true; + } else { + resize(W_ITEM+194*mw->isTabletStyle, H_ITEM); + ui->lePassword->setText(""); + ui->wbg_3->hide(); + ui->leInfo_1->hide(); + ui->leInfo_2->hide(); + ui->leInfo_3->hide(); + + + this->isSelected = false; + } + + ui->wbg->hide(); + ui->wbg_2->hide(); + ui->lbSignal->show(); + ui->lePassword->hide(); + ui->checkBoxPwd->hide(); + ui->line->hide(); + ui->btnConn->hide(); + ui->btnConnPWD->hide(); + ui->btnHideConn->hide(); + ui->btnInfo->show(); + + if (isConnected) { + if (this->isWaiting) { + ui->lbName->setStyleSheet("QLabel{font-size:14px;color:rgba(38,38,38,0.45);}"); + ui->lbSignal->setStyleSheet("QLabel{border-radius:0px;border-image:url(:/res/w/pb-top-wifi-offline.png);}"); + ui->btnDisConn->hide(); +// ui->btnInfo_2->hide(); + } else { + ui->lbName->setStyleSheet("QLabel{font-size:14px;color:rgba(47, 179, 232, 1);}"); + setSignalOn(signalStrong,signalHasPw); + ui->btnDisConn->show(); +// ui->btnInfo_2->show(); + } + ui->btnInfo_2->hide(); + } else { + ui->lbName->setStyleSheet("QLabel{font-size:14px;color:rgba(38,38,38,0.45);}"); + ui->lbSignal->setStyleSheet("QLabel{border-radius:0px;border-image:url(:/res/w/pb-top-wifi-offline.png);}"); + ui->btnDisConn->hide(); + ui->btnInfo_2->hide(); + } + + this->isTopItem = true; +} +// 点击窗口下面的item时 +void OneConnForm::setSelected(bool isSelected, bool isCurrName) +{ + if (isSelected) { + resize(W_ITEM+194*mw->isTabletStyle, H_ITEM_BIG); + ui->line->move(X_LINE_BIG_EXTEND, Y_LINE_BIG_EXTEND); + ui->wbg->hide(); + ui->wbg_2->hide(); + ui->wbg_3->show(); + ui->leInfo_1->show(); + ui->leInfo_2->show(); + ui->leInfo_3->show(); + ui->btnConn->hide(); + ui->btnConnSub->show(); + + this->isSelected = true; + } else { + resize(W_ITEM+194*mw->isTabletStyle, H_ITEM); + ui->lePassword->setStyleSheet("QLineEdit{border:2px solid rgba(47, 179, 232, 1);border-radius:4px;" + "background:rgba(255,255,255,1);color:rgba(38, 38, 38, 1);font-size:14px;}"); + + ui->line->move(X_LINE, Y_LINE); + ui->wbg->show(); + ui->wbg_2->hide(); + ui->wbg_3->hide(); + ui->leInfo_1->hide(); + ui->leInfo_2->hide(); + ui->leInfo_3->hide(); + + if (isCurrName) { + //ui->btnConn->show(); + } else { + ui->btnConn->hide(); + } + ui->btnConnSub->hide(); + + this->isSelected = false; + } + + ui->lePassword->hide(); + ui->checkBoxPwd->hide(); + ui->btnConnPWD->hide(); + ui->lbSignal->show(); + ui->btnDisConn->hide(); + ui->btnHideConn->hide(); + ui->btnInfo->hide(); + + this->isTopItem = false; +} +// 点击连接隐藏wifi的item时 +void OneConnForm::setHideItem(bool isHideItem, bool isShowHideBtn) +{ + if (isHideItem) { + ui->lbName->move(14, 30); + ui->wbg->hide(); + ui->btnConn->hide(); + } else { + ui->lbName->move(62, 18); + ui->wbg->show(); + //ui->btnConn->show(); + } + + if (isShowHideBtn) { + ui->btnHideConn->show(); + } else{ + ui->btnHideConn->hide(); + } +} + +void OneConnForm::setConnedString(bool showLable, QString str, QString str1) +{ + if (!showLable) { + ui->lbConned->setText(str1); + ui->lbConned->hide(); + } else { + ui->lbConned->setText(str); + } + ui->lbName->move(63, 18); +} + +void OneConnForm::setName(QString name) +{ + ui->lbName->setText(name); + wifiName = name; +} +void OneConnForm::setSpecialName(QString name) +{ + ui->lbName->setText(tr("Connect to Hidden Wi-Fi Network")); //连接到隐藏的 Wi-Fi 网络 + wifiName = name; +} + +QString OneConnForm::getName() +{ + return ui->lbName->text(); +} + +void OneConnForm::setRate(QString rate) +{ +// QString txt(tr("Rate"));//"速率" +// this->setToolTip("  " + txt + ": " + rate + "  "); +// this->setToolTip(txt + ":" + rate); +} + +void OneConnForm::setLine(bool isShow) +{ + if (isShow) { + ui->line->show(); + } else { + ui->line->hide(); + } +} + +void OneConnForm::setSignalOn(QString lv, QString secu) +{ + + int signal = lv.toInt(); + if (secu == "--" || secu == "") { + hasPwd = false; + } else { + hasPwd = true; + } + + if (signal > 75) { + if (hasPwd) { + ui->lbSignal->setStyleSheet("QLabel{border-radius:0px;border-image:url(:/res/w/wifi-full-pwd.png);}"); + } else { + ui->lbSignal->setStyleSheet("QLabel{border-radius:0px;border-image:url(:/res/w/wifi-full.png);}"); + } + signalLv = 1; + } + if (signal > 55 && signal <= 75) { + if (hasPwd) { + ui->lbSignal->setStyleSheet("QLabel{border-radius:0px;border-image:url(:/res/w/wifi-high-pwd.png);}"); + } else { + ui->lbSignal->setStyleSheet("QLabel{border-radius:0px;border-image:url(:/res/w/wifi-high.png);}"); + } + signalLv = 2; + } + if (signal > 35 && signal <= 55) { + if (hasPwd) { + ui->lbSignal->setStyleSheet("QLabel{border-radius:0px;border-image:url(:/res/w/wifi-medium-pwd.png);}"); + } else { + ui->lbSignal->setStyleSheet("QLabel{border-radius:0px;border-image:url(:/res/w/wifi-medium.png);}"); + } + signalLv = 3; + } + if (signal > 15 && signal <= 35) { + if (hasPwd) { + ui->lbSignal->setStyleSheet("QLabel{border-radius:0px;border-image:url(:/res/w/wifi-low-pwd.png);}"); + } else { + ui->lbSignal->setStyleSheet("QLabel{border-radius:0px;border-image:url(:/res/w/wifi-low.png);}"); + } + signalLv = 4; + } + if (signal <= 15) { + if (hasPwd) { + ui->lbSignal->setStyleSheet("QLabel{border-radius:0px;border-image:url(:/res/w/wifi-none-pwd.png);}"); + } else { + ui->lbSignal->setStyleSheet("QLabel{border-radius:0px;border-image:url(:/res/w/wifi-none.png);}"); + } + signalLv = 4; + } +} + +void OneConnForm::setSignal(QString lv, QString secu) +{ + signalStrong=lv; + signalHasPw=secu; + + int signal = lv.toInt(); + if (secu == "--" || secu == "") { + hasPwd = false; + } else { + hasPwd = true; + } + + if (signal > 75) { + if (hasPwd) { + ui->lbSignal->setStyleSheet("QLabel{border-radius:0px;border-image:url(:/res/w/wifi-full-pwd-off.png);}"); + } else { + ui->lbSignal->setStyleSheet("QLabel{border-radius:0px;border-image:url(:/res/w/wifi-full-off.png);}"); + } + signalLv = 1; + } + if (signal > 55 && signal <= 75) { + if (hasPwd) { + ui->lbSignal->setStyleSheet("QLabel{border-radius:0px;border-image:url(:/res/w/wifi-high-pwd-off.png);}"); + } else { + ui->lbSignal->setStyleSheet("QLabel{border-radius:0px;border-image:url(:/res/w/wifi-high-off.png);}"); + } + signalLv = 2; + } + if (signal > 35 && signal <= 55) { + if (hasPwd) { + ui->lbSignal->setStyleSheet("QLabel{border-radius:0px;border-image:url(:/res/w/wifi-medium-pwd-off.png);}"); + } else { + ui->lbSignal->setStyleSheet("QLabel{border-radius:0px;border-image:url(:/res/w/wifi-medium-off.png);}"); + } + signalLv = 3; + } + if (signal > 15 && signal <= 35) { + if (hasPwd) { + ui->lbSignal->setStyleSheet("QLabel{border-radius:0px;border-image:url(:/res/w/wifi-low-pwd-off.png);}"); + } else { + ui->lbSignal->setStyleSheet("QLabel{border-radius:0px;border-image:url(:/res/w/wifi-low-off.png);}"); + } + signalLv = 4; + } + if (signal <= 15) { + if (hasPwd) { + ui->lbSignal->setStyleSheet("QLabel{border-radius:0px;border-image:url(:/res/w/wifi-none-pwd-off.png);}"); + } else { + ui->lbSignal->setStyleSheet("QLabel{border-radius:0px;border-image:url(:/res/w/wifi-none-off.png);}"); + } + signalLv = 4; + } +} + +void OneConnForm::setWifiInfo(QString str1, QString str2, QString str3) +{ + if (str1 == "--" || str1 == ""){ str1 = tr("None"); }; + + QString strSecurity = QString(tr("WiFi Security:")); + QString strSignal = QString(tr("Signal:")); + QString strMAC = QString(tr("MAC:")); + + ui->leInfo_1->setText(strSecurity + str1); + ui->leInfo_2->setText(strSignal + str2); + ui->leInfo_3->setText(strMAC + str3); +} + +void OneConnForm::slotConnWifi() +{ + //mw->startLoading(); + this->startWaiting(true); + emit sigConnWifi(ui->lbName->text()); +} +void OneConnForm::slotConnWifiPWD() +{ + //mw->startLoading(); + this->startWaiting(true); + emit sigConnWifiPWD(ui->lbName->text(), ui->lePassword->text()); +} + +//点击后断开wifi网络 +void OneConnForm::on_btnDisConn_clicked() +{ + syslog(LOG_DEBUG, "DisConnect button about wifi net is clicked, current wifi name is %s .", ui->lbName->text().toUtf8().data()); + qDebug()<<"DisConnect button about wifi net is clicked, current wifi name is "<lbName->text(); + + this->startWaiting(false); + + mw->is_stop_check_net_state = 1; + mw->on_btnHotspotState(); + QString name = ui->lbName->text(); + kylin_network_set_con_down(name.replace("\"","\\\"").toUtf8().data()); + disconnect(this, SIGNAL(selectedOneWifiForm(QString,int)), mw, SLOT(oneWifiFormSelected(QString,int))); + emit disconnActiveWifi(); +} + +//点击列表item扩展时会出现该按钮 用于连接网络 +void OneConnForm::on_btnConnSub_clicked() +{ + syslog(LOG_DEBUG, "A button named on_btnConnSub about wifi net is clicked."); + qDebug()<<"A button named on_btnConnSub about wifi net is clicked."; + toConnectWirelessNetwork(); +} + +//无需密码的wifi连接 +void OneConnForm::on_btnConn_clicked() +{ +// syslog(LOG_DEBUG, "A button named btnConn about wifi net is clicked."); +// qDebug()<<"A button named btnConn about wifi net is clicked."; +// toConnectWirelessNetwork(); + on_btnInfo_clicked(); +} + +void OneConnForm::toConnectWirelessNetwork() +{ + if (ui->lbConned->text() == "--" || ui->lbConned->text() == " ") { + if (!isWifiConfExist(ui->lbName->text())) { + //没有配置文件,使用有密码的wifi连接 + on_btnConnPWD_clicked(); + return; + } + } + + mw->is_stop_check_net_state = 1; + QThread *t = new QThread(); + BackThread *bt = new BackThread(); + bt->moveToThread(t); + connect(t, SIGNAL(finished()), t, SLOT(deleteLater())); + connect(t, SIGNAL(started()), this, SLOT(slotConnWifi())); + connect(this, SIGNAL(sigConnWifi(QString)), bt, SLOT(execConnWifi(QString))); + connect(bt, &BackThread::connDone, this,[=](int t) { + mw->connWifiDone(t); + }); + connect(bt, SIGNAL(connDone(int)), this, SLOT(slotConnWifiResult(int))); + connect(bt, SIGNAL(btFinish()), t, SLOT(quit())); + t->start(); + is_connecting = true; +} + +//需要密码的wifi连接 +void OneConnForm::on_btnConnPWD_clicked() +{ + syslog(LOG_DEBUG, "A button named btnConnPWD about wifi net is clicked."); + qDebug()<<"A button named btnConnPWD about wifi net is clicked."; + + ui->btnConnPWD->setStyleSheet("QPushButton{border:0px;border-radius:4px;background-color:rgba(47, 179, 232, 1);color:white;font-size:14px;}" + "QPushButton:Hover{border:0px solid rgba(255,255,255,0.2);border-radius:4px;background-color:rgba(70, 193, 243, 1);}" + "QPushButton:Pressed{border-radius:4px;background-color:rgba(27,160,213,1);}" + "QPushButton:Disabled{border-radius:4px;background-color:#BEBEBE;}"); + ui->btnConnPWD->setEnabled(false); + + //调用kylinnm的接口,屏蔽输错密码框后弹出提示框 + QDBusInterface interface("com.kylin.network", + "/com/kylin/network", + "com.kylin.network", + QDBusConnection::sessionBus()); + QDBusMessage result = interface.call("keyRingClear"); + + mw->is_stop_check_net_state = 1; + QThread *t = new QThread(); + BackThread *bt = new BackThread(); + bt->moveToThread(t); + connect(t, SIGNAL(finished()), t, SLOT(deleteLater())); + connect(t, SIGNAL(started()), this, SLOT(slotConnWifiPWD())); + connect(this, SIGNAL(sigConnWifiPWD(QString, QString)), bt, SLOT(execConnWifiPWD(QString, QString))); + connect(bt, SIGNAL(connDone(int)), mw, SLOT(connWifiDone(int))); + connect(bt, SIGNAL(connDone(int)), this, SLOT(slotConnWifiResult(int))); + connect(bt, SIGNAL(btFinish()), t, SLOT(quit())); + ui->lePassword->setEnabled(false); + t->start(); +} + +//点击后弹出连接隐藏wifi网络窗口 +void OneConnForm::on_btnHideConn_clicked() +{ +// QApplication::setQuitOnLastWindowClosed(false); + DlgConnHidWifi *connHidWifi = new DlgConnHidWifi(0, mw, this->parentWidget()); + connect(connHidWifi, SIGNAL(reSetWifiList() ), mw, SLOT(on_btnWifiList_clicked()) ); + connHidWifi->show(); +} + +bool OneConnForm::isWifiConfExist(QString netName) +{ + //dbusWifiMac = ""; //这个函数之前是用来获取已经连接的wifi的MAC地址 + + QDBusInterface m_interface("org.freedesktop.NetworkManager", + "/org/freedesktop/NetworkManager/Settings", + "org.freedesktop.NetworkManager.Settings", + QDBusConnection::systemBus() ); + QDBusReply> m_reply = m_interface.call("ListConnections"); + + QList m_objNets = m_reply.value(); + foreach (QDBusObjectPath objNet, m_objNets){ + QDBusInterface m_interface("org.freedesktop.NetworkManager", + objNet.path(), + "org.freedesktop.NetworkManager.Settings.Connection", + QDBusConnection::systemBus()); + QDBusMessage reply = m_interface.call("GetSettings"); + const QDBusArgument &dbusArg = reply.arguments().at( 0 ).value(); + QMap> map; + dbusArg >> map; + + for(QString key : map.keys() ){ + QMap innerMap = map.value(key); + if (key == "connection") { + for (QString inner_key : innerMap.keys()) { + if (inner_key == "id"){ + if (netName == innerMap.value(inner_key).toString()) { + return true; + } + } + } + } + } + + } // end foreach (QDBusObjectPath objNet, m_objNets) + + return false; +} + +//设置密码隐藏或可见 +void OneConnForm::on_checkBoxPwd_stateChanged(int arg1) +{ + if (arg1 == 0) { + ui->lePassword ->setEchoMode(QLineEdit::Password); + } else { + ui->lePassword->setEchoMode(QLineEdit::Normal); + } +} + +void OneConnForm::on_lePassword_textEdited(const QString &arg1) +{ + ui->lePassword->setStyleSheet("QLineEdit{border:2px solid rgba(47, 179, 232, 1);border-radius:4px;" + "background:rgba(255,255,255,1);color:rgba(38, 38, 38, 1);font-size:14px;}"); + + if (ui->lePassword->text().size() < 8){ + ui->btnConnPWD->setStyleSheet("QPushButton{border:0px;border-radius:4px;background-color:rgba(47, 179, 232, 1);color:white;font-size:14px;}" + "QPushButton:Hover{border:0px solid rgba(255,255,255,0.2);border-radius:4px;background-color:rgba(70, 193, 243, 1);}" + "QPushButton:Pressed{border-radius:4px;background-color:rgba(27,160,213,1);}" + "QPushButton:Disabled{border-radius:4px;background-color:rgba(168, 168, 168, 1)}"); + ui->btnConnPWD->setEnabled(false);//连接时设置为不可点击 + if (ui->lePassword->text().size() == 0){ + ui->lePassword->setStyleSheet("QLineEdit{border:2px solid rgba(47, 179, 232, 1);border-radius:4px;" + "background:rgba(255,255,255,1);color:red;font-size:14px;}"); + } + } else { + ui->btnConnPWD->setStyleSheet("QPushButton{border:0px;border-radius:4px;background-color:rgba(47, 179, 232, 1);color:white;font-size:14px;}" + "QPushButton:Hover{border:0px solid rgba(255,255,255,0.2);border-radius:4px;background-color:rgba(70,193,243,1);}" + "QPushButton:Pressed{border-radius:4px;background-color:rgba(27,160,213,1);}"); + ui->btnConnPWD->setEnabled(true); + } +} + +void OneConnForm::on_btnInfo_2_clicked(){ + on_btnInfo_clicked(); +} + +void OneConnForm::on_btnInfo_clicked() +{ + QPoint pos = QCursor::pos(); + QRect primaryGeometry; + for (QScreen *screen : qApp->screens()) { + if (screen->geometry().contains(pos)) { + primaryGeometry = screen->geometry(); + } + } + + if (primaryGeometry.isEmpty()) { + primaryGeometry = qApp->primaryScreen()->geometry(); + } + + BackThread *bt = new BackThread(); + QString connProp = bt->getConnProp(ui->lbName->text()); + QStringList propList = connProp.split("|"); + QString v4method, addr, mask, gateway, dns; + foreach (QString line, propList) { + if (line.startsWith("method:")) { + v4method = line.split(":").at(1); + } + if (line.startsWith("addr:")) { + addr = line.split(":").at(1); + } + if (line.startsWith("mask:")) { + mask = line.split(":").at(1); + } + if (line.startsWith("gateway:")) { + gateway= line.split(":").at(1); + } + if (line.startsWith("dns:")) { + dns = line.split(":").at(1); + } + } + // qDebug()<<"v4method:"<setProp(ui->lbName->text(), v4method, addr, mask, gateway, dns, this->isActive); + + cf->move(primaryGeometry.width() / 2 - cf->width() / 2, primaryGeometry.height() / 2 - cf->height() / 2); + cf->show(); + cf->raise(); + + bt->deleteLater(); +} + +// Wifi连接结果,0成功 1失败 2没有配置文件 +void OneConnForm::slotConnWifiResult(int connFlag) +{ + mw->connWifiDone(connFlag); + qDebug()<<"Function slotConnWifiResult receives a number: "<currSelNetName = ""; + emit selectedOneWifiForm(ui->lbName->text(), H_WIFI_ITEM_SMALL_EXTEND); + + resize(W_ITEM, H_ITEM_MIDDLE); + ui->wbg->hide(); + ui->wbg_2->show(); + ui->wbg_3->hide(); + ui->leInfo_1->hide(); + ui->leInfo_2->hide(); + ui->leInfo_3->hide(); + ui->btnHideConn->hide(); + ui->btnDisConn->hide(); + ui->btnConn->hide(); + ui->btnConnSub->hide(); + // ui->line->move(X_LINE_SMALL_EXTEND, Y_LINE_SMALL_EXTEND); + + ui->lePassword->show(); + ui->lePassword->setFocus(); + Q_EMIT onLineEditClicked(); + ui->checkBoxPwd->show(); + ui->btnConnPWD->show(); + ui->autoConn->show(); + ui->autoConn->setChecked(true); + this->isSelected = true; + } + + if (connFlag == 1) { + // 使用配置文件连接失败,需要删除该配置文件 + QString txt(tr("Conn Wifi Failed"));//"连接 Wifi 失败" + syslog(LOG_DEBUG, "Try to connect wifi named %s, but failed, will delete it's configuration file", ui->lbName->text().toUtf8().data()); + + //YYF 20200922 +// ui->lePassword->setText(""); + + KylinDBus kylindbus; + kylindbus.showDesktopNotify(txt); + //QString cmd = "export LANG='en_US.UTF-8';export LANGUAGE='en_US';nmcli connection delete '" + ui->lbName->text() + "';notify-send '" + txt + "...' -t 3800"; + QString name = ui->lbName->text(); + QString cmd = "export LANG='en_US.UTF-8';export LANGUAGE='en_US';nmcli connection delete \"" + name.replace("\"","\\\"") + "\""; + int status = system(cmd.toUtf8().data()); + if (status != 0) { + syslog(LOG_ERR, "execute 'nmcli connection delete' in function 'slotConnWifiResult' failed"); + } +// ui->btnConnPWD->setStyleSheet("QPushButton{border:0px;border-radius:4px;background-color:rgba(47, 179, 232, 1);color:white;font-size:14px;}" +// "QPushButton:Hover{border:0px solid rgba(255,255,255,0.2);border-radius:4px;background-color:rgba(70,193,243,1);}" +// "QPushButton:Pressed{border-radius:4px;background-color:rgba(27,160,213,1);}"); +// ui->btnConnPWD->setEnabled(true); + ui->lePassword->setEnabled(true); + ui->lePassword->setFocus(); + Q_EMIT onLineEditClicked(); + ui->lePassword->selectAll(); + ui->lePassword->setStyleSheet("QLineEdit{border:2px solid rgba(255, 0, 0, 0.5);border-radius:4px;" + "background:palette(base);color:rgba(38, 38, 38, 1);font-size:14px;" + "selection-background-color: rgba(255, 0, 0, 0.5);}"); + } + if (connFlag == 0){ + //连接成功,配置是否自启动 + //若含中文的QString直接转换,会出现乱码,使用如下转换方式解决此问题(必须分两句转换) + QString wifi_name = wifiName; + wifi_name.replace("\"","\\\""); + QByteArray ba = wifi_name.toLocal8Bit(); + char *name = ba.data(); + if (ui->autoConn->isVisible()) { + //仅当首次连接配置时执行此操作 + kylin_network_set_autoconnect(name,ui->autoConn->isChecked()); + } + } + // 设置全局变量,当前连接Wifi的信号强度 + currentActWifiSignalLv = signalLv; + + this->stopWaiting(); + is_connecting = false; + //if (connFlag != 0){ + // mw->stopLoading(); + //} +} + +void OneConnForm::waitAnimStep() +{ + //YYF 平板模式设计稿中没有此图标 +// QString qpmQss = "QLabel{background-image:url(':/res/s/conning-a/"; +// qpmQss.append(QString::number(this->waitPage)); +// qpmQss.append(".png');}"); +// ui->lbWaitingIcon->setStyleSheet(qpmQss); + +// this->waitPage --; +// if (this->waitPage < 1) { +// this->waitPage = TOTAL_PAGE; //循环播放8张图片 +// } + +// this->countCurrentTime += FRAME_SPEED; +// if (this->countCurrentTime >= LIMIT_TIME) { +// QString cmd = "kill -9 $(pidof nmcli)"; //杀掉当前正在进行的有关nmcli命令的进程 +// int status = system(cmd.toUtf8().data()); +// if (status != 0) { +// qDebug()<<"execute 'kill -9 $(pidof nmcli)' in function 'waitAnimStep' failed"; +// syslog(LOG_ERR, "execute 'kill -9 $(pidof nmcli)' in function 'waitAnimStep' failed"); +// } + +// this->stopWaiting(); //动画超出时间限制,强制停止动画 + +// mw->is_stop_check_net_state = 0; +// } +} + +void OneConnForm::startWaiting(bool isConn) +{ + //YYF 平板模式设计稿中没有此图标 +// this->isWaiting = true; +// if (isConn) { +// ui->lbWaiting->setStyleSheet("QLabel{border:0px;border-radius:10px;background-color:rgba(61,107,229,1);}"); +// } else { +// ui->btnDisConn->hide(); +// ui->lbWaiting->setStyleSheet("QLabel{border:0px;border-radius:10px;background-color:rgba(255,255,255,0.12);}"); +// } +// this->countCurrentTime = 0; +// this->waitPage = TOTAL_PAGE; //总共有8张图片 +// this->waitTimer->start(FRAME_SPEED); +// ui->lbWaiting->show(); +// ui->lbWaitingIcon->show(); + +// mw->setTrayLoading(true); +} + +void OneConnForm::stopWaiting() +{ + //YYF 平板模式设计稿中没有此图标 +// this->isWaiting = false; +// this->waitTimer->stop(); +// ui->lbWaiting->hide(); +// ui->lbWaitingIcon->hide(); + +// mw->setTrayLoading(false); +// mw->getActiveInfo(); +} diff --git a/KylinNM/src/oneconnform.h b/KylinNM/src/oneconnform.h new file mode 100644 index 0000000..70bbbd8 --- /dev/null +++ b/KylinNM/src/oneconnform.h @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include +#include +#include +#include +#include +#include + +#include "confform.h" +#include "backthread.h" +#include "ksimplenm.h" + +#define FRAME_SPEED 150 +#define LIMIT_TIME 20*1000 +#define TOTAL_PAGE 8 + +#define W_ITEM 414 +#define H_ITEM 60 +#define H_ITEM_BIG 138 +#define H_ITEM_MIDDLE 156 +#define H_WIFI_ITEM_BIG_EXTEND 78 //138 - 60 +#define H_WIFI_ITEM_SMALL_EXTEND 96 //156 - 56 +#define Y_LINE 59 +#define X_LINE 2 +#define Y_LINE_SMALL_EXTEND 105 +#define X_LINE_SMALL_EXTEND 2 +#define Y_LINE_BIG_EXTEND 137 +#define X_LINE_BIG_EXTEND 2 + +class KylinNM; + +namespace Ui { +class OneConnForm; +} + +class OneConnForm : public QWidget +{ + Q_OBJECT + +public: + explicit OneConnForm(QWidget *parent = 0, KylinNM *mw = 0, ConfForm *confForm = 0, KSimpleNM *ksnm = 0); + ~OneConnForm(); + + void setSignal(QString lv, QString secu); + void setSignalOn(QString lv, QString secu); + void setName(QString name); + void setSpecialName(QString name); + QString getName(); + void setRate(QString rate); + void setLine(bool isShow); + void setWifiInfo(QString str1, QString str2, QString str3); + + void setSelected(bool isSelected, bool isCurrName); + void setHideItem(bool isHideItem, bool isShowHideBtn); + void setTopItem(bool isSelected); + void setAct(bool isAct); + + void setConnedString(bool showLable, QString str, QString str1); + + bool isWifiConfExist(QString netName); + + QString wifiName; + bool isSelected; + bool isActive; + bool isConnected; + bool isTopItem; + int signalLv; + +protected: + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event) override; + bool eventFilter(QObject *obj, QEvent *event); + +private Q_SLOTS: + void tabletStyle(); + void on_btnConn_clicked(); + void on_btnConnSub_clicked(); + void on_btnDisConn_clicked(); + void on_btnInfo_2_clicked(); + void toConnectWirelessNetwork(); + void on_autoConn_toggled(bool val); + + void slotConnWifi(); + void slotConnWifiPWD(); + void slotConnWifiResult(int connFlag); + + void on_btnConnPWD_clicked(); + + void on_btnHideConn_clicked(); + + void on_checkBoxPwd_stateChanged(int arg1); + + void on_lePassword_textEdited(const QString &arg1); + + void waitAnimStep(); + void startWaiting(bool isConn); + void stopWaiting(); + + void on_btnInfo_clicked(); + +private: + QTimer *waitTimer = nullptr; + int waitPage; + int countCurrentTime; + bool isWaiting = false; + + Ui::OneConnForm *ui = nullptr; + KylinNM *mw = nullptr; + ConfForm *cf = nullptr; + KSimpleNM *ks = nullptr; + bool hasPwd; + + QString leQssLow, leQssHigh; + + QString signalStrong="0"; + QString signalHasPw="--"; + bool is_connecting = false; + +Q_SIGNALS: + void selectedOneWifiForm(QString wifiName, int extendLength); + void connDone(int connFlag); + void disconnActiveWifi(); + + void sigConnWifi(QString); + void sigConnWifiPWD(QString, QString); + + void onLineEditClicked(); +}; + +#endif // ONECONNFORM_H diff --git a/KylinNM/src/oneconnform.ui b/KylinNM/src/oneconnform.ui new file mode 100644 index 0000000..77e347e --- /dev/null +++ b/KylinNM/src/oneconnform.ui @@ -0,0 +1,306 @@ + + + OneConnForm + + + + 0 + 0 + 424 + 156 + + + + Form + + + + + 63 + 8 + 190 + 20 + + + + + + + + + + 316 + 14 + 100 + 34 + + + + + + + + + + 316 + 14 + 100 + 34 + + + + + + + + + + 14 + 14 + 28 + 28 + + + + + + + + + + 63 + 31 + 140 + 20 + + + + + + + + + + 0 + 0 + 424 + 156 + + + + + + 56 + 56 + 254 + 48 + + + + + + + 286 + 75 + 16 + 8 + + + + + + + + + + 56 + 110 + 414 + 40 + + + + + + + + + + + 326 + 14 + 80 + 40 + + + + + + + + + + 318 + 56 + 80 + 48 + + + + + + + + + + 316 + 14 + 100 + 34 + + + + + + + + + + 0 + 0 + 424 + 138 + + + + + + 63 + 64 + 230 + 56 + + + + + + + + + + 63 + 62 + 230 + 20 + + + + + + + 63 + 86 + 230 + 20 + + + + + + + 63 + 110 + 230 + 20 + + + + leInfo_1 + leInfo_2 + leInfo_3 + btnInfo + + + + + 2 + 59 + 421 + 2 + + + + background-color: rgba(255, 255, 255,0.05); + + + Qt::Horizontal + + + + + + 0 + 0 + 414 + 56 + + + + + + + 280 + 20 + 20 + 20 + + + + + + + + + + 280 + 20 + 20 + 20 + + + + + + + + + + 372 + 14 + 16 + 16 + + + + + + + wbg_3 + wbg_2 + lbName + lbSignal + lbConned + line + wbg + btnConnSub + btnConn + btnConnPWD + btnDisConn + btnHideConn + lbWaiting + lbWaitingIcon + btnInfo_2 + + + + diff --git a/KylinNM/src/onelancform.cpp b/KylinNM/src/onelancform.cpp new file mode 100644 index 0000000..b297910 --- /dev/null +++ b/KylinNM/src/onelancform.cpp @@ -0,0 +1,482 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see + +OneLancForm::OneLancForm(QWidget *parent, KylinNM *mainWindow, ConfForm *confForm, KSimpleNM *ksnm) : + QWidget(parent), + ui(new Ui::OneLancForm) +{ + ui->setupUi(this); + + ui->btnConnSub->setText(tr("Connect"));//"设置" + ui->btnConn->setText(tr("Connect"));//"连接" + ui->btnConn->hide();//YYF + ui->btnDisConn->setText(tr("Disconnect"));//"断开连接" + + ui->lbConned->setAlignment(Qt::AlignLeft); + + leQssLow = "QLineEdit{border:none;background:transparent;font-size:14px;color:rgba(255,255,255,0.57);font-family:Noto Sans CJK SC;}"; + leQssHigh = "QLineEdit{border:none;background:transparent;font-size:14px;color:rgba(255,255,255,0.91);font-family:Noto Sans CJK SC;}"; + + ui->leInfo_1->setStyleSheet(leQssLow); + ui->leInfo_2->setStyleSheet(leQssLow); + ui->leInfo_3->setStyleSheet(leQssLow); + ui->leInfo_4->setStyleSheet(leQssLow); + ui->btnInfo->setStyleSheet("QPushButton{border:none;background:transparent;}"); + ui->wbg->setStyleSheet("#wbg{border-radius:8px;background-color:rgba(255,255,255,0.1);border:1px solid red;}");//主窗口(展开) + ui->wbg_2->setStyleSheet("#wbg_2{border-radius:8px;background-color:rgba(255,255,255,0);}");//列表窗口(闭合) + ui->lbName->setStyleSheet("QLabel{font-size:14px;color:rgba(38, 38, 38, 1);}");//列表名称 + ui->lbConned->setStyleSheet("QLabel{font-size:14px;color:rgba(47, 179, 232, 1);}");//已连接 + ui->btnConnSub->setStyleSheet("QPushButton{border:0px;border-radius:4px;background-color:rgba(61,107,229,1);color:white;font-size:14px;}" + "QPushButton:Hover{border:0px solid rgba(255,255,255,0.2);border-radius:4px;background-color:rgba(107,142,235,1);}" + "QPushButton:Pressed{border-radius:4px;background-color:rgba(50,87,202,1);}"); + ui->btnConn->setStyleSheet("QPushButton{border:0px;border-radius:4px;background-color:rgba(61,107,229,1);color:white;font-size:14px;}" //连接按钮 + "QPushButton:Hover{border:0px solid rgba(255,255,255,0.2);border-radius:4px;background-color:rgba(107,142,235,1);}" + "QPushButton:Pressed{border-radius:4px;background-color:rgba(50,87,202,1);}"); +// ui->btnDisConn->setStyleSheet("QPushButton{border:0px;border-radius:4px;background-color:rgba(255,255,255,0.12);color:white;font-size:14px;}" +// "QPushButton:Hover{border:0px solid rgba(255,255,255,0.2);border-radius:4px;background-color:rgba(255,255,255,0.2);}" +// "QPushButton:Pressed{border-radius:8px;background-color:rgba(255,255,255,0.08);}"); + ui->btnDisConn->setStyleSheet("#btnDisConn{font-family: NotoSansCJKsc-Regular, NotoSansCJKsc;" + "font-weight: 400;color: rgba(38,38,38,0.75);line-height: 20px;font-size:14px;" + "border:0px;background-color:rgba(38,38,38,0.1);border-radius:8px;}" + "#btnDisConn:Hover{border:0px solid rgba(255,255,255,0.2);border-radius:8px;background-color:rgba(38,38,38,0.2);}" + "#btnDisConn:Pressed{border-radius:8px;background-color:rgba(38,38,38,0.06);}");//断开链接按钮 + ui->btnConnCfg->setStyleSheet("border:0px;border-radius:4px;border-image:url(:/res/l/pb-network-info.png);");//链接详情按钮 + connect(ui->btnConnCfg,&QPushButton::clicked,this,&OneLancForm::on_btnInfo_clicked); + ui->btnConnCfg->hide(); + + + ui->btnInfo->setCursor(QCursor(Qt::PointingHandCursor)); + ui->btnInfo->setFocusPolicy(Qt::NoFocus); + ui->btnConnSub->setFocusPolicy(Qt::NoFocus); + ui->btnConn->setFocusPolicy(Qt::NoFocus); + ui->btnDisConn->setFocusPolicy(Qt::NoFocus); + + ui->wbg->hide(); + ui->wbg_2->show(); + ui->lbName->show(); + ui->btnConnSub->hide(); + //YYF ui->btnConn->hide(); + ui->btnDisConn->hide(); + ui->line->show(); + ui->lbWaiting->hide(); + ui->lbWaitingIcon->hide(); + + this->mw = mainWindow; + this->cf = confForm; + this->ks = ksnm; + + this->isSelected = false; + this->isActive = false; + + this->setAttribute(Qt::WA_Hover,true); + this->installEventFilter(this); + ui->btnInfo->setAttribute(Qt::WA_Hover,true); + ui->btnInfo->installEventFilter(this); + + this->waitTimer = new QTimer(this); + connect(waitTimer, SIGNAL(timeout()), this, SLOT(waitAnimStep())); + + connect(mw, SIGNAL(waitLanStop()), this, SLOT(stopWaiting())); + + srand((unsigned)time(NULL)); + + ui->lbWaiting->setStyleSheet("QLabel{border:0px;border-radius:4px;background-color:rgba(61,107,229,1);}"); + //ui->lbWaitingIcon->setStyleSheet("QLabel{border:0px;background-color:transparent;}"); + ui->lbWaitingIcon->setStyleSheet("QLabel{border:0px;background-color:transparent;}"); + ui->lbWaitingIcon->move(316+194*mw->isTabletStyle,20);// YYF + tabletStyle();//平板模式PC桌面样式 +} + +void OneLancForm::tabletStyle()//平板桌面模式特有设置 +{ + ui->lbName->setFixedWidth(190+194*mw->isTabletStyle); + ui->btnInfo->setFixedWidth(414+194*mw->isTabletStyle); + ui->wbg_2->setFixedWidth(414+194*mw->isTabletStyle); + ui->btnDisConn->move(316+194*mw->isTabletStyle,8 + 18*mw->isTabletStyle); + ui->btnConnCfg->move(364+194*mw->isTabletStyle,18 + 18*mw->isTabletStyle); +} + +OneLancForm::~OneLancForm() +{ + delete ui; +} + +void OneLancForm::mousePressEvent(QMouseEvent *event) +{ + if (event->button() != Qt::LeftButton) { + event->ignore(); + return; + } + ui->wbg_2->setStyleSheet("#wbg_2{border-radius:8px;background-color:rgba(38,38,38,0.1);}"); +} + +void OneLancForm::mouseReleaseEvent(QMouseEvent *event) +{ + if (event->button() != Qt::LeftButton || !rect().contains(event->pos())) { + event->ignore(); + return; + } + if(ui->btnDisConn->isHidden()) { + on_btnConn_clicked();// YYF + } + ui->wbg_2->setStyleSheet("#wbg_2{border-radius:8px;background-color:rgba(255,255,255,0);}"); +} + +//事件过滤器 +bool OneLancForm::eventFilter(QObject *obj, QEvent *event) +{ + if (obj == ui->btnInfo) { + if(event->type() == QEvent::HoverEnter) { + ui->leInfo_1->setStyleSheet(leQssHigh); + ui->leInfo_2->setStyleSheet(leQssHigh); + ui->leInfo_3->setStyleSheet(leQssHigh); + ui->leInfo_4->setStyleSheet(leQssHigh); + return true; + } else if(event->type() == QEvent::HoverLeave) { + ui->leInfo_1->setStyleSheet(leQssLow); + ui->leInfo_2->setStyleSheet(leQssLow); + ui->leInfo_3->setStyleSheet(leQssLow); + ui->leInfo_4->setStyleSheet(leQssLow); + return true; + } + } else if (obj == this) { + if (event->type() == QEvent::HoverEnter) { + if (!this->isTopItem) { + if (!this->isSelected) { + //YYF ui->btnConn->show(); + ui->wbg_2->setStyleSheet("#wbg_2{border-radius:8px;background-color:rgba(38,38,38,0.05);}"); + ui->wbg_2->show(); + } + } + return true; + } else if(event->type() == QEvent::HoverLeave) { + //YYF ui->btnConn->hide(); + ui->wbg_2->setStyleSheet("#wbg_2{border-radius:8px;background-color:rgba(255,255,255,0);}"); + ui->wbg_2->hide(); + return true; + } + } + + return QWidget::eventFilter(obj,event); +} + +// 是否当前连接的网络,字体设置不同 +void OneLancForm::setAct(bool isAct) +{ + if (isAct) { + ui->lbName->setStyleSheet("QLabel{font-size:14px;color:rgba(47, 179, 232, 1);}"); + ui->lbConned->show(); + ui->btnConnSub->hide(); + } else { + ui->lbName->setStyleSheet("QLabel{font-size:14px;color:rgba(38, 38, 38, 0.45);}"); + ui->lbConned->hide(); + ui->btnConnSub->hide(); + } + isActive = isAct; +} + +// 是否选中 +void OneLancForm::setSelected(bool isSelected, bool isCurrName) +{ + if (isSelected) { + resize(W_ITEM, H_ITEM_EXTEND); + ui->wbg->show(); + ui->wbg_2->hide(); + ui->line->move(X_LINE_EXTEND, Y_LINE_EXTEND); + //YYF ui->btnConn->hide(); + ui->btnConnSub->show(); + + this->isSelected = true; + } else { + resize(W_ITEM, H_ITEM); + ui->wbg->hide(); + ui->wbg_2->show(); + ui->line->move(X_LINE, Y_LINE); + if(isCurrName){ + //YYF ui->btnConn->show(); + }else{ + //YYF ui->btnConn->hide(); + } + ui->btnConnSub->hide(); + + this->isSelected = false; + } + + ui->btnDisConn->hide(); + + this->isTopItem = false; +} + +//设置顶部这个item的显示 +void OneLancForm::setTopItem(bool isSelected) +{ +// if (isSelected) { +// resize(W_ITEM, H_ITEM_EXTEND); +// ui->wbg->show(); +// ui->btnConnSub->hide(); +// this->isSelected = true; +// } else { +// resize(W_ITEM, H_ITEM); +// ui->wbg->hide(); +// ui->btnConnSub->hide(); +// this->isSelected = false; +// } + + if (isConnected) { + ui->lbIcon->setStyleSheet("QLabel{border-radius:0px;border-image:url(:/res/l/pb-network-online.png);}"); + ui->btnDisConn->show(); +// ui->btnConnCfg->show(); + } else { + ui->lbIcon->setStyleSheet("QLabel{border-radius:0px;border-image:url(:/res/l/pb-top-network-offline.png);}"); +// ui->btnConnCfg->hide(); + ui->btnDisConn->hide(); + } + ui->btnConnCfg->hide(); + + //YYF ui->btnConn->hide(); + ui->wbg_2->hide(); + ui->line->hide(); + + this->isTopItem = true; +} + +//设置网络名称 +void OneLancForm::setName(QString name, QString uniName) +{ + ui->lbName->setText(name); + lanName = name; + uniqueName = uniName; +} + +//根据有线网络连接与否,设置显示'已连接'文字的控件的可见与否 +void OneLancForm::setConnedString(bool showLable, QString str) +{ + if (!showLable) { + ui->lbConned->hide(); + ui->lbName->move(63, 18); + } else { + str="";//YYF 平板模式 + ui->lbConned->setText(str); + ui->lbName->move(63, 18); //YYF + } +} + +//设置item被扩展后出现的网络信息 +void OneLancForm::setLanInfo(QString str1, QString str2, QString str3, QString str4) +{ + if (str1 == "" || str1 == "auto") { + str1 = tr("No Configuration"); + } + + if (str2 == "" || str2 == "auto") { + str2 = tr("No Configuration"); + } + + QString strIPv4 = QString(tr("IPv4:")); + QString strIPv6 = QString(tr("IPv6:")); + QString strBW = QString(tr("BandWidth:")); + QString strMAC = QString(tr("MAC:")); + + ui->leInfo_1->setText(strIPv4 + str1); + ui->leInfo_2->setText(strIPv6 + str2); + ui->leInfo_3->setText(strBW + str3); + ui->leInfo_4->setText(strMAC + str4); +} + +//根据网络是否连接,设置网络状态图标 +void OneLancForm::setIcon(bool isOn) +{ + if (isOn) { + ui->lbIcon->setStyleSheet("QLabel{border-radius:0px;border-image:url(:/res/l/pb-network-online.png);}"); + } else { + ui->lbIcon->setStyleSheet("QLabel{border-radius:0px;border-image:url(:/res/l/pb-network-offline.png);}"); + } +} + +//设置item下方横线的可见与否 +void OneLancForm::setLine(bool isShow) +{ + if (isShow) { + ui->line->show(); + } else { + ui->line->hide(); + } +} + +void OneLancForm::slotConnLan() +{ + //mw->startLoading(); + this->startWaiting(true); + emit sigConnLan(ui->lbName->text()); +} + +//点击网络断开按钮,执行该函数 +void OneLancForm::on_btnDisConn_clicked() +{ + syslog(LOG_DEBUG, "DisConnect button about lan net is clicked, current wired net name is %s .", ui->lbName->text().toUtf8().data()); + qDebug()<<"DisConnect button about lan net is clicked, current wired net name is "<lbName->text(); + + this->startWaiting(false); + mw->is_stop_check_net_state = 1; + + kylin_network_set_con_down(ui->lbName->text().toUtf8().data()); + + disconnect(this, SIGNAL(selectedOneLanForm(QString, QString)), mw, SLOT(oneTopLanFormSelected(QString, QString))); + + emit disconnActiveLan(); +} + +//点击了连接网络按钮,执行该函数 +void OneLancForm::on_btnConn_clicked() +{ + syslog(LOG_DEBUG, "A button named btnConn about lan net is clicked."); + qDebug()<<"A button named btnConn about lan net is clicked."; + toConnectWiredNetwork(); +} + +//点击了item被扩展中的连接网络按钮,执行该函数 +void OneLancForm::on_btnConnSub_clicked() +{ + syslog(LOG_DEBUG, "A button named btnConnSub about lan net is clicked."); + qDebug()<<"A button named btnConnSub about lan net is clicked."; + toConnectWiredNetwork(); +} + +void OneLancForm::toConnectWiredNetwork() +{ + mw->is_stop_check_net_state = 1; + QThread *t = new QThread(); + BackThread *bt = new BackThread(); + bt->moveToThread(t); + connect(t, SIGNAL(finished()), t, SLOT(deleteLater())); + connect(t, SIGNAL(started()), this, SLOT(slotConnLan())); + connect(this, SIGNAL(sigConnLan(QString)), bt, SLOT(execConnLan(QString))); +// connect(bt, SIGNAL(connDone(int)), mw, SLOT(connLanDone(int))); + connect(bt, &BackThread::connDone, this, [=](int f) { + mw->connLanDone(f); + }); + connect(bt, SIGNAL(btFinish()), t, SLOT(quit())); + t->start(); +} + +//点击列表中item扩展后显示信息的位置时,执行该函数,用于显示网络配置界面 +void OneLancForm::on_btnInfo_clicked() +{ + QPoint pos = QCursor::pos(); + QRect primaryGeometry; + for (QScreen *screen : qApp->screens()) { + if (screen->geometry().contains(pos)) { + primaryGeometry = screen->geometry(); + } + } + + if (primaryGeometry.isEmpty()) { + primaryGeometry = qApp->primaryScreen()->geometry(); + } + + BackThread *bt = new BackThread(); + QString connProp = bt->getConnProp(ui->lbName->text()); + QStringList propList = connProp.split("|"); + QString v4method, addr, mask, gateway, dns; + foreach (QString line, propList) { + if (line.startsWith("method:")) { + v4method = line.split(":").at(1); + } + if (line.startsWith("addr:")) { + addr = line.split(":").at(1); + } + if (line.startsWith("mask:")) { + mask = line.split(":").at(1); + } + if (line.startsWith("gateway:")) { + gateway= line.split(":").at(1); + } + if (line.startsWith("dns:")) { + dns = line.split(":").at(1); + } + } + // qDebug()<setProp(ui->lbName->text(), v4method, addr, mask, gateway, dns, this->isActive); + connect(cf, SIGNAL(requestRefreshLanList(int)), mw, SLOT(onBtnNetListClicked(int))); + + cf->move(primaryGeometry.width() / 2 - cf->width() / 2, primaryGeometry.height() / 2 - cf->height() / 2); + cf->show(); + cf->raise(); + cf->activateWindow(); +} + +void OneLancForm::waitAnimStep() +{ + QString qpmQss = "QLabel{background-image:url(':/res/s/conning-a/"; + qpmQss.append(QString::number(this->waitPage)); + qpmQss.append(".png');}"); + ui->lbWaitingIcon->setStyleSheet(qpmQss); + + this->waitPage --; + if (this->waitPage < 1) { + this->waitPage = TOTAL_PAGE; //循环播放8张图片 + } + + this->countCurrentTime += FRAME_SPEED; + if (this->countCurrentTime >= LIMIT_TIME) { + QString cmd = "kill -9 $(pidof nmcli)"; //杀掉当前正在进行的有关nmcli命令的进程 + int status = system(cmd.toUtf8().data()); + if (status != 0) { + qDebug()<<"execute 'kill -9 $(pidof nmcli)' in function 'waitAnimStep' failed"; + syslog(LOG_ERR, "execute 'kill -9 $(pidof nmcli)' in function 'waitAnimStep' failed"); + } + + this->stopWaiting(); //动画超出时间限制,强制停止动画 + + mw->is_stop_check_net_state = 0; + } +} + +void OneLancForm::startWaiting(bool isConn) +{ + if (isConn) { + ui->lbWaiting->setStyleSheet("QLabel{border:0px;border-radius:4px;background-color:rgba(61,107,229,1);}"); + } else { + ui->btnDisConn->hide(); + ui->lbWaiting->setStyleSheet("QLabel{border:0px;border-radius:4px;background-color:rgba(255,255,255,0.12);}"); + } + this->countCurrentTime = 0; + this->waitPage = TOTAL_PAGE; //总共有8张图片 + this->waitTimer->start(FRAME_SPEED); +// ui->lbWaiting->show(); YYF + ui->lbWaitingIcon->show(); + + mw->setTrayLoading(true); +} + +void OneLancForm::stopWaiting() +{ + this->waitTimer->stop(); + ui->lbWaiting->hide(); + ui->lbWaitingIcon->hide(); + + mw->setTrayLoading(false); + mw->getActiveInfo(); +} diff --git a/KylinNM/src/onelancform.h b/KylinNM/src/onelancform.h new file mode 100644 index 0000000..3d1f196 --- /dev/null +++ b/KylinNM/src/onelancform.h @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include +#include +#include + +#include "confform.h" +#include "kylin-network-interface.h" +#include "backthread.h" +#include "ksimplenm.h" + +#define FRAME_SPEED 150 +#define LIMIT_TIME 20*1000 +#define TOTAL_PAGE 8 + +#define W_ITEM 424 +#define H_ITEM 60 +#define H_ITEM_EXTEND 162 +#define Y_LINE 59 +#define X_LINE 2 +#define Y_LINE_EXTEND 161 +#define X_LINE_EXTEND 2 + +class KylinNM; + +namespace Ui { +class OneLancForm; +} + +class OneLancForm : public QWidget +{ + Q_OBJECT + +public: + explicit OneLancForm(QWidget *parent = 0, KylinNM *mw = 0, ConfForm *confForm = 0, KSimpleNM *ksnm = 0); + ~OneLancForm(); + + void setName(QString name, QString uniName); + void setIcon(bool isOn); + void setLine(bool isShow); + void setLanInfo(QString str1, QString str2, QString str3, QString str4); + + void setSelected(bool isSelected, bool isCurrName); + void setTopItem(bool isSelected); + void setAct(bool isAct); + + void setConnedString(bool showLable, QString str); + + bool isSelected; + bool isTopItem; + bool isActive; + bool isConnected; + QString lanName; + QString uniqueName; + +protected: + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event) override; + bool eventFilter(QObject *obj, QEvent *event); + +private Q_SLOTS: + void on_btnConn_clicked(); + void on_btnConnSub_clicked(); + void on_btnDisConn_clicked(); + void toConnectWiredNetwork(); + + void slotConnLan(); + + void waitAnimStep(); + void startWaiting(bool isConn); + void stopWaiting(); + + void on_btnInfo_clicked(); + +private: + void tabletStyle();//YYF 平板桌面模式特有设置 + + QTimer *waitTimer = nullptr; + int waitPage; + int countCurrentTime; + + Ui::OneLancForm *ui = nullptr; + KylinNM *mw = nullptr; + ConfForm *cf = nullptr; + KSimpleNM *ks = nullptr; + + QString leQssLow, leQssHigh; + +Q_SIGNALS: + void selectedOneLanForm(QString lanName, QString uniqueName); + void connDone(int connFlag); + void disconnActiveLan(); + + void sigConnLan(QString); +}; + +#endif // ONELANCFORM_H diff --git a/KylinNM/src/onelancform.ui b/KylinNM/src/onelancform.ui new file mode 100644 index 0000000..f57060f --- /dev/null +++ b/KylinNM/src/onelancform.ui @@ -0,0 +1,242 @@ + + + OneLancForm + + + + 0 + 0 + 424 + 250 + + + + Form + + + + + 63 + 8 + 190 + 20 + + + + + + + + + + 316 + 14 + 100 + 34 + + + + + + + + + + 316 + 14 + 100 + 34 + + + + + + + + + + 316 + 18 + 80 + 40 + + + + + + + + + + 63 + 31 + 52 + 20 + + + + + + + + + + 0 + 0 + 424 + 162 + + + + + + 63 + 64 + 238 + 85 + + + + + + + + + + 63 + 62 + 238 + 20 + + + + + + + 63 + 86 + 250 + 20 + + + + + + + 63 + 110 + 238 + 20 + + + + + + + 63 + 134 + 238 + 20 + + + + leInfo_1 + leInfo_2 + leInfo_3 + leInfo_4 + btnInfo + + + + + 14 + 14 + 24 + 24 + + + + + + + + + + 2 + 59 + 421 + 2 + + + + background-color: rgba(255, 255, 255,0.05); + + + Qt::Horizontal + + + + + + 0 + 0 + 340 + 56 + + + + + + + 316 + 14 + 100 + 34 + + + + + + + + + + 356 + 20 + 20 + 20 + + + + + + + + + + 364 + 18 + 16 + 16 + + + + + + + lbName + lbConned + lbIcon + line + wbg + wbg_2 + btnConnSub + btnDisConn + btnConn + lbWaiting + lbWaitingIcon + btnConnCfg + + + + diff --git a/KylinNM/src/swipegesturerecognizer.cpp b/KylinNM/src/swipegesturerecognizer.cpp new file mode 100644 index 0000000..87b553c --- /dev/null +++ b/KylinNM/src/swipegesturerecognizer.cpp @@ -0,0 +1,154 @@ +#include +#include + +#include +#include "swipegesturerecognizer.h" + +SwipeGestureRecognizer::SwipeGestureRecognizer(QObject *parent) : QObject(parent) +{ +} + +bool +SwipeGestureRecognizer::IsValidMove(int dx, int dy) +{ + // The moved distance is to small to count as not just a glitch. + if ((qAbs(dx) < MINIMUM_DISTANCE) && (qAbs(dy) < MINIMUM_DISTANCE)) { + return false; + } + + return true; +} + + +// virtual +QGesture* +SwipeGestureRecognizer::create(QObject* pTarget) +{ + QGesture *pGesture = new QSwipeGesture(pTarget); + return pGesture; +} + + +// virtual +QGestureRecognizer::Result +SwipeGestureRecognizer::recognize(QGesture* pGesture, QObject *pWatched, QEvent *pEvent) +{ + QGestureRecognizer::Result result = QGestureRecognizer::Ignore; + QSwipeGesture *pSwipe = static_cast(pGesture); + + switch(pEvent->type()) { + case QEvent::MouseButtonPress: { + QMouseEvent* pMouseEvent = static_cast(pEvent); +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) + pSwipe->setProperty("startPoint", pMouseEvent->posF()); +#else + pSwipe->setProperty("startPoint", pMouseEvent->localPos()); +#endif + result = QGestureRecognizer::MayBeGesture; + //qDebug() << "Swipe gesture started (start point=" << pSwipe->property("startPoint").toPointF() << ")"; + } + break; + case QEvent::MouseButtonRelease: { + QMouseEvent* pMouseEvent = static_cast(pEvent); + const QVariant& propValue = pSwipe->property("startPoint"); + QPointF startPoint = propValue.toPointF(); +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) + QPointF endPoint = pMouseEvent->posF(); +#else + QPointF endPoint = pMouseEvent->localPos(); +#endif + + // process distance and direction + int dx = endPoint.x() - startPoint.x(); + int dy = endPoint.y() - startPoint.y(); + + //bugfix: startPoint.isNull because we sometimes get false events with startpoint 0 -> wrong swipe detected! + if ( (!IsValidMove(dx, dy)) || ( startPoint.isNull() ) ) { + // Just a click, so no gesture. + result = QGestureRecognizer::CancelGesture; + //qDebug("Swipe gesture canceled"); + } else { + // Compute the angle. + // qDebug() << " startPoint= " << startPoint << " endPoint=" << endPoint << " dx=" << dx << " dy=" << dy; + Q_EMIT onSwipeGesture(dx, dy); + qreal angle = ComputeAngle(dx, dy); + pSwipe->setSwipeAngle(angle); + result = QGestureRecognizer::FinishGesture; + //qDebug("Swipe gesture finished"); + } + } + break; + default: + break; + } + + return result; +} + +void +SwipeGestureRecognizer::reset(QGesture *pGesture) +{ + pGesture->setProperty("startPoint", QVariant(QVariant::Invalid)); + parent::reset(pGesture); +} + +qreal +SwipeGestureRecognizer::ComputeAngle(int dx, int dy) +{ + double PI = 3.14159265; + + // Need to convert from screen coordinates direction + // into classical coordinates direction. + dy = -dy; + + double result = atan2((double)dy, (double)dx) ; + result = (result * 180) / PI; + + // Always return positive angle. + if (result < 0) { + result += 360; + } + + return result; +} + + + +/* +========================================================================== +*/ + +QSwipeGesture::SwipeDirection +SwipeGestureUtil::GetHorizontalDirection(QSwipeGesture *pSwipeGesture) +{ + qreal angle = pSwipeGesture->swipeAngle(); + if (0 <= angle && angle <= 45) { + return QSwipeGesture::Right; + } + + if (135 <= angle && angle <= 225) { + return QSwipeGesture::Left; + } + + if (315 <= angle && angle <= 360) { + return QSwipeGesture::Right; + } + + return QSwipeGesture::NoDirection; +} + +QSwipeGesture::SwipeDirection +SwipeGestureUtil::GetVerticalDirection(QSwipeGesture *pSwipeGesture) +{ + qreal angle = pSwipeGesture->swipeAngle(); + + if (45 < angle && angle < 135) { + return QSwipeGesture::Up; + } + + if (225 < angle && angle < 315) { + return QSwipeGesture::Down; + } + + return QSwipeGesture::NoDirection; +} diff --git a/KylinNM/src/swipegesturerecognizer.h b/KylinNM/src/swipegesturerecognizer.h new file mode 100644 index 0000000..2e51665 --- /dev/null +++ b/KylinNM/src/swipegesturerecognizer.h @@ -0,0 +1,46 @@ +#ifndef SWIPEGESTURERECOGNIZER_H +#define SWIPEGESTURERECOGNIZER_H + +#include +#include +#include +#include "kylinnm.h" + +/** + * @brief The SwipeGestureRecognizer class + * needed because the defalt implementation of swipe is a little bit odd: it only triggers on 3 finger swipes! (5.3) + * + * http://developer.android.com/design/patterns/gestures.html + * http://qt-project.org/doc/qt-5/gestures-overview.html + */ +class SwipeGestureRecognizer :public QObject, public QGestureRecognizer +{ + Q_OBJECT +public: + SwipeGestureRecognizer(QObject *parent = nullptr); +private: + static const int MINIMUM_DISTANCE = 10; + + typedef QGestureRecognizer parent; + + bool IsValidMove(int dx, int dy); + + qreal ComputeAngle(int dx, int dy); + + virtual QGesture* create(QObject* pTarget); + + virtual QGestureRecognizer::Result recognize(QGesture* pGesture, QObject *pWatched, QEvent *pEvent); + + void reset (QGesture *pGesture); +signals: + void onSwipeGesture(int dx, int dy); +}; + + +class SwipeGestureUtil { +public: + static QSwipeGesture::SwipeDirection GetHorizontalDirection(QSwipeGesture *pSwipeGesture); + static QSwipeGesture::SwipeDirection GetVerticalDirection(QSwipeGesture *pSwipeGesture); +}; + +#endif // SWIPEGESTURERECOGNIZER_H diff --git a/KylinNM/src/switchbutton.cpp b/KylinNM/src/switchbutton.cpp new file mode 100644 index 0000000..714e249 --- /dev/null +++ b/KylinNM/src/switchbutton.cpp @@ -0,0 +1,92 @@ +#include "switchbutton.h" + +SwitchButton::SwitchButton(QWidget *parent) : QWidget(parent) +{ + + setFixedSize(50,24); + m_fWidth = (float)width(); + m_fHeight = (float)height(); + m_cTimer = new QTimer(this); + m_cTimer->setInterval(5); + + if(m_bIsOn == 1) { + m_fCurrentValue = m_fWidth - 16 - 4; + } + else { + m_fCurrentValue = 4; + } + connect(m_cTimer,SIGNAL(timeout()),this,SLOT(startAnimation())); + + +} + +void SwitchButton::setSwitchStatus(bool check) { + if(check == true) { + m_bIsOn = 1; + } else { + m_bIsOn = 0; + } + + m_cTimer->start(); //开始播放动画 +} + + +/* 播放按钮开启关闭动画 */ +void SwitchButton::startAnimation() { //滑动按钮动作播放 + int pos = 4; + int size = m_fWidth - 16; + if(m_bIsOn) { + m_fCurrentValue ++; //往右滑动 + if(m_fCurrentValue >= size - pos) { //到达边界停下来 + m_fCurrentValue = size - pos; + m_cTimer->stop(); + } + + } else { + m_fCurrentValue --; + if(m_fCurrentValue <= pos) { //到达最小值,停止继续前进 + m_fCurrentValue = pos; + m_cTimer->stop(); + } + } + update(); +} + +/* 按钮按下处理 */ +void SwitchButton::mousePressEvent(QMouseEvent *event) { + Q_UNUSED(event); + m_bIsOn = !m_bIsOn; + + Q_EMIT clicked(m_bIsOn); + + return QWidget::mousePressEvent(event); +} + +/* 绘制滑动按钮主体 */ +void SwitchButton::paintEvent(QPaintEvent *event) { + Q_UNUSED(event); + QPainter painter(this); + painter.setRenderHint(QPainter::SmoothPixmapTransform); + painter.setRenderHint(QPainter::Antialiasing); //抗锯齿效果 + painter.setPen(Qt::NoPen); + QColor colorActive(61,107,229); + QColor colorInactive(111,111,111); + colorInactive.setAlphaF(0.12); + if(m_bIsOn) { + painter.save(); + painter.setBrush(colorActive); + QRectF active_rect = QRectF(0,0,m_fWidth,m_fHeight); + painter.drawRoundedRect(active_rect, 0.5 * m_fHeight, 0.5 * m_fHeight); //画开启状态 + } else { + painter.save(); + painter.setBrush(colorInactive); + QRectF inactive_rect = QRectF(0 ,0,m_fWidth,m_fHeight); + painter.drawRoundedRect(inactive_rect, 0.5 * m_fHeight, 0.5 * m_fHeight); //画关闭状态 + } + painter.restore(); + painter.save(); + painter.setBrush(Qt::white); + painter.drawEllipse(m_fCurrentValue,4, 16, 16); + painter.restore(); +} + diff --git a/KylinNM/src/switchbutton.h b/KylinNM/src/switchbutton.h new file mode 100644 index 0000000..7c0f1e5 --- /dev/null +++ b/KylinNM/src/switchbutton.h @@ -0,0 +1,32 @@ +#ifndef SWITCHBUTTON_H +#define SWITCHBUTTON_H + +#include +#include +#include +#include + +class SwitchButton : public QWidget +{ + Q_OBJECT +public: + explicit SwitchButton(QWidget *parent = nullptr); + void setSwitchStatus(bool check); + +private: + int m_bIsOn = 1; + QTimer *m_cTimer; + float m_fWidth; + float m_fHeight; + float m_fCurrentValue; + void paintEvent(QPaintEvent *event); + void mousePressEvent(QMouseEvent *event); + +Q_SIGNALS: + void clicked(int check); +private Q_SLOTS: + void startAnimation(); + +}; + +#endif // SWITCHBUTTON_H diff --git a/KylinNM/src/utils.cpp b/KylinNM/src/utils.cpp new file mode 100644 index 0000000..7821508 --- /dev/null +++ b/KylinNM/src/utils.cpp @@ -0,0 +1,267 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include +#include +#include +#include + +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////////// +// The Utils class + +Utils::Utils(){} + +int Utils::m_system(char *cmd) +{ + int status = 0; + pid_t pid; + + if ((pid = vfork()) <0) { + qDebug()<<"failed to create a subprocess by using vfork"; + syslog(LOG_ERR, "failed to create a subprocess by using vfork"); + status = -1; + } else if (pid==0) { + const char *new_argv[4]; + struct sigaction sa_cld; + sa_cld.sa_handler = SIG_DFL; + sa_cld.sa_flags = 0; + + // 在子进程中放开SIGINT信号 + sigemptyset(&sa_cld.sa_mask); + sigaction (SIGINT, &sa_cld, NULL); + sigaction (SIGQUIT, &sa_cld, NULL); + + new_argv[0] = "sh"; + new_argv[1] = "-c"; + new_argv[2] = cmd; + new_argv[3] = NULL; + + // execl("/bin/sh","sh","-c" ,cmd,(char *)0); + if (execve("/bin/sh",(char *const *) new_argv, NULL) <0) { + qDebug()<<"failed to execve a shell command in function m_system"; + syslog(LOG_ERR, "failed to execve %s! errno: %d\n",cmd, errno); + exit(1); + } else { + exit(0); + } + } else { + waitpid(pid,&status,0); + } + + return status; +} + +void Utils::onRequestSendDesktopNotify(QString message) +{ + QDBusInterface iface("org.freedesktop.Notifications", + "/org/freedesktop/Notifications", + "org.freedesktop.Notifications", + QDBusConnection::sessionBus()); + QList args; + args<<(QCoreApplication::applicationName()) + <<((unsigned int) 0) + < +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +/////////////////////////////////////////////////////////////////////////////// +// The Utils class, used to do some assist function + +class Utils : public QObject +{ + Q_OBJECT +public: + Utils(); + + static int m_system(char *cmd); + +public Q_SLOTS: + void onRequestSendDesktopNotify(QString message); + +}; + + +/////////////////////////////////////////////////////////////////////////////// +// The UseQssFile class, set control style by using .qss file + +class UseQssFile +{ +public: + static void setStyle(const QString &style) + { + QString styleName = ":/qss/" + style; + + QFile qss(styleName); + qss.open(QFile::ReadOnly); + qApp->setStyleSheet(qss.readAll()); + qss.close(); + } +}; + + +/////////////////////////////////////////////////////////////////////////////// +// The NetworkSpeed class, get the network upload and download speed + +class NetworkSpeed : public QObject +{ + Q_OBJECT +public: + explicit NetworkSpeed(QObject *parent = nullptr); + + int getCurrentDownloadRates(char *netname,long int * save_rate,long int * tx_rate); //获取当前的流量,参数为将获取到的流量保 +}; + + + + +/////////////////////////////////////////////////////////////////////////////// +// The CustomStyle class, inherit from class QProxyStyle, to change control style customize + +class CustomStyle : public QProxyStyle +{ + Q_OBJECT +public: + explicit CustomStyle(const QString &proxyStyleName = "windows", QObject *parent = nullptr); + + + virtual void drawComplexControl(QStyle::ComplexControl control, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget = nullptr) const; + + virtual void drawControl(QStyle::ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = nullptr) const; + virtual void drawItemPixmap(QPainter *painter, const QRect &rectangle, int alignment, const QPixmap &pixmap) const; + virtual void drawItemText(QPainter *painter, const QRect &rectangle, int alignment, const QPalette &palette, bool enabled, const QString &text, QPalette::ColorRole textRole = QPalette::NoRole) const; + + virtual void drawPrimitive(QStyle::PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = nullptr) const; + virtual QPixmap generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap, const QStyleOption *option) const; + virtual QStyle::SubControl hitTestComplexControl(QStyle::ComplexControl control, const QStyleOptionComplex *option, const QPoint &position, const QWidget *widget = nullptr) const; + virtual QRect itemPixmapRect(const QRect &rectangle, int alignment, const QPixmap &pixmap) const; + virtual QRect itemTextRect(const QFontMetrics &metrics, const QRect &rectangle, int alignment, bool enabled, const QString &text) const; + //virtual int layoutSpacing(QSizePolicy::ControlType control1, QSizePolicy::ControlType control2, Qt::Orientation orientation, const QStyleOption *option, const QWidget *widget); + virtual int pixelMetric(QStyle::PixelMetric metric, const QStyleOption *option = nullptr, const QWidget *widget = nullptr) const; + + virtual void polish(QWidget *widget); + virtual void polish(QApplication *application); + virtual void polish(QPalette &palette); + virtual void unpolish(QWidget *widget); + virtual void unpolish(QApplication *application); + + virtual QSize sizeFromContents(QStyle::ContentsType type, const QStyleOption *option, const QSize &contentsSize, const QWidget *widget = nullptr) const; + virtual QIcon standardIcon(QStyle::StandardPixmap standardIcon, const QStyleOption *option, const QWidget *widget) const; + virtual QPalette standardPalette() const; + + virtual int styleHint(QStyle::StyleHint hint, const QStyleOption *option = nullptr, const QWidget *widget = nullptr, QStyleHintReturn *returnData = nullptr) const; + + virtual QRect subControlRect(QStyle::ComplexControl control, const QStyleOptionComplex *option, QStyle::SubControl subControl, const QWidget *widget = nullptr) const; + + virtual QRect subElementRect(QStyle::SubElement element, const QStyleOption *option, const QWidget *widget = nullptr) const; + +Q_SIGNALS: + +public Q_SLOTS: +}; + +#endif // UTILS_H diff --git a/KylinNM/wireless-security/dlgconnhidwifi.cpp b/KylinNM/wireless-security/dlgconnhidwifi.cpp new file mode 100644 index 0000000..3901449 --- /dev/null +++ b/KylinNM/wireless-security/dlgconnhidwifi.cpp @@ -0,0 +1,379 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see + +#include +#include + +DlgConnHidWifi::DlgConnHidWifi(int type, KylinNM *mainWindow, QWidget *parent) : + isUsed(type), + QDialog(parent), + ui(new Ui::DlgConnHidWifi()) +{ + ui->setupUi(this); + + this->setWindowFlags(Qt::FramelessWindowHint); + this->setAttribute(Qt::WA_TranslucentBackground); + //需要添加 void paintEvent(QPaintEvent *event) 函数 + + QPainterPath path; + auto rect = this->rect(); + rect.adjust(1, 1, -1, -1); + path.addRoundedRect(rect, 6, 6); + setProperty("blurRegion", QRegion(path.toFillPolygon().toPolygon())); + + KylinDBus mkylindbus; + QString strTrans; + strTrans = QString::number(1, 10, 2); + QString sty = "QWidget{border-radius:6px;background-color:rgba(255,255,255,1);border:1px solid rgba(255, 255, 255, 1);}"; + this->setStyleSheet(sty); + //this->setStyleSheet("QWidget{border-radius:6px;background-color:rgba(19,19,20,0.7);border:1px solid rgba(255, 255, 255, 0.05);}"); + + MyQss objQss; + + ui->lbBoder->setStyleSheet("QLabel{border-radius:6px;background-color:rgba(19,19,20,0.95);border:1px solid rgba(255, 255, 255, 0.05);}"); + ui->lbBoder->hide(); + ui->lbLeftupTitle->setStyleSheet("QLabel{border:0px;font-size:20px;color:rgba(255,255,255,0.97);background-color:transparent;}"); + ui->lbConn->setStyleSheet(objQss.labelQss); + ui->lbNetName->setStyleSheet(objQss.labelQss); + ui->lbSecurity->setStyleSheet(objQss.labelQss); + + ui->cbxConn->setStyleSheet(objQss.cbxQss); + ui->cbxConn->setView(new QListView()); + ui->leNetName->setStyleSheet(objQss.leQss); + ui->cbxSecurity->setStyleSheet(objQss.cbxQss); + ui->cbxSecurity->setView(new QListView()); + + ui->btnConnect->setStyleSheet(objQss.btnConnQss); + ui->btnCancel->setStyleSheet(objQss.btnCancelQss); + ui->lineUp->setStyleSheet(objQss.lineQss); + ui->lineDown->setStyleSheet(objQss.lineQss); + + ui->lbLeftupTitle->setText(tr("Add Hidden Wi-Fi")); //加入隐藏Wi-Fi + ui->lbConn->setText(tr("Connection")); //连接设置: + ui->lbNetName->setText(tr("Wi-Fi name")); //网络名称: + ui->lbSecurity->setText(tr("Wi-Fi security")); //Wi-Fi安全性: + ui->btnCancel->setText(tr("Cancel")); //取消 + ui->btnConnect->setText(tr("Connect")); //连接 + + ui->cbxConn->addItem(tr("C_reate…")); //新建... + QString tmpPath = "/tmp/kylin-nm-connshow-" + QDir::home().dirName(); + QString cmd = "nmcli connection show>" + tmpPath; + int status = system(cmd.toUtf8().data()); + if (status != 0){ syslog(LOG_ERR, "execute 'nmcli connection show' in function 'DlgConnHidWifi' failed");} + QFile file(tmpPath); + if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){ + qDebug()<<"Can't open the file!"; + } + QString txt = file.readAll(); + QStringList txtLine = txt.split("\n"); + file.close(); + foreach (QString line, txtLine) { + if (line.indexOf("wifi") != -1 || line.indexOf("802-11-wireless") != -1) { + QStringList subLine = line.split(" "); + ui->cbxConn->addItem(subLine[0]); + } + } + ui->cbxConn->setCurrentIndex(0); + connect(ui->cbxConn,SIGNAL(currentIndexChanged(QString)),this,SLOT(changeWindow())); + + ui->cbxSecurity->addItem(tr("None")); //无 + ui->cbxSecurity->addItem(tr("WPA & WPA2 Personal")); //WPA 及 WPA2 个人 + //ui->cbxSecurity->addItem(tr("WEP 40/128-bit Key (Hex or ASCII)")); //WEP 40/128 位密钥(十六进制或ASCII) + //ui->cbxSecurity->addItem(tr("WEP 128-bit Passphrase")); //WEP 128 位密码句 + //ui->cbxSecurity->addItem("LEAP"); + //ui->cbxSecurity->addItem(tr("Dynamic WEP (802.1X)")); //动态 WEP (802.1x) + //ui->cbxSecurity->addItem(tr("WPA & WPA2 Enterprise")); //WPA 及 WPA2 企业 + ui->cbxSecurity->setCurrentIndex(0); + connect(ui->cbxSecurity,SIGNAL(currentIndexChanged(QString)),this,SLOT(changeDialog())); + + if (isUsed == 0){ + ui->btnConnect->setEnabled(false); + }else{ + ui->cbxConn->setCurrentIndex(isUsed); + ui->leNetName->setText(ui->cbxConn->currentText()); + ui->lbNetName->setEnabled(false); + ui->leNetName->setEnabled(false); + ui->lbSecurity->setEnabled(false); + ui->cbxSecurity->setEnabled(false); + ui->btnConnect->setEnabled(true); + } + + this->setFixedSize(432,358); + + this->mw = mainWindow; + + /**解决锁屏设置 X11BypassWindowManagerHint 属性导致QCombox弹框异常的问题---START---- + ** 手动绑定下拉框视图和下拉框 + **/ + ui->cbxConn->view()->setWindowFlags(Qt::Popup | Qt::X11BypassWindowManagerHint); + ui->cbxConn->view()->setParent(this); + ui->cbxConn->view()->hide(); + ui->cbxConn->installEventFilter(this); + + ui->cbxSecurity->view()->setWindowFlags(Qt::Popup | Qt::X11BypassWindowManagerHint); + ui->cbxSecurity->view()->setParent(this); + ui->cbxSecurity->view()->hide(); + ui->cbxSecurity->installEventFilter(this); + + connect(ui->cbxConn->view(), &QAbstractItemView::pressed, this, [=](QModelIndex index){ + Q_EMIT ui->cbxConn->setCurrentIndex(index.row()); + ui->cbxConn->view()->hide(); + }); + + connect(ui->cbxSecurity->view(), &QAbstractItemView::pressed, this, [=](QModelIndex index){ + ui->cbxSecurity->view()->hide(); + Q_EMIT ui->cbxSecurity->setCurrentIndex(index.row()); + }); + + ui->cbxConn->view()->setGeometry(QRect(ui->cbxConn->geometry().left(), ui->cbxConn->geometry().bottom(), ui->cbxConn->view()->width(), ui->cbxConn->view()->height())); + ui->cbxSecurity->view()->setGeometry(QRect(ui->cbxSecurity->geometry().left(), ui->cbxSecurity->geometry().bottom(), ui->cbxSecurity->view()->width(), ui->cbxSecurity->view()->height())); + /**解决锁屏设置 X11BypassWindowManagerHint 属性导致QCombox弹框异常的问题---END---- + ** 手动绑定下拉框视图和下拉框 + **/ +} + +DlgConnHidWifi::~DlgConnHidWifi() +{ + delete ui; +} + +/**解决锁屏设置 X11BypassWindowManagerHint 属性导致QCombox弹框异常的问题---START---- +** 手动绑定下拉框视图和下拉框 +**/ +bool DlgConnHidWifi::eventFilter(QObject *obj, QEvent *ev) +{ + if(ev->type() == QEvent::MouseButtonPress) + { + if(obj == ui->cbxConn) + { + ui->cbxConn->view()->setVisible(!ui->cbxConn->view()->isVisible()); + if(ui->cbxConn->view()->isVisible()) + ui->cbxConn->view()->setFocus(); + } else if (obj == ui->cbxSecurity) + { + ui->cbxSecurity->view()->setVisible(!ui->cbxSecurity->view()->isVisible()); + if(ui->cbxSecurity->view()->isVisible()) + ui->cbxSecurity->view()->setFocus(); + } + } + return false; +} +/**解决锁屏设置 X11BypassWindowManagerHint 属性导致QCombox弹框异常的问题---END---- +** 手动绑定下拉框视图和下拉框 +**/ + +void DlgConnHidWifi::mousePressEvent(QMouseEvent *event){ + if(event->button() == Qt::LeftButton){ + this->isPress = true; + this->winPos = this->pos(); + this->dragPos = event->globalPos(); + event->accept(); + } +} +void DlgConnHidWifi::mouseReleaseEvent(QMouseEvent *event){ + this->isPress = false; +} +void DlgConnHidWifi::mouseMoveEvent(QMouseEvent *event){ + if(this->isPress){ + this->move(this->winPos - (this->dragPos - event->globalPos())); + event->accept(); + } +} + +//切换到其他Wi-Fi安全类型 +void DlgConnHidWifi::changeDialog() +{ + if(ui->cbxSecurity->currentIndex()==0){ + qDebug()<<"it's not need to change dialog"; + } else if(ui->cbxSecurity->currentIndex()==1) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiWpa *connHidWifiWpa = new DlgConnHidWifiWpa(0, mw, this->parentWidget()); + connHidWifiWpa->show(); + connect(connHidWifiWpa, SIGNAL(reSetWifiList() ), mw, SLOT(on_btnWifiList_clicked()) ); + } else if(ui->cbxSecurity->currentIndex()==2) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiWep *connHidWifiWep = new DlgConnHidWifiWep(0,this->parentWidget()); + connHidWifiWep->show(); + } else if(ui->cbxSecurity->currentIndex()==3) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiWep *connHidWifiWep = new DlgConnHidWifiWep(1,this->parentWidget()); + connHidWifiWep->show(); + } else if(ui->cbxSecurity->currentIndex()==4) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiLeap *connHidWifiLeap = new DlgConnHidWifiLeap(this->parentWidget()); + connHidWifiLeap->show(); + } else if(ui->cbxSecurity->currentIndex()==5) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecTls *connHidWifiSecTls = new DlgConnHidWifiSecTls(0,this->parentWidget()); + connHidWifiSecTls->show(); + } else { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecTls *connHidWifiSecTls = new DlgConnHidWifiSecTls(1,this->parentWidget()); + connHidWifiSecTls->show(); + } +} + +//同一 Wi-Fi安全类型的窗口变换 +void DlgConnHidWifi::changeWindow(){ + if (ui->cbxConn->currentIndex() == 0){ + isUsed = ui->cbxConn->currentIndex(); + ui->cbxConn->setCurrentIndex(0); + ui->leNetName->setText(""); + ui->lbNetName->setEnabled(true); + ui->leNetName->setEnabled(true); + ui->lbSecurity->setEnabled(true); + ui->cbxSecurity->setEnabled(true); + ui->btnConnect->setEnabled(false); + }else if (ui->cbxConn->currentIndex() >= 1){ + QString tmpPath = "/tmp/kylin-nm-connshow-" + QDir::home().dirName(); + QString name = ui->cbxConn->currentText(); + QString currStr = "nmcli connection show \"" + name.replace("\"","\\\"") + "\" >" + tmpPath; + + int status = system(currStr.toUtf8().data()); + if (status != 0){ syslog(LOG_ERR, "execute 'nmcli connection show' in function 'changeWindow' failed");} + + QFile file(tmpPath); + if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){ + qDebug()<<"Can't open the file!"; + } + QString txt = file.readAll(); + file.close(); + if (txt.indexOf("802-11-wireless-security.key-mgmt:") != -1){ +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiWpa *connHidWifiWpa = new DlgConnHidWifiWpa(ui->cbxConn->currentIndex(), mw, this->parentWidget()); + connHidWifiWpa->show(); + connect(connHidWifiWpa, SIGNAL(reSetWifiList() ), mw, SLOT(on_btnWifiList_clicked()) ); + }else { + isUsed = ui->cbxConn->currentIndex(); + ui->leNetName->setText(ui->cbxConn->currentText()); + ui->lbNetName->setEnabled(false); + ui->leNetName->setEnabled(false); + ui->lbSecurity->setEnabled(false); + ui->cbxSecurity->setEnabled(false); + ui->btnConnect->setEnabled(true); + } + } +} + +void DlgConnHidWifi::on_btnCancel_clicked() +{ + this->close(); +} + +void DlgConnHidWifi::on_btnConnect_clicked() +{ + QThread *t = new QThread(); + connect(t, SIGNAL(finished()), t, SLOT(deleteLater())); + connect(t, SIGNAL(started()), this, SLOT(slotStartLoading())); + connect(this, SIGNAL(stopSignal()), t, SLOT(quit())); + t->start(); + + QString wifiName = ui->leNetName->text(); + BackThread *bt = new BackThread(); + strWifiname = wifiName; + //点击连接按钮以连接隐藏WiFi + if (isUsed == 0) { + int x = 0; + do { + sleep(1); + QString tmpPath = "/tmp/kylin-nm-btoutput-" + QDir::home().dirName(); + QString cmd = "nmcli device wifi connect \"" + wifiName.replace("\"","\\\"") + "\" password '' hidden yes > " + tmpPath; + + int status = system(cmd.toUtf8().data()); + if (status != 0) + syslog(LOG_ERR, "execute 'nmcli device wifi connect' in function 'on_btnConnect_clicked' failed"); + + QFile file(tmpPath); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + qDebug()<<"Can't open the file!"<execConnWifi(wifiName); + connect(this, SIGNAL(sendMessage()), this,SLOT(emitSignal() )); + QTimer::singleShot(4*1000, this, SLOT(emitSignal() )); + } + this->close(); +} + +void DlgConnHidWifi::on_leNetName_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == ""){ + ui->btnConnect->setEnabled(false); + } else { + ui->btnConnect->setEnabled(true); + } +} + +void DlgConnHidWifi::slotStartLoading() +{ + mw->startLoading(); +} + +void DlgConnHidWifi::on_execSecConn() +{ + QString name = strWifiname; + QString str = "nmcli device wifi connect \"" + name.replace("\"","\\\"") + "\" password ''"; + int status = system(str.toUtf8().data()); + if (status != 0){ syslog(LOG_ERR, "execute 'nmcli device wifi connect' in function 'on_execSecConn' failed");} + connect(this, SIGNAL(sendMessage()), this,SLOT(emitSignal() )); + QTimer::singleShot(3*1000, this, SLOT(emitSignal() )); +} + +void DlgConnHidWifi::emitSignal() +{ + emit reSetWifiList(); + mw->stopLoading(); + emit this->stopSignal(); +} + +void DlgConnHidWifi::paintEvent(QPaintEvent *event) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); + QWidget::paintEvent(event); +} diff --git a/KylinNM/wireless-security/dlgconnhidwifi.h b/KylinNM/wireless-security/dlgconnhidwifi.h new file mode 100644 index 0000000..33d4c42 --- /dev/null +++ b/KylinNM/wireless-security/dlgconnhidwifi.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include +#include +#include +#include + +class KylinNM; + +namespace Ui { +class DlgConnHidWifi; +} + +class DlgConnHidWifi : public QDialog +{ + Q_OBJECT + +public: + explicit DlgConnHidWifi(int type, KylinNM *mw = 0, QWidget *parent = 0); + ~DlgConnHidWifi(); + +protected: + void paintEvent(QPaintEvent *event); + + /**解决锁屏设置 X11BypassWindowManagerHint 属性导致QCombox弹框异常的问题---START---- + ** 手动绑定下拉框视图和下拉框 过滤点击事件 + **/ + bool eventFilter(QObject *obj, QEvent *ev) override; + +public Q_SLOTS: + void changeDialog(); + void changeWindow(); + void emitSignal(); + void on_execSecConn(); + void slotStartLoading(); + +private Q_SLOTS: + void on_btnCancel_clicked(); + + void on_btnConnect_clicked(); + + void on_leNetName_textEdited(const QString &arg1); + +Q_SIGNALS: + void reSetWifiList(); + void sendMessage(); + void execSecConn(); + void stopSignal(); + +private: + Ui::DlgConnHidWifi *ui; + int isUsed;//=0 current wifi not used before; >=1 used + KylinNM *mw; + QString strWifiname; +// QString labelQss, cbxQss, leQss, btnConnQss, btnCancelQss, lineQss; + + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + + bool isPress; + QPoint winPos; + QPoint dragPos; +}; + +#endif // DLGCONNHIDWIFI_H diff --git a/KylinNM/wireless-security/dlgconnhidwifi.ui b/KylinNM/wireless-security/dlgconnhidwifi.ui new file mode 100644 index 0000000..1d23ac6 --- /dev/null +++ b/KylinNM/wireless-security/dlgconnhidwifi.ui @@ -0,0 +1,238 @@ + + + DlgConnHidWifi + + + + 0 + 0 + 432 + 358 + + + + Connect to Hidden Wi-Fi Network + + + + + 76 + 80 + 90 + 20 + + + + + 10 + + + + Qt::LeftToRight + + + + + + + + + 175 + 75 + 182 + 32 + + + + + 10 + + + + + + + + + + 76 + 221 + 90 + 20 + + + + + 10 + + + + + + + + + + 175 + 170 + 182 + 32 + + + + + 10 + + + + + + + + + + 76 + 176 + 90 + 20 + + + + + 10 + + + + + + + + + + 175 + 215 + 182 + 32 + + + + + 10 + + + + + + + 315 + 310 + 90 + 30 + + + + + 10 + + + + + + + + + + 215 + 310 + 90 + 30 + + + + + 10 + + + + + + + + + + 30 + 30 + 240 + 22 + + + + + 9 + + + + + + + + + + 0 + 0 + 432 + 358 + + + + + + + + + + 10 + 140 + 412 + 1 + + + + + + + Qt::Horizontal + + + + + + 10 + 280 + 412 + 1 + + + + + + + Qt::Horizontal + + + lbBoder + lbConn + cbxConn + lbNetName + cbxSecurity + lbSecurity + leNetName + btnConnect + btnCancel + lbLeftupTitle + lineUp + lineDown + + + + diff --git a/KylinNM/wireless-security/dlgconnhidwifileap.cpp b/KylinNM/wireless-security/dlgconnhidwifileap.cpp new file mode 100644 index 0000000..5926b12 --- /dev/null +++ b/KylinNM/wireless-security/dlgconnhidwifileap.cpp @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see + +DlgConnHidWifiLeap::DlgConnHidWifiLeap(QWidget *parent) : + QDialog(parent), + ui(new Ui::DlgConnHidWifiLeap) +{ + ui->setupUi(this); + + this->setWindowFlags(Qt::FramelessWindowHint); + this->setAttribute(Qt::WA_TranslucentBackground); + //需要添加 void paintEvent(QPaintEvent *event) 函数 + + QPainterPath path; + auto rect = this->rect(); + rect.adjust(1, 1, -1, -1); + path.addRoundedRect(rect, 6, 6); + setProperty("blurRegion", QRegion(path.toFillPolygon().toPolygon())); + + this->setStyleSheet("QWidget{border-radius:6px;background-color:rgba(19,19,20,0.7);border:1px solid rgba(255, 255, 255, 0.05);}"); + + MyQss objQss; + + ui->lbBoder->setStyleSheet("QLabel{border-radius:6px;background-color:rgba(19,19,20,0.95);border:1px solid rgba(255, 255, 255, 0.05);}"); + ui->lbBoder->hide(); + ui->lbLeftupTitle->setStyleSheet("QLabel{border:0px;font-size:20px;color:rgba(255,255,255,0.97);background-color:transparent;}"); + ui->lbConn->setStyleSheet(objQss.labelQss); + ui->lbNetName->setStyleSheet(objQss.labelQss); + ui->lbSecurity->setStyleSheet(objQss.labelQss); + ui->lbUserName->setStyleSheet(objQss.labelQss); + ui->lbPassword->setStyleSheet(objQss.labelQss); + + ui->cbxConn->setStyleSheet(objQss.cbxQss); + ui->cbxConn->setView(new QListView()); + ui->leNetName->setStyleSheet(objQss.leQss); + ui->leUserName->setStyleSheet(objQss.leQss); + ui->lePassword->setStyleSheet(objQss.leQss); + ui->cbxSecurity->setStyleSheet(objQss.cbxQss); + ui->cbxSecurity->setView(new QListView()); + ui->checkBoxPwd->setStyleSheet(objQss.checkBoxQss); + + ui->btnCancel->setStyleSheet(objQss.btnCancelQss); + ui->btnConnect->setStyleSheet(objQss.btnConnQss); + ui->lineUp->setStyleSheet(objQss.lineQss); + ui->lineDown->setStyleSheet(objQss.lineQss); + + ui->lbLeftupTitle->setText(tr("Add hidden Wi-Fi")); //加入隐藏Wi-Fi + ui->lbConn->setText(tr("Connection")); //连接设置: + ui->lbNetName->setText(tr("Network name")); //网络名称: + ui->lbSecurity->setText(tr("Wi-Fi security")); //Wi-Fi安全性: + ui->lbUserName->setText(tr("Username")); //用户名: + ui->lbPassword->setText(tr("Password")); //密码: + ui->btnCancel->setText(tr("Cancel")); //取消 + ui->btnConnect->setText(tr("Connect")); //连接 + + ui->cbxConn->addItem(tr("C_reate…")); //新建... + int status = system("nmcli connection show>/tmp/kylin-nm-connshow"); + if (status != 0){ syslog(LOG_ERR, "execute 'nmcli connection show' in function 'DlgConnHidWifiLeap' failed");} + QFile file("/tmp/kylin-nm-connshow"); + if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){ + qDebug()<<"Can't open the file!"; + } + QString txt = file.readAll(); + QStringList txtLine = txt.split("\n"); + file.close(); + foreach (QString line, txtLine) { + if(line.indexOf("wifi") != -1){ + QStringList subLine = line.split(" "); + ui->cbxConn->addItem(subLine[0]); + } + } + ui->cbxConn->setCurrentIndex(0); + + ui->cbxSecurity->addItem(tr("None")); //无 + ui->cbxSecurity->addItem(tr("WPA & WPA2 Personal")); //WPA 及 WPA2 个人 + ui->cbxSecurity->addItem(tr("WEP 40/128-bit Key (Hex or ASCII)")); //WEP 40/128 位密钥(十六进制或ASCII) + ui->cbxSecurity->addItem(tr("WEP 128-bit Passphrase")); //WEP 128 位密码句 + ui->cbxSecurity->addItem("LEAP"); + ui->cbxSecurity->addItem(tr("Dynamic WEP (802.1X)")); //动态 WEP (802.1x) + ui->cbxSecurity->addItem(tr("WPA & WPA2 Enterprise")); //WPA 及 WPA2 企业 + ui->cbxSecurity->setCurrentIndex(4); + connect(ui->cbxSecurity,SIGNAL(currentIndexChanged(QString)),this,SLOT(changeDialog())); + + ui->btnConnect->setEnabled(false); + + this->setFixedSize(432,434); + + /**解决锁屏设置 X11BypassWindowManagerHint 属性导致QCombox弹框异常的问题---START---- + ** 手动绑定下拉框视图和下拉框 + **/ + ui->cbxConn->view()->setWindowFlags(Qt::Popup | Qt::X11BypassWindowManagerHint); + ui->cbxConn->view()->setParent(this); + ui->cbxConn->view()->hide(); + ui->cbxConn->installEventFilter(this); + + ui->cbxSecurity->view()->setWindowFlags(Qt::Popup | Qt::X11BypassWindowManagerHint); + ui->cbxSecurity->view()->setParent(this); + ui->cbxSecurity->view()->hide(); + ui->cbxSecurity->installEventFilter(this); + + connect(ui->cbxConn->view(), &QAbstractItemView::pressed, this, [=](QModelIndex index){ + Q_EMIT ui->cbxConn->setCurrentIndex(index.row()); + ui->cbxConn->view()->hide(); + }); + + connect(ui->cbxSecurity->view(), &QAbstractItemView::pressed, this, [=](QModelIndex index){ + ui->cbxSecurity->view()->hide(); + Q_EMIT ui->cbxSecurity->setCurrentIndex(index.row()); + }); + + ui->cbxConn->view()->setGeometry(QRect(ui->cbxConn->geometry().left(), ui->cbxConn->geometry().bottom(), ui->cbxConn->view()->width(), ui->cbxConn->view()->height())); + ui->cbxSecurity->view()->setGeometry(QRect(ui->cbxSecurity->geometry().left(), ui->cbxSecurity->geometry().bottom(), ui->cbxSecurity->view()->width(), ui->cbxSecurity->view()->height())); + /**解决锁屏设置 X11BypassWindowManagerHint 属性导致QCombox弹框异常的问题---END---- + ** 手动绑定下拉框视图和下拉框 + **/ +} + +DlgConnHidWifiLeap::~DlgConnHidWifiLeap() +{ + delete ui; +} + +/**解决锁屏设置 X11BypassWindowManagerHint 属性导致QCombox弹框异常的问题---START---- +** 手动绑定下拉框视图和下拉框 +**/ +bool DlgConnHidWifiLeap::eventFilter(QObject *obj, QEvent *ev) +{ + if(ev->type() == QEvent::MouseButtonPress) + { + if(obj == ui->cbxConn) + { + ui->cbxConn->view()->setVisible(!ui->cbxConn->view()->isVisible()); + if(ui->cbxConn->view()->isVisible()) + ui->cbxConn->view()->setFocus(); + } else if (obj == ui->cbxSecurity) + { + ui->cbxSecurity->view()->setVisible(!ui->cbxSecurity->view()->isVisible()); + if(ui->cbxSecurity->view()->isVisible()) + ui->cbxSecurity->view()->setFocus(); + } + } + return false; +} +/**解决锁屏设置 X11BypassWindowManagerHint 属性导致QCombox弹框异常的问题---END---- +** 手动绑定下拉框视图和下拉框 +**/ + +void DlgConnHidWifiLeap::mousePressEvent(QMouseEvent *event){ + if(event->button() == Qt::LeftButton){ + this->isPress = true; + this->winPos = this->pos(); + this->dragPos = event->globalPos(); + event->accept(); + } +} +void DlgConnHidWifiLeap::mouseReleaseEvent(QMouseEvent *event){ + this->isPress = false; +} +void DlgConnHidWifiLeap::mouseMoveEvent(QMouseEvent *event){ + if(this->isPress){ + this->move(this->winPos - (this->dragPos - event->globalPos())); + event->accept(); + } +} + +//切换到其他Wi-Fi安全类型 +void DlgConnHidWifiLeap::changeDialog() +{ + if(ui->cbxSecurity->currentIndex()==0){ +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifi *connHidWifi = new DlgConnHidWifi(0, 0,this->parentWidget()); + connHidWifi->show(); + } else if(ui->cbxSecurity->currentIndex()==1) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiWpa *connHidWifiWpa = new DlgConnHidWifiWpa(0, 0, this->parentWidget()); + connHidWifiWpa->show(); + } else if(ui->cbxSecurity->currentIndex()==2) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiWep *connHidWifiWep = new DlgConnHidWifiWep(0, this->parentWidget()); + connHidWifiWep->show(); + } else if(ui->cbxSecurity->currentIndex()==3) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiWep *connHidWifiWep = new DlgConnHidWifiWep(1, this->parentWidget()); + connHidWifiWep->show(); + } else if(ui->cbxSecurity->currentIndex()==4) { + qDebug()<<"it's not need to change dialog"; + } else if(ui->cbxSecurity->currentIndex()==5) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecTls *connHidWifiSecTls = new DlgConnHidWifiSecTls(0, this->parentWidget()); + connHidWifiSecTls->show(); + } else { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecTls *connHidWifiSecTls = new DlgConnHidWifiSecTls(1, this->parentWidget()); + connHidWifiSecTls->show(); + } +} + +void DlgConnHidWifiLeap::on_btnCancel_clicked() +{ + this->close(); +} + +void DlgConnHidWifiLeap::on_btnConnect_clicked() +{ + this->close(); +} + +void DlgConnHidWifiLeap::on_checkBoxPwd_stateChanged(int arg1) +{ + if (arg1 == 0) { + ui->lePassword ->setEchoMode(QLineEdit::Password); + } else { + ui->lePassword->setEchoMode(QLineEdit::Normal); + } +} + +void DlgConnHidWifiLeap::on_leNetName_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == "" || ui->lePassword->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->leNetName->text() == "" || ui->leUserName->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->lePassword->text() == "" || ui->leUserName->text() == ""){ + ui->btnConnect->setEnabled(false); + } else { + ui->btnConnect->setEnabled(true); + } +} + +void DlgConnHidWifiLeap::on_leUserName_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->leUserName->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->lePassword->text() == ""){ + ui->btnConnect->setEnabled(false); + } else { + ui->btnConnect->setEnabled(true); + } +} + +void DlgConnHidWifiLeap::on_lePassword_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->leUserName->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->lePassword->text() == ""){ + ui->btnConnect->setEnabled(false); + } else { + ui->btnConnect->setEnabled(true); + } +} + +void DlgConnHidWifiLeap::paintEvent(QPaintEvent *event) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); + QWidget::paintEvent(event); +} diff --git a/KylinNM/wireless-security/dlgconnhidwifileap.h b/KylinNM/wireless-security/dlgconnhidwifileap.h new file mode 100644 index 0000000..de96ce2 --- /dev/null +++ b/KylinNM/wireless-security/dlgconnhidwifileap.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include + +namespace Ui { +class DlgConnHidWifiLeap; +} + +class DlgConnHidWifiLeap : public QDialog +{ + Q_OBJECT + +public: + explicit DlgConnHidWifiLeap(QWidget *parent = 0); + ~DlgConnHidWifiLeap(); + +protected: + void paintEvent(QPaintEvent *event); + + /**解决锁屏设置 X11BypassWindowManagerHint 属性导致QCombox弹框异常的问题---START---- + ** 手动绑定下拉框视图和下拉框 过滤点击事件 + **/ + bool eventFilter(QObject *obj, QEvent *ev) override; + +public slots: + void changeDialog(); + +private slots: + void on_btnCancel_clicked(); + + void on_btnConnect_clicked(); + + void on_checkBoxPwd_stateChanged(int arg1); + + void on_leNetName_textEdited(const QString &arg1); + + void on_leUserName_textEdited(const QString &arg1); + + void on_lePassword_textEdited(const QString &arg1); + +private: + Ui::DlgConnHidWifiLeap *ui; + + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + +// QString labelQss, cbxQss, leQss, btnConnQss, btnCancelQss, lineQss, checkBoxQss; + + bool isPress; + QPoint winPos; + QPoint dragPos; +}; + +#endif // DLGCONNHIDWIFILEAP_H diff --git a/KylinNM/wireless-security/dlgconnhidwifileap.ui b/KylinNM/wireless-security/dlgconnhidwifileap.ui new file mode 100644 index 0000000..b8a5b4b --- /dev/null +++ b/KylinNM/wireless-security/dlgconnhidwifileap.ui @@ -0,0 +1,319 @@ + + + DlgConnHidWifiLeap + + + + 0 + 0 + 432 + 434 + + + + Connect to Hidden Wi-Fi Network + + + + + 175 + 264 + 182 + 32 + + + + + 10 + + + + + + + 175 + 219 + 182 + 32 + + + + + 10 + + + + + + + + + + 215 + 390 + 90 + 30 + + + + + 10 + + + + + + + + + + 76 + 84 + 90 + 20 + + + + + 10 + + + + Qt::LeftToRight + + + + + + + + + 175 + 79 + 182 + 32 + + + + + 10 + + + + + + + + + + 76 + 270 + 90 + 20 + + + + + 10 + + + + + + + + + + 332 + 321 + 18 + 9 + + + + + 10 + + + + + + + + + + 76 + 315 + 90 + 20 + + + + + 10 + + + + + + + + + + 76 + 180 + 90 + 20 + + + + + 10 + + + + + + + + + + 76 + 225 + 90 + 20 + + + + + 10 + + + + + + + + + + 315 + 390 + 90 + 30 + + + + + 10 + + + + + + + + + + 175 + 309 + 182 + 32 + + + + + 10 + + + + QLineEdit::Password + + + + + + 175 + 174 + 182 + 32 + + + + + 10 + + + + + + + 0 + 0 + 432 + 434 + + + + + + + + + + 30 + 32 + 140 + 22 + + + + + + + + + + 10 + 134 + 412 + 1 + + + + Qt::Horizontal + + + + + + 10 + 364 + 412 + 1 + + + + Qt::Horizontal + + + lbBoder + leUserName + cbxSecurity + btnCancel + lbConn + cbxConn + lbUserName + lbPassword + lbNetName + lbSecurity + btnConnect + lePassword + leNetName + checkBoxPwd + lbLeftupTitle + lineUp + lineDown + + + + diff --git a/KylinNM/wireless-security/dlgconnhidwifisecfast.cpp b/KylinNM/wireless-security/dlgconnhidwifisecfast.cpp new file mode 100644 index 0000000..b4d07f5 --- /dev/null +++ b/KylinNM/wireless-security/dlgconnhidwifisecfast.cpp @@ -0,0 +1,403 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see + +DlgConnHidWifiSecFast::DlgConnHidWifiSecFast(int type, QWidget *parent) : + WepOrWpa(type), + QDialog(parent), + ui(new Ui::DlgConnHidWifiSecFast) +{ + ui->setupUi(this); + + this->setWindowFlags(Qt::FramelessWindowHint); + this->setAttribute(Qt::WA_TranslucentBackground); + //需要添加 void paintEvent(QPaintEvent *event) 函数 + + QPainterPath path; + auto rect = this->rect(); + rect.adjust(1, 1, -1, -1); + path.addRoundedRect(rect, 6, 6); + setProperty("blurRegion", QRegion(path.toFillPolygon().toPolygon())); + + this->setStyleSheet("QWidget{border-radius:6px;background-color:rgba(19,19,20,0.7);border:1px solid rgba(255, 255, 255, 0.05);}"); + + MyQss objQss; + + ui->lbBoder->setStyleSheet("QLabel{border-radius:6px;background-color:rgba(19,19,20,0.95);border:1px solid rgba(255, 255, 255, 0.05);}"); + ui->lbBoder->hide(); + ui->lbLeftupTitle->setStyleSheet("QLabel{border:0px;font-size:20px;color:rgba(255,255,255,0.97);background-color:transparent;}"); + ui->lbConn->setStyleSheet(objQss.labelQss); + ui->lbNetName->setStyleSheet(objQss.labelQss); + ui->lbSecurity->setStyleSheet(objQss.labelQss); + ui->lbAuth->setStyleSheet(objQss.labelQss); + ui->lbAnonyId->setStyleSheet(objQss.labelQss); + ui->checkBoxAutoPCA->setStyleSheet(objQss.checkBoxCAQss); + ui->lbPCAfile->setStyleSheet(objQss.labelQss); + ui->lbInnerAuth->setStyleSheet(objQss.labelQss); + ui->lbUserName->setStyleSheet(objQss.labelQss); + ui->lbPassword->setStyleSheet(objQss.labelQss); + + ui->cbxConn->setStyleSheet(objQss.cbxQss); + ui->cbxConn->setView(new QListView()); + ui->leNetName->setStyleSheet(objQss.leQss); + ui->cbxSecurity->setStyleSheet(objQss.cbxQss); + ui->cbxSecurity->setView(new QListView()); + ui->cbxAuth->setStyleSheet(objQss.cbxQss); + ui->cbxAuth->setView(new QListView()); + ui->leAnonyId->setStyleSheet(objQss.leQss); + ui->cbxAutoPCA->setStyleSheet(objQss.cbxQss); + ui->cbxAutoPCA->setView(new QListView()); + ui->lePCAfile->setStyleSheet(objQss.leQss); + ui->cbxInnerAuth->setStyleSheet(objQss.cbxQss); + ui->cbxInnerAuth->setView(new QListView()); + ui->leUserName->setStyleSheet(objQss.leQss); + ui->lePassword->setStyleSheet(objQss.leQss); + ui->checkBoxPwd->setStyleSheet(objQss.checkBoxQss); + + ui->btnCancel->setStyleSheet(objQss.btnCancelQss); + ui->btnConnect->setStyleSheet(objQss.btnConnQss); + ui->lineUp->setStyleSheet(objQss.lineQss); + ui->lineDown->setStyleSheet(objQss.lineQss); + + ui->lbLeftupTitle->setText(tr("Add hidden Wi-Fi")); //加入隐藏Wi-Fi + ui->lbConn->setText(tr("Connection")); //连接设置: + ui->lbNetName->setText(tr("Network name")); //网络名称: + ui->lbSecurity->setText(tr("Wi-Fi security")); //Wi-Fi安全性: + ui->lbAuth->setText(tr("Authentication")); //认证: + ui->lbAnonyId->setText(tr("Anonymous identity")); //匿名身份: + ui->checkBoxAutoPCA->setText(tr("Allow automatic PAC pro_visioning")); //自动PAC配置: + ui->lbPCAfile->setText(tr("PAC file"));//PAC文件: + ui->lbInnerAuth->setText(tr("Inner authentication")); //内部认证: + ui->lbUserName->setText(tr("Username")); //用户名: + ui->lbPassword->setText(tr("Password")); //密码: + ui->btnCancel->setText(tr("Cancel")); //取消 + ui->btnConnect->setText(tr("Connect")); //连接 + + ui->checkBoxAutoPCA->setFocusPolicy(Qt::NoFocus); + ui->checkBoxPwd->setFocusPolicy(Qt::NoFocus); + + ui->cbxConn->addItem(tr("C_reate…")); //新建... + int status = system("nmcli connection show>/tmp/kylin-nm-connshow"); + if (status != 0){ syslog(LOG_ERR, "execute 'nmcli connection show' in function 'DlgConnHidWifiSecFast' failed");} + QFile file("/tmp/kylin-nm-connshow"); + if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){ + qDebug()<<"Can't open the file!"; + } + QString txt = file.readAll(); + QStringList txtLine = txt.split("\n"); + file.close(); + foreach (QString line, txtLine) { + if(line.indexOf("wifi") != -1){ + QStringList subLine = line.split(" "); + ui->cbxConn->addItem(subLine[0]); + } + } + ui->cbxConn->setCurrentIndex(0); + + ui->cbxSecurity->addItem(tr("None")); //无 + ui->cbxSecurity->addItem(tr("WPA & WPA2 Personal")); //WPA 及 WPA2 个人 + ui->cbxSecurity->addItem(tr("WEP 40/128-bit Key (Hex or ASCII)")); //WEP 40/128 位密钥(十六进制或ASCII) + ui->cbxSecurity->addItem(tr("WEP 128-bit Passphrase")); //WEP 128 位密码句 + ui->cbxSecurity->addItem("LEAP"); + ui->cbxSecurity->addItem(tr("Dynamic WEP (802.1X)")); //动态 WEP (802.1x) + ui->cbxSecurity->addItem(tr("WPA & WPA2 Enterprise")); //WPA 及 WPA2 企业 + if (WepOrWpa == 0) { + ui->cbxSecurity->setCurrentIndex(5); + } else if (WepOrWpa == 1) { + ui->cbxSecurity->setCurrentIndex(6); + } + connect(ui->cbxSecurity,SIGNAL(currentIndexChanged(QString)),this,SLOT(changeDialogSecu())); + + ui->cbxAuth->addItem("TLS"); + ui->cbxAuth->addItem("LEAP"); + ui->cbxAuth->addItem("PWD"); + ui->cbxAuth->addItem("FAST"); + ui->cbxAuth->addItem(tr("Tunneled TLS"));//隧道 TLS + ui->cbxAuth->addItem(tr("Protected EAP (PEAP)")); //受保护的 EAP + ui->cbxAuth->setCurrentIndex(3); + connect(ui->cbxAuth,SIGNAL(currentIndexChanged(QString)),this,SLOT(changeDialogAuth())); + + ui->checkBoxAutoPCA->setCheckState(Qt::Checked); + + ui->cbxAutoPCA->addItem(tr("Anonymous")); //匿名 + ui->cbxAutoPCA->addItem(tr("Authenticated")); //已认证 + ui->cbxAutoPCA->addItem(tr("Both")); //两者兼用 + ui->cbxAutoPCA->setCurrentIndex(0); + + ui->lePCAfile->setText(tr("None")); //(无) + + ui->cbxInnerAuth->addItem("GTC"); + ui->cbxInnerAuth->addItem("MSCHAPv2"); + ui->cbxInnerAuth->setCurrentIndex(0); + + ui->btnConnect->setEnabled(false); + + this->setFixedSize(432,673); + + /**解决锁屏设置 X11BypassWindowManagerHint 属性导致QCombox弹框异常的问题---START---- + ** 手动绑定下拉框视图和下拉框 + **/ + ui->cbxConn->view()->setWindowFlags(Qt::Popup | Qt::X11BypassWindowManagerHint); + ui->cbxConn->view()->setParent(this); + ui->cbxConn->view()->hide(); + ui->cbxConn->installEventFilter(this); + + ui->cbxSecurity->view()->setWindowFlags(Qt::Popup | Qt::X11BypassWindowManagerHint); + ui->cbxSecurity->view()->setParent(this); + ui->cbxSecurity->view()->hide(); + ui->cbxSecurity->installEventFilter(this); + + connect(ui->cbxConn->view(), &QAbstractItemView::pressed, this, [=](QModelIndex index){ + Q_EMIT ui->cbxConn->setCurrentIndex(index.row()); + ui->cbxConn->view()->hide(); + }); + + connect(ui->cbxSecurity->view(), &QAbstractItemView::pressed, this, [=](QModelIndex index){ + ui->cbxSecurity->view()->hide(); + Q_EMIT ui->cbxSecurity->setCurrentIndex(index.row()); + }); + + ui->cbxConn->view()->setGeometry(QRect(ui->cbxConn->geometry().left(), ui->cbxConn->geometry().bottom(), ui->cbxConn->view()->width(), ui->cbxConn->view()->height())); + ui->cbxSecurity->view()->setGeometry(QRect(ui->cbxSecurity->geometry().left(), ui->cbxSecurity->geometry().bottom(), ui->cbxSecurity->view()->width(), ui->cbxSecurity->view()->height())); + /**解决锁屏设置 X11BypassWindowManagerHint 属性导致QCombox弹框异常的问题---END---- + ** 手动绑定下拉框视图和下拉框 + **/ +} + +DlgConnHidWifiSecFast::~DlgConnHidWifiSecFast() +{ + delete ui; +} + +/**解决锁屏设置 X11BypassWindowManagerHint 属性导致QCombox弹框异常的问题---START---- +** 手动绑定下拉框视图和下拉框 +**/ +bool DlgConnHidWifiSecFast::eventFilter(QObject *obj, QEvent *ev) +{ + if(ev->type() == QEvent::MouseButtonPress) + { + if(obj == ui->cbxConn) + { + ui->cbxConn->view()->setVisible(!ui->cbxConn->view()->isVisible()); + if(ui->cbxConn->view()->isVisible()) + ui->cbxConn->view()->setFocus(); + } else if (obj == ui->cbxSecurity) + { + ui->cbxSecurity->view()->setVisible(!ui->cbxSecurity->view()->isVisible()); + if(ui->cbxSecurity->view()->isVisible()) + ui->cbxSecurity->view()->setFocus(); + } + } + return false; +} +/**解决锁屏设置 X11BypassWindowManagerHint 属性导致QCombox弹框异常的问题---END---- +** 手动绑定下拉框视图和下拉框 +**/ + +void DlgConnHidWifiSecFast::mousePressEvent(QMouseEvent *event){ + if(event->button() == Qt::LeftButton){ + this->isPress = true; + this->winPos = this->pos(); + this->dragPos = event->globalPos(); + event->accept(); + } +} +void DlgConnHidWifiSecFast::mouseReleaseEvent(QMouseEvent *event){ + this->isPress = false; +} +void DlgConnHidWifiSecFast::mouseMoveEvent(QMouseEvent *event){ + if(this->isPress){ + this->move(this->winPos - (this->dragPos - event->globalPos())); + event->accept(); + } +} + +void DlgConnHidWifiSecFast::changeDialogSecu() +{ + if(ui->cbxSecurity->currentIndex()==0){ +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifi *connHidWifi = new DlgConnHidWifi(0, 0, this->parentWidget()); + connHidWifi->show(); + } else if(ui->cbxSecurity->currentIndex()==1) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiWpa *connHidWifiWpa = new DlgConnHidWifiWpa(0, 0, this->parentWidget()); + connHidWifiWpa->show(); + } else if(ui->cbxSecurity->currentIndex()==2) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiWep *connHidWifiWep = new DlgConnHidWifiWep(0, this->parentWidget()); + connHidWifiWep->show(); + } else if(ui->cbxSecurity->currentIndex()==3) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiWep *connHidWifiWep = new DlgConnHidWifiWep(1, this->parentWidget()); + connHidWifiWep->show(); + } else if(ui->cbxSecurity->currentIndex()==4) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiLeap *connHidWifiLeap = new DlgConnHidWifiLeap(this->parentWidget()); + connHidWifiLeap->show(); + } else if(ui->cbxSecurity->currentIndex()==5) { + if (WepOrWpa == 1) { + ui->cbxSecurity->setCurrentIndex(5); + WepOrWpa = 0; + } + } else { + if (WepOrWpa == 0){ + ui->cbxSecurity->setCurrentIndex(6); + WepOrWpa = 1; + } + } +} + +void DlgConnHidWifiSecFast::changeDialogAuth() +{ + if(ui->cbxAuth->currentIndex()==0){ +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecTls *connHidWifiSecTls = new DlgConnHidWifiSecTls(WepOrWpa, this->parentWidget()); + connHidWifiSecTls->show(); + } else if(ui->cbxAuth->currentIndex()==1) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecLeap *connHidWifiSecLeap = new DlgConnHidWifiSecLeap(WepOrWpa, this->parentWidget()); + connHidWifiSecLeap->show(); + } else if(ui->cbxAuth->currentIndex()==2) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecPwd *connHidWifiSecPwd = new DlgConnHidWifiSecPwd(WepOrWpa, this->parentWidget()); + connHidWifiSecPwd->show(); + } else if(ui->cbxAuth->currentIndex()==3) { + qDebug()<<"it's not need to change dialog"; + } else if(ui->cbxAuth->currentIndex()==4) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecTunnelTLS *connHidWifiSecTuTls = new DlgConnHidWifiSecTunnelTLS(WepOrWpa, this->parentWidget()); + connHidWifiSecTuTls->show(); + } else { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecPeap *connHidWifiSecPeap = new DlgConnHidWifiSecPeap(WepOrWpa); + connHidWifiSecPeap->show(); + } +} + +void DlgConnHidWifiSecFast::on_btnCancel_clicked() +{ + this->close(); +} + +void DlgConnHidWifiSecFast::on_btnConnect_clicked() +{ + this->close(); +} + +void DlgConnHidWifiSecFast::on_checkBoxAutoPCA_stateChanged(int arg1) +{ + if (arg1 == 0) { + ui->cbxAutoPCA->setEnabled(false); + } else { + ui->cbxAutoPCA->setEnabled(true); + } +} + +void DlgConnHidWifiSecFast::on_checkBoxPwd_stateChanged(int arg1) +{ + if (arg1 == 0) { + ui->lePassword ->setEchoMode(QLineEdit::Password); + } else { + ui->lePassword->setEchoMode(QLineEdit::Normal); + } +} + +void DlgConnHidWifiSecFast::on_leNetName_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if(ui->leAnonyId->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->leUserName->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->lePassword->text() == ""){ + ui->btnConnect->setEnabled(false); + } else { + ui->btnConnect->setEnabled(true); + } +} + +void DlgConnHidWifiSecFast::on_leAnonyId_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if(ui->leAnonyId->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->leUserName->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->lePassword->text() == ""){ + ui->btnConnect->setEnabled(false); + } else { + ui->btnConnect->setEnabled(true); + } +} + +void DlgConnHidWifiSecFast::on_leUserName_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if(ui->leAnonyId->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->leUserName->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->lePassword->text() == ""){ + ui->btnConnect->setEnabled(false); + } else { + ui->btnConnect->setEnabled(true); + } +} + +void DlgConnHidWifiSecFast::on_lePassword_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if(ui->leAnonyId->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->leUserName->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->lePassword->text() == ""){ + ui->btnConnect->setEnabled(false); + } else { + ui->btnConnect->setEnabled(true); + } +} + +void DlgConnHidWifiSecFast::paintEvent(QPaintEvent *event) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); + QWidget::paintEvent(event); +} diff --git a/KylinNM/wireless-security/dlgconnhidwifisecfast.h b/KylinNM/wireless-security/dlgconnhidwifisecfast.h new file mode 100644 index 0000000..7a1491a --- /dev/null +++ b/KylinNM/wireless-security/dlgconnhidwifisecfast.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include + +namespace Ui { +class DlgConnHidWifiSecFast; +} + +class DlgConnHidWifiSecFast : public QDialog +{ + Q_OBJECT + +public: + explicit DlgConnHidWifiSecFast(int type, QWidget *parent = 0); + ~DlgConnHidWifiSecFast(); + +protected: + void paintEvent(QPaintEvent *event); + + /**解决锁屏设置 X11BypassWindowManagerHint 属性导致QCombox弹框异常的问题---START---- + ** 手动绑定下拉框视图和下拉框 过滤点击事件 + **/ + bool eventFilter(QObject *obj, QEvent *ev) override; + +public slots: + void changeDialogSecu(); + void changeDialogAuth(); + +private slots: + void on_btnCancel_clicked(); + + void on_btnConnect_clicked(); + + void on_checkBoxAutoPCA_stateChanged(int arg1); + + void on_checkBoxPwd_stateChanged(int arg1); + + void on_leNetName_textEdited(const QString &arg1); + + void on_leAnonyId_textEdited(const QString &arg1); + + void on_leUserName_textEdited(const QString &arg1); + + void on_lePassword_textEdited(const QString &arg1); + +private: + Ui::DlgConnHidWifiSecFast *ui; + int WepOrWpa = 0;//0 WEP;1WPA + + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + +// QString labelQss, cbxQss, leQss, btnConnQss, btnCancelQss, lineQss, checkBoxQss; + + bool isPress; + QPoint winPos; + QPoint dragPos; +}; + +#endif // DLGCONNHIDWIFISECFAST_H diff --git a/KylinNM/wireless-security/dlgconnhidwifisecfast.ui b/KylinNM/wireless-security/dlgconnhidwifisecfast.ui new file mode 100644 index 0000000..8b91563 --- /dev/null +++ b/KylinNM/wireless-security/dlgconnhidwifisecfast.ui @@ -0,0 +1,505 @@ + + + DlgConnHidWifiSecFast + + + + 0 + 0 + 432 + 673 + + + + Connect to Hidden Wi-Fi Network + + + + + 332 + 542 + 18 + 9 + + + + + 11 + + + + + + + + + + 76 + 176 + 90 + 20 + + + + + 10 + + + + + + + + + + 180 + 170 + 182 + 30 + + + + + 10 + + + + + + + 76 + 80 + 90 + 20 + + + + + 10 + + + + + + + + + + 180 + 260 + 182 + 30 + + + + + 10 + + + + + + + + + + 180 + 530 + 182 + 32 + + + + + 10 + + + + QLineEdit::Password + + + + + + 180 + 485 + 182 + 30 + + + + + 10 + + + + + + + 76 + 491 + 90 + 20 + + + + + 10 + + + + + + + + + + 180 + 215 + 182 + 30 + + + + + 10 + + + + + + + + + + 76 + 536 + 90 + 20 + + + + + 10 + + + + + + + + + + 315 + 620 + 90 + 30 + + + + + 10 + + + + + + + + + + 180 + 75 + 182 + 32 + + + + + 10 + + + + + + + + + + 76 + 266 + 90 + 20 + + + + + 10 + + + + + + + + + + 76 + 221 + 90 + 20 + + + + + 10 + + + + + + + + + + 215 + 620 + 90 + 30 + + + + + 10 + + + + + + + + + + 76 + 311 + 90 + 20 + + + + + 10 + + + + + + + + + + 180 + 305 + 182 + 32 + + + + + 10 + + + + + + + 76 + 356 + 100 + 25 + + + + + 10 + + + + + + + + + + 180 + 350 + 182 + 32 + + + + + 10 + + + + + + + + + + 76 + 401 + 90 + 20 + + + + + 10 + + + + + + + + + + 180 + 395 + 182 + 32 + + + + + 10 + + + + + + + 76 + 446 + 90 + 20 + + + + + 10 + + + + + + + + + + 180 + 440 + 182 + 32 + + + + + 10 + + + + + + + + + + 30 + 30 + 140 + 22 + + + + + 9 + + + + + + + + + + 0 + 0 + 432 + 673 + + + + + + + + + + 10 + 140 + 412 + 1 + + + + Qt::Horizontal + + + + + + 10 + 590 + 412 + 1 + + + + Qt::Horizontal + + + lbBoder + lbNetName + leNetName + lbConn + cbxAuth + lePassword + leUserName + lbUserName + cbxSecurity + lbPassword + btnConnect + cbxConn + lbAuth + lbSecurity + btnCancel + lbAnonyId + leAnonyId + checkBoxAutoPCA + cbxAutoPCA + lbPCAfile + lePCAfile + lbInnerAuth + cbxInnerAuth + checkBoxPwd + lbLeftupTitle + lineUp + lineDown + + + + diff --git a/KylinNM/wireless-security/dlgconnhidwifisecleap.cpp b/KylinNM/wireless-security/dlgconnhidwifisecleap.cpp new file mode 100644 index 0000000..a9d9e41 --- /dev/null +++ b/KylinNM/wireless-security/dlgconnhidwifisecleap.cpp @@ -0,0 +1,343 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see + +DlgConnHidWifiSecLeap::DlgConnHidWifiSecLeap(int type, QWidget *parent) : + WepOrWpa(type), + QDialog(parent), + ui(new Ui::DlgConnHidWifiSecLeap) +{ + ui->setupUi(this); + + this->setWindowFlags(Qt::FramelessWindowHint); + this->setAttribute(Qt::WA_TranslucentBackground); + //需要添加 void paintEvent(QPaintEvent *event) 函数 + + QPainterPath path; + auto rect = this->rect(); + rect.adjust(1, 1, -1, -1); + path.addRoundedRect(rect, 6, 6); + setProperty("blurRegion", QRegion(path.toFillPolygon().toPolygon())); + + this->setStyleSheet("QWidget{border-radius:6px;background-color:rgba(19,19,20,0.7);border:1px solid rgba(255, 255, 255, 0.05);}"); + + MyQss objQss; + + ui->lbBoder->setStyleSheet("QLabel{border-radius:6px;background-color:rgba(19,19,20,0.95);border:1px solid rgba(255, 255, 255, 0.05);}"); + ui->lbBoder->hide(); + ui->lbLeftupTitle->setStyleSheet("QLabel{border:0px;font-size:20px;color:rgba(255,255,255,0.97);background-color:transparent;}"); + ui->lbConn->setStyleSheet(objQss.labelQss); + ui->lbNetName->setStyleSheet(objQss.labelQss); + ui->lbSecurity->setStyleSheet(objQss.labelQss); + ui->lbAuth->setStyleSheet(objQss.labelQss); + ui->lbUserName->setStyleSheet(objQss.labelQss); + ui->lbPassword->setStyleSheet(objQss.labelQss); + + ui->cbxConn->setStyleSheet(objQss.cbxQss); + ui->cbxConn->setView(new QListView()); + ui->leNetName->setStyleSheet(objQss.leQss); + ui->cbxSecurity->setStyleSheet(objQss.cbxQss); + ui->cbxSecurity->setView(new QListView()); + ui->cbxAuth->setStyleSheet(objQss.cbxQss); + ui->cbxAuth->setView(new QListView()); + ui->leUserName->setStyleSheet(objQss.leQss); + ui->lePassword->setStyleSheet(objQss.leQss); + ui->checkBox->setStyleSheet(objQss.checkBoxQss); + + ui->btnCancel->setStyleSheet(objQss.btnCancelQss); + ui->btnConnect->setStyleSheet(objQss.btnConnQss); + ui->lineUp->setStyleSheet(objQss.lineQss); + ui->lineDown->setStyleSheet(objQss.lineQss); + + ui->lbLeftupTitle->setText(tr("Add hidden Wi-Fi")); //加入隐藏Wi-Fi + ui->lbConn->setText(tr("Connection")); //连接设置: + ui->lbNetName->setText(tr("Network name")); //网络名称: + ui->lbSecurity->setText(tr("Wi-Fi security")); //Wi-Fi安全性: + ui->lbAuth->setText(tr("Authentication")); //认证: + ui->lbUserName->setText(tr("Username")); //用户名: + ui->lbPassword->setText(tr("Password")); //密码: + ui->btnCancel->setText(tr("Cancel")); //取消 + ui->btnConnect->setText(tr("Connect")); //连接 + + ui->cbxConn->addItem(tr("C_reate…")); //新建... + int status = system("nmcli connection show>/tmp/kylin-nm-connshow"); + if (status != 0){ syslog(LOG_ERR, "execute 'nmcli connection show' in function 'DlgConnHidWifiSecLeap' failed");} + QFile file("/tmp/kylin-nm-connshow"); + if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){ + qDebug()<<"Can't open the file!"; + } + QString txt = file.readAll(); + QStringList txtLine = txt.split("\n"); + file.close(); + foreach (QString line, txtLine) { + if(line.indexOf("wifi") != -1){ + QStringList subLine = line.split(" "); + ui->cbxConn->addItem(subLine[0]); + } + } + ui->cbxConn->setCurrentIndex(0); + + ui->cbxSecurity->addItem(tr("None")); //无 + ui->cbxSecurity->addItem(tr("WPA & WPA2 Personal")); //WPA 及 WPA2 个人 + ui->cbxSecurity->addItem(tr("WEP 40/128-bit Key (Hex or ASCII)")); //WEP 40/128 位密钥(十六进制或ASCII) + ui->cbxSecurity->addItem(tr("WEP 128-bit Passphrase")); //WEP 128 位密码句 + ui->cbxSecurity->addItem("LEAP"); + ui->cbxSecurity->addItem(tr("Dynamic WEP (802.1X)")); //动态 WEP (802.1x) + ui->cbxSecurity->addItem(tr("WPA & WPA2 Enterprise")); //WPA 及 WPA2 企业 + if (WepOrWpa == 0) { + ui->cbxSecurity->setCurrentIndex(5); + } else if (WepOrWpa == 1) { + ui->cbxSecurity->setCurrentIndex(6); + } + connect(ui->cbxSecurity,SIGNAL(currentIndexChanged(QString)),this,SLOT(changeDialogSecu())); + + ui->cbxAuth->addItem("TLS"); + ui->cbxAuth->addItem("LEAP"); + ui->cbxAuth->addItem("PWD"); + ui->cbxAuth->addItem("FAST"); + ui->cbxAuth->addItem(tr("Tunneled TLS"));//隧道 TLS + ui->cbxAuth->addItem(tr("Protected EAP (PEAP)")); //受保护的 EAP + ui->cbxAuth->setCurrentIndex(1); + connect(ui->cbxAuth,SIGNAL(currentIndexChanged(QString)),this,SLOT(changeDialogAuth())); + + ui->btnConnect->setEnabled(false); + + this->setFixedSize(432,487); + + /**解决锁屏设置 X11BypassWindowManagerHint 属性导致QCombox弹框异常的问题---START---- + ** 手动绑定下拉框视图和下拉框 + **/ + ui->cbxConn->view()->setWindowFlags(Qt::Popup | Qt::X11BypassWindowManagerHint); + ui->cbxConn->view()->setParent(this); + ui->cbxConn->view()->hide(); + ui->cbxConn->installEventFilter(this); + + ui->cbxSecurity->view()->setWindowFlags(Qt::Popup | Qt::X11BypassWindowManagerHint); + ui->cbxSecurity->view()->setParent(this); + ui->cbxSecurity->view()->hide(); + ui->cbxSecurity->installEventFilter(this); + + connect(ui->cbxConn->view(), &QAbstractItemView::pressed, this, [=](QModelIndex index){ + Q_EMIT ui->cbxConn->setCurrentIndex(index.row()); + ui->cbxConn->view()->hide(); + }); + + connect(ui->cbxSecurity->view(), &QAbstractItemView::pressed, this, [=](QModelIndex index){ + ui->cbxSecurity->view()->hide(); + Q_EMIT ui->cbxSecurity->setCurrentIndex(index.row()); + }); + + ui->cbxConn->view()->setGeometry(QRect(ui->cbxConn->geometry().left(), ui->cbxConn->geometry().bottom(), ui->cbxConn->view()->width(), ui->cbxConn->view()->height())); + ui->cbxSecurity->view()->setGeometry(QRect(ui->cbxSecurity->geometry().left(), ui->cbxSecurity->geometry().bottom(), ui->cbxSecurity->view()->width(), ui->cbxSecurity->view()->height())); + /**解决锁屏设置 X11BypassWindowManagerHint 属性导致QCombox弹框异常的问题---END---- + ** 手动绑定下拉框视图和下拉框 + **/ +} + +DlgConnHidWifiSecLeap::~DlgConnHidWifiSecLeap() +{ + delete ui; +} + +/**解决锁屏设置 X11BypassWindowManagerHint 属性导致QCombox弹框异常的问题---START---- +** 手动绑定下拉框视图和下拉框 +**/ +bool DlgConnHidWifiSecLeap::eventFilter(QObject *obj, QEvent *ev) +{ + if(ev->type() == QEvent::MouseButtonPress) + { + if(obj == ui->cbxConn) + { + ui->cbxConn->view()->setVisible(!ui->cbxConn->view()->isVisible()); + if(ui->cbxConn->view()->isVisible()) + ui->cbxConn->view()->setFocus(); + } else if (obj == ui->cbxSecurity) + { + ui->cbxSecurity->view()->setVisible(!ui->cbxSecurity->view()->isVisible()); + if(ui->cbxSecurity->view()->isVisible()) + ui->cbxSecurity->view()->setFocus(); + } + } + return false; +} +/**解决锁屏设置 X11BypassWindowManagerHint 属性导致QCombox弹框异常的问题---END---- +** 手动绑定下拉框视图和下拉框 +**/ + +void DlgConnHidWifiSecLeap::mousePressEvent(QMouseEvent *event){ + if(event->button() == Qt::LeftButton){ + this->isPress = true; + this->winPos = this->pos(); + this->dragPos = event->globalPos(); + event->accept(); + } +} +void DlgConnHidWifiSecLeap::mouseReleaseEvent(QMouseEvent *event){ + this->isPress = false; +} +void DlgConnHidWifiSecLeap::mouseMoveEvent(QMouseEvent *event){ + if(this->isPress){ + this->move(this->winPos - (this->dragPos - event->globalPos())); + event->accept(); + } +} + +void DlgConnHidWifiSecLeap::changeDialogSecu() +{ + if(ui->cbxSecurity->currentIndex()==0){ +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifi *connHidWifi = new DlgConnHidWifi(0, 0, this->parentWidget()); + connHidWifi->show(); + } else if(ui->cbxSecurity->currentIndex()==1) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiWpa *connHidWifiWpa = new DlgConnHidWifiWpa(0, 0, this->parentWidget()); + connHidWifiWpa->show(); + } else if(ui->cbxSecurity->currentIndex()==2) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiWep *connHidWifiWep = new DlgConnHidWifiWep(0, this->parentWidget()); + connHidWifiWep->show(); + } else if(ui->cbxSecurity->currentIndex()==3) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiWep *connHidWifiWep = new DlgConnHidWifiWep(1, this->parentWidget()); + connHidWifiWep->show(); + } else if(ui->cbxSecurity->currentIndex()==4) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiLeap *connHidWifiLeap = new DlgConnHidWifiLeap(this->parentWidget()); + connHidWifiLeap->show(); + } else if(ui->cbxSecurity->currentIndex()==5) { + if (WepOrWpa == 1) { + ui->cbxSecurity->setCurrentIndex(5); + WepOrWpa = 0; + } + } else { + if (WepOrWpa == 0){ + ui->cbxSecurity->setCurrentIndex(6); + WepOrWpa = 1; + } + } +} + +void DlgConnHidWifiSecLeap::changeDialogAuth() +{ + if(ui->cbxAuth->currentIndex()==0){ +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecTls *connHidWifiSecTls = new DlgConnHidWifiSecTls(WepOrWpa, this->parentWidget()); + connHidWifiSecTls->show(); + } else if(ui->cbxAuth->currentIndex()==1) { + qDebug()<<"it's not need to change dialog"; + } else if(ui->cbxAuth->currentIndex()==2) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecPwd *connHidWifiSecPwd = new DlgConnHidWifiSecPwd(WepOrWpa, this->parentWidget()); + connHidWifiSecPwd->show(); + } else if(ui->cbxAuth->currentIndex()==3) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecFast *connHidWifiSecFast = new DlgConnHidWifiSecFast(WepOrWpa, this->parentWidget()); + connHidWifiSecFast->show(); + } else if(ui->cbxAuth->currentIndex()==4) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecTunnelTLS *connHidWifiSecTuTls = new DlgConnHidWifiSecTunnelTLS(WepOrWpa, this->parentWidget()); + connHidWifiSecTuTls->show(); + } else { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecPeap *connHidWifiSecPeap = new DlgConnHidWifiSecPeap(WepOrWpa, this->parentWidget()); + connHidWifiSecPeap->show(); + } +} + +void DlgConnHidWifiSecLeap::on_btnCancel_clicked() +{ + this->close(); +} + +void DlgConnHidWifiSecLeap::on_btnConnect_clicked() +{ + this->close(); +} + +void DlgConnHidWifiSecLeap::on_checkBox_stateChanged(int arg1) +{ + if (arg1 == 0) { + ui->lePassword ->setEchoMode(QLineEdit::Password); + } else { + ui->lePassword->setEchoMode(QLineEdit::Normal); + } +} + +void DlgConnHidWifiSecLeap::on_leNetName_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->leUserName->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->lePassword->text() == ""){ + ui->btnConnect->setEnabled(false); + } else { + ui->btnConnect->setEnabled(true); + } +} + +void DlgConnHidWifiSecLeap::on_leUserName_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->leUserName->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->lePassword->text() == ""){ + ui->btnConnect->setEnabled(false); + } else { + ui->btnConnect->setEnabled(true); + } +} + +void DlgConnHidWifiSecLeap::on_lePassword_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->leUserName->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->lePassword->text() == ""){ + ui->btnConnect->setEnabled(false); + } else { + ui->btnConnect->setEnabled(true); + } +} + +void DlgConnHidWifiSecLeap::paintEvent(QPaintEvent *event) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); + QWidget::paintEvent(event); +} diff --git a/KylinNM/wireless-security/dlgconnhidwifisecleap.h b/KylinNM/wireless-security/dlgconnhidwifisecleap.h new file mode 100644 index 0000000..4336b65 --- /dev/null +++ b/KylinNM/wireless-security/dlgconnhidwifisecleap.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include + +namespace Ui { +class DlgConnHidWifiSecLeap; +} + +class DlgConnHidWifiSecLeap : public QDialog +{ + Q_OBJECT + +public: + explicit DlgConnHidWifiSecLeap(int type, QWidget *parent = 0); + ~DlgConnHidWifiSecLeap(); + +protected: + void paintEvent(QPaintEvent *event); + + /**解决锁屏设置 X11BypassWindowManagerHint 属性导致QCombox弹框异常的问题---START---- + ** 手动绑定下拉框视图和下拉框 过滤点击事件 + **/ + bool eventFilter(QObject *obj, QEvent *ev) override; + +public slots: + void changeDialogSecu(); + void changeDialogAuth(); + +private slots: + void on_btnCancel_clicked(); + + void on_btnConnect_clicked(); + + void on_checkBox_stateChanged(int arg1); + + void on_leNetName_textEdited(const QString &arg1); + + void on_leUserName_textEdited(const QString &arg1); + + void on_lePassword_textEdited(const QString &arg1); + +private: + Ui::DlgConnHidWifiSecLeap *ui; + int WepOrWpa = 0;//0 WEP;1WPA + + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + +// QString labelQss, cbxQss, leQss, btnConnQss, btnCancelQss, lineQss, checkBoxQss, checkBoxCAQss; + + bool isPress; + QPoint winPos; + QPoint dragPos; +}; + +#endif // DLGCONNHIDWIFISECLEAP_H diff --git a/KylinNM/wireless-security/dlgconnhidwifisecleap.ui b/KylinNM/wireless-security/dlgconnhidwifisecleap.ui new file mode 100644 index 0000000..9664fd8 --- /dev/null +++ b/KylinNM/wireless-security/dlgconnhidwifisecleap.ui @@ -0,0 +1,354 @@ + + + DlgConnHidWifiSecLeap + + + + 0 + 0 + 432 + 487 + + + + Connect to Hidden Wi-Fi Network + + + + + 175 + 215 + 182 + 32 + + + + + 10 + + + + + + + + + + 175 + 350 + 182 + 32 + + + + + 10 + + + + QLineEdit::Password + + + + + + 332 + 362 + 18 + 9 + + + + + 10 + + + + + + + + + + 76 + 176 + 90 + 20 + + + + + 10 + + + + + + + + + + 76 + 221 + 90 + 20 + + + + + 10 + + + + + + + + + + 175 + 305 + 182 + 32 + + + + + 10 + + + + + + + 315 + 440 + 90 + 30 + + + + + 10 + + + + + + + + + + 175 + 75 + 182 + 32 + + + + + 10 + + + + + + + + + + 76 + 311 + 90 + 20 + + + + + 10 + + + + + + + + + + 215 + 440 + 90 + 30 + + + + + 10 + + + + + + + + + + 175 + 260 + 182 + 32 + + + + + 10 + + + + + + + + + + 76 + 266 + 90 + 20 + + + + + 10 + + + + + + + + + + 175 + 170 + 182 + 32 + + + + + 10 + + + + + + + 76 + 356 + 90 + 20 + + + + + 10 + + + + + + + + + + 76 + 80 + 90 + 20 + + + + + 10 + + + + + + + + + + 30 + 30 + 140 + 22 + + + + + + + + + + 0 + 0 + 432 + 487 + + + + + + + + + + 10 + 140 + 412 + 1 + + + + Qt::Horizontal + + + + + + 10 + 410 + 412 + 1 + + + + Qt::Horizontal + + + lbBoder + cbxSecurity + lePassword + checkBox + lbNetName + lbSecurity + leUserName + btnConnect + cbxConn + lbUserName + btnCancel + cbxAuth + lbAuth + leNetName + lbPassword + lbConn + lbLeftupTitle + lineUp + lineDown + + + + diff --git a/KylinNM/wireless-security/dlgconnhidwifisecpeap.cpp b/KylinNM/wireless-security/dlgconnhidwifisecpeap.cpp new file mode 100644 index 0000000..50a0c9f --- /dev/null +++ b/KylinNM/wireless-security/dlgconnhidwifisecpeap.cpp @@ -0,0 +1,461 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see + +DlgConnHidWifiSecPeap::DlgConnHidWifiSecPeap(int type, QWidget *parent) : + WepOrWpa(type), + QDialog(parent), + ui(new Ui::DlgConnHidWifiSecPeap) +{ + ui->setupUi(this); + + this->setWindowFlags(Qt::FramelessWindowHint); + this->setAttribute(Qt::WA_TranslucentBackground); + //需要添加 void paintEvent(QPaintEvent *event) 函数 + + QPainterPath path; + auto rect = this->rect(); + rect.adjust(1, 1, -1, -1); + path.addRoundedRect(rect, 6, 6); + setProperty("blurRegion", QRegion(path.toFillPolygon().toPolygon())); + + this->setStyleSheet("QWidget{border-radius:6px;background-color:rgba(19,19,20,0.7);border:1px solid rgba(255, 255, 255, 0.05);}"); + + MyQss objQss; + + ui->lbBoder->setStyleSheet("QLabel{border-radius:6px;background-color:rgba(19,19,20,0.95);border:1px solid rgba(255, 255, 255, 0.05);}"); + ui->lbBoder->hide(); + ui->lbLeftupTitle->setStyleSheet("QLabel{border:0px;font-size:20px;color:rgba(255,255,255,0.97);background-color:transparent;}"); + ui->lbConn->setStyleSheet(objQss.labelQss); + ui->lbNetName->setStyleSheet(objQss.labelQss); + ui->lbSecurity->setStyleSheet(objQss.labelQss); + ui->lbAuth->setStyleSheet(objQss.labelQss); + ui->lbAnonyId->setStyleSheet(objQss.labelQss); + ui->lbDomain->setStyleSheet(objQss.labelQss); + ui->lbCA->setStyleSheet(objQss.labelQss); + ui->lbCaPwd->setStyleSheet(objQss.labelQss); + ui->lbPEAPver->setStyleSheet(objQss.labelQss); + ui->lbInnerAuth->setStyleSheet(objQss.labelQss); + ui->lbUserName->setStyleSheet(objQss.labelQss); + ui->lbPassword->setStyleSheet(objQss.labelQss); + + ui->cbxConn->setStyleSheet(objQss.cbxQss); + ui->cbxConn->setView(new QListView()); + ui->leNetName->setStyleSheet(objQss.leQss); + ui->cbxSecurity->setStyleSheet(objQss.cbxQss); + ui->cbxSecurity->setView(new QListView()); + ui->cbxAuth->setStyleSheet(objQss.cbxQss); + ui->cbxAuth->setView(new QListView()); + ui->leAnonyId->setStyleSheet(objQss.leQss); + ui->leDomain->setStyleSheet(objQss.leQss); + ui->cbxCA->setStyleSheet(objQss.cbxQss); + ui->cbxCA->setView(new QListView()); + ui->leCaPwd->setStyleSheet(objQss.leQss); + ui->checkBoxPwd->setStyleSheet(objQss.checkBoxQss); + ui->checkBoxCA->setStyleSheet(objQss.checkBoxCAQss); + ui->cbxPEAPver->setStyleSheet(objQss.cbxQss); + ui->cbxPEAPver->setView(new QListView()); + ui->cbxInnerAuth->setStyleSheet(objQss.cbxQss); + ui->cbxInnerAuth->setView(new QListView()); + ui->leUserName->setStyleSheet(objQss.leQss); + ui->lePassword->setStyleSheet(objQss.leQss); + ui->checkBoxPwdSec->setStyleSheet(objQss.checkBoxQss); + + ui->btnCancel->setStyleSheet(objQss.btnCancelQss); + ui->btnConnect->setStyleSheet(objQss.btnConnQss); + ui->lineUp->setStyleSheet(objQss.lineQss); + ui->lineDown->setStyleSheet(objQss.lineQss); + + + ui->lbLeftupTitle->setText(tr("Add hidden Wi-Fi")); //加入隐藏Wi-Fi + ui->lbConn->setText(tr("Connection")); //连接设置: + ui->lbNetName->setText(tr("Network name")); //网络名称: + ui->lbSecurity->setText(tr("Wi-Fi security")); //Wi-Fi安全性: + ui->lbAuth->setText(tr("Authentication")); //认证: + ui->lbAnonyId->setText(tr("Anonymous identity")); //匿名身份: + ui->lbDomain->setText(tr("Domain")); //域名: + ui->lbCA->setText(tr("CA certificate")); //CA 证书: + ui->lbCaPwd->setText(tr("CA certificate password")); //CA 证书密码: + ui->checkBoxCA->setText(tr("No CA certificate is required")); //不需要CA证书 + ui->lbPEAPver->setText(tr("PEAP version")); //PEAP版本: + ui->lbInnerAuth->setText(tr("Inner authentication")); //内部认证: + ui->lbUserName->setText(tr("Username")); //用户名: + ui->lbPassword->setText(tr("Password")); //密码: + ui->btnCancel->setText(tr("Cancel")); //取消 + ui->btnConnect->setText(tr("Connect")); //连接 + + ui->cbxConn->addItem("新建..."); + int status = system("nmcli connection show>/tmp/kylin-nm-connshow"); + if (status != 0){ syslog(LOG_ERR, "execute 'nmcli connection show' in function 'DlgConnHidWifiSecPeap' failed");} + QFile file("/tmp/kylin-nm-connshow"); + if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){ + qDebug()<<"Can't open the file!"; + } + QString txt = file.readAll(); + QStringList txtLine = txt.split("\n"); + file.close(); + foreach (QString line, txtLine) { + if(line.indexOf("wifi") != -1){ + QStringList subLine = line.split(" "); + ui->cbxConn->addItem(subLine[0]); + } + } + ui->cbxConn->setCurrentIndex(0); + + ui->cbxSecurity->addItem(tr("None")); //无 + ui->cbxSecurity->addItem(tr("WPA & WPA2 Personal")); //WPA 及 WPA2 个人 + ui->cbxSecurity->addItem(tr("WEP 40/128-bit Key (Hex or ASCII)")); //WEP 40/128 位密钥(十六进制或ASCII) + ui->cbxSecurity->addItem(tr("WEP 128-bit Passphrase")); //WEP 128 位密码句 + ui->cbxSecurity->addItem("LEAP"); + ui->cbxSecurity->addItem(tr("Dynamic WEP (802.1X)")); //动态 WEP (802.1x) + ui->cbxSecurity->addItem(tr("WPA & WPA2 Enterprise")); //WPA 及 WPA2 企业 + if (WepOrWpa == 0) { + ui->cbxSecurity->setCurrentIndex(5); + } else if (WepOrWpa == 1) { + ui->cbxSecurity->setCurrentIndex(6); + } + connect(ui->cbxSecurity,SIGNAL(currentIndexChanged(QString)),this,SLOT(changeDialogSecu())); + + ui->cbxAuth->addItem("TLS"); + ui->cbxAuth->addItem("LEAP"); + ui->cbxAuth->addItem("PWD"); + ui->cbxAuth->addItem("FAST"); + ui->cbxAuth->addItem(tr("Tunneled TLS"));//隧道 TLS + ui->cbxAuth->addItem(tr("Protected EAP (PEAP)")); //受保护的 EAP + ui->cbxAuth->setCurrentIndex(5); + connect(ui->cbxAuth,SIGNAL(currentIndexChanged(QString)),this,SLOT(changeDialogAuth())); + + ui->cbxCA->addItem(tr("None")); //无 + ui->cbxCA->addItem(tr("Choose from file")); //从文件选择... + ui->cbxCA->setCurrentIndex(0); + + ui->cbxPEAPver->addItem(tr("Automatic")); //自动 + ui->cbxPEAPver->addItem(tr("Version 0")); //版本 0 + ui->cbxPEAPver->addItem(tr("Version 1")); //版本 1 + ui->cbxPEAPver->setCurrentIndex(0); + + ui->cbxInnerAuth->addItem("MSCHAPv2"); + ui->cbxInnerAuth->addItem("MDS"); + ui->cbxInnerAuth->addItem("GTC"); + ui->cbxInnerAuth->setCurrentIndex(0); + + ui->btnConnect->setEnabled(false); + + this->setFixedSize(432,700); + +} + +DlgConnHidWifiSecPeap::~DlgConnHidWifiSecPeap() +{ + delete ui; +} + +void DlgConnHidWifiSecPeap::mousePressEvent(QMouseEvent *event){ + if(event->button() == Qt::LeftButton){ + this->isPress = true; + this->winPos = this->pos(); + this->dragPos = event->globalPos(); + event->accept(); + } +} +void DlgConnHidWifiSecPeap::mouseReleaseEvent(QMouseEvent *event){ + this->isPress = false; +} +void DlgConnHidWifiSecPeap::mouseMoveEvent(QMouseEvent *event){ + if(this->isPress){ + this->move(this->winPos - (this->dragPos - event->globalPos())); + event->accept(); + } +} + +void DlgConnHidWifiSecPeap::changeDialogSecu() +{ + if(ui->cbxSecurity->currentIndex()==0){ +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifi *connHidWifi = new DlgConnHidWifi(0, 0,this->parentWidget()); + connHidWifi->show(); + } else if(ui->cbxSecurity->currentIndex()==1) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiWpa *connHidWifiWpa = new DlgConnHidWifiWpa(0, 0, this->parentWidget()); + connHidWifiWpa->show(); + } else if(ui->cbxSecurity->currentIndex()==2) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiWep *connHidWifiWep = new DlgConnHidWifiWep(0, this->parentWidget()); + connHidWifiWep->show(); + } else if(ui->cbxSecurity->currentIndex()==3) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiWep *connHidWifiWep = new DlgConnHidWifiWep(1, this->parentWidget()); + connHidWifiWep->show(); + } else if(ui->cbxSecurity->currentIndex()==4) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiLeap *connHidWifiLeap = new DlgConnHidWifiLeap(this->parentWidget()); + connHidWifiLeap->show(); + } else if(ui->cbxSecurity->currentIndex()==5) { + if (WepOrWpa == 1) { + ui->cbxSecurity->setCurrentIndex(5); + WepOrWpa = 0; + } + } else { + if (WepOrWpa == 0){ + ui->cbxSecurity->setCurrentIndex(6); + WepOrWpa = 1; + } + } +} + +void DlgConnHidWifiSecPeap::changeDialogAuth() +{ + if(ui->cbxAuth->currentIndex()==0){ +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecTls *connHidWifiSecTls = new DlgConnHidWifiSecTls(WepOrWpa, this->parentWidget()); + connHidWifiSecTls->show(); + } else if(ui->cbxAuth->currentIndex()==1) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecLeap *connHidWifiSecLeap = new DlgConnHidWifiSecLeap(WepOrWpa, this->parentWidget()); + connHidWifiSecLeap->show(); + } else if(ui->cbxAuth->currentIndex()==2) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecPwd *connHidWifiSecPwd = new DlgConnHidWifiSecPwd(WepOrWpa); + connHidWifiSecPwd->show(); + } else if(ui->cbxAuth->currentIndex()==3) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecFast *connHidWifiSecFast = new DlgConnHidWifiSecFast(WepOrWpa); + connHidWifiSecFast->show(); + } else if(ui->cbxAuth->currentIndex()==4) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecTunnelTLS *connHidWifiSecTuTls = new DlgConnHidWifiSecTunnelTLS(WepOrWpa); + connHidWifiSecTuTls->show(); + } else { + qDebug()<<"it's not need to change dialog"; + } +} + +void DlgConnHidWifiSecPeap::on_cbxCA_currentIndexChanged(const QString &arg1) +{ + if (ui->cbxCA->currentIndex() == 0){ + ui->leCaPwd->setText(""); + ui->lbCaPwd->setEnabled(false); + ui->leCaPwd->setEnabled(false); + } else { + ui->leCaPwd->setText(""); + ui->lbCaPwd->setEnabled(true); + ui->leCaPwd->setEnabled(true); + } +} + +void DlgConnHidWifiSecPeap::on_btnCancel_clicked() +{ + this->close(); +} + +void DlgConnHidWifiSecPeap::on_btnConnect_clicked() +{ + this->close(); +} + +void DlgConnHidWifiSecPeap::on_checkBoxPwd_stateChanged(int arg1) +{ + if (arg1 == 0) { + ui->leCaPwd->setEchoMode(QLineEdit::Password); + } else { + ui->leCaPwd->setEchoMode(QLineEdit::Normal); + } +} + +void DlgConnHidWifiSecPeap::on_checkBoxCA_stateChanged(int arg1) +{ + if (arg1 == 0) { + ui->cbxCA->setCurrentIndex(0); + ui->leCaPwd->setText(""); + ui->lbCA->setEnabled(true); + ui->cbxCA->setEnabled(true); + } else { + ui->cbxCA->setCurrentIndex(0); + ui->leCaPwd->setText(""); + ui->lbCA->setEnabled(false); + ui->cbxCA->setEnabled(false); + } +} + +void DlgConnHidWifiSecPeap::on_checkBoxPwdSec_stateChanged(int arg1) +{ + if (arg1 == 0) { + ui->lePassword->setEchoMode(QLineEdit::Password); + } else { + ui->lePassword->setEchoMode(QLineEdit::Normal); + } +} + +void DlgConnHidWifiSecPeap::on_leNetName_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leAnonyId->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leDomain->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leUserName->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->lePassword->text() == ""){ + ui->btnConnect->setEnabled(false); + }else{ + if (ui->cbxCA->currentIndex() == 0){ + ui->btnConnect->setEnabled(true); + }else if(ui->leCaPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + }else{ + ui->btnConnect->setEnabled(true); + } + } +} + +void DlgConnHidWifiSecPeap::on_leAnonyId_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leAnonyId->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leDomain->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leUserName->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->lePassword->text() == ""){ + ui->btnConnect->setEnabled(false); + }else{ + if (ui->cbxCA->currentIndex() == 0){ + ui->btnConnect->setEnabled(true); + }else if(ui->leCaPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + }else{ + ui->btnConnect->setEnabled(true); + } + } +} + +void DlgConnHidWifiSecPeap::on_leDomain_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leAnonyId->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leDomain->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leUserName->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->lePassword->text() == ""){ + ui->btnConnect->setEnabled(false); + }else{ + if (ui->cbxCA->currentIndex() == 0){ + ui->btnConnect->setEnabled(true); + }else if(ui->leCaPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + }else{ + ui->btnConnect->setEnabled(true); + } + } +} + +void DlgConnHidWifiSecPeap::on_leCaPwd_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leAnonyId->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leDomain->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leUserName->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->lePassword->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leCaPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + }else{ + ui->btnConnect->setEnabled(true); + } +} + +void DlgConnHidWifiSecPeap::on_leUserName_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leAnonyId->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leDomain->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leUserName->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->lePassword->text() == ""){ + ui->btnConnect->setEnabled(false); + }else{ + if (ui->cbxCA->currentIndex() == 0){ + ui->btnConnect->setEnabled(true); + }else if(ui->leCaPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + }else{ + ui->btnConnect->setEnabled(true); + } + } +} + +void DlgConnHidWifiSecPeap::on_lePassword_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leAnonyId->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leDomain->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leUserName->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->lePassword->text() == ""){ + ui->btnConnect->setEnabled(false); + }else{ + if (ui->cbxCA->currentIndex() == 0){ + ui->btnConnect->setEnabled(true); + }else if(ui->leCaPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + }else{ + ui->btnConnect->setEnabled(true); + } + } +} + +void DlgConnHidWifiSecPeap::paintEvent(QPaintEvent *event) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); + QWidget::paintEvent(event); +} diff --git a/KylinNM/wireless-security/dlgconnhidwifisecpeap.h b/KylinNM/wireless-security/dlgconnhidwifisecpeap.h new file mode 100644 index 0000000..d0361df --- /dev/null +++ b/KylinNM/wireless-security/dlgconnhidwifisecpeap.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include + +namespace Ui { +class DlgConnHidWifiSecPeap; +} + +class DlgConnHidWifiSecPeap : public QDialog +{ + Q_OBJECT + +public: + explicit DlgConnHidWifiSecPeap(int type, QWidget *parent = 0); + ~DlgConnHidWifiSecPeap(); + +protected: + void paintEvent(QPaintEvent *event); + +public slots: + void changeDialogSecu(); + void changeDialogAuth(); + +private slots: + void on_btnCancel_clicked(); + + void on_btnConnect_clicked(); + + void on_cbxCA_currentIndexChanged(const QString &arg1); + + void on_checkBoxPwd_stateChanged(int arg1); + + void on_checkBoxCA_stateChanged(int arg1); + + void on_checkBoxPwdSec_stateChanged(int arg1); + + void on_leNetName_textEdited(const QString &arg1); + + void on_leAnonyId_textEdited(const QString &arg1); + + void on_leDomain_textEdited(const QString &arg1); + + void on_leCaPwd_textEdited(const QString &arg1); + + void on_leUserName_textEdited(const QString &arg1); + + void on_lePassword_textEdited(const QString &arg1); + +private: + Ui::DlgConnHidWifiSecPeap *ui; + int WepOrWpa = 0;//0 WEP;1WPA + + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + +// QString labelQss, cbxQss, leQss, btnConnQss, btnCancelQss, lineQss, checkBoxQss, checkBoxCAQss; + + bool isPress; + QPoint winPos; + QPoint dragPos; +}; + +#endif // DLGCONNHIDWIFISECPEAP_H diff --git a/KylinNM/wireless-security/dlgconnhidwifisecpeap.ui b/KylinNM/wireless-security/dlgconnhidwifisecpeap.ui new file mode 100644 index 0000000..82bc354 --- /dev/null +++ b/KylinNM/wireless-security/dlgconnhidwifisecpeap.ui @@ -0,0 +1,614 @@ + + + DlgConnHidWifiSecPeap + + + + 0 + 0 + 432 + 700 + + + + Connect to Hidden Wi-Fi Network + + + + + 180 + 141 + 200 + 32 + + + + + 10 + + + + + + + 180 + 341 + 200 + 32 + + + + + 10 + + + + + + + + + + 76 + 76 + 90 + 20 + + + + + 10 + + + + + + + + + + 180 + 301 + 200 + 32 + + + + + 10 + + + + + + + 180 + 71 + 200 + 32 + + + + + 10 + + + + + + + + + + 180 + 381 + 200 + 32 + + + + + 10 + + + + QLineEdit::Password + + + + + + 315 + 650 + 90 + 30 + + + + + 10 + + + + + + + + + + 180 + 181 + 200 + 32 + + + + + 10 + + + + + + + + + + 180 + 261 + 200 + 32 + + + + + 10 + + + + + + + 183 + 421 + 200 + 25 + + + + + 10 + + + + + + + + + + 76 + 146 + 90 + 20 + + + + + 10 + + + + + + + + + + 215 + 650 + 90 + 30 + + + + + 10 + + + + + + + + + + 76 + 306 + 90 + 20 + + + + + 10 + + + + + + + + + + 76 + 386 + 90 + 20 + + + + + 10 + + + + + + + + + + 357 + 593 + 18 + 9 + + + + + 10 + + + + + + + + + + 357 + 393 + 18 + 9 + + + + + 10 + + + + + + + + + + 76 + 186 + 90 + 20 + + + + + 10 + + + + + + + + + + 180 + 461 + 200 + 32 + + + + + 10 + + + + + + + + + + 76 + 226 + 90 + 20 + + + + + 10 + + + + + + + + + + 76 + 466 + 90 + 20 + + + + + 10 + + + + + + + + + + 180 + 221 + 200 + 32 + + + + + 10 + + + + + + + + + + 76 + 346 + 90 + 20 + + + + + 10 + + + + + + + + + + 76 + 266 + 90 + 20 + + + + + 10 + + + + + + + + + + 76 + 586 + 90 + 20 + + + + + 10 + + + + + + + + + + 180 + 541 + 200 + 32 + + + + + 10 + + + + + + + 180 + 501 + 200 + 32 + + + + + 10 + + + + + + + + + + 180 + 581 + 200 + 32 + + + + + 10 + + + + QLineEdit::Password + + + + + + 76 + 506 + 90 + 20 + + + + + 10 + + + + + + + + + + 76 + 546 + 90 + 20 + + + + + 10 + + + + + + + + + + 0 + 0 + 432 + 700 + + + + + + + + + + 30 + 28 + 140 + 22 + + + + + + + + + + 10 + 121 + 412 + 1 + + + + Qt::Horizontal + + + + + + 10 + 631 + 412 + 1 + + + + Qt::Horizontal + + + lbBoder + leNetName + cbxCA + lbConn + leDomain + cbxConn + leCaPwd + btnConnect + cbxSecurity + leAnonyId + checkBoxCA + lbNetName + btnCancel + lbDomain + lbCaPwd + checkBoxPwd + lbSecurity + cbxPEAPver + lbAuth + lbPEAPver + cbxAuth + lbCA + lbAnonyId + lbPassword + leUserName + cbxInnerAuth + lePassword + lbInnerAuth + lbUserName + checkBoxPwdSec + lbLeftupTitle + lineUp + lineDown + + + + diff --git a/KylinNM/wireless-security/dlgconnhidwifisecpwd.cpp b/KylinNM/wireless-security/dlgconnhidwifisecpwd.cpp new file mode 100644 index 0000000..94b90d7 --- /dev/null +++ b/KylinNM/wireless-security/dlgconnhidwifisecpwd.cpp @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see + +DlgConnHidWifiSecPwd::DlgConnHidWifiSecPwd(int type, QWidget *parent) : + WepOrWpa(type), + QDialog(parent), + ui(new Ui::DlgConnHidWifiSecPwd) +{ + ui->setupUi(this); + + this->setWindowFlags(Qt::FramelessWindowHint); + this->setAttribute(Qt::WA_TranslucentBackground); + //需要添加 void paintEvent(QPaintEvent *event) 函数 + + QPainterPath path; + auto rect = this->rect(); + rect.adjust(1, 1, -1, -1); + path.addRoundedRect(rect, 6, 6); + setProperty("blurRegion", QRegion(path.toFillPolygon().toPolygon())); + + this->setStyleSheet("QWidget{border-radius:6px;background-color:rgba(19,19,20,0.7);border:1px solid rgba(255, 255, 255, 0.05);}"); + + MyQss objQss; + + ui->lbBoder->setStyleSheet("QLabel{border-radius:6px;background-color:rgba(19,19,20,0.95);border:1px solid rgba(255, 255, 255, 0.05);}"); + ui->lbBoder->hide(); + ui->lbLeftupTitle->setStyleSheet("QLabel{border:0px;font-size:20px;color:rgba(255,255,255,0.97);background-color:transparent;}"); + ui->lbConn->setStyleSheet(objQss.labelQss); + ui->lbNetName->setStyleSheet(objQss.labelQss); + ui->lbSecurity->setStyleSheet(objQss.labelQss); + ui->lbAuth->setStyleSheet(objQss.labelQss); + ui->lbUserName->setStyleSheet(objQss.labelQss); + ui->lbPassword->setStyleSheet(objQss.labelQss); + + ui->cbxConn->setStyleSheet(objQss.cbxQss); + ui->cbxConn->setView(new QListView()); + ui->leNetName->setStyleSheet(objQss.leQss); + ui->cbxSecurity->setStyleSheet(objQss.cbxQss); + ui->cbxSecurity->setView(new QListView()); + ui->cbxAuth->setStyleSheet(objQss.cbxQss); + ui->cbxAuth->setView(new QListView()); + ui->leUserName->setStyleSheet(objQss.leQss); + ui->lePassword->setStyleSheet(objQss.leQss); + ui->checkBox->setStyleSheet(objQss.checkBoxQss); + + ui->btnCancel->setStyleSheet(objQss.btnCancelQss); + ui->btnConnect->setStyleSheet(objQss.btnConnQss); + ui->lineUp->setStyleSheet(objQss.lineQss); + ui->lineDown->setStyleSheet(objQss.lineQss); + + + ui->lbLeftupTitle->setText(tr("Add hidden Wi-Fi")); //加入隐藏Wi-Fi + ui->lbConn->setText(tr("Connection")); //连接设置: + ui->lbNetName->setText(tr("Network name")); //网络名称: + ui->lbSecurity->setText(tr("Wi-Fi security")); //Wi-Fi安全性: + ui->lbAuth->setText(tr("Authentication")); //认证: + ui->lbUserName->setText(tr("Username")); //用户名: + ui->lbPassword->setText(tr("Password")); //密码: + ui->btnCancel->setText(tr("Cancel")); //取消 + ui->btnConnect->setText(tr("Connect")); //连接 + + ui->cbxConn->addItem(tr("C_reate…")); //新建... + int status = system("nmcli connection show>/tmp/kylin-nm-connshow"); + if (status != 0){ syslog(LOG_ERR, "execute 'nmcli connection show' in function 'DlgConnHidWifiSecPwd' failed");} + QFile file("/tmp/kylin-nm-connshow"); + if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){ + qDebug()<<"Can't open the file!"; + } + QString txt = file.readAll(); + QStringList txtLine = txt.split("\n"); + file.close(); + foreach (QString line, txtLine) { + if(line.indexOf("wifi") != -1){ + QStringList subLine = line.split(" "); + ui->cbxConn->addItem(subLine[0]); + } + } + ui->cbxConn->setCurrentIndex(0); + + ui->cbxSecurity->addItem(tr("None")); //无 + ui->cbxSecurity->addItem(tr("WPA & WPA2 Personal")); //WPA 及 WPA2 个人 + ui->cbxSecurity->addItem(tr("WEP 40/128-bit Key (Hex or ASCII)")); //WEP 40/128 位密钥(十六进制或ASCII) + ui->cbxSecurity->addItem(tr("WEP 128-bit Passphrase")); //WEP 128 位密码句 + ui->cbxSecurity->addItem("LEAP"); + ui->cbxSecurity->addItem(tr("Dynamic WEP (802.1X)")); //动态 WEP (802.1x) + ui->cbxSecurity->addItem(tr("WPA & WPA2 Enterprise")); //WPA 及 WPA2 企业 + if (WepOrWpa == 0) { + ui->cbxSecurity->setCurrentIndex(5); + } else if (WepOrWpa == 1) { + ui->cbxSecurity->setCurrentIndex(6); + } + connect(ui->cbxSecurity,SIGNAL(currentIndexChanged(QString)),this,SLOT(changeDialogSecu())); + + ui->cbxAuth->addItem("TLS"); + ui->cbxAuth->addItem("LEAP"); + ui->cbxAuth->addItem("PWD"); + ui->cbxAuth->addItem("FAST"); + ui->cbxAuth->addItem(tr("Tunneled TLS"));//隧道 TLS + ui->cbxAuth->addItem(tr("Protected EAP (PEAP)")); //受保护的 EAP + ui->cbxAuth->setCurrentIndex(2); + connect(ui->cbxAuth,SIGNAL(currentIndexChanged(QString)),this,SLOT(changeDialogAuth())); + + ui->btnConnect->setEnabled(false); + + this->setFixedSize(432,487); + +} + +DlgConnHidWifiSecPwd::~DlgConnHidWifiSecPwd() +{ + delete ui; +} + +void DlgConnHidWifiSecPwd::mousePressEvent(QMouseEvent *event){ + if(event->button() == Qt::LeftButton){ + this->isPress = true; + this->winPos = this->pos(); + this->dragPos = event->globalPos(); + event->accept(); + } +} +void DlgConnHidWifiSecPwd::mouseReleaseEvent(QMouseEvent *event){ + this->isPress = false; +} +void DlgConnHidWifiSecPwd::mouseMoveEvent(QMouseEvent *event){ + if(this->isPress){ + this->move(this->winPos - (this->dragPos - event->globalPos())); + event->accept(); + } +} + +void DlgConnHidWifiSecPwd::changeDialogSecu() +{ + if(ui->cbxSecurity->currentIndex()==0){ +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifi *connHidWifi = new DlgConnHidWifi(0, 0, this->parentWidget()); + connHidWifi->show(); + } else if(ui->cbxSecurity->currentIndex()==1) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiWpa *connHidWifiWpa = new DlgConnHidWifiWpa(0, 0, this->parentWidget()); + connHidWifiWpa->show(); + } else if(ui->cbxSecurity->currentIndex()==2) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiWep *connHidWifiWep = new DlgConnHidWifiWep(0, this->parentWidget()); + connHidWifiWep->show(); + } else if(ui->cbxSecurity->currentIndex()==3) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiWep *connHidWifiWep = new DlgConnHidWifiWep(1, this->parentWidget()); + connHidWifiWep->show(); + } else if(ui->cbxSecurity->currentIndex()==4) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiLeap *connHidWifiLeap = new DlgConnHidWifiLeap(this->parentWidget()); + connHidWifiLeap->show(); + } else if(ui->cbxSecurity->currentIndex()==5) { + if (WepOrWpa == 1) { + ui->cbxSecurity->setCurrentIndex(5); + WepOrWpa = 0; + } + } else { + if (WepOrWpa == 0){ + ui->cbxSecurity->setCurrentIndex(6); + WepOrWpa = 1; + } + } +} + +void DlgConnHidWifiSecPwd::changeDialogAuth() +{ + if(ui->cbxAuth->currentIndex()==0){ +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecTls *connHidWifiSecTls = new DlgConnHidWifiSecTls(WepOrWpa, this->parentWidget()); + connHidWifiSecTls->show(); + } else if(ui->cbxAuth->currentIndex()==1) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecLeap *connHidWifiSecLeap = new DlgConnHidWifiSecLeap(WepOrWpa, this->parentWidget()); + connHidWifiSecLeap->show(); + } else if(ui->cbxAuth->currentIndex()==2) { + qDebug()<<"it's not need to change dialog"; + } else if(ui->cbxAuth->currentIndex()==3) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecFast *connHidWifiSecFast = new DlgConnHidWifiSecFast(WepOrWpa); + connHidWifiSecFast->show(); + } else if(ui->cbxAuth->currentIndex()==4) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecTunnelTLS *connHidWifiSecTuTls = new DlgConnHidWifiSecTunnelTLS(WepOrWpa); + connHidWifiSecTuTls->show(); + } else { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecPeap *connHidWifiSecPeap = new DlgConnHidWifiSecPeap(WepOrWpa); + connHidWifiSecPeap->show(); + } +} + +void DlgConnHidWifiSecPwd::on_btnCancel_clicked() +{ + this->close(); +} + +void DlgConnHidWifiSecPwd::on_btnConnect_clicked() +{ + this->close(); +} + +void DlgConnHidWifiSecPwd::on_checkBox_stateChanged(int arg1) +{ + if (arg1 == 0) { + ui->lePassword ->setEchoMode(QLineEdit::Password); + } else { + ui->lePassword->setEchoMode(QLineEdit::Normal); + } +} + +void DlgConnHidWifiSecPwd::on_leNetName_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->leUserName->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->lePassword->text() == ""){ + ui->btnConnect->setEnabled(false); + } else { + ui->btnConnect->setEnabled(true); + } +} + +void DlgConnHidWifiSecPwd::on_leUserName_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->leUserName->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->lePassword->text() == ""){ + ui->btnConnect->setEnabled(false); + } else { + ui->btnConnect->setEnabled(true); + } +} + +void DlgConnHidWifiSecPwd::on_lePassword_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->leUserName->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->lePassword->text() == ""){ + ui->btnConnect->setEnabled(false); + } else { + ui->btnConnect->setEnabled(true); + } +} + +void DlgConnHidWifiSecPwd::paintEvent(QPaintEvent *event) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); + QWidget::paintEvent(event); +} diff --git a/KylinNM/wireless-security/dlgconnhidwifisecpwd.h b/KylinNM/wireless-security/dlgconnhidwifisecpwd.h new file mode 100644 index 0000000..d44250d --- /dev/null +++ b/KylinNM/wireless-security/dlgconnhidwifisecpwd.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include + +namespace Ui { +class DlgConnHidWifiSecPwd; +} + +class DlgConnHidWifiSecPwd : public QDialog +{ + Q_OBJECT + +public: + explicit DlgConnHidWifiSecPwd(int type, QWidget *parent = 0); + ~DlgConnHidWifiSecPwd(); + +protected: + void paintEvent(QPaintEvent *event); + +public slots: + void changeDialogSecu(); + void changeDialogAuth(); + +private slots: + void on_btnCancel_clicked(); + + void on_btnConnect_clicked(); + + void on_checkBox_stateChanged(int arg1); + + void on_leNetName_textEdited(const QString &arg1); + + void on_leUserName_textEdited(const QString &arg1); + + void on_lePassword_textEdited(const QString &arg1); + +private: + Ui::DlgConnHidWifiSecPwd *ui; + int WepOrWpa = 0;//0 WEP;1WPA + + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + +// QString labelQss, cbxQss, leQss, btnConnQss, btnCancelQss, lineQss, checkBoxQss, checkBoxCAQss; + + bool isPress; + QPoint winPos; + QPoint dragPos; +}; + +#endif // DLGCONNHIDWIFISECPWD_H diff --git a/KylinNM/wireless-security/dlgconnhidwifisecpwd.ui b/KylinNM/wireless-security/dlgconnhidwifisecpwd.ui new file mode 100644 index 0000000..7dc8cba --- /dev/null +++ b/KylinNM/wireless-security/dlgconnhidwifisecpwd.ui @@ -0,0 +1,354 @@ + + + DlgConnHidWifiSecPwd + + + + 0 + 0 + 432 + 487 + + + + Connect to Hidden Wi-Fi Network + + + + + 333 + 363 + 18 + 9 + + + + + 10 + + + + + + + + + + 215 + 440 + 90 + 30 + + + + + 10 + + + + + + + + + + 0 + 0 + 432 + 487 + + + + + + + + + + 30 + 30 + 140 + 22 + + + + + + + + + + 175 + 170 + 182 + 32 + + + + + 10 + + + + + + + 76 + 266 + 90 + 20 + + + + + 10 + + + + + + + + + + 76 + 311 + 90 + 20 + + + + + 10 + + + + + + + + + + 76 + 80 + 90 + 20 + + + + + 10 + + + + + + + + + + 175 + 75 + 182 + 32 + + + + + 10 + + + + + + + + + + 315 + 440 + 90 + 30 + + + + + 10 + + + + + + + + + + 175 + 215 + 182 + 32 + + + + + 10 + + + + + + + + + + 175 + 305 + 182 + 32 + + + + + 10 + + + + + + + 175 + 350 + 182 + 32 + + + + + 10 + + + + QLineEdit::Password + + + + + + 76 + 176 + 90 + 20 + + + + + 10 + + + + + + + + + + 76 + 221 + 90 + 20 + + + + + 10 + + + + + + + + + + 76 + 356 + 90 + 20 + + + + + 10 + + + + + + + + + + 175 + 260 + 182 + 32 + + + + + 10 + + + + + + + + + + 10 + 140 + 412 + 1 + + + + Qt::Horizontal + + + + + + 10 + 410 + 412 + 1 + + + + Qt::Horizontal + + + lbBoder + btnCancel + leNetName + lbAuth + lbUserName + lbConn + cbxConn + btnConnect + cbxSecurity + leUserName + lePassword + lbNetName + lbSecurity + lbPassword + cbxAuth + lbLeftupTitle + lineUp + lineDown + checkBox + + + + diff --git a/KylinNM/wireless-security/dlgconnhidwifisectls.cpp b/KylinNM/wireless-security/dlgconnhidwifisectls.cpp new file mode 100644 index 0000000..5a61d76 --- /dev/null +++ b/KylinNM/wireless-security/dlgconnhidwifisectls.cpp @@ -0,0 +1,628 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see + +DlgConnHidWifiSecTls::DlgConnHidWifiSecTls(int type, QWidget *parent) : + WepOrWpa(type), + QDialog(parent), + ui(new Ui::DlgConnHidWifiSecTls) +{ + ui->setupUi(this); + + this->setWindowFlags(Qt::FramelessWindowHint); + this->setAttribute(Qt::WA_TranslucentBackground); + //需要添加 void paintEvent(QPaintEvent *event) 函数 + + QPainterPath path; + auto rect = this->rect(); + rect.adjust(1, 1, -1, -1); + path.addRoundedRect(rect, 6, 6); + setProperty("blurRegion", QRegion(path.toFillPolygon().toPolygon())); + + this->setStyleSheet("QWidget{border-radius:6px;background-color:rgba(19,19,20,0.7);border:1px solid rgba(255, 255, 255, 0.05);}"); + + MyQss objQss; + + ui->lbBoder->setStyleSheet("QLabel{border-radius:6px;background-color:rgba(19,19,20,0.95);border:1px solid rgba(255, 255, 255, 0.05);}"); + ui->lbBoder->hide(); + ui->lbLeftupTitle->setStyleSheet("QLabel{border:0px;font-size:20px;color:rgba(255,255,255,0.97);background-color:transparent;}"); + ui->lbConn->setStyleSheet(objQss.labelQss); + ui->lbNetName->setStyleSheet(objQss.labelQss); + ui->lbSecurity->setStyleSheet(objQss.labelQss); + ui->lbAuth->setStyleSheet(objQss.labelQss); + ui->lbIdentity->setStyleSheet(objQss.labelQss); + ui->lbDomain->setStyleSheet(objQss.labelQss); + ui->lbCA->setStyleSheet(objQss.labelQss); + ui->lbCaPwd->setStyleSheet(objQss.labelQss); + ui->lbUserCertify->setStyleSheet(objQss.labelQss); + ui->lbUserCertifyPwd->setStyleSheet(objQss.labelQss); + ui->lbUserPriKey->setStyleSheet(objQss.labelQss); + ui->lbUserKeyPwd->setStyleSheet(objQss.labelQss); + + ui->cbxConn->setStyleSheet(objQss.cbxQss); + ui->cbxConn->setView(new QListView()); + ui->leNetName->setStyleSheet(objQss.leQss); + ui->cbxSecurity->setStyleSheet(objQss.cbxQss); + ui->cbxSecurity->setView(new QListView()); + ui->cbxAuth->setStyleSheet(objQss.cbxQss); + ui->cbxAuth->setView(new QListView()); + ui->leIdentity->setStyleSheet(objQss.leQss); + ui->leDomain->setStyleSheet(objQss.leQss); + ui->cbxCA->setStyleSheet(objQss.cbxQss); + ui->cbxCA->setView(new QListView()); + ui->leCaPwd->setStyleSheet(objQss.leQss); + ui->checkBoxPwd->setStyleSheet(objQss.checkBoxQss); + ui->checkBoxCA->setStyleSheet(objQss.checkBoxCAQss); + ui->cbxUserCertify->setStyleSheet(objQss.cbxQss); + ui->cbxUserCertify->setView(new QListView()); + ui->leUserCertifyPwd->setStyleSheet(objQss.leQss); + ui->cbxUserPriKey->setStyleSheet(objQss.cbxQss); + ui->cbxUserPriKey->setView(new QListView()); + ui->leUserKeyPwd->setStyleSheet(objQss.leQss); + ui->checkBoxPwdSec->setStyleSheet(objQss.checkBoxQss); + + ui->btnCancel->setStyleSheet(objQss.btnCancelQss); + ui->btnConnect->setStyleSheet(objQss.btnCancelQss); + ui->lineUp->setStyleSheet(objQss.lineQss); + ui->lineDown->setStyleSheet(objQss.lineQss); + ui->checkBoxCA->setFocusPolicy(Qt::NoFocus); + + ui->lbLeftupTitle->setText(tr("Add hidden Wi-Fi")); //加入隐藏Wi-Fi + ui->lbConn->setText(tr("Connection")); //连接设置: + ui->lbNetName->setText(tr("Network name")); //网络名称: + ui->lbSecurity->setText(tr("Wi-Fi security")); //Wi-Fi安全性: + ui->lbAuth->setText(tr("Authentication")); //认证: + ui->lbIdentity->setText(tr("Identity")); //身份: + ui->lbDomain->setText(tr("Domain")); //域名: + ui->lbCA->setText(tr("CA certificate")); //CA 证书: + ui->lbCaPwd->setText(tr("CA certificate password")); //CA 证书密码: + ui->checkBoxCA->setText(tr("No CA certificate is required")); //不需要CA证书 + ui->lbUserCertify->setText(tr("User certificate")); //用户证书: + ui->lbUserCertifyPwd->setText(tr("User certificate password")); //用户证书密码: + ui->lbUserPriKey->setText(tr("User private key")); //用户私钥: + ui->lbUserKeyPwd->setText(tr("User key password")); //用户密钥密码: + ui->btnCancel->setText(tr("Cancel")); //取消 + ui->btnConnect->setText(tr("Connect")); //连接 + + ui->cbxConn->addItem(tr("C_reate…")); //新建... + int status = system("nmcli connection show>/tmp/kylin-nm-connshow"); + if (status != 0){ syslog(LOG_ERR, "execute 'nmcli connection show' in function 'DlgConnHidWifiSecTls' failed");} + QFile file("/tmp/kylin-nm-connshow"); + if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){ + qDebug()<<"Can't open the file!"; + } + QString txt = file.readAll(); + QStringList txtLine = txt.split("\n"); + file.close(); + foreach (QString line, txtLine) { + if(line.indexOf("wifi") != -1){ + QStringList subLine = line.split(" "); + ui->cbxConn->addItem(subLine[0]); + } + } + ui->cbxConn->setCurrentIndex(0); + + ui->cbxSecurity->addItem(tr("None")); //无 + ui->cbxSecurity->addItem(tr("WPA & WPA2 Personal")); //WPA 及 WPA2 个人 + ui->cbxSecurity->addItem(tr("WEP 40/128-bit Key (Hex or ASCII)")); //WEP 40/128 位密钥(十六进制或ASCII) + ui->cbxSecurity->addItem(tr("WEP 128-bit Passphrase")); //WEP 128 位密码句 + ui->cbxSecurity->addItem("LEAP"); + ui->cbxSecurity->addItem(tr("Dynamic WEP (802.1X)")); //动态 WEP (802.1x) + ui->cbxSecurity->addItem(tr("WPA & WPA2 Enterprise")); //WPA 及 WPA2 企业 + if (WepOrWpa == 0) { + ui->cbxSecurity->setCurrentIndex(5); + } else if (WepOrWpa == 1) { + ui->cbxSecurity->setCurrentIndex(6); + } + connect(ui->cbxSecurity,SIGNAL(currentIndexChanged(QString)),this,SLOT(changeDialogSecu())); + + ui->cbxAuth->addItem("TLS"); + ui->cbxAuth->addItem("LEAP"); + ui->cbxAuth->addItem("PWD"); + ui->cbxAuth->addItem("FAST"); + ui->cbxAuth->addItem(tr("Tunneled TLS"));//隧道 TLS + ui->cbxAuth->addItem(tr("Protected EAP (PEAP)")); //受保护的 EAP + ui->cbxAuth->setCurrentIndex(0); + connect(ui->cbxAuth,SIGNAL(currentIndexChanged(QString)),this,SLOT(changeDialogAuth())); + + ui->cbxCA->addItem(tr("None")); //无 + ui->cbxCA->addItem(tr("Choose from file")); //从文件选择... + ui->cbxCA->setCurrentIndex(0); + + ui->cbxUserCertify->addItem(tr("None")); //无 + ui->cbxUserCertify->addItem(tr("Choose from file")); //从文件选择... + ui->cbxUserCertify->setCurrentIndex(0); + + ui->cbxUserPriKey->addItem(tr("None")); //无 + ui->cbxUserPriKey->addItem(tr("Choose from file")); //从文件选择... + ui->cbxUserPriKey->setCurrentIndex(0); + + ui->btnConnect->setEnabled(false); + + ui->lbCaPwd->setEnabled(false); + ui->leCaPwd->setEnabled(false); + + ui->lbUserCertifyPwd->setEnabled(false); + ui->leUserCertifyPwd->setEnabled(false); + + ui->lbUserPriKey->setEnabled(false); + ui->cbxUserPriKey->setEnabled(false); + + ui->lbUserKeyPwd->setEnabled(false); + ui->leUserKeyPwd->setEnabled(false); + + this->setFixedSize(432,705); + +} + +DlgConnHidWifiSecTls::~DlgConnHidWifiSecTls() +{ + delete ui; +} + +void DlgConnHidWifiSecTls::mousePressEvent(QMouseEvent *event){ + if(event->button() == Qt::LeftButton){ + this->isPress = true; + this->winPos = this->pos(); + this->dragPos = event->globalPos(); + event->accept(); + } +} +void DlgConnHidWifiSecTls::mouseReleaseEvent(QMouseEvent *event){ + this->isPress = false; +} +void DlgConnHidWifiSecTls::mouseMoveEvent(QMouseEvent *event){ + if(this->isPress){ + this->move(this->winPos - (this->dragPos - event->globalPos())); + event->accept(); + } +} + +void DlgConnHidWifiSecTls::changeDialogSecu() +{ + if(ui->cbxSecurity->currentIndex()==0){ +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifi *connHidWifi = new DlgConnHidWifi(0, 0, this->parentWidget()); + connHidWifi->show(); + } else if(ui->cbxSecurity->currentIndex()==1) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiWpa *connHidWifiWpa = new DlgConnHidWifiWpa(0, 0, this->parentWidget()); + connHidWifiWpa->show(); + } else if(ui->cbxSecurity->currentIndex()==2) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiWep *connHidWifiWep = new DlgConnHidWifiWep(0, this->parentWidget()); + connHidWifiWep->show(); + } else if(ui->cbxSecurity->currentIndex()==3) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiWep *connHidWifiWep = new DlgConnHidWifiWep(1, this->parentWidget()); + connHidWifiWep->show(); + } else if(ui->cbxSecurity->currentIndex()==4) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiLeap *connHidWifiLeap = new DlgConnHidWifiLeap(this->parentWidget()); + connHidWifiLeap->show(); + } else if(ui->cbxSecurity->currentIndex()==5) { + if (WepOrWpa == 1) { + ui->cbxSecurity->setCurrentIndex(5); + WepOrWpa = 0; + } + } else { + if (WepOrWpa == 0){ + ui->cbxSecurity->setCurrentIndex(6); + WepOrWpa = 1; + } + } +} + +void DlgConnHidWifiSecTls::changeDialogAuth() +{ + if(ui->cbxAuth->currentIndex()==0){ + qDebug()<<"it's not need to change dialog"; + } else if(ui->cbxAuth->currentIndex()==1) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecLeap *connHidWifiSecLeap = new DlgConnHidWifiSecLeap(WepOrWpa); + connHidWifiSecLeap->show(); + } else if(ui->cbxAuth->currentIndex()==2) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecPwd *connHidWifiSecPwd = new DlgConnHidWifiSecPwd(WepOrWpa); + connHidWifiSecPwd->show(); + } else if(ui->cbxAuth->currentIndex()==3) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecFast *connHidWifiSecFast = new DlgConnHidWifiSecFast(WepOrWpa); + connHidWifiSecFast->show(); + } else if(ui->cbxAuth->currentIndex()==4) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecTunnelTLS *connHidWifiSecTuTls = new DlgConnHidWifiSecTunnelTLS(WepOrWpa); + connHidWifiSecTuTls->show(); + } else { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecPeap *connHidWifiSecPeap = new DlgConnHidWifiSecPeap(WepOrWpa); + connHidWifiSecPeap->show(); + } +} + +void DlgConnHidWifiSecTls::on_cbxCA_currentIndexChanged(const QString &arg1) +{ + if (ui->cbxCA->currentIndex() == 0){ + ui->leCaPwd->setText(""); + ui->lbCaPwd->setEnabled(false); + ui->leCaPwd->setEnabled(false); + }else{ + ui->leCaPwd->setText(""); + ui->lbCaPwd->setEnabled(true); + ui->leCaPwd->setEnabled(true); + } +} + +void DlgConnHidWifiSecTls::on_cbxUserCertify_currentIndexChanged(const QString &arg1) +{ + if (ui->cbxUserCertify->currentIndex() == 0){ + ui->leUserCertifyPwd->setText(""); + ui->lbUserCertifyPwd->setEnabled(false); + ui->leUserCertifyPwd->setEnabled(false); + + ui->lbUserPriKey->setEnabled(false); + ui->cbxUserPriKey->setEnabled(false); + ui->cbxUserPriKey->setCurrentIndex(0); + + ui->leUserKeyPwd->setText(""); + ui->lbUserKeyPwd->setEnabled(false); + ui->leUserKeyPwd->setEnabled(false); + }else{ + ui->leUserCertifyPwd->setText(""); + ui->lbUserCertifyPwd->setEnabled(true); + ui->leUserCertifyPwd->setEnabled(true); + + ui->lbUserPriKey->setEnabled(true); + ui->cbxUserPriKey->setEnabled(true); + ui->cbxUserPriKey->setCurrentIndex(0); + + ui->leUserKeyPwd->setText(""); + ui->lbUserKeyPwd->setEnabled(false); + ui->leUserKeyPwd->setEnabled(false); + } +} + +void DlgConnHidWifiSecTls::on_cbxUserPriKey_currentIndexChanged(const QString &arg1) +{ + if (ui->cbxUserPriKey->currentIndex() == 0){ + ui->leUserKeyPwd->setText(""); + ui->lbUserKeyPwd->setEnabled(false); + ui->leUserKeyPwd->setEnabled(false); + }else{ + ui->leUserKeyPwd->setText(""); + ui->lbUserKeyPwd->setEnabled(true); + ui->leUserKeyPwd->setEnabled(true); + } +} + +void DlgConnHidWifiSecTls::on_btnCancel_clicked() +{ + this->close(); +} + +void DlgConnHidWifiSecTls::on_btnConnect_clicked() +{ + this->close(); +} + +void DlgConnHidWifiSecTls::on_checkBoxCA_stateChanged(int arg1) +{ + if (arg1 == 0) { + ui->cbxCA->setCurrentIndex(0); + ui->lbCA->setEnabled(true); + ui->cbxCA->setEnabled(true); + } else { + ui->cbxCA->setCurrentIndex(0); + ui->lbCA->setEnabled(false); + ui->cbxCA->setEnabled(false); + } +} + +void DlgConnHidWifiSecTls::on_checkBoxPwd_stateChanged(int arg1) +{ + if (arg1 == 0) { + ui->leCaPwd->setEchoMode(QLineEdit::Password); + } else { + ui->leCaPwd->setEchoMode(QLineEdit::Normal); + } +} + +void DlgConnHidWifiSecTls::on_checkBoxPwdSec_stateChanged(int arg1) +{ + if (arg1 == 0) { + ui->leUserCertifyPwd->setEchoMode(QLineEdit::Password); + ui->leUserKeyPwd->setEchoMode(QLineEdit::Password); + } else { + ui->leUserCertifyPwd->setEchoMode(QLineEdit::Normal); + ui->leUserKeyPwd->setEchoMode(QLineEdit::Normal); + } +} + +void DlgConnHidWifiSecTls::on_leNetName_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->leIdentity->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->leDomain->text() == ""){ + ui->btnConnect->setEnabled(false); + } else { + if (ui->cbxCA->currentIndex() == 0){ + if (ui->cbxUserCertify->currentIndex() == 0){ + ui->btnConnect->setEnabled(true); + }else { + if (ui->leUserCertifyPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + } else { + if (ui->cbxUserPriKey->currentIndex() == 0){ + ui->btnConnect->setEnabled(true); + }else if (ui->leUserKeyPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + } else{ + ui->btnConnect->setEnabled(true); + } + } + } + } else { + if (ui->leCaPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + } else { + if (ui->cbxUserCertify->currentIndex() == 0){ + ui->btnConnect->setEnabled(true); + }else { + if (ui->leUserCertifyPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + } else { + if (ui->cbxUserPriKey->currentIndex() == 0){ + ui->btnConnect->setEnabled(true); + }else if (ui->leUserKeyPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + } else{ + ui->btnConnect->setEnabled(true); + } + } + } + } + } + } +} + +void DlgConnHidWifiSecTls::on_leIdentity_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->leIdentity->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->leDomain->text() == ""){ + ui->btnConnect->setEnabled(false); + } else { + if (ui->cbxCA->currentIndex() == 0){ + if (ui->cbxUserCertify->currentIndex() == 0){ + ui->btnConnect->setEnabled(true); + }else { + if (ui->leUserCertifyPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + } else { + if (ui->cbxUserPriKey->currentIndex() == 0){ + ui->btnConnect->setEnabled(true); + }else if (ui->leUserKeyPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + } else{ + ui->btnConnect->setEnabled(true); + } + } + } + } else { + if (ui->leCaPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + } else { + if (ui->cbxUserCertify->currentIndex() == 0){ + ui->btnConnect->setEnabled(true); + }else { + if (ui->leUserCertifyPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + } else { + if (ui->cbxUserPriKey->currentIndex() == 0){ + ui->btnConnect->setEnabled(true); + }else if (ui->leUserKeyPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + } else{ + ui->btnConnect->setEnabled(true); + } + } + } + } + } + } +} + +void DlgConnHidWifiSecTls::on_leDomain_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->leIdentity->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->leDomain->text() == ""){ + ui->btnConnect->setEnabled(false); + } else { + if (ui->cbxCA->currentIndex() == 0){ + if (ui->cbxUserCertify->currentIndex() == 0){ + ui->btnConnect->setEnabled(true); + }else { + if (ui->leUserCertifyPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + } else { + if (ui->cbxUserPriKey->currentIndex() == 0){ + ui->btnConnect->setEnabled(true); + }else if (ui->leUserKeyPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + } else{ + ui->btnConnect->setEnabled(true); + } + } + } + } else { + if (ui->leCaPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + } else { + if (ui->cbxUserCertify->currentIndex() == 0){ + ui->btnConnect->setEnabled(true); + }else { + if (ui->leUserCertifyPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + } else { + if (ui->cbxUserPriKey->currentIndex() == 0){ + ui->btnConnect->setEnabled(true); + }else if (ui->leUserKeyPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + } else{ + ui->btnConnect->setEnabled(true); + } + } + } + } + } + } +} + +void DlgConnHidWifiSecTls::on_leCaPwd_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->leIdentity->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->leDomain->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if(ui->leCaPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + } else { + if (ui->cbxUserCertify->currentIndex() == 0){ + ui->btnConnect->setEnabled(true); + }else { + if (ui->leUserCertifyPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + } else { + if (ui->cbxUserPriKey->currentIndex() == 0){ + ui->btnConnect->setEnabled(true); + }else if (ui->leUserKeyPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + } else{ + ui->btnConnect->setEnabled(true); + } + } + } + } +} + +void DlgConnHidWifiSecTls::on_leUserCertifyPwd_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->leIdentity->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->leDomain->text() == ""){ + ui->btnConnect->setEnabled(false); + } else { + if (ui->cbxCA->currentIndex() == 0){ + if (ui->leUserCertifyPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + }else{ + if (ui->cbxUserPriKey->currentIndex() == 0){ + ui->btnConnect->setEnabled(true); + }else if (ui->leUserKeyPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + } else{ + ui->btnConnect->setEnabled(true); + } + } + } else { + if (ui->leCaPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + }else{ + if (ui->leUserCertifyPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + }else{ + if (ui->cbxUserPriKey->currentIndex() == 0){ + ui->btnConnect->setEnabled(true); + }else if (ui->leUserKeyPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + } else{ + ui->btnConnect->setEnabled(true); + } + } + } + } + } +} + +void DlgConnHidWifiSecTls::on_leUserKeyPwd_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->leIdentity->text() == ""){ + ui->btnConnect->setEnabled(false); + } else if (ui->leDomain->text() == ""){ + ui->btnConnect->setEnabled(false); + } else { + if (ui->cbxCA->currentIndex() == 0){ + if (ui->leUserCertifyPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leUserKeyPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + } else{ + ui->btnConnect->setEnabled(true); + } + } else { + if (ui->leCaPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + }else{ + if (ui->leUserCertifyPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leUserKeyPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + } else{ + ui->btnConnect->setEnabled(true); + } + } + } + } +} + +void DlgConnHidWifiSecTls::paintEvent(QPaintEvent *event) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); + QWidget::paintEvent(event); +} diff --git a/KylinNM/wireless-security/dlgconnhidwifisectls.h b/KylinNM/wireless-security/dlgconnhidwifisectls.h new file mode 100644 index 0000000..c133807 --- /dev/null +++ b/KylinNM/wireless-security/dlgconnhidwifisectls.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include + +namespace Ui { +class DlgConnHidWifiSecTls; +} + +class DlgConnHidWifiSecTls : public QDialog +{ + Q_OBJECT + +public: + explicit DlgConnHidWifiSecTls(int type, QWidget *parent = 0); + ~DlgConnHidWifiSecTls(); + +protected: + void paintEvent(QPaintEvent *event); + +public slots: + void changeDialogSecu(); + void changeDialogAuth(); + +private slots: + void on_btnCancel_clicked(); + + void on_btnConnect_clicked(); + + void on_checkBoxCA_stateChanged(int arg1); + + void on_checkBoxPwd_stateChanged(int arg1); + + void on_checkBoxPwdSec_stateChanged(int arg1); + + void on_leNetName_textEdited(const QString &arg1); + + void on_leIdentity_textEdited(const QString &arg1); + + void on_leDomain_textEdited(const QString &arg1); + + void on_leCaPwd_textEdited(const QString &arg1); + + void on_leUserCertifyPwd_textEdited(const QString &arg1); + + void on_leUserKeyPwd_textEdited(const QString &arg1); + + void on_cbxCA_currentIndexChanged(const QString &arg1); + + void on_cbxUserCertify_currentIndexChanged(const QString &arg1); + + void on_cbxUserPriKey_currentIndexChanged(const QString &arg1); + +private: + Ui::DlgConnHidWifiSecTls *ui; + int WepOrWpa = 0;//0 WEP;1WPA + + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + +// QString labelQss, cbxQss, leQss, btnConnQss, btnCancelQss, lineQss, checkBoxQss, checkBoxCAQss; + + bool isPress; + QPoint winPos; + QPoint dragPos; +}; + +#endif // DLGCONNHIDWIFISECTLS_H diff --git a/KylinNM/wireless-security/dlgconnhidwifisectls.ui b/KylinNM/wireless-security/dlgconnhidwifisectls.ui new file mode 100644 index 0000000..969ab4f --- /dev/null +++ b/KylinNM/wireless-security/dlgconnhidwifisectls.ui @@ -0,0 +1,617 @@ + + + DlgConnHidWifiSecTls + + + + 0 + 0 + 432 + 705 + + + + Connect to Hidden Wi-Fi Network + + + + + 180 + 185 + 200 + 32 + + + + + 10 + + + + + + + + + + 180 + 145 + 200 + 32 + + + + + 10 + + + + + + + 180 + 505 + 200 + 32 + + + + + 10 + + + + QLineEdit::Password + + + + + + 180 + 385 + 200 + 32 + + + + + 10 + + + + QLineEdit::Password + + + + + + 357 + 397 + 18 + 9 + + + + + 10 + + + + + + + + + + 76 + 80 + 90 + 20 + + + + + 10 + + + + + + + + + + 76 + 150 + 90 + 20 + + + + + 10 + + + + + + + + + + 76 + 310 + 90 + 20 + + + + + 10 + + + + + + + + + + 76 + 350 + 90 + 20 + + + + + 10 + + + + + + + + + + 180 + 465 + 200 + 32 + + + + + 10 + + + + + + + + + + 76 + 270 + 90 + 20 + + + + + 10 + + + + + + + + + + 76 + 510 + 90 + 20 + + + + + 10 + + + + + + + + + + 215 + 660 + 90 + 30 + + + + + 10 + + + + + + + + + + 76 + 590 + 90 + 20 + + + + + 10 + + + + + + + + + + 180 + 345 + 200 + 32 + + + + + 10 + + + + + + + + + + 76 + 550 + 90 + 20 + + + + + 10 + + + + + + + + + + 76 + 390 + 90 + 20 + + + + + 10 + + + + + + + + + + 180 + 305 + 200 + 32 + + + + + 10 + + + + + + + 357 + 597 + 18 + 9 + + + + + 10 + + + + + + + + + + 180 + 265 + 200 + 32 + + + + + 10 + + + + + + + 180 + 585 + 200 + 32 + + + + + 10 + + + + QLineEdit::Password + + + + + + 76 + 470 + 90 + 20 + + + + + 10 + + + + + + + + + + 76 + 230 + 90 + 20 + + + + + 10 + + + + + + + + + + 180 + 225 + 200 + 32 + + + + + 10 + + + + + + + + + + 315 + 660 + 90 + 30 + + + + + 10 + + + + + + + + + + 180 + 425 + 200 + 25 + + + + + 10 + + + + + + + + + + 180 + 75 + 200 + 32 + + + + + 10 + + + + + + + + + + 180 + 545 + 200 + 32 + + + + + 10 + + + + + + + + + + 76 + 190 + 90 + 20 + + + + + 10 + + + + + + + + + + 0 + 0 + 432 + 705 + + + + + + + + + + 30 + 30 + 140 + 22 + + + + + + + + + + 10 + 125 + 412 + 1 + + + + Qt::Horizontal + + + + + + 10 + 640 + 412 + 1 + + + + Qt::Horizontal + + + lbBoder + cbxSecurity + leNetName + leUserCertifyPwd + leCaPwd + checkBoxPwd + lbConn + lbNetName + lbDomain + lbCA + cbxUserCertify + lbIdentity + lbUserCertifyPwd + btnCancel + lbUserKeyPwd + cbxCA + lbUserPriKey + lbCaPwd + leDomain + leIdentity + leUserKeyPwd + lbUserCertify + lbAuth + cbxAuth + btnConnect + checkBoxCA + cbxConn + cbxUserPriKey + lbSecurity + lbLeftupTitle + checkBoxPwdSec + lineUp + lineDown + + + + diff --git a/KylinNM/wireless-security/dlgconnhidwifisectunneltls.cpp b/KylinNM/wireless-security/dlgconnhidwifisectunneltls.cpp new file mode 100644 index 0000000..fb02a60 --- /dev/null +++ b/KylinNM/wireless-security/dlgconnhidwifisectunneltls.cpp @@ -0,0 +1,457 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see + +DlgConnHidWifiSecTunnelTLS::DlgConnHidWifiSecTunnelTLS(int type, QWidget *parent) : + WepOrWpa(type), + QDialog(parent), + ui(new Ui::DlgConnHidWifiSecTunnelTLS) +{ + ui->setupUi(this); + + this->setWindowFlags(Qt::FramelessWindowHint); + this->setAttribute(Qt::WA_TranslucentBackground); + //需要添加 void paintEvent(QPaintEvent *event) 函数 + + QPainterPath path; + auto rect = this->rect(); + rect.adjust(1, 1, -1, -1); + path.addRoundedRect(rect, 6, 6); + setProperty("blurRegion", QRegion(path.toFillPolygon().toPolygon())); + + this->setStyleSheet("QWidget{border-radius:6px;background-color:rgba(19,19,20,0.7);border:1px solid rgba(255, 255, 255, 0.05);}"); + + MyQss objQss; + + ui->lbBoder->setStyleSheet("QLabel{border-radius:6px;background-color:rgba(19,19,20,0.95);border:1px solid rgba(255, 255, 255, 0.05);}"); + ui->lbBoder->hide(); + ui->lbLeftupTitle->setStyleSheet("QLabel{border:0px;font-size:20px;color:rgba(255,255,255,0.97);background-color:transparent;}"); + ui->lbConn->setStyleSheet(objQss.labelQss); + ui->lbNetName->setStyleSheet(objQss.labelQss); + ui->lbSecurity->setStyleSheet(objQss.labelQss); + ui->lbAuth->setStyleSheet(objQss.labelQss); + ui->lbAnonyId->setStyleSheet(objQss.labelQss); + ui->lbDomain->setStyleSheet(objQss.labelQss); + ui->lbCA->setStyleSheet(objQss.labelQss); + ui->lbCaPwd->setStyleSheet(objQss.labelQss); + ui->lbInnerAuth->setStyleSheet(objQss.labelQss); + ui->lbUserName->setStyleSheet(objQss.labelQss); + ui->lbPassword->setStyleSheet(objQss.labelQss); + + ui->cbxConn->setStyleSheet(objQss.cbxQss); + ui->cbxConn->setView(new QListView()); + ui->leNetName->setStyleSheet(objQss.leQss); + ui->cbxSecurity->setStyleSheet(objQss.cbxQss); + ui->cbxSecurity->setView(new QListView()); + ui->cbxAuth->setStyleSheet(objQss.cbxQss); + ui->cbxAuth->setView(new QListView()); + ui->leAnonyId->setStyleSheet(objQss.leQss); + ui->leDomain->setStyleSheet(objQss.leQss); + ui->cbxCA->setStyleSheet(objQss.cbxQss); + ui->cbxCA->setView(new QListView()); + ui->leCaPwd->setStyleSheet(objQss.leQss); + ui->checkBoxPwd->setStyleSheet(objQss.checkBoxQss); + ui->checkBoxCA->setStyleSheet(objQss.checkBoxCAQss); + ui->cbxInnerAuth->setStyleSheet(objQss.cbxQss); + ui->cbxInnerAuth->setView(new QListView()); + ui->leUserName->setStyleSheet(objQss.leQss); + ui->lePassword->setStyleSheet(objQss.leQss); + ui->checkBoxPwdSec->setStyleSheet(objQss.checkBoxQss); + + ui->btnCancel->setStyleSheet(objQss.btnCancelQss); + ui->btnConnect->setStyleSheet(objQss.btnConnQss); + ui->lineUp->setStyleSheet(objQss.lineQss); + ui->lineDown->setStyleSheet(objQss.lineQss); + ui->checkBoxCA->setFocusPolicy(Qt::NoFocus); + + + ui->lbLeftupTitle->setText(tr("Add hidden Wi-Fi")); //加入隐藏Wi-Fi + ui->lbConn->setText(tr("Connection")); //连接设置: + ui->lbNetName->setText(tr("Network name")); //网络名称: + ui->lbSecurity->setText(tr("Wi-Fi security")); //Wi-Fi安全性: + ui->lbAuth->setText(tr("Authentication")); //认证: + ui->lbAnonyId->setText(tr("Anonymous identity")); //匿名身份: + ui->lbDomain->setText(tr("Domain")); //域名: + ui->lbCA->setText(tr("CA certificate")); //CA 证书: + ui->lbCaPwd->setText(tr("CA certificate password")); //CA 证书密码: + ui->checkBoxCA->setText(tr("No CA certificate is required")); //不需要CA证书 + ui->lbInnerAuth->setText(tr("Inner authentication")); //内部认证: + ui->lbUserName->setText(tr("Username")); //用户名: + ui->lbPassword->setText(tr("Password")); //密码: + ui->btnCancel->setText(tr("Cancel")); //取消 + ui->btnConnect->setText(tr("Connect")); //连接 + + ui->cbxConn->addItem(tr("C_reate…")); //新建... + int status = system("nmcli connection show>/tmp/kylin-nm-connshow"); + if (status != 0){ syslog(LOG_ERR, "execute 'nmcli connection show' in function 'DlgConnHidWifiSecTunnelTLS' failed");} + QFile file("/tmp/kylin-nm-connshow"); + if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){ + qDebug()<<"Can't open the file!"; + } + QString txt = file.readAll(); + QStringList txtLine = txt.split("\n"); + file.close(); + foreach (QString line, txtLine) { + if(line.indexOf("wifi") != -1){ + QStringList subLine = line.split(" "); + ui->cbxConn->addItem(subLine[0]); + } + } + ui->cbxConn->setCurrentIndex(0); + + ui->cbxSecurity->addItem(tr("None")); //无 + ui->cbxSecurity->addItem(tr("WPA & WPA2 Personal")); //WPA 及 WPA2 个人 + ui->cbxSecurity->addItem(tr("WEP 40/128-bit Key (Hex or ASCII)")); //WEP 40/128 位密钥(十六进制或ASCII) + ui->cbxSecurity->addItem(tr("WEP 128-bit Passphrase")); //WEP 128 位密码句 + ui->cbxSecurity->addItem("LEAP"); + ui->cbxSecurity->addItem(tr("Dynamic WEP (802.1X)")); //动态 WEP (802.1x) + ui->cbxSecurity->addItem(tr("WPA & WPA2 Enterprise")); //WPA 及 WPA2 企业 + if (WepOrWpa == 0) { + ui->cbxSecurity->setCurrentIndex(5); + } else if (WepOrWpa == 1) { + ui->cbxSecurity->setCurrentIndex(6); + } + connect(ui->cbxSecurity,SIGNAL(currentIndexChanged(QString)),this,SLOT(changeDialogSecu())); + + ui->cbxAuth->addItem("TLS"); + ui->cbxAuth->addItem("LEAP"); + ui->cbxAuth->addItem("PWD"); + ui->cbxAuth->addItem("FAST"); + ui->cbxAuth->addItem(tr("Tunneled TLS"));//隧道 TLS + ui->cbxAuth->addItem(tr("Protected EAP (PEAP)")); //受保护的 EAP + ui->cbxAuth->setCurrentIndex(4); + connect(ui->cbxAuth,SIGNAL(currentIndexChanged(QString)),this,SLOT(changeDialogAuth())); + + ui->cbxCA->addItem(tr("None")); //无 + ui->cbxCA->addItem(tr("Choose from file")); //从文件选择... + ui->cbxCA->setCurrentIndex(0); + + ui->cbxInnerAuth->addItem("PAP"); + ui->cbxInnerAuth->addItem("MSCHAP"); + ui->cbxInnerAuth->addItem("MSCHAPv2"); + ui->cbxInnerAuth->addItem("MSCHAPv2(no EAP)"); + ui->cbxInnerAuth->addItem("CHAP"); + ui->cbxInnerAuth->addItem("MDS"); + ui->cbxInnerAuth->addItem("GTC"); + ui->cbxInnerAuth->setCurrentIndex(0); + + ui->btnConnect->setEnabled(false); + + this->setFixedSize(432,665); + +} + +DlgConnHidWifiSecTunnelTLS::~DlgConnHidWifiSecTunnelTLS() +{ + delete ui; +} + +void DlgConnHidWifiSecTunnelTLS::mousePressEvent(QMouseEvent *event){ + if(event->button() == Qt::LeftButton){ + this->isPress = true; + this->winPos = this->pos(); + this->dragPos = event->globalPos(); + event->accept(); + } +} +void DlgConnHidWifiSecTunnelTLS::mouseReleaseEvent(QMouseEvent *event){ + this->isPress = false; +} +void DlgConnHidWifiSecTunnelTLS::mouseMoveEvent(QMouseEvent *event){ + if(this->isPress){ + this->move(this->winPos - (this->dragPos - event->globalPos())); + event->accept(); + } +} + +void DlgConnHidWifiSecTunnelTLS::changeDialogSecu() +{ + if(ui->cbxSecurity->currentIndex()==0){ +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifi *connHidWifi = new DlgConnHidWifi(0, 0, this->parentWidget()); + connHidWifi->show(); + } else if(ui->cbxSecurity->currentIndex()==1) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiWpa *connHidWifiWpa = new DlgConnHidWifiWpa(0, 0, this->parentWidget()); + connHidWifiWpa->show(); + } else if(ui->cbxSecurity->currentIndex()==2) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiWep *connHidWifiWep = new DlgConnHidWifiWep(0, this->parentWidget()); + connHidWifiWep->show(); + } else if(ui->cbxSecurity->currentIndex()==3) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiWep *connHidWifiWep = new DlgConnHidWifiWep(1, this->parentWidget()); + connHidWifiWep->show(); + } else if(ui->cbxSecurity->currentIndex()==4) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiLeap *connHidWifiLeap = new DlgConnHidWifiLeap(this->parentWidget()); + connHidWifiLeap->show(); + } else if(ui->cbxSecurity->currentIndex()==5) { + if (WepOrWpa == 1) { + ui->cbxSecurity->setCurrentIndex(5); + WepOrWpa = 0; + } + } else { + if (WepOrWpa == 0){ + ui->cbxSecurity->setCurrentIndex(6); + WepOrWpa = 1; + } + } +} + +void DlgConnHidWifiSecTunnelTLS::changeDialogAuth() +{ + if(ui->cbxAuth->currentIndex()==0){ +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecTls *connHidWifiSecTls = new DlgConnHidWifiSecTls(WepOrWpa, this->parentWidget()); + connHidWifiSecTls->show(); + } else if(ui->cbxAuth->currentIndex()==1) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecLeap *connHidWifiSecLeap = new DlgConnHidWifiSecLeap(WepOrWpa, this->parentWidget()); + connHidWifiSecLeap->show(); + } else if(ui->cbxAuth->currentIndex()==2) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecPwd *connHidWifiSecPwd = new DlgConnHidWifiSecPwd(WepOrWpa, this->parentWidget()); + connHidWifiSecPwd->show(); + } else if(ui->cbxAuth->currentIndex()==3) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecFast *connHidWifiSecFast = new DlgConnHidWifiSecFast(WepOrWpa, this->parentWidget()); + connHidWifiSecFast->show(); + } else if(ui->cbxAuth->currentIndex()==4) { + qDebug()<<"it's not need to change dialog"; + } else { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecPeap *connHidWifiSecPeap = new DlgConnHidWifiSecPeap(WepOrWpa, this->parentWidget()); + connHidWifiSecPeap->show(); + } +} + +void DlgConnHidWifiSecTunnelTLS::on_cbxCA_currentIndexChanged(const QString &arg1) +{ + if (ui->cbxCA->currentIndex() == 0){ + ui->leCaPwd->setText(""); + ui->lbCaPwd->setEnabled(false); + ui->leCaPwd->setEnabled(false); + } else { + ui->leCaPwd->setText(""); + ui->lbCaPwd->setEnabled(true); + ui->leCaPwd->setEnabled(true); + } +} + +void DlgConnHidWifiSecTunnelTLS::on_btnCancel_clicked() +{ + this->close(); +} + +void DlgConnHidWifiSecTunnelTLS::on_btnConnect_clicked() +{ + this->close(); +} + +void DlgConnHidWifiSecTunnelTLS::on_checkBoxPwd_stateChanged(int arg1) +{ + if (arg1 == 0) { + ui->leCaPwd->setEchoMode(QLineEdit::Password); + } else { + ui->leCaPwd->setEchoMode(QLineEdit::Normal); + } +} + +void DlgConnHidWifiSecTunnelTLS::on_checkBoxCA_stateChanged(int arg1) +{ + if (arg1 == 0) { + ui->cbxCA->setCurrentIndex(0); + ui->leCaPwd->setText(""); + ui->lbCA->setEnabled(true); + ui->cbxCA->setEnabled(true); + } else { + ui->cbxCA->setCurrentIndex(0); + ui->leCaPwd->setText(""); + ui->lbCA->setEnabled(false); + ui->cbxCA->setEnabled(false); + } +} + +void DlgConnHidWifiSecTunnelTLS::on_checkBoxPwdSec_stateChanged(int arg1) +{ + if (arg1 == 0) { + ui->lePassword->setEchoMode(QLineEdit::Password); + } else { + ui->lePassword->setEchoMode(QLineEdit::Normal); + } +} + +void DlgConnHidWifiSecTunnelTLS::on_leNetName_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leAnonyId->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leDomain->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leUserName->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->lePassword->text() == ""){ + ui->btnConnect->setEnabled(false); + }else{ + if (ui->cbxCA->currentIndex() == 0){ + ui->btnConnect->setEnabled(true); + }else if(ui->leCaPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + }else{ + ui->btnConnect->setEnabled(true); + } + } +} + +void DlgConnHidWifiSecTunnelTLS::on_leAnonyId_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leAnonyId->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leDomain->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leUserName->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->lePassword->text() == ""){ + ui->btnConnect->setEnabled(false); + }else{ + if (ui->cbxCA->currentIndex() == 0){ + ui->btnConnect->setEnabled(true); + }else if(ui->leCaPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + }else{ + ui->btnConnect->setEnabled(true); + } + } +} + +void DlgConnHidWifiSecTunnelTLS::on_leDomain_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leAnonyId->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leDomain->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leUserName->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->lePassword->text() == ""){ + ui->btnConnect->setEnabled(false); + }else{ + if (ui->cbxCA->currentIndex() == 0){ + ui->btnConnect->setEnabled(true); + }else if(ui->leCaPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + }else{ + ui->btnConnect->setEnabled(true); + } + } +} + +void DlgConnHidWifiSecTunnelTLS::on_leCaPwd_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leAnonyId->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leDomain->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leUserName->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->lePassword->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leCaPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + }else{ + ui->btnConnect->setEnabled(true); + } +} + +void DlgConnHidWifiSecTunnelTLS::on_leUserName_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leAnonyId->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leDomain->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leUserName->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->lePassword->text() == ""){ + ui->btnConnect->setEnabled(false); + }else{ + if (ui->cbxCA->currentIndex() == 0){ + ui->btnConnect->setEnabled(true); + }else if(ui->leCaPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + }else{ + ui->btnConnect->setEnabled(true); + } + } +} + +void DlgConnHidWifiSecTunnelTLS::on_lePwd_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leAnonyId->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leDomain->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->leUserName->text() == ""){ + ui->btnConnect->setEnabled(false); + }else if (ui->lePassword->text() == ""){ + ui->btnConnect->setEnabled(false); + }else{ + if (ui->cbxCA->currentIndex() == 0){ + ui->btnConnect->setEnabled(true); + }else if(ui->leCaPwd->text() == ""){ + ui->btnConnect->setEnabled(false); + }else{ + ui->btnConnect->setEnabled(true); + } + } +} + +void DlgConnHidWifiSecTunnelTLS::paintEvent(QPaintEvent *event) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); + QWidget::paintEvent(event); +} diff --git a/KylinNM/wireless-security/dlgconnhidwifisectunneltls.h b/KylinNM/wireless-security/dlgconnhidwifisectunneltls.h new file mode 100644 index 0000000..bbd881a --- /dev/null +++ b/KylinNM/wireless-security/dlgconnhidwifisectunneltls.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include + +namespace Ui { +class DlgConnHidWifiSecTunnelTLS; +} + +class DlgConnHidWifiSecTunnelTLS : public QDialog +{ + Q_OBJECT + +public: + explicit DlgConnHidWifiSecTunnelTLS(int type, QWidget *parent = 0); + ~DlgConnHidWifiSecTunnelTLS(); + +protected: + void paintEvent(QPaintEvent *event); + +public slots: + void changeDialogSecu(); + void changeDialogAuth(); + +private slots: + void on_btnCancel_clicked(); + + void on_btnConnect_clicked(); + + void on_cbxCA_currentIndexChanged(const QString &arg1); + + void on_checkBoxPwd_stateChanged(int arg1); + + void on_checkBoxCA_stateChanged(int arg1); + + void on_checkBoxPwdSec_stateChanged(int arg1); + + void on_leNetName_textEdited(const QString &arg1); + + void on_leAnonyId_textEdited(const QString &arg1); + + void on_leDomain_textEdited(const QString &arg1); + + void on_leCaPwd_textEdited(const QString &arg1); + + void on_leUserName_textEdited(const QString &arg1); + + void on_lePwd_textEdited(const QString &arg1); + +private: + Ui::DlgConnHidWifiSecTunnelTLS *ui; + int WepOrWpa = 0;//0 WEP;1WPA + + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + +// QString labelQss, cbxQss, leQss, btnConnQss, btnCancelQss, lineQss, checkBoxQss, checkBoxCAQss; + + bool isPress; + QPoint winPos; + QPoint dragPos; +}; + +#endif // DLGCONNHIDWIFISECTUNNELTLS_H diff --git a/KylinNM/wireless-security/dlgconnhidwifisectunneltls.ui b/KylinNM/wireless-security/dlgconnhidwifisectunneltls.ui new file mode 100644 index 0000000..c625c2f --- /dev/null +++ b/KylinNM/wireless-security/dlgconnhidwifisectunneltls.ui @@ -0,0 +1,579 @@ + + + DlgConnHidWifiSecTunnelTLS + + + + 0 + 0 + 432 + 665 + + + + Connect to Hidden Wi-Fi Network + + + + + 180 + 75 + 200 + 32 + + + + + 10 + + + + + + + + + + 180 + 385 + 200 + 32 + + + + + 10 + + + + QLineEdit::Password + + + + + + 180 + 145 + 200 + 32 + + + + + 10 + + + + + + + 315 + 620 + 90 + 30 + + + + + 10 + + + + + + + + + + 180 + 545 + 200 + 32 + + + + + 10 + + + + QLineEdit::Password + + + + + + 215 + 620 + 90 + 30 + + + + + 10 + + + + + + + + + + 76 + 310 + 90 + 20 + + + + + 10 + + + + + + + + + + 357 + 557 + 18 + 9 + + + + + 10 + + + + + + + + + + 76 + 510 + 90 + 20 + + + + + 10 + + + + + + + + + + 76 + 190 + 90 + 20 + + + + + 10 + + + + + + + + + + 180 + 185 + 200 + 32 + + + + + 10 + + + + + + + + + + 180 + 345 + 200 + 32 + + + + + 10 + + + + + + + + + + 180 + 465 + 200 + 32 + + + + + 10 + + + + + + + + + + 180 + 505 + 200 + 32 + + + + + 10 + + + + QLineEdit::Normal + + + + + + 76 + 350 + 90 + 20 + + + + + 10 + + + + + + + + + + 180 + 225 + 200 + 32 + + + + + 10 + + + + + + + + + + 76 + 270 + 90 + 20 + + + + + 10 + + + + + + + + + + 76 + 150 + 90 + 20 + + + + + 10 + + + + + + + + + + 180 + 425 + 200 + 25 + + + + + 10 + + + + + + + + + + 76 + 470 + 90 + 20 + + + + + 10 + + + + + + + + + + 180 + 305 + 200 + 32 + + + + + 10 + + + + + + + 76 + 230 + 90 + 20 + + + + + 10 + + + + + + + + + + 76 + 80 + 90 + 20 + + + + + 10 + + + + + + + + + + 76 + 390 + 90 + 20 + + + + + 10 + + + + + + + + + + 76 + 550 + 90 + 20 + + + + + 10 + + + + + + + + + + 357 + 397 + 18 + 9 + + + + + 10 + + + + + + + + + + 180 + 265 + 200 + 32 + + + + + 10 + + + + + + + 0 + 0 + 432 + 665 + + + + + + + + + + 30 + 30 + 140 + 22 + + + + + + + + + + 10 + 125 + 412 + 1 + + + + Qt::Horizontal + + + + + + 10 + 600 + 412 + 1 + + + + Qt::Horizontal + + + lbBoder + cbxConn + leCaPwd + leNetName + btnConnect + lePassword + btnCancel + lbDomain + checkBoxPwdSec + lbUserName + lbSecurity + cbxSecurity + cbxCA + cbxInnerAuth + leUserName + lbCA + cbxAuth + lbAnonyId + lbNetName + checkBoxCA + lbInnerAuth + leDomain + lbAuth + lbConn + lbCaPwd + lbPassword + checkBoxPwd + leAnonyId + lbLeftupTitle + lineUp + lineDown + + + + diff --git a/KylinNM/wireless-security/dlgconnhidwifiwep.cpp b/KylinNM/wireless-security/dlgconnhidwifiwep.cpp new file mode 100644 index 0000000..72fa078 --- /dev/null +++ b/KylinNM/wireless-security/dlgconnhidwifiwep.cpp @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see + +DlgConnHidWifiWep::DlgConnHidWifiWep(int type, QWidget *parent) : + WepPwdOrCode(type), + QDialog(parent), + ui(new Ui::DlgConnHidWifiWep) +{ + ui->setupUi(this); + + this->setWindowFlags(Qt::FramelessWindowHint); + this->setAttribute(Qt::WA_TranslucentBackground); + //需要添加 void paintEvent(QPaintEvent *event) 函数 + + QPainterPath path; + auto rect = this->rect(); + rect.adjust(1, 1, -1, -1); + path.addRoundedRect(rect, 6, 6); + setProperty("blurRegion", QRegion(path.toFillPolygon().toPolygon())); + + this->setStyleSheet("QWidget{border-radius:6px;background-color:rgba(19,19,20,0.7);border:1px solid rgba(255, 255, 255, 0.05);}"); + + MyQss objQss; + + ui->lbBoder->setStyleSheet("QLabel{border-radius:6px;background-color:rgba(19,19,20,0.95);border:1px solid rgba(255, 255, 255, 0.05);}"); + ui->lbBoder->hide(); + ui->lbLeftupTitle->setStyleSheet("QLabel{border:0px;font-size:20px;color:rgba(255,255,255,0.97);background-color:transparent;}"); + ui->lbConn->setStyleSheet(objQss.labelQss); + ui->lbNetName->setStyleSheet(objQss.labelQss); + ui->lbSecurity->setStyleSheet(objQss.labelQss); + ui->lbKey->setStyleSheet(objQss.labelQss); + ui->lbWEPindex->setStyleSheet(objQss.labelQss); + ui->lbAuth->setStyleSheet(objQss.labelQss); + + ui->cbxConn->setStyleSheet(objQss.cbxQss); + ui->cbxConn->setView(new QListView()); + ui->leNetName->setStyleSheet(objQss.leQss); + ui->leKey->setStyleSheet(objQss.leQss); + ui->checkBoxPwd->setStyleSheet(objQss.checkBoxQss); + ui->cbxSecurity->setStyleSheet(objQss.cbxQss); + ui->cbxSecurity->setView(new QListView()); + ui->cbxWEPindex->setStyleSheet(objQss.cbxQss); + ui->cbxWEPindex->setView(new QListView()); + ui->cbxAuth->setStyleSheet(objQss.cbxQss); + ui->cbxAuth->setView(new QListView()); + + ui->btnCancel->setStyleSheet(objQss.btnCancelQss); + ui->btnConnect->setStyleSheet(objQss.btnConnQss); + ui->lineUp->setStyleSheet(objQss.lineQss); + ui->lineDown->setStyleSheet(objQss.lineQss); + + ui->lbLeftupTitle->setText(tr("Add hidden Wi-Fi")); //加入隐藏Wi-Fi + ui->lbConn->setText(tr("Connection")); //连接设置: + ui->lbNetName->setText(tr("Network name")); //网络名称: + ui->lbSecurity->setText(tr("Wi-Fi security")); //Wi-Fi 安全性: + ui->lbKey->setText(tr("Key")); //密钥: + ui->lbWEPindex->setText(tr("WEP index")); //WEP 检索: + ui->lbAuth->setText(tr("Authentication")); //认证: + ui->btnCancel->setText(tr("Cancel")); //取消 + ui->btnConnect->setText(tr("Connect")); //连接 + + ui->cbxConn->addItem(tr("C_reate…")); //新建... + int status = system("nmcli connection show>/tmp/kylin-nm-connshow"); + if (status != 0){ syslog(LOG_ERR, "execute 'nmcli connection show' in function 'DlgConnHidWifiWep' failed");} + QFile file("/tmp/kylin-nm-connshow"); + if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){ + qDebug()<<"Can't open the file!"; + } + QString txt = file.readAll(); + QStringList txtLine = txt.split("\n"); + file.close(); + foreach (QString line, txtLine) { + if(line.indexOf("wifi") != -1){ + QStringList subLine = line.split(" "); + ui->cbxConn->addItem(subLine[0]); + } + } + ui->cbxConn->setCurrentIndex(0); + + ui->cbxSecurity->addItem(tr("None")); //无 + ui->cbxSecurity->addItem(tr("WPA & WPA2 Personal")); //WPA 及 WPA2 个人 + ui->cbxSecurity->addItem(tr("WEP 40/128-bit Key (Hex or ASCII)")); //WEP 40/128 位密钥(十六进制或ASCII) + ui->cbxSecurity->addItem(tr("WEP 128-bit Passphrase")); //WEP 128 位密码句 + ui->cbxSecurity->addItem("LEAP"); + ui->cbxSecurity->addItem(tr("Dynamic WEP (802.1X)")); //动态 WEP (802.1x) + ui->cbxSecurity->addItem(tr("WPA & WPA2 Enterprise")); //WPA 及 WPA2 企业 + if (WepPwdOrCode == 0) { + ui->cbxSecurity->setCurrentIndex(2); + } else if (WepPwdOrCode == 1) { + ui->cbxSecurity->setCurrentIndex(3); + } + connect(ui->cbxSecurity,SIGNAL(currentIndexChanged(QString)),this,SLOT(changeDialog())); + + ui->cbxWEPindex->addItem(tr("1(default)")); //1(默认) + ui->cbxWEPindex->addItem("2"); + ui->cbxWEPindex->addItem("3"); + ui->cbxWEPindex->addItem("4"); + ui->cbxWEPindex->setCurrentIndex(0); + + ui->cbxAuth->addItem(tr("Open System")); //开放式系统 + ui->cbxAuth->addItem(tr("Shared Key")); //共享密钥 + ui->cbxAuth->setCurrentIndex(0); + + ui->btnConnect->setEnabled(false); + + this->setFixedSize(432,493); + +} + +DlgConnHidWifiWep::~DlgConnHidWifiWep() +{ + delete ui; +} + +void DlgConnHidWifiWep::mousePressEvent(QMouseEvent *event){ + if(event->button() == Qt::LeftButton){ + this->isPress = true; + this->winPos = this->pos(); + this->dragPos = event->globalPos(); + event->accept(); + } +} +void DlgConnHidWifiWep::mouseReleaseEvent(QMouseEvent *event){ + this->isPress = false; +} +void DlgConnHidWifiWep::mouseMoveEvent(QMouseEvent *event){ + if(this->isPress){ + this->move(this->winPos - (this->dragPos - event->globalPos())); + event->accept(); + } +} + +//切换到其他Wi-Fi安全类型 +void DlgConnHidWifiWep::changeDialog() +{ + if(ui->cbxSecurity->currentIndex()==0){ +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifi *connHidWifi = new DlgConnHidWifi(0, 0, this->parentWidget()); + connHidWifi->show(); + } else if(ui->cbxSecurity->currentIndex()==1) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiWpa *connHidWifiWpa = new DlgConnHidWifiWpa(0, 0, this->parentWidget()); + connHidWifiWpa->show(); + } else if(ui->cbxSecurity->currentIndex()==2) { + if (WepPwdOrCode == 1) { + ui->cbxSecurity->setCurrentIndex(2); + WepPwdOrCode = 0; + } + } else if(ui->cbxSecurity->currentIndex()==3) { + if (WepPwdOrCode == 0) { + ui->cbxSecurity->setCurrentIndex(3); + WepPwdOrCode = 1; + } + } else if(ui->cbxSecurity->currentIndex()==4) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiLeap *connHidWifiLeap = new DlgConnHidWifiLeap(this->parentWidget()); + connHidWifiLeap->show(); + } else if(ui->cbxSecurity->currentIndex()==5) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecTls *connHidWifiSecTls = new DlgConnHidWifiSecTls(0, this->parentWidget()); + connHidWifiSecTls->show(); + } else { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecTls *connHidWifiSecTls = new DlgConnHidWifiSecTls(1, this->parentWidget()); + connHidWifiSecTls->show(); + } +} + +void DlgConnHidWifiWep::on_btnCancel_clicked() +{ + this->close(); +} + +void DlgConnHidWifiWep::on_btnConnect_clicked() +{ + this->close(); +} + +void DlgConnHidWifiWep::on_checkBoxPwd_stateChanged(int arg1) +{ + if (arg1 == 0) { + ui->leKey->setEchoMode(QLineEdit::Password); + } else { + ui->leKey->setEchoMode(QLineEdit::Normal); + } +} + +void DlgConnHidWifiWep::on_leKey_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == "" || ui->leKey->text() == ""){ + ui->btnConnect->setEnabled(false); + } else { + ui->btnConnect->setEnabled(true); + } +} + +void DlgConnHidWifiWep::on_leNetName_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == "" || ui->leKey->text() == ""){ + ui->btnConnect->setEnabled(false); + } else { + ui->btnConnect->setEnabled(true); + } +} + +void DlgConnHidWifiWep::paintEvent(QPaintEvent *event) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); + QWidget::paintEvent(event); +} diff --git a/KylinNM/wireless-security/dlgconnhidwifiwep.h b/KylinNM/wireless-security/dlgconnhidwifiwep.h new file mode 100644 index 0000000..bd071ac --- /dev/null +++ b/KylinNM/wireless-security/dlgconnhidwifiwep.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include + +namespace Ui { +class DlgConnHidWifiWep; +} + +class DlgConnHidWifiWep : public QDialog +{ + Q_OBJECT + +public: + explicit DlgConnHidWifiWep(int type, QWidget *parent = 0); + ~DlgConnHidWifiWep(); + +protected: + void paintEvent(QPaintEvent *event); + +public slots: + void changeDialog(); + +private slots: + void on_btnCancel_clicked(); + + void on_btnConnect_clicked(); + + void on_checkBoxPwd_stateChanged(int arg1); + + void on_leKey_textEdited(const QString &arg1); + + void on_leNetName_textEdited(const QString &arg1); + +private: + Ui::DlgConnHidWifiWep *ui; + int WepPwdOrCode = 0; //0 WEP password;1 WEP Code Sentence + + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + +// QString labelQss, cbxQss, leQss, btnConnQss, btnCancelQss, lineQss, checkBoxQss; + + bool isPress; + QPoint winPos; + QPoint dragPos; +}; + +#endif // DLGCONNHIDWIFIWEP_H diff --git a/KylinNM/wireless-security/dlgconnhidwifiwep.ui b/KylinNM/wireless-security/dlgconnhidwifiwep.ui new file mode 100644 index 0000000..baac5c8 --- /dev/null +++ b/KylinNM/wireless-security/dlgconnhidwifiwep.ui @@ -0,0 +1,360 @@ + + + DlgConnHidWifiWep + + + + 0 + 0 + 432 + 493 + + + + Connect to Hidden Wi-Fi Network + + + + + 76 + 266 + 90 + 20 + + + + + 10 + + + + + + + + + + 76 + 311 + 90 + 20 + + + + + 10 + + + + + + + + + + 76 + 356 + 90 + 20 + + + + + 10 + + + + + + + + + + 175 + 215 + 182 + 32 + + + + + 10 + + + + + + + + + + 315 + 440 + 90 + 30 + + + + + 10 + + + + + + + + + + 175 + 350 + 182 + 32 + + + + + 10 + + + + + + + + + + 76 + 80 + 90 + 20 + + + + + 10 + + + + Qt::LeftToRight + + + + + + + + + 175 + 305 + 182 + 32 + + + + + 10 + + + + + + + + + + 215 + 440 + 90 + 30 + + + + + 10 + + + + + + + + + + 332 + 272 + 18 + 9 + + + + + 10 + + + + + + + + + + 76 + 176 + 90 + 20 + + + + + 10 + + + + + + + + + + 175 + 260 + 182 + 32 + + + + + 10 + + + + QLineEdit::Password + + + + + + 175 + 170 + 182 + 32 + + + + + 10 + + + + + + + 76 + 221 + 90 + 20 + + + + + 10 + + + + + + + + + + 175 + 75 + 182 + 32 + + + + + 10 + + + + + + + + + + 0 + 0 + 432 + 493 + + + + + + + + + + 30 + 30 + 140 + 22 + + + + + + + + + + 10 + 140 + 412 + 1 + + + + Qt::Horizontal + + + + + + 10 + 410 + 412 + 1 + + + + Qt::Horizontal + + + lbBoder + leKey + lbKey + lbWEPindex + lbAuth + cbxSecurity + btnConnect + cbxAuth + lbConn + cbxWEPindex + btnCancel + checkBoxPwd + lbNetName + leNetName + lbSecurity + cbxConn + lbLeftupTitle + lineUp + lineDown + + + + diff --git a/KylinNM/wireless-security/dlgconnhidwifiwpa.cpp b/KylinNM/wireless-security/dlgconnhidwifiwpa.cpp new file mode 100644 index 0000000..c1a9fdc --- /dev/null +++ b/KylinNM/wireless-security/dlgconnhidwifiwpa.cpp @@ -0,0 +1,411 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include + +#include + +DlgConnHidWifiWpa::DlgConnHidWifiWpa(int type, KylinNM *mainWindow, QWidget *parent) : + isUsed(type), + QDialog(parent), + ui(new Ui::DlgConnHidWifiWpa) +{ + ui->setupUi(this); + + this->setWindowFlags(Qt::FramelessWindowHint); + this->setAttribute(Qt::WA_TranslucentBackground); + //需要添加 void paintEvent(QPaintEvent *event) 函数 + + QPainterPath path; + auto rect = this->rect(); + rect.adjust(1, 1, -1, -1); + path.addRoundedRect(rect, 6, 6); + setProperty("blurRegion", QRegion(path.toFillPolygon().toPolygon())); + + KylinDBus mkylindbus; + double trans = mkylindbus.getTransparentData(); + QString strTrans; + strTrans = QString::number(trans, 10, 2); + QString sty = "QWidget{border-radius:6px;background-color:rgba(255,255,255,1);border:1px solid rgba(255, 255, 255, 0.05);}"; + this->setStyleSheet(sty); + //this->setStyleSheet("QWidget{border-radius:6px;background-color:rgba(19,19,20,0.7);border:1px solid rgba(255, 255, 255, 0.05);}"); + + MyQss objQss; + + ui->lbBoder->setStyleSheet("QLabel{border-radius:6px;background-color:rgba(19,19,20,0.95);border:1px solid rgba(255, 255, 255, 0.05);}"); + ui->lbBoder->hide(); + ui->lbLeftupTitle->setStyleSheet("QLabel{border:0px;font-size:20px;color:rgba(255,255,255,0.97);background-color:transparent;}"); + ui->lbConn->setStyleSheet(objQss.labelQss); + ui->lbNetName->setStyleSheet(objQss.labelQss); + ui->lbSecurity->setStyleSheet(objQss.labelQss); + ui->lbPassword->setStyleSheet(objQss.labelQss); + + ui->cbxConn->setStyleSheet(objQss.cbxQss); + ui->cbxConn->setView(new QListView()); + ui->leNetName->setStyleSheet(objQss.leQss); + ui->lePassword->setStyleSheet(objQss.leQss); + ui->cbxSecurity->setStyleSheet(objQss.cbxQss); + ui->cbxSecurity->setView(new QListView()); + ui->checkBoxPwd->setStyleSheet(objQss.checkBoxQss); + + ui->btnCancel->setStyleSheet(objQss.btnCancelQss); + ui->btnConnect->setStyleSheet(objQss.btnConnQss); + ui->lineUp->setStyleSheet(objQss.lineQss); + ui->lineDown->setStyleSheet(objQss.lineQss); + ui->btnCancel->setFocusPolicy(Qt::NoFocus); + ui->checkBoxPwd->setFocusPolicy(Qt::NoFocus); + + + ui->lbLeftupTitle->setText(tr("Add Hidden Wi-Fi")); //加入隐藏Wi-Fi + ui->lbConn->setText(tr("Connection")); //连接设置: + ui->lbNetName->setText(tr("Wi-Fi name")); //网络名称: + ui->lbSecurity->setText(tr("Wi-Fi security")); //Wi-Fi 安全性: + ui->lbPassword->setText(tr("Password")); //密码: + ui->btnCancel->setText(tr("Cancel")); //取消 + ui->btnConnect->setText(tr("Connect")); //连接 + + ui->cbxConn->addItem(tr("C_reate…")); //新建... + QString tmpPath = "/tmp/kylin-nm-connshow-" + QDir::home().dirName(); + QString cmd = "nmcli connection show > " + tmpPath; + int status = system(cmd.toUtf8().data()); + if (status != 0) { + syslog(LOG_ERR, "execute 'nmcli connection show' in function 'DlgConnHidWifiWpa' failed"); + } + QFile file(tmpPath); + if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){ + qDebug()<<"Can't open the file!"; + } + QString txt = file.readAll(); + QStringList txtLine = txt.split("\n"); + file.close(); + foreach (QString line, txtLine) { + if (line.indexOf("wifi") != -1 || line.indexOf("802-11-wireless") != -1) { + QStringList subLine = line.split(" "); + ui->cbxConn->addItem(subLine[0]); + } + } + ui->cbxConn->setCurrentIndex(0); + connect(ui->cbxConn,SIGNAL(currentIndexChanged(QString)),this,SLOT(changeWindow())); + + ui->cbxSecurity->addItem(tr("None")); //无 + ui->cbxSecurity->addItem(tr("WPA & WPA2 Personal")); //WPA 及 WPA2 个人 + //ui->cbxSecurity->addItem(tr("WEP 40/128-bit Key (Hex or ASCII)")); //WEP 40/128 位密钥(十六进制或ASCII) + //ui->cbxSecurity->addItem(tr("WEP 128-bit Passphrase")); //WEP 128 位密码句 + //ui->cbxSecurity->addItem("LEAP"); + //ui->cbxSecurity->addItem(tr("Dynamic WEP (802.1X)")); //动态 WEP (802.1x) + //ui->cbxSecurity->addItem(tr("WPA & WPA2 Enterprise")); //WPA 及 WPA2 企业 + ui->cbxSecurity->setCurrentIndex(1); + connect(ui->cbxSecurity,SIGNAL(currentIndexChanged(QString)),this,SLOT(changeDialog())); + + if (isUsed == 0){ + ui->btnConnect->setEnabled(false); + } else { + ui->cbxConn->setCurrentIndex(isUsed); + ui->leNetName->setText(ui->cbxConn->currentText()); + ui->leNetName->setEnabled(false); + ui->lbNetName->setEnabled(false); + ui->lbSecurity->setEnabled(false); + ui->cbxSecurity->setEnabled(false); + ui->lbPassword->setEnabled(false); + ui->lePassword->setText(""); + ui->lePassword->setEnabled(false); + ui->btnConnect->setEnabled(true); + ui->checkBoxPwd->setEnabled(false); + } + + this->setFixedSize(432,397); + + this->mw = mainWindow; + + /**解决锁屏设置 X11BypassWindowManagerHint 属性导致QCombox弹框异常的问题---START---- + ** 手动绑定下拉框视图和下拉框 + **/ + ui->cbxConn->view()->setWindowFlags(Qt::Popup | Qt::X11BypassWindowManagerHint); + ui->cbxConn->view()->setParent(this); + ui->cbxConn->view()->hide(); + ui->cbxConn->installEventFilter(this); + + ui->cbxSecurity->view()->setWindowFlags(Qt::Popup | Qt::X11BypassWindowManagerHint); + ui->cbxSecurity->view()->setParent(this); + ui->cbxSecurity->view()->hide(); + ui->cbxSecurity->installEventFilter(this); + + connect(ui->cbxConn->view(), &QAbstractItemView::pressed, this, [=](QModelIndex index){ + Q_EMIT ui->cbxConn->setCurrentIndex(index.row()); + ui->cbxConn->view()->hide(); + }); + + connect(ui->cbxSecurity->view(), &QAbstractItemView::pressed, this, [=](QModelIndex index){ + ui->cbxSecurity->view()->hide(); + Q_EMIT ui->cbxSecurity->setCurrentIndex(index.row()); + }); + + ui->cbxConn->view()->setGeometry(QRect(ui->cbxConn->geometry().left(), ui->cbxConn->geometry().bottom(), ui->cbxConn->view()->width(), ui->cbxConn->view()->height())); + ui->cbxSecurity->view()->setGeometry(QRect(ui->cbxSecurity->geometry().left(), ui->cbxSecurity->geometry().bottom(), ui->cbxSecurity->view()->width(), ui->cbxSecurity->view()->height())); + /**解决锁屏设置 X11BypassWindowManagerHint 属性导致QCombox弹框异常的问题---END---- + ** 手动绑定下拉框视图和下拉框 + **/ +} + +DlgConnHidWifiWpa::~DlgConnHidWifiWpa() +{ + delete ui; +} + +/**解决锁屏设置 X11BypassWindowManagerHint 属性导致QCombox弹框异常的问题---START---- +** 手动绑定下拉框视图和下拉框 +**/ +bool DlgConnHidWifiWpa::eventFilter(QObject *obj, QEvent *ev) +{ + if(ev->type() == QEvent::MouseButtonPress) + { + if(obj == ui->cbxConn) + { + ui->cbxConn->view()->setVisible(!ui->cbxConn->view()->isVisible()); + if(ui->cbxConn->view()->isVisible()) + ui->cbxConn->view()->setFocus(); + } else if (obj == ui->cbxSecurity) + { + ui->cbxSecurity->view()->setVisible(!ui->cbxSecurity->view()->isVisible()); + if(ui->cbxSecurity->view()->isVisible()) + ui->cbxSecurity->view()->setFocus(); + } + } + return false; +} +/**解决锁屏设置 X11BypassWindowManagerHint 属性导致QCombox弹框异常的问题---END---- +** 手动绑定下拉框视图和下拉框 +**/ + +void DlgConnHidWifiWpa::mousePressEvent(QMouseEvent *event){ + if(event->button() == Qt::LeftButton){ + this->isPress = true; + this->winPos = this->pos(); + this->dragPos = event->globalPos(); + event->accept(); + } +} +void DlgConnHidWifiWpa::mouseReleaseEvent(QMouseEvent *event){ + this->isPress = false; +} +void DlgConnHidWifiWpa::mouseMoveEvent(QMouseEvent *event){ + if(this->isPress){ + this->move(this->winPos - (this->dragPos - event->globalPos())); + event->accept(); + } +} + +//切换到其他Wi-Fi安全类型 +void DlgConnHidWifiWpa::changeDialog() +{ + if(ui->cbxSecurity->currentIndex()==0){ +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifi *connHidWifi = new DlgConnHidWifi(0,mw, this->parentWidget()); + connHidWifi->show(); + connect(connHidWifi, SIGNAL(reSetWifiList() ), mw, SLOT(on_btnWifiList_clicked()) ); + } else if(ui->cbxSecurity->currentIndex()==1) { + qDebug()<<"it's not need to change dialog"; + } else if(ui->cbxSecurity->currentIndex()==2) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiWep *connHidWifiWep = new DlgConnHidWifiWep(0, this->parentWidget()); + connHidWifiWep->show(); + } else if(ui->cbxSecurity->currentIndex()==3) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiWep *connHidWifiWep = new DlgConnHidWifiWep(1,this->parentWidget()); + connHidWifiWep->show(); + } else if(ui->cbxSecurity->currentIndex()==4) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiLeap *connHidWifiLeap = new DlgConnHidWifiLeap(this->parentWidget()); + connHidWifiLeap->show(); + } else if(ui->cbxSecurity->currentIndex()==5) { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecTls *connHidWifiSecTls = new DlgConnHidWifiSecTls(0, this->parentWidget()); + connHidWifiSecTls->show(); + } else { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifiSecTls *connHidWifiSecTls = new DlgConnHidWifiSecTls(1, this->parentWidget()); + connHidWifiSecTls->show(); + } +} + +//同一 Wi-Fi安全类型的窗口变换 +void DlgConnHidWifiWpa::changeWindow() +{ + if (ui->cbxConn->currentIndex() == 0){ +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifi *connHidWifi = new DlgConnHidWifi(0, mw, this->parentWidget()); + connHidWifi->show(); + connect(connHidWifi, SIGNAL(reSetWifiList() ), mw, SLOT(on_btnWifiList_clicked()) ); + }else if (ui->cbxConn->currentIndex() >= 1){ + QString tmpPath = "/tmp/kylin-nm-connshow-" + QDir::home().dirName(); + QString name = ui->cbxConn->currentText(); + QString currStr = "nmcli connection show \"" + name.replace("\"","\\\"") + "\" > " + tmpPath; + + int status = system(currStr.toUtf8().data()); + if(status != 0){ + syslog(LOG_ERR, "execute 'nmcli connection show' in function 'changeWindow' failed"); + } + QFile file(tmpPath); + if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){ + qDebug()<<"Can't open the file!"; + } + QString txt = file.readAll(); + file.close(); + if (txt.indexOf("802-11-wireless-security.key-mgmt:") != -1){ + isUsed = ui->cbxConn->currentIndex(); + ui->lbNetName->setEnabled(false); + ui->leNetName->setText(ui->cbxConn->currentText()); + ui->leNetName->setEnabled(false); + ui->lbSecurity->setEnabled(false); + ui->cbxSecurity->setEnabled(false); + ui->lePassword->setText(""); + ui->lbPassword->setEnabled(false); + ui->lePassword->setEnabled(false); + ui->btnConnect->setEnabled(true); + }else { +// QApplication::setQuitOnLastWindowClosed(false); + this->hide(); + DlgConnHidWifi *connHidWifi = new DlgConnHidWifi(ui->cbxConn->currentIndex(), mw); + connHidWifi->show(); + connect(connHidWifi, SIGNAL(reSetWifiList() ), mw, SLOT(on_btnWifiList_clicked()) ); + } + } +} + +void DlgConnHidWifiWpa::on_btnCancel_clicked() +{ + this->close(); +} + +void DlgConnHidWifiWpa::on_btnConnect_clicked() +{ + QThread *t = new QThread(); + connect(t, SIGNAL(finished()), t, SLOT(deleteLater())); + connect(t, SIGNAL(started()), this, SLOT(slotStartLoading())); + connect(this, SIGNAL(stopSignal()), t, SLOT(quit())); + t->start(); + + QString wifiName = ui->leNetName->text(); + QString wifiPassword = ui->lePassword->text(); + BackThread *bt = new BackThread(); + strWifiname = wifiName; + strWifiPassword = wifiPassword; + if (isUsed == 0){ + int x = 0; + do{ + sleep(1); + QString tmpPath = "/tmp/kylin-nm-btoutput-" + QDir::home().dirName(); + QString cmd = "nmcli device wifi connect \"" + wifiName.replace("\"","\\\"") + "\" password \"" + wifiPassword.replace("\"","\\\"") + "\" hidden yes > " + tmpPath; + + int status = system(cmd.toUtf8().data()); + if (status != 0) { + syslog(LOG_ERR, "execute 'nmcli device wifi connect' in function 'on_btnConnect_clicked' failed"); + } + + QFile file(tmpPath); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + qDebug()<<"Can't open the file!"<execConnWifi(wifiName); + connect(this, SIGNAL(sendMessage()), this,SLOT(emitSignal() )); + QTimer::singleShot(4*1000, this, SLOT(emitSignal() )); + } + this->close(); +} + +void DlgConnHidWifiWpa::on_checkBoxPwd_stateChanged(int arg1) +{ + if (arg1 == 0) { + ui->lePassword ->setEchoMode(QLineEdit::Password); + } else { + ui->lePassword->setEchoMode(QLineEdit::Normal); + } +} + +void DlgConnHidWifiWpa::on_leNetName_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == "" || ui->lePassword->text() == ""){ + ui->btnConnect->setEnabled(false); + } else { + ui->btnConnect->setEnabled(true); + } +} + +void DlgConnHidWifiWpa::on_lePassword_textEdited(const QString &arg1) +{ + if (ui->leNetName->text() == "" || ui->lePassword->text() == ""){ + ui->btnConnect->setEnabled(false); + } else { + ui->btnConnect->setEnabled(true); + } +} + +void DlgConnHidWifiWpa::slotStartLoading() +{ + mw->startLoading(); +} + +void DlgConnHidWifiWpa::on_execSecConn() +{ + QString name = strWifiname; + QString pwd = strWifiPassword; + QString str = "nmcli device wifi connect \"" + name.replace("\"","\\\"") + "\" password\"" + pwd.replace("\"","\\\"") + "\""; + int status = system(str.toUtf8().data()); + if (status != 0){ syslog(LOG_ERR, "execute 'nmcli device wifi connect' in function 'on_execSecConn' failed");} + connect(this, SIGNAL(sendMessage()), this,SLOT(emitSignal() )); + QTimer::singleShot(3*1000, this, SLOT(emitSignal() )); +} + +void DlgConnHidWifiWpa::emitSignal() +{ + emit reSetWifiList(); + mw->stopLoading(); + emit this->stopSignal(); +} + +void DlgConnHidWifiWpa::paintEvent(QPaintEvent *event) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); + QWidget::paintEvent(event); +} diff --git a/KylinNM/wireless-security/dlgconnhidwifiwpa.h b/KylinNM/wireless-security/dlgconnhidwifiwpa.h new file mode 100644 index 0000000..3b73126 --- /dev/null +++ b/KylinNM/wireless-security/dlgconnhidwifiwpa.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include +#include + +class KylinNM; + +namespace Ui { +class DlgConnHidWifiWpa; +} + +class DlgConnHidWifiWpa : public QDialog +{ + Q_OBJECT + +public: + explicit DlgConnHidWifiWpa(int type, KylinNM *mw = 0, QWidget *parent = 0); + ~DlgConnHidWifiWpa(); + +protected: + void paintEvent(QPaintEvent *event); + + /**解决锁屏设置 X11BypassWindowManagerHint 属性导致QCombox弹框异常的问题---START---- + ** 手动绑定下拉框视图和下拉框 过滤点击事件 + **/ + bool eventFilter(QObject *obj, QEvent *ev) override; + +public slots: + void changeDialog(); + void changeWindow(); + void emitSignal(); + void on_execSecConn(); + void slotStartLoading(); + +private slots: + void on_btnCancel_clicked(); + + void on_btnConnect_clicked(); + + void on_checkBoxPwd_stateChanged(int arg1); + + void on_leNetName_textEdited(const QString &arg1); + + void on_lePassword_textEdited(const QString &arg1); + +signals: + void reSetWifiList(); + void sendMessage(); + void execSecConn(); + void stopSignal(); + +private: + Ui::DlgConnHidWifiWpa *ui; + int isUsed;//=0 current wifi not used before; >=1 used + KylinNM *mw; + QString strWifiname; + QString strWifiPassword; + + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + +// QString labelQss, cbxQss, leQss, btnConnQss, btnCancelQss, lineQss, checkBoxQss; + + bool isPress; + QPoint winPos; + QPoint dragPos; +}; + +#endif // DLGCONNHIDWIFIWPA_H diff --git a/KylinNM/wireless-security/dlgconnhidwifiwpa.ui b/KylinNM/wireless-security/dlgconnhidwifiwpa.ui new file mode 100644 index 0000000..093ab22 --- /dev/null +++ b/KylinNM/wireless-security/dlgconnhidwifiwpa.ui @@ -0,0 +1,284 @@ + + + DlgConnHidWifiWpa + + + + 0 + 0 + 432 + 397 + + + + Connect to Hidden Wi-Fi Network + + + + + 76 + 221 + 90 + 20 + + + + + 10 + + + + + + + + + + 76 + 176 + 90 + 20 + + + + + 10 + + + + + + + + + + 215 + 350 + 90 + 30 + + + + + 10 + + + + + + + + + + 175 + 75 + 182 + 32 + + + + + 10 + + + + + + + + + + 332 + 272 + 18 + 9 + + + + + 10 + + + + + + + + + + 76 + 266 + 90 + 20 + + + + + 10 + + + + + + + + + + 175 + 170 + 182 + 32 + + + + + 10 + + + + + + + + + + 175 + 261 + 182 + 32 + + + + + 10 + + + + QLineEdit::Password + + + + + + 315 + 350 + 90 + 30 + + + + + 10 + + + + + + + + + + 76 + 80 + 90 + 20 + + + + + 10 + + + + Qt::LeftToRight + + + + + + + + + 175 + 215 + 182 + 32 + + + + + 10 + + + + + + + 0 + 0 + 432 + 397 + + + + + + + + + + 30 + 30 + 240 + 22 + + + + + + + + + + 10 + 140 + 412 + 1 + + + + Qt::Horizontal + + + + + + 10 + 320 + 412 + 1 + + + + Qt::Horizontal + + + lbBoder + lePassword + lbNetName + lbSecurity + btnCancel + cbxConn + checkBoxPwd + lbPassword + cbxSecurity + btnConnect + lbConn + leNetName + lbLeftupTitle + lineUp + lineDown + + + + diff --git a/KylinNM/wireless-security/kylinheadfile.cpp b/KylinNM/wireless-security/kylinheadfile.cpp new file mode 100644 index 0000000..e8b92dc --- /dev/null +++ b/KylinNM/wireless-security/kylinheadfile.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see +#include +#include +#include +#include +#include + +class MyQss +{ +public: + MyQss(); + + QString labelQss, cbxQss, leQss, btnConnQss, btnCancelQss, lineQss, checkBoxQss, checkBoxCAQss; + +}; + +#endif // KYLINHEADFILE_H diff --git a/README.md b/README.md new file mode 100644 index 0000000..8f8048e --- /dev/null +++ b/README.md @@ -0,0 +1,66 @@ +## ukui-screensaver + +![build](https://github.com/ukui/ukui-screensaver/tree/master) + +ukui-screensaver是UKUI桌面环境的锁屏及屏保 + +### 依赖 + +------ + +### 编译依赖 + +- KF5 + - libkf5windowsystem-dev + +- cmake (>=2.6) +- qtbase5-dev +- libqt5x11extras5-dev +- libpam0g-dev +- qttools5-dev +- qttools5-dev-tools +- libglib2.0-dev +- libopencv-dev +- libx11-dev +- libxtst-dev +- libqt5svg5-dev +- libgsettings-qt-dev +- libmatemixer-dev +- libukui-log4qt-dev + +### 运行依赖 + +- ukui-session-manager +- ethtool +- mate-desktop-common + +### 编译 + +------ + +```shell +$ cd ukui-screensaver +$ mkdir build +$ cd build +$ cmake .. +$ make +``` + +### 安装 + +------ + +```shell +$ sudo make install +``` + +### 主体框架 + - **InProgress** + - [x] 界面绘制 + - [x] 功能实现 + - [x] 界面美化 + - **TROUBLE** + - 无 + - **TODO** + - 功能插件 + - 应用窗口置顶 diff --git a/VirtualKeyboard/CMakeLists.txt b/VirtualKeyboard/CMakeLists.txt new file mode 100644 index 0000000..f9d52c7 --- /dev/null +++ b/VirtualKeyboard/CMakeLists.txt @@ -0,0 +1,24 @@ +find_package(Qt5 COMPONENTS Core Widgets REQUIRED) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) + +qt5_add_resources(VirtualKeyboard_SRC + src/keyboard.qrc) + +set(VirtualKeyboard_SRC + ${VirtualKeyboard_SRC} + src/cursormonitor.cpp + src/keyboardwidget.cpp + src/virtualkeyboard.cpp + src/x11keyboard.cpp + src/keyboard.qrc) + +include_directories( + ${Qt5Core_INCLUDE_DIRS} + ${Qt5Widgets_INCLUDE_DIRS} + ) + +add_library(VirtualKeyboard STATIC ${VirtualKeyboard_SRC}) +target_link_libraries(VirtualKeyboard Qt5::Core Qt5::Widgets) diff --git a/VirtualKeyboard/README.md b/VirtualKeyboard/README.md new file mode 100644 index 0000000..c067665 --- /dev/null +++ b/VirtualKeyboard/README.md @@ -0,0 +1 @@ +A simple virtual keyboard in X11 diff --git a/VirtualKeyboard/VirtualKeyboard.pri b/VirtualKeyboard/VirtualKeyboard.pri new file mode 100644 index 0000000..e8029dc --- /dev/null +++ b/VirtualKeyboard/VirtualKeyboard.pri @@ -0,0 +1,19 @@ +SOURCES += \ + $$PWD/src/keyboardwidget.cpp \ + $$PWD/src/x11keyboard.cpp \ + $$PWD/src/cursormonitor.cpp \ + $$PWD/src/virtualkeyboard.cpp + + +HEADERS += \ + $$PWD/src/keyboardwidget.h \ + $$PWD/src/x11keyboard.h \ + $$PWD/src/cursormonitor.h \ + $$PWD/src/virtualkeyboard.h + +FORMS += \ + $$PWD/src/keyboardwidget.ui + + +RESOURCES += \ + $$PWD/src/keyboard.qrc diff --git a/VirtualKeyboard/VirtualKeyboard.pro b/VirtualKeyboard/VirtualKeyboard.pro new file mode 100644 index 0000000..0e979d8 --- /dev/null +++ b/VirtualKeyboard/VirtualKeyboard.pro @@ -0,0 +1,32 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2018-10-12T16:43:42 +# +#------------------------------------------------- + +QT += core gui + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = VirtualKeyboard +TEMPLATE = app + +# The following define makes your compiler emit warnings if you use +# any feature of Qt which has been marked as deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if you use deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +CONFIG += link_pkgconfig debug + +PKGCONFIG += xtst x11 + +include(VirtualKeyboard.pri) + +SOURCES += \ + $$PWD/src/main.cpp diff --git a/VirtualKeyboard/src/cursormonitor.cpp b/VirtualKeyboard/src/cursormonitor.cpp new file mode 100644 index 0000000..2c76123 --- /dev/null +++ b/VirtualKeyboard/src/cursormonitor.cpp @@ -0,0 +1,70 @@ +/** + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301, USA. +**/ +#include "cursormonitor.h" +#include +#include +#include +CursorMonitor::CursorMonitor(QObject *parent) : QThread(parent) +{ + +} + +void CursorMonitor::run() +{ + Display *display; + XEvent xevent; + Window window; + + display = XOpenDisplay(NULL); + if (display == 0) { + syslog(LOG_ERR, "CursorMonitor unable to open display"); + return; + } + + + window = DefaultRootWindow(display); + XAllowEvents(display, AsyncBoth, CurrentTime); + + XGrabPointer(display, + window, + 1, + PointerMotionMask | ButtonPressMask | ButtonReleaseMask , + GrabModeAsync, + GrabModeAsync, + None, + None, + CurrentTime); + + while(1) { + XNextEvent(display, &xevent); + + switch (xevent.type) { + case MotionNotify: + //printf("Mouse move : [%d, %d]\n", xevent.xmotion.x_root, xevent.xmotion.y_root); + Q_EMIT cursorPosChanged(QPoint(xevent.xmotion.x_root, xevent.xmotion.y_root)); + break; + case ButtonPress: +// printf("Button pressed : %s\n", key_name[xevent.xbutton.button - 1]); + break; + case ButtonRelease: +// printf("Button released : %s\n", key_name[xevent.xbutton.button - 1]); + break; + } + } +} diff --git a/VirtualKeyboard/src/cursormonitor.h b/VirtualKeyboard/src/cursormonitor.h new file mode 100644 index 0000000..7722453 --- /dev/null +++ b/VirtualKeyboard/src/cursormonitor.h @@ -0,0 +1,36 @@ +/** + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301, USA. +**/ +#ifndef CURSORMONITOR_H +#define CURSORMONITOR_H + +#include +#include + +class CursorMonitor : public QThread +{ + Q_OBJECT +public: + explicit CursorMonitor(QObject *parent = nullptr); + void run(); + +Q_SIGNALS: + void cursorPosChanged(const QPoint& pos); +}; + +#endif // CURSORMONITOR_H diff --git a/VirtualKeyboard/src/images/backspace.svg b/VirtualKeyboard/src/images/backspace.svg new file mode 100644 index 0000000..092d72d --- /dev/null +++ b/VirtualKeyboard/src/images/backspace.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/VirtualKeyboard/src/images/backspace_click.svg b/VirtualKeyboard/src/images/backspace_click.svg new file mode 100644 index 0000000..4a9d7fa --- /dev/null +++ b/VirtualKeyboard/src/images/backspace_click.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/VirtualKeyboard/src/images/capslock.svg b/VirtualKeyboard/src/images/capslock.svg new file mode 100644 index 0000000..a5a367c --- /dev/null +++ b/VirtualKeyboard/src/images/capslock.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/VirtualKeyboard/src/images/capslock_click.svg b/VirtualKeyboard/src/images/capslock_click.svg new file mode 100644 index 0000000..36ae8f8 --- /dev/null +++ b/VirtualKeyboard/src/images/capslock_click.svg @@ -0,0 +1,9 @@ + + + + + + + diff --git a/VirtualKeyboard/src/images/capslock_hl.svg b/VirtualKeyboard/src/images/capslock_hl.svg new file mode 100644 index 0000000..4f8870a --- /dev/null +++ b/VirtualKeyboard/src/images/capslock_hl.svg @@ -0,0 +1,7 @@ + + + + + diff --git a/VirtualKeyboard/src/images/capslock_hl_click.svg b/VirtualKeyboard/src/images/capslock_hl_click.svg new file mode 100644 index 0000000..65b0a21 --- /dev/null +++ b/VirtualKeyboard/src/images/capslock_hl_click.svg @@ -0,0 +1,7 @@ + + + + + diff --git a/VirtualKeyboard/src/images/close.svg b/VirtualKeyboard/src/images/close.svg new file mode 100644 index 0000000..c509415 --- /dev/null +++ b/VirtualKeyboard/src/images/close.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/VirtualKeyboard/src/images/close_click.svg b/VirtualKeyboard/src/images/close_click.svg new file mode 100644 index 0000000..d143065 --- /dev/null +++ b/VirtualKeyboard/src/images/close_click.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/VirtualKeyboard/src/images/down.svg b/VirtualKeyboard/src/images/down.svg new file mode 100644 index 0000000..73361dd --- /dev/null +++ b/VirtualKeyboard/src/images/down.svg @@ -0,0 +1,9 @@ + + + +画板 15 + + + + diff --git a/VirtualKeyboard/src/images/down_click.svg b/VirtualKeyboard/src/images/down_click.svg new file mode 100644 index 0000000..9140b38 --- /dev/null +++ b/VirtualKeyboard/src/images/down_click.svg @@ -0,0 +1,6 @@ + + + + + diff --git a/VirtualKeyboard/src/images/enter.svg b/VirtualKeyboard/src/images/enter.svg new file mode 100644 index 0000000..5cbc51d --- /dev/null +++ b/VirtualKeyboard/src/images/enter.svg @@ -0,0 +1,11 @@ + + + + + + + diff --git a/VirtualKeyboard/src/images/enter_click.svg b/VirtualKeyboard/src/images/enter_click.svg new file mode 100644 index 0000000..106cc7b --- /dev/null +++ b/VirtualKeyboard/src/images/enter_click.svg @@ -0,0 +1,9 @@ + + + + + + diff --git a/VirtualKeyboard/src/images/left.svg b/VirtualKeyboard/src/images/left.svg new file mode 100644 index 0000000..6ac0fa8 --- /dev/null +++ b/VirtualKeyboard/src/images/left.svg @@ -0,0 +1,7 @@ + + + +画板 19 + + diff --git a/VirtualKeyboard/src/images/left_click.svg b/VirtualKeyboard/src/images/left_click.svg new file mode 100644 index 0000000..edfd291 --- /dev/null +++ b/VirtualKeyboard/src/images/left_click.svg @@ -0,0 +1,7 @@ + + + +画板 20 + + diff --git a/VirtualKeyboard/src/images/right.svg b/VirtualKeyboard/src/images/right.svg new file mode 100644 index 0000000..62c301c --- /dev/null +++ b/VirtualKeyboard/src/images/right.svg @@ -0,0 +1,6 @@ + + + + + diff --git a/VirtualKeyboard/src/images/right_click.svg b/VirtualKeyboard/src/images/right_click.svg new file mode 100644 index 0000000..5ba0ec6 --- /dev/null +++ b/VirtualKeyboard/src/images/right_click.svg @@ -0,0 +1,6 @@ + + + + + diff --git a/VirtualKeyboard/src/images/super.svg b/VirtualKeyboard/src/images/super.svg new file mode 100644 index 0000000..c847e44 --- /dev/null +++ b/VirtualKeyboard/src/images/super.svg @@ -0,0 +1 @@ +画板 11 \ No newline at end of file diff --git a/VirtualKeyboard/src/images/super_click.svg b/VirtualKeyboard/src/images/super_click.svg new file mode 100644 index 0000000..7fcc495 --- /dev/null +++ b/VirtualKeyboard/src/images/super_click.svg @@ -0,0 +1 @@ +画板 12 \ No newline at end of file diff --git a/VirtualKeyboard/src/images/up.svg b/VirtualKeyboard/src/images/up.svg new file mode 100644 index 0000000..d5613b2 --- /dev/null +++ b/VirtualKeyboard/src/images/up.svg @@ -0,0 +1,9 @@ + + + +画板 13 + + + + diff --git a/VirtualKeyboard/src/images/up_click.svg b/VirtualKeyboard/src/images/up_click.svg new file mode 100644 index 0000000..225e401 --- /dev/null +++ b/VirtualKeyboard/src/images/up_click.svg @@ -0,0 +1,6 @@ + + + + + diff --git a/VirtualKeyboard/src/keyboard.qrc b/VirtualKeyboard/src/keyboard.qrc new file mode 100644 index 0000000..4e38067 --- /dev/null +++ b/VirtualKeyboard/src/keyboard.qrc @@ -0,0 +1,27 @@ + + + keyboard.qss + + + images/backspace_click.svg + images/backspace.svg + images/capslock_click.svg + images/capslock_hl.svg + images/capslock.svg + images/down_click.svg + images/down.svg + images/enter_click.svg + images/enter.svg + images/left_click.svg + images/left.svg + images/right_click.svg + images/right.svg + images/super_click.svg + images/super.svg + images/up.svg + images/close_click.svg + images/close.svg + images/capslock_hl_click.svg + images/up_click.svg + + diff --git a/VirtualKeyboard/src/keyboard.qss b/VirtualKeyboard/src/keyboard.qss new file mode 100644 index 0000000..e91e23c --- /dev/null +++ b/VirtualKeyboard/src/keyboard.qss @@ -0,0 +1,37 @@ +QPushButton +{ + border: none; + font: 24px; + color: white; + background: #35322f; + border-radius: 5px; +} + +QPushButton::pressed +{ + color: gray; + background: #2a2826; +} + +#btn_backspace, #btn_enter, #btn_shift_l, +#btn_shift_r, #btn_ctrl_l, #btn_ctrl_r, +#btn_alt_l, #btn_alt_r, #btn_super +{ + font: 16px; + background: #1e1b18 +} + +#btn_backspace::pressed, #btn_enter::pressed, #btn_shift_l::pressed, +#btn_shift_r::pressed, #btn_ctrl_l::pressed, #btn_ctrl_r::pressed, +#btn_alt_l::pressed, #btn_alt_r::pressed, #btn_super::pressed +{ + background: #181613; + color: gray; +} + +#btn_letter, #btn_symbol, #btn_number, +#btn_insert, #btn_delete, #btn_home, +#btn_end, #btn_pgup, #btn_pgdn, #btn_close +{ + font: 16px; +} diff --git a/VirtualKeyboard/src/keyboardwidget.cpp b/VirtualKeyboard/src/keyboardwidget.cpp new file mode 100644 index 0000000..e97a710 --- /dev/null +++ b/VirtualKeyboard/src/keyboardwidget.cpp @@ -0,0 +1,459 @@ +/** + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301, USA. +**/ +#include "keyboardwidget.h" +#include "ui_keyboardwidget.h" +#include +#include +#include + +#define SYMBOL_KEY_COUNT 29 +#define SYMBOL_PAGE_COUNT 2 + +#define BUTTON_BG "QPushButton{background:#1E1B18}" +#define BUTTON_BG_PRESSED "QPushButton{background: #181613;}" +#define BUTTON_BG_HL "QPushButton{background:#80c342}" +#define BUTTON_BG_HL_PRESSED "QPushButton{background:#486E25}" + + +QChar symbols[SYMBOL_PAGE_COUNT][SYMBOL_KEY_COUNT] = + { {'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', + 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', + 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/'}, + {'!', '@', '#', '$', '%', '^', '&', '*', '(', ')', + '`', '-', '=', '[', ']', '\\', '|', '{', '}', + '~','<', '>', ':', ';', '\'', '"', '_', '+', '?'}}; + + +KeyboardWidget::KeyboardWidget(QWidget *parent) : + QWidget(parent), + ui(new Ui::KeyboardWidget), + capsLock(false), + isShift(false), + page(0) +{ + vKeyboard = new X11Keyboard(this); + connect(this, SIGNAL(keyPressed(QChar)), + vKeyboard, SLOT(onKeyPressed(QChar))); + connect(this, SIGNAL(keyPressed(FuncKey::FUNCKEY)), + vKeyboard, SLOT(onKeyPressed(FuncKey::FUNCKEY))); + + ui->setupUi(this); + bindSingal(); + setDefaultIcon(); +} + +KeyboardWidget::~KeyboardWidget() +{ + delete ui; +} + +void KeyboardWidget::resizeEvent(QResizeEvent */*event*/) +{ + int w = width(); + int h = height(); + int mainLeftMargin = ui->hl_main->contentsMargins().left(); + int mainRightMargin = ui->hl_main->contentsMargins().right(); + int mainTopMargin = ui->hl_main->contentsMargins().left(); + int mainBottomMargin = ui->hl_main->contentsMargins().right(); + int mainSpacing = ui->hl_main->spacing(); + int itemSpacing = ui->hl_1->spacing(); + + int btnWidthCount = w - 11 * itemSpacing - mainSpacing- mainLeftMargin - mainRightMargin; + int btnHeightCount = h - 3 * itemSpacing - mainTopMargin - mainBottomMargin; + double btnWidth = btnWidthCount / 12; + double btnHeight = btnHeightCount / 4; + for(int i = 0; i <= 28; i++) { + QString btnObjName = "btn_" + QString::number(i); + QPushButton *btn = ui->page_letter->findChild(btnObjName); + btn->setFixedSize(btnWidth, btnHeight); + } + ui->btn_ctrl_l->setFixedSize(btnWidth * 1.3, btnHeight); + ui->btn_ctrl_r->setFixedSize(btnWidth * 1.3, btnHeight); + ui->btn_alt_l->setFixedSize(btnWidth, btnHeight); + ui->btn_alt_r->setFixedSize(btnWidth, btnHeight); + ui->btn_super->setFixedSize(btnWidth, btnHeight); + ui->btn_shift_l->setFixedSize(btnWidth, btnHeight); + ui->btn_shift_r->setFixedHeight(btnHeight); + ui->spacer_2->changeSize(btnWidth / 2, 20); + + + for(int i = 1; i <= 9; i++) { + QString btnObjName = "btn_num_" + QString::number(i); + QPushButton *btn = ui->page_number->findChild(btnObjName); + btn->setFixedWidth(btnWidth); + } + ui->btn_backspace_num->setFixedSize(btnWidth,btnHeight); + ui->btn_insert->setFixedWidth(btnWidth); + ui->btn_delete->setFixedWidth(btnWidth); + ui->btn_home->setFixedWidth(btnWidth); + ui->btn_end->setFixedWidth(btnWidth); + ui->btn_pgup->setFixedWidth(btnWidth); + ui->btn_pgdn->setFixedWidth(btnWidth); + ui->btn_up->setFixedSize(btnWidth,btnHeight); + ui->btn_down->setFixedSize(btnWidth,btnHeight); + ui->btn_left->setFixedSize(btnWidth,btnHeight); + ui->btn_right->setFixedSize(btnWidth,btnHeight); + + ui->btn_close->setFixedHeight(btnHeight); + ui->btn_letter->setFixedHeight(btnHeight); + ui->btn_symbol->setFixedHeight(btnHeight); + ui->btn_number->setFixedHeight(btnHeight); + + setIconSize(); +} + +float hScale = 0.6; +float wScale = hScale; +#define SET_ICON_SIZE_SCALE(btn) \ + ui->btn_##btn->setIconSize(QSize(ui->btn_##btn->width() * hScale, ui->btn_##btn->height() * wScale)); + +#define SET_ICON_SIZE(btn) \ + ui->btn_##btn->setIconSize(QSize(ui->btn_##btn->width(), ui->btn_##btn->height())); + +#define SET_SHIFT_ICON_SIZE_SCALE(btn) \ + ui->btn_##btn->setIconSize(QSize(ui->btn_##btn->height() * 0.4, ui->btn_##btn->height() * 0.4)); + +#define SET_ENTER_ICON_SIZE_SCALE(btn) \ + ui->btn_##btn->setIconSize(QSize(ui->btn_##btn->width() * 0.4, ui->btn_##btn->height() * 0.4)); + +void KeyboardWidget::setIconSize() +{ + SET_ICON_SIZE_SCALE(backspace); + SET_ICON_SIZE_SCALE(backspace_num); + SET_ENTER_ICON_SIZE_SCALE(enter); + SET_ICON_SIZE_SCALE(enter_num); + SET_ICON_SIZE_SCALE(close); + SET_ICON_SIZE_SCALE(super); + + SET_SHIFT_ICON_SIZE_SCALE(shift_l); + SET_SHIFT_ICON_SIZE_SCALE(shift_r); + SET_ICON_SIZE(up); + SET_ICON_SIZE(down); + SET_ICON_SIZE(left); + SET_ICON_SIZE(right); +} + +void KeyboardWidget::bindSingal() +{ + for(auto obj : ui->page_letter->children()) { + if(obj->metaObject()->className() == QString("QPushButton")) { + QPushButton *btn = static_cast(obj); + btn->setFocusPolicy(Qt::NoFocus); + connect(btn, &QPushButton::clicked, this, &KeyboardWidget::onButtonClicked); + connect(btn, &QPushButton::pressed, this, &KeyboardWidget::onButtonPressed); + connect(btn, &QPushButton::released, this, &KeyboardWidget::onButtonReleased); + } + } + for(auto obj : ui->page_number->children()) { + if(obj->metaObject()->className() == QString("QPushButton")) { + QPushButton *btn = static_cast(obj); + btn->setFocusPolicy(Qt::NoFocus); + connect(btn, &QPushButton::clicked, this, &KeyboardWidget::onButtonClicked); + connect(btn, &QPushButton::pressed, this, &KeyboardWidget::onButtonPressed); + connect(btn, &QPushButton::released, this, &KeyboardWidget::onButtonReleased); + } + } + ui->btn_close->setFocusPolicy(Qt::NoFocus); + ui->btn_letter->setFocusPolicy(Qt::NoFocus); + ui->btn_symbol->setFocusPolicy(Qt::NoFocus); + ui->btn_number->setFocusPolicy(Qt::NoFocus); + + connect(ui->btn_letter, &QPushButton::clicked, this, [&] { + ui->stackedWidget->setCurrentWidget(ui->page_letter); + page = 0; + switchPage(); + }); + connect(ui->btn_symbol, &QPushButton::clicked, this, [&] { + ui->stackedWidget->setCurrentWidget(ui->page_letter); + page = 1; + switchPage(); + }); + connect(ui->btn_number, &QPushButton::clicked, this, [&] { + ui->stackedWidget->setCurrentWidget(ui->page_number); + }); + connect(ui->btn_close, &QPushButton::clicked, + this, &KeyboardWidget::aboutToClose); + + connect(ui->btn_close, &QPushButton::pressed, + this, &KeyboardWidget::onButtonPressed); + connect(ui->btn_close, &QPushButton::released, + this, &KeyboardWidget::onButtonReleased); +} + +void KeyboardWidget::setDefaultIcon() +{ + ui->btn_backspace->setIcon(QIcon(":/images/images/backspace.svg")); + ui->btn_backspace_num->setIcon(QIcon(":/images/images/backspace.svg")); + ui->btn_enter->setIcon(QIcon(":/images/images/enter.svg")); + ui->btn_enter_num->setIcon(QIcon(":/images/images/enter.svg")); + ui->btn_shift_l->setIcon(QIcon(":/images/images/capslock.svg")); + ui->btn_shift_r->setIcon(QIcon(":/images/images/capslock.svg")); + ui->btn_close->setIcon(QIcon(":/images/images/close.svg")); + //ui->btn_super->setIcon(QIcon(":/images/images/super.svg")); + ui->btn_super->setText("Super"); + ui->btn_up->setIcon(QIcon(":/images/images/up.svg")); + ui->btn_down->setIcon(QIcon(":/images/images/down.svg")); + ui->btn_left->setIcon(QIcon(":/images/images/left.svg")); + ui->btn_right->setIcon(QIcon(":/images/images/right.svg")); +} + +QString KeyboardWidget::getKeyName(QPushButton *btn) +{ + QString objName = btn->objectName(); + int lastUnderline = objName.lastIndexOf('_'); + int start = strlen("btn_"); + int keyLength = lastUnderline - start; + QString keyName = objName.mid(start, keyLength); + return keyName; +} + +void KeyboardWidget::changeFuncKeyStyle(QPushButton *btn, bool isPressed) +{ + QString modName = getKeyName(btn); + Modifier::MOD mod = Modifier::getModifier(modName); + + if(vKeyboard->hasModifier(mod)) { + if(isPressed) + btn->setStyleSheet(BUTTON_BG_HL_PRESSED); + else + btn->setStyleSheet(BUTTON_BG_HL); + } else { + if(isPressed) + btn->setStyleSheet(BUTTON_BG_PRESSED); + else + btn->setStyleSheet(BUTTON_BG); + } +} + +void KeyboardWidget::changeShitKeyStyle(QPushButton *btn, bool isPressed) +{ + if(page == 0){ + if(isShift) { + if(capsLock){ + if(isPressed) { + btn->setStyleSheet(BUTTON_BG_HL_PRESSED); + btn->setIcon(QIcon(":/images/images/capslock_click.svg")); + } else { + btn->setStyleSheet(BUTTON_BG_HL); + btn->setIcon(QIcon(":/images/images/capslock.svg")); + } + } + else { + if(isPressed) + btn->setIcon(QIcon(":/images/images/capslock_hl_click.svg")); + else + btn->setIcon(QIcon(":/images/images/capslock_hl.svg")); + } + } else { + if(isPressed) + btn->setIcon(QIcon(":/images/images/capslock_click.svg")); + else + btn->setIcon(QIcon(":/images/images/capslock.svg")); + } + } +} + + +void KeyboardWidget::changeDirectKeyStyle(QPushButton *btn, bool isPressed) +{ + QString keyName = getKeyName(btn); + FuncKey::FUNCKEY key = FuncKey::getKey(keyName); + if(key == FuncKey::UNKNOWN) + return; + + QString iconName = QString(":/images/images/%1.svg").arg(keyName); + QString iconNamePressed = QString(":/images/images/%1_click.svg").arg(keyName); + + if(isPressed) + btn->setIcon(QIcon(iconNamePressed)); + else + btn->setIcon(QIcon(iconName)); +} + +/** + * @brief 修改按键样式 + * @param obj 按键 + * @param isPressed 按下或者松开 + */ +void KeyboardWidget::changeKeyStyle(QPushButton *btn, bool isPressed) +{ + if(btn == ui->btn_ctrl_l || btn == ui->btn_ctrl_r || + btn == ui->btn_alt_l || btn == ui->btn_alt_r || + btn == ui->btn_super) { + changeFuncKeyStyle(btn, isPressed); + } + + if(btn == ui->btn_shift_l) + changeShitKeyStyle(ui->btn_shift_l, isPressed); + if(btn == ui->btn_shift_r) + changeShitKeyStyle(ui->btn_shift_r, isPressed); + + changeDirectKeyStyle(btn, isPressed); +} + +void KeyboardWidget::onButtonPressed() +{ + QPushButton *btn = static_cast(sender()); + changeKeyStyle(btn, true); +} + +void KeyboardWidget::onButtonReleased() +{ + QPushButton *btn = static_cast(sender()); + changeKeyStyle(btn, false); +} + +void KeyboardWidget::onButtonClicked() +{ + QObject *obj = sender(); + if(obj->metaObject()->className() != QString("QPushButton")) + return; + + QPushButton *btn = static_cast(obj); + QString keyName = getKeyName(btn); + qDebug() << "keyName: " << keyName; + + Modifier::MOD mod = Modifier::getModifier(keyName); + FuncKey::FUNCKEY funcKey = FuncKey::getKey(keyName); + + if(keyName == "shift") { + if(page == 0) { + isShift = !isShift; + if(isShift) { //第一次被按下 + capsLock = false; + shiftLastClicked = QTime::currentTime(); + ui->btn_shift_l->setIcon(QIcon(":/images/images/capslock_hl.svg")); + ui->btn_shift_r->setIcon(QIcon(":/images/images/capslock_hl.svg")); + } + else { + int doubleClickInterval = QApplication::doubleClickInterval(); + if(shiftLastClicked.msecsTo(QTime::currentTime()) <= doubleClickInterval) { + //shift键双击,锁定大写 + capsLock = true; + isShift = true; + ui->btn_shift_l->setIcon(QIcon(":/images/images/capslock.svg")); + ui->btn_shift_r->setIcon(QIcon(":/images/images/capslock.svg")); + ui->btn_shift_l->setStyleSheet("QPushButton{background:#80c342}"); + ui->btn_shift_r->setStyleSheet("QPushButton{background:#80c342}"); + } else { + ui->btn_shift_l->setIcon(QIcon(":/images/images/capslock.svg")); + ui->btn_shift_r->setIcon(QIcon(":/images/images/capslock.svg")); + ui->btn_shift_l->setStyleSheet("QPushButton{background:#1e1b18}"); + ui->btn_shift_r->setStyleSheet("QPushButton{background:#1e1b18}"); + } + } + toggleCase(); + } else { + page = page % (SYMBOL_PAGE_COUNT - 1) + 1; + switchPage(); + } + } else if(mod != Modifier::UNKNOWN) { + if(vKeyboard->hasModifier(mod)) { + vKeyboard->removeModifier(mod); + btn->setStyleSheet(BUTTON_BG); + btn->setStyleSheet(BUTTON_BG); + } else { + vKeyboard->addModifier(mod); + btn->setStyleSheet(BUTTON_BG_HL); + btn->setStyleSheet(BUTTON_BG_HL); + } + } else if(funcKey != FuncKey::UNKNOWN) { + Q_EMIT keyPressed(funcKey); + } else { //字符键 + QChar c; + QString text = btn->text(); + qDebug() << "clicked button text: " << text; + if(text == "&&") + c = '&'; + else if(text.length() == 1) + c = text.at(0); + + Q_EMIT keyPressed(c); + + //如果shift键被单击,按一个键后就恢复为小写 + if(isShift && !capsLock) { + isShift = false; + toggleCase(); + changeShitKeyStyle(ui->btn_shift_l, false); + changeShitKeyStyle(ui->btn_shift_r, false); + } + clearModifier(); + } +} + +void KeyboardWidget::clearModifier() +{ + for(auto mod : vKeyboard->getAllModifier()) { + QString modName = Modifier::getModifierName(mod); + if(mod == Modifier::SUPER) { + QString objName = QString("btn_%1").arg(modName); + QPushButton *btn = ui->page_letter->findChild(objName); + btn->setStyleSheet(BUTTON_BG); + } else { + QString objName = QString("btn_%1_l").arg(modName); + QPushButton *btn = ui->page_letter->findChild(objName); + btn->setStyleSheet(BUTTON_BG); + objName = QString("btn_%1_r").arg(modName); + btn = ui->page_letter->findChild(objName); + btn->setStyleSheet(BUTTON_BG); + } + } + vKeyboard->clearModifier(); +} + +void KeyboardWidget::toggleCase() +{ + for(int i = 0; i < 26; i++) { + QString objName = "btn_" + QString::number(i); + QPushButton *btn = findChild(objName); + QChar ch; + if(isShift) { //切换到大写 + ch = symbols[0][i].toUpper(); + } else { + ch = symbols[0][i]; + } + btn->setText(ch); + } +} + +void KeyboardWidget::switchPage() +{ + if(page == 0) { + ui->btn_shift_l->setText(""); + ui->btn_shift_r->setText(""); + ui->btn_shift_l->setIcon(QIcon(":/images/images/capslock.svg")); + ui->btn_shift_r->setIcon(QIcon(":/images/images/capslock.svg")); + } else { + ui->btn_shift_l->setText(""); + ui->btn_shift_r->setText(""); + ui->btn_shift_l->setIcon(QIcon(":/images/images/capslock.svg")); + ui->btn_shift_r->setIcon(QIcon(":/images/images/capslock.svg")); + } + + for(int i = 0; i < SYMBOL_KEY_COUNT; i++) { + QString btnObjName = "btn_" + QString::number(i); + QPushButton *btn = ui->page_letter->findChild(btnObjName); + QChar c = symbols[page][i]; + if(c == '&') + btn->setText("&&"); + else + btn->setText(c); + } +} + diff --git a/VirtualKeyboard/src/keyboardwidget.h b/VirtualKeyboard/src/keyboardwidget.h new file mode 100644 index 0000000..25591ae --- /dev/null +++ b/VirtualKeyboard/src/keyboardwidget.h @@ -0,0 +1,77 @@ +/** + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301, USA. +**/ +#ifndef KEYBOARDWIDGET_H +#define KEYBOARDWIDGET_H + +#include +#include +#include +#include "x11keyboard.h" + +namespace Ui { +class KeyboardWidget; +} + +class QPushButton; + +class KeyboardWidget : public QWidget +{ + Q_OBJECT + +public: + explicit KeyboardWidget(QWidget *parent = 0); + ~KeyboardWidget(); + +protected: + void resizeEvent(QResizeEvent *event); + +private: + void bindSingal(); + void toggleCase(); + void switchPage(); + void setDefaultIcon(); + void setIconSize(); + void changeKeyStyle(QPushButton *btn, bool isPressed); + void changeFuncKeyStyle(QPushButton *btn, bool isPressed); + void changeShitKeyStyle(QPushButton *btn, bool isPressed); + void changeDirectKeyStyle(QPushButton *btn, bool isPressed); + void clearModifier(); + QString getKeyName(QPushButton *btn); + + +private Q_SLOTS: + void onButtonClicked(); + void onButtonPressed(); + void onButtonReleased(); + +Q_SIGNALS: + void aboutToClose(); + void keyPressed(QChar c); + void keyPressed(FuncKey::FUNCKEY key); + +private: + Ui::KeyboardWidget *ui; + bool capsLock; //是否大写锁定 + bool isShift; + QTime shiftLastClicked; //shift键上次被点击的时间 + int page; //当前是第几页的键盘 + X11Keyboard *vKeyboard; +}; + +#endif // KEYBOARDWIDGET_H diff --git a/VirtualKeyboard/src/keyboardwidget.ui b/VirtualKeyboard/src/keyboardwidget.ui new file mode 100644 index 0000000..ac55eff --- /dev/null +++ b/VirtualKeyboard/src/keyboardwidget.ui @@ -0,0 +1,1203 @@ + + + KeyboardWidget + + + Qt::NonModal + + + + 0 + 0 + 1132 + 293 + + + + + 0 + 0 + + + + + 0 + 0 + + + + KeyboardWidget + + + + 0 + + + QLayout::SetMinAndMaxSize + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 20 + + + QLayout::SetNoConstraint + + + 10 + + + 10 + + + 10 + + + 10 + + + + + 1 + + + + + 0 + + + QLayout::SetNoConstraint + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 5 + + + QLayout::SetNoConstraint + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + 10 + + + QLayout::SetNoConstraint + + + + + + 0 + 0 + + + + q + + + + + + + + 0 + 0 + + + + w + + + + + + + + 0 + 0 + + + + e + + + + + + + + 0 + 0 + + + + r + + + + + + + + 0 + 0 + + + + t + + + + + + + + 0 + 0 + + + + y + + + + + + + + 0 + 0 + + + + u + + + + + + + + 0 + 0 + + + + i + + + + + + + + 0 + 0 + + + + o + + + + + + + + 0 + 0 + + + + p + + + + + + + + 0 + 0 + + + + + + + + + + + + + 10 + + + QLayout::SetNoConstraint + + + + + Qt::Horizontal + + + QSizePolicy::Maximum + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + a + + + + + + + + 0 + 0 + + + + s + + + + + + + + 0 + 0 + + + + d + + + + + + + + 0 + 0 + + + + f + + + + + + + + 0 + 0 + + + + g + + + + + + + + 0 + 0 + + + + h + + + + + + + + 0 + 0 + + + + j + + + + + + + + 0 + 0 + + + + k + + + + + + + + 0 + 0 + + + + l + + + + + + + + 0 + 0 + + + + + + + + + + + + + + + + 10 + + + QLayout::SetNoConstraint + + + + + + 0 + 0 + + + + + + + + + + + + + + + 0 + 0 + + + + z + + + + + + + + 0 + 0 + + + + x + + + + + + + + 0 + 0 + + + + c + + + + + + + + 0 + 0 + + + + v + + + + + + + + 0 + 0 + + + + b + + + + + + + + 0 + 0 + + + + n + + + + + + + + 0 + 0 + + + + m + + + + + + + + 0 + 0 + + + + , + + + + + + + + 0 + 0 + + + + . + + + + + + + + 0 + 0 + + + + + + + + + + + + + + + + 10 + + + QLayout::SetNoConstraint + + + + + + 0 + 0 + + + + + + + Ctrl + + + + + + + + 0 + 0 + + + + + + + + + + + + 0 + 0 + + + + Alt + + + + + + + + 0 + 0 + + + + + + + + + + + + 0 + 0 + + + + Alt + + + + + + + + 0 + 0 + + + + / + + + + + + + + 0 + 0 + + + + Ctrl + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + QLayout::SetNoConstraint + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + 10 + + + QLayout::SetNoConstraint + + + + + QLayout::SetNoConstraint + + + 10 + + + + + + 0 + 0 + + + + 9 + + + + + + + + 0 + 0 + + + + 5 + + + + + + + + 0 + 0 + + + + 8 + + + + + + + + 0 + 0 + + + + 4 + + + + + + + + 0 + 0 + + + + 2 + + + + + + + + 0 + 0 + + + + 7 + + + + + + + + 0 + 0 + + + + 1 + + + + + + + + 0 + 0 + + + + 6 + + + + + + + + 0 + 0 + + + + 3 + + + + + + + + 0 + 0 + + + + 0 + + + + + + + + 0 + 0 + + + + + + + + + + + + 0 + 0 + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + QLayout::SetNoConstraint + + + 10 + + + + + + 0 + 0 + + + + Delete + + + + + + + + 0 + 0 + + + + End + + + + + + + + 0 + 0 + + + + Insert + + + + + + + + 0 + 0 + + + + PgUp + + + + + + + + 0 + 0 + + + + + + + + + + + + 0 + 0 + + + + + + + + + + + + 0 + 0 + + + + Home + + + + + + + + 0 + 0 + + + + + + + + + + + + 0 + 0 + + + + + + + + + + + + 0 + 0 + + + + PgDn + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + 5 + + + QLayout::SetNoConstraint + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + + 100 + 16777215 + + + + + + + + + + + + 0 + 0 + + + + + 100 + 16777215 + + + + Abc + + + + + + + + 0 + 0 + + + + + 100 + 16777215 + + + + @ + + + + + + + + 0 + 0 + + + + + 100 + 16777215 + + + + 123 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + diff --git a/VirtualKeyboard/src/main.cpp b/VirtualKeyboard/src/main.cpp new file mode 100644 index 0000000..99fc2f0 --- /dev/null +++ b/VirtualKeyboard/src/main.cpp @@ -0,0 +1,32 @@ +/** + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301, USA. +**/ +#include "virtualkeyboard.h" +#include + +int main(int argc, char *argv[]) +{ +// qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard")); + QApplication a(argc, argv); + VirtualKeyboard *keyboard = new VirtualKeyboard; + QObject::connect(keyboard, &VirtualKeyboard::aboutToClose, &a, &QApplication::quit); + keyboard->resize(400,300); + keyboard->show(); + + return a.exec(); +} diff --git a/VirtualKeyboard/src/virtualkeyboard.cpp b/VirtualKeyboard/src/virtualkeyboard.cpp new file mode 100644 index 0000000..ea46695 --- /dev/null +++ b/VirtualKeyboard/src/virtualkeyboard.cpp @@ -0,0 +1,79 @@ +/** + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301, USA. +**/ +#include "virtualkeyboard.h" +#include +#include +#include +#include + +VirtualKeyboard::VirtualKeyboard(QWidget *parent) + : QWidget(parent) +{ + Q_INIT_RESOURCE(keyboard); + + setAutoFillBackground(true); + QPalette plt; + plt.setBrush(QPalette::Background, Qt::black); + setPalette(plt); + + setWindowFlags(Qt::FramelessWindowHint | + Qt::WindowStaysOnTopHint | + Qt::WindowDoesNotAcceptFocus); + + + keyboardWidget = new KeyboardWidget(this); + QHBoxLayout *hl_keyboard = new QHBoxLayout(this); + QSpacerItem *spacer = new QSpacerItem(40, 20); + hl_keyboard->addSpacerItem(spacer); + hl_keyboard->addWidget(keyboardWidget); + QSpacerItem *spacer2 = new QSpacerItem(40, 20); + hl_keyboard->addSpacerItem(spacer2); + + QFile qssFile(":/qss/keyboard.qss"); + qssFile.open(QIODevice::ReadOnly); + setStyleSheet(qssFile.readAll()); + qssFile.close(); + + QDesktopWidget *desktop = QApplication::desktop(); + cursorMonitor = new CursorMonitor(this); + + //在多显示器情况下,监视鼠标指针的位置和主显示器变化信号 + connect(cursorMonitor, &CursorMonitor::cursorPosChanged, + this, [&](const QPoint &pos){ + adjustGeometry(desktop->screenNumber(pos)); + }); + + connect(desktop, &QDesktopWidget::primaryScreenChanged, + this, [&]{ + adjustGeometry(desktop->primaryScreen()); + }); + + connect(keyboardWidget, &KeyboardWidget::aboutToClose, + this, &VirtualKeyboard::aboutToClose); + + adjustGeometry(desktop->primaryScreen()); +} + +void VirtualKeyboard::adjustGeometry(int screen) +{ + QDesktopWidget *desktop = QApplication::desktop(); + QWidget *activateScreen = desktop->screen(screen); + setGeometry(0, activateScreen->height() - activateScreen->height() / 3, + activateScreen->width(), activateScreen->height() / 3); +} diff --git a/VirtualKeyboard/src/virtualkeyboard.h b/VirtualKeyboard/src/virtualkeyboard.h new file mode 100644 index 0000000..44b628a --- /dev/null +++ b/VirtualKeyboard/src/virtualkeyboard.h @@ -0,0 +1,47 @@ +/** + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301, USA. +**/ +#ifndef VIRTUALKEYBOARD_H +#define VIRTUALKEYBOARD_H + +#include +#include + +#include "keyboardwidget.h" +#include "cursormonitor.h" + +class VirtualKeyboard : public QWidget +{ + Q_OBJECT +public: + explicit VirtualKeyboard(QWidget *parent = 0); + +private: + void adjustGeometry(int screen); + +Q_SIGNALS: + void aboutToClose(); + +private: + KeyboardWidget *keyboardWidget; + CursorMonitor *cursorMonitor; + bool isApplication; +}; + + +#endif // VIRTUALKEYBOARD_H diff --git a/VirtualKeyboard/src/x11keyboard.cpp b/VirtualKeyboard/src/x11keyboard.cpp new file mode 100644 index 0000000..5e160ed --- /dev/null +++ b/VirtualKeyboard/src/x11keyboard.cpp @@ -0,0 +1,208 @@ +/** + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301, USA. +**/ +#include "x11keyboard.h" +#include +#include +#include +#include + + + +struct CharMap { + QChar name; + KeySym code; +}; + +struct CharMap XSpecialSymbolMap[] { + {' ', XK_space}, + {',', XK_comma}, + {'.', XK_period}, + {'\'', XK_quoteright}, + {'@', XK_at}, + {'#', XK_numbersign}, + {'$', XK_dollar}, + {'%', XK_percent}, + {'&', XK_ampersand}, + {'*', XK_asterisk}, + {'(', XK_parenleft}, + {')', XK_parenright}, + {'-', XK_minus}, + {'+', XK_plus}, + {'!', XK_exclam}, + {'"', XK_quotedbl}, + {'<', XK_less}, + {'>', XK_greater}, + {':', XK_colon}, + {';', XK_semicolon}, + {'/', XK_slash}, + {'?', XK_question}, + {'=', XK_equal}, + {'.', XK_kana_middledot}, + {'~', XK_asciitilde}, + {'`', XK_grave}, + {'|', XK_bar}, + {'^', XK_asciicircum}, + {'{', XK_braceleft}, + {'}', XK_braceright}, + {'[', XK_bracketleft}, + {']', XK_bracketright}, + {'_', XK_underscore}, + {'\\', XK_backslash}, +}; + +QMap funckeyMap = { + {FuncKey::SPACE, XK_space}, + {FuncKey::BACKSPACE, XK_BackSpace}, + {FuncKey::ENTER, XK_Return}, + {FuncKey::HOME, XK_Home}, + {FuncKey::END, XK_End}, + {FuncKey::PGUP, XK_Page_Up}, + {FuncKey::PGDN, XK_Page_Down}, + {FuncKey::INSERT, XK_Insert}, + {FuncKey::DELETE, XK_Delete}, + {FuncKey::UP, XK_Up}, + {FuncKey::DOWN, XK_Down}, + {FuncKey::LEFT, XK_Left}, + {FuncKey::RIGHT, XK_Right} +}; + +QMap modifierMap = { + {Modifier::CTRL, XK_Control_L}, + {Modifier::ALT, XK_Alt_L}, + {Modifier::SUPER, XK_Super_L}, + {Modifier::SHIFT, XK_Shift_L} +}; + +QVector shiftKeyVec = {'~', '!', '@', '#', '$', '%', '^', '&', '*', + '(', ')', '_', '+', '{', '}', '|', ':', '"', + '>', '?'}; + +static Display *display = XOpenDisplay(0); +bool isShift = false; +bool isLetter = false; +unsigned int keyCodeOfChar(QChar c) +{ + QString text(c); + + KeySym keysym = XStringToKeysym(text.toLocal8Bit().data()); + + if(keysym == NoSymbol) { + int symbolCount = sizeof(XSpecialSymbolMap) / sizeof(struct CharMap); + for(int i = 0; i < symbolCount; i++) { + if(XSpecialSymbolMap[i].name == c) { + keysym = XSpecialSymbolMap[i].code; + break; + } + } + } + qDebug() << "keysym: " << keysym; + + isShift = shiftKeyVec.contains(c) || (c >= 'A' && c <= 'Z'); + + isLetter = c.isLetter(); + + KeyCode code = XKeysymToKeycode(display, keysym); + + return code; +} + +X11Keyboard::X11Keyboard(QObject *parent) + : QObject(parent) +{ + +} + +X11Keyboard::~X11Keyboard() +{ + XCloseDisplay(display); +} + +void X11Keyboard::addModifier(Modifier::MOD mod) +{ + modList.push_back(mod); +} + +void X11Keyboard::removeModifier(Modifier::MOD mod) +{ + modList.removeOne(mod); +} + +bool X11Keyboard::hasModifier(Modifier::MOD mod) +{ + return modList.contains(mod); +} + +QList X11Keyboard::getAllModifier() +{ + return modList; +} + +void X11Keyboard::clearModifier() +{ + modList.clear(); +} + + +void X11Keyboard::onKeyPressed(QChar c) +{ + unsigned int keyCode = keyCodeOfChar(c); + sendKey(keyCode); +} + +void X11Keyboard::onKeyPressed(FuncKey::FUNCKEY key) +{ + KeyCode keyCode; + KeySym keysym = funckeyMap[key]; + + if(keysym != NoSymbol) + keyCode = XKeysymToKeycode(display, keysym); + + sendKey(keyCode); +} + +void X11Keyboard::sendKey(unsigned int keyCode) +{ + Window focusWindow; + int revert; + XGetInputFocus(display, &focusWindow, &revert); + + for(auto mod : modList){ + KeyCode keyCode = XKeysymToKeycode(display, modifierMap[mod]); + XTestFakeKeyEvent(display, keyCode, True, 2); + } + + //如果使用了修饰键(如ctrl、alt)且字符键是字母,则不起用shift键,否则快捷键不起作用 + if(!modList.isEmpty() && isLetter) + isShift = false; + + if(isShift) + XTestFakeKeyEvent(display, XKeysymToKeycode(display, XK_Shift_L), True, 2); + + XTestFakeKeyEvent(display, keyCode, True, CurrentTime); + XTestFakeKeyEvent(display, keyCode, False, CurrentTime); + + if(isShift) + XTestFakeKeyEvent(display, XKeysymToKeycode(display, XK_Shift_L), False, 2); + for(auto mod : modList){ + KeyCode keyCode = XKeysymToKeycode(display, modifierMap[mod]); + XTestFakeKeyEvent(display, keyCode, False, 2); + } + + XFlush(display); +} diff --git a/VirtualKeyboard/src/x11keyboard.h b/VirtualKeyboard/src/x11keyboard.h new file mode 100644 index 0000000..42fd997 --- /dev/null +++ b/VirtualKeyboard/src/x11keyboard.h @@ -0,0 +1,116 @@ +/** + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301, USA. +**/ +#ifndef X11KEYBOARD_H +#define X11KEYBOARD_H + +#include +#include + +class Modifier : public QObject +{ + Q_OBJECT +public: + Modifier(){} + + enum MOD{ + UNKNOWN = -1, + CTRL, + SHIFT, + ALT, + SUPER + }; + Q_ENUM(MOD) + + static QString getModifierName(int mod) + { + QMetaEnum metaEnum = QMetaEnum::fromType(); + const char* modName = metaEnum.valueToKey(mod); + QString result = QString(modName).toLower(); + return result; + } + static MOD getModifier(const QString &modName) + { + QMetaEnum metaEnum = QMetaEnum::fromType(); + MOD mod = (MOD)metaEnum.keyToValue(modName.toUpper().toLocal8Bit().data()); + return mod; + } +}; + +class FuncKey : public QObject +{ + Q_OBJECT +public: + FuncKey(){} + + enum FUNCKEY { + UNKNOWN = -1, + SPACE = 0, + BACKSPACE, + ENTER, + HOME, + END, + PGUP, + PGDN, + INSERT, + DELETE, + UP, + DOWN, + LEFT, + RIGHT + }; + Q_ENUM(FUNCKEY) + static QString getKeyName(int key) + { + QMetaEnum metaEnum = QMetaEnum::fromType(); + const char* keyName = metaEnum.valueToKey(key); + QString result = QString(keyName).toLower(); + return result; + } + static FUNCKEY getKey(const QString &keyName) + { + QMetaEnum metaEnum = QMetaEnum::fromType(); + FUNCKEY key = (FUNCKEY)metaEnum.keyToValue(keyName.toUpper().toLocal8Bit().data()); + return key; + } +}; + +class X11Keyboard : public QObject +{ + Q_OBJECT +public: + explicit X11Keyboard(QObject *parent = nullptr); + ~X11Keyboard(); + void addModifier(Modifier::MOD mod); + void removeModifier(Modifier::MOD mod); + bool hasModifier(Modifier::MOD mod); + QList getAllModifier(); + void clearModifier(); + +public Q_SLOTS: + void onKeyPressed(QChar c); + void onKeyPressed(FuncKey::FUNCKEY key); + +private: + void sendKey(unsigned int keyCode); + +private: + QList modList; +}; + +#endif // X11KEYBOARD_H diff --git a/config.h.in b/config.h.in new file mode 100644 index 0000000..a66e4e2 --- /dev/null +++ b/config.h.in @@ -0,0 +1,9 @@ +#cmakedefine USE_INTEL + +// == 下面是测试用的 +#cmakedefine01 TEST_ON +#cmakedefine01 TEST_OF + +#ifndef __${VAR}___@VAR@ + +#endif diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt new file mode 100644 index 0000000..ac9b470 --- /dev/null +++ b/data/CMakeLists.txt @@ -0,0 +1,6 @@ +install(FILES org.ukui.screensaver.gschema.xml DESTINATION /usr/share/glib-2.0/schemas) +install(FILES ukui-screensaver.desktop DESTINATION /etc/xdg/autostart) +install(FILES ukui-screensaver.directory DESTINATION /usr/share/desktop-directories) +install(FILES ukui-screensavers.menu DESTINATION /etc/xdg/menus) +install(FILES ukui-screensaver-qt DESTINATION /etc/pam.d) +install(FILES screensaver-startup.sh DESTINATION /usr/bin) diff --git a/data/org.ukui.screensaver.gschema.xml b/data/org.ukui.screensaver.gschema.xml new file mode 100644 index 0000000..b9d8aeb --- /dev/null +++ b/data/org.ukui.screensaver.gschema.xml @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + false +

Automatic switching background + Set this to TRUE to activate the automatic switching background. + + + true + show rest time on custom screensaver + Set this to TRUE to show rest time on custom screensaver. + + + true + show rest time on ukui screensaver + Set this to TRUE to show rest time on ukui screensaver. + + + + "" + set user text on screensaver. + set user text on screensaver. + + + "/usr/share/backgrounds" + set background path on screensaver. + set background path on screensaver. + + + true + set text postion + Set this to TRUE to show text on screensaver center. + + + 300 + Time to update background on ukui-screensavaer-default + The number of seconds to change screensaver background. + + + + + true + Activate when idle + Set this to TRUE to activate the screensaver when the session is idle. + + + true + Activation when lock + Set this to TRUE to lock the screen when the the session is idle. + + + true + Lock on activation + Set this to TRUE to lock the screen when the screensaver goes active. + + + 5 + idle delay to lock + Wait idle delay to lock + + + 5 + idle lock to lock + Display the lock screen + + + true + Lock when sleep + Set this to TRUE to lock the screen when the system goes sleep. + + + 'default-ukui' + Screensaver theme selection mode + The selection mode used by screensaver. May be "blank-only" to enable the screensaver without using any theme on activation, "single" to enable screensaver using only one theme on activation (specified in "themes" key), and "random" to enable the screensaver using a random theme on activation. + + + 'fade-in-out' + Effect for images transition + The selection effect used by screensaer which play images. May be "none" to play images without any transition effect. It's ignored when "mode" is not "image". + + + 60 + Time interval of switching images + The number of seconds of time interval of switching images + + + [] + Screensaver themes + This key specifies the list of themes to be used by the screensaver. It's ignored when "mode" key is "blank-only", should provide the theme name when "mode" is "single", and should provide a list of themes when "mode" is "random". + + + 30 + Time before power management baseline + The number of seconds of inactivity before signalling to power management. This key is set and maintained by the session power management agent. + + + 10 + Time before theme change + The number of minutes to run before changing the screensaver theme. + + + 0 + Time before locking + The number of minutes after screensaver activation before locking the screen. + + + false + Allow embedding a keyboard into the window + Set this to TRUE to allow embedding a keyboard into the window when trying to unlock. The "keyboard_command" key must be set with the appropriate command. + + + '' + Embedded keyboard command + The command that will be run, if the "embedded_keyboard_enabled" key is set to TRUE, to embed a keyboard widget into the window. This command should implement an XEMBED plug interface and output a window XID on the standard output. + + + false + Allow logout + Set this to TRUE to offer an option in the unlock dialog to allow logging out after a delay. The delay is specified in the "logout_delay" key. + + + 120 + Time before logout option + The number of minutes after the screensaver activation before a logout option will appear in the unlock dialog. This key has effect only if the "logout_enable" key is set to TRUE. + + + '' + Logout command + The command to invoke when the logout button is clicked. This command should simply log the user out without any interaction. This key has effect only if the "logout_enable" key is set to TRUE. + + + true + Allow user switching + Set this to TRUE to offer an option in the unlock dialog to switch to a different user account. + + + true + Allow the session status message to be displayed + Allow the session status message to be displayed when the screen is locked. + + + '/usr/share/backgrounds/1-warty-final-ubuntukylin.jpg' + the background iamge of lockscreen + Allow the user to set the background iamge of lockscreen. + + + 0 + Number of side-bar notify messages + The number of sider notify messages + + + true + Allow to show messages + Set this to TRUE to if u want to see how many masseges not read + + + diff --git a/data/screensaver-startup.sh b/data/screensaver-startup.sh new file mode 100755 index 0000000..fa8710d --- /dev/null +++ b/data/screensaver-startup.sh @@ -0,0 +1,5 @@ +#!/bin/bash +/usr/lib/ukui-screensaver/set4kScale >/dev/null 2>&1 +ukui-screensaver-dialog --lock-startup & >/dev/null 2>&1 +/usr/lib/ukui-screensaver/screensaver-focus-helper & >/dev/null 2>&1 + diff --git a/data/ukui-screensaver-qt b/data/ukui-screensaver-qt new file mode 100644 index 0000000..8ac0d54 --- /dev/null +++ b/data/ukui-screensaver-qt @@ -0,0 +1,14 @@ +auth requisite pam_nologin.so +auth sufficient pam_succeed_if.so user ingroup nopasswdlogin + +@include common-auth +auth optional pam_gnome_keyring.so + + +#If you are using Arch,comment out the +#above and use the following. + +#auth include system-auth +#account include system-auth +#password include system-auth +#session include system-auth diff --git a/data/ukui-screensaver.desktop b/data/ukui-screensaver.desktop new file mode 100644 index 0000000..ce57723 --- /dev/null +++ b/data/ukui-screensaver.desktop @@ -0,0 +1,19 @@ +[Desktop Entry] +Type=Application +Name=Screensaver +Name[tr]=Ekran Koruyucu +Name[zh_CN]=屏幕保护程序 +Name[zh_HK]=螢幕保護程式 +Name[zh_TW]=螢幕保護程式 +Comment[tr]=Ekran koruyucuyu çalıştır ve programı kilitler +Comment[zh_CN]=运行屏幕保护及锁定程序 +Comment[zh_TW]=啟動螢幕保護程式以及鎖定程式 +Exec=ukui-screensaver-backend +NoDisplay=true +X-UKUI-Autostart-Phase=Application +X-UKUI-Autostart-Notify=true +X-UKUI-Bugzilla-Bugzilla=UKUI +X-UKUI-Bugzilla-Product=ukui-screensaver +X-UKUI-Bugzilla-Component=general +X-UKUI-Bugzilla-Version=1.0.0 +OnlyShowIn=UKUI diff --git a/data/ukui-screensaver.directory b/data/ukui-screensaver.directory new file mode 100644 index 0000000..2fd2ff2 --- /dev/null +++ b/data/ukui-screensaver.directory @@ -0,0 +1,180 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=Screensavers +Name[am]=የመመልከቻው ማዳኛዎች +Name[ar]=حافظات الشاشة +Name[as]=স্ক্ৰীনছেভাৰ +Name[ast]=Curiapantalles +Name[be]=Ахоўнікі экрану +Name[be@latin]=Źbierahalniki ekranu +Name[bg]=Предпазители на екрана +Name[bn]=স্ক্রীন সেভার +Name[bn_IN]=স্ক্রীনসেভার +Name[br]=Damanterioù skramm +Name[ca]=Estalvis de pantalla +Name[ca@valencia]=Estalvis de pantalla +Name[cmn]=螢幕保護程式 +Name[crh]=Ekran qoruyıcıları +Name[cs]=Šetřiče obrazovky +Name[cy]=Arbedwyr Sgrin +Name[da]=Pauseskærme +Name[de]=Bildschirmschoner +Name[dz]=གསལ་གཞི་ཉེན་སྲུང་ཚུ། +Name[el]=Θέματα +Name[en_AU]=Screensavers +Name[en_CA]=Screensavers +Name[en_GB]=Screensavers +Name[es]=Salvapantallas +Name[es_AR]=Protectores de pantalla +Name[et]=Ekraanisäästjad +Name[eu]=Pantaila-babeslea +Name[fa]=محافظ‌های صفحهٔ نمایش +Name[fi]=Näytönsäästäjät +Name[fr]=Économiseurs d'écran +Name[ga]=Spárálaithe Scáileáin +Name[gl]=Protectores de pantalla +Name[gu]=સ્ક્રીનસેવરો +Name[he]=שומרי מסך +Name[hi]=स्क्रीनसेवरs +Name[hr]=Zaslonski čuvari +Name[hu]=Képernyővédők +Name[id]=Screensaver +Name[is]=Skjáhvílur +Name[it]=Salvaschermo +Name[ja]=いろいろなスクリーンセーバーを格納するフォルダ +Name[ka]=ეკრანმზოგები +Name[kn]=ಸ್ಕ್ರೀನ್‌ಸೇವರುಗಳು +Name[ko]=화면 보호기 +Name[ku]=Parêzvanên ekranê +Name[ky]=Экран сактагычтар +Name[lt]=Ekrano užsklandos +Name[lv]=Ekrānsaudzētāji +Name[mai]=स्क्रीनसेवर +Name[mg]=Sary mitsitsy +Name[mk]=Чувари на екранот +Name[ml]=സ്ക്രീന്‍സേവറുകള്‍ +Name[mn]=_Дэлгэц гамнагчүүд +Name[mr]=स्क्रीनसेवर +Name[ms]=Penyelamat skrin +Name[nb]=Skjermsparere +Name[nds]=Billschirmschoners +Name[ne]=स्क्रिनसेभर +Name[nl]=Screensavers +Name[nn]=Skjermsparare +Name[oc]=Estalviaires d'ecran +Name[or]=ପରଦା ସଂରକ୍ଷକ +Name[pa]=ਸਕਰੀਨ-ਸੇਵਰ +Name[pl]=Wygaszacze ekranu +Name[ps]=پرده ساتونکي +Name[pt]=Protetores de Ecrã +Name[pt_BR]=Proteções de tela +Name[ro]=Economizoare de ecran +Name[ru]=Хранители экрана +Name[si]=තිරආවරක +Name[sk]=Šetriče obrazovky +Name[sl]=Ohranjevalniki zaslona +Name[sq]=Ruajtës ekrani +Name[sr]=Чувари екрана +Name[sr@latin]=Čuvari ekrana +Name[sv]=Skärmsläckare +Name[ta]=திரைசேமிப்பிகள் +Name[te]=తెరకాపరులు +Name[th]=โปรแกรมรักษาหน้าจอ +Name[tr]=Ekran koruyucular +Name[uk]=Заставки екрану +Name[ur]=سکرین سیور +Name[vi]=Ảnh bảo vệ màn hình +Name[zh_CN]=屏幕保护程序 +Name[zh_HK]=螢幕保護程式 +Name[zh_TW]=螢幕保護程式 +Comment=Screensaver themes +Comment[am]=የመመልከቻው ማዳኛ ገጽታዎች +Comment[ar]=سِمات حافظة الشاشة +Comment[as]=স্ক্ৰীনছেভাৰ থিম +Comment[ast]=Temes del Curiapantalles +Comment[be]=Тэмы ахоўніка экрану +Comment[be@latin]=Matyvy źbierahalnika ekranu +Comment[bg]=Теми за предпазителя +Comment[bn]=স্ক্রীনসেভার থীম +Comment[bn_IN]=স্ক্রীনসেভার থিম +Comment[br]=Neuzioù an damanter skramm +Comment[ca]=Temes de l'estalvi de pantalla +Comment[ca@valencia]=Temes de l'estalvi de pantalla +Comment[cmn]=螢幕保護程式布景主題 +Comment[crh]=Ekran qoruyıcısı temaları +Comment[cs]=Motivy šetřiče obrazovky +Comment[cy]=Themâu arbedwr sgrin +Comment[da]=Pauseskærm temaer +Comment[de]=Bildschirmschonerthemen +Comment[dz]=གསལ་གཞི་ཉེན་སྲུང་བརྗོད་དོན་ཚུ། +Comment[el]=Θέματα προστασίας οθόνης +Comment[en@shaw]=𐑕𐑒𐑮𐑰𐑯𐑕𐑱𐑝𐑼 𐑔𐑰𐑥𐑟 +Comment[en_AU]=Screensaver themes +Comment[en_CA]=Screensaver themes +Comment[en_GB]=Screensaver themes +Comment[es]=Temas del salvapantallas +Comment[es_AR]=Temas de protector de pantalla +Comment[et]=Ekraanisäästja teemad +Comment[eu]=Pantaila-babeslearen gaiak +Comment[fa]=تم‌های محافظ صفحهٔ نمایش +Comment[fi]=Näytönsäästäjän teemat +Comment[fr]=Thèmes de l'économiseur d'écran +Comment[ga]=Téamaí spárálaí scáileáin +Comment[gl]=Temas do protector de pantalla +Comment[gu]=સ્ક્રીનસેવર થીમો +Comment[he]=ערכות נושא לשומר המסך +Comment[hi]=स्क्रीनसेवर थीम +Comment[hr]=Teme zaslonskog čuvara +Comment[hu]=Képernyővédő-témák +Comment[id]=Tema screensaver +Comment[is]=Ásýnd skjáhvílu +Comment[it]=Temi del salvaschermo +Comment[ja]=スクリーンセーバーのテーマ +Comment[ka]=ეკრანმზოგის გაფორმების თემები +Comment[kn]=ಸ್ಕ್ರೀನ್‌ಸೇವರ್ ಥೀಮ್‌ಗಳು +Comment[ko]=화면 보호기 테마 +Comment[ku]=Dirbên dîmenderparêzê +Comment[ky]=Экран сактагыч темалары +Comment[lt]=Ekrano užsklandos temos +Comment[lv]=Ekrānsaudzētāja tēmas +Comment[mai]=स्क्रीनसेवर थीम +Comment[mg]=Endri-tsary mitsitsy +Comment[mk]=Теми за чуварот на екранот +Comment[ml]=സ്ക്രീന്‍സേവറിന്റെ പ്രമേയങ്ങള്‍ +Comment[mn]=_Дэлгэц гамнагчийн загварууд +Comment[mr]=स्क्रीनसेवर सुत्रयोजना +Comment[ms]=Tema penyelamat skrin +Comment[nb]=Tema for skjermsparer +Comment[nds]=Billschirmschonerthemen +Comment[ne]=स्क्रिनसेभर विषयवस्तु +Comment[nl]=Screensavers +Comment[nn]=Tema for skjermsparar +Comment[oc]=Tèmas d'estalviaire d'ecran +Comment[or]=ପରଦା ସଂରକ୍ଷକ ପ୍ରସଙ୍ଗ +Comment[pa]=ਸਕਰੀਨ-ਸੇਵਰ ਥੀਮ +Comment[pl]=Motywy wygaszacza ekranu +Comment[ps]=د پرده ساتونکي کوندې +Comment[pt]=Temas do Protetor de Ecrã +Comment[pt_BR]=Temas de proteção de tela +Comment[ro]=Teme pentru economizor +Comment[ru]=Темы хранителя экрана +Comment[si]=තිරආවරකයේ තේමා +Comment[sk]=Témy šetriča obrazovky +Comment[sl]=Teme ohranjevalnika zaslona +Comment[sq]=Temat e ruajtësit të ekranit +Comment[sr]=Теме чувара екрана +Comment[sr@latin]=Teme čuvara ekrana +Comment[sv]=Skärmsläckarteman +Comment[ta]=திரைசேமிப்பி சூழல்கள் +Comment[te]=తెరకాపరి వైవిద్యాంశాలు +Comment[th]=ชุดโปรแกรมรักษาหน้าจอ +Comment[tr]=Ekran koruyucu temaları +Comment[uk]=Теми збереження екрану +Comment[ur]=سکرین سیور کی تھیم +Comment[vi]=Sắc thái của ảnh bảo vệ màn hình +Comment[zh_CN]=屏幕保护程序主题 +Comment[zh_HK]=螢幕保護程式佈景主題 +Comment[zh_TW]=螢幕保護程式布景主題 +Icon=screensaver +Type=Directory +NoDisplay=false diff --git a/data/ukui-screensavers.menu b/data/ukui-screensavers.menu new file mode 100644 index 0000000..f3dc43d --- /dev/null +++ b/data/ukui-screensavers.menu @@ -0,0 +1,20 @@ + + + + + Screensavers + ukui-screensaver.directory + + + + + + + + + + Screensaver + + + diff --git a/i18n_ts/CMakeLists.txt b/i18n_ts/CMakeLists.txt new file mode 100644 index 0000000..f4b0cef --- /dev/null +++ b/i18n_ts/CMakeLists.txt @@ -0,0 +1,12 @@ +find_package(Qt5LinguistTools) + +file(GLOB ts_files *.ts) +qt5_create_translation(qm_files ${ts_files}) +add_custom_target(i18n + DEPENDS ${qm_files} + SOURCES ${ts_files} + ) +# 让主目标依赖翻译文件,这样才会执行i18n +add_dependencies(ukui-screensaver-dialog i18n) + +install(FILES ${qm_files} DESTINATION /usr/share/ukui-screensaver/i18n_qm/) diff --git a/i18n_ts/bo.ts b/i18n_ts/bo.ts new file mode 100644 index 0000000..1f66585 --- /dev/null +++ b/i18n_ts/bo.ts @@ -0,0 +1,2088 @@ + + + + + AuthDialog + + + Authentication failure, Please try again + + + + + + + + Please try again in %1 minutes. + + + + + + + + Please try again in %1 seconds. + + + + + + + + Account locked permanently. + + + + + Verify face recognition or enter password to unlock + + + + + Press fingerprint or enter password to unlock + + + + + Verify voiceprint or enter password to unlock + + + + + Verify finger vein or enter password to unlock + + + + + Verify iris or enter password to unlock + + + + + Use the bound wechat scanning code or enter the password to unlock + + + + + + Password cannot be empty + + + + + Password + + + + + Input Password + + + + + Login + + + + + Retry + + + + + Failed to verify %1, please enter password to unlock + + + + + Unable to verify %1, please enter password to unlock + + + + + Failed to verify %1, you still have %2 verification opportunities + + + + + NET Exception + + + + + BiometricAuthWidget + + + Current device: + + + + + Identify failed, Please retry. + + + + + BiometricDevicesWidget + + + Please select the biometric device + + + + + Device type: + + + + + Device name: + + + + + OK + + + + + ConfForm + + + edit network + + + + + LAN name: + + + + + Method: + + + + + Address: + + + + + Netmask: + + + + + Gateway: + + + + + DNS 1: + + + + + DNS 2: + + + + + Edit Conn + + + + + + Auto(DHCP) + + + + + + Manual + + + + + Cancel + + + + + Save + + + + + Ok + + + + + Can not create new wired network for without wired card + + + + + New network already created + + + + + New network settings already finished + + + + + New settings already effective + + + + + Edit Network + + + + + Add Wired Network + + + + + DeviceType + + + FingerPrint + + + + + FingerVein + + + + + Iris + + + + + Face + + + + + VoicePrint + + + + + QRCode + + + + + DigitalAuthDialog + + + + LoginByUEdu + + + + + ResetPWD? + + + + + + SetNewUEduPWD + + + + + clear + + + + + ConfirmNewUEduPWD + + + + + The two password entries are inconsistent, please reset + + + + + Password entered incorrectly, please try again + + + + + DlgConnHidWifi + + + + Connect to Hidden Wi-Fi Network + + + + + Add Hidden Wi-Fi + + + + + Connection + + + + + Wi-Fi name + + + + + Wi-Fi security + + + + + Cancel + + + + + Connect + + + + + C_reate… + + + + + None + + + + + WPA & WPA2 Personal + + + + + DlgConnHidWifiLeap + + + + Connect to Hidden Wi-Fi Network + + + + + Add hidden Wi-Fi + + + + + Connection + + + + + Network name + + + + + Wi-Fi security + + + + + Username + + + + + Password + + + + + Cancel + + + + + Connect + + + + + C_reate… + + + + + None + + + + + WPA & WPA2 Personal + + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + + WEP 128-bit Passphrase + + + + + Dynamic WEP (802.1X) + + + + + WPA & WPA2 Enterprise + + + + + DlgConnHidWifiSecFast + + + + Connect to Hidden Wi-Fi Network + + + + + Add hidden Wi-Fi + + + + + Connection + + + + + Network name + + + + + Wi-Fi security + + + + + Authentication + + + + + Anonymous identity + + + + + Allow automatic PAC pro_visioning + + + + + PAC file + + + + + Inner authentication + + + + + Username + + + + + Password + + + + + Cancel + + + + + Connect + + + + + C_reate… + + + + + + None + + + + + WPA & WPA2 Personal + + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + + WEP 128-bit Passphrase + + + + + Dynamic WEP (802.1X) + + + + + WPA & WPA2 Enterprise + + + + + Tunneled TLS + + + + + Protected EAP (PEAP) + + + + + Anonymous + + + + + Authenticated + + + + + Both + + + + + DlgConnHidWifiSecLeap + + + + Connect to Hidden Wi-Fi Network + + + + + Add hidden Wi-Fi + + + + + Connection + + + + + Network name + + + + + Wi-Fi security + + + + + Authentication + + + + + Username + + + + + Password + + + + + Cancel + + + + + Connect + + + + + C_reate… + + + + + None + + + + + WPA & WPA2 Personal + + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + + WEP 128-bit Passphrase + + + + + Dynamic WEP (802.1X) + + + + + WPA & WPA2 Enterprise + + + + + Tunneled TLS + + + + + Protected EAP (PEAP) + + + + + DlgConnHidWifiSecPeap + + + + Connect to Hidden Wi-Fi Network + + + + + Add hidden Wi-Fi + + + + + Connection + + + + + Network name + + + + + Wi-Fi security + + + + + Authentication + + + + + Anonymous identity + + + + + Domain + + + + + CA certificate + + + + + CA certificate password + + + + + No CA certificate is required + + + + + PEAP version + + + + + Inner authentication + + + + + Username + + + + + Password + + + + + Cancel + + + + + Connect + + + + + + None + + + + + WPA & WPA2 Personal + + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + + WEP 128-bit Passphrase + + + + + Dynamic WEP (802.1X) + + + + + WPA & WPA2 Enterprise + + + + + Tunneled TLS + + + + + Protected EAP (PEAP) + + + + + Choose from file + + + + + Automatic + + + + + Version 0 + + + + + Version 1 + + + + + DlgConnHidWifiSecPwd + + + + Connect to Hidden Wi-Fi Network + + + + + Add hidden Wi-Fi + + + + + Connection + + + + + Network name + + + + + Wi-Fi security + + + + + Authentication + + + + + Username + + + + + Password + + + + + Cancel + + + + + Connect + + + + + C_reate… + + + + + None + + + + + WPA & WPA2 Personal + + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + + WEP 128-bit Passphrase + + + + + Dynamic WEP (802.1X) + + + + + WPA & WPA2 Enterprise + + + + + Tunneled TLS + + + + + Protected EAP (PEAP) + + + + + DlgConnHidWifiSecTls + + + + Connect to Hidden Wi-Fi Network + + + + + Add hidden Wi-Fi + + + + + Connection + + + + + Network name + + + + + Wi-Fi security + + + + + Authentication + + + + + Identity + + + + + Domain + + + + + CA certificate + + + + + CA certificate password + + + + + No CA certificate is required + + + + + User certificate + + + + + User certificate password + + + + + User private key + + + + + User key password + + + + + Cancel + + + + + Connect + + + + + C_reate… + + + + + + + + None + + + + + WPA & WPA2 Personal + + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + + WEP 128-bit Passphrase + + + + + Dynamic WEP (802.1X) + + + + + WPA & WPA2 Enterprise + + + + + Tunneled TLS + + + + + Protected EAP (PEAP) + + + + + + + Choose from file + + + + + DlgConnHidWifiSecTunnelTLS + + + + Connect to Hidden Wi-Fi Network + + + + + Add hidden Wi-Fi + + + + + Connection + + + + + Network name + + + + + Wi-Fi security + + + + + Authentication + + + + + Anonymous identity + + + + + Domain + + + + + CA certificate + + + + + CA certificate password + + + + + No CA certificate is required + + + + + Inner authentication + + + + + Username + + + + + Password + + + + + Cancel + + + + + Connect + + + + + C_reate… + + + + + + None + + + + + WPA & WPA2 Personal + + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + + WEP 128-bit Passphrase + + + + + Dynamic WEP (802.1X) + + + + + WPA & WPA2 Enterprise + + + + + Tunneled TLS + + + + + Protected EAP (PEAP) + + + + + Choose from file + + + + + DlgConnHidWifiWep + + + + Connect to Hidden Wi-Fi Network + + + + + Add hidden Wi-Fi + + + + + Connection + + + + + Network name + + + + + Wi-Fi security + + + + + Key + + + + + WEP index + + + + + Authentication + + + + + Cancel + + + + + Connect + + + + + C_reate… + + + + + None + + + + + WPA & WPA2 Personal + + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + + WEP 128-bit Passphrase + + + + + Dynamic WEP (802.1X) + + + + + WPA & WPA2 Enterprise + + + + + 1(default) + + + + + Open System + + + + + Shared Key + + + + + DlgConnHidWifiWpa + + + + Connect to Hidden Wi-Fi Network + + + + + Add Hidden Wi-Fi + + + + + Connection + + + + + Wi-Fi name + + + + + Wi-Fi security + + + + + Password + + + + + Cancel + + + + + Connect + + + + + C_reate… + + + + + None + + + + + WPA & WPA2 Personal + + + + + DlgHotspotCreate + + + + Dialog + + + + + Create Hotspot + + + + + Network name + + + + + Wi-Fi security + + + + + Password + + + + + Cancel + + + + + Ok + + + + + None + + + + + WPA & WPA2 Personal + + + + + InputInfos + + + + Get code + + + + + Recapture(60s) + + + + + Recapture(%1s) + + + + + Service exception... + + + + + Invaild parameters... + + + + + Unknown fault:%1 + + + + + KeyboardWidget + + + + KeyboardWidget + + + + + KylinNM + + + + kylin-nm + + + + + No usable network in the list + + + + + Inactivated LAN + + + + + Other WLAN + + + + + + LAN + + + + + + WLAN + + + + + HotSpot + + + + + FlyMode + + + + + + Advanced + + + + + Show KylinNM + + + + + No wireless card detected + + + + + Activated LAN + + + + + Activated WLAN + + + + + + + + + Not connected + + + + + + + + + + + + + Disconnected + + + + + + NetOn, + + + + + No Other Wired Network Scheme + + + + + No Other Wireless Network Scheme + + + + + Wired net is disconnected + + + + + Conn Ethernet Success + + + + + Conn Ethernet Fail + + + + + Conn Wifi Success + + + + + Confirm your Wi-Fi password or usable of wireless card + + + + + LockWidget + + + + Form + + + + + + Date + + + + + + Time + + + + + LoginOptionsWidget + + + Login Options + + + + + Identify device removed! + + + + + MyLineEdit + + + + + Verification code + + + + + OneConnForm + + + + Form + + + + + + + + Connect + + + + + Disconnect + + + + + Input Password... + + + + + Automatically join the network + + + + + Connect to Hidden Wi-Fi Network + + + + + None + + + + + WiFi Security: + + + + + Signal: + + + + + MAC: + + + + + Conn Wifi Failed + + + + + OneLancForm + + + + Form + + + + + + Connect + + + + + Disconnect + + + + + + No Configuration + + + + + IPv4: + + + + + IPv6: + + + + + BandWidth: + + + + + MAC: + + + + + PhoneAuthWidget + + + + Verification by phoneNum + + + + + 「 Use bound Phone number to verification 」 + + + + + + commit + + + + + 「 Use SMS to verification 」 + + + + + + Network not connected~ + + + + + Verification Code invalid! + + + + + Verification Code incorrect.Please retry! + + + + + Failed time over limit!Retry after 1 hour! + + + + + verifaction failed! + + + + + Network unavailable~ + + + + + PowerManager + + + Log Out + + + + + lock + + + + + + Restart + + + + + + Power Off + + + + + Suspend + + + + + Sleep + + + + + QObject + + + The screensaver is active. + + + + + The screensaver is inactive. + + + + + Screensaver + + + Picture does not exist + + + + + View + + + + + You have new notification + + + + + SleepTime + + + You have rested: + + + + + SwitchButtonGroup + + + uEduPWD + + + + + Wechat + + + + + TabletLockWidget + + + + + + + + Cancel + + + + + + Back + + + + + Skip + + + + + New password is the same as old + + + + + Reset password error:%1 + + + + + Please scan by correct WeChat + + + + + Utils + + + kylin network applet desktop message + + + + + VerificationWidget + + + Please scan by bound WeChat + + + + + VerticalVerificationWidget + + + Please scan by bound WeChat + + + + + WeChatAuthDialog + + + + Login by wechat + + + + + + 「 Use registered WeChat account to login 」 + + + + + + Verification by wechat + + + + + + 「 Use bound WeChat account to verification 」 + + + + + + Network not connected~ + + + + + Scan code successfully + + + + + Timeout!Try again! + + + + + main + + + Screensaver for ukui-screensaver + + + + + show on root window + + + + + show on window. + + + + + window id + + + + + Start command for the ukui ScreenSaver. + + + + + + + lock the screen immediately + + + + + query the status of the screen saver + + + + + unlock the screen saver + + + + + show the screensaver + + + + + Dialog for the ukui ScreenSaver. + + + + + activated by session idle signal + + + + + + lock the screen and show screensaver immediately + + + + + show screensaver immediately + + + + diff --git a/i18n_ts/bo_CN.ts b/i18n_ts/bo_CN.ts new file mode 100644 index 0000000..4135111 --- /dev/null +++ b/i18n_ts/bo_CN.ts @@ -0,0 +1,1679 @@ + + + + + AuthDialog + + Retry + ཡང་བསྐྱར་ཚོད་ལེན། + + + Please enter your password or enroll your fingerprint + གསང་ཨང་མནོན་པའམ་མཛུབ་རིམ་མནོན་རོགས། + + + Too many unsuccessful attempts,please enter password. + མཛུབ་རིས་ར་སྤྲོད་ཕམ་ཉེས་ཚད་ཆེས་ཆེ་ལས་བརྒལ་བ། ཁྱོས་གསང་ཨང་བཀོལ་ནས་ཟྭ་འབྱེད་རོགས། + + + Fingerprint authentication failed, you still have %1 verification opportunities + མཛུབ་རིས་ར་སྤྲོད་ཕམ་ཉེས་བྱུང་། ཁྱོད་ལ་ད་དུང་གོ་སྐབས་གཅིག་ཡོད། + + + Password: + གསང་ཨང་། + + + Biometric Authentication + སྐྱེ་དངོས་ངོས་འཛིན་ཀྱི་དཔང་ལེན། + + + Other Devices + སྒྲིག་ཆས་གཞན་དག + + + Password Authentication + གསང་ཨང་ར་སྤྲོད་གྲོས་ཆིངས། + + + Authentication failure, Please try again + དཔང་ལེན་བྱ་མ་ཐུབ། ཡང་བསྐྱར་ཚོད་ལྟ་བྱོས། + + + Please try again in %1 minutes. + + + + Please try again in %1 seconds. + + + + Account locked permanently. + + + + Verify face recognition or enter password to unlock + + + + Press fingerprint or enter password to unlock + + + + Verify voiceprint or enter password to unlock + + + + Verify finger vein or enter password to unlock + + + + Verify iris or enter password to unlock + + + + Password cannot be empty + + + + Password + + + + Login + + + + NET Exception + + + + Failed to verify %1, you still have %2 verification opportunities + + + + Use the bound wechat scanning code or enter the password to unlock + + + + Failed to verify %1, please enter password to unlock + + + + Unable to verify %1, please enter password to unlock + + + + Input Password + + + + + BiometricAuthWidget + + Identify failed, Please retry. + ངོས་འཛིན་མ་ཐུབ། ཡང་བསྐྱར་ཚོད་ལྟ་བྱོས། + + + Current device: + མིག་སྔའི་སྒྲིག་ཆས་རེའུ་མིག: + + + + BiometricDevicesWidget + + OK + གཏན་འཁེལ་བྱེད་པ། + + + Please select the biometric device + ཉིད་ཀྱིས་སྐྱེ་དངོས་སྒྲིག་ཆས་འདེམ་རོགས། + + + Device type: + སྒྲིག་ཆས་ཀྱི་རིགས། + + + Device name: + སྒྲིག་ཆས་ཀྱི་མིང་། + + + + ConfForm + + edit network + + + + LAN name: + + + + Method: + + + + Address: + + + + Netmask: + + + + Gateway: + + + + DNS 1: + + + + DNS 2: + + + + Edit Conn + + + + Auto(DHCP) + + + + Manual + + + + Cancel + + + + Save + + + + Ok + + + + Can not create new wired network for without wired card + + + + New network already created + + + + New network settings already finished + + + + New settings already effective + + + + Edit Network + + + + Add Wired Network + + + + + DeviceType + + Face + མིའི་གདོང་། + + + Iris + འཇའ་སྐྱི། + + + VoicePrint + སྒྲ་རིས། + + + FingerPrint + མཛུབ་རིས། + + + FingerVein + མཛུབ་མོའི་སྡོད་རྩ། + + + QRCode + + + + + DigitalAuthDialog + + LoginByUEdu + + + + ResetPWD? + + + + SetNewUEduPWD + + + + clear + + + + ConfirmNewUEduPWD + + + + The two password entries are inconsistent, please reset + + + + Password entered incorrectly, please try again + + + + + DlgConnHidWifi + + Connect to Hidden Wi-Fi Network + + + + Add Hidden Wi-Fi + + + + Connection + + + + Wi-Fi name + + + + Wi-Fi security + + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + + DlgConnHidWifiLeap + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Username + + + + Password + + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + + DlgConnHidWifiSecFast + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Authentication + + + + Anonymous identity + + + + Allow automatic PAC pro_visioning + + + + PAC file + + + + Inner authentication + + + + Username + + + + Password + + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + Tunneled TLS + + + + Protected EAP (PEAP) + + + + Anonymous + + + + Authenticated + + + + Both + + + + + DlgConnHidWifiSecLeap + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Authentication + + + + Username + + + + Password + + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + Tunneled TLS + + + + Protected EAP (PEAP) + + + + + DlgConnHidWifiSecPeap + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Authentication + + + + Anonymous identity + + + + Domain + + + + CA certificate + + + + CA certificate password + + + + No CA certificate is required + + + + PEAP version + + + + Inner authentication + + + + Username + + + + Password + + + + Cancel + + + + Connect + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + Tunneled TLS + + + + Protected EAP (PEAP) + + + + Choose from file + + + + Automatic + + + + Version 0 + + + + Version 1 + + + + + DlgConnHidWifiSecPwd + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Authentication + + + + Username + + + + Password + + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + Tunneled TLS + + + + Protected EAP (PEAP) + + + + + DlgConnHidWifiSecTls + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Authentication + + + + Identity + + + + Domain + + + + CA certificate + + + + CA certificate password + + + + No CA certificate is required + + + + User certificate + + + + User certificate password + + + + User private key + + + + User key password + + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + Tunneled TLS + + + + Protected EAP (PEAP) + + + + Choose from file + + + + + DlgConnHidWifiSecTunnelTLS + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Authentication + + + + Anonymous identity + + + + Domain + + + + CA certificate + + + + CA certificate password + + + + No CA certificate is required + + + + Inner authentication + + + + Username + + + + Password + + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + Tunneled TLS + + + + Protected EAP (PEAP) + + + + Choose from file + + + + + DlgConnHidWifiWep + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Key + + + + WEP index + + + + Authentication + + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + 1(default) + + + + Open System + + + + Shared Key + + + + + DlgConnHidWifiWpa + + Connect to Hidden Wi-Fi Network + + + + Add Hidden Wi-Fi + + + + Connection + + + + Wi-Fi name + + + + Wi-Fi security + + + + Password + + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + + DlgHotspotCreate + + Dialog + + + + Create Hotspot + + + + Network name + + + + Wi-Fi security + + + + Password + + + + Cancel + + + + Ok + + + + None + + + + WPA & WPA2 Personal + + + + + InputInfos + + Get code + + + + Recapture(60s) + + + + Recapture(%1s) + + + + Service exception... + + + + Invaild parameters... + + + + Unknown fault:%1 + + + + + KeyboardWidget + + KeyboardWidget + + + + + KylinNM + + kylin-nm + + + + No usable network in the list + + + + HotSpot + + + + FlyMode + + + + Advanced + + + + Show KylinNM + + + + No wireless card detected + + + + Not connected + + + + Disconnected + + + + NetOn, + + + + No Other Wired Network Scheme + + + + No Other Wireless Network Scheme + + + + Wired net is disconnected + + + + Conn Ethernet Success + + + + Conn Ethernet Fail + + + + Conn Wifi Success + + + + Confirm your Wi-Fi password or usable of wireless card + + + + Inactivated LAN + + + + LAN + + + + WLAN + + + + Activated LAN + + + + Activated WLAN + + + + Other WLAN + + + + + LockWidget + + Date + དུས་ཚོད། + + + Form + ནས་འོང་བ། + + + Time + དུས་ཚོད། + + + Guest + མགྲོན་པོ། + + + SwitchUser + བཀོལ་མི་བརྗེ་བ། + + + + LoginOptionsWidget + + Login Options + + + + Identify device removed! + + + + + MyLineEdit + + Verification code + + + + + OneConnForm + + Form + ནས་འོང་བ། + + + Connect + + + + Disconnect + + + + Input Password... + + + + Automatically join the network + + + + Connect to Hidden Wi-Fi Network + + + + None + + + + WiFi Security: + + + + Signal: + + + + MAC: + + + + Conn Wifi Failed + + + + + OneLancForm + + Form + ནས་འོང་བ། + + + Connect + + + + Disconnect + + + + No Configuration + + + + IPv4: + + + + IPv6: + + + + BandWidth: + + + + MAC: + + + + + PhoneAuthWidget + + Verification by phoneNum + + + + 「 Use bound Phone number to verification 」 + + + + commit + + + + 「 Use SMS to verification 」 + + + + Network not connected~ + + + + Verification Code invalid! + + + + Verification Code incorrect.Please retry! + + + + Failed time over limit!Retry after 1 hour! + + + + verifaction failed! + + + + Network unavailable~ + + + + + PowerManager + + Sleep + མལ་གསོ། + + + Log Out + རྩིས་ཐེམ་ནས་བསུབ་པ། + + + Lock Screen + བརྙན་ཡོལ་ཟྭ་རྒྱག།(_L) + + + Restart + བསྐྱར་སློང་། + + + Switch User + སྤྱོད་མཁན་བརྗེ་རེས། + + + Suspend + འགེལ་འཇོག + + + Power Off + འཁོར་ཁ་རྒྱག་པ། + + + lock + + + + + QObject + + The screensaver is inactive. + འཆར་ངོས་སྲུང་སྐྱོབ་བྱ་རིམ་སད་མི་འདུག + + + The screensaver is active. + འཆར་ངོས་སྲུང་སྐྱོབ་བྱ་རིམ་སད་འདུག + + + + Screensaver + + Automatic switching + རང་འགུལ་བརྗེ་སྤོར། + + + Picture does not exist + པར་རིས་མི་འདུག + + + Set as desktop wallpaper + ཅོག་ངོས་ཀྱི་གདོང་ཤོག་ཏུ་འཇོག་པ། + + + View + + + + You have new notification + + + + + SleepTime + + You have rested: + ཁྱེད་ཀྱིས་ ལ་ངལ་གསོས་ཟིན། + + + + SwitchButtonGroup + + uEduPWD + + + + Wechat + + + + + TabletLockWidget + + Cancel + + + + Back + + + + Skip + + + + New password is the same as old + + + + Reset password error:%1 + + + + Please scan by correct WeChat + + + + + Utils + + kylin network applet desktop message + + + + + VerificationWidget + + Please scan by bound WeChat + + + + + VerticalVerificationWidget + + Please scan by bound WeChat + + + + + WeChatAuthDialog + + Login by wechat + + + + 「 Use registered WeChat account to login 」 + + + + Verification by wechat + + + + 「 Use bound WeChat account to verification 」 + + + + Network not connected~ + + + + Scan code successfully + + + + Timeout!Try again! + + + + + main + + Start command for the ukui ScreenSaver. + ཡོལ་སྒོའི་ཁ་འབྱེད་བཀའ་བརྡ། + + + lock the screen immediately + མྱུར་དུ་འཆར་ངོས་ཀྱི་ཟྭ་རྒྱག་པ། + + + query the status of the screen saver + འཆར་ངོས་ལ་ཟྭ་བརྒྱབ་པའི་གནས་ཚུལ་ལེན་པ། + + + unlock the screen saver + འཆར་ངོས་ཀྱི་ཟྭ་ཕྱེ་བ། + + + Screensaver for ukui-screensaver + + + + show on root window + + + + show on window. + + + + window id + + + + show the screensaver + + + + Dialog for the ukui ScreenSaver. + + + + activated by session idle signal + + + + lock the screen and show screensaver immediately + + + + show screensaver immediately + + + + diff --git a/i18n_ts/es.ts b/i18n_ts/es.ts new file mode 100644 index 0000000..21c1487 --- /dev/null +++ b/i18n_ts/es.ts @@ -0,0 +1,1736 @@ + + + + + AuthDialog + + Form + Formar + + + More Devices + Más dispositivos + + + Biometric + Biometrico + + + Password + Contraseña + + + Retry + Procesar de nuevo + + + UnLock + Desbloquear + + + LoggedIn + Conectado + + + Password Incorrect, Please try again + Contraseña incorrecta, por favor intente de nuevo + + + Authentication failure, Please try again + + + + Please try again in %1 minutes. + + + + Please try again in %1 seconds. + + + + Account locked permanently. + + + + Password cannot be empty + + + + Login + + + + Password + + + + Verify face recognition or enter password to unlock + + + + Press fingerprint or enter password to unlock + + + + Verify voiceprint or enter password to unlock + + + + Verify finger vein or enter password to unlock + + + + Verify iris or enter password to unlock + + + + NET Exception + + + + Failed to verify %1, you still have %2 verification opportunities + + + + Use the bound wechat scanning code or enter the password to unlock + + + + Failed to verify %1, please enter password to unlock + + + + Unable to verify %1, please enter password to unlock + + + + Input Password + + + + + BioAuthWidget + + Form + Formar + + + TextLabel + TextLabel + + + More + Más + + + Retry + Procesar de nuevo + + + Password + Contraseña + + + + BioDevices + + FingerPrint + Huella dactilar + + + FingerVein + FingerVein + + + Iris + Iris + + + Face + Cara + + + VoicePrint + Impresión de voz + + + + BioDevicesWidget + + Form + Formar + + + Please select other biometric devices + Por favor seleccione otros dispositivos biométricos + + + Device Type: + Tipo de dispositivo: + + + Device Name: + Nombre del dispositivo: + + + + BiometricAuthWidget + + Current device: + + + + Identify failed, Please retry. + + + + + BiometricDevicesWidget + + Please select the biometric device + + + + Device type: + + + + Device name: + + + + OK + + + + + ConfForm + + edit network + + + + LAN name: + + + + Method: + + + + Address: + + + + Netmask: + + + + Gateway: + + + + DNS 1: + + + + DNS 2: + + + + Edit Conn + + + + Auto(DHCP) + + + + Manual + + + + Cancel + + + + Save + + + + Ok + + + + Can not create new wired network for without wired card + + + + New network already created + + + + New network settings already finished + + + + New settings already effective + + + + Edit Network + + + + Add Wired Network + + + + + DeviceType + + FingerPrint + Huella dactilar + + + FingerVein + FingerVein + + + Iris + Iris + + + Face + Cara + + + VoicePrint + Impresión de voz + + + QRCode + + + + + DigitalAuthDialog + + LoginByUEdu + + + + ResetPWD? + + + + SetNewUEduPWD + + + + clear + + + + ConfirmNewUEduPWD + + + + The two password entries are inconsistent, please reset + + + + Password entered incorrectly, please try again + + + + + DlgConnHidWifi + + Connect to Hidden Wi-Fi Network + + + + Add Hidden Wi-Fi + + + + Connection + + + + Wi-Fi name + + + + Wi-Fi security + + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + + DlgConnHidWifiLeap + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Username + + + + Password + Contraseña + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + + DlgConnHidWifiSecFast + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Authentication + + + + Anonymous identity + + + + Allow automatic PAC pro_visioning + + + + PAC file + + + + Inner authentication + + + + Username + + + + Password + Contraseña + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + Tunneled TLS + + + + Protected EAP (PEAP) + + + + Anonymous + + + + Authenticated + + + + Both + + + + + DlgConnHidWifiSecLeap + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Authentication + + + + Username + + + + Password + Contraseña + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + Tunneled TLS + + + + Protected EAP (PEAP) + + + + + DlgConnHidWifiSecPeap + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Authentication + + + + Anonymous identity + + + + Domain + + + + CA certificate + + + + CA certificate password + + + + No CA certificate is required + + + + PEAP version + + + + Inner authentication + + + + Username + + + + Password + Contraseña + + + Cancel + + + + Connect + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + Tunneled TLS + + + + Protected EAP (PEAP) + + + + Choose from file + + + + Automatic + + + + Version 0 + + + + Version 1 + + + + + DlgConnHidWifiSecPwd + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Authentication + + + + Username + + + + Password + Contraseña + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + Tunneled TLS + + + + Protected EAP (PEAP) + + + + + DlgConnHidWifiSecTls + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Authentication + + + + Identity + + + + Domain + + + + CA certificate + + + + CA certificate password + + + + No CA certificate is required + + + + User certificate + + + + User certificate password + + + + User private key + + + + User key password + + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + Tunneled TLS + + + + Protected EAP (PEAP) + + + + Choose from file + + + + + DlgConnHidWifiSecTunnelTLS + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Authentication + + + + Anonymous identity + + + + Domain + + + + CA certificate + + + + CA certificate password + + + + No CA certificate is required + + + + Inner authentication + + + + Username + + + + Password + Contraseña + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + Tunneled TLS + + + + Protected EAP (PEAP) + + + + Choose from file + + + + + DlgConnHidWifiWep + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Key + + + + WEP index + + + + Authentication + + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + 1(default) + + + + Open System + + + + Shared Key + + + + + DlgConnHidWifiWpa + + Connect to Hidden Wi-Fi Network + + + + Add Hidden Wi-Fi + + + + Connection + + + + Wi-Fi name + + + + Wi-Fi security + + + + Password + Contraseña + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + + DlgHotspotCreate + + Dialog + + + + Create Hotspot + + + + Network name + + + + Wi-Fi security + + + + Password + Contraseña + + + Cancel + + + + Ok + + + + None + + + + WPA & WPA2 Personal + + + + + InputInfos + + Get code + + + + Recapture(60s) + + + + Recapture(%1s) + + + + Service exception... + + + + Invaild parameters... + + + + Unknown fault:%1 + + + + + KeyboardWidget + + KeyboardWidget + TecladoWidget + + + + KylinNM + + kylin-nm + + + + No usable network in the list + + + + HotSpot + + + + FlyMode + + + + Advanced + + + + Show KylinNM + + + + No wireless card detected + + + + Not connected + + + + Disconnected + + + + NetOn, + + + + No Other Wired Network Scheme + + + + No Other Wireless Network Scheme + + + + Wired net is disconnected + + + + Conn Ethernet Success + + + + Conn Ethernet Fail + + + + Conn Wifi Success + + + + Confirm your Wi-Fi password or usable of wireless card + + + + Inactivated LAN + + + + LAN + + + + WLAN + + + + Activated LAN + + + + Activated WLAN + + + + Other WLAN + + + + + LockWidget + + Form + Formar + + + Date + Fecha + + + Time + Hora + + + Guest + Huésped + + + SwitchUser + Cambiar de usuario + + + + LoginOptionsWidget + + Login Options + + + + Password + Contraseña + + + Identify device removed! + + + + + MyLineEdit + + Verification code + + + + + OneConnForm + + Form + Formar + + + Connect + + + + Disconnect + + + + Input Password... + + + + Automatically join the network + + + + Connect to Hidden Wi-Fi Network + + + + None + + + + WiFi Security: + + + + Signal: + + + + MAC: + + + + Conn Wifi Failed + + + + + OneLancForm + + Form + Formar + + + Connect + + + + Disconnect + + + + No Configuration + + + + IPv4: + + + + IPv6: + + + + BandWidth: + + + + MAC: + + + + + PhoneAuthWidget + + Verification by phoneNum + + + + 「 Use bound Phone number to verification 」 + + + + commit + + + + 「 Use SMS to verification 」 + + + + Network not connected~ + + + + Verification Code invalid! + + + + Verification Code incorrect.Please retry! + + + + Failed time over limit!Retry after 1 hour! + + + + verifaction failed! + + + + Network unavailable~ + + + + + PowerManager + + SwitchUser + Cambiar de usuario + + + Log Out + + + + Restart + + + + Power Off + + + + Suspend + + + + Sleep + + + + lock + + + + + QObject + + The screensaver is active. + + + + The screensaver is inactive. + + + + + Screensaver + + Picture does not exist + + + + View + + + + You have new notification + + + + + SleepTime + + You have rested: + + + + + SwitchButtonGroup + + uEduPWD + + + + Wechat + + + + + TabletLockWidget + + Cancel + + + + Back + + + + Skip + + + + New password is the same as old + + + + Reset password error:%1 + + + + Please scan by correct WeChat + + + + + Utils + + kylin network applet desktop message + + + + + VerificationWidget + + Please scan by bound WeChat + + + + + VerticalVerificationWidget + + Please scan by bound WeChat + + + + + WeChatAuthDialog + + Login by wechat + + + + 「 Use registered WeChat account to login 」 + + + + Verification by wechat + + + + 「 Use bound WeChat account to verification 」 + + + + Network not connected~ + + + + Scan code successfully + + + + Timeout!Try again! + + + + + main + + Start command for the ukui ScreenSaver. + Comando de inicio para el protector de pantalla de ukui. + + + lock the screen immediately + bloquea la pantalla inmediatamente + + + Dialog for the ukui ScreenSaver. + Diálogo para el protector de pantalla de ukui. + + + activated by session idle signal + activado por la señal de inactividad de la sesión + + + Screensaver for ukui-screensaver + + + + show on root window + + + + show on window. + + + + window id + + + + query the status of the screen saver + + + + lock the screen and show screensaver immediately + + + + unlock the screen saver + + + + show the screensaver + + + + show screensaver immediately + + + + diff --git a/i18n_ts/fr.ts b/i18n_ts/fr.ts new file mode 100644 index 0000000..c4588df --- /dev/null +++ b/i18n_ts/fr.ts @@ -0,0 +1,1736 @@ + + + + + AuthDialog + + Form + Forme + + + More Devices + Plus d'appareils + + + Biometric + Biométrique + + + Password + Mot de passe + + + Retry + Réessayez + + + UnLock + Ouvrir + + + LoggedIn + Connecté + + + Password Incorrect, Please try again + Mot de passe incorrect, veuillez réessayer + + + Authentication failure, Please try again + + + + Please try again in %1 minutes. + + + + Please try again in %1 seconds. + + + + Account locked permanently. + + + + Password cannot be empty + + + + Login + + + + Password + + + + Verify face recognition or enter password to unlock + + + + Press fingerprint or enter password to unlock + + + + Verify voiceprint or enter password to unlock + + + + Verify finger vein or enter password to unlock + + + + Verify iris or enter password to unlock + + + + NET Exception + + + + Failed to verify %1, you still have %2 verification opportunities + + + + Use the bound wechat scanning code or enter the password to unlock + + + + Failed to verify %1, please enter password to unlock + + + + Unable to verify %1, please enter password to unlock + + + + Input Password + + + + + BioAuthWidget + + Form + Forme + + + TextLabel + TextLabel + + + More + Plus + + + Retry + Réessayez + + + Password + Mot de passe + + + + BioDevices + + FingerPrint + Empreinte digitale + + + FingerVein + FingerVein + + + Iris + Iris + + + Face + Visage + + + VoicePrint + VoicePrint + + + + BioDevicesWidget + + Form + Forme + + + Please select other biometric devices + Veuillez sélectionner d'autres appareils biométriques + + + Device Type: + Type d'appareil: + + + Device Name: + Nom de l'appareil: + + + + BiometricAuthWidget + + Current device: + + + + Identify failed, Please retry. + + + + + BiometricDevicesWidget + + Please select the biometric device + + + + Device type: + + + + Device name: + + + + OK + + + + + ConfForm + + edit network + + + + LAN name: + + + + Method: + + + + Address: + + + + Netmask: + + + + Gateway: + + + + DNS 1: + + + + DNS 2: + + + + Edit Conn + + + + Auto(DHCP) + + + + Manual + + + + Cancel + + + + Save + + + + Ok + + + + Can not create new wired network for without wired card + + + + New network already created + + + + New network settings already finished + + + + New settings already effective + + + + Edit Network + + + + Add Wired Network + + + + + DeviceType + + FingerPrint + Empreinte digitale + + + FingerVein + FingerVein + + + Iris + Iris + + + Face + Visage + + + VoicePrint + VoicePrint + + + QRCode + + + + + DigitalAuthDialog + + LoginByUEdu + + + + ResetPWD? + + + + SetNewUEduPWD + + + + clear + + + + ConfirmNewUEduPWD + + + + The two password entries are inconsistent, please reset + + + + Password entered incorrectly, please try again + + + + + DlgConnHidWifi + + Connect to Hidden Wi-Fi Network + + + + Add Hidden Wi-Fi + + + + Connection + + + + Wi-Fi name + + + + Wi-Fi security + + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + + DlgConnHidWifiLeap + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Username + + + + Password + Mot de passe + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + + DlgConnHidWifiSecFast + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Authentication + + + + Anonymous identity + + + + Allow automatic PAC pro_visioning + + + + PAC file + + + + Inner authentication + + + + Username + + + + Password + Mot de passe + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + Tunneled TLS + + + + Protected EAP (PEAP) + + + + Anonymous + + + + Authenticated + + + + Both + + + + + DlgConnHidWifiSecLeap + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Authentication + + + + Username + + + + Password + Mot de passe + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + Tunneled TLS + + + + Protected EAP (PEAP) + + + + + DlgConnHidWifiSecPeap + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Authentication + + + + Anonymous identity + + + + Domain + + + + CA certificate + + + + CA certificate password + + + + No CA certificate is required + + + + PEAP version + + + + Inner authentication + + + + Username + + + + Password + Mot de passe + + + Cancel + + + + Connect + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + Tunneled TLS + + + + Protected EAP (PEAP) + + + + Choose from file + + + + Automatic + + + + Version 0 + + + + Version 1 + + + + + DlgConnHidWifiSecPwd + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Authentication + + + + Username + + + + Password + Mot de passe + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + Tunneled TLS + + + + Protected EAP (PEAP) + + + + + DlgConnHidWifiSecTls + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Authentication + + + + Identity + + + + Domain + + + + CA certificate + + + + CA certificate password + + + + No CA certificate is required + + + + User certificate + + + + User certificate password + + + + User private key + + + + User key password + + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + Tunneled TLS + + + + Protected EAP (PEAP) + + + + Choose from file + + + + + DlgConnHidWifiSecTunnelTLS + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Authentication + + + + Anonymous identity + + + + Domain + + + + CA certificate + + + + CA certificate password + + + + No CA certificate is required + + + + Inner authentication + + + + Username + + + + Password + Mot de passe + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + Tunneled TLS + + + + Protected EAP (PEAP) + + + + Choose from file + + + + + DlgConnHidWifiWep + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Key + + + + WEP index + + + + Authentication + + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + 1(default) + + + + Open System + + + + Shared Key + + + + + DlgConnHidWifiWpa + + Connect to Hidden Wi-Fi Network + + + + Add Hidden Wi-Fi + + + + Connection + + + + Wi-Fi name + + + + Wi-Fi security + + + + Password + Mot de passe + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + + DlgHotspotCreate + + Dialog + + + + Create Hotspot + + + + Network name + + + + Wi-Fi security + + + + Password + Mot de passe + + + Cancel + + + + Ok + + + + None + + + + WPA & WPA2 Personal + + + + + InputInfos + + Get code + + + + Recapture(60s) + + + + Recapture(%1s) + + + + Service exception... + + + + Invaild parameters... + + + + Unknown fault:%1 + + + + + KeyboardWidget + + KeyboardWidget + KeyboardWidget + + + + KylinNM + + kylin-nm + + + + No usable network in the list + + + + HotSpot + + + + FlyMode + + + + Advanced + + + + Show KylinNM + + + + No wireless card detected + + + + Not connected + + + + Disconnected + + + + NetOn, + + + + No Other Wired Network Scheme + + + + No Other Wireless Network Scheme + + + + Wired net is disconnected + + + + Conn Ethernet Success + + + + Conn Ethernet Fail + + + + Conn Wifi Success + + + + Confirm your Wi-Fi password or usable of wireless card + + + + Inactivated LAN + + + + LAN + + + + WLAN + + + + Activated LAN + + + + Activated WLAN + + + + Other WLAN + + + + + LockWidget + + Form + Forme + + + Date + Rendez-vous amoureux + + + Time + Temps + + + Guest + Client + + + SwitchUser + Changer d'utilisateur + + + + LoginOptionsWidget + + Login Options + + + + Password + Mot de passe + + + Identify device removed! + + + + + MyLineEdit + + Verification code + + + + + OneConnForm + + Form + Forme + + + Connect + + + + Disconnect + + + + Input Password... + + + + Automatically join the network + + + + Connect to Hidden Wi-Fi Network + + + + None + + + + WiFi Security: + + + + Signal: + + + + MAC: + + + + Conn Wifi Failed + + + + + OneLancForm + + Form + Forme + + + Connect + + + + Disconnect + + + + No Configuration + + + + IPv4: + + + + IPv6: + + + + BandWidth: + + + + MAC: + + + + + PhoneAuthWidget + + Verification by phoneNum + + + + 「 Use bound Phone number to verification 」 + + + + commit + + + + 「 Use SMS to verification 」 + + + + Network not connected~ + + + + Verification Code invalid! + + + + Verification Code incorrect.Please retry! + + + + Failed time over limit!Retry after 1 hour! + + + + verifaction failed! + + + + Network unavailable~ + + + + + PowerManager + + SwitchUser + Changer d'utilisateur + + + Log Out + + + + Restart + + + + Power Off + + + + Suspend + + + + Sleep + + + + lock + + + + + QObject + + The screensaver is active. + + + + The screensaver is inactive. + + + + + Screensaver + + Picture does not exist + + + + View + + + + You have new notification + + + + + SleepTime + + You have rested: + + + + + SwitchButtonGroup + + uEduPWD + + + + Wechat + + + + + TabletLockWidget + + Cancel + + + + Back + + + + Skip + + + + New password is the same as old + + + + Reset password error:%1 + + + + Please scan by correct WeChat + + + + + Utils + + kylin network applet desktop message + + + + + VerificationWidget + + Please scan by bound WeChat + + + + + VerticalVerificationWidget + + Please scan by bound WeChat + + + + + WeChatAuthDialog + + Login by wechat + + + + 「 Use registered WeChat account to login 」 + + + + Verification by wechat + + + + 「 Use bound WeChat account to verification 」 + + + + Network not connected~ + + + + Scan code successfully + + + + Timeout!Try again! + + + + + main + + Start command for the ukui ScreenSaver. + Commande de démarrage pour l'ukui ScreenSaver. + + + lock the screen immediately + verrouiller l'écran immédiatement + + + Dialog for the ukui ScreenSaver. + Boîte de dialogue pour l'écran de veille ukui. + + + activated by session idle signal + activé par le signal d'inactivité de la session + + + Screensaver for ukui-screensaver + + + + show on root window + + + + show on window. + + + + window id + + + + query the status of the screen saver + + + + lock the screen and show screensaver immediately + + + + unlock the screen saver + + + + show the screensaver + + + + show screensaver immediately + + + + diff --git a/i18n_ts/pt.ts b/i18n_ts/pt.ts new file mode 100644 index 0000000..2de6ea7 --- /dev/null +++ b/i18n_ts/pt.ts @@ -0,0 +1,1736 @@ + + + + + AuthDialog + + Form + Formato + + + More Devices + Mais dispositivos + + + Biometric + Biométrico + + + Password + Senha + + + Retry + Tente novamente + + + UnLock + Desbloquear + + + LoggedIn + Logado + + + Password Incorrect, Please try again + Senha incorreta, por favor tente novamente + + + Authentication failure, Please try again + + + + Please try again in %1 minutes. + + + + Please try again in %1 seconds. + + + + Account locked permanently. + + + + Password cannot be empty + + + + Login + + + + Password + + + + Verify face recognition or enter password to unlock + + + + Press fingerprint or enter password to unlock + + + + Verify voiceprint or enter password to unlock + + + + Verify finger vein or enter password to unlock + + + + Verify iris or enter password to unlock + + + + NET Exception + + + + Failed to verify %1, you still have %2 verification opportunities + + + + Use the bound wechat scanning code or enter the password to unlock + + + + Failed to verify %1, please enter password to unlock + + + + Unable to verify %1, please enter password to unlock + + + + Input Password + + + + + BioAuthWidget + + Form + Formato + + + TextLabel + TextLabel + + + More + Mais + + + Retry + Tente novamente + + + Password + Senha + + + + BioDevices + + FingerPrint + Impressão digital + + + FingerVein + FingerVein + + + Iris + Íris + + + Face + Face + + + VoicePrint + VoicePrint + + + + BioDevicesWidget + + Form + Formato + + + Please select other biometric devices + Por favor, selecione outros dispositivos biométricos + + + Device Type: + Tipo de dispositivo: + + + Device Name: + Nome do dispositivo: + + + + BiometricAuthWidget + + Current device: + + + + Identify failed, Please retry. + + + + + BiometricDevicesWidget + + Please select the biometric device + + + + Device type: + + + + Device name: + + + + OK + + + + + ConfForm + + edit network + + + + LAN name: + + + + Method: + + + + Address: + + + + Netmask: + + + + Gateway: + + + + DNS 1: + + + + DNS 2: + + + + Edit Conn + + + + Auto(DHCP) + + + + Manual + + + + Cancel + + + + Save + + + + Ok + + + + Can not create new wired network for without wired card + + + + New network already created + + + + New network settings already finished + + + + New settings already effective + + + + Edit Network + + + + Add Wired Network + + + + + DeviceType + + FingerPrint + Impressão digital + + + FingerVein + FingerVein + + + Iris + Íris + + + Face + Face + + + VoicePrint + VoicePrint + + + QRCode + + + + + DigitalAuthDialog + + LoginByUEdu + + + + ResetPWD? + + + + SetNewUEduPWD + + + + clear + + + + ConfirmNewUEduPWD + + + + The two password entries are inconsistent, please reset + + + + Password entered incorrectly, please try again + + + + + DlgConnHidWifi + + Connect to Hidden Wi-Fi Network + + + + Add Hidden Wi-Fi + + + + Connection + + + + Wi-Fi name + + + + Wi-Fi security + + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + + DlgConnHidWifiLeap + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Username + + + + Password + Senha + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + + DlgConnHidWifiSecFast + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Authentication + + + + Anonymous identity + + + + Allow automatic PAC pro_visioning + + + + PAC file + + + + Inner authentication + + + + Username + + + + Password + Senha + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + Tunneled TLS + + + + Protected EAP (PEAP) + + + + Anonymous + + + + Authenticated + + + + Both + + + + + DlgConnHidWifiSecLeap + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Authentication + + + + Username + + + + Password + Senha + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + Tunneled TLS + + + + Protected EAP (PEAP) + + + + + DlgConnHidWifiSecPeap + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Authentication + + + + Anonymous identity + + + + Domain + + + + CA certificate + + + + CA certificate password + + + + No CA certificate is required + + + + PEAP version + + + + Inner authentication + + + + Username + + + + Password + Senha + + + Cancel + + + + Connect + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + Tunneled TLS + + + + Protected EAP (PEAP) + + + + Choose from file + + + + Automatic + + + + Version 0 + + + + Version 1 + + + + + DlgConnHidWifiSecPwd + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Authentication + + + + Username + + + + Password + Senha + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + Tunneled TLS + + + + Protected EAP (PEAP) + + + + + DlgConnHidWifiSecTls + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Authentication + + + + Identity + + + + Domain + + + + CA certificate + + + + CA certificate password + + + + No CA certificate is required + + + + User certificate + + + + User certificate password + + + + User private key + + + + User key password + + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + Tunneled TLS + + + + Protected EAP (PEAP) + + + + Choose from file + + + + + DlgConnHidWifiSecTunnelTLS + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Authentication + + + + Anonymous identity + + + + Domain + + + + CA certificate + + + + CA certificate password + + + + No CA certificate is required + + + + Inner authentication + + + + Username + + + + Password + Senha + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + Tunneled TLS + + + + Protected EAP (PEAP) + + + + Choose from file + + + + + DlgConnHidWifiWep + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Key + + + + WEP index + + + + Authentication + + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + 1(default) + + + + Open System + + + + Shared Key + + + + + DlgConnHidWifiWpa + + Connect to Hidden Wi-Fi Network + + + + Add Hidden Wi-Fi + + + + Connection + + + + Wi-Fi name + + + + Wi-Fi security + + + + Password + Senha + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + + DlgHotspotCreate + + Dialog + + + + Create Hotspot + + + + Network name + + + + Wi-Fi security + + + + Password + Senha + + + Cancel + + + + Ok + + + + None + + + + WPA & WPA2 Personal + + + + + InputInfos + + Get code + + + + Recapture(60s) + + + + Recapture(%1s) + + + + Service exception... + + + + Invaild parameters... + + + + Unknown fault:%1 + + + + + KeyboardWidget + + KeyboardWidget + KeyboardWidget + + + + KylinNM + + kylin-nm + + + + No usable network in the list + + + + HotSpot + + + + FlyMode + + + + Advanced + + + + Show KylinNM + + + + No wireless card detected + + + + Not connected + + + + Disconnected + + + + NetOn, + + + + No Other Wired Network Scheme + + + + No Other Wireless Network Scheme + + + + Wired net is disconnected + + + + Conn Ethernet Success + + + + Conn Ethernet Fail + + + + Conn Wifi Success + + + + Confirm your Wi-Fi password or usable of wireless card + + + + Inactivated LAN + + + + LAN + + + + WLAN + + + + Activated LAN + + + + Activated WLAN + + + + Other WLAN + + + + + LockWidget + + Form + Formato + + + Date + Encontro + + + Time + Tempo + + + Guest + Convidado + + + SwitchUser + Mudar de utilizador + + + + LoginOptionsWidget + + Login Options + + + + Password + Senha + + + Identify device removed! + + + + + MyLineEdit + + Verification code + + + + + OneConnForm + + Form + Formato + + + Connect + + + + Disconnect + + + + Input Password... + + + + Automatically join the network + + + + Connect to Hidden Wi-Fi Network + + + + None + + + + WiFi Security: + + + + Signal: + + + + MAC: + + + + Conn Wifi Failed + + + + + OneLancForm + + Form + Formato + + + Connect + + + + Disconnect + + + + No Configuration + + + + IPv4: + + + + IPv6: + + + + BandWidth: + + + + MAC: + + + + + PhoneAuthWidget + + Verification by phoneNum + + + + 「 Use bound Phone number to verification 」 + + + + commit + + + + 「 Use SMS to verification 」 + + + + Network not connected~ + + + + Verification Code invalid! + + + + Verification Code incorrect.Please retry! + + + + Failed time over limit!Retry after 1 hour! + + + + verifaction failed! + + + + Network unavailable~ + + + + + PowerManager + + SwitchUser + Mudar de utilizador + + + Log Out + + + + Restart + + + + Power Off + + + + Suspend + + + + Sleep + + + + lock + + + + + QObject + + The screensaver is active. + + + + The screensaver is inactive. + + + + + Screensaver + + Picture does not exist + + + + View + + + + You have new notification + + + + + SleepTime + + You have rested: + + + + + SwitchButtonGroup + + uEduPWD + + + + Wechat + + + + + TabletLockWidget + + Cancel + + + + Back + + + + Skip + + + + New password is the same as old + + + + Reset password error:%1 + + + + Please scan by correct WeChat + + + + + Utils + + kylin network applet desktop message + + + + + VerificationWidget + + Please scan by bound WeChat + + + + + VerticalVerificationWidget + + Please scan by bound WeChat + + + + + WeChatAuthDialog + + Login by wechat + + + + 「 Use registered WeChat account to login 」 + + + + Verification by wechat + + + + 「 Use bound WeChat account to verification 」 + + + + Network not connected~ + + + + Scan code successfully + + + + Timeout!Try again! + + + + + main + + Start command for the ukui ScreenSaver. + Inicie o comando para o ScreenSaver do ukui. + + + lock the screen immediately + bloquear a tela imediatamente + + + Dialog for the ukui ScreenSaver. + Diálogo para o ScreenSaver ukui. + + + activated by session idle signal + ativado por sinal ocioso de sessão + + + Screensaver for ukui-screensaver + + + + show on root window + + + + show on window. + + + + window id + + + + query the status of the screen saver + + + + lock the screen and show screensaver immediately + + + + unlock the screen saver + + + + show the screensaver + + + + show screensaver immediately + + + + diff --git a/i18n_ts/ru.ts b/i18n_ts/ru.ts new file mode 100644 index 0000000..f3ab806 --- /dev/null +++ b/i18n_ts/ru.ts @@ -0,0 +1,1736 @@ + + + + + AuthDialog + + Form + форма + + + More Devices + Дополнительные устройства + + + Biometric + Биометрические + + + Password + пароль + + + Retry + Retry + + + UnLock + отпереть + + + LoggedIn + LoggedIn + + + Password Incorrect, Please try again + Пароль неверен, повторите попытку + + + Authentication failure, Please try again + + + + Please try again in %1 minutes. + + + + Please try again in %1 seconds. + + + + Account locked permanently. + + + + Password cannot be empty + + + + Login + + + + Password + + + + Verify face recognition or enter password to unlock + + + + Press fingerprint or enter password to unlock + + + + Verify voiceprint or enter password to unlock + + + + Verify finger vein or enter password to unlock + + + + Verify iris or enter password to unlock + + + + NET Exception + + + + Failed to verify %1, you still have %2 verification opportunities + + + + Use the bound wechat scanning code or enter the password to unlock + + + + Failed to verify %1, please enter password to unlock + + + + Unable to verify %1, please enter password to unlock + + + + Input Password + + + + + BioAuthWidget + + Form + форма + + + TextLabel + TextLabel + + + More + Больше + + + Retry + Retry + + + Password + пароль + + + + BioDevices + + FingerPrint + FingerPrint + + + FingerVein + FingerVein + + + Iris + Ирис + + + Face + Лицо + + + VoicePrint + Voiceprint + + + + BioDevicesWidget + + Form + форма + + + Please select other biometric devices + Выберите другие биометрические устройства + + + Device Type: + Тип устройства: + + + Device Name: + Имя устройства: + + + + BiometricAuthWidget + + Current device: + + + + Identify failed, Please retry. + + + + + BiometricDevicesWidget + + Please select the biometric device + + + + Device type: + + + + Device name: + + + + OK + + + + + ConfForm + + edit network + + + + LAN name: + + + + Method: + + + + Address: + + + + Netmask: + + + + Gateway: + + + + DNS 1: + + + + DNS 2: + + + + Edit Conn + + + + Auto(DHCP) + + + + Manual + + + + Cancel + + + + Save + + + + Ok + + + + Can not create new wired network for without wired card + + + + New network already created + + + + New network settings already finished + + + + New settings already effective + + + + Edit Network + + + + Add Wired Network + + + + + DeviceType + + FingerPrint + FingerPrint + + + FingerVein + FingerVein + + + Iris + Ирис + + + Face + Лицо + + + VoicePrint + Voiceprint + + + QRCode + + + + + DigitalAuthDialog + + LoginByUEdu + + + + ResetPWD? + + + + SetNewUEduPWD + + + + clear + + + + ConfirmNewUEduPWD + + + + The two password entries are inconsistent, please reset + + + + Password entered incorrectly, please try again + + + + + DlgConnHidWifi + + Connect to Hidden Wi-Fi Network + + + + Add Hidden Wi-Fi + + + + Connection + + + + Wi-Fi name + + + + Wi-Fi security + + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + + DlgConnHidWifiLeap + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Username + + + + Password + пароль + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + + DlgConnHidWifiSecFast + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Authentication + + + + Anonymous identity + + + + Allow automatic PAC pro_visioning + + + + PAC file + + + + Inner authentication + + + + Username + + + + Password + пароль + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + Tunneled TLS + + + + Protected EAP (PEAP) + + + + Anonymous + + + + Authenticated + + + + Both + + + + + DlgConnHidWifiSecLeap + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Authentication + + + + Username + + + + Password + пароль + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + Tunneled TLS + + + + Protected EAP (PEAP) + + + + + DlgConnHidWifiSecPeap + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Authentication + + + + Anonymous identity + + + + Domain + + + + CA certificate + + + + CA certificate password + + + + No CA certificate is required + + + + PEAP version + + + + Inner authentication + + + + Username + + + + Password + пароль + + + Cancel + + + + Connect + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + Tunneled TLS + + + + Protected EAP (PEAP) + + + + Choose from file + + + + Automatic + + + + Version 0 + + + + Version 1 + + + + + DlgConnHidWifiSecPwd + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Authentication + + + + Username + + + + Password + пароль + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + Tunneled TLS + + + + Protected EAP (PEAP) + + + + + DlgConnHidWifiSecTls + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Authentication + + + + Identity + + + + Domain + + + + CA certificate + + + + CA certificate password + + + + No CA certificate is required + + + + User certificate + + + + User certificate password + + + + User private key + + + + User key password + + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + Tunneled TLS + + + + Protected EAP (PEAP) + + + + Choose from file + + + + + DlgConnHidWifiSecTunnelTLS + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Authentication + + + + Anonymous identity + + + + Domain + + + + CA certificate + + + + CA certificate password + + + + No CA certificate is required + + + + Inner authentication + + + + Username + + + + Password + пароль + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + Tunneled TLS + + + + Protected EAP (PEAP) + + + + Choose from file + + + + + DlgConnHidWifiWep + + Connect to Hidden Wi-Fi Network + + + + Add hidden Wi-Fi + + + + Connection + + + + Network name + + + + Wi-Fi security + + + + Key + + + + WEP index + + + + Authentication + + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + WEP 128-bit Passphrase + + + + Dynamic WEP (802.1X) + + + + WPA & WPA2 Enterprise + + + + 1(default) + + + + Open System + + + + Shared Key + + + + + DlgConnHidWifiWpa + + Connect to Hidden Wi-Fi Network + + + + Add Hidden Wi-Fi + + + + Connection + + + + Wi-Fi name + + + + Wi-Fi security + + + + Password + пароль + + + Cancel + + + + Connect + + + + C_reate… + + + + None + + + + WPA & WPA2 Personal + + + + + DlgHotspotCreate + + Dialog + + + + Create Hotspot + + + + Network name + + + + Wi-Fi security + + + + Password + пароль + + + Cancel + + + + Ok + + + + None + + + + WPA & WPA2 Personal + + + + + InputInfos + + Get code + + + + Recapture(60s) + + + + Recapture(%1s) + + + + Service exception... + + + + Invaild parameters... + + + + Unknown fault:%1 + + + + + KeyboardWidget + + KeyboardWidget + KeyboardWidget + + + + KylinNM + + kylin-nm + + + + No usable network in the list + + + + HotSpot + + + + FlyMode + + + + Advanced + + + + Show KylinNM + + + + No wireless card detected + + + + Not connected + + + + Disconnected + + + + NetOn, + + + + No Other Wired Network Scheme + + + + No Other Wireless Network Scheme + + + + Wired net is disconnected + + + + Conn Ethernet Success + + + + Conn Ethernet Fail + + + + Conn Wifi Success + + + + Confirm your Wi-Fi password or usable of wireless card + + + + Inactivated LAN + + + + LAN + + + + WLAN + + + + Activated LAN + + + + Activated WLAN + + + + Other WLAN + + + + + LockWidget + + Form + форма + + + Date + Дата + + + Time + Время + + + Guest + гость + + + SwitchUser + Сменить пользователя + + + + LoginOptionsWidget + + Login Options + + + + Password + пароль + + + Identify device removed! + + + + + MyLineEdit + + Verification code + + + + + OneConnForm + + Form + форма + + + Connect + + + + Disconnect + + + + Input Password... + + + + Automatically join the network + + + + Connect to Hidden Wi-Fi Network + + + + None + + + + WiFi Security: + + + + Signal: + + + + MAC: + + + + Conn Wifi Failed + + + + + OneLancForm + + Form + форма + + + Connect + + + + Disconnect + + + + No Configuration + + + + IPv4: + + + + IPv6: + + + + BandWidth: + + + + MAC: + + + + + PhoneAuthWidget + + Verification by phoneNum + + + + 「 Use bound Phone number to verification 」 + + + + commit + + + + 「 Use SMS to verification 」 + + + + Network not connected~ + + + + Verification Code invalid! + + + + Verification Code incorrect.Please retry! + + + + Failed time over limit!Retry after 1 hour! + + + + verifaction failed! + + + + Network unavailable~ + + + + + PowerManager + + SwitchUser + Сменить пользователя + + + Log Out + + + + Restart + + + + Power Off + + + + Suspend + + + + Sleep + + + + lock + + + + + QObject + + The screensaver is active. + + + + The screensaver is inactive. + + + + + Screensaver + + Picture does not exist + + + + View + + + + You have new notification + + + + + SleepTime + + You have rested: + + + + + SwitchButtonGroup + + uEduPWD + + + + Wechat + + + + + TabletLockWidget + + Cancel + + + + Back + + + + Skip + + + + New password is the same as old + + + + Reset password error:%1 + + + + Please scan by correct WeChat + + + + + Utils + + kylin network applet desktop message + + + + + VerificationWidget + + Please scan by bound WeChat + + + + + VerticalVerificationWidget + + Please scan by bound WeChat + + + + + WeChatAuthDialog + + Login by wechat + + + + 「 Use registered WeChat account to login 」 + + + + Verification by wechat + + + + 「 Use bound WeChat account to verification 」 + + + + Network not connected~ + + + + Scan code successfully + + + + Timeout!Try again! + + + + + main + + Start command for the ukui ScreenSaver. + Начните команду для ukui ScreenSaver. + + + lock the screen immediately + немедленно заблокируйте экран + + + Dialog for the ukui ScreenSaver. + Диалог для экранного экрана ukui. + + + activated by session idle signal + активируется сигналом холостого хода + + + Screensaver for ukui-screensaver + + + + show on root window + + + + show on window. + + + + window id + + + + query the status of the screen saver + + + + lock the screen and show screensaver immediately + + + + unlock the screen saver + + + + show the screensaver + + + + show screensaver immediately + + + + diff --git a/i18n_ts/tr.ts b/i18n_ts/tr.ts new file mode 100644 index 0000000..1d46147 --- /dev/null +++ b/i18n_ts/tr.ts @@ -0,0 +1,2222 @@ + + + + + AuthDialog + + More Devices + Diğer Cihazlar + + + Biometric + Biometrik + + + Password + Parola + + + + Retry + Yeniden Dene + + + UnLock + Kilidi Aç + + + Password: + Parola + + + Account locked %1 minutes due to %2 fail attempts + %2 başarısız denemeden dolayı hesap %1 dakika kilitlendi + + + Password Incorrect, Please try again + Parola yanlış, Lütfen tekrar deneyin + + + Authentication failure,there are still %1 remaining opportunities + Kimlik doğrulama hatası, hala %1 kalan denemen var + + + + Authentication failure, Please try again + + + + + + + + Please try again in %1 minutes. + + + + + + + + Please try again in %1 seconds. + + + + + + + + Account locked permanently. + + + + + Verify face recognition or enter password to unlock + + + + + Press fingerprint or enter password to unlock + + + + + Verify voiceprint or enter password to unlock + + + + + Verify finger vein or enter password to unlock + + + + + Verify iris or enter password to unlock + + + + + Use the bound wechat scanning code or enter the password to unlock + + + + + + Password cannot be empty + + + + + Password + + + + + Input Password + + + + + Login + + + + + Failed to verify %1, please enter password to unlock + + + + + Unable to verify %1, please enter password to unlock + + + + + Failed to verify %1, you still have %2 verification opportunities + + + + + NET Exception + + + + Biometric Authentication + Biyometrik Kimlik Doğrulama + + + Password Authentication + Parola Doğrulama + + + Other Devices + Diğer Ayıtlar + + + + BioDevices + + FingerPrint + Parmak İzi + + + FingerVein + Damar İzi + + + Iris + Göz + + + Face + Yüz + + + VoicePrint + Ses İzi + + + + BioDevicesWidget + + Please select other biometric devices + Lütfen diğer biyometrik cihazları seçin + + + Device Type: + Aygıt Türü: + + + Device Name: + Aygıt Adı: + + + + BiometricAuthWidget + + + Current device: + Şuanki aygıt: + + + + Identify failed, Please retry. + Tanımlama başarısız, Lütfen tekrar deneyin. + + + + BiometricDevicesWidget + + + Please select the biometric device + Lütfen biyometrik aygıtı seçin + + + + Device type: + Aygıt türü: + + + + Device name: + Aygıt adı: + + + + OK + Tamam + + + + ConfForm + + + edit network + + + + + LAN name: + + + + + Method: + + + + + Address: + + + + + Netmask: + + + + + Gateway: + + + + + DNS 1: + + + + + DNS 2: + + + + + Edit Conn + + + + + + Auto(DHCP) + + + + + + Manual + + + + + Cancel + + + + + Save + + + + + Ok + + + + + Can not create new wired network for without wired card + + + + + New network already created + + + + + New network settings already finished + + + + + New settings already effective + + + + + Edit Network + + + + + Add Wired Network + + + + + DeviceType + + + FingerPrint + Parmak İzi + + + + FingerVein + Parmak Damarı + + + + Iris + Göz + + + + Face + Yüz + + + + VoicePrint + Ses İzi + + + + QRCode + + + + + DigitalAuthDialog + + + + LoginByUEdu + + + + + ResetPWD? + + + + + + SetNewUEduPWD + + + + + clear + + + + + ConfirmNewUEduPWD + + + + + The two password entries are inconsistent, please reset + + + + + Password entered incorrectly, please try again + + + + + DlgConnHidWifi + + + + Connect to Hidden Wi-Fi Network + + + + + Add Hidden Wi-Fi + + + + + Connection + + + + + Wi-Fi name + + + + + Wi-Fi security + + + + + Cancel + + + + + Connect + + + + + C_reate… + + + + + None + + + + + WPA & WPA2 Personal + + + + + DlgConnHidWifiLeap + + + + Connect to Hidden Wi-Fi Network + + + + + Add hidden Wi-Fi + + + + + Connection + + + + + Network name + + + + + Wi-Fi security + + + + + Username + + + + + Password + Parola + + + + Cancel + + + + + Connect + + + + + C_reate… + + + + + None + + + + + WPA & WPA2 Personal + + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + + WEP 128-bit Passphrase + + + + + Dynamic WEP (802.1X) + + + + + WPA & WPA2 Enterprise + + + + + DlgConnHidWifiSecFast + + + + Connect to Hidden Wi-Fi Network + + + + + Add hidden Wi-Fi + + + + + Connection + + + + + Network name + + + + + Wi-Fi security + + + + + Authentication + + + + + Anonymous identity + + + + + Allow automatic PAC pro_visioning + + + + + PAC file + + + + + Inner authentication + + + + + Username + + + + + Password + Parola + + + + Cancel + + + + + Connect + + + + + C_reate… + + + + + + None + + + + + WPA & WPA2 Personal + + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + + WEP 128-bit Passphrase + + + + + Dynamic WEP (802.1X) + + + + + WPA & WPA2 Enterprise + + + + + Tunneled TLS + + + + + Protected EAP (PEAP) + + + + + Anonymous + + + + + Authenticated + + + + + Both + + + + + DlgConnHidWifiSecLeap + + + + Connect to Hidden Wi-Fi Network + + + + + Add hidden Wi-Fi + + + + + Connection + + + + + Network name + + + + + Wi-Fi security + + + + + Authentication + + + + + Username + + + + + Password + Parola + + + + Cancel + + + + + Connect + + + + + C_reate… + + + + + None + + + + + WPA & WPA2 Personal + + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + + WEP 128-bit Passphrase + + + + + Dynamic WEP (802.1X) + + + + + WPA & WPA2 Enterprise + + + + + Tunneled TLS + + + + + Protected EAP (PEAP) + + + + + DlgConnHidWifiSecPeap + + + + Connect to Hidden Wi-Fi Network + + + + + Add hidden Wi-Fi + + + + + Connection + + + + + Network name + + + + + Wi-Fi security + + + + + Authentication + + + + + Anonymous identity + + + + + Domain + + + + + CA certificate + + + + + CA certificate password + + + + + No CA certificate is required + + + + + PEAP version + + + + + Inner authentication + + + + + Username + + + + + Password + Parola + + + + Cancel + + + + + Connect + + + + + + None + + + + + WPA & WPA2 Personal + + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + + WEP 128-bit Passphrase + + + + + Dynamic WEP (802.1X) + + + + + WPA & WPA2 Enterprise + + + + + Tunneled TLS + + + + + Protected EAP (PEAP) + + + + + Choose from file + + + + + Automatic + + + + + Version 0 + + + + + Version 1 + + + + + DlgConnHidWifiSecPwd + + + + Connect to Hidden Wi-Fi Network + + + + + Add hidden Wi-Fi + + + + + Connection + + + + + Network name + + + + + Wi-Fi security + + + + + Authentication + + + + + Username + + + + + Password + Parola + + + + Cancel + + + + + Connect + + + + + C_reate… + + + + + None + + + + + WPA & WPA2 Personal + + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + + WEP 128-bit Passphrase + + + + + Dynamic WEP (802.1X) + + + + + WPA & WPA2 Enterprise + + + + + Tunneled TLS + + + + + Protected EAP (PEAP) + + + + + DlgConnHidWifiSecTls + + + + Connect to Hidden Wi-Fi Network + + + + + Add hidden Wi-Fi + + + + + Connection + + + + + Network name + + + + + Wi-Fi security + + + + + Authentication + + + + + Identity + + + + + Domain + + + + + CA certificate + + + + + CA certificate password + + + + + No CA certificate is required + + + + + User certificate + + + + + User certificate password + + + + + User private key + + + + + User key password + + + + + Cancel + + + + + Connect + + + + + C_reate… + + + + + + + + None + + + + + WPA & WPA2 Personal + + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + + WEP 128-bit Passphrase + + + + + Dynamic WEP (802.1X) + + + + + WPA & WPA2 Enterprise + + + + + Tunneled TLS + + + + + Protected EAP (PEAP) + + + + + + + Choose from file + + + + + DlgConnHidWifiSecTunnelTLS + + + + Connect to Hidden Wi-Fi Network + + + + + Add hidden Wi-Fi + + + + + Connection + + + + + Network name + + + + + Wi-Fi security + + + + + Authentication + + + + + Anonymous identity + + + + + Domain + + + + + CA certificate + + + + + CA certificate password + + + + + No CA certificate is required + + + + + Inner authentication + + + + + Username + + + + + Password + Parola + + + + Cancel + + + + + Connect + + + + + C_reate… + + + + + + None + + + + + WPA & WPA2 Personal + + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + + WEP 128-bit Passphrase + + + + + Dynamic WEP (802.1X) + + + + + WPA & WPA2 Enterprise + + + + + Tunneled TLS + + + + + Protected EAP (PEAP) + + + + + Choose from file + + + + + DlgConnHidWifiWep + + + + Connect to Hidden Wi-Fi Network + + + + + Add hidden Wi-Fi + + + + + Connection + + + + + Network name + + + + + Wi-Fi security + + + + + Key + + + + + WEP index + + + + + Authentication + + + + + Cancel + + + + + Connect + + + + + C_reate… + + + + + None + + + + + WPA & WPA2 Personal + + + + + WEP 40/128-bit Key (Hex or ASCII) + + + + + WEP 128-bit Passphrase + + + + + Dynamic WEP (802.1X) + + + + + WPA & WPA2 Enterprise + + + + + 1(default) + + + + + Open System + + + + + Shared Key + + + + + DlgConnHidWifiWpa + + + + Connect to Hidden Wi-Fi Network + + + + + Add Hidden Wi-Fi + + + + + Connection + + + + + Wi-Fi name + + + + + Wi-Fi security + + + + + Password + Parola + + + + Cancel + + + + + Connect + + + + + C_reate… + + + + + None + + + + + WPA & WPA2 Personal + + + + + DlgHotspotCreate + + + + Dialog + + + + + Create Hotspot + + + + + Network name + + + + + Wi-Fi security + + + + + Password + Parola + + + + Cancel + + + + + Ok + + + + + None + + + + + WPA & WPA2 Personal + + + + + InputInfos + + + + Get code + + + + + Recapture(60s) + + + + + Recapture(%1s) + + + + + Service exception... + + + + + Invaild parameters... + + + + + Unknown fault:%1 + + + + + KeyboardWidget + + + + KeyboardWidget + + + + + KylinNM + + + + kylin-nm + + + + + No usable network in the list + + + + + Inactivated LAN + + + + + Other WLAN + + + + + + LAN + + + + + + WLAN + + + + + HotSpot + + + + + FlyMode + + + + + + Advanced + + + + + Show KylinNM + + + + + No wireless card detected + + + + + Activated LAN + + + + + Activated WLAN + + + + + + + + + Not connected + + + + + + + + + + + + + Disconnected + + + + + + NetOn, + + + + + No Other Wired Network Scheme + + + + + No Other Wireless Network Scheme + + + + + Wired net is disconnected + + + + + Conn Ethernet Success + + + + + Conn Ethernet Fail + + + + + Conn Wifi Success + + + + + Confirm your Wi-Fi password or usable of wireless card + + + + + LockWidget + + + + Form + + + + + + Date + Tarih + + + + + Time + Zaman + + + Guest + Misafir + + + SwitchUser + Kullanıcı Değiştir + + + + LoginOptionsWidget + + + Login Options + + + + Password + Parola + + + + Identify device removed! + + + + + MyLineEdit + + + + + Verification code + + + + + OneConnForm + + + + Form + + + + + + + + Connect + + + + + Disconnect + + + + + Input Password... + + + + + Automatically join the network + + + + + Connect to Hidden Wi-Fi Network + + + + + None + + + + + WiFi Security: + + + + + Signal: + + + + + MAC: + + + + + Conn Wifi Failed + + + + + OneLancForm + + + + Form + + + + + + Connect + + + + + Disconnect + + + + + + No Configuration + + + + + IPv4: + + + + + IPv6: + + + + + BandWidth: + + + + + MAC: + + + + + PhoneAuthWidget + + + + Verification by phoneNum + + + + + 「 Use bound Phone number to verification 」 + + + + + + commit + + + + + 「 Use SMS to verification 」 + + + + + + Network not connected~ + + + + + Verification Code invalid! + + + + + Verification Code incorrect.Please retry! + + + + + Failed time over limit!Retry after 1 hour! + + + + + verifaction failed! + + + + + Network unavailable~ + + + + + PowerManager + + + lock + kilit + + + SwitchUser + Kullanıcı Değiştir + + + logout + Çıkış + + + reboot + yeniden başlat + + + shutdown + kapat + + + Lock Screen + Ekranı Kilitle + + + Switch User + Kullanıcı Değiştir + + + + Log Out + Çıkış + + + + + Restart + Yeniden Başlat + + + + + Power Off + Bilgisayarı Kapat + + + + Suspend + + + + + Sleep + + + + + QObject + + + The screensaver is active. + + + + + The screensaver is inactive. + + + + + Screensaver + + exit(Esc) + çıkış(Esc) + + + exit + çıkış + + + + Picture does not exist + + + + + View + + + + + You have new notification + + + + Set as desktop wallpaper + Masaüstü arkaplanı olarak ayarla + + + Automatic switching + Otomatik değiştir + + + + SleepTime + + + You have rested: + + + + + SwitchButtonGroup + + + uEduPWD + + + + + Wechat + + + + + TabletLockWidget + + + + + + + + Cancel + + + + + + Back + + + + + Skip + + + + + New password is the same as old + + + + + Reset password error:%1 + + + + + Please scan by correct WeChat + + + + + Utils + + + kylin network applet desktop message + + + + + VerificationWidget + + + Please scan by bound WeChat + + + + + VerticalVerificationWidget + + + Please scan by bound WeChat + + + + + WeChatAuthDialog + + + + Login by wechat + + + + + + 「 Use registered WeChat account to login 」 + + + + + + Verification by wechat + + + + + + 「 Use bound WeChat account to verification 」 + + + + + + Network not connected~ + + + + + Scan code successfully + + + + + Timeout!Try again! + + + + + main + + + Start command for the ukui ScreenSaver. + Ukui Ekran Koruyucu için başlatma komutu. + + + + + + lock the screen immediately + Ekranı hemen kilitle + + + + query the status of the screen saver + + + + + unlock the screen saver + + + + + show the screensaver + + + + + Dialog for the ukui ScreenSaver. + + + + + activated by session idle signal + + + + + + lock the screen and show screensaver immediately + + + + + show screensaver immediately + + + + + Screensaver for ukui-screensaver + + + + + show on root window + + + + + show on window. + + + + + window id + + + + diff --git a/i18n_ts/zh_CN.ts b/i18n_ts/zh_CN.ts new file mode 100644 index 0000000..c76dc77 --- /dev/null +++ b/i18n_ts/zh_CN.ts @@ -0,0 +1,2506 @@ + + + + + AuthDialog + + More Devices + 选择其他设备 + + + Biometric + 使用生物识别认证 + + + Password + 使用密码认证 + + + + Retry + 重试 + + + UnLock + 解锁 + + + Slide to unlock + 向上滑动解锁 + + + You have %1 unread message + 您有%1条未读消息 + + + LoggedIn + 已登录 + + + Password: + 密码: + + + Account locked %1 minutes due to %2 fail attempts + 账户锁定%1分钟由于%2次错误尝试 + + + + + + + Please try again in %1 minutes. + 请%1分钟后再试 + + + + + + + Please try again in %1 seconds. + 请%1秒后再试 + + + + + + + Account locked permanently. + 账号已被永久锁定 + + + + Verify face recognition or enter password to unlock + 验证人脸识别或输入密码解锁 + + + + Press fingerprint or enter password to unlock + 按压指纹或输入密码解锁 + + + + Verify voiceprint or enter password to unlock + 验证声纹或输入密码解锁 + + + + Verify finger vein or enter password to unlock + 验证指静脉或输入密码解锁 + + + + Verify iris or enter password to unlock + 验证虹膜或输入密码解锁 + + + + Input Password + 输入密码 + + + + Failed to verify %1, please enter password to unlock + 验证%1失败,请输入密码解锁 + + + + Unable to verify %1, please enter password to unlock + 无法验证%1,请输入密码解锁 + + + Use the bound wechat scanning code or enter the password to log in + 使用绑定的微信扫码或输入密码登录 + + + + + Password cannot be empty + 密码不能为空 + + + Failed to verify %1, please enter password. + 验证%1失败,请输入密码. + + + Unable to verify %1, please enter password. + 无法验证%1,请输入密码. + + + + Failed to verify %1, you still have %2 verification opportunities + 验证%1失败,您还有%2次尝试机会 + + + Biometric/code scan authentication failed too many times, please enter the password. + 生物/扫码验证失败达最大次数,请使用密码解锁 + + + Bioauth/code scan authentication failed, you still have %1 verification opportunities + 生物/扫码验证失败,您还有%1次尝试机会 + + + + NET Exception + 网络异常 + + + Password Incorrect, Please try again + 密码错误,请重试 + + + Authentication failure,there are still %1 remaining opportunities + 认证失败,还剩%1次尝试机会 + + + Please enter your password or enroll your fingerprint + 请输入密码或者录入指纹 + + + + Authentication failure, Please try again + 认证失败,请重试 + + + + Use the bound wechat scanning code or enter the password to unlock + 使用绑定的微信扫码或输入密码解锁 + + + + Password + 密码 + + + + Login + 登录 + + + Biometric Authentication + 生物识别认证 + + + Password Authentication + 密码认证 + + + Other Devices + 其他设备 + + + Too many unsuccessful attempts,please enter password. + 指纹验证失败达最大次数,请使用密码登录 + + + Fingerprint authentication failed, you still have %1 verification opportunities + 指纹验证失败,您还有%1次尝试机会 + + + + BioDevices + + FingerPrint + 指纹 + + + FingerVein + 指静脉 + + + Iris + 虹膜 + + + Face + 人脸 + + + VoicePrint + 声纹 + + + + BioDevicesWidget + + Please select other biometric devices + 请选择其他生物识别设备 + + + Device Type: + 设备类型: + + + Device Name: + 设备名称: + + + + BiometricAuthWidget + + + Current device: + 当前设备: + + + + Identify failed, Please retry. + 识别失败,请重试 + + + + BiometricDevicesWidget + + + Please select the biometric device + 请选择生物设备 + + + + Device type: + 设备类型: + + + + Device name: + 设备型号: + + + + OK + 确定 + + + + ConfForm + + + edit network + 网络属性 + + + + LAN name: + 网络名称: + + + + Method: + 编辑IP设置: + + + + Address: + IP地址: + + + + Netmask: + 子网掩码: + + + + Gateway: + 默认网关: + + + + DNS 1: + 首选DNS: + + + + DNS 2: + 备选DNS: + + + + Edit Conn + 网络设置 + + + + + Auto(DHCP) + 自动(DHCP) + + + + + Manual + 手动 + + + + Cancel + 返回 + + + + Save + 确定 + + + + Ok + 确定 + + + + Can not create new wired network for without wired card + 缺少有线网卡 无法新建网络 + + + + New network already created + 已创建新的有线网络 + + + + New network settings already finished + 新的网络配置已经完成 + + + + Edit Network + 网络属性 + + + + Add Wired Network + 新建有线网络 + + + create wired network successfully + 已创建新的有线网络 + + + change configuration of wired network successfully + 新的设置已经生效 + + + + New settings already effective + 新的设置已经生效 + + + There is a same named LAN exsits. + 已有同名连接存在 + + + + DeviceType + + + FingerPrint + 指纹 + + + + FingerVein + 指静脉 + + + + Iris + 虹膜 + + + + Face + 人脸识别 + + + + VoicePrint + 声纹 + + + + QRCode + 二维码 + + + + DigitalAuthDialog + + + + LoginByUEdu + 请输入锁屏密码 + + + now is authing, wait a moment + 认证中,请稍后 + + + Password Incorrect, Please try again + 密码错误,请重试 + + + + ResetPWD? + 忘记密码? + + + + + SetNewUEduPWD + 设置新锁屏密码 + + + + ConfirmNewUEduPWD + 确认新锁屏密码 + + + + The two password entries are inconsistent, please reset + 两次密码输入不一致,请重设 + + + + Password entered incorrectly, please try again + 密码输入错误,请重试 + + + + clear + 清空 + + + + DlgConnHidWifi + + + Add Hidden Wi-Fi + 加入无线网络 + + + + Connection + 连接设置: + + + + Wi-Fi name + 网络名称: + + + + Wi-Fi security + Wi-Fi 安全性: + + + + Cancel + 返回 + + + + Connect + 确定 + + + + C_reate… + 新建... + + + + None + + + + + WPA & WPA2 Personal + WPA 及 WPA2 个人 + + + WEP 40/128-bit Key (Hex or ASCII) + WEP 40/128 位密钥(十六进制或ASCII) + + + WEP 128-bit Passphrase + WEP 128 位密码句 + + + Dynamic WEP (802.1X) + 动态 WEP (802.1x) + + + WPA & WPA2 Enterprise + WPA 及 WPA2 企业 + + + + Connect to Hidden Wi-Fi Network + 连接到隐藏 Wi-Fi 网络 + + + + DlgConnHidWifiLeap + + + Connect to Hidden Wi-Fi Network + 连接到隐藏 Wi-Fi 网络 + + + + Add hidden Wi-Fi + 加入无线网络 + + + + Connection + 连接设置: + + + + Network name + 网络名称: + + + + Wi-Fi security + Wi-Fi 安全性: + + + + Username + 用户名: + + + + Password + 密码: + + + + Cancel + 返回 + + + + Connect + 连接 + + + + C_reate… + 新建... + + + + None + + + + + WPA & WPA2 Personal + WPA 及 WPA2 个人 + + + + WEP 40/128-bit Key (Hex or ASCII) + WEP 40/128 位密钥(十六进制或ASCII) + + + + WEP 128-bit Passphrase + WEP 128 位密码句 + + + + Dynamic WEP (802.1X) + 动态 WEP (802.1x) + + + + WPA & WPA2 Enterprise + WPA 及 WPA2 企业 + + + + DlgConnHidWifiSecFast + + + Connect to Hidden Wi-Fi Network + 连接到隐藏 Wi-Fi 网络 + + + + Add hidden Wi-Fi + 加入无线网络 + + + + Connection + 连接设置: + + + + Network name + 网络名称: + + + + Wi-Fi security + Wi-Fi 安全性: + + + + Authentication + 认证: + + + + Anonymous identity + 匿名身份: + + + + Allow automatic PAC pro_visioning + 自动PAC配置: + + + + PAC file + PAC文件: + + + + Inner authentication + 内部认证: + + + + Username + 用户名: + + + + Password + 密码: + + + + Cancel + 返回 + + + + Connect + 连接 + + + + C_reate… + 新建... + + + + + None + + + + + WPA & WPA2 Personal + WPA 及 WPA2 个人 + + + + WEP 40/128-bit Key (Hex or ASCII) + WEP 40/128 位密钥(十六进制或ASCII) + + + + WEP 128-bit Passphrase + WEP 128 位密码句 + + + + Dynamic WEP (802.1X) + 动态 WEP (802.1x) + + + + WPA & WPA2 Enterprise + WPA 及 WPA2 企业 + + + + Tunneled TLS + 隧道 TLS + + + + Protected EAP (PEAP) + 受保护的 EAP + + + + Anonymous + 匿名 + + + + Authenticated + 已认证 + + + + Both + 两者兼用 + + + + DlgConnHidWifiSecLeap + + + Connect to Hidden Wi-Fi Network + 连接到隐藏 Wi-Fi 网络 + + + + Add hidden Wi-Fi + 加入无线网络 + + + + Connection + 连接设置: + + + + Network name + 网络名称: + + + + Wi-Fi security + Wi-Fi 安全性: + + + + Authentication + 认证: + + + + Username + 用户名: + + + + Password + 密码: + + + + Cancel + 返回 + + + + Connect + 连接 + + + + C_reate… + 新建... + + + + None + + + + + WPA & WPA2 Personal + WPA 及 WPA2 个人 + + + + WEP 40/128-bit Key (Hex or ASCII) + WEP 40/128 位密钥(十六进制或ASCII) + + + + WEP 128-bit Passphrase + WEP 128 位密码句 + + + + Dynamic WEP (802.1X) + 动态 WEP (802.1x) + + + + WPA & WPA2 Enterprise + WPA 及 WPA2 企业 + + + + Tunneled TLS + 隧道 TLS + + + + Protected EAP (PEAP) + 受保护的 EAP + + + + DlgConnHidWifiSecPeap + + + Connect to Hidden Wi-Fi Network + 连接到隐藏 Wi-Fi 网络 + + + + Add hidden Wi-Fi + 加入无线网络 + + + + Connection + 连接设置: + + + + Network name + 网络名称: + + + + Wi-Fi security + Wi-Fi 安全性: + + + + Authentication + 认证: + + + + Anonymous identity + 匿名身份: + + + + Domain + 域名: + + + + CA certificate + CA 证书: + + + + CA certificate password + CA 证书密码: + + + + No CA certificate is required + 不需要CA证书 + + + + PEAP version + PEAP版本: + + + + Inner authentication + 内部认证: + + + + Username + 用户名: + + + + Password + 密码: + + + + Cancel + 返回 + + + + Connect + 连接 + + + + + None + + + + + WPA & WPA2 Personal + WPA 及 WPA2 个人 + + + + WEP 40/128-bit Key (Hex or ASCII) + WEP 40/128 位密钥(十六进制或ASCII) + + + + WEP 128-bit Passphrase + WEP 128 位密码句 + + + + Dynamic WEP (802.1X) + 动态 WEP (802.1x) + + + + WPA & WPA2 Enterprise + WPA 及 WPA2 企业 + + + + Tunneled TLS + 隧道 TLS + + + + Protected EAP (PEAP) + 受保护的 EAP + + + + Choose from file + 从文件选择... + + + + Automatic + 自动 + + + + Version 0 + 版本 0 + + + + Version 1 + 版本 1 + + + + DlgConnHidWifiSecPwd + + + Connect to Hidden Wi-Fi Network + 连接到隐藏 Wi-Fi 网络 + + + + Add hidden Wi-Fi + 加入无线网络 + + + + Connection + 连接设置: + + + + Network name + 网络名称: + + + + Wi-Fi security + Wi-Fi 安全性: + + + + Authentication + 认证: + + + + Username + 用户名: + + + + Password + 密码: + + + + Cancel + 返回 + + + + Connect + 连接 + + + + C_reate… + 新建... + + + + None + + + + + WPA & WPA2 Personal + WPA 及 WPA2 个人 + + + + WEP 40/128-bit Key (Hex or ASCII) + WEP 40/128 位密钥(十六进制或ASCII) + + + + WEP 128-bit Passphrase + WEP 128 位密码句 + + + + Dynamic WEP (802.1X) + 动态 WEP (802.1x) + + + + WPA & WPA2 Enterprise + WPA 及 WPA2 企业 + + + + Tunneled TLS + 隧道 TLS + + + + Protected EAP (PEAP) + 受保护的 EAP + + + + DlgConnHidWifiSecTls + + + Connect to Hidden Wi-Fi Network + 连接到隐藏 Wi-Fi 网络 + + + + Add hidden Wi-Fi + 加入无线网络 + + + + Connection + 连接设置: + + + + Network name + 网络名称: + + + + Wi-Fi security + Wi-Fi 安全性: + + + + Authentication + 认证: + + + + Identity + 身份: + + + + Domain + 域名: + + + + CA certificate + CA 证书: + + + + CA certificate password + CA 证书密码: + + + + No CA certificate is required + 不需要CA证书 + + + + User certificate + 用户证书: + + + + User certificate password + 用户证书密码: + + + + User private key + 用户私钥: + + + + User key password + 用户密钥密码: + + + + Cancel + 返回 + + + + Connect + 连接 + + + + C_reate… + 新建... + + + + + + + None + + + + + WPA & WPA2 Personal + WPA 及 WPA2 个人 + + + + WEP 40/128-bit Key (Hex or ASCII) + WEP 40/128 位密钥(十六进制或ASCII) + + + + WEP 128-bit Passphrase + WEP 128 位密码句 + + + + Dynamic WEP (802.1X) + 动态 WEP (802.1x) + + + + WPA & WPA2 Enterprise + WPA 及 WPA2 企业 + + + + Tunneled TLS + 隧道 TLS + + + + Protected EAP (PEAP) + 受保护的 EAP + + + + + + Choose from file + 从文件选择... + + + + DlgConnHidWifiSecTunnelTLS + + + Connect to Hidden Wi-Fi Network + 连接到隐藏 Wi-Fi 网络 + + + + Add hidden Wi-Fi + 加入无线网络 + + + + Connection + 连接设置: + + + + Network name + 网络名称: + + + + Wi-Fi security + Wi-Fi 安全性: + + + + Authentication + 认证: + + + + Anonymous identity + 匿名身份: + + + + Domain + 域名: + + + + CA certificate + CA 证书: + + + + CA certificate password + CA 证书密码: + + + + No CA certificate is required + 不需要CA证书 + + + + Inner authentication + 内部认证: + + + + Username + 用户名: + + + + Password + 密码: + + + + Cancel + 返回 + + + + Connect + 连接 + + + + C_reate… + 新建... + + + + + None + + + + + WPA & WPA2 Personal + WPA 及 WPA2 个人 + + + + WEP 40/128-bit Key (Hex or ASCII) + WEP 40/128 位密钥(十六进制或ASCII) + + + + WEP 128-bit Passphrase + WEP 128 位密码句 + + + + Dynamic WEP (802.1X) + 动态 WEP (802.1x) + + + + WPA & WPA2 Enterprise + WPA 及 WPA2 企业 + + + + Tunneled TLS + 隧道 TLS + + + + Protected EAP (PEAP) + 受保护的 EAP + + + + Choose from file + 从文件选择... + + + + DlgConnHidWifiWep + + + Connect to Hidden Wi-Fi Network + 连接到隐藏 Wi-Fi 网络 + + + + Add hidden Wi-Fi + 加入无线网络 + + + + Connection + 连接设置: + + + + Network name + 网络名称: + + + + Wi-Fi security + Wi-Fi 安全性: + + + + Key + 密钥 + + + + WEP index + WEP 检索 + + + + Authentication + 认证: + + + + Cancel + 返回 + + + + Connect + 连接 + + + + C_reate… + 新建... + + + + None + + + + + WPA & WPA2 Personal + WPA 及 WPA2 个人 + + + + WEP 40/128-bit Key (Hex or ASCII) + WEP 40/128 位密钥(十六进制或ASCII) + + + + WEP 128-bit Passphrase + WEP 128 位密码句 + + + + Dynamic WEP (802.1X) + 动态 WEP (802.1x) + + + + WPA & WPA2 Enterprise + WPA 及 WPA2 企业 + + + + 1(default) + 1(默认) + + + + Open System + 开放式系统 + + + + Shared Key + 共享密钥 + + + + DlgConnHidWifiWpa + + + Connect to Hidden Wi-Fi Network + 连接到隐藏 Wi-Fi 网络 + + + + Add Hidden Wi-Fi + 加入无线网络 + + + + Connection + 连接设置: + + + + Wi-Fi name + 网络名称: + + + + Wi-Fi security + Wi-Fi 安全性: + + + + Password + 密码: + + + + Cancel + 返回 + + + + Connect + 连接 + + + + C_reate… + 新建... + + + + None + + + + + WPA & WPA2 Personal + WPA 及 WPA2 个人 + + + WEP 40/128-bit Key (Hex or ASCII) + WEP 40/128 位密钥(十六进制或ASCII) + + + WEP 128-bit Passphrase + WEP 128 位密码句 + + + Dynamic WEP (802.1X) + 动态 WEP (802.1x) + + + WPA & WPA2 Enterprise + WPA 及 WPA2 企业 + + + + DlgHotspotCreate + + + Dialog + + + + + Create Hotspot + 创建个人热点 + + + + Network name + 网络名称: + + + + Wi-Fi security + Wi-Fi 安全性: + + + + Password + 密码: + + + + Cancel + 返回 + + + + Ok + 确定 + + + + None + + + + + WPA & WPA2 Personal + WPA 及 WPA2 个人 + + + + InputInfos + + + Service exception... + 服务异常,重试中... + + + + Invaild parameters... + 参数异常,重试中... + + + + Unknown fault:%1 + 未知错误:%1 + + + + Recapture(60s) + 重新获取(60s) + + + + Recapture(%1s) + 重新获取(%1s) + + + + + Get code + 获取验证码 + + + + KeyboardWidget + + + KeyboardWidget + + + + + KylinDBus + + kylin network applet desktop message + 麒麟网络工具信息提示 + + + + KylinNM + + + kylin-nm + 网络工具 + + + + + LAN + 有线网络 + + + Enabel LAN List + 其他有线网络 + + + WiFi + 无线网络 + + + Enabel WiFi List + 其他无线网络 + + + New WiFi + 加入其他网络 + + + Network + 网络 + + + + + Advanced + 设置网络 + + + Ethernet + 有线网络 + + + Connect Hide Network + 加入网络 + + + Wifi + 无线网络 + + + Enabled + 已开启 + + + Disabled + 已关闭 + + + + HotSpot + 个人热点 + + + + FlyMode + 飞行模式 + + + Show MainWindow + 显示网络连接界面 + + + + Inactivated LAN + 未激活 + + + Inactivated WLAN + 未激活 + + + + Other WLAN + 其他 + + + + + WLAN + 无线局域网 + + + + Show KylinNM + + + + + No wireless card detected + 未检测到无线网卡 + + + + Activated LAN + 已激活 + + + + Activated WLAN + 已激活 + + + + + + + + Not connected + 未连接任何网络 + + + + + + + + + + + + Disconnected + 未连接 + + + + No Other Wired Network Scheme + 列表中无其他有线网络 + + + Edit + 编辑 + + + Done + 完成 + + + No wifi connected. + 未连接任何网络 + + + + No Other Wireless Network Scheme + 未检测到其他无线网络 + + + + Wired net is disconnected + 断开有线网络 + + + Wi-Fi is disconnected + 断开无线网络 + + + + Confirm your Wi-Fi password or usable of wireless card + 请确认Wi-Fi密码或无线设备 + + + Ethernet Networks + 其他有线网络 + + + New LAN + 新建有线网络 + + + Hide WiFi + 加入网络 + + + + No usable network in the list + 列表暂无可连接网络 + + + + + NetOn, + 已连接, + + + Wifi Networks + 其他无线网络 + + + None + + + + keep wired network switch is on before turning on wireless switch + 打开无线网开关前保持有线网开关打开 + + + please insert the wireless network adapter + 请先插入无线网卡 + + + Abnormal connection exist, program will delete it + 正在断开异常连接的网络 + + + update Wi-Fi list now, click again + 正在更新 Wi-Fi列表 请再次点击 + + + update Wi-Fi list now + 正在更新 Wi-Fi列表 + + + + Conn Ethernet Success + 连接有线网络成功 + + + + Conn Ethernet Fail + 连接有线网络失败 + + + + Conn Wifi Success + 连接无线网络成功 + + + + LockWidget + + + Form + + + + + Date + 日期 + + + + Time + 时间 + + + Guest + 游客 + + + SwitchUser + 切换用户 + + + + LoginOptionsWidget + + + Login Options + 登录选项 + + + Password + 密码 + + + Wechat + 微信 + + + + Identify device removed! + 校验设备已移除! + + + + MyLineEdit + + + + + Verification code + 短信验证码 + + + + OneConnForm + + + Form + -- + + + + Automatically join the network + 自动加入该网络 + + + Input password + 输入密码 + + + Config + 设置 + + + + + + + Connect + 连接 + + + + Disconnect + 断开连接 + + + + Input Password... + 输入密码... + + + + Connect to Hidden Wi-Fi Network + 连接到隐藏 Wi-Fi 网络 + + + + Signal: + 信号强度: + + + Public + 开放 + + + Safe + 安全 + + + Rate + 速率 + + + + None + + + + + WiFi Security: + WiFi安全性: + + + + MAC: + 物理地址: + + + + Conn Wifi Failed + 连接无线网络失败 + + + + OneLancForm + + + Form + -- + + + Config + 设置 + + + + + Connect + 连接 + + + + Disconnect + 断开连接 + + + + + No Configuration + 未配置 + + + + IPv4: + IPv4地址: + + + + IPv6: + IPv6地址: + + + + BandWidth: + 带宽: + + + + MAC: + 物理地址: + + + Auto + 自动 + + + + PhoneAuthWidget + + + + Verification by phoneNum + 手机号验证 + + + + 「 Use bound Phone number to verification 」 + + + + + 「 Use SMS to verification 」 + 「 请使用绑定该账户手机号验证 」 + + + + + commit + 提交 + + + + + Network not connected~ + 系统未联网,请检查网络连接~ + + + + Network unavailable~ + 网络状态差,请检查网络连接~ + + + + Verification Code invalid! + 验证码失效 + + + + Verification Code incorrect.Please retry! + 验证码错误!请填写正确的验证码! + + + + Failed time over limit!Retry after 1 hour! + 验证码错误次数超过10次,1小时后再试 + + + + verifaction failed! + 手机验证失败 + + + + PowerManager + + + lock + 锁定 + + + SwitchUser + 切换用户 + + + logout + 注销 + + + reboot + 重启 + + + shutdown + 关机 + + + Lock Screen + 锁屏 + + + Switch User + 切换用户 + + + + Log Out + 注销 + + + + + Restart + 重启 + + + + Power Off + 关机 + + + + Shut Down + 关机 + + + + Hibernate + 休眠 + + + + Suspend + 睡眠 + + + Sleep + 休眠 + + + + QObject + + + The screensaver is active. + 屏幕保护程序已激活 + + + + The screensaver is inactive. + 屏幕保护程序未激活 + + + + Screensaver + + exit(Esc) + 退出(Esc) + + + exit + 退出 + + + + Picture does not exist + 图片不存在 + + + Set as desktop wallpaper + 设置为桌面壁纸 + + + Automatic switching + 自动切换 + + + You have %1 unread message + 您有%1条未读消息 + + + + You have new notification + 您有新的消息 + + + + View + 预览 + + + + SleepTime + + + You have rested: + 您已休息: + + + + SureWindow + + + Form + -- + + + + TextLabel + + + + + Cancel + 取消 + + + + Confirm + 确认 + + + + Multiple users are logged in at the same time.Are you sure you want to reboot this system? + 同时有多个用户登录系统,您确定要退出系统吗? + + + + SwitchButton + + login by password + 密码登录 + + + login by qr code + 微信登录 + + + + SwitchButtonGroup + + + uEduPWD + 密码登录 + + + + Wechat + 微信登录 + + + + TabletLockWidget + + You have %1 unread message + 您有%1条未读消息 + + + Slide to unlock + 向上滑动解锁 + + + + New password is the same as old + 新密码与原密码相同 + + + + Reset password error:%1 + 重置密码失败:%1 + + + + Please scan by correct WeChat + 请使用正确的微信扫码 + + + + + + + + + Cancel + 取消 + + + + + Back + 返回 + + + + Skip + 跳过 + + + + Utils + + + kylin network applet desktop message + 麒麟网络工具信息提示 + + + + VerificationWidget + + + Please scan by bound WeChat + 请使用已绑定的微信扫码 + + + + VerticalVerificationWidget + + + Please scan by bound WeChat + 请使用已绑定的微信扫码 + + + + WeChatAuthDialog + + + + Login by wechat + 微信登录 + + + + + Verification by wechat + 微信验证 + + + + + 「 Use registered WeChat account to login 」 + 「 使用已注册的微信号登录 」 + + + + + 「 Use bound WeChat account to verification 」 + 「 请使用绑定该账号的微信验证 」 + + + + + Network not connected~ + 系统未联网,请检查网络连接~ + + + + Scan code successfully + 扫码成功 + + + + Timeout!Try again! + 超时!请重新扫码! + + + Login failed + 登录失败 + + + + main + + + Start command for the ukui ScreenSaver. + 启动锁屏到命令 + + + + + + lock the screen immediately + 马上锁定屏幕 + + + + query the status of the screen saver + 获取锁屏状态 + + + + unlock the screen saver + 解锁屏幕 + + + + show the screensaver + + + + + Dialog for the ukui ScreenSaver. + + + + + activated by session idle signal + + + + + + lock the screen and show screensaver immediately + + + + + show screensaver immediately + + + + + Screensaver for ukui-screensaver + + + + + show on root window + + + + + show on window. + + + + + window id + + + + diff --git a/man/ukui-screensaver-backend.1 b/man/ukui-screensaver-backend.1 new file mode 100644 index 0000000..d39bf68 --- /dev/null +++ b/man/ukui-screensaver-backend.1 @@ -0,0 +1,62 @@ +.\" ukui-screensaver manual page +.\" +.\" This is free software; you may redistribute it and/or modify +.\" it under the terms of the GNU General Public License as +.\" published by the Free Software Foundation; either version 2, +.\" or (at your option) any later version. +.\" +.\" This is distributed in the hope that it will be useful, but +.\" WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +.\" GNU General Public License for more details. +.\" +.\"You should have received a copy of the GNU General Public License along +.\"with this program; if not, write to the Free Software Foundation, Inc., +.\"51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +.\" +.TH UKUI-SCREENSAVER 1 "03 May 2018" "UKUI Desktop Environment" +.\" Please adjust this date whenever revising the manpage. +.\" +.SH "NAME" +ukui-screensaver \- The UKUI Desktop Screensaver and Locker +.SH "SYNOPSIS" +.B ukui-screensaver [OPTIONS] +.SH "DESCRIPTION" +UKUI Screensaver is the default screen saver and locker for the UKUI Desktop. It is designed with simplicity and security in mind. \fBukui-screensaver\fP integrates well with the UKUI desktop, and it supports User Switching and the ability to lock down configuration settings. +.SH "OPTIONS" +.TP +\fB\-\-display=DISPLAY\fR +X display to use. +.TP +\fB\-\-no\-daemon\fR +Don't start as a daemon +.TP +\fB\-\-debug\fR +Enable debugging messages for \fBukui-screensaver\fP +.TP +\fB\-\-version\fR +Output version information and exit. +.TP +\fB\-h, \-\-help\fR +Print standard command line options. +.TP +\fB\-\-help\-all\fR +Print all command line options. +.P +This program also accepts the standard GTK options. +.SH "BUGS" +.SS Should you encounter any bugs, they may be reported at: +http://github.com/ukui-desktop/ukui-screensaver/issues +.SH "AUTHORS" +.SS ukui-screensaver is maintained by: +.nf +Hao Lee +.fi +.SS This Man Page has been updated for the UKUI Desktop Environment by: +Hao Lee (2018) +.SH "SEE ALSO" +.SS Further information may also be available at: http://wiki.ukui-desktop.org/docs +.P +.BR ukui-screensaver-command (1), +.BR ukui-screensaver-preferences (1), +.BR gtk-options (7) diff --git a/man/ukui-screensaver-command.1 b/man/ukui-screensaver-command.1 new file mode 100644 index 0000000..6e808e2 --- /dev/null +++ b/man/ukui-screensaver-command.1 @@ -0,0 +1,60 @@ +.\" ukui-screensaver manual page +.\" +.\" This is free software; you may redistribute it and/or modify +.\" it under the terms of the GNU General Public License as +.\" published by the Free Software Foundation; either version 2, +.\" or (at your option) any later version. +.\" +.\" This is distributed in the hope that it will be useful, but +.\" WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +.\" GNU General Public License for more details. +.\" +.\"You should have received a copy of the GNU General Public License along +.\"with this program; if not, write to the Free Software Foundation, Inc., +.\"51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +.\" +.TH UKUI-SCREENSAVER 1 "03 May 2018" "UKUI Desktop Environment" +.\" Please adjust this date whenever revising the manpage. +.\" +.SH "NAME" +ukui-screensaver \- The UKUI Desktop Screensaver and Locker +.SH "SYNOPSIS" +.B ukui-screensaver [OPTIONS] +.SH "DESCRIPTION" +UKUI Screensaver is the default screen saver and locker for the UKUI Desktop. It is designed with simplicity and security in mind. \fBukui-screensaver\fP integrates well with the UKUI desktop, and it supports User Switching and the ability to lock down configuration settings. +.SH "OPTIONS" +.TP +\fB\-\-display=DISPLAY\fR +X display to use. +.TP +\fB\-\-no\-daemon\fR +Don't start as a daemon +.TP +\fB \-\-debug\fR +Enable debugging messages for \fBukui-screensaver\fP +.TP +\fB\-\-version\fR +Output version information and exit. +.TP +\fB\-h, \-\-help\fR +Print standard command line options. +.TP +\fB\-\-help\-all\fR +Print all command line options. +.P +This program also accepts the standard GTK options. +.SH "BUGS" +.SS Should you encounter any bugs, they may be reported at: +http://github.com/ukui-desktop/ukui-screensaver/issues +.SH "AUTHORS" +.SS ukui-screensaver is maintained by: +.nf +Hao Lee +.fi +.SS This Man Page has been updated/re-written for the UKUI Desktop Environment by: +Hao Lee (2018) +.SH "SEE ALSO" +.SS Further information may also be available at: http://wiki.ukui-desktop.org/docs +.P +.BR ukui-screensaver-command (1), diff --git a/man/ukui-screensaver-dialog.1 b/man/ukui-screensaver-dialog.1 new file mode 100644 index 0000000..d39bf68 --- /dev/null +++ b/man/ukui-screensaver-dialog.1 @@ -0,0 +1,62 @@ +.\" ukui-screensaver manual page +.\" +.\" This is free software; you may redistribute it and/or modify +.\" it under the terms of the GNU General Public License as +.\" published by the Free Software Foundation; either version 2, +.\" or (at your option) any later version. +.\" +.\" This is distributed in the hope that it will be useful, but +.\" WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +.\" GNU General Public License for more details. +.\" +.\"You should have received a copy of the GNU General Public License along +.\"with this program; if not, write to the Free Software Foundation, Inc., +.\"51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +.\" +.TH UKUI-SCREENSAVER 1 "03 May 2018" "UKUI Desktop Environment" +.\" Please adjust this date whenever revising the manpage. +.\" +.SH "NAME" +ukui-screensaver \- The UKUI Desktop Screensaver and Locker +.SH "SYNOPSIS" +.B ukui-screensaver [OPTIONS] +.SH "DESCRIPTION" +UKUI Screensaver is the default screen saver and locker for the UKUI Desktop. It is designed with simplicity and security in mind. \fBukui-screensaver\fP integrates well with the UKUI desktop, and it supports User Switching and the ability to lock down configuration settings. +.SH "OPTIONS" +.TP +\fB\-\-display=DISPLAY\fR +X display to use. +.TP +\fB\-\-no\-daemon\fR +Don't start as a daemon +.TP +\fB\-\-debug\fR +Enable debugging messages for \fBukui-screensaver\fP +.TP +\fB\-\-version\fR +Output version information and exit. +.TP +\fB\-h, \-\-help\fR +Print standard command line options. +.TP +\fB\-\-help\-all\fR +Print all command line options. +.P +This program also accepts the standard GTK options. +.SH "BUGS" +.SS Should you encounter any bugs, they may be reported at: +http://github.com/ukui-desktop/ukui-screensaver/issues +.SH "AUTHORS" +.SS ukui-screensaver is maintained by: +.nf +Hao Lee +.fi +.SS This Man Page has been updated for the UKUI Desktop Environment by: +Hao Lee (2018) +.SH "SEE ALSO" +.SS Further information may also be available at: http://wiki.ukui-desktop.org/docs +.P +.BR ukui-screensaver-command (1), +.BR ukui-screensaver-preferences (1), +.BR gtk-options (7) diff --git a/screensaver-focus-helper/CMakeLists.txt b/screensaver-focus-helper/CMakeLists.txt new file mode 100644 index 0000000..2a8374d --- /dev/null +++ b/screensaver-focus-helper/CMakeLists.txt @@ -0,0 +1,27 @@ +project(screensaver-capslock-helper) + +pkg_check_modules(X11 REQUIRED x11) +pkg_check_modules(XCB REQUIRED xcb) + +include_directories( + ${X11_INCLUDE_DIRS} + ${XCB_INCLUDE_DIRS} + ) + +set(CMAKE_AUTOMOC ON) + +qt5_wrap_cpp(bin_SRCS + blackwindow.h + ) +set(bin_SRCS + ${bin_SRCS} + main.cpp + blackwindow.cpp + ) + +add_executable(screensaver-focus-helper ${bin_SRCS}) +target_link_libraries(screensaver-focus-helper Qt5::Core Qt5::Widgets Qt5::X11Extras ${X11_LIBRARIES} ${XCB_LIBRARIES}) + +install(TARGETS + screensaver-focus-helper + DESTINATION lib/ukui-screensaver) diff --git a/screensaver-focus-helper/blackwindow.cpp b/screensaver-focus-helper/blackwindow.cpp new file mode 100644 index 0000000..52f270e --- /dev/null +++ b/screensaver-focus-helper/blackwindow.cpp @@ -0,0 +1,93 @@ +#include "blackwindow.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +BlackWindow::BlackWindow(QWidget *parent) : QWidget(parent) +{ + setWindowFlags(Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint + | Qt::X11BypassWindowManagerHint); + + setAttribute(Qt::WA_TranslucentBackground); + + setCursor(Qt::BlankCursor); + //qApp->installNativeEventFilter(this); + //installEventFilter(this); +} + +void BlackWindow::paintEvent(QPaintEvent *event) +{ + for(auto screen : QGuiApplication::screens()) + { + QPainter painter(this); + QColor cor = "#000000"; + painter.setBrush(cor); + painter.drawRect(screen->geometry()); + + } + return QWidget::paintEvent(event); +} + +void BlackWindow::laterActivate() +{ + raise(); +} + +bool BlackWindow::eventFilter(QObject *obj, QEvent *event) +{ + if(event->type() == QEvent::WindowDeactivate){ + QTimer::singleShot(50,this,SLOT(laterActivate())); + } + + return false; +} + +bool BlackWindow::nativeEventFilter(const QByteArray &eventType, void *message, long *result) +{ + if (qstrcmp(eventType, "xcb_generic_event_t") != 0 || screensaverIsShow) { + return false; + } + xcb_generic_event_t *event = reinterpret_cast(message); + const uint8_t responseType = event->response_type & ~0x80; + if (responseType == XCB_CONFIGURE_NOTIFY) { + xcb_configure_notify_event_t *xc = reinterpret_cast(event); + if(xc->window == winId()) + return false; + XWindowAttributes window_attributes; + XGetWindowAttributes (QX11Info::display(), xc->window,&window_attributes); + XClassHint ch; + ch.res_name = NULL; + ch.res_class = NULL; + XGetClassHint (QX11Info::display(), xc->window, &ch); + if(QString(ch.res_name) == "ukui-screensaver-dialog"){ + // screensaverIsShow = true; + return false; + } + + laterActivate(); + }else if(responseType == XCB_MAP_NOTIFY){ + xcb_map_notify_event_t *xm = reinterpret_cast(event); + if(xm->window == winId()) + return false; + XWindowAttributes window_attributes; + XGetWindowAttributes (QX11Info::display(), xm->window,&window_attributes); + XClassHint ch; + ch.res_name = NULL; + ch.res_class = NULL; + XGetClassHint (QX11Info::display(), xm->window, &ch); + if(QString(ch.res_name) == "ukui-screensaver-dialog"){ + // screensaverIsShow = true; + return false; + } + laterActivate(); + } + return false; +} diff --git a/screensaver-focus-helper/blackwindow.h b/screensaver-focus-helper/blackwindow.h new file mode 100644 index 0000000..9f30d24 --- /dev/null +++ b/screensaver-focus-helper/blackwindow.h @@ -0,0 +1,25 @@ +#ifndef BLACKWINDOW_H +#define BLACKWINDOW_H + +#include +#include + +class BlackWindow : public QWidget, public QAbstractNativeEventFilter +{ + Q_OBJECT +public: + explicit BlackWindow(QWidget *parent = nullptr); + + virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) override; + bool eventFilter(QObject *obj, QEvent *event); +signals: + +private Q_SLOTS: + void laterActivate(); + +protected: + void paintEvent(QPaintEvent *event); + bool screensaverIsShow = false; +}; + +#endif // BLACKWINDOW_H diff --git a/screensaver-focus-helper/main.cpp b/screensaver-focus-helper/main.cpp new file mode 100644 index 0000000..35a93f6 --- /dev/null +++ b/screensaver-focus-helper/main.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "blackwindow.h" +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + BlackWindow w; + w.setGeometry(QApplication::desktop()->geometry()); + //w.activateWindow(); + w.show(); + a.exec(); + return 0; +} diff --git a/screensaver/CMakeLists.txt b/screensaver/CMakeLists.txt new file mode 100644 index 0000000..7bce0d7 --- /dev/null +++ b/screensaver/CMakeLists.txt @@ -0,0 +1,58 @@ +pkg_check_modules(X11 REQUIRED x11) +pkg_check_modules(XTST REQUIRED xtst) +pkg_check_modules(QGS REQUIRED gsettings-qt) + +include_directories(${PROJECT_BINARY_DIR}) +include_directories(${PROJECT_SOURCE_DIR}/Common) + +include_directories( + ${X11_INCLUDE_DIRS} + ${XTST_INCLUDE_DIRS} + ${QGS_INCLUDE_DIRS} + ) + +set(EXTRA_LIBS + ${EXTRA_LIBS} + ${X11_LIBRARIES} + ${XTST_LIBRARIES} + ${QGS_LIBRARIES} + Common + ) +qt5_add_resources(screensaver_SRC + default.qrc + ) + +qt5_wrap_cpp(screensaver_SRC + chinesedate.h + screensaver.h + mbackground.h + cyclelabel.h + scconfiguration.h + sleeptime.h + weathermanager.h + ../src/networkwatcher.h + ) +set(screensaver_SRC + ${screensaver_SRC} + main.cpp + chinesedate.cpp + mbackground.cpp + screensaver.cpp + cyclelabel.cpp + scconfiguration.cpp + sleeptime.cpp + weathermanager.cpp + ../src/networkwatcher.cpp + ) +add_executable(ukui-screensaver-default ${screensaver_SRC}) +target_link_libraries(ukui-screensaver-default Qt5::Core Qt5::Widgets Qt5::X11Extras Qt5::Xml Qt5::Network ${EXTRA_LIBS}) + +install(TARGETS + ukui-screensaver-default + DESTINATION lib/ukui-screensaver) + +install(FILES + language/screensaver-zh_CN.ini + language/screensaver-en_US.ini + language/screensaver-jd.ini + DESTINATION share/ukui-screensaver/language) diff --git a/screensaver/assets/default-intel.qss b/screensaver/assets/default-intel.qss new file mode 100644 index 0000000..ca19d3c --- /dev/null +++ b/screensaver/assets/default-intel.qss @@ -0,0 +1,86 @@ +QLabel#m_weatherArea { + font-size:26px; + color: #ffffff; +} +QLabel#dateOfWeek { + font-size:40px; + color: #ffffff; + text-align:left; +} +QLabel#dateOfLocaltime { + font-size:96px; + color: #ffffff; + text-align:left; +} +QLabel#dateOfDay { + font-size:32px; + color: #ffffff; + text-align:left; +} +QLabel#dateOfLunar { + font-size:18px; + color: #ffffff; +} +QLabel#clockTime { + background:rgba(255,255,255,0.15); + border-radius: 6px; + font-size:20px; + color: #ffffff; +} +QLabel#colon { + font-size:20px; + color: #ffffff; +} +QLabel#restTime { + font-size:20px; + color: #ffffff; + opacity:0.6; +} +QLabel#centerLabel { + font-size:18px; + color: #ffffff; +} +QLabel#authorLabel { + font-size:18px; + color: #ffffff; +} +QPushButton{ + background:rgba(255,255,255,0.12); +} +#escButton,#prevButton,#nextButton,#settingsButton{ + background: rgba(255, 255, 255, 12%); + border-radius:24px; + border-size:0px; + color:white; + icon-size:24px; +} +#escButton::hover,#prevButton::hover,#nextButton::hover,#settingsButton::hover +{ + background: rgba(255, 255, 255, 30%); +} +#escButton::pressed,#prevButton::pressed,#nextButton::pressed,#settingsButton::pressed +{ + background: rgba(255, 255, 255, 8%); +} +#vboxFrame{ + background: rgba(255, 255, 255, 12%); + border: 0px; + border-radius:4px; +} +#WallpaperButton{ + background: rgba(255, 255, 255, 0%); + border-radius:4px; + color:white; + icon-size:24px; +} +#WallpaperButton::hover{ + background: rgba(61, 107, 229, 255); +} +#autoSwitchLabel{ + font-size:14px; + color: #ffffff; +} +#autoSwitch{ + background: rgba(255, 255, 255, 0%); + border-radius:4px; +} diff --git a/screensaver/assets/default.qss b/screensaver/assets/default.qss new file mode 100644 index 0000000..2abe225 --- /dev/null +++ b/screensaver/assets/default.qss @@ -0,0 +1,90 @@ +QLabel#dateOfWeek { + font-size:16px; + color: #ffffff; +} +QLabel#dateOfLocaltime { + font-size:50px; + color: #ffffff; +} +QLabel#dateOfDay { + font-size:16px; + color: #ffffff; +} +QLabel#dateOfLunar { + font-size:16px; + color: #ffffff; +} +QLabel#clockTime { + background:rgba(255,255,255,0.15); + border-radius: 6px; + font-size:20px; + color: #ffffff; +} +QLabel#colon { + font-size:20px; + color: #ffffff; +} +QLabel#restTime { + font-size:20px; + color: #ffffff; + opacity:0.6; +} +QLabel#centerLabel { + font-size:36px; + color: #ffffff; +} +QLabel#authorLabel { + font-size:28px; + color: #ffffff; +} +QLabel#myText{ + font-size:18px; + border-radius: 6px; + background: rgba(255, 255, 255, 82%); + padding: 24px 48px 24px 48px; + color: #000000; +} +QPushButton{ + background:rgba(255,255,255,0.12); +} +#escButton,#prevButton,#nextButton,#settingsButton{ + background: rgba(255, 255, 255, 12%); + border-radius:24px; + border-size:0px; + color:white; + icon-size:24px; +} +#escButton::hover,#prevButton::hover,#nextButton::hover,#settingsButton::hover +{ + background: rgba(255, 255, 255, 30%); +} +#escButton::pressed,#prevButton::pressed,#nextButton::pressed,#settingsButton::pressed +{ + background: rgba(255, 255, 255, 8%); +} +#vboxFrame{ + background: rgba(255, 255, 255, 12%); + border: 0px; + border-radius:4px; +} +#WallpaperButton{ + background: rgba(255, 255, 255, 0%); + border-radius:4px; + color:white; + icon-size:24px; +} +#WallpaperButton::hover{ + background: rgba(61, 107, 229, 255); +} +#autoSwitchLabel{ + font-size:14px; + color: #ffffff; +} +#screenLabel{ + font-size:14px; + color: #ffffff; +} +#autoSwitch{ + background: rgba(255, 255, 255, 0%); + border-radius:4px; +} diff --git a/screensaver/assets/download.svg b/screensaver/assets/download.svg new file mode 100644 index 0000000..c335266 --- /dev/null +++ b/screensaver/assets/download.svg @@ -0,0 +1 @@ +下载 \ No newline at end of file diff --git a/screensaver/assets/logo-kylin.svg b/screensaver/assets/logo-kylin.svg new file mode 100644 index 0000000..d76a963 --- /dev/null +++ b/screensaver/assets/logo-kylin.svg @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/screensaver/assets/logo.svg b/screensaver/assets/logo.svg new file mode 100644 index 0000000..c7d90aa --- /dev/null +++ b/screensaver/assets/logo.svg @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/screensaver/assets/message.png b/screensaver/assets/message.png new file mode 100644 index 0000000000000000000000000000000000000000..cfe54a49c7e90d7497cdf15ac2b2cf508493d2c4 GIT binary patch literal 573 zcmV-D0>b@?P);Guv?Dd|(+ z4!eY4Tf%GL-f>N=2uoa&078cqb=a3h5LbW~NUr>2@v5BQrTAvbEjMM^DOOncp0oB% z9P6UDv!i8Bs$60{CuPe>U^!DDk下一张 \ No newline at end of file diff --git a/screensaver/assets/prev.svg b/screensaver/assets/prev.svg new file mode 100644 index 0000000..597f035 --- /dev/null +++ b/screensaver/assets/prev.svg @@ -0,0 +1 @@ +上一张 \ No newline at end of file diff --git a/screensaver/assets/setBackground.svg b/screensaver/assets/setBackground.svg new file mode 100644 index 0000000..2662a40 --- /dev/null +++ b/screensaver/assets/setBackground.svg @@ -0,0 +1 @@ +设为壁纸 \ No newline at end of file diff --git a/screensaver/assets/settings.svg b/screensaver/assets/settings.svg new file mode 100644 index 0000000..860b704 --- /dev/null +++ b/screensaver/assets/settings.svg @@ -0,0 +1 @@ +设置 \ No newline at end of file diff --git a/screensaver/assets/wallpaper.svg b/screensaver/assets/wallpaper.svg new file mode 100644 index 0000000..2662a40 --- /dev/null +++ b/screensaver/assets/wallpaper.svg @@ -0,0 +1 @@ +设为壁纸 \ No newline at end of file diff --git a/screensaver/assets/weather-icon/.DS_Store b/screensaver/assets/weather-icon/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..dbb415d6a473f77f4058a5e8260eba862d1e13ef GIT binary patch literal 14340 zcmeHNO>9(E6h5zjfv9c4mX=1tGbo5UW-6eiCZdv_I1v8 z&OPTl=iYnfiHLV(VX{{wFCtrU>Dc}V-aZ!bb+lgkf4w5FO}OLX8pMU)+eKcM+~~f> zs^sK&fCcS>)Rt7p%Yt7jkul`Rth zx%f9)_#fP(3=7G!unFlftWFGh*dO+7+ytY1U#_T=w zK<0tW18W}8`rzZzaUd9%&B)S$H{HVP|8)z`7khTAoD=f1N(7n7th{(k}G|9 z>DR^yp5KqjMVXhHOvx;+v@`fmNVxSgxJVBX4@>U+?en)As~`MG%t7GI$R+%g(=p{0 zV6Lv6>$R*Lz}Y@8xpUWvR)l?3AkM~|?X?Jexk0{*^4cBP7a$ym6=9Dd143CU}~g%=w0`B zvKHl&t;OeDUeeeV@-fj1(ZX5PQ@8%gh4ZC&kuzL^u{A+jB_M0tPx-_Hq`; zGlO5qTimg^XuMU2x7OK|qD%Sd=xT+o!}u|AFC@j(0}~zDPC1A?L#B>kRKIfE!4pKLfkaOUlB1M^zh^M_h2hUiBtOL^&&-Pj}5*Xzmb&F$ zwJNpTu$Z%*ILNZb>geGBNvqow7#Qt226f3n=ph0!sIl6(GrGUr0c*l|`SKe4J&D{2 z?Tf`^^lht*-;pnkLzZ3BjGX3r8lE!3#@k67ksrXbz2(agYWV^XYq336%$J<7vGx!P zSrF7E#j_b^*dMJ+^(5wzDN!xpe^8fHOO$=lx|FO%S!8Q@3UlF_6x1cvVskP?3#&)? z_dT#|Q=i4MR_HO=tQta;ST5+g=g79pA(PFQKWh9%FU$ZzSAbY~<&q=cMtS5grH8Kbh@$B}NOG&1(;tMttT*0ZT5no&I*S)&;_dZt{y2I(TIm}Wz)-O7L%X=LVX zWvm<0gW##oJ6;-*b)-9KHyB^OHr9Rax+Q-vvHQY`-A*Eb@8kVzSm8|4ur!u7RtK@b z>e(WrF$iB)Fe_cR&oMHwvtvwc9JPtO9q$j2ittPg2xg~|gryO-v33!QHU9MHjB+&3 zrV+X1xZ8-vI&kL7%>dEPsYEQYz{cvLb>PtO(^qcNI(L_rjHaxx(bWIF$VFX$G?#3< z5`3ns#^!E|6OR)IHNUpB7J+cbJ%LzGBckEXGzk~{aBv?*mEg)fcbC>xoAF&4;YhYp z5>Y($EhLYE3FT&)raA7&?qF>KD@)HV{sQT@!J_lHbf3`VTRUA99E|A6B)gM&AoIZe z_COC5bZ{-ull45Zw}^GJf}bURf39_?7Qbd{Rg-6|szRYpLnPhb&wu)RV9#5e%RDNZK2@-enXs2}Fn16W)xUvD zP6ns5vp{XSuh2T@V6>xWzzSX3wsH$7@A9l2|AYcOr0ba@-$r@(_;mg1DCCbqd%0N; zqy_7jBi%)5_;>s(YPa{@M-XQ{w=`X+8SfP<-cJ$@J|6jmXwh~5b>LO72AO_Ox(2Lg zVrww=!rPTC)L?odb*zuRq@s}%Hd+tu#i~2rzjCYa4H4aaPdX;nKF7k{#KcK!;d|m8 z4>~OVT4bMxt;6tx=8-S2GhPWhU#Sf+gH<)_$lx#F@MUKj|LB@FTTb#%`XC*g-_(!OG8p z-_0p>Ml>HQz-_t{b}W00ILL_4t`!%FG{TuxfHH~S;!(f?2Su6Hi;hcf?(XTm^w9`+ z({q@m+EYg4Q+%eC!ol1LJXQ)l!?P~i=l8Hf5Nn?@!NGchSn$?(e}HE1T#Lvj-K&|- zKr%9z;UJsdG5pu~BS0Pjnyw(GtEjmeV#nI5RmHn#ab-7cg)d=ih-q895kHxUs3;El zb41q~PVAEhOWRI;-S`v!)sw&sBDE2J-F_cFngR2*`h=g3os(7`N2__d4V4ESHI1cR zcN6`sdu<0vyTo}xy$L$H(VCl#rz~6dcFgm1YWt=>D<0YTKRf?h&s(y?W1oq$^S^mY zHR+t5o&PsHwVR#)vBSzJx;$h^Yc=$sf#p??{rz8G15RA)aq$uuF6yv}35Fk%x bb{`=-|7&N$KI0~yAGM>Ho&V)OaQ^=XP&-fC literal 0 HcmV?d00001 diff --git a/screensaver/assets/weather-icon/._.DS_Store b/screensaver/assets/weather-icon/._.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..9ad849cdb9e467065f3aa98e6e57ed2552be42dd GIT binary patch literal 4096 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIhYCu0iY;W;207T zWIgNTe~1o-3apAo1xG_*Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0qLx9R5zz8%C zguy^ABqOs}p(wRDzqBYhRUs|EC|e;juOv0EBr`uRF(;=|AtyDhL?J0BF)tg~)rIOB Q(iE!y;a-tpko*510KSwTa{vGU literal 0 HcmV?d00001 diff --git a/screensaver/assets/weather-icon/._511-中度霾-Moderate haze.svg b/screensaver/assets/weather-icon/._511-中度霾-Moderate haze.svg new file mode 100644 index 0000000000000000000000000000000000000000..58f211943d70ee60f320ccb2b59d00fd654894f7 GIT binary patch literal 4096 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103vvYvJF zKST$^9-#6FG%X<0LE-`o3{uJYxq68O1v#mDIf=z3rNyZ!DTyVi$^pSpJ!gQl2Ll5? zLS1fRat=^%X~3U2keaI}4k?3ZCI+!>ot}T*`EafD+n&eWW~dGl9R;HyFd71*Aut*O zqaiRF0;3@?8UmvsFd71*Aut*OBP9eFfgS~6Fpvw$$ShVUN-fSWElN&RNXswER>;gN rNzE(C%+E{A$*EMxNlhzJNXkjf%Lev^p}K}Nh3bE}S7aFE{{II6H-tMt literal 0 HcmV?d00001 diff --git a/screensaver/assets/weather-icon/100.svg b/screensaver/assets/weather-icon/100.svg new file mode 100644 index 0000000..26f3a2a --- /dev/null +++ b/screensaver/assets/weather-icon/100.svg @@ -0,0 +1 @@ +100-晴-SunnyClear \ No newline at end of file diff --git a/screensaver/assets/weather-icon/101.svg b/screensaver/assets/weather-icon/101.svg new file mode 100644 index 0000000..7fa3796 --- /dev/null +++ b/screensaver/assets/weather-icon/101.svg @@ -0,0 +1 @@ +101-多云-Cloudy \ No newline at end of file diff --git a/screensaver/assets/weather-icon/102.svg b/screensaver/assets/weather-icon/102.svg new file mode 100644 index 0000000..0bd2141 --- /dev/null +++ b/screensaver/assets/weather-icon/102.svg @@ -0,0 +1 @@ +102-少云-Few Clouds \ No newline at end of file diff --git a/screensaver/assets/weather-icon/103.svg b/screensaver/assets/weather-icon/103.svg new file mode 100644 index 0000000..ebd0846 --- /dev/null +++ b/screensaver/assets/weather-icon/103.svg @@ -0,0 +1 @@ +103-晴间多云-Partly Cloudy \ No newline at end of file diff --git a/screensaver/assets/weather-icon/104.svg b/screensaver/assets/weather-icon/104.svg new file mode 100644 index 0000000..1fafb80 --- /dev/null +++ b/screensaver/assets/weather-icon/104.svg @@ -0,0 +1 @@ +104-阴-Overcast \ No newline at end of file diff --git a/screensaver/assets/weather-icon/200.svg b/screensaver/assets/weather-icon/200.svg new file mode 100644 index 0000000..1c5af38 --- /dev/null +++ b/screensaver/assets/weather-icon/200.svg @@ -0,0 +1 @@ +200-有风-Windy \ No newline at end of file diff --git a/screensaver/assets/weather-icon/201.svg b/screensaver/assets/weather-icon/201.svg new file mode 100644 index 0000000..61a0d38 --- /dev/null +++ b/screensaver/assets/weather-icon/201.svg @@ -0,0 +1 @@ +201-平静-Calm \ No newline at end of file diff --git a/screensaver/assets/weather-icon/202.svg b/screensaver/assets/weather-icon/202.svg new file mode 100644 index 0000000..f6299d0 --- /dev/null +++ b/screensaver/assets/weather-icon/202.svg @@ -0,0 +1 @@ +202-微风-Light Breeze \ No newline at end of file diff --git a/screensaver/assets/weather-icon/203.svg b/screensaver/assets/weather-icon/203.svg new file mode 100644 index 0000000..0d759fc --- /dev/null +++ b/screensaver/assets/weather-icon/203.svg @@ -0,0 +1 @@ +203-和风-Moderate Gentle Breeze \ No newline at end of file diff --git a/screensaver/assets/weather-icon/204.svg b/screensaver/assets/weather-icon/204.svg new file mode 100644 index 0000000..f7b5410 --- /dev/null +++ b/screensaver/assets/weather-icon/204.svg @@ -0,0 +1 @@ +204-清风-Fresh Breeze \ No newline at end of file diff --git a/screensaver/assets/weather-icon/205.svg b/screensaver/assets/weather-icon/205.svg new file mode 100644 index 0000000..e84f495 --- /dev/null +++ b/screensaver/assets/weather-icon/205.svg @@ -0,0 +1 @@ +205-强风劲风-Strong Breeze \ No newline at end of file diff --git a/screensaver/assets/weather-icon/206.svg b/screensaver/assets/weather-icon/206.svg new file mode 100644 index 0000000..0e1a298 --- /dev/null +++ b/screensaver/assets/weather-icon/206.svg @@ -0,0 +1 @@ +206-疾风-High Wind, Near Gale \ No newline at end of file diff --git a/screensaver/assets/weather-icon/207.svg b/screensaver/assets/weather-icon/207.svg new file mode 100644 index 0000000..d9337d4 --- /dev/null +++ b/screensaver/assets/weather-icon/207.svg @@ -0,0 +1 @@ +207-大风-Gale \ No newline at end of file diff --git a/screensaver/assets/weather-icon/208.svg b/screensaver/assets/weather-icon/208.svg new file mode 100644 index 0000000..f98d914 --- /dev/null +++ b/screensaver/assets/weather-icon/208.svg @@ -0,0 +1 @@ +208-烈风Strong Gale \ No newline at end of file diff --git a/screensaver/assets/weather-icon/209.svg b/screensaver/assets/weather-icon/209.svg new file mode 100644 index 0000000..fe17ad5 --- /dev/null +++ b/screensaver/assets/weather-icon/209.svg @@ -0,0 +1 @@ +209-风暴-Storm \ No newline at end of file diff --git a/screensaver/assets/weather-icon/210.svg b/screensaver/assets/weather-icon/210.svg new file mode 100644 index 0000000..decfa5d --- /dev/null +++ b/screensaver/assets/weather-icon/210.svg @@ -0,0 +1 @@ +210-狂爆风-Violent Storm \ No newline at end of file diff --git a/screensaver/assets/weather-icon/211.svg b/screensaver/assets/weather-icon/211.svg new file mode 100644 index 0000000..99103df --- /dev/null +++ b/screensaver/assets/weather-icon/211.svg @@ -0,0 +1 @@ +211-飓风-Hurricane \ No newline at end of file diff --git a/screensaver/assets/weather-icon/212.svg b/screensaver/assets/weather-icon/212.svg new file mode 100644 index 0000000..9bbbc3a --- /dev/null +++ b/screensaver/assets/weather-icon/212.svg @@ -0,0 +1 @@ +212-龙卷风-Tornado \ No newline at end of file diff --git a/screensaver/assets/weather-icon/213.svg b/screensaver/assets/weather-icon/213.svg new file mode 100644 index 0000000..6c5b028 --- /dev/null +++ b/screensaver/assets/weather-icon/213.svg @@ -0,0 +1 @@ +213-热带风暴Tropical Storm \ No newline at end of file diff --git a/screensaver/assets/weather-icon/300.svg b/screensaver/assets/weather-icon/300.svg new file mode 100644 index 0000000..94af903 --- /dev/null +++ b/screensaver/assets/weather-icon/300.svg @@ -0,0 +1 @@ +300-阵雨-Shower Rain \ No newline at end of file diff --git a/screensaver/assets/weather-icon/301.svg b/screensaver/assets/weather-icon/301.svg new file mode 100644 index 0000000..3e68269 --- /dev/null +++ b/screensaver/assets/weather-icon/301.svg @@ -0,0 +1 @@ +301-强阵雨-Heavy Shower Rain \ No newline at end of file diff --git a/screensaver/assets/weather-icon/302.svg b/screensaver/assets/weather-icon/302.svg new file mode 100644 index 0000000..7a8679d --- /dev/null +++ b/screensaver/assets/weather-icon/302.svg @@ -0,0 +1 @@ +302-雷阵雨-Thundershower \ No newline at end of file diff --git a/screensaver/assets/weather-icon/303.svg b/screensaver/assets/weather-icon/303.svg new file mode 100644 index 0000000..9f0910a --- /dev/null +++ b/screensaver/assets/weather-icon/303.svg @@ -0,0 +1 @@ +303-强雷阵雨-Heavy Thunderstorm \ No newline at end of file diff --git a/screensaver/assets/weather-icon/304.svg b/screensaver/assets/weather-icon/304.svg new file mode 100644 index 0000000..afbac16 --- /dev/null +++ b/screensaver/assets/weather-icon/304.svg @@ -0,0 +1 @@ +304-雷阵雨伴有冰雹-hailThundershower with hail \ No newline at end of file diff --git a/screensaver/assets/weather-icon/305.svg b/screensaver/assets/weather-icon/305.svg new file mode 100644 index 0000000..e3a8649 --- /dev/null +++ b/screensaver/assets/weather-icon/305.svg @@ -0,0 +1 @@ +305-小雨-Light Rain \ No newline at end of file diff --git a/screensaver/assets/weather-icon/306.svg b/screensaver/assets/weather-icon/306.svg new file mode 100644 index 0000000..d106107 --- /dev/null +++ b/screensaver/assets/weather-icon/306.svg @@ -0,0 +1 @@ +306-中雨-Moderate Rain \ No newline at end of file diff --git a/screensaver/assets/weather-icon/307.svg b/screensaver/assets/weather-icon/307.svg new file mode 100644 index 0000000..fed2a07 --- /dev/null +++ b/screensaver/assets/weather-icon/307.svg @@ -0,0 +1 @@ +307-大雨-Heavy Rain \ No newline at end of file diff --git a/screensaver/assets/weather-icon/308.svg b/screensaver/assets/weather-icon/308.svg new file mode 100644 index 0000000..5659581 --- /dev/null +++ b/screensaver/assets/weather-icon/308.svg @@ -0,0 +1 @@ +308-极端降雨-Extreme Rain \ No newline at end of file diff --git a/screensaver/assets/weather-icon/309.svg b/screensaver/assets/weather-icon/309.svg new file mode 100644 index 0000000..a140288 --- /dev/null +++ b/screensaver/assets/weather-icon/309.svg @@ -0,0 +1 @@ +309-毛毛雨细雨-Drizzle Rain \ No newline at end of file diff --git a/screensaver/assets/weather-icon/310.svg b/screensaver/assets/weather-icon/310.svg new file mode 100644 index 0000000..95f74da --- /dev/null +++ b/screensaver/assets/weather-icon/310.svg @@ -0,0 +1 @@ +310-暴雨-Storm \ No newline at end of file diff --git a/screensaver/assets/weather-icon/311.svg b/screensaver/assets/weather-icon/311.svg new file mode 100644 index 0000000..312cd20 --- /dev/null +++ b/screensaver/assets/weather-icon/311.svg @@ -0,0 +1 @@ +311-大暴雨-Heavy Storm \ No newline at end of file diff --git a/screensaver/assets/weather-icon/312.svg b/screensaver/assets/weather-icon/312.svg new file mode 100644 index 0000000..189f4a8 --- /dev/null +++ b/screensaver/assets/weather-icon/312.svg @@ -0,0 +1 @@ +312-特大暴雨-Severe Storm \ No newline at end of file diff --git a/screensaver/assets/weather-icon/313.svg b/screensaver/assets/weather-icon/313.svg new file mode 100644 index 0000000..1994d7b --- /dev/null +++ b/screensaver/assets/weather-icon/313.svg @@ -0,0 +1 @@ +313-冻雨-Freezing Rain \ No newline at end of file diff --git a/screensaver/assets/weather-icon/314.svg b/screensaver/assets/weather-icon/314.svg new file mode 100644 index 0000000..4fae886 --- /dev/null +++ b/screensaver/assets/weather-icon/314.svg @@ -0,0 +1 @@ +314-小到中雨-Light to moderate rain \ No newline at end of file diff --git a/screensaver/assets/weather-icon/315.svg b/screensaver/assets/weather-icon/315.svg new file mode 100644 index 0000000..735772e --- /dev/null +++ b/screensaver/assets/weather-icon/315.svg @@ -0,0 +1 @@ +315-中到大雨-Moderate to heavy rain \ No newline at end of file diff --git a/screensaver/assets/weather-icon/316.svg b/screensaver/assets/weather-icon/316.svg new file mode 100644 index 0000000..57da6bc --- /dev/null +++ b/screensaver/assets/weather-icon/316.svg @@ -0,0 +1 @@ +316-大到暴雨-Heavy rain to storm \ No newline at end of file diff --git a/screensaver/assets/weather-icon/317.svg b/screensaver/assets/weather-icon/317.svg new file mode 100644 index 0000000..c9790eb --- /dev/null +++ b/screensaver/assets/weather-icon/317.svg @@ -0,0 +1 @@ +317-暴雨到大暴雨-Storm to heavy storm \ No newline at end of file diff --git a/screensaver/assets/weather-icon/318.svg b/screensaver/assets/weather-icon/318.svg new file mode 100644 index 0000000..7f39b9d --- /dev/null +++ b/screensaver/assets/weather-icon/318.svg @@ -0,0 +1 @@ +318-大暴雨到特大暴雨-Heavy to severe storm \ No newline at end of file diff --git a/screensaver/assets/weather-icon/399.svg b/screensaver/assets/weather-icon/399.svg new file mode 100644 index 0000000..69ddae1 --- /dev/null +++ b/screensaver/assets/weather-icon/399.svg @@ -0,0 +1 @@ +399-雨-Rain \ No newline at end of file diff --git a/screensaver/assets/weather-icon/400.svg b/screensaver/assets/weather-icon/400.svg new file mode 100644 index 0000000..7b56e19 --- /dev/null +++ b/screensaver/assets/weather-icon/400.svg @@ -0,0 +1 @@ +400-小雪-Light Snow \ No newline at end of file diff --git a/screensaver/assets/weather-icon/401.svg b/screensaver/assets/weather-icon/401.svg new file mode 100644 index 0000000..0c88512 --- /dev/null +++ b/screensaver/assets/weather-icon/401.svg @@ -0,0 +1 @@ +401-中雪-Moderate Snow \ No newline at end of file diff --git a/screensaver/assets/weather-icon/402.svg b/screensaver/assets/weather-icon/402.svg new file mode 100644 index 0000000..aeb88cd --- /dev/null +++ b/screensaver/assets/weather-icon/402.svg @@ -0,0 +1 @@ +402-大雪-Heavy Snow \ No newline at end of file diff --git a/screensaver/assets/weather-icon/403.svg b/screensaver/assets/weather-icon/403.svg new file mode 100644 index 0000000..925b7f6 --- /dev/null +++ b/screensaver/assets/weather-icon/403.svg @@ -0,0 +1 @@ +403-暴雪-Snowstorm \ No newline at end of file diff --git a/screensaver/assets/weather-icon/404.svg b/screensaver/assets/weather-icon/404.svg new file mode 100644 index 0000000..a4c1177 --- /dev/null +++ b/screensaver/assets/weather-icon/404.svg @@ -0,0 +1 @@ +404-雨夹雪-Sleet \ No newline at end of file diff --git a/screensaver/assets/weather-icon/405.svg b/screensaver/assets/weather-icon/405.svg new file mode 100644 index 0000000..f9769f9 --- /dev/null +++ b/screensaver/assets/weather-icon/405.svg @@ -0,0 +1 @@ +405-雨雪天气-Rain And Snow \ No newline at end of file diff --git a/screensaver/assets/weather-icon/406.svg b/screensaver/assets/weather-icon/406.svg new file mode 100644 index 0000000..5c0dfa2 --- /dev/null +++ b/screensaver/assets/weather-icon/406.svg @@ -0,0 +1 @@ +406-阵雨夹雪-Shower Snow \ No newline at end of file diff --git a/screensaver/assets/weather-icon/407.svg b/screensaver/assets/weather-icon/407.svg new file mode 100644 index 0000000..7851fed --- /dev/null +++ b/screensaver/assets/weather-icon/407.svg @@ -0,0 +1 @@ +407-阵雪-Snow Flurry \ No newline at end of file diff --git a/screensaver/assets/weather-icon/408.svg b/screensaver/assets/weather-icon/408.svg new file mode 100644 index 0000000..a5a6ea3 --- /dev/null +++ b/screensaver/assets/weather-icon/408.svg @@ -0,0 +1 @@ +408-小到中雪-Light to moderate snow \ No newline at end of file diff --git a/screensaver/assets/weather-icon/409.svg b/screensaver/assets/weather-icon/409.svg new file mode 100644 index 0000000..d9011e1 --- /dev/null +++ b/screensaver/assets/weather-icon/409.svg @@ -0,0 +1 @@ +409-中到大雪-Moderate to heavy snow \ No newline at end of file diff --git a/screensaver/assets/weather-icon/410.svg b/screensaver/assets/weather-icon/410.svg new file mode 100644 index 0000000..32d3cc8 --- /dev/null +++ b/screensaver/assets/weather-icon/410.svg @@ -0,0 +1 @@ +410-大到暴雪-Heavy snow to snowstorm \ No newline at end of file diff --git a/screensaver/assets/weather-icon/499.svg b/screensaver/assets/weather-icon/499.svg new file mode 100644 index 0000000..a1b390d --- /dev/null +++ b/screensaver/assets/weather-icon/499.svg @@ -0,0 +1 @@ +499-雪-snow \ No newline at end of file diff --git a/screensaver/assets/weather-icon/500.svg b/screensaver/assets/weather-icon/500.svg new file mode 100644 index 0000000..447377b --- /dev/null +++ b/screensaver/assets/weather-icon/500.svg @@ -0,0 +1 @@ +500-薄雾-Mist \ No newline at end of file diff --git a/screensaver/assets/weather-icon/501.svg b/screensaver/assets/weather-icon/501.svg new file mode 100644 index 0000000..69f35ef --- /dev/null +++ b/screensaver/assets/weather-icon/501.svg @@ -0,0 +1 @@ +501-雾-Foggy \ No newline at end of file diff --git a/screensaver/assets/weather-icon/502.svg b/screensaver/assets/weather-icon/502.svg new file mode 100644 index 0000000..965a144 --- /dev/null +++ b/screensaver/assets/weather-icon/502.svg @@ -0,0 +1 @@ +502-霾-Haze \ No newline at end of file diff --git a/screensaver/assets/weather-icon/503.svg b/screensaver/assets/weather-icon/503.svg new file mode 100644 index 0000000..f13b1ca --- /dev/null +++ b/screensaver/assets/weather-icon/503.svg @@ -0,0 +1 @@ +503-扬沙-Sand \ No newline at end of file diff --git a/screensaver/assets/weather-icon/504.svg b/screensaver/assets/weather-icon/504.svg new file mode 100644 index 0000000..2f03641 --- /dev/null +++ b/screensaver/assets/weather-icon/504.svg @@ -0,0 +1 @@ +504-浮尘-Dust \ No newline at end of file diff --git a/screensaver/assets/weather-icon/507.svg b/screensaver/assets/weather-icon/507.svg new file mode 100644 index 0000000..e420e64 --- /dev/null +++ b/screensaver/assets/weather-icon/507.svg @@ -0,0 +1 @@ +507-沙尘暴-Duststorm \ No newline at end of file diff --git a/screensaver/assets/weather-icon/508.svg b/screensaver/assets/weather-icon/508.svg new file mode 100644 index 0000000..299f3ba --- /dev/null +++ b/screensaver/assets/weather-icon/508.svg @@ -0,0 +1 @@ +508-强沙尘暴-Sandstorm \ No newline at end of file diff --git a/screensaver/assets/weather-icon/509.svg b/screensaver/assets/weather-icon/509.svg new file mode 100644 index 0000000..6c44bfd --- /dev/null +++ b/screensaver/assets/weather-icon/509.svg @@ -0,0 +1 @@ +509-浓雾-Dense fog \ No newline at end of file diff --git a/screensaver/assets/weather-icon/510.svg b/screensaver/assets/weather-icon/510.svg new file mode 100644 index 0000000..b1b02d5 --- /dev/null +++ b/screensaver/assets/weather-icon/510.svg @@ -0,0 +1 @@ +510-强浓雾-Strong fog \ No newline at end of file diff --git a/screensaver/assets/weather-icon/511.svg b/screensaver/assets/weather-icon/511.svg new file mode 100644 index 0000000..475d564 --- /dev/null +++ b/screensaver/assets/weather-icon/511.svg @@ -0,0 +1 @@ +511-中度霾-Moderate haze \ No newline at end of file diff --git a/screensaver/assets/weather-icon/512.svg b/screensaver/assets/weather-icon/512.svg new file mode 100644 index 0000000..c72f806 --- /dev/null +++ b/screensaver/assets/weather-icon/512.svg @@ -0,0 +1 @@ +512-重度霾-Heavy haze \ No newline at end of file diff --git a/screensaver/assets/weather-icon/513.svg b/screensaver/assets/weather-icon/513.svg new file mode 100644 index 0000000..c788e77 --- /dev/null +++ b/screensaver/assets/weather-icon/513.svg @@ -0,0 +1 @@ +513-严重霾-Severe haze \ No newline at end of file diff --git a/screensaver/assets/weather-icon/514.svg b/screensaver/assets/weather-icon/514.svg new file mode 100644 index 0000000..c2a618c --- /dev/null +++ b/screensaver/assets/weather-icon/514.svg @@ -0,0 +1 @@ +514-大雾-Heavy fog \ No newline at end of file diff --git a/screensaver/assets/weather-icon/515.svg b/screensaver/assets/weather-icon/515.svg new file mode 100644 index 0000000..30513e7 --- /dev/null +++ b/screensaver/assets/weather-icon/515.svg @@ -0,0 +1 @@ +515-特强浓雾-Extra heavy fog \ No newline at end of file diff --git a/screensaver/assets/weather-icon/900.svg b/screensaver/assets/weather-icon/900.svg new file mode 100644 index 0000000..2208903 --- /dev/null +++ b/screensaver/assets/weather-icon/900.svg @@ -0,0 +1 @@ +900-热-Hot \ No newline at end of file diff --git a/screensaver/assets/weather-icon/901.svg b/screensaver/assets/weather-icon/901.svg new file mode 100644 index 0000000..ad9fd2c --- /dev/null +++ b/screensaver/assets/weather-icon/901.svg @@ -0,0 +1 @@ +901-冷-Cold \ No newline at end of file diff --git a/screensaver/assets/weather-icon/999.svg b/screensaver/assets/weather-icon/999.svg new file mode 100644 index 0000000..0e8ce03 --- /dev/null +++ b/screensaver/assets/weather-icon/999.svg @@ -0,0 +1,16 @@ + + + 天气 + + + + + + + + + + + + + \ No newline at end of file diff --git a/screensaver/chinesedate.cpp b/screensaver/chinesedate.cpp new file mode 100644 index 0000000..811e504 --- /dev/null +++ b/screensaver/chinesedate.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ + +#include "chinesedate.h" +#include +#include +#include +unsigned int chineseDays[201]={0x04bd8,0x04ae0,0x0a570,0x054d5,0x0d260,0x0d950,0x16554,0x056a0,0x09ad0,0x055d2,//1900-1909 + 0x04ae0,0x0a5b6,0x0a4d0,0x0d250,0x1d255,0x0b540,0x0d6a0,0x0ada2,0x095b0,0x14977,//1910-1919 + 0x04970,0x0a4b0,0x0b4b5,0x06a50,0x06d40,0x1ab54,0x02b60,0x09570,0x052f2,0x04970,//1920-1929 + 0x06566,0x0d4a0,0x0ea50,0x16a95,0x05ad0,0x02b60,0x186e3,0x092e0,0x1c8d7,0x0c950,//1930-1939 + 0x0d4a0,0x1d8a6,0x0b550,0x056a0,0x1a5b4,0x025d0,0x092d0,0x0d2b2,0x0a950,0x0b557,//1940-1949 + 0x06ca0,0x0b550,0x15355,0x04da0,0x0a5b0,0x14573,0x052b0,0x0a9a8,0x0e950,0x06aa0,//1950-1959 + 0x0aea6,0x0ab50,0x04b60,0x0aae4,0x0a570,0x05260,0x0f263,0x0d950,0x05b57,0x056a0,//1960-1969 + 0x096d0,0x04dd5,0x04ad0,0x0a4d0,0x0d4d4,0x0d250,0x0d558,0x0b540,0x0b6a0,0x195a6,//1970-1979 + 0x095b0,0x049b0,0x0a974,0x0a4b0,0x0b27a,0x06a50,0x06d40,0x0af46,0x0ab60,0x09570,//1980-1989 + 0x04af5,0x04970,0x064b0,0x074a3,0x0ea50,0x06b58,0x05ac0,0x0ab60,0x096d5,0x092e0,//1990-1999 + 0x0c960,0x0d954,0x0d4a0,0x0da50,0x07552,0x056a0,0x0abb7,0x025d0,0x092d0,0x0cab5,//2000-2009 + 0x0a950,0x0b4a0,0x0baa4,0x0ad50,0x055d9,0x04ba0,0x0a5b0,0x15176,0x052b0,0x0a930,//2010-2019 + 0x07954,0x06aa0,0x0ad50,0x05b52,0x04b60,0x0a6e6,0x0a4e0,0x0d260,0x0ea65,0x0d530,//2020-2029 + 0x05aa0,0x076a3,0x096d0,0x04afb,0x04ad0,0x0a4d0,0x1d0b6,0x0d250,0x0d520,0x0dd45,//2030-2039 + 0x0b5a0,0x056d0,0x055b2,0x049b0,0x0a577,0x0a4b0,0x0aa50,0x1b255,0x06d20,0x0ada0,//2040-2049 + /**Add By JJonline@JJonline.Cn**/ + 0x14b63,0x09370,0x049f8,0x04970,0x064b0,0x168a6,0x0ea50, 0x06b20,0x1a6c4,0x0aae0,//2050-2059 + 0x092e0,0x0d2e3,0x0c960,0x0d557,0x0d4a0,0x0da50,0x05d55,0x056a0,0x0a6d0,0x055d4,//2060-2069 + 0x052d0,0x0a9b8,0x0a950,0x0b4a0,0x0b6a6,0x0ad50,0x055a0,0x0aba4,0x0a5b0,0x052b0,//2070-2079 + 0x0b273,0x06930,0x07337,0x06aa0,0x0ad50,0x14b55,0x04b60,0x0a570,0x054e4,0x0d160,//2080-2089 + 0x0e968,0x0d520,0x0daa0,0x16aa6,0x056d0,0x04ae0,0x0a9d4,0x0a2d0,0x0d150,0x0f252,//2090-2099 + 0x0d520}; + +int Month[12] = {31,28,31,30,31,30,31,31,30,31,30,31}; +ChineseDate::ChineseDate() +{ + ChineseMonth<<""<<"正"<<"二"<<"三"<<"四"<<"五"<<"六"<<"七"<<"八"<<"九"<<"十"<<"冬"<<"腊"; + ChineseDay<<""<<"一"<<"二"<<"三"<<"四"<<"五"<<"六"<<"七"<<"八"<<"九"; + ChineseDays<<"初"<<"十"<<"廿"<<"三十"; + +} + +int ChineseDate::getYearDays(int year) +{ + int sum = 12 * 29; + for (unsigned int i = 0x8000; i > 0x8; i = i >> 1) { + sum += chineseDays[year - 1900] & i ? 1 : 0; + } + sum += getLeapMonthDays(year); + return sum; +} + +bool ChineseDate::isLeapMonth(int year) +{ + return chineseDays[year - 1900] & 0xf ? true : false; +} + +int ChineseDate::getLeapMonthDays(int year) +{ + if(isLeapMonth(year)) + return chineseDays[year-1900]& 0x10000 ? 30 : 29; + return 0; +} + +int ChineseDate::getLeapMonth(int year) +{ + if(isLeapMonth(year)) + return chineseDays[year-1900] & 0xf; + return 0; +} + +int ChineseDate::getYearMonthDays(int year,int month) +{ + return chineseDays[year-1900] & (0x10000 >> month) ? 30 : 29; +} + +QString ChineseDate::getChineseDays(int month, int day, bool isleap) +{ + QString res = ""; + if(isleap) + res = res + "闰"; + + res = res + ChineseMonth.at(month) + "月"; + if(day == 10) + res = res + "初十"; + else if(day == 20) + res = res + "二十"; + else if(day == 30) + res = res + "三十"; + else + res = res + ChineseDays.at(day/10) + ChineseDay.at(day%10); + return res; +} + +QString ChineseDate::getDateLunar() +{ + int leapyear,leapmonth,leapday; + QDateTime currentTime = QDateTime::currentDateTime(); + QDate oldTile(1900, 1, 31); + //获取当前日期到1900年1月31日的天数 + int offset = oldTile.daysTo(currentTime.date()); + + //获取农历年 + int i,yearlength; + for (i = 1900; i < 2101 && offset > 0; i++) { + yearlength = getYearDays(i); + offset -= yearlength; + } + if (offset < 0) { + offset += yearlength; + i--; + } + leapyear = i; + + //获取农历月 + bool isLeap = false; + int temp; + int leapMonth = getLeapMonth(leapyear); + for (i = 1; i < 13 && offset >= 0; i++) { + temp = getYearMonthDays(leapyear,i); + offset -= temp; + isLeap = false; + if (i == leapMonth) { + if (offset < 0) { + i--; + isLeap = true; + } else { + temp = getLeapMonthDays(leapyear); + offset -= temp; + } + } + } + if (offset < 0) { + if (i == leapMonth && isLeap) { + offset += temp; + isLeap = false; + } else if (i == (leapMonth + 1)) { + offset += temp; + isLeap = true; + i--; + } else { + offset += temp; + i--; + } + } + leapmonth = i; + leapday = offset + 1; + + return getChineseDays(leapmonth,leapday,isLeap); +} diff --git a/screensaver/chinesedate.h b/screensaver/chinesedate.h new file mode 100644 index 0000000..8f580fc --- /dev/null +++ b/screensaver/chinesedate.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ + +#ifndef CHINESEDATE_H +#define CHINESEDATE_H +#include +#include + +class ChineseDate +{ +public: + ChineseDate(); + QString getDateLunar(); +private: + /** + * @brief 根据农历年份获取该年天数 + * @param year 农历年份 + * @return + */ + int getYearDays(int year); + /** + * @brief 根据农历年份判断该年是否有闰月 + * @param year 农历年份 + * @return + */ + bool isLeapMonth(int year); + /** + * @brief 根据农历年份获取闰月的天数,如果存在闰月的话 + * @param year 农历年份 + * @return + */ + int getLeapMonthDays(int year); + /** + * @brief 根据农历年份获取闰月的月份 + * @param year 农历年份 + * @return + */ + int getLeapMonth(int year); + /** + * @brief 根据农历年份和月份获取当月的天数 + * @param year 农历年份 + * @param month 农历月份 + * @return + */ + int getYearMonthDays(int year,int month); + /** + * @brief 获取农历日期 + * @return + */ + QString getChineseDays(int month,int day,bool isleap); + + QStringList ChineseMonth; + QStringList ChineseDay; + QStringList ChineseDays; + +}; + +#endif // CHINESEDATE_H diff --git a/screensaver/cyclelabel.cpp b/screensaver/cyclelabel.cpp new file mode 100644 index 0000000..a97f42f --- /dev/null +++ b/screensaver/cyclelabel.cpp @@ -0,0 +1,98 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * Copyright (C) 2019 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "cyclelabel.h" + +CycleLabel::CycleLabel(QWidget *parent) + :QFrame(parent), + m_width(20), + m_height(20) +{ + this->setFixedSize(QSize(m_width, m_height)); + setWindowFlags(Qt::FramelessWindowHint); + setAttribute(Qt::WA_TranslucentBackground, true); +} + +void CycleLabel::paintEvent(QPaintEvent *){ + //启用反锯齿 + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing, true); + + drawBg(&painter); + drawSlider(&painter); +} + +void CycleLabel::setSize(QSize size) +{ + m_width = size.width(); + m_height = size.height(); + setFixedSize(size); + repaint(); +} + +void CycleLabel::drawBg(QPainter *painter) +{ + painter->save(); + + if (!checked){ + painter->setPen(QColor(0,0,0,66)); + painter->setBrush(QColor(0,0,0,66)); + } + else{ + painter->setPen(Qt::NoPen); + painter->setBrush(bgColorOn); + } + + //circle in + QRect rect(0, 0, width(), height()); + //半径为高度的一半 + int radius = rect.height() / 2; + //圆的宽度为高度 + int circleWidth = rect.height(); + + QPainterPath path; + path.moveTo(radius, rect.left()); + path.arcTo(QRectF(rect.left(), rect.top(), circleWidth, circleWidth), 90, 180); + path.lineTo(rect.width() - radius, rect.height()); + path.arcTo(QRectF(rect.width() - rect.height(), rect.top(), circleWidth, circleWidth), 270, 180); + path.lineTo(radius, rect.top()); + + painter->drawPath(path); + + painter->restore(); + +} + +void CycleLabel::drawSlider(QPainter *painter){ + painter->save(); + painter->setPen(Qt::NoPen); + + painter->setBrush(QColor("#ffffff")); + + //circle in + QRect rect(0, 0, m_width, m_height); + int sliderWidth = m_width/2; + QRect sliderRect(sliderWidth/2, sliderWidth/2, sliderWidth, sliderWidth); + painter->drawEllipse(sliderRect); + + painter->restore(); +} + + diff --git a/screensaver/cyclelabel.h b/screensaver/cyclelabel.h new file mode 100644 index 0000000..9967836 --- /dev/null +++ b/screensaver/cyclelabel.h @@ -0,0 +1,64 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * Copyright (C) 2019 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef CYCLELABEL_H +#define CYCLELABEL_H +#include +#include +#include +#include +#include +#include + +class CycleLabel : public QFrame +{ + Q_OBJECT +public: + CycleLabel(QWidget *parent = 0); + void setSize(QSize size); + +protected: + + void paintEvent(QPaintEvent *); + +private: + bool checked; + + QColor borderColorOff; + QColor bgColorOff; + QColor bgColorOn; + QColor sliderColorOff; + QColor sliderColorOn; + int space; //滑块离背景间隔 + int rectRadius; //圆角角度 + int step; //移动步长 + int startX; + int endX; + QTimer * timer; + + void drawBg(QPainter *painter); + void drawSlider(QPainter *painter); + int m_width; + int m_height; + + +}; + +#endif // CHECKBUTTON_H diff --git a/screensaver/default.qrc b/screensaver/default.qrc new file mode 100644 index 0000000..2bdb7f2 --- /dev/null +++ b/screensaver/default.qrc @@ -0,0 +1,87 @@ + + + assets/default.qss + assets/default-intel.qss + + + assets/logo.svg + assets/download.svg + assets/prev.svg + assets/next.svg + assets/setBackground.svg + assets/settings.svg + assets/wallpaper.svg + assets/logo-kylin.svg + assets/message.png + + + assets/weather-icon/100.svg + assets/weather-icon/101.svg + assets/weather-icon/102.svg + assets/weather-icon/103.svg + assets/weather-icon/104.svg + assets/weather-icon/200.svg + assets/weather-icon/201.svg + assets/weather-icon/202.svg + assets/weather-icon/203.svg + assets/weather-icon/204.svg + assets/weather-icon/205.svg + assets/weather-icon/206.svg + assets/weather-icon/207.svg + assets/weather-icon/208.svg + assets/weather-icon/209.svg + assets/weather-icon/210.svg + assets/weather-icon/211.svg + assets/weather-icon/212.svg + assets/weather-icon/213.svg + assets/weather-icon/300.svg + assets/weather-icon/301.svg + assets/weather-icon/302.svg + assets/weather-icon/303.svg + assets/weather-icon/304.svg + assets/weather-icon/305.svg + assets/weather-icon/306.svg + assets/weather-icon/307.svg + assets/weather-icon/308.svg + assets/weather-icon/309.svg + assets/weather-icon/310.svg + assets/weather-icon/311.svg + assets/weather-icon/312.svg + assets/weather-icon/313.svg + assets/weather-icon/314.svg + assets/weather-icon/315.svg + assets/weather-icon/316.svg + assets/weather-icon/317.svg + assets/weather-icon/318.svg + assets/weather-icon/399.svg + assets/weather-icon/400.svg + assets/weather-icon/401.svg + assets/weather-icon/402.svg + assets/weather-icon/403.svg + assets/weather-icon/404.svg + assets/weather-icon/405.svg + assets/weather-icon/406.svg + assets/weather-icon/407.svg + assets/weather-icon/408.svg + assets/weather-icon/409.svg + assets/weather-icon/410.svg + assets/weather-icon/499.svg + assets/weather-icon/500.svg + assets/weather-icon/501.svg + assets/weather-icon/502.svg + assets/weather-icon/503.svg + assets/weather-icon/504.svg + assets/weather-icon/507.svg + assets/weather-icon/508.svg + assets/weather-icon/509.svg + assets/weather-icon/510.svg + assets/weather-icon/511.svg + assets/weather-icon/512.svg + assets/weather-icon/513.svg + assets/weather-icon/514.svg + assets/weather-icon/515.svg + assets/weather-icon/900.svg + assets/weather-icon/901.svg + assets/weather-icon/999.svg + + diff --git a/screensaver/language/screensaver-en_US.ini b/screensaver/language/screensaver-en_US.ini new file mode 100644 index 0000000..11c66b9 --- /dev/null +++ b/screensaver/language/screensaver-en_US.ini @@ -0,0 +1,130 @@ +[1] +FL="May there be enough clouds in your life " +SL="to make a beautiful sunset." +author= +[2] +OL="And forever has no end." +author= +[3] +FL="Just because someone doesn't love you the way you want them to," +SL="doesn't mean they don't love you with all they have." +author= +[4] +OL="Happiness is good health and a bad memory." +author= +[5] +FL="The best and most beautiful things in the world can not be" +SL="seen or even touched,they must be felt with heart." +author= +[6] +FL="I am not afraid of tomorrow for I have seen" +SL="yesterday and love today." +author= +[7] +OL="Enjoy when you can,and endure when you must." +author= +[8] +FL="A friend is one of the nicest things you can have," +SL="and one of the best things you can be." +author= +[9] +OL="Only you can control your future." +author= +[10] +OL="Don't let what you cannot do interfere with what you can do." +author= +[11] +OL="Seeing is believing.But is it a truth?" +author= +[12] +FL="You don't have to see the whole staircase," +SL="just take the first step." +author= +[13] +FL="Start where you are.Use what you have." +SL="Do what you can." +author= +[14] +FL="If I had eight hours to chop down a tree," +SL="I'd spend six hours sharpening my ax." +author= +[15] +OL="Actions is the foundational key to all success." +author= +[16] +OL="Refrain from excess." +author= +[17] +FL="The longest way must have its close;" +SL="the gloomiest night will wear on to a morning." +author= +[18] +OL="Sometimes ever,sometimes never." +author= +[19] +OL="My angel,flung out of space." +author= +[20] +FL="Three thousand,see light is a cloud;" +SL="Troubles are many,would like to open is a sunny day." +author= +[21] +OL="Everything comes full circle." +author= +[22] +FL="In delay there lies no plenty,Then come kiss me," +SL="sweet and twenty,Youth's a stuff that will not endure." +author= +[23] +OL="There is always a better way." +author= +[24] +OL="No act of kindness,no matter how small,is ever wasted." +author= +[25] +FL="Laugh loudly,laugh often,and most important," +SL="laugh at yourself." +author= +[26] +FL="When you want something,at the universe conspires" +SL="in helping you to achieve it." +author= +[27] +FL="It's the possibility of having a dream come true" +SL="that makes life interesting." +author= +[28] +FL="Some people don't belong to you," +SL="but it's good to meet them." +author= +[29] +OL="Miracles happen every day." +author= +[30] +OL="You can't change the past." +author= +[31] +OL="To make each day count." +author= +[32] +FL="If you reveal your secrets to the wind you should not" +SL="blame the wind revealing them to the trees." +author= +[33] +FL="I love three things in the world:the sun,the moon and you." +SL="The sun for day,the moon for night and you forever." +author= +[34] +OL="In me the tiger sniffs the rose." +author= +[35] +OL="The longest day has an end." +author= +[36] +FL="Sometimes accompanied,sometimes alone," +SL="stay awesome all the time." +author= +[37] +FL="There are no easy answers," +SL="there's only living through the questions." +author= diff --git a/screensaver/language/screensaver-jd.ini b/screensaver/language/screensaver-jd.ini new file mode 100644 index 0000000..113d502 --- /dev/null +++ b/screensaver/language/screensaver-jd.ini @@ -0,0 +1,161 @@ +[1] +OL=没有共产党,就没有新中国! +author= +[2] +OL=解放思想,实事求是! +author= +[3] +OL=中国产生了共产党,这是开天辟地的大事变! +author= +[4] +FL=一百年来,中国共产党团结带领中国人民 +SL=书写了中华民族几千年历史上最恢宏的史诗! +author= +[5] +FL=一百年来,中国共产党团结带领中国人民进行的一切奋斗、一切牺牲、 +SL=一切创造,归结起来就是一个主题:实现中华民族伟大复兴! +author= +[6] +OL=初心易得,始终难守! +author= +[7] +FL=我们要用历史映照现实,远观未来。看清楚过去为什么能够成功, +SL=弄明白未来怎样才能继续成功! +author= +[8] +FL=中国共产党领导是党和国家的根本所在,命脉所在; +SL=是全国各族人民的利益所系、命运所系! +author= +[9] +OL=中国共产党根基在人民,血脉在人民、力量在人民! +author= +[10] +FL=中国共产党始终代表最广大人民根本利益, +SL=没有任何自己特殊的利益! +author= +[11] +FL=用马克思主义观察时代、把握时代、引领时代, +SL=继续发展当代中国马克思主义、21世纪马克思主义! +author= +[12] +FL=走自己的路是党的全部理论和实践立足点, +SL=更是党百年奋斗得出的历史结论! +author= +[13] +OL=强国必须强军,军强才能国安! +author= +[14] +OL=敢于斗争,敢于胜利,是中国共产党不可战胜的强大精神力量! +author= +[15] +FL=新时代的中国青年,要以实现中华民族伟大复兴为己任, +SL=增强做中国人的志气、骨气、底气! +author= +[16] +FL=伟大、光荣、正确的中国共产党万岁! +SL=伟大、光荣、英雄的中国人民万岁! +author= +[17] +OL=发展才是硬道理! +author= +[18] +OL=聚精会神搞建设,一心一意谋发展! +author= +[19] +OL=看准了,就大胆地试,大胆地闯! +author= +[20] +OL=人固有一死,或重于泰山,或轻于鸿毛! +SL= +author= +[21] +FL=理论联系实际,是马克思主义“活的灵魂”, +SL=是共产党人从斗争中创造新局面的思想路线! +author= +[22] +OL=用中国理论阐释中国实践,用中国实践升华中国理论! +author= +[23] +FL=理论联系实际、和人民群众紧密联系在一起、 +SL=批评与自我批评,是中国共产党的三大优良作风! +author= +[24] +FL=坚持中国特色社会主义道路自信、 +SL=理论自信、制度自信、文化自信! +author= +[25] +OL=让初心薪火相传,把使命永担在肩! +author= +[26] +OL=心有所信,方能行远! +author= +[27] +FL=面向未来,走好新时代的长征路, +SL=我们更需要坚定理想信念、矢志拼搏奋斗! +author= +[28] +FL=牢记初心使命,坚定必胜信念, +SL=发扬斗争精神,增强斗争本领! +author= +[29] +OL=发扬优良传统,承担历史使命! +author= +[30] +FL=实事求是、坚持真理,科学应变、主动求变,咬定目标、 +SL=勇往直前,走好新时代的长征路! +author= +[31] +FL=缅怀革命先烈,赓续共产党人精神血脉, +SL=坚定理想信念,砥砺革命意志! +author= +[32] +FL=革命理想高于天,理想信念之火 +SL=一经点燃就会产生巨大的精神力量! +author= +[33] +OL=信仰、信念、信心是最好的防腐剂! +author= +[34] +OL=学史明理、学史增信、学史崇德、学史力行! +author= +[35] +OL=时代是思想之母,实践是理论之源! +author= +[36] +FL=实践发展永无止境,我们认识真理、 +SL=进行理论创新就永无止境! +author= +[37] +OL=理想因其远大而为理想,信念因其执着而为信念! +author= +[38] +FL=在胜利和顺境时不骄傲不急躁, +SL=在困难和逆境时不消沉不动摇! +author= +[39] +OL=从群众中来、到群众中去! +author= +[40] +FL=中国特色社会主义道路是实现社会主义现代化的必由之路, +SL=是创造人民美好生活的必由之路! +author= +[41] +FL=人民立场是中国共产党的根本政治立场, +SL=是马克思主义政党区别于其他政党的显著标志! +author= +[42] +OL=方向决定道路,道路决定命运! +author= +[43] +OL=全党要坚定道路自信、理论自信、制度自信、文化自信! +author= +[44] +OL=得众则得国,失众则失国! +author= +[45] +OL=自信人生二百年,会当水击三千里! +author= +[46] +FL=发展是党执政兴国的第一要务, +SL=是解决中国所有问题的关键! +author= diff --git a/screensaver/language/screensaver-zh_CN.ini b/screensaver/language/screensaver-zh_CN.ini new file mode 100644 index 0000000..f8befd1 --- /dev/null +++ b/screensaver/language/screensaver-zh_CN.ini @@ -0,0 +1,161 @@ +[1] +FL=因为有悔,所以披星戴月 +SL=因为有梦,所以奋不顾身 +author= +[2] +OL=大直若屈,大巧若拙,大辩若讷。 +author=《老子》 +[3] +OL=博学之,审问之,慎思之,明辨之,笃行之。 +author=《礼记》 +[4] +OL=兼听则明,偏听则暗。 +author=《资治通鉴》 +[5] +FL=一花一世界,一叶一追寻。 +SL=一曲一场叹,一生为一人。 +author=威廉·布莱克《天真的预言》 +[6] +FL=月光下,你带着笑地向我步来, +SL=月色与雪色之间,你是第三种绝色。 +author=余光中 +[7] +OL=不要问我心里有没有你,我余光中都是你。 +author=余光中 +[8] +FL=你要是愿意,我就永远爱你, +SL=你要是不愿意,我就永远相思。 +author=王小波 +[9] +FL=草在结它的种子,风在摇它的叶子。 +SL=我们站着,不说话,就十分美好。 +author=顾城 +[10] +OL=见到你,我觉得多少适应了这个世界。 +author=村上春树 +[11] +OL=不须耳鬓常厮伴,一笑低头意已倾。 +author=朱生豪 +[12] +FL=君臣一梦,今古空名。 +SL=但远山长,云山乱,晓山青。 +author=苏轼 +[13] +FL=心如规矩,志如尺衡, +SL=平静如水,正直如绳。 +author=严遵 +[14] +OL=近水知鱼性,近山识鸟音。 +author=周希陶 +[15] +OL=此处果有可乐,我即别无所思。 +author=林语堂 +[16] +FL=心之所向,素履以往, +SL=生如逆旅,一苇以航。 +author=七堇年《尘曲》 +[17] +FL=你说,我们就山居于此吧, +SL=胭脂用尽时,桃花就开了。 +author=与谢野晶子 +[18] +OL=世间所有的相遇,都是久别重逢。 +author=白落梅 +[19] +OL=浮云一别后,流水十年间。 +author=韦应物《淮上喜会梁川故人》 +[20] +OL=最是人间留不住,朱颜辞镜花辞树。 +author=王国维《蝶恋花》 +[21] +OL=行到水穷处,坐看云起时。 +author=王维 +[22] +OL=我有一瓢酒,可以慰风尘。 +author=韦应物 +[23] +OL=墙头马上遥相顾,一见知君即断肠。 +author=白居易 +[24] +OL=人生到处知何似,应似飞鸿踏雪泥。 +author=《和子由渑池怀旧》 +[25] +OL=粗缯大布裹生涯,腹有诗书气自华。 +author=《和董传留别》 +[26] +OL=清风徐来,水波不兴。 +author=《前赤壁赋》 +[27] +OL=我有斗酒,藏之久矣,以待子不时之须。 +author=《后赤壁赋》 +[28] +FL=若是有缘,千山暮雪,万里层云,终会重逢。 +SL=若是无缘,自此一去,天涯海角,再难相会。 +author=白落梅《你若安好便是晴天》 +[29] +FL=愿你一生努力,一生被爱。 +SL=想要的都拥有,得不到的都释怀。 +author=八月长安 +[30] +OL=凌晨四点醒来,发现海棠花未眠。 +author=川端康成 +[31] +OL=无路可走的时候,就不断回到原点。 +author=东野圭吾 +[32] +OL=你要批评指点四周的风景,你首先要爬上屋顶。 +author=歌德 +[33] +OL=只有流过血的手指,才能弹出世间的绝唱。 +author=泰戈尔 +[34] +FL=幸亏时光不会倒流, +SL=否则万物一定会朝旧岁月里疾步奔跑。 +author=《迷楼》 +[35] +FL=我看到那些岁月如何奔驰, +SL=挨过了冬季,便迎来了春天。 +author=《瓦尔登湖》 +[36] +FL=整个世界展现在我们面前, +SL=期待着我们去创造,而不是去重复。 +author=毕加索 +[37] +FL=不必太纠结于当下,也不必太忧虑未来, +SL=当你经历过一些事情的时候,眼前的风景已经和从前不一样了。 +author=村上春树 +[38] +OL=三更梦醒,你是檐上落下的星。 +author=佚名 +[39] +OL=我将永远忠于自己,披星戴月的奔向理想与自由。 +author=佚名 +[40] +OL=有一天,我看了43次日落! +author=《小王子》 +[41] +OL=当太阳升到最高点的时候,影子就不见了。 +author=佚名 +[42] +OL=拯救地球好累,虽然有些疲惫但我还是会。 +author=《超人不会飞》 +[43] +OL=陌上花开,可缓缓归矣 +author=佚名 +[44] +OL=别慌,月亮也正在大海某处迷茫 +author=佚名 +[45] +OL=夏天到了,春天的不甘该统统放下了 +author=佚名 +[46] +OL=保持热爱,奔赴山海 +author=佚名 +[47] +FL=你好,是故事的开始, +SL=你要好好的,是故事的结局。 +author=佚名 +[48] +OL=工欲善其事,必先利其器。 +author=《孔子》 + diff --git a/screensaver/main.cpp b/screensaver/main.cpp new file mode 100644 index 0000000..eb40b19 --- /dev/null +++ b/screensaver/main.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ + +#include "screensaver.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" + +#define WORKING_DIRECTORY "/usr/share/ukui-screensaver" +bool bControlFlg = false;//是否控制面板窗口 + +int main(int argc, char *argv[]) +{ +#if(QT_VERSION>=QT_VERSION_CHECK(5,6,0)) + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); +#endif + QApplication a(argc, argv); + prctl(PR_SET_PDEATHSIG, SIGHUP); + //加载翻译文件 + QString locale = QLocale::system().name(); + QTranslator translator; + QString qmFile = QString(WORKING_DIRECTORY"/i18n_qm/%1.qm").arg(locale); + translator.load(qmFile); + a.installTranslator(&translator); + qDebug() << "load translation file " << qmFile; + + + QCommandLineParser parser; + QString windowId; + Screensaver s; + XWindowAttributes xwa; + + parser.setApplicationDescription("Test helper"); + parser.addHelpOption(); + parser.addVersionOption(); + parser.addPositionalArgument("source", QCoreApplication::translate("main", "Screensaver for ukui-screensaver")); + parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions); + parser.addOptions({ + {{"r", "root"}, + QCoreApplication::translate("main", "show on root window")}, + {{"w", "window-id"}, + QCoreApplication::translate("main", "show on window."), + QCoreApplication::translate("main", "window id")}, + }); + parser.process(a); + + bool onWindow = parser.isSet("window-id"); + bool onRoot = parser.isSet("root"); + + double scale = 1; + QScreen *screen = QApplication::primaryScreen(); + scale = screen->devicePixelRatio(); + + if(onWindow){ + windowId = parser.value("window-id"); + WId wid = windowId.toULong(); + QWindow* window = QWindow::fromWinId(wid); + window->setProperty("_q_embedded_native_parent_handle",QVariant(wid)); + s.winId(); + s.windowHandle()->setParent(window); + XGetWindowAttributes (QX11Info::display(), wid, &xwa); + +#ifndef USE_INTEL + XClassHint ch; + ch.res_name = NULL; + ch.res_class = NULL; + XGetClassHint (QX11Info::display(), wid, &ch); + if(ch.res_name && strcmp(ch.res_name,"ukui-control-center")==0){ + bControlFlg = true; + s.addClickedEvent(); + } +#endif + + //获取屏保所在屏幕对应的缩放比例。 + for(auto screen : QGuiApplication::screens()) + { + QPoint pos(xwa.x,xwa.y); + if(screen->geometry().contains(pos)){ + scale = screen->devicePixelRatio(); + } + } + + s.resize(xwa.width/scale + 1,xwa.height/scale + 1); + s.move(0,0); + s.show(); + } + else if(onRoot){ + bControlFlg = false; + WId wid = QX11Info::appRootWindow(); + QWindow* window = QWindow::fromWinId(wid); + window->setProperty("_q_embedded_native_parent_handle",QVariant(wid)); + s.winId(); + s.windowHandle()->setParent(window); + XGetWindowAttributes (QX11Info::display(), wid, &xwa); + qDebug()<<"xwa.width = "<. + * +**/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "mbackground.h" + +#include "commonfunc.h" + +MBackground::MBackground(): + currentIndex(0) +{ + getAllPixmap(); +} + +QString MBackground::getCurrent() +{ + if(list.count()<0) + return ""; + + if(currentIndex>=0 && currentIndex=list.count() ||currentIndex<0) + return ""; + + if(currentIndex == list.count() - 1) + currentIndex = 0; + else + currentIndex++; + + return list.at(currentIndex); +} + +QString MBackground::getPrev() +{ + if(list.count() == 1) + return list.at(0); + + if(list.count()<=0 || currentIndex>=list.count() || currentIndex<0) + return ""; + + if(currentIndex == 0) + currentIndex = list.count() - 1; + else + currentIndex--; + + return list.at(currentIndex); +} + +QString MBackground::getRand() +{ + if(list.count() <= 0) + return ""; + qsrand(time(NULL)); + currentIndex = qrand() % list.count(); + + return list.at(currentIndex); +} + +void MBackground::getAllPixmap() +{ + QDomDocument doc; + QFile *file; + file = new QFile("/usr/share/ukui-background-properties/focal-ubuntukylin-wallpapers.xml"); + if (!file->open(QIODevice::ReadOnly)) + { + qDebug()<fileName()<<" open failed"; + return ; + } + + if (!doc.setContent(file)) + { + file->close(); + return ; + } + file->close(); + file->deleteLater(); + QDomElement root = doc.documentElement();//读取根节点 + QDomNode node = root.firstChild();//读取第一个子节点 QDomNode 节点 + while (!node.isNull()) + { + QDomElement node1 = node.firstChildElement("filename"); + QString fileName = node1.text(); + QMimeDatabase db; + QMimeType mime = db.mimeTypeForFile(fileName); + if(mime.name().startsWith("image/")){ + list.append(fileName); + } + + node = node.nextSibling();//读取下一个兄弟节点 + } +} diff --git a/screensaver/mbackground.h b/screensaver/mbackground.h new file mode 100644 index 0000000..747cf1d --- /dev/null +++ b/screensaver/mbackground.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ + +#ifndef MBACKGROUND_H +#define MBACKGROUND_H + +#include +#include +#include +#include +#include +#include "scconfiguration.h" + +class MBackground +{ +public: + MBackground(); + QString getCurrent(); + QString getNext(); + QString getPrev(); + QString getRand(); + +private: + void getAllPixmap(); + + QStringList list; + int currentIndex; +}; + +#endif // MBACKGROUND_H diff --git a/screensaver/scconfiguration.cpp b/screensaver/scconfiguration.cpp new file mode 100644 index 0000000..a348df4 --- /dev/null +++ b/screensaver/scconfiguration.cpp @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#include "scconfiguration.h" +#include "commonfunc.h" +#include +#include +#include +#include + +#define GSETTINGS_SCHEMA_SCREENSAVER "org.ukui.screensaver" +#define GSETTINGS_SCHEMA_MATE_BACKGROUND "org.mate.background" +#define GSETTINGS_SCHEMA_SCREENSAVER_DEFAULT "org.ukui.screensaver-default" +#define TIME_TYPE_SCHEMA "org.ukui.control-center.panel.plugins" +#define GSETTINGS_SCHEMA_STYLE "org.ukui.style" + +SCConfiguration* SCConfiguration::instance_ = nullptr; + +SCConfiguration::SCConfiguration(QObject *parent) : + QObject(parent), + mgsettings(nullptr), + ukgsettings(nullptr), + udgsettings(nullptr), + timegsettings(nullptr), + stygsettings(nullptr) +{ + initGsettings(); +} + +SCConfiguration* SCConfiguration::instance(QObject *parent) +{ + if(instance_ == nullptr) + instance_ = new SCConfiguration(parent); + return instance_; +} + +void SCConfiguration::initGsettings() +{ + if(QGSettings::isSchemaInstalled(GSETTINGS_SCHEMA_MATE_BACKGROUND)) + mgsettings = new QGSettings(GSETTINGS_SCHEMA_MATE_BACKGROUND, "", this); + + if(QGSettings::isSchemaInstalled(GSETTINGS_SCHEMA_SCREENSAVER)) + ukgsettings = new QGSettings(GSETTINGS_SCHEMA_SCREENSAVER, "", this); + + if(QGSettings::isSchemaInstalled(GSETTINGS_SCHEMA_SCREENSAVER_DEFAULT)) + udgsettings = new QGSettings(GSETTINGS_SCHEMA_SCREENSAVER_DEFAULT,"",this); + + if(QGSettings::isSchemaInstalled(TIME_TYPE_SCHEMA)) + timegsettings = new QGSettings(TIME_TYPE_SCHEMA,"",this); + + if(QGSettings::isSchemaInstalled(GSETTINGS_SCHEMA_STYLE)) + stygsettings = new QGSettings(GSETTINGS_SCHEMA_STYLE, "", this); + + connect(udgsettings, &QGSettings::changed, + this, &SCConfiguration::onConfigurationChanged); + connect(ukgsettings, &QGSettings::changed, + this, &SCConfiguration::onConfigurationChanged); + connect(timegsettings, &QGSettings::changed, + this, &SCConfiguration::onConfigurationChanged); + connect(stygsettings, &QGSettings::changed, + this, &SCConfiguration::onConfigurationChanged); +} + +void SCConfiguration::initDefaultSettings() +{ + + +} + +void SCConfiguration::onConfigurationChanged(QString key) +{ + if(key == "cycleTime"){ + int cycleTime = getCycleTime(); + Q_EMIT cycleTimeChanged(cycleTime); + }else if(key == "automaticSwitchingEnabled"){ + bool changed = getAutoSwitch(); + Q_EMIT autoSwitchChanged(changed); + }else if(key == "backgroundPath"){ + QString path = getBackgroundPath(); + Q_EMIT backgroundPathChanged(path); + }else if(key == "mytext"){ + QString text = getMyText(); + Q_EMIT myTextChanged(text); + }else if(key == "showCustomRestTime"){ + bool ret = getCShowRestTime(); + Q_EMIT showCRestTimeChanged(ret); + }else if(key == "showUkuiRestTime"){ + bool ret = getUShowRestTime(); + Q_EMIT showURestTimeChanged(ret); + }else if(key == "textIsCenter"){ + bool ret = getTextIsCenter(); + Q_EMIT textIsCenterChanged(ret); + }else if(key == "showMessageEnabled"){ + bool ret = getMessageShowEnable(); + Q_EMIT messageShowEnableChanged(ret); + }else if(key == "messageNumber"){ + int num = getMessageNumber(); + Q_EMIT messageNumberChanged(num); + }else if(key == "hoursystem"){ + int timeType = timegsettings->get("hoursystem").toInt(); + Q_EMIT timeTypeChanged(timeType); + }else if(key == "type"){ + QString dateType = timegsettings->get("date").toString(); + Q_EMIT dateTypeChanged(dateType); + }else if(key == "menuTransparency"){ + int blur_Num = stygsettings->get("menuTransparency").toInt(); + Q_EMIT blurChanged(blur_Num); + }else if(key == "styleName"){ + QString m_curStyle = stygsettings->get("styleName").toString(); + Q_EMIT styleChanged(m_curStyle); + } +} + +QString SCConfiguration::getDefaultBackground() +{ + QString backgroundFile = ""; + if(ukgsettings){ + backgroundFile = ukgsettings->get("background").toString(); + } + + if(ispicture(backgroundFile)) + return backgroundFile; + else + return "/usr/share/backgrounds/1-warty-final-ubuntukylin.jpg"; +} + +int SCConfiguration::getTimeType() +{ + int timeType = 24; + if(timegsettings){ + QStringList keys = timegsettings->keys(); + if (keys.contains("hoursystem")) { + timeType = timegsettings->get("hoursystem").toInt(); + } + } + return timeType; +} + +QString SCConfiguration::getDateType() +{ + QString dateType = "cn"; + if(timegsettings){ + QStringList keys = timegsettings->keys(); + if (keys.contains("date")) { + dateType = timegsettings->get("date").toString(); + } + } + return dateType; +} + +bool SCConfiguration::getAutoSwitch() +{ + bool ret = false; + if(udgsettings){ + ret = udgsettings->get("automatic-switching-enabled").toBool(); + } + + return ret; +} + +bool SCConfiguration::getIsCustom() +{ + bool ret = false; + if(ukgsettings){ + ret = (ukgsettings->get("mode").toString() == "default-ukui-custom"); + } + + return ret; +} + +bool SCConfiguration::getMessageShowEnable() +{ + bool ret = false; + if(ukgsettings){ + ret = ukgsettings->get("show-message-enabled").toBool(); + } + + return ret; +} + +int SCConfiguration::getMessageNumber() +{ + bool ret = false; + if(ukgsettings){ + ret = (ukgsettings->get("mode").toString() == "default-ukui-custom"); + } + + return ret; +} + +bool SCConfiguration::getCShowRestTime() +{ + bool ret = true; + if(udgsettings){ + QStringList keys = udgsettings->keys(); + ret = udgsettings->get("show-custom-rest-time").toBool(); + } + return ret; +} + +bool SCConfiguration::getUShowRestTime() +{ + bool ret = true; + if(udgsettings){ + QStringList keys = udgsettings->keys(); + ret = udgsettings->get("show-ukui-rest-time").toBool(); + } + return ret; +} + +int SCConfiguration::getCycleTime() +{ + int cycleTime = 300; + if(udgsettings){ + cycleTime = udgsettings->get("cycle-time").toInt(); + } + return cycleTime; +} + +QString SCConfiguration::getBackgroundPath() +{ + QString backgroundPath = "/usr/share/backgrounds"; + if(udgsettings){ + backgroundPath = udgsettings->get("background-path").toString(); + } + return backgroundPath; +} + +bool SCConfiguration::getTextIsCenter() +{ + bool ret = true; + if(udgsettings){ + ret = udgsettings->get("text-is-center").toBool(); + } + return ret; +} + +QString SCConfiguration::getMyText() +{ + QString myText = ""; + if(udgsettings){ + myText = udgsettings->get("mytext").toString(); + } + return myText; +} + +int SCConfiguration::getBlurNumber() +{ + int blurNum = 50; + if(stygsettings){ + blurNum = stygsettings->get("menu-transparency").toInt(); + qDebug()<<"????nm"<get("style-name").toString(); + } + return curStyle; +} diff --git a/screensaver/scconfiguration.h b/screensaver/scconfiguration.h new file mode 100644 index 0000000..e41ba78 --- /dev/null +++ b/screensaver/scconfiguration.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#ifndef SCCONFIGURATION_H +#define SCCONFIGURATION_H + +#ifndef QT_NO_KEYWORDS +#define QT_NO_KEYWORDS +#endif + + +#include +#include + +class QGSettings; + +class SCConfiguration : public QObject +{ + Q_OBJECT +public: + explicit SCConfiguration(QObject *parent = nullptr); + static SCConfiguration *instance(QObject *parent = nullptr); + + QString getDefaultBackground(); //获取屏保默认背景 + int getTimeType(); //获取显示时间格式 + QString getDateType(); //获取日期格式 + bool getAutoSwitch(); //获取是否自动切换 + bool getCShowRestTime(); //自定义是否显示休息时间 + bool getUShowRestTime(); //UKUI是否显示休息时间 + bool getIsCustom(); //屏保是否自定义 + int getCycleTime(); //背景图片切换间隔时间 + QString getBackgroundPath(); //背景图片来源 + bool getTextIsCenter(); //文字是否居中 + QString getMyText(); //获取文字 + bool getMessageShowEnable(); //获取是否显示消息 + int getMessageNumber(); //获取消息数量 + int getBlurNumber(); + QString getcurStyle(); +public: + +public Q_SLOTS: + void onConfigurationChanged(QString key); + +Q_SIGNALS: + void cycleTimeChanged(int cycleTime); + bool autoSwitchChanged(bool changed); + QString backgroundPathChanged(QString path); + QString myTextChanged(QString text); + bool showCRestTimeChanged(bool isShow); + bool showURestTimeChanged(bool isShow); + bool textIsCenterChanged(bool isCenter); + int messageNumberChanged(int number); + bool messageShowEnableChanged(bool enabled); + int timeTypeChanged(int time); + QString dateTypeChanged(QString type); + int blurChanged(int num); + QString styleChanged(QString type); + +private: + void initGsettings(); + void initDefaultSettings(); +private: + QGSettings *mgsettings; + QGSettings *ukgsettings; + QGSettings *udgsettings; + QGSettings *timegsettings; + QGSettings *stygsettings; + + static SCConfiguration *instance_; +}; + +#endif // CONFIGURATION_H diff --git a/screensaver/screensaver.cpp b/screensaver/screensaver.cpp new file mode 100644 index 0000000..e481abe --- /dev/null +++ b/screensaver/screensaver.cpp @@ -0,0 +1,1406 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "screensaver.h" +#include +#include +#include +#include + +#include "commonfunc.h" +#include "config.h" + +#define TIME_TYPE_SCHEMA "org.ukui.control-center.panel.plugins" +#define THEME_TYPE_SCHENA "org.ukui.style" + +#define GSETTINGS_SCHEMA_SCREENSAVER "org.ukui.screensaver" +#define KEY_MESSAGE_NUMBER "message-number" +#define KEY_MESSAGE_SHOW_ENABLED "show-message-enabled" +#define KEY_HOURSYSTEM "hoursystem" +#define KEY_DATE_FORMAT "date" + +QTime Screensaver::m_currentTime = QTime::currentTime(); +extern bool bControlFlg; + +Screensaver::Screensaver(QWidget *parent): + QWidget(parent), + switchTimer(nullptr), + backgroundPath(""), + cycleTime(300), + isCustom(false), + isCShowRestTime(true), + isUShowRestTime(true), + myTextLabel(nullptr), + myPreviewLabel(nullptr),//预览label标签 + configuration(SCConfiguration::instance()), + myTextWidget(nullptr), + centerWidget(nullptr), + sleepTime(nullptr), + timer(nullptr), + background(""), + autoSwitch(nullptr), + vboxFrame(nullptr), + isAutoSwitch(false), + flag(0), + hasChanged(false), + process(nullptr), + screenLabel(nullptr), + respondClick(false), + m_weatherManager(new WeatherManager(this)) +{ + installEventFilter(this); + // setWindowFlags(Qt::X11BypassWindowManagerHint); + setUpdateCenterWidget(); + setMouseTracking(true); + qsrand(QTime(0,0,0).secsTo(QTime::currentTime())); + + isCustom = configuration->getIsCustom(); + if(isCustom){ + cycleTime = configuration->getCycleTime(); + isAutoSwitch = configuration->getAutoSwitch(); + backgroundPath = configuration->getBackgroundPath(); + isCShowRestTime = configuration->getCShowRestTime(); + textIsCenter = configuration->getTextIsCenter(); + myText = configuration->getMyText(); + }else + isUShowRestTime = configuration->getUShowRestTime(); + + initUI(); + m_background = new MBackground(); + + QString backgroundFile = configuration->getDefaultBackground(); + background = QPixmap(backgroundFile); + + QList labelList = this->findChildren(); + for(int i = 0;isetAlignment(Qt::AlignCenter); + } +#ifndef USE_INTEL + updateBackgroundPath(); + startSwitchImages(); + + connectSingles(); +#endif + QGSettings *themeSettings; + if(QGSettings::isSchemaInstalled(TIME_TYPE_SCHEMA)) + themeSettings = new QGSettings(TIME_TYPE_SCHEMA,"",this); + + connect(themeSettings, &QGSettings::changed, + this, &Screensaver::themeChanged); + + screenLabel = new QLabel(this); + screenLabel->setObjectName("screenLabel"); + screenLabel->setText(tr("Picture does not exist")); + screenLabel->adjustSize(); + screenLabel->hide(); + +} + +Screensaver::~Screensaver() +{ + +} + +void Screensaver::themeChanged() +{ + if(myTextLabel){ + QColor highLightColor = palette().color(QPalette::Base); + QString stringColor = QString("rgba(%1,%2,%3,82%)") + .arg(highLightColor.red()) + .arg(highLightColor.green()) + .arg(highLightColor.blue()); + QColor textColor = palette().color(QPalette::Text); + QString textString = QString("rgb(%1,%2,%3)") + .arg(textColor.red()) + .arg(textColor.green()) + .arg(textColor.blue()); + QColor borderColor = palette().color(QPalette::BrightText); + QString borderString = QString("rgba(%1,%2,%3,85%)") + .arg(borderColor.red()) + .arg(borderColor.green()) + .arg(borderColor.blue()); + + if(width() < 600 || height()<400) + myTextLabel->setStyleSheet(QString("font-size:5px;border-radius: 2px;background: %1;color: %2;padding: 4px 8px 4px 8px;border-width: 1px;border-style: solid;border-color:%3;") \ + .arg(stringColor).arg(textString).arg(borderString)); + else + myTextLabel->setStyleSheet(QString("font-size:18px;border-radius: 6px;background: %1;color: %2;padding: 24px 48px 24px 48px;border-width: 1px;border-style: solid;border-color:%3;") \ + .arg(stringColor).arg(textString).arg(borderString)); + } +} + +void Screensaver::connectSingles() +{ + connect(configuration, &SCConfiguration::autoSwitchChanged, + this, &Screensaver::autoSwitchChanged); + connect(configuration, &SCConfiguration::backgroundPathChanged, + this, &Screensaver::backgroundPathChanged); + connect(configuration, &SCConfiguration::cycleTimeChanged, + this, &Screensaver::cycleTimeChanged); + connect(configuration, &SCConfiguration::myTextChanged, + this, &Screensaver::myTextChanged); + connect(configuration, &SCConfiguration::showCRestTimeChanged, + this, &Screensaver::showCRestTimeChanged); + connect(configuration, &SCConfiguration::showURestTimeChanged, + this, &Screensaver::showURestTimeChanged); + connect(configuration, &SCConfiguration::textIsCenterChanged, + this, &Screensaver::textIsCenterChanged); + connect(configuration, &SCConfiguration::messageNumberChanged, + this, &Screensaver::onMessageNumberChanged); + connect(configuration, &SCConfiguration::messageShowEnableChanged, + this, &Screensaver::onMessageShowEnabledChanged); + connect(configuration, &SCConfiguration::timeTypeChanged, + this, &Screensaver::onTimeFormatChanged); + connect(configuration, &SCConfiguration::dateTypeChanged, + this, &Screensaver::onDateFormatChanged); + connect(configuration, &SCConfiguration::blurChanged, + this, &Screensaver::onBlurNumChanged); + connect(configuration, &SCConfiguration::styleChanged, + this, &Screensaver::onStyleChanged); +} + +void Screensaver::onBlurNumChanged(int num) +{ + blur_Num = num; + if(curStyle == "ukui-dark" || curStyle == "ukui-black"){ + myTextLabel->setStyleSheet(QString("QLabel{background: rgba(0, 0, 0, %1); color:#FFFFFF; border-radius:16px}").arg(blur_Num * 0.01)); + cycleLabel->setStyleSheet(QString("QLabel{background: rgba(0, 0, 0, %1); color:#FFFFFF; border-radius:8px}").arg(blur_Num * 0.01)); + } else{ + myTextLabel->setStyleSheet(QString("QLabel{background: rgba(255, 255, 255, %1); border-radius:16px}").arg(blur_Num * 0.01)); + cycleLabel->setStyleSheet(QString("QLabel{background: rgba(255, 255, 255, %1); border-radius:8px}").arg(blur_Num * 0.01)); + } +} + +void Screensaver::onStyleChanged(QString style) +{ + curStyle = style; + if(curStyle == "ukui-dark" || curStyle == "ukui-black"){ + myTextLabel->setStyleSheet(QString("QLabel{background: rgba(0, 0, 0, %1); color:#FFFFFF; border-radius:16px}").arg(blur_Num * 0.01)); + cycleLabel->setStyleSheet(QString("QLabel{background: rgba(0, 0, 0, %1); color:#FFFFFF; border-radius:8px}").arg(blur_Num * 0.01)); + } else{ + myTextLabel->setStyleSheet(QString("QLabel{background: rgba(255, 255, 255, %1); border-radius:16px}").arg(blur_Num * 0.01)); + cycleLabel->setStyleSheet(QString("QLabel{background: rgba(255, 255, 255, %1); border-radius:8px}").arg(blur_Num * 0.01)); + } +} + +void Screensaver::onTimeFormatChanged(int type){ + timeType = type; +} + +void Screensaver::onDateFormatChanged(QString type){ + dateType = type; +} + +void Screensaver::onMessageNumberChanged(int num) +{ + int number = configuration->getMessageNumber(); + (configuration->getMessageShowEnable() && number > 0) ? showNotice() : hideNotice(); +} + +void Screensaver::onMessageShowEnabledChanged(bool enabled) +{ + int number = configuration->getMessageNumber(); + (configuration->getMessageShowEnable() && number > 0) ? showNotice() : hideNotice(); +} + +void Screensaver::autoSwitchChanged(bool isSwitch) +{ + if(!isCustom) + return ; + isAutoSwitch = isSwitch; + if(!isSwitch){ + stopSwitchImages(); + } + startSwitchImages(); +} + +/* +* 图片路径改变 +*/ +void Screensaver::backgroundPathChanged(QString path) +{ + backgroundPath = path; + if(!isCustom) + return ; + updateBackgroundPath();//更新图片路径 + stopSwitchImages(); + startSwitchImages(); +} + +void Screensaver::cycleTimeChanged(int cTime) +{ + cycleTime = cTime; + if(!isCustom || !autoSwitch) + return ; + stopSwitchImages(); + startSwitchImages(); +} + +void Screensaver::myTextChanged(QString text) +{ + if(!isCustom) + return ; + + myText = text; + + if(textIsCenter && centerWidget){ + if(centerlabel1) + centerlabel1->setText(myText); + if(centerlabel2){ + centerlabel2->setText(""); + centerlabel2->hide(); + } + if(authorlabel){ + authorlabel->setText(""); + authorlabel->hide(); + } + centerWidget->adjustSize(); + + centerWidget->setGeometry((width()-centerWidget->width())/2,(height()-centerWidget->height())/2, + centerWidget->width(),centerWidget->height()); + + if((height()-centerWidget->height())/2 < timeLayout->y() + timeLayout->height()) + centerWidget->setGeometry((width()-centerWidget->width())/2,timeLayout->y() + timeLayout->height(), + centerWidget->width(),centerWidget->height()); + }else{ + setRandomText(); + setRandomPos(); + if(centerWidget) + centerWidget->hide(); + } +} + +void Screensaver::showCRestTimeChanged(bool isShow) +{ + isCShowRestTime = isShow; + if(!isCustom) + return; + + setSleeptime(isCShowRestTime); +} + +void Screensaver::showURestTimeChanged(bool isShow) +{ + isUShowRestTime = isShow; + if(isCustom) + return; + + setSleeptime(isUShowRestTime); +} + +void Screensaver::textIsCenterChanged(bool isCenter) +{ + textIsCenter = isCenter; + if(!isCustom) + return ; + + if(isCenter){ + if(myTextWidget) + myTextWidget->hide(); + if(!centerWidget){ + setCenterWidget(); + resize(width(),height()); + } + else{ + centerWidget->show(); + myTextChanged(myText); + } + }else{ + if(centerWidget) + centerWidget->hide(); + setRandomText(); + setRandomPos(); + } +} + +bool Screensaver::eventFilter(QObject *obj, QEvent *event) +{ +#ifndef USE_INTEL + if(obj == this){ + if(event->type()==QEvent::MouseButtonPress){ + if(respondClick){ + if(!process){ + process = new QProcess(this); + } + process->start("ukui-screensaver-command -s"); + } + } + } +#endif + return false; +} + +void Screensaver::paintEvent(QPaintEvent *event) +{ + QPainter painter(this); + + /*时间未同步的时候重新刷新一次,主要是避免睡眠唤醒时的时间跳变*/ +/* if (m_currentTime.hour() != QTime::currentTime().hour() || + m_currentTime.minute() != QTime::currentTime().minute()) { + updateTime(); + } +*/ + if(isCustom && imagePaths.count()==0){ + painter.setBrush(QColor("#000000")); + if(screenLabel->isHidden()){ + screenLabel->show(); + } + }else{ + if(background.isNull()){ + QColor cor = "#035290"; + painter.setBrush(cor); + painter.drawRect(-1,-1,this->width()+1,this->height()+1); + } else { + painter.drawPixmap(0,0,this->width(),this->height(), getPaddingPixmap()); + QPainterPath path; + QPainter painter1(this); + painter1.setOpacity(0.25); + painter1.setRenderHint(QPainter::Antialiasing); // 反锯齿; + painter1.setClipping(true); + painter1.setPen(Qt::transparent); + path.addRect(this->rect()); + path.setFillRule(Qt::WindingFill); + painter1.setBrush(QColor("#000000")); + painter1.setPen(Qt::transparent); + painter1.drawPath(path); + } + } + /*这里是为了不显示笔的线条*/ + //painter.drawRect(-1,-1,this->width()+1,this->height()+1); +} + +void Screensaver::isMovie() +{ + if(!currentPixmap.isEmpty()){ + currentPixmap.clear(); + currentCount = 0; + if(movieTimer){ + movieTimer->stop(); + movieTimer->deleteLater(); + movieTimer = nullptr; + } + } + if(!is_gif){ + repaint(); + return; + } + QMovie *movie = new QMovie(currentPath); + movie->setScaledSize(QSize(this->width(), this->height())); + for (int i =0; i< movie->frameCount(); ++i) { + movie->jumpToFrame(i); + currentPixmap.append(movie->currentPixmap()); + if(i == 0) + delayTime = movie->nextFrameDelay(); + } + if(!movieTimer){ + movieTimer = new QTimer(this); + connect(movieTimer, &QTimer::timeout, this, [&]{ + if(currentCount == currentPixmap.count() - 1) + currentCount = 0; + else + currentCount += 1; + background = currentPixmap.at(currentCount); + repaint(); + + }); + } + if(delayTime < 50) + movieTimer->start(50); + else + movieTimer->start(delayTime); + +} + +QPixmap Screensaver::getPaddingPixmap() +{ + if (background.width() == 0 || background.height() == 0) + { + return QPixmap(); + } + + bool useHeight; + float scaled = 0.0; + QPixmap scaledPixmap; + QPixmap paddingPixmap; + qint64 rw = qint64(this->height()) * qint64(background.width()) / qint64(background.height()); + + useHeight = (rw >= this->width()); + if (useHeight) { + scaled = float(this->height()) / float(background.height()); + scaledPixmap = background.scaled(background.width() * scaled, this->height()); + paddingPixmap = scaledPixmap.copy((background.width() * scaled - this->width()) / 2 , 0, this->width(), this->height()); + } else { + scaled = float(this->width()) / float(background.width()); + scaledPixmap = background.scaled(this->width(), background.height() * scaled); + paddingPixmap = scaledPixmap.copy(0 , (background.height() * scaled - this->height()) / 2,this->width(), this->height()); + } + + return paddingPixmap; +} + +void Screensaver::addClickedEvent(){ + respondClick = true; +} + +void Screensaver::resizeEvent(QResizeEvent */*event*/) +{ + float scale = 1.0; + scale = (float)width()/1920; + if(width() < 600 || height()<400){//当显示在控制面板上时,字体缩小三倍。 + if(flag == 0) + { + QList labelList = this->findChildren(); + for(int i = 0;ifont().pixelSize(); +#ifdef USE_INTEL + const QString SheetStyle = QString("font-size:%1px;").arg(fontsize/3); +#else + const QString SheetStyle = QString("font-size:%1px;").arg(fontsize/4); +#endif + labelList.at(i)->setStyleSheet(SheetStyle); + } + QList childList = timeLayout->findChildren(); + for (int i = 0; i < childList.count(); ++i) { + childList.at(i)->adjustSize(); + } + timeLayout->adjustSize(); + if(centerWidget) + centerWidget->adjustSize(); + } + flag = 1; +#ifndef USE_INTEL + if(myTextWidget){ + +// QColor highLightColor = palette().color(QPalette::Base); +// QString stringColor = QString("rgba(%1,%2,%3,82%)") +// .arg(highLightColor.red()) +// .arg(highLightColor.green()) +// .arg(highLightColor.blue()); +// QColor textColor = palette().color(QPalette::Text); +// QString textString = QString("rgb(%1,%2,%3)") +// .arg(textColor.red()) +// .arg(textColor.green()) +// .arg(textColor.blue()); +// QColor borderColor = palette().color(QPalette::BrightText); +// QString borderString = QString("rgba(%1,%2,%3,85%)") +// .arg(borderColor.red()) +// .arg(borderColor.green()) +// .arg(borderColor.blue()); + +// myTextLabel->setStyleSheet(QString("font-size:5px;border-radius: 2px;background: %1;color: %2;padding: 4px 8px 4px 8px;border-width: 1px;border-style: solid;border-color:%3;") \ +// .arg(stringColor).arg(textString).arg(borderString)); + if(curStyle == "ukui-dark" || curStyle == "ukui-black"){ + myTextLabel->setStyleSheet(QString("QLabel{font-size:5px;border-radius: 2px;padding: 4px 8px 4px 8px;border-width: 1px;\ + background: rgba(0, 0, 0, %1); color:#FFFFFF; border-radius:16px}").arg(blur_Num * 0.01)); + cycleLabel->setStyleSheet(QString("QLabel{background: rgba(0, 0, 0, %1); color:#FFFFFF; border-radius:8px}").arg(blur_Num * 0.01)); + } else{ + myTextLabel->setStyleSheet(QString("QLabel{font-size:5px;border-radius: 2px;padding: 4px 8px 4px 8px;border-width: 1px;\ + background: rgba(255, 255, 255, %1);}").arg(blur_Num * 0.01)); + cycleLabel->setStyleSheet(QString("QLabel{background: rgba(255, 255, 255, %1); border-radius:8px}").arg(blur_Num * 0.01)); + } + cycleLabel->setFixedSize(5, 5); + } + if(screenLabel) + screenLabel->adjustSize(); + if(sleepTime) + sleepTime->setSmallMode(); +#endif + scale = 0.1; + } + +#ifdef USE_INTEL + int x = 840*scale; + int y = 96*scale; + + m_weatherLaout->setGeometry((this->width()-m_weatherLaout->width())/2, y, m_weatherLaout->geometry().width(), m_weatherLaout->geometry().height()); + + timeLayout->setGeometry((this->width()-timeLayout->width())/2,m_weatherLaout->geometry().bottom()+33, + timeLayout->geometry().width(),timeLayout->geometry().height()); + + if(centerWidget){ + centerWidget->adjustSize(); + centerWidget->setGeometry((width()-centerWidget->width())/2,(height() * 85) / 100 - (centerWidget->height())/2, + centerWidget->width(),centerWidget->height()); + + if((height()-centerWidget->height())/2 < timeLayout->y() + timeLayout->height()) + centerWidget->setGeometry((width()-centerWidget->width())/2,timeLayout->y() + timeLayout->height(), + centerWidget->width(),centerWidget->height()); + } + + if (m_widgetNotice) + { + m_widgetNotice->setGeometry((this->width()-m_widgetNotice->width())/2, (centerWidget->y() + centerWidget->height() + 20), + m_widgetNotice->geometry().width(), m_widgetNotice->geometry().height()); + } +#else + int x = (this->width()-timeLayout->geometry().width())/2; + int y = 59*scale; + + timeLayout->setGeometry(x,y,timeLayout->geometry().width(),timeLayout->geometry().height()); + + if(sleepTime){ + x = 26*scale; + y = this->height() - sleepTime->geometry().height() - 26*scale; + sleepTime->setGeometry(x,y,sleepTime->geometry().width(),sleepTime->geometry().height()); + } + + if(screenLabel){ + screenLabel->setGeometry((width() - screenLabel->width())/2,y,screenLabel->width(),screenLabel->height()); + } + + if(centerWidget){ + centerWidget->adjustSize(); + centerWidget->setGeometry((width()-centerWidget->width())/2,(height()-centerWidget->height())/2, + centerWidget->width(),centerWidget->height()); + + if((height()-centerWidget->height())/2 < timeLayout->y() + timeLayout->height()) + centerWidget->setGeometry((width()-centerWidget->width())/2,timeLayout->y() + timeLayout->height(), + centerWidget->width(),centerWidget->height()); + } + + if(myTextWidget) + setRandomPos(); +#endif +} + +void Screensaver::setRandomPos() +{ + myTextWidget->adjustSize(); + int x1 = 10; + int x2 = width() - 10 - myTextWidget->width(); + int y1 = timeLayout->geometry().bottom() + 10; + int y2; + if(sleepTime) + y2 = sleepTime->geometry().top() - myTextWidget->height() - 10; + else + y2 = geometry().bottom() - myTextWidget->height() - 10; + + int x = 0; + int y = 0; + if(x2 > x1) + x = qrand()%(x2 - x1) + x1; + if(y2 > y1) + y = qrand()%(y2 - y1) + y1; + + myTextWidget->move(x,y); + +} + +void Screensaver::setUpdateCenterWidget() +{ + //QString lang = qgetenv("LANG"); + QString lang = QLocale::system().name(); + QString homePath=qgetenv("HOME"); + QString cwdPath="/usr/share/ukui-screensaver/"; + QString languageDirPath=cwdPath+"language/"; + QString defaultLanguageFilePath=languageDirPath+"screensaver-en_US.ini"; + qDebug()<<"homePath="< formats = QImageReader::supportedImageFormats(); + if(fileInfo.isDir()) { + QDir dir(backgroundPath); + QStringList files = dir.entryList(QDir::Files | QDir::Readable); + for(QString file : files) { + fileInfo.setFile(file); + QString suffix = fileInfo.suffix(); + if(formats.contains(suffix.toUtf8()) && file.right(4) != ".ico" && file.right(4) != ".tga"){ + // qDebug() << "file:" <=imagePaths.count() - 1 || currentIndex<0){ + currentIndex = 0; + }else{ + currentIndex ++; + } + background = QPixmap(imagePaths.at(currentIndex)); + currentPath = imagePaths.at(currentIndex); + is_gif = currentPath.endsWith(".gif"); + } + isMovie(); + }); + } + + switchTimer->start(cycleTime * 1000); + + isMovie(); + } +} + +void Screensaver::stopSwitchImages() +{ + if(switchTimer && switchTimer->isActive()) + switchTimer->stop(); +} + +void Screensaver::onBackgroundChanged() +{ + opacity = 1.0; + fadeTimer = new QTimer(this); + connect(fadeTimer, &QTimer::timeout, this, [&]{ + opacity -= 0.1; + if(opacity <= 0) + fadeTimer->stop(); + else + repaint(); + + }); + fadeTimer->start(50); +} +void Screensaver::updateCenterWidget(int index) +{ + if(!centerWidget ) + return ; + + QStringList qlist = qsettings->childGroups(); + if(qlist.count()<1) + return; + + if(index<=1){ + index = qrand() % qlist.count() + 1; + } + qsettings->beginGroup(QString::number(index)); + if(qsettings->contains("OL")){ + centerlabel1->setText(qsettings->value("OL").toString()); + centerlabel2->hide(); +#ifndef USE_INTEL + authorlabel->setText(qsettings->value("author").toString()); +#endif + } + else if(qsettings->contains("FL")) + { + centerlabel1->setText(qsettings->value("FL").toString()); + centerlabel2->setText(qsettings->value("SL").toString()); + centerlabel2->show(); +#ifndef USE_INTEL + authorlabel->setText(qsettings->value("author").toString()); +#endif + } + +#ifdef USE_INTEL + if(qsettings->contains("author") && !qsettings->value("author").toString().isEmpty()) + { + authorlabel->setText(qsettings->value("author").toString()); + authorlabel->show(); + } + else + { + authorlabel->setText(""); + authorlabel->hide(); + } +#endif + + centerWidget->adjustSize(); + +#ifdef USE_INTEL + centerWidget->setGeometry((width()-centerWidget->width())/2,(height() * 85) / 100 - (centerWidget->height())/2, + centerWidget->width(),centerWidget->height()); + + if((height()-centerWidget->height())/2 < timeLayout->y() + timeLayout->height()) + centerWidget->setGeometry((width()-centerWidget->width())/2,timeLayout->y() + timeLayout->height(), + centerWidget->width(),centerWidget->height()); + +#else + centerWidget->setGeometry((width()-centerWidget->width())/2,(height()-centerWidget->height())/2, + centerWidget->width(),centerWidget->height()); + + if((height()-centerWidget->height())/2 < timeLayout->y() + timeLayout->height()) + centerWidget->setGeometry((width()-centerWidget->width())/2,timeLayout->y() + timeLayout->height(), + centerWidget->width(),centerWidget->height()); +#endif + qsettings->endGroup(); + +} + +void Screensaver::initUI() +{ + QFile qssFile; + +#ifdef USE_INTEL + qssFile.setFileName(":/qss/assets/default-intel.qss"); +#else + qssFile.setFileName(":/qss/assets/default.qss"); +#endif + + if(qssFile.open(QIODevice::ReadOnly)) { + setStyleSheet(qssFile.readAll()); + } + qssFile.close(); + +#ifdef USE_INTEL + setWeatherLayout(); + setDatelayout(); + setCenterWidget(); + setNoticeLaout(); + m_weatherManager->getWeather(); + connect(m_weatherManager, &WeatherManager::onWeatherUpdate, + this, &Screensaver::getWeatherFinish); +#else + if(isCustom) + setSleeptime(isCShowRestTime); + else + setSleeptime(isUShowRestTime); + setDatelayout(); + setCenterWidget(); + setRandomText(); + if(textIsCenter || myText == ""){ + myTextWidget->hide(); + centerWidget->show(); + }else{ + centerWidget->hide(); + myTextWidget->show(); + } +#endif +} + +void Screensaver::setDatelayout() +{ + timeType = configuration->getTimeType(); + dateType = configuration->getDateType(); +#ifdef USE_INTEL + timeLayout = new QWidget(this); + timeLayout->setFixedSize(((timeType == 12) ? 397:326), 117); + + QHBoxLayout *htimeLayout = new QHBoxLayout(timeLayout); + htimeLayout->setContentsMargins(0,0,0,0); + + QWidget *timeWidget = new QWidget(this); + timeWidget->setFixedSize(235, 117); + QGridLayout *gtimeLayout = new QGridLayout(timeWidget); + gtimeLayout->setContentsMargins(0,0,0,12); + + this->dateOfLocaltimeHour = new QLabel(this); + this->dateOfLocaltimeHour->setObjectName("dateOfLocaltime"); + this->dateOfLocaltimeHour->setAlignment(Qt::AlignTop | Qt::AlignLeft); + this->dateOfLocaltimeHour->setFixedSize(107, 96); + + this->dateofLocaltimeColon = new QLabel(this); + this->dateofLocaltimeColon->setObjectName("dateOfLocaltime"); + this->dateofLocaltimeColon->setAlignment(Qt::AlignCenter); + this->dateofLocaltimeColon->setFixedSize(21, 96); + this->dateofLocaltimeColon->setText(":"); + + this->dateOfLocaltimeMinute = new QLabel(this); + this->dateOfLocaltimeMinute->setObjectName("dateOfLocaltime"); + this->dateOfLocaltimeMinute->setAlignment(Qt::AlignTop | Qt::AlignLeft); + this->dateOfLocaltimeMinute->setFixedSize(107, 96); + + gtimeLayout->setSpacing(0); + gtimeLayout->setRowMinimumHeight(0, 9); + gtimeLayout->setRowMinimumHeight(1, 87); + gtimeLayout->setRowMinimumHeight(2, 9); + + gtimeLayout->setColumnMinimumWidth(0, 107); + gtimeLayout->setColumnMinimumWidth(1, 21); + gtimeLayout->setColumnMinimumWidth(2, 107); + + gtimeLayout->addWidget(dateOfLocaltimeHour, 1, 0, 2, 1); + gtimeLayout->addWidget(dateofLocaltimeColon, 0, 1, 2, 1); + gtimeLayout->addWidget(dateOfLocaltimeMinute, 1, 2, 2, 1); + + QWidget *dateWidget = new QWidget(this); + dateWidget->setFixedSize(((timeType == 12) ? 154:83), 117); + QVBoxLayout *vDateLaout = new QVBoxLayout(dateWidget); + vDateLaout->setAlignment(Qt::AlignTop | Qt::AlignLeft); + vDateLaout->setContentsMargins(0,18,0,12); + + this->dateOfWeek = new QLabel(this); + this->dateOfWeek->setObjectName("dateOfWeek"); + this->dateOfWeek->setAlignment(Qt::AlignTop | Qt::AlignLeft); + this->dateOfWeek->setFixedWidth(83); + + this->dateOfDay = new QLabel(this); + this->dateOfDay->setObjectName("dateOfDay"); + this->dateOfDay->setAlignment(Qt::AlignTop | Qt::AlignLeft); + this->dateOfDay->setFixedSize(((timeType == 12) ? 154:83),32); + updateTime(); + updateDate(); + + this->dateOfWeek->adjustSize(); + vDateLaout->addWidget(dateOfWeek); + vDateLaout->setSpacing(4); + vDateLaout->addWidget(dateOfDay); + + htimeLayout->addWidget(timeWidget); + htimeLayout->addSpacing(8); + htimeLayout->addWidget(dateWidget); + #else + timeLayout = new QWidget(this); + QVBoxLayout *vtimeLayout = new QVBoxLayout(timeLayout); + + this->dateOfLocaltime = new QLabel(this); + if(timeType == 12) + this->dateOfLocaltime->setText(QDateTime::currentDateTime().toString("A hh:mm")); + else + this->dateOfLocaltime->setText(QDateTime::currentDateTime().toString("hh:mm")); + + this->dateOfLocaltime->setObjectName("dateOfLocaltime"); + this->dateOfLocaltime->setAlignment(Qt::AlignCenter); + this->dateOfLocaltime->adjustSize(); + vtimeLayout->addWidget(dateOfLocaltime); + + this->dateOfDay = new QLabel(this); + if(dateType == "cn") + this->dateOfDay->setText(QDate::currentDate().toString("yyyy/MM/dd ddd")); + else + this->dateOfDay->setText(QDate::currentDate().toString("yyyy-MM-dd ddd")); + this->dateOfDay->setObjectName("dateOfDay"); + this->dateOfDay->setAlignment(Qt::AlignCenter); + this->dateOfDay->adjustSize(); + + vtimeLayout->addWidget(this->dateOfDay); + timeLayout->adjustSize(); + updateDate(); +#endif + +} + +void Screensaver::setWeatherLayout() +{ + m_weatherLaout = new QWidget(this); + QHBoxLayout *hWeatherLayout = new QHBoxLayout(m_weatherLaout); + hWeatherLayout->setContentsMargins(0, 0, 0, 0); + + this->m_weatherIcon = new QLabel(this); + this->m_weatherArea = new QLabel(this); + this->m_weatherCond = new QLabel(this); + this->m_weatherTemperature = new QLabel(this); + + m_weatherIcon->setPixmap(m_weatherManager->getWeatherIcon()); + m_weatherArea->setText(m_weatherManager->getCityName()); + if (!m_weatherManager->getCond().isEmpty()) + m_weatherCond->setText("·" + m_weatherManager->getCond()); + m_weatherTemperature->setText(m_weatherManager->getTemperature()); + + m_weatherArea->setStyleSheet("font-size:26px;color:#ffffff"); + m_weatherCond->setStyleSheet("font-size:26px;color:#ffffff"); + m_weatherTemperature->setStyleSheet("font-size:26px;color:#ffffff"); + + hWeatherLayout->addWidget(m_weatherIcon); + hWeatherLayout->addSpacing(8); + hWeatherLayout->addWidget(m_weatherArea); + hWeatherLayout->addWidget(m_weatherCond); + hWeatherLayout->addSpacing(8); + hWeatherLayout->addWidget(m_weatherTemperature); + m_weatherLaout->adjustSize(); +} + +void Screensaver::setSleeptime(bool Isshow) +{ + if(!sleepTime) + sleepTime = new SleepTime(this); + + sleepTime->adjustSize(); + if(Isshow){ + sleepTime->show(); + } + else{ + sleepTime->hide(); + if(timer){ + timer->stop(); + } + } +} + +void Screensaver::updateDate() +{ + if(!timer){ + timer = new QTimer(this); + timer->setTimerType(Qt::PreciseTimer); + connect(timer, SIGNAL(timeout()), this, SLOT(updateTime())); + } + timer->start(800); + updateTime(); +} + +void Screensaver::updateTime() +{ +#ifdef USE_INTEL + QLocale locale(QLocale::system().name()); + + QTimeZone timeZone(QString::fromLatin1(QTimeZone::systemTimeZoneId()).toLatin1()); + QDateTime tzNow = QDateTime::currentDateTime().toTimeZone(timeZone); + + QString time; + + if (timeType == 12) + { + time = tzNow.toString("hh:mm AP").split(" ").at(0); + this->dateOfWeek->setText(tzNow.toString("AP")); + if(dateType == "en") + { + this->dateOfDay->setText(tzNow.toString("ddd MM-dd")); + } else { + this->dateOfDay->setText(tzNow.toString("ddd MM/dd")); + } + } else { + time = tzNow.toString("hh:mm"); + this->dateOfWeek->setText(tzNow.toString("ddd")); + if(dateType == "en") + { + this->dateOfDay->setText(tzNow.toString("MM-dd")); + } else { + this->dateOfDay->setText(tzNow.toString("MM/dd")); + } + } + + this->dateOfLocaltimeHour->setText(time.split(":").at(0)); + this->dateOfLocaltimeMinute->setText(time.split(":").at(1)); + + m_currentTime = QTime::currentTime(); +#else + if(timeType == 12) + this->dateOfLocaltime->setText(QDateTime::currentDateTime().toString("A hh:mm")); + else + this->dateOfLocaltime->setText(QDateTime::currentDateTime().toString("hh:mm")); + + if(dateType == "cn") + this->dateOfDay->setText(QDate::currentDate().toString("yyyy/MM/dd ddd")); + else + this->dateOfDay->setText(QDate::currentDate().toString("yyyy-MM-dd ddd")); + + if(sleepTime){ + if(!sleepTime->setTime(QDateTime::currentDateTime())){ + sleepTime->hide(); + delete sleepTime; + sleepTime=NULL; + } + } +#endif +} + +void Screensaver::setUpdateBackground() +{ + +} + +void Screensaver::updateBackground() +{ + QString path = m_background->getRand(); + if(!path.isEmpty() && ispicture(path)){ + background = QPixmap(path); + hasChanged=true; + isMovie(); + } + // updateCenterWidget(-1); +} + +void Screensaver::setRandomText() +{ + if(!myTextWidget){ + myTextWidget = new QWidget(this); + QHBoxLayout *layout = new QHBoxLayout(myTextWidget); + cycleLabel = new QLabel(this); + cycleLabel->setFixedSize(16,16); + layout->addWidget(cycleLabel); + layout->setSpacing(16); + myTextLabel = new QLabel(myTextWidget); + myTextLabel->setObjectName("myText"); +// myTextLabel->setBackgroundRole(QPalette::Base); +// myTextLabel->setAutoFillBackground(true); +// myTextLabel->setMaximumWidth(800); +// QColor highLightColor = palette().color(QPalette::Base); +// QString stringColor = QString("rgba(%1,%2,%3,82%)") +// .arg(highLightColor.red()) +// .arg(highLightColor.green()) +// .arg(highLightColor.blue()); +// QColor textColor = palette().color(QPalette::Text); +// QString textString = QString("rgb(%1,%2,%3)") +// .arg(textColor.red()) +// .arg(textColor.green()) +// .arg(textColor.blue()); +// QColor borderColor = palette().color(QPalette::BrightText); +// QString borderString = QString("rgba(%1,%2,%3,85%)") +// .arg(borderColor.red()) +// .arg(borderColor.green()) +// .arg(borderColor.blue()); +// myTextLabel->setStyleSheet(QString("font-size:18px;border-radius: 6px;background: %1;color: %2;padding: 24px 48px 24px 48px;border-width: 1px;border-style: solid;border-color:%3;") \ +// .arg(stringColor).arg(textString).arg(borderString)); + blur_Num = configuration->getBlurNumber(); + curStyle = configuration->getcurStyle(); + qDebug()<<"cu人Style= "<setStyleSheet(QString("QLabel{background: rgba(0, 0, 0, %1); color:#FFFFFF; border-radius:16px}").arg(blur_Num * 0.01)); + cycleLabel->setStyleSheet(QString("QLabel{background: rgba(0, 0, 0, %1); color:#FFFFFF; border-radius:8px}").arg(blur_Num * 0.01)); + } else{ + myTextLabel->setStyleSheet(QString("QLabel{background: rgba(255, 255, 255, %1); border-radius:16px}").arg(blur_Num * 0.01)); + cycleLabel->setStyleSheet(QString("QLabel{background: rgba(255, 255, 255, %1); border-radius:8px}").arg(blur_Num * 0.01)); + } + layout->addWidget(myTextLabel); + } + + myTextLabel->setText(myText); + myTextWidget->adjustSize(); + if(myText != "") + myTextWidget->setVisible(true); + else + myTextWidget->setVisible(false); +} + +void Screensaver::setPreviewText(bool bVisible) +{ + if(!myPreviewLabel){ + myPreviewLabel = new QLabel(this); + myPreviewLabel->setFixedSize(58,30); + + //设置样式 + myPreviewLabel->setStyleSheet("background-color: rgb(38,38,38); border-radius: 0px; color:white;"); + + //先采取固定大小方式 + myPreviewLabel->move(120,142); + myPreviewLabel->setAlignment(Qt::AlignCenter); + } + + myPreviewLabel->setText(tr("View")); + myPreviewLabel->adjustSize(); + + myPreviewLabel->setVisible(bVisible); + +} + +void Screensaver::setCenterWidget() +{ + QStringList qlist = qsettings->childGroups(); + if(qlist.count()<1) + return; + + QDate date = QDate::currentDate(); + int days = date.daysTo(QDate(2100,1,1)); + int index = days%qlist.count()+1; + + QString configPath = QDir::homePath() + "/.ukui-screensaver-default.conf"; + QSettings settings1(configPath, QSettings::IniFormat); + if(settings1.value("FIRST").toString().isEmpty()){ + settings1.setValue("FIRST",QDate::currentDate().toString("yy/MM/dd")); + index = 1; + } + if(settings1.value("FIRST").toString() == QDate::currentDate().toString("yy/MM/dd")) + index = 1; + + qsettings->beginGroup(QString::number(index)); + if(isCustom){ + centerlabel1 = new QLabel(myText); + centerlabel2 = new QLabel(""); + centerlabel2->hide(); + authorlabel = new QLabel(""); + } + else if(qsettings->contains("OL")){ + centerlabel1 = new QLabel(qsettings->value("OL").toString()); + centerlabel2 = new QLabel(""); + centerlabel2->hide(); +#ifndef USE_INTEL + authorlabel = new QLabel(qsettings->value("author").toString()); +#endif + } + else if(qsettings->contains("FL")) + { + centerlabel1 = new QLabel(qsettings->value("FL").toString()); + centerlabel2 = new QLabel(qsettings->value("SL").toString()); + centerlabel2->show(); +#ifndef USE_INTEL + authorlabel = new QLabel(qsettings->value("author").toString()); +#endif + } + +#ifdef USE_INTEL + if(qsettings->contains("author") && !qsettings->value("author").toString().isEmpty()) + { + authorlabel = new QLabel(qsettings->value("author").toString()); + authorlabel->show(); + } + else + { + authorlabel = new QLabel(""); + authorlabel->hide(); + } +#endif + + centerlabel1->setObjectName("centerLabel"); + centerlabel2->setObjectName("centerLabel"); + authorlabel->setObjectName("authorLabel"); + + qsettings->endGroup(); + +#ifdef USE_INTEL + //设置背景透明,qss中更改为透明不生效 + centerlabel1->setStyleSheet("QLabel{background-color: transparent;}"); + centerlabel2->setStyleSheet("QLabel{background-color: transparent;}"); + authorlabel->setStyleSheet("QLabel{background-color: transparent;}"); + + centerWidget = new QWidget(this); + centerWidget->setStyleSheet("QWidget{background:rgb(0,0,0,64);border-radius:16px}"); + QVBoxLayout *layout = new QVBoxLayout(centerWidget); + +// QPushButton *line =new QPushButton(this); +// line->setWindowOpacity(0.08); +// line->setFocusPolicy(Qt::NoFocus); +// line->setMaximumHeight(1); + + layout->addWidget(centerlabel1); + layout->addWidget(centerlabel2); +// layout->addWidget(line); + layout->addWidget(authorlabel); + + + adjustSize(); + centerWidget->setVisible(true); +#else + centerWidget = new QWidget(this); + QVBoxLayout *layout = new QVBoxLayout(centerWidget); + layout->addWidget(centerlabel1); + layout->addWidget(centerlabel2); + + if(!isCustom){ + QPushButton *line =new QPushButton(this); + line->setWindowOpacity(0.08); + line->setFocusPolicy(Qt::NoFocus); + line->setMaximumHeight(1); + layout->addWidget(line); + } + + layout->addWidget(authorlabel); + + centerWidget->adjustSize(); + centerWidget->setGeometry((width()-centerWidget->width())/2,(height()-centerWidget->height())/2, + centerWidget->width(),centerWidget->height()); + centerWidget->setVisible(true); +#endif +} + +void Screensaver::getWeatherFinish(QString city, QString cond, QString tmp) +{ + qDebug() << "getWeatherFinish"; + qDebug() << city << "," << cond << "," << tmp; + + this->m_weatherIcon->setPixmap(m_weatherManager->getWeatherIcon(cond)); + this->m_weatherArea->setText(city); + + if(!cond.isEmpty()) + { + this->m_weatherCond->show(); + this->m_weatherCond->setText("·" + cond); + } + else + this->m_weatherCond->hide(); + + if(!tmp.isEmpty()) + { + this->m_weatherTemperature->show(); + this->m_weatherTemperature->setText(tmp); + } + else + this->m_weatherTemperature->hide(); + + m_weatherLaout->adjustSize(); + m_weatherLaout->setGeometry((this->width()-m_weatherLaout->width())/2,96 * (float)width()/1920, + m_weatherLaout->geometry().width(), m_weatherLaout->geometry().height()); +} + +void Screensaver::setNoticeLaout() +{ + m_widgetNotice = new QWidget(this); + QHBoxLayout *hNoticeLayout = new QHBoxLayout(m_widgetNotice); + hNoticeLayout->setContentsMargins(0, 0, 0, 0); + m_labelNoticeIcon = new QLabel(this); + m_labelNoticeMessage = new QLabel(this); + m_labelNoticeMessage->setStyleSheet("font-size:16px;color:#ffffff"); + + m_labelNoticeIcon->setPixmap(QPixmap(":/assets/message.png")); + + m_labelNoticeMessage->setText(tr("You have new notification")); + + int num = configuration->getMessageNumber(); + (configuration->getMessageShowEnable() && num > 0)? showNotice() : hideNotice(); + + hNoticeLayout->addWidget(m_labelNoticeIcon); + hNoticeLayout->addWidget(m_labelNoticeMessage); + m_widgetNotice->adjustSize(); +} + +//显示新消息通知 +void Screensaver::showNotice() +{ + m_widgetNotice->show(); +} + +void Screensaver::hideNotice() +{ + m_widgetNotice->hide(); +} +/* +void Screensaver::setDesktopBackground() +{ + vboxFrame->hide(); + QString mBackground; + + if(!hasChanged){ + mBackground=defaultBackground; + }else{ + if(m_background->getCurrent().isEmpty()) + return; + mBackground=m_background->getCurrent(); + } + + settings->set("picture-filename",QVariant(mBackground)); + + QDBusInterface * interface = new QDBusInterface("org.freedesktop.Accounts", + "/org/freedesktop/Accounts", + "org.freedesktop.Accounts", + QDBusConnection::systemBus()); + + if (!interface->isValid()){ + return; + } + + QDBusReply reply = interface->call("FindUserByName", getenv("USER")); + QString userPath; + if (reply.isValid()){ + userPath = reply.value().path(); + } + else { + return; + } + + QDBusInterface * useriFace = new QDBusInterface("org.freedesktop.Accounts", + userPath, + "org.freedesktop.Accounts.User", + QDBusConnection::systemBus()); + + if (!useriFace->isValid()){ + return; + } + + QDBusMessage msg = useriFace->call("SetBackgroundFile", mBackground); + if (!msg.errorMessage().isEmpty()) + qDebug() << "update user background file error: " << msg.errorMessage(); + +}*/ diff --git a/screensaver/screensaver.h b/screensaver/screensaver.h new file mode 100644 index 0000000..cca782b --- /dev/null +++ b/screensaver/screensaver.h @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ + +#ifndef SCREENSAVER_H +#define SCREENSAVER_H +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sleeptime.h" +#include "chinesedate.h" +#include "mbackground.h" +#include "checkbutton.h" +#include "scconfiguration.h" +#include "cyclelabel.h" +#include "weathermanager.h" + +class Screensaver : public QWidget +{ + Q_OBJECT + +public: + explicit Screensaver(QWidget *parent = 0); + ~Screensaver(); + void addClickedEvent(); + +private: + void initUI(); + void setDatelayout(); + void setWeatherLayout(); + void setNoticeLaout(); + void setSleeptime(bool Isshow); + void setCenterWidget(); + void updateDate(); + void setUpdateBackground(); + void setUpdateCenterWidget(); + void startSwitchImages(); + void stopSwitchImages(); + void onBackgroundChanged(); + void setRandomText(); + void setPreviewText(bool bVisible);//设置预览样式 + void setRandomPos(); + void connectSingles(); + void updateBackgroundPath(); + void enterEvent(QEvent*); + void leaveEvent(QEvent*); + void isMovie(); + + QTimer *switchTimer; + QTimer *fadeTimer; + QStringList imagePaths; + QString backgroundPath; + int cycleTime; + float opacity; + bool isCustom; + bool isCShowRestTime; + bool isUShowRestTime; + bool textIsCenter; + QString myText; + QLabel *myTextLabel; + QLabel *myPreviewLabel; + QLabel *cycleLabel; + + SCConfiguration *configuration; + void showNotice(); + void hideNotice(); + + QWidget *myTextWidget; + QLabel *dateOfLocaltime; + + QLabel *dateOfWeek; + QLabel *dateOfLocaltimeHour; + QLabel *dateofLocaltimeColon; + QLabel *dateOfLocaltimeMinute; + QLabel *dateOfDay; + QLabel *dateOfLunar; + QWidget *centerWidget; + + QWidget *timeLayout; + SleepTime *sleepTime; + QTimer *timer; + + QPixmap background; + + QPushButton *settingsButton; + QPushButton *WallpaperButton; + QWidget *buttonWidget; + QSettings *qsettings; + + MBackground *m_background; + + QLabel *centerlabel1; + QLabel *centerlabel2; + QLabel *authorlabel; + + checkButton *checkSwitch; + QLabel *autoSwitchLabel; + QFrame *autoSwitch; + + QFrame *vboxFrame; + bool isAutoSwitch; + + int flag; + bool hasChanged; + int timeType; + QString dateType; + QProcess *process; + QLabel *screenLabel; + bool respondClick; + static QTime m_currentTime; + int blur_Num; + QString curStyle; + + WeatherManager *m_weatherManager; + QWidget *m_weatherLaout; + QLabel *m_weatherIcon; + QLabel *m_weatherArea; + QLabel *m_weatherCond; + QLabel *m_weatherTemperature; + + QWidget *m_widgetNotice; + QLabel *m_labelNoticeIcon; + QLabel *m_labelNoticeMessage; + int currentIndex = 0; + + QString currentPath; + bool is_gif = false; + QList currentPixmap; + int delayTime; + QTimer *movieTimer = nullptr; + int currentCount = 0; + +protected: + void paintEvent(QPaintEvent *event); + void resizeEvent(QResizeEvent *event); + bool eventFilter(QObject *obj, QEvent *event); + +private Q_SLOTS: + void updateTime(); + void updateBackground(); + void updateCenterWidget(int index); + void autoSwitchChanged(bool iswitch); + void backgroundPathChanged(QString path); + void cycleTimeChanged(int cTime); + void myTextChanged(QString text); + void showCRestTimeChanged(bool isShow); + void showURestTimeChanged(bool isShow); + void textIsCenterChanged(bool isCenter); + void themeChanged(); + void getWeatherFinish(QString city, QString cond, QString tmp); + void onMessageNumberChanged(int num); + void onMessageShowEnabledChanged(bool enabled); + void onTimeFormatChanged(int type); + void onDateFormatChanged(QString type); + void onBlurNumChanged(int num); + void onStyleChanged(QString style); + QPixmap getPaddingPixmap(); +}; + +#endif // MAINWINDOW_H diff --git a/screensaver/sleeptime.cpp b/screensaver/sleeptime.cpp new file mode 100644 index 0000000..9fd6650 --- /dev/null +++ b/screensaver/sleeptime.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ + +#include "sleeptime.h" +#include +#include +#include + +SleepTime::SleepTime(QWidget *parent) : QWidget(parent), + sleepTime(0) +{ + init(); +} + +SleepTime::~SleepTime() +{ + +} + +void SleepTime::init() +{ + layout = new QHBoxLayout(this); + layout->setDirection(QBoxLayout::RightToLeft); + layout->setSpacing(4); + + for(int i=0;i<2;i++) + { + QLabel *label = new QLabel(this); + label->setText("0"); + label->setFixedSize(40,40); + label->setObjectName("clockTime"); + list.append(label); + } + + QLabel *colon = new QLabel(this); + colon->setText(":"); + colon->setObjectName("colon"); + list.append(colon); + + for(int i=0;i<2;i++) + { + QLabel *label = new QLabel(this); + label->setText("0"); + label->setFixedSize(40,40); + label->setObjectName("clockTime"); + list.append(label); + } + + for(int i=0;iaddWidget(list.at(i)); + } + + restTime = new QLabel(this); + restTime->setText(tr("You have rested:")); + restTime->setObjectName("restTime"); + restTime->setAlignment(Qt::AlignBottom); + restTime->adjustSize(); + + layout->addWidget(restTime); + + initTime = QDateTime::currentDateTime(); +} + +int SleepTime::setTime(QDateTime time) +{ + sleepTime = initTime.secsTo(time); + if(sleepTime>5999 || sleepTime<0){ + hide(); + return false; + } + + int sec = sleepTime % 60; + int min = sleepTime/60; + setSeconds(sec); + setMinute(min); + + return true; +} + +void SleepTime::setSeconds(int seconds) +{ + int sec1 = seconds%10; + int sec2 = seconds/10; + + list.at(0)->setText(QString::number(sec1)); + list.at(1)->setText(QString::number(sec2)); +} + +void SleepTime::setMinute(int minutes) +{ + + int min1 = minutes%10; + int min2 = minutes/10; + list.at(3)->setText(QString::number(min1)); + list.at(4)->setText(QString::number(min2)); +} + +void SleepTime::setSmallMode() +{ + for(int i = 0;i<5;i++) + list.at(i)->setFixedSize(8,8); + adjustSize(); +} diff --git a/screensaver/sleeptime.h b/screensaver/sleeptime.h new file mode 100644 index 0000000..bef0e32 --- /dev/null +++ b/screensaver/sleeptime.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ + +#ifndef SLEEPTIME_H +#define SLEEPTIME_H + +#include +#include +#include +#include +#include +#include + +class SleepTime : public QWidget +{ + Q_OBJECT +public: + explicit SleepTime(QWidget *parent = nullptr); + ~SleepTime(); + int setTime(QDateTime time); + void setSmallMode(); + +private: + QLabel *restTime; + QList list; + QHBoxLayout *layout; + int sleepTime; + QDateTime initTime; + + void init(); + void setSeconds(int seconds); + void setMinute(int minutes); +}; + +#endif // SLEEPTIME_H diff --git a/screensaver/ukui-screensaver-default.pro b/screensaver/ukui-screensaver-default.pro new file mode 100644 index 0000000..5dd421d --- /dev/null +++ b/screensaver/ukui-screensaver-default.pro @@ -0,0 +1,49 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2020-01-10T17:10:24 +# +#------------------------------------------------- + +QT += core gui x11extras + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = ukui-screensaver-default +TEMPLATE = app + +# The following define makes your compiler emit warnings if you use +# any feature of Qt which as been marked as deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if you use deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +LIBS += -lX11 + +SOURCES += \ + main.cpp \ + screensaver.cpp \ + sleeptime.cpp \ + chinesedate.cpp \ + mbackground.cpp \ + scconfiguration.cpp + +HEADERS += \ + screensaver.h \ + sleeptime.h \ + chinesedate.h \ + mbackground.h \ + scconfiguration.h + +TRANSLATIONS = ../i18n_ts/zh_CN.ts \ + ../i18n_ts/ru.ts \ + ../i18n_ts/fr.ts \ + ../i18n_ts/pt.ts \ + ../i18n_ts/es.ts + +RESOURCES = default.qrc + diff --git a/screensaver/weathermanager.cpp b/screensaver/weathermanager.cpp new file mode 100644 index 0000000..506b34c --- /dev/null +++ b/screensaver/weathermanager.cpp @@ -0,0 +1,375 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Authors: ZHAI Kang-ning +**/ +#include "weathermanager.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const QString weatherServerAddr = "http://service.ubuntukylin.com:8001/weather/api/3.0/heweather_data_s6/"; +const QString defaultCityId = "101030100"; //"101030100" 默认天津 +const int weatherReqInterval = 1000 * 60 * 20; //定时更新天气,和麒麟天气保持一致 + +const QByteArray schemaWeather = "org.china-weather-data.settings"; + +static const QMap weatherMap { + {"晴", "100"},{"多云", "101"},{"少云", "102"},{"晴间多云", "103"},{"阴", "104"}, + {"有风", "200"},{"平静", "201"},{"微风", "202"},{"和风", "203"},{"清风", "204"},{"强风劲风", "205"},{"疾风", "206"},{"大风", "207"},{"烈风", "208"},{"风暴", "209"}, + {"狂暴风", "210"},{"飓风", "211"},{"龙卷风", "212"},{"热带风暴", "213"}, + {"阵雨", "300"},{"强阵雨", "301"},{"雷阵雨", "302"},{"强雷阵雨", "303"},{"雷阵雨伴有冰雹", "304"},{"小雨", "305"},{"中雨", "306"},{"大雨", "307"},{"极端降雨", "308"},{"毛毛雨细雨", "309"}, + {"暴雨", "310"},{"大暴雨", "311"},{"特大暴雨", "312"},{"冻雨", "313"},{"小到中雨", "314"},{"中到大雨", "315"},{"大到暴雨", "316"},{"暴雨到大暴雨", "317"},{"大暴雨到特大暴雨", "318"}, + {"雨", "399"}, + {"小雪", "400"},{"中雪", "401"},{"大雪", "402"},{"暴雪", "403"},{"雨夹雪", "404"},{"雨雪天气", "405"},{"阵雨夹雪", "406"},{"阵雪", "407"},{"小到中雪", "408"},{"中到大雪", "409"}, + {"大到暴雪", "410"},{"雪", "499"}, + {"薄雾", "500"},{"雾", "501"},{"霾", "502"},{"扬沙", "503"},{"浮尘", "504"},{"沙尘暴", "507"},{"强沙尘暴", "508"},{"大雾", "509"}, + {"强浓雾", "510"},{"中度霾", "511"},{"重度霾", "512"},{"严重霾", "513"},{"大雾", "514"},{"特强浓雾", "515"}, + {"热", "900"},{"冷", "901"}, + {"未知", "999"} +}; + +WeatherManager::WeatherManager(QObject *parent) : QObject(parent) +{ + //初始化m_net_manager请求天气 + m_net_manager = new QNetworkAccessManager(this); + QObject::connect(m_net_manager, SIGNAL(finished(QNetworkReply*)), + this, SLOT(replyFinished(QNetworkReply*))); + + m_timer = new QTimer(this); + m_timer->setInterval(weatherReqInterval); + + m_local_weather_info = new LocalWeatherInfo(this); + + connect(m_timer, &QTimer::timeout, this, &WeatherManager::weatherRequest); + + m_networkWatcher = new NetWorkWatcher(this); + connect(m_networkWatcher, &NetWorkWatcher::NetworkStateChanged, this, &WeatherManager::onNetworkStateChanged); + + m_networkWatcher->checkOnline(); +} + +void WeatherManager::onNetworkStateChanged(uint state) +{ + qDebug() << state; + if (NM_STATE_CONNECTED_GLOBAL != state) + emit onWeatherUpdate("天气不可用", NULL, NULL); + else + getWeather(); +} + +void WeatherManager::getWeather() +{ + weatherRequest(); + if (m_timer != NULL) + { + m_timer->start(); + } + return; +} + +void WeatherManager::weatherRequest() +{ + if (updateLocation()) + return; + QNetworkRequest request(QUrl(weatherServerAddr + m_city_id + "/")); + m_net_manager->get(request); +} + +bool WeatherManager::updateLocation() +{ + //更新城市信息 未实现 麒麟天气提供gsettings,完成后对接 + if (QGSettings::isSchemaInstalled(schemaWeather)) { + m_settings = new QGSettings(schemaWeather,"",this); //org/ukui/indicator-china-weather path + if(getLogcalWeather()) + { + emit onWeatherUpdate(m_local_weather_info->getCityName(), + m_local_weather_info->getCondText(), + m_local_weather_info->getTemperature()); + + + m_networkWatcher->checkOnline(); + return true; + } + m_city_id = getLogcalCityId(); + } + + if (m_city_id.isEmpty()) + m_city_id = defaultCityId; + + return false; +} + +bool WeatherManager::getLogcalWeather() +{ + if (m_settings != nullptr) + { + //"1920-08-27 10:17:42,101310204,澄迈,小雨,95%,25℃,北风,1级," 时间,城市编码,城市名称,天气,湿度,温度,风向,风力 + QString weather = m_settings->get("weather").toString(); + QStringList weatherInfoList = weather.split(","); + if (weatherInfoList.size() < 9) + return false; + m_local_weather_info->setTime(weatherInfoList.at(0)); + if(!m_local_weather_info->isTimeValid()) + return false; + + m_local_weather_info->setCityId(weatherInfoList.at(1)); + m_local_weather_info->setCityName(weatherInfoList.at(2)); + m_local_weather_info->setCondText(weatherInfoList.at(3)); + m_local_weather_info->setAirHumidity(weatherInfoList.at(4)); + m_local_weather_info->setTemperature(weatherInfoList.at(5)); + m_local_weather_info->setWindDirection(weatherInfoList.at(6)); + m_local_weather_info->setWindForce(weatherInfoList.at(7)); + return true; + } + + return false; +} + +QString WeatherManager::getLogcalCityId() +{ + if (m_settings != nullptr) { + QString citys = m_settings->get("citylist").toString();//"101010100," + if (citys.isEmpty()) + return ""; + + QStringList cityList = citys.split(","); + if (cityList.size() >= 1) + { + QString s = cityList.at(0); + qDebug() << "local city id = " << s; + return s; + } + } + return ""; +} + +void WeatherManager::replyFinished(QNetworkReply *reply) +{ + if(reply != nullptr && reply->error() != QNetworkReply::NoError) + { + qWarning() << "[WeatherManager][replyFinished] get weather error:(" + << reply->error() << ")" << reply->errorString(); + if (m_networkTryNum < 15) + { + m_networkTryNum++; + QTimer::singleShot(1000, this, [=]{ + weatherRequest(); + }); + } else { + m_networkTryNum = 0; + } + emit onWeatherUpdate("天气不可用", "", ""); + return; + } + + //注:天气信息只解析了锁屏需要展示的部分 + QByteArray BA; + QJsonDocument JD; + QJsonParseError JPE; + + BA = reply->readAll(); + // QTextCodec *codec = QTextCodec::codecForName("UTF-8"); + // QString all = codec->toUnicode(BA); + // qDebug() << "reply is:" << all; + + JD = QJsonDocument::fromJson(BA, &JPE); + if (JPE.error == QJsonParseError::NoError) + { + if (JD.isObject()) + { + QJsonObject kylinWeatherObj = JD.object().value("KylinWeather").toObject(); + + QString nowWeather = kylinWeatherObj.value("weather").toObject().value("now").toString(); + m_city_name = kylinWeatherObj.value("weather").toObject().value("location").toString(); + QStringList nowList = nowWeather.split(","); + for(QString now : nowList) + { + if(now.contains("cond_txt")) { + m_cond_txt = now.mid(9); + } + + if (now.contains("tmp")){ + m_temperature = now.mid(4) + "°C"; + } + } + emit onWeatherUpdate(m_city_name, m_cond_txt, m_temperature); + } + } else { + qWarning() << "get weather info error : " << JPE.errorString(); + emit onWeatherUpdate("天气不可用", "", ""); + } + + reply->deleteLater(); +} + +QPixmap WeatherManager::getWeatherIcon() +{ + return getWeatherIcon(m_cond_txt); +} + +QPixmap WeatherManager::getWeatherIcon(QString cond) +{ + if (cond.isEmpty()) + { + qWarning() << "cond info is unknown"; + return QPixmap(":/weather/assets/weather-icon/999.svg").scaled(32,32); + } + + //根据m_cond_txt + QString numStr = weatherMap.value(cond); + if (!numStr.isEmpty()) { + qDebug() << "----------------numStr=" + numStr; + return QPixmap(":/weather/assets/weather-icon/" + numStr +".svg").scaled(32,32); + } + + qWarning() << "天气为|" << cond << "|"; + return QPixmap(":/weather/assets/weather-icon/999.svg").scaled(32,32); +} + +QString WeatherManager::getCityName() +{ + return ""; +} + +QString WeatherManager::getCond() +{ + return ""; +} + +QString WeatherManager::getTemperature() +{ + return ""; +} + +LocalWeatherInfo::LocalWeatherInfo(QObject *parent) +{ +} + +bool LocalWeatherInfo::isTimeValid() +{ + if (m_update_time != nullptr && !m_update_time.isEmpty()) + { + QString strBuffer; + QDateTime time = QDateTime::fromString(m_update_time, "yyyy-MM-dd hh:mm:ss"); + QDateTime currentTime = QDateTime::currentDateTime(); + + if (!time.isValid()) + return false; + + uint timeInterval = currentTime.toTime_t() - time.toTime_t(); + + if((timeInterval <= 21 * 60) + && (timeInterval > 0)) + { + //麒麟天气更新时间为20分钟,加入1分钟容错机制 + return true; + } + } + return false; +} + +void LocalWeatherInfo::setTime(QString time) +{ + m_update_time = time; +} + +QString LocalWeatherInfo::getTime() +{ + return m_update_time; +} + +void LocalWeatherInfo::setCityId(QString cityId) +{ + m_city_id = cityId; +} + +QString LocalWeatherInfo::getCityId() +{ + return m_city_id; +} + +void LocalWeatherInfo::setCityName(QString cityName) +{ + m_city_name = cityName; +} + +QString LocalWeatherInfo::getCityName() +{ + return m_city_name; +} + +void LocalWeatherInfo::setCondText(QString condText) +{ + m_cond_text = condText; +} + +QString LocalWeatherInfo::getCondText() +{ + return m_cond_text; +} + +void LocalWeatherInfo::setAirHumidity(QString airHumidity) +{ + m_air_humidity = airHumidity; +} + +QString LocalWeatherInfo::getAirHumidity() +{ + return m_air_humidity; +} + +void LocalWeatherInfo::setTemperature(QString temperature) +{ + m_temperature = temperature; +} + +QString LocalWeatherInfo::getTemperature() +{ + return m_temperature; +} + +void LocalWeatherInfo::setWindDirection(QString windDirection) +{ + m_wind_direction = windDirection; +} + +QString LocalWeatherInfo::getWindDirection() +{ + return m_wind_direction; +} + +void LocalWeatherInfo::setWindForce(QString windForce) +{ + m_wind_force = windForce; +} + +QString LocalWeatherInfo::getWindForce() +{ + return m_wind_force; +} diff --git a/screensaver/weathermanager.h b/screensaver/weathermanager.h new file mode 100644 index 0000000..3f29c40 --- /dev/null +++ b/screensaver/weathermanager.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Authors: ZHAI Kang-ning +**/ +#ifndef WEATHERMANAGER_H +#define WEATHERMANAGER_H + +#include +#include +#include +#include + +#include "../src/networkwatcher.h" + +class QNetworkAccessManager; +class QNetworkReply; +class LocalWeatherInfo; + +class WeatherManager : public QObject +{ + Q_OBJECT +public: + explicit WeatherManager(QObject *parent = nullptr); + +Q_SIGNALS: + void onWeatherUpdate(QString city, QString cond, QString temperature); + +private Q_SLOTS: + void replyFinished(QNetworkReply *); + void onNetworkStateChanged(uint state); + +public: + void getWeather(); + QPixmap getWeatherIcon(); + QPixmap getWeatherIcon(QString cond); + + QString getCityName(); + QString getCond(); + QString getTemperature(); + +private: + bool updateLocation();//更新位置,从用户设置获取城市信息,如有多个,只取第一个,未对接 + void weatherRequest(); + + bool getLogcalWeather(); + QString getLogcalCityId(); + +private: + QString m_city_id; // "101030100" 默认天津 + QString m_city_name; + QString m_cond_txt; //天气条件 晴、阴等 + QString m_temperature;//温度 10、20等 + + QNetworkAccessManager *m_net_manager; + QTimer *m_timer; + QGSettings *m_settings; + + LocalWeatherInfo *m_local_weather_info; + NetWorkWatcher *m_networkWatcher; + + int m_networkTryNum = 0; +}; + +class LocalWeatherInfo : QObject +{ + //"1920-08-27 10:17:42,101310204,澄迈,小雨,95%,25℃,北风,1级," 时间,城市编码,城市名称,天气,湿度,温度,风向,风力 + Q_OBJECT +public: + explicit LocalWeatherInfo(QObject *parent = nullptr); + +private: + QString m_update_time; + QString m_city_id; + QString m_city_name; + QString m_cond_text; + QString m_air_humidity; + QString m_temperature; + QString m_wind_direction; + QString m_wind_force; +public: + bool isTimeValid(); + + void setTime(QString time); + QString getTime(); + + void setCityId(QString cityId); + QString getCityId(); + + void setCityName(QString cityName); + QString getCityName(); + + void setCondText(QString condText); + QString getCondText(); + + void setAirHumidity(QString airHumidity); + QString getAirHumidity(); + + void setTemperature(QString temperature); + QString getTemperature(); + + void setWindDirection(QString windDirection); + QString getWindDirection(); + + void setWindForce(QString windForce); + QString getWindForce(); +}; + +#endif // WEATHERMANAGER_H diff --git a/set4kScale/CMakeLists.txt b/set4kScale/CMakeLists.txt new file mode 100644 index 0000000..09b8eef --- /dev/null +++ b/set4kScale/CMakeLists.txt @@ -0,0 +1,25 @@ +project(set4kScale) + +pkg_check_modules(X11 REQUIRED x11) +pkg_check_modules(XCB REQUIRED xcb) +pkg_check_modules(QGS REQUIRED gsettings-qt) + +include_directories( + ${X11_INCLUDE_DIRS} + ${XCB_INCLUDE_DIRS} + ${QGS_INCLUDE_DIRS} + ) + +set(CMAKE_AUTOMOC ON) + +set(bin_SRCS + ${bin_SRCS} + main.cpp + ) + +add_executable(set4kScale ${bin_SRCS}) +target_link_libraries(set4kScale Qt5::Core Qt5::Widgets Qt5::X11Extras ${X11_LIBRARIES} ${XCB_LIBRARIES} ${QGS_LIBRARIES}) + +install(TARGETS + set4kScale + DESTINATION lib/ukui-screensaver) diff --git a/set4kScale/main.cpp b/set4kScale/main.cpp new file mode 100644 index 0000000..5ec6932 --- /dev/null +++ b/set4kScale/main.cpp @@ -0,0 +1,208 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +extern "C" { +#include +#include +} + + +#define XSETTINGS_SCHEMA "org.ukui.SettingsDaemon.plugins.xsettings" +#define MOUSE_SCHEMA "org.ukui.peripherals-mouse" +#define SCALING_KEY "scaling-factor" +#define CURSOR_SIZE "cursor-size" +#define CURSOR_THEME "cursor-theme" + + +/* 过滤低分辨率高缩放比情况 */ +void screenScaleJudgement(QGSettings *settings) +{ + qreal scaling = qApp->devicePixelRatio(); + double scale; + scale = settings->get(SCALING_KEY).toDouble(); + if (scale > 1.25) { + bool state = false; + bool mScale = false; + + for (QScreen *screen : QGuiApplication::screens()) { + int width = screen->geometry().width() * scaling; + int height = screen->geometry().height() * scaling; + + if (width < 1920 && height < 1080) { + state = true; + } else if (width == 1920 && height == 1080 && scale > 1.5) { + state = true; + } else if (width > 2560 && height > 1440) { + mScale = true; + } + } + + if (state && !mScale) { + QGSettings *mGsettings = new QGSettings(MOUSE_SCHEMA); + mGsettings->set(CURSOR_SIZE, 24); + settings->set(SCALING_KEY, 1.0); + delete mGsettings; + } + } +} + +/* 设置DPI环境变量 */ +void setXresources(int dpi) +{ + Display *dpy; + QGSettings *mouse_settings = new QGSettings(MOUSE_SCHEMA); + QString str = QString("Xft.dpi:\t%1\nXcursor.size:\t%2\nXcursor.theme:\t%3\n") + .arg(dpi) + .arg(mouse_settings->get(CURSOR_SIZE).toInt()) + .arg(mouse_settings->get(CURSOR_THEME).toString()); + + dpy = XOpenDisplay(NULL); + XChangeProperty(dpy, RootWindow(dpy, 0), XA_RESOURCE_MANAGER, XA_STRING, 8, + PropModeReplace, (unsigned char *) str.toLatin1().data(), str.length()); + XCloseDisplay(dpy); + + qDebug() << "setXresources:" << str; + + delete mouse_settings; +} + +/* 判断文件是否存在 */ +bool isFileExist(QString XresourcesFile) +{ + QFileInfo fileInfo(XresourcesFile); + if (fileInfo.isFile()) { + qDebug() << "File exists"; + return true; + } + + qDebug() << "File does not exis"; + + return false; +} + +/* 编写判断标志文件,更改 鼠标/DPI 配置大小*/ +void writeXresourcesFile(QString XresourcesFile, QGSettings *settings, double scaling) +{ + QFile file(XresourcesFile); + QString content = QString("Xft.dpi:%1\nXcursor.size:%2").arg(96.0 * scaling).arg(24.0 * scaling); + QByteArray str = content.toLatin1().data(); + + file.open(QIODevice::ReadWrite | QIODevice::Text); + file.write(str); + file.close(); + + QGSettings *Font = new QGSettings("org.ukui.font-rendering"); + QGSettings *mouse_settings = new QGSettings(MOUSE_SCHEMA); + + Font->set("dpi", 96.0); + settings->set(SCALING_KEY, scaling); + mouse_settings->set(CURSOR_SIZE, scaling * 24.0); + + qDebug() << " writeXresourcesFile: content = " << content + << " scalings = " << settings->get(SCALING_KEY).toDouble() + << "cursor size = " << mouse_settings->get(CURSOR_SIZE).toInt(); + delete Font; + delete mouse_settings; +} + +/* 判断是否为首次登陆 */ + +bool isTheFirstLogin(QGSettings *settings) +{ + QString homePath = getenv("HOME"); + QString XresourcesFile = homePath+"/.config/xresources"; + QString Xresources = homePath+"/.Xresources"; + qreal scaling = qApp->devicePixelRatio(); + bool zoom1 = false, zoom2 = false, zoom3 = false; + double mScaling; + bool xres, Xres; + + Xres = isFileExist(Xresources); + xres = isFileExist(XresourcesFile); //判断标志文件是否存在 + + if (xres && !Xres) { + return false; + } else if (xres && Xres) { + QFile::remove(Xresources); + return false; + } else if (Xres && !xres) { + QFile::rename(Xresources, XresourcesFile); + return false; + } + + for (QScreen *screen : QGuiApplication::screens()) { + int width = screen->geometry().width() * scaling; + int height = screen->geometry().height() * scaling; + + if (width <= 1920 && height <= 1080) { + zoom1 = true; + } else if (width > 1920 && height > 1080 && width <= 2560 && height <=1500) { + zoom2 = true; + } else if (width > 2560 && height > 1440) { + zoom3 = true; + } + } + + if (zoom1) { + mScaling = 1.0; + } else if (!zoom1 && zoom2) { + mScaling = 1.5; + } else if (!zoom1 && !zoom2 && zoom3) { + mScaling = 2.0; + } + + writeXresourcesFile(XresourcesFile, settings, mScaling); + + return true; +} + + +/* 配置新装系统、新建用户第一次登陆时,4K缩放功能*/ +void setHightResolutionScreenZoom() +{ + QGSettings *settings; + double dpi; + int ScreenNum = QApplication::screens().length(); + if (!QGSettings::isSchemaInstalled(XSETTINGS_SCHEMA) || !QGSettings::isSchemaInstalled("org.ukui.font-rendering") || + !QGSettings::isSchemaInstalled(MOUSE_SCHEMA)) { + qDebug() << "Error: ukui-settings-daemon's Schema is not installed, will not setting dpi!"; + delete settings; + return; + } + settings = new QGSettings(XSETTINGS_SCHEMA); + + if (isTheFirstLogin(settings)) { + qDebug() << "Set the default zoom value when logging in for the first time."; + goto end; + } + /* 过滤单双屏下小分辨率大缩放值 */ + + if (ScreenNum > 1) { + goto end; + } + + screenScaleJudgement(settings); + +end: + dpi = 0.0; + dpi = settings->get(SCALING_KEY).toDouble() * 96.0; + setXresources(dpi); + delete settings; +} + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + setHightResolutionScreenZoom(); + return 0; +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..c4b7f93 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,199 @@ +find_package(X11 REQUIRED) + +pkg_check_modules(XTST REQUIRED xtst) +pkg_check_modules(XCB REQUIRED xcb) +pkg_check_modules(QGS REQUIRED gsettings-qt) + +pkg_check_modules(GLIB REQUIRED glib-2.0) +pkg_check_modules(MMIX REQUIRED libmatemixer) + +find_library(PAM_LIBRARIES pam) + +include_directories(${PROJECT_BINARY_DIR}) +include_directories(${PROJECT_SOURCE_DIR}/VirtualKeyboard/src) +include_directories(${PROJECT_SOURCE_DIR}/BiometricAuth) +include_directories(${PROJECT_SOURCE_DIR}/Common) +include_directories(${PROJECT_SOURCE_DIR}/KylinNM) +include_directories(${PROJECT_SOURCE_DIR}/KylinNM/src) +include_directories(${PROJECT_SOURCE_DIR}/KylinNM/hot-spot) +include_directories(${PROJECT_SOURCE_DIR}/KylinNM/wireless-security) + +include_directories( + ${X11_INCLUDE_DIRS} + ${XTST_INCLUDE_DIRS} + ${XCB_INCLUDE_DIRS} + ${QGS_INCLUDE_DIRS} + ${GLIB_INCLUDE_DIRS} + ${MMIX_INCLUDE_DIRS} + ) + +set(EXTRA_LIBS + ${EXTRA_LIBS} + ${PAM_LIBRARIES} + ${X11_LIBRARIES} + ${XTST_LIBRARIES} + ${XCB_LIBRARIES} + ${QGS_LIBRARIES} + ${GLIB_LIBRARIES} + ${MMIX_LIBRARIES} + -lrt + -lpthread + ) + +qt5_wrap_ui(dialog_SRC + lockwidget.ui + surewindow.ui + ) + +qt5_add_resources(dialog_SRC + assets.qrc + ../KylinNM/nmqrc.qrc #暂时将麒麟网络的资源文件放到这里,否则显示不出来,暂时不知道原因 + ) + +# 头文件中包含了Xlib.h,需要单独拿出来处理,不知道原因 +qt5_wrap_cpp(dialog_SRC + pam-tally.h + fullbackgroundwidget.h + lockwidget.h + authdialog.h + loginoptionswidget.h + screensaverwidget.h + auth.h + auth-pam.h + screensaver.h + xeventmonitor.h + monitorwatcher.h + configuration.h + utils.h + users.h + hoverwidget.h + displaymanager.h + iconedit.h + imageutil.h + powermanager.h + logind.h + weathermanager.h + tabletlockwidget.h + gestureauthdialog.h + digitalauthdialog.h + switchbuttongroup.h + wechatauthdialog.h + verificationwidget.h + verticalVerificationwidget.h + common.h + eduplatforminterface.h + accountsinterface.h + networkwatcher.h + digitalkeyboard.h + surewindow.h + PhysicalDeviceSet/brightnessdeviceset.h + PhysicalDeviceSet/flightmodeset.h + PhysicalDeviceSet/sounddeviceset.h + PhysicalDeviceSet/touchscreenset.h + ) + +set(dialog_SRC + ${dialog_SRC} + pam-tally.c + ukui-screensaver-dialog.cpp + fullbackgroundwidget.cpp + lockwidget.cpp + authdialog.cpp + loginoptionswidget.cpp + screensaverwidget.cpp + auth-pam.cpp + xeventmonitor.cpp + monitorwatcher.cpp + grab-x11.cpp + configuration.cpp + screensaver.cpp + powermanager.cpp + utils.cpp + users.cpp + displaymanager.cpp + iconedit.cpp + imageutil.cpp + logind.cpp + hoverwidget.cpp + weathermanager.cpp + tabletlockwidget.cpp + gestureauthdialog.cpp + digitalauthdialog.cpp + switchbuttongroup.cpp + wechatauthdialog.cpp + verificationwidget.cpp + verticalVerificationwidget.cpp + eduplatforminterface.cpp + accountsinterface.cpp + networkwatcher.cpp + digitalkeyboard.cpp + surewindow.cpp + PhysicalDeviceSet/brightnessdeviceset.cpp + PhysicalDeviceSet/flightmodeset.cpp + PhysicalDeviceSet/sounddeviceset.cpp + PhysicalDeviceSet/touchscreenset.cpp + ) +add_executable(ukui-screensaver-dialog ${dialog_SRC}) + +target_link_libraries(ukui-screensaver-dialog + Qt5::Core + Qt5::Widgets + Qt5::DBus + Qt5::Svg + Qt5::X11Extras + Qt5::Network + ${EXTRA_LIBS} + BiometricAuth + VirtualKeyboard + Common + Kylin-nm + ukui-log4qt + ) +link_libraries(libmatemixer.so glib-2.0.so) + +qt5_add_dbus_adaptor(backend_SRC + org.ukui.ScreenSaver.xml + interface.h + Interface + ) + +qt5_wrap_cpp(backend_SRC + interface.h + sessionwatcher.h + logind.h + ) +set(backend_SRC + ${backend_SRC} + ukui-screensaver-backend.cpp + interface.cpp + sessionwatcher.cpp + logind.cpp + ) +add_executable(ukui-screensaver-backend ${backend_SRC}) +target_link_libraries(ukui-screensaver-backend Qt5::Core Qt5::DBus ${QGS_LIBRARIES} ukui-log4qt) + +set(command_SRC + ukui-screensaver-command.cpp + ) +add_executable(ukui-screensaver-command ${command_SRC}) +target_link_libraries(ukui-screensaver-command Qt5::Core Qt5::DBus ukui-log4qt) + +set(checkpass_SRC + ukui-screensaver-checkpass.cpp + ) +add_executable(ukui-screensaver-checkpass ${checkpass_SRC}) +target_link_libraries(ukui-screensaver-checkpass ${PAM_LIBRARIES}) + +install(TARGETS + ukui-screensaver-dialog + ukui-screensaver-backend + ukui-screensaver-command + ukui-screensaver-checkpass + DESTINATION bin) + +#set(test-act_SRC +# users.cpp +# test-accounts.cpp +# ) +#add_executable(test-accounts ${test-act_SRC}) +#target_link_libraries(test-accounts Qt5::Core Qt5::DBus) diff --git a/src/PhysicalDeviceSet/brightnessdeviceset.cpp b/src/PhysicalDeviceSet/brightnessdeviceset.cpp new file mode 100644 index 0000000..455c5d0 --- /dev/null +++ b/src/PhysicalDeviceSet/brightnessdeviceset.cpp @@ -0,0 +1,104 @@ +#include "brightnessdeviceset.h" + +int BrightnessDeviceSet::m_value = -1; +int BrightnessDeviceSet::m_maxBrightnessValue = -1; +bool BrightnessDeviceSet::m_isPowerSupply = false; +int BrightnessDeviceSet::m_curBrightnessValue = -1; + +BrightnessDeviceSet* BrightnessDeviceSet::instance(QObject *parent) +{ + static BrightnessDeviceSet* _instance = nullptr; + QMutex mutex; + mutex.lock(); + if(_instance == nullptr) + _instance = new BrightnessDeviceSet(parent); + mutex.unlock(); + return _instance; +} + +BrightnessDeviceSet::BrightnessDeviceSet(QObject *parent): + QObject(parent) +{ + init(); +} + +void BrightnessDeviceSet::init() +{ + //m_isPowerSupply = true; + m_isPowerSupply = isPowerSupply(); + if(!m_isPowerSupply){ + qWarning() << "info: [BrightnessDeviceSet][init]: current device not support brightness-change!"; + } + m_curBrightnessValue = getCurBrightness(); + m_maxBrightnessValue = getMaxBrightness(); + //m_maxBrightnessValue = 19200; + m_value = m_curBrightnessValue * 1.0 / m_maxBrightnessValue * 100; +} + +int BrightnessDeviceSet::getValue() +{ + return m_value; +} + +void BrightnessDeviceSet::setValue(int value) +{ + if(!m_isPowerSupply) return; + if(value == m_value) return; + if(value > 100) value = 100; + else if(value < 0) value = 0; + m_value = value; + //emit valueChanged(m_value); + // 调整亮度 + int val = value * 1.0 / 100 * m_maxBrightnessValue; + setBrightness(val); +} + +bool BrightnessDeviceSet::isPowerSupply() +{ + QProcess process; + QString command = "/usr/lib/ukui-greeter/greeter-backlight-helper --get-support-brightness"; + + process.start(command); + process.waitForFinished(3000); + QString result = process.readAll(); + + return result == "true"; +} + +int BrightnessDeviceSet::getMaxBrightness() +{ + if(!m_isPowerSupply) return 0; + QProcess process; + QString command = "/usr/lib/ukui-greeter/greeter-backlight-helper --get-max-brightness"; + + process.start(command); + process.waitForFinished(3000); + QString result = process.readAll(); + return result.toInt(); +} + +int BrightnessDeviceSet::getCurBrightness() +{ + if(!m_isPowerSupply) return 0; + QProcess process; + QString command = "/usr/lib/ukui-greeter/greeter-backlight-helper --get-brightness"; + + process.start(command); + process.waitForFinished(3000); + QString result = process.readAll(); + return result.toInt(); +} + +void BrightnessDeviceSet::setBrightness(int val) +{ + if(!m_isPowerSupply) return; + if(val > m_maxBrightnessValue) val = m_maxBrightnessValue; + else if(val < 100) val = 100; + + QString command = "/usr/lib/ukui-greeter/greeter-backlight-helper --set-brightness " + QString::number(val); + + QProcess process; + process.start(command); + process.waitForFinished(3000); + //QString result = process.readAll(); +} diff --git a/src/PhysicalDeviceSet/brightnessdeviceset.h b/src/PhysicalDeviceSet/brightnessdeviceset.h new file mode 100644 index 0000000..77cd162 --- /dev/null +++ b/src/PhysicalDeviceSet/brightnessdeviceset.h @@ -0,0 +1,40 @@ +#ifndef BRIGHTNESSDEVICESET_H +#define BRIGHTNESSDEVICESET_H + +#include +#include +#include +#include + +class BrightnessDeviceSet : public QObject +{ + Q_OBJECT +public: + static BrightnessDeviceSet* instance(QObject *parent = nullptr); + +public Q_SLOTS: + // val from 0 to 100 change + void setValue(int value); + int getValue(); + +private: + BrightnessDeviceSet(QObject *parent = nullptr); + void init(); + bool isPowerSupply(); + int getMaxBrightness(); + int getCurBrightness(); + // val from 0 to MaxBrightness change + void setBrightness(int val); + +Q_SIGNALS: + // val form to 100 change + //void valueChanged(int val); + +private: + static int m_value; + static int m_maxBrightnessValue; + static int m_curBrightnessValue; + static bool m_isPowerSupply; +}; + +#endif // BRIGHTNESSDEVICESET_H diff --git a/src/PhysicalDeviceSet/flightmodeset.cpp b/src/PhysicalDeviceSet/flightmodeset.cpp new file mode 100644 index 0000000..adcbf67 --- /dev/null +++ b/src/PhysicalDeviceSet/flightmodeset.cpp @@ -0,0 +1,20 @@ +#include "flightmodeset.h" + +bool FlightModeSet::m_isFlightModeOpen = false; + +FlightModeSet* FlightModeSet::instance(QObject *parent) +{ + static FlightModeSet* _instance = nullptr; + QMutex mutex; + mutex.lock(); + if(_instance == nullptr) + _instance = new FlightModeSet(parent); + mutex.unlock(); + return _instance; +} + +FlightModeSet::FlightModeSet(QObject *parent): + QObject(parent) +{ + +} diff --git a/src/PhysicalDeviceSet/flightmodeset.h b/src/PhysicalDeviceSet/flightmodeset.h new file mode 100644 index 0000000..57842a4 --- /dev/null +++ b/src/PhysicalDeviceSet/flightmodeset.h @@ -0,0 +1,28 @@ +#ifndef FLIGHTMODESET_H +#define FLIGHTMODESET_H +/*! + *@brief 设置飞行模式 + * 本想在登录界面加载时 提供实体键 飞行模式的功能 + * 后来发现有效。但是飞行模式具体设置是谁负责管理的 还未知。 + * 最初实现的策略是通过 nmcli networking off 的方式设置飞行模式。 + */ + + +#include +#include + +class FlightModeSet : public QObject +{ + Q_OBJECT +public: + static FlightModeSet* instance(QObject *parent = nullptr); + +private: + FlightModeSet(QObject *parent = nullptr); + +private: + static bool m_isFlightModeOpen; + +}; + +#endif // FLIGHTMODESET_H diff --git a/src/PhysicalDeviceSet/physical-device-set.pri b/src/PhysicalDeviceSet/physical-device-set.pri new file mode 100644 index 0000000..365f7bf --- /dev/null +++ b/src/PhysicalDeviceSet/physical-device-set.pri @@ -0,0 +1,11 @@ +HEADERS += \ + $$PWD/brightnessdeviceset.h \ + $$PWD/flightmodeset.h \ + $$PWD/sounddeviceset.h \ + $$PWD/touchscreenset.h + +SOURCES += \ + $$PWD/brightnessdeviceset.cpp \ + $$PWD/flightmodeset.cpp \ + $$PWD/sounddeviceset.cpp \ + $$PWD/touchscreenset.cpp diff --git a/src/PhysicalDeviceSet/sounddeviceset.cpp b/src/PhysicalDeviceSet/sounddeviceset.cpp new file mode 100644 index 0000000..f16ba8c --- /dev/null +++ b/src/PhysicalDeviceSet/sounddeviceset.cpp @@ -0,0 +1,221 @@ +#include "sounddeviceset.h" + +int SoundDeviceSet::m_value = -1; + +SoundDeviceSet* SoundDeviceSet::instance(QObject *parent) +{ + static SoundDeviceSet* _instance = nullptr; + QMutex mutex; + mutex.lock(); + if(!_instance) + _instance = new SoundDeviceSet(parent); + mutex.unlock(); + return _instance; +} + +SoundDeviceSet::SoundDeviceSet(QObject *parent): + QObject(parent) +{ + init(); +} + + +void SoundDeviceSet::init(){ + if (mate_mixer_init () == FALSE){ + qDebug()<<"matemixer init false"; + return ; + } + + context = mate_mixer_context_new (); + if (mate_mixer_context_open (context) == FALSE) { + qDebug()<<"matemixer context open context false."; + return ; + } + + MateMixerState state = mate_mixer_context_get_state (context); + + switch (state) { + case MATE_MIXER_STATE_READY: + connected (context); + break; + case MATE_MIXER_STATE_CONNECTING: + g_print ("Waiting for connection...\n"); + + /* The state will change asynchronously to either MATE_MIXER_STATE_READY + * or MATE_MIXER_STATE_FAILED, wait for the change in a main loop */ + g_signal_connect (G_OBJECT (context), + "notify::state", + G_CALLBACK (on_context_state_notify), + NULL); + break; + default: + qDebug()<<"it's can not reached"; + break; + } +} + +void SoundDeviceSet::emitSignal(bool val){ + emit muteChanged(val); +} + +void SoundDeviceSet::setValue(int val){ + if(!context) + return ; + if(val > 100) val = 100; + else if(val < 0) val = 0; + if(val == m_value) return; + m_value = val; + emit valueChanged(m_value); + + MateMixerStream *m_pOutputStream = mate_mixer_context_get_default_output_stream(context); + if(m_pOutputStream == NULL) + return; + + MateMixerStreamControl *control = mate_mixer_stream_get_default_control(m_pOutputStream); + + bool status = false; + int volume = val*65536/100; + + mate_mixer_stream_control_set_volume(control,guint(volume)); + if (val <= 0) { + status = true; + mate_mixer_stream_control_set_mute(control,status); + mate_mixer_stream_control_set_volume(control,0); + } + else { + mate_mixer_stream_control_set_mute(control,status); + } +} + +void SoundDeviceSet::onStreamControlMuteNotify(MateMixerStreamControl *m_pControl,GParamSpec *pspec,gpointer point){ + + if(!m_pControl) + return ; + + bool state = mate_mixer_stream_control_get_mute (m_pControl); + SoundDeviceSet::instance()->emitSignal(state); +} + +void SoundDeviceSet::onStreamControlVolumeNotify(MateMixerStreamControl *m_pControl,GParamSpec *pspec,gpointer point){ + + MateMixerStreamControlFlags flags; + guint volume = 0; + + if (m_pControl != nullptr) + flags = mate_mixer_stream_control_get_flags(m_pControl); + + if (flags&MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE) { + volume = mate_mixer_stream_control_get_volume(m_pControl); + } + + MateMixerDirection direction; + MateMixerStream *m_pStream = mate_mixer_stream_control_get_stream(m_pControl); + + direction = mate_mixer_stream_get_direction(m_pStream); + //设置输出滑动条的值 + int value = volume*100/65536.0 + 0.5; + if (direction == MATE_MIXER_DIRECTION_OUTPUT) { + SoundDeviceSet::instance()->setValue(value); + } +} + +void SoundDeviceSet::connected (MateMixerContext *context) +{ + + MateMixerStream *m_pOutputStream = mate_mixer_context_get_default_output_stream(context); + if(m_pOutputStream == NULL) + return; + + MateMixerStreamControl *control = mate_mixer_stream_get_default_control(m_pOutputStream); + + g_signal_connect ( G_OBJECT (control), + "notify::volume", + G_CALLBACK (onStreamControlVolumeNotify), + NULL); + + g_signal_connect (control, + "notify::mute", + G_CALLBACK (onStreamControlMuteNotify), + NULL); + + SoundDeviceSet::instance()->setDefaultVal(); +} + +void SoundDeviceSet::setDefaultVal() +{ + if(!context) + return ; + + MateMixerStream *m_pOutputStream = mate_mixer_context_get_default_output_stream(context); + if(m_pOutputStream == NULL) + return; + + MateMixerStreamControl *control = mate_mixer_stream_get_default_control(m_pOutputStream); + +// int volume = mate_mixer_stream_control_get_volume(control); +// int value = volume *100 /65536.0+0.5; + +// setValue(value); +} + +void SoundDeviceSet::on_context_state_notify (MateMixerContext *context,GParamSpec *pspec,gpointer point ) +{ + MateMixerState state; + + state = mate_mixer_context_get_state (context); + + switch (state) { + case MATE_MIXER_STATE_READY: + /* This state can be reached repeatedly if the context is connected + * to a sound server, the connection is dropped and then reestablished */ + connected (context); + break; + case MATE_MIXER_STATE_FAILED: + qDebug()<<"matemixser state failed"; + break; + default: + break; + } +} + +bool SoundDeviceSet::getIsMute(){ + if(!context) + return false; + + MateMixerState state; + state = mate_mixer_context_get_state (context); + if(state != MATE_MIXER_STATE_READY) + return false; + + MateMixerStream *m_pOutputStream = mate_mixer_context_get_default_output_stream(context); + if(m_pOutputStream == NULL) + return false; + + MateMixerStreamControl *control = mate_mixer_stream_get_default_control(m_pOutputStream); + + return mate_mixer_stream_control_get_mute(control); +} + +void SoundDeviceSet::setMute(bool val){ + if(!context) + return; + + MateMixerState state; + state = mate_mixer_context_get_state (context); + if(state != MATE_MIXER_STATE_READY) + return; + + MateMixerStream *m_pOutputStream = mate_mixer_context_get_default_output_stream(context); + if(m_pOutputStream == NULL) + return; + + MateMixerStreamControl *control = mate_mixer_stream_get_default_control(m_pOutputStream); + + mate_mixer_stream_control_set_mute(control,val); +} + +bool SoundDeviceSet::getIsReady(){ + MateMixerState state; + state = mate_mixer_context_get_state (context); + return state == MATE_MIXER_STATE_READY; +} diff --git a/src/PhysicalDeviceSet/sounddeviceset.h b/src/PhysicalDeviceSet/sounddeviceset.h new file mode 100644 index 0000000..2b98aaa --- /dev/null +++ b/src/PhysicalDeviceSet/sounddeviceset.h @@ -0,0 +1,48 @@ +#ifndef SOUNDDEVICESET_H +#define SOUNDDEVICESET_H + +#include +#include +#include +#include +#include +#include + +class SoundDeviceSet : public QObject +{ + Q_OBJECT +public: + static SoundDeviceSet* instance(QObject *parent = nullptr); + void setMute(bool val); + bool getIsMute(); + void setDefaultVal(); + int getValue(){ return m_value; } + +Q_SIGNALS: + void muteChanged(bool mute); + // val from 0 to 100 change + void valueChanged(int val); + +public Q_SLOTS: + // val from 0 to 100 change + void setValue(int val); + +private: + SoundDeviceSet(QObject *parent = nullptr); + +private: + MateMixerContext *context; + + static void onStreamControlMuteNotify(MateMixerStreamControl *m_pControl,GParamSpec *pspec,gpointer point); + static void onStreamControlVolumeNotify(MateMixerStreamControl *m_pControl,GParamSpec *pspec,gpointer point); + static void on_context_state_notify (MateMixerContext *context,GParamSpec *pspec,gpointer point ); + static void connected (MateMixerContext *context); + void init(); + void emitSignal(bool val); + bool getIsReady(); + + static int m_value; // 当前的音量值 + +}; + +#endif // SOUNDDEVICESET_H diff --git a/src/PhysicalDeviceSet/touchscreenset.cpp b/src/PhysicalDeviceSet/touchscreenset.cpp new file mode 100644 index 0000000..0ba4449 --- /dev/null +++ b/src/PhysicalDeviceSet/touchscreenset.cpp @@ -0,0 +1,30 @@ +#include "touchscreenset.h" + +#include + +TouchScreenSet* TouchScreenSet::instance(QObject *parent) +{ + static TouchScreenSet* _instance = nullptr; + QMutex mutex; + mutex.lock(); + if(!_instance) + _instance = new TouchScreenSet(parent); + mutex.unlock(); + return _instance; +} + +TouchScreenSet::TouchScreenSet(QObject *parent): + QObject(parent) +{ + init(); +} + +void TouchScreenSet::init() +{ + +} + +int TouchScreenSet::getTouchScreenID() +{ + +} diff --git a/src/PhysicalDeviceSet/touchscreenset.h b/src/PhysicalDeviceSet/touchscreenset.h new file mode 100644 index 0000000..fd6c5ef --- /dev/null +++ b/src/PhysicalDeviceSet/touchscreenset.h @@ -0,0 +1,18 @@ +#ifndef TOUCHSCREENSET_H +#define TOUCHSCREENSET_H + +#include + +class TouchScreenSet : public QObject +{ + Q_OBJECT +public: + TouchScreenSet *instance(QObject* parent = nullptr); + void init(); + int getTouchScreenID(); + +private: + TouchScreenSet(QObject *parent = nullptr); +}; + +#endif // TOUCHSCREENSET_H diff --git a/src/accountsinterface.cpp b/src/accountsinterface.cpp new file mode 100644 index 0000000..4b6abb5 --- /dev/null +++ b/src/accountsinterface.cpp @@ -0,0 +1,110 @@ +#include "accountsinterface.h" + +const QString STR_EDU_SERVICE = "cn.kylinos.SSOBackend"; +const QString STR_EDU_PATH = "/cn/kylinos/SSOBackend"; + +AccountsInterface* AccountsInterface::m_instance = nullptr; + +AccountsInterface::AccountsInterface(const QString &strService,const QString &strPath,\ + const QDBusConnection &connection, QObject *parent)\ + :QDBusAbstractInterface(strService,strPath,getInterfaceName(),connection,parent) +{ + +} + +AccountsInterface* AccountsInterface::getInstance() +{ + static QMutex mutex; + mutex.lock(); + if(m_instance == nullptr) + m_instance = new AccountsInterface(STR_EDU_SERVICE, STR_EDU_PATH, QDBusConnection::systemBus()); + mutex.unlock(); + return m_instance; +} + +DBusMsgCode AccountsInterface::SetAccountPincode(const QString &username, const QString &pincode) +{ + QDBusReply reply = call("SetAccountPincode", username, pincode); + if(!reply.isValid()) + { + qDebug() << "error: [AccountsInterface][SetAccountPincode]: DBus Connect Failed!"; + return DBusMsgCode::Error_NoReply; + } + if(reply.value() != 0) + { + qDebug() << "error: [AccountsInterface][SetAccountPincode]: DBus request failed!"; + //return static_cast(reply.value()); + return DBusMsgCode::Error_NoReply; + } + + return DBusMsgCode::No_Error; +} + +DBusMsgCode AccountsInterface::GetAccountPincode(const QString &username, QString &pincode) +{ + QDBusMessage message = call("GetAccountPincode", username); + if(QDBusMessage::ErrorMessage == message.type()) + { + qDebug() << "error: [AccountsInterface][GetAccountPincode]: DBus Connect Failed!"; + return DBusMsgCode::Error_NoReply; + } + QList argvs = message.arguments(); + if(argvs.size() != 2) + { + qDebug() << "error: [AccountsInterface][GetAccountPincode]: DBus arguments error!"; + return DBusMsgCode::Error_ArgCnt; + } + int state = argvs.at(1).value(); + if(state != 0) + { + qDebug() << "error: [AccountsInterface][GetAccountPincode]: DBus request failed!"; + //return static_cast(state); + return DBusMsgCode::Error_ArgCnt; + } + pincode = argvs.at(0).value(); + qDebug() << "pincode:" << pincode; + + return DBusMsgCode::No_Error; +} + +DBusMsgCode AccountsInterface::CheckUserIsNew(const QString &username, bool &isNewUser) +{ + QDBusReply reply = call("CheckUserIsNew", username); + if(!reply.isValid()) + { + qDebug() << "info: [AccountsInterface][CheckUserIsNew]: DBus connect failed!"; + return DBusMsgCode::Error_NoReply; + } + if(reply.value()) + { + qDebug() << "info: [AccountsInterface][CheckUserIsNew]: DBus request failed!"; + return DBusMsgCode::Error_UnknownReason; + } + + isNewUser = static_cast(reply.value()); + return DBusMsgCode::No_Error; +} + +DBusMsgCode AccountsInterface::GetUserPhone(const QString &username, QString &phonenum) +{ + QDBusMessage message = call("GetAccountBasicInfo", username); + if(QDBusMessage::ErrorMessage == message.type()) + { + qDebug() << "info: [AccountsInterface][GetUserPhone]: DBus Connect Failed!"; + return DBusMsgCode::Error_NoReply; + } + QList argvs = message.arguments(); + if(argvs.size() != 7) + { + qDebug() << "info: [AccountsInterface][GetUserPhone]: DBus arguments =" << argvs.size() << "error!"; + return DBusMsgCode::Error_ArgCnt; + } + int role = argvs.at(0).value(); Q_UNUSED(role); + QString school = argvs.at(1).value(); Q_UNUSED(school); + QString province = argvs.at(2).value(); Q_UNUSED(province); + QString city = argvs.at(3).value(); Q_UNUSED(city); + QString county = argvs.at(4).value(); Q_UNUSED(county); + phonenum = argvs.at(5).value(); + int state = argvs.at(6).value(); Q_UNUSED(state); + return state ? DBusMsgCode::Error_UnknownReason : DBusMsgCode::No_Error; +} diff --git a/src/accountsinterface.h b/src/accountsinterface.h new file mode 100644 index 0000000..cc97edd --- /dev/null +++ b/src/accountsinterface.h @@ -0,0 +1,41 @@ +#ifndef ACCOUNTSINTERFACE_H +#define ACCOUNTSINTERFACE_H + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "common.h" + +class AccountsInterface: public QDBusAbstractInterface +{ + Q_OBJECT +public: + static inline const char* getInterfaceName() + {return "cn.kylinos.SSOBackend.accounts";} +public: + static AccountsInterface* getInstance(); + + AccountsInterface(const QString &strService,const QString &strPath,\ + const QDBusConnection &connection, QObject *parent = 0); + + DBusMsgCode SetAccountPincode(const QString &username, const QString &pincode); + + DBusMsgCode GetAccountPincode(const QString &username, QString &pincode); + + // 检查用户是不是新用户 + DBusMsgCode CheckUserIsNew(const QString &username, bool &isNewUser); + // 获得用户的手机号 + DBusMsgCode GetUserPhone(const QString &username, QString &phone); +private: + static AccountsInterface *m_instance; +}; + +#endif // ACCOUNTSINTERFACE_H diff --git a/src/assets.qrc b/src/assets.qrc new file mode 100644 index 0000000..6059541 --- /dev/null +++ b/src/assets.qrc @@ -0,0 +1,129 @@ + + + assets/authdialog.qss + + + assets/show-password.png + assets/hide-password.png + assets/keyboard.png + assets/switchGreeter.png + assets/switchUser.png + assets/waiting.png + assets/capslock.png + assets/combobox_down.png + assets/scrollbar_down.png + assets/scrollbar_down_clicked.png + assets/scrollbar_down_hover.png + assets/scrollbar_up.png + assets/scrollbar_up_clicked.png + assets/scrollbar_up_hover.png + assets/unlock-button.png + assets/login-button-hover.svg + assets/capslock.svg + assets/login-button.svg + assets/unlock-button-hover.png + assets/unlock-button-pressed.png + assets/iconFace.png + assets/iconFace.svg + assets/powerManager.png + assets/lock.png + assets/logout.png + assets/shutdown.png + assets/suspend.png + assets/hibernate.png + assets/reboot.png + assets/bio-login.png + assets/password-login.png + assets/weather/68.png + assets/weather/67.png + assets/weather/66.png + assets/weather/63.png + assets/weather/62.png + assets/weather/60.png + assets/weather/59.png + assets/weather/58.png + assets/weather/56.png + assets/weather/55.png + assets/weather/53.png + assets/weather/52.png + assets/weather/51.png + assets/weather/49.png + assets/weather/48.png + assets/weather/47.png + assets/weather/45.png + assets/weather/43.png + assets/weather/41.png + assets/weather/40.png + assets/weather/39.png + assets/weather/38.png + assets/weather/36.png + assets/weather/35.png + assets/weather/33.png + assets/weather/31.png + assets/weather/30.png + assets/weather/29.png + assets/weather/28.png + assets/weather/27.png + assets/weather/24.png + assets/weather/22.png + assets/weather/21.png + assets/weather/20.png + assets/weather/19.png + assets/weather/18.png + assets/weather/17.png + assets/weather/16.png + assets/weather/15.png + assets/weather/14.png + assets/weather/13.png + assets/weather/11.png + assets/weather/10.png + assets/weather/9.png + assets/weather/8.png + assets/weather/7.png + assets/weather/6.png + assets/weather/5.png + assets/weather/4.png + assets/weather/3.png + assets/weather/2.png + assets/weather/1.png + assets/weather/0.png + assets/intel/message.png + assets/intel/more.png + assets/intel/slide_unlock.png + assets/intel/qr-code.png + assets/intel/wifi.png + assets/intel/delete.svg + assets/intel/pin.png + assets/intel/wechat.png + assets/intel/back.png + assets/intel/cancel.png + assets/intel/qr-reload.png + assets/intel/icon-wechat-noqrcode.png + assets/intel/icon-no-signal.png + assets/intel/icon-wired.png + assets/intel/icon-wifi.png + assets/intel/sms.png + assets/intel/phone.png + assets/intel/icon-backspace.png + assets/intel/lock.png + assets/intel/logout.png + assets/intel/shutdown.png + assets/intel/reboot.png + assets/intel/powerManager.png + assets/intel/keyboard.png + assets/ukui-qrcode-null.svg + assets/ukui-loginopt-voice.svg + assets/ukui-loginopt-qrcode.svg + assets/ukui-loginopt-iris.svg + assets/ukui-loginopt-fingervein.svg + assets/ukui-loginopt-finger.svg + assets/ukui-loginopt-face.svg + assets/ukui-loginopt-password.svg + assets/selected.svg + assets/keyboard.svg + assets/ukui-loginopt-smile.svg + assets/ukui-loginopt-lose.svg + assets/01-default-commercial.png + assets/01-default-community.png + + diff --git a/src/assets/01-default-commercial.png b/src/assets/01-default-commercial.png new file mode 100644 index 0000000000000000000000000000000000000000..eda418c0ae2877ff35b25551e9a13cb1fd0e6448 GIT binary patch literal 68347 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

|>y5}(m4hbL*0g+IXfh#vx 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 literal 0 HcmV?d00001 diff --git a/src/assets/capslock.png b/src/assets/capslock.png new file mode 100644 index 0000000000000000000000000000000000000000..b4259ab09f0f4a4781c81d823f0002244d427139 GIT binary patch literal 292 zcmV+<0o(qGP)PZed$6Eu@8Qff7OqaSOEI7V=@4A?EIUlQCapzkJr+Y9+1U zAFswo%_T_<&;hpH*Sy(Q3J}l&4RES?uvui3c0p|5C~3b6agg3@pMh(fS0lc}_XTL9 zioX*}60l=z1GWteRH$B3tl%3S{u$_&RA-4L9T{EVS!g=9CE}9ZI-rjv%`7$zah~*! z画板 5 \ No newline at end of file diff --git a/src/assets/combobox_down.png b/src/assets/combobox_down.png new file mode 100644 index 0000000000000000000000000000000000000000..e07de91b143c1b744e63308111b6fba69ecf72da GIT binary patch literal 181 zcmeAS@N?(olHy`uVBq!ia0vp^JV4CG!3HG1zpHNqQZ=3~jv*3LOV4fOWO5W>y-?o0 zSK@Y4m)r~4B=)cr#zhg0jh!939BgbwJ6Ur$)s$tp%^%J`K7VD=x%f993~Y+$Z1l`1 zaItO3o^!}nI4kc1-9Vin?gSY@*}&dF z?gVxwus4u5K#nPR!9frOO0rDc<#LHg@d3o=0fIRM5jTjqL&OFVFW8m+EC(s_ZR|!5 z8UTI)KnVb=f#uh!KxY%OcB5GNqFDb#ie>?PtP3eY!Uwh4r&?osj2S8L(nqC|Gx+2+2YfcF?E2#o4+tCj~-AxeTn^ zg#^qgItK6&8z@BYY+zCwgfqlerYX0jp2{qu*r~ zE#!z8DA*n|ouDe-$i1Tbi-~$$Pi$&tav>^q2m15H3LXF&Dw^J>e>&&hjJgNWZ+i^8 zOk}SGCr*442Z-h<(ymM-?|YL>u!0b5JUK<#;ivI89BX{6(gwzGv@R_`2B@ z=TyKnR!LA|pOO@25$j+pqJ*rrEv4y6l!J`Bk7c{C&@@^!99W6lXem(5@j+NBkt8~^ zD5q@nhXkhi=A8RIQi+17hL#kLj1*q!Clst_qBWWacnWe*{@*!#aJ-(P2)U8=`87pU z_vzdKJGf@n^Tg|qbI#iI^eRJ^erDuxO+@WXMDNCKB&-?d3Cbs?yLVxf&A8uB8(}IR z`R?}llw6Gme?03Xyg2%qb%ytEMT{g@bju+{&xh{a$ePg?+(`wnH;5w#Zzw%Vo_?kD z_#T<^tto&vN}BY0|2pT6shCCyRmXaO?+-~RaHD5T$v2=00q{S5&gw1O=K`T<`670l cY;6Ge0L`3Ni&5T9hX4Qo07*qoM6N<$f>%77%K!iX literal 0 HcmV?d00001 diff --git a/src/assets/hide-password.png b/src/assets/hide-password.png new file mode 100644 index 0000000000000000000000000000000000000000..bd85995a17a5cb3cf8d9f9ba0fce448b458943d8 GIT binary patch literal 335 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 literal 0 HcmV?d00001 diff --git a/src/assets/iconFace.svg b/src/assets/iconFace.svg new file mode 100644 index 0000000..7d361c2 --- /dev/null +++ b/src/assets/iconFace.svg @@ -0,0 +1 @@ +avatar_128 \ No newline at end of file diff --git a/src/assets/intel/back.png b/src/assets/intel/back.png new file mode 100644 index 0000000000000000000000000000000000000000..c20cb56a867d1f77bf437d4dbeb816d670a344a8 GIT binary patch literal 299 zcmV+`0o4A9P)=9^|I?DRqelDy!H%wv2SuEL=o!5SswrZ002ovPDHLkV1ntvc*6hy literal 0 HcmV?d00001 diff --git a/src/assets/intel/cancel.png b/src/assets/intel/cancel.png new file mode 100644 index 0000000000000000000000000000000000000000..0895e2735ca1892b18ede6ff9c034cdacb9bba0e GIT binary patch literal 299 zcmV+`0o4A9P)|V3#X+ffP1+0^ z;y0juKv)dVfh&h3zymNiQk=>jCEY1{{>-l31X{ooP>Jd-a0`3@i>#f71IEC8c6wjZ zvzc{)S;TviUd`+#JV@me2YdnUd|G~55;%kqCt$jjr + + + + + diff --git a/src/assets/intel/icon-backspace.png b/src/assets/intel/icon-backspace.png new file mode 100644 index 0000000000000000000000000000000000000000..ba8ff487b84e60228dfa476d60fe39a2f27bc5fb GIT binary patch literal 307 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 F2>`2szT^M^ literal 0 HcmV?d00001 diff --git a/src/assets/intel/keyboard.png b/src/assets/intel/keyboard.png new file mode 100755 index 0000000000000000000000000000000000000000..f02fb5f18666aa153d7ce42859a603e49c7bf2e2 GIT binary patch literal 301 zcmV+|0n+}7P)Myl0W_& zJ9b3yP_TXC0cQ3Ad;#y!7|?(X@F_F<1G+(>`gg#gLC@=cngjegOGy*oHM0dU2HKML zX4V3Bk7_btCTX4kQ^4ov2aue2O!KN61mzfVh%#UctN>4Z4;TX9z%TFyjFRx4rjrfU z0c%Nvl#4#_0}Lds)0)pI(D>QYcsU?EM2#oA9-=DXRA?o{N-1uW#K0wrfr(5+q70N=280;{hlyJZT#7OvB?={!iNb(^qsRp3#8^m_&P<9D zk%gTEqcEWD$-sVKBQUcG#0Q`sI3?*#5zs<_ws!!hfhmbWe*kBJ!;*d_`V5=31ZaCS za1PjA7vv-G0C)%djHO-%%&4=w2dtO$DHXC+!1KVKjQER?72r;zUbDRzH~}or_&fww zNeWrbJ;{K!w*!|midRA=B@N|8v3(FY35<%i7bNY?^GhzV?FqmuU|JNtDrsvci+M$vCY0E3V10bpVD?~&A37I00N(kOCY(u$&R z5%3IfAZi?wbi6F!HlR0(T$8k=B;ZcqXw>MHbfhd`5WE;gE=k%|2C!A%lpgE?{+}hb zJu9q9z-nN8tG$zGKM33f4oQ01$fscsMz|+H*!enf!gjN$5o9yKTR`q_sLkYkl6vYj zn*n|XCPw3>k{-7iW?0Vl65vtP9+WgG70@+9Nx7e*GVr!XjMXfIUBFt_JC;G)V}Z?? ze|GMs9s95|CANJdT!a)U>hyOrn3=}P_h7ZB zIAtIj*>tQ2yMWmY*slCGQ~7-`tozm7sN+w(b|+bxSze3ILNmW&canBfl+pXA+j1GO an(rSC0!=u)G1J2U00003QEzabrPLQ6oSM@q7WLLLZPEoC^Q~I$cj#)B?=W089AG? za$URj-h1xaBX*K4c6VmZ@66npbI*v#Ma}rktRJWVO?5Kt?G?C}^p-)lp~|<#&1@D} z1;&axP1!vIhrqg|m%Ki|0Gio8u$b3LM*Rz5O455q{hHb*ppV>1!q}zf4#?#&wRjIO zl=y94(os>st3+B)fjLPoJwak-yTGz{axQ5)kHcC7eL*~WLl?P98F1eB1aRqnUrQP< z0yqrZc*9#s?t3X>W+On=JF7|>&EvQp!5}9;E%+Az2Y>~?i3dpsQAIEhOe5X4r??9k z8i7#zj;Gkn`XoIy+ek+N{qcGX>`7XQu1L&m6WH=#+mbfYke!ZT640@&N!m$51_Syi z6s?6xzboOrm-E&k_+MnX0WX>*C#qBAQ5&@%ZGwuc% zZ=?2Uj`yg`@^}RI!7Ph}=yU-?qF2gE(h9)L;=VMMb?uD);_haF@s2)x!;E)$I*UBj99Di*|_Ks`38z{~yh=J~%YaXbS)U002ovPDHLk FV1mAA@0b@?P);Guv?Dd|(+ z4!eY4Tf%GL-f>N=2uoa&078cqb=a3h5LbW~NUr>2@v5BQrTAvbEjMM^DOOncp0oB% z9P6UDv!i8Bs$60{CuPe>U^!DDk;_F7x`<51O3pbO{#+JF|I5oq|M$fU2m18=}H@B};ncOr71 zR6uy1qe zCL$*RfVmpX?X%`s5|M3jr@E=E|E)&l?->AdeOm%PjuQi5DQ}OD)&n;NU>azz0)Qr9 z1z2>lwt#iuH9~e)bJLpfXaMG`l>Dp$%VipD0BaFiWi>Y~fENR>P^I|4>B?haC9GVv z6rojCbJGHNuLGzrf%+y`4};z(Le}8$Hx+irVSf)p4uJQ^VQX{wTM%(x;BQIH0kGDW zzeQ08!TNxHmc@fpgsK*KDO7HZI4`g|p}(y$2f^RwXlN<;+a3>B6}-J5Q3k=^D>7vu i{JbO;RcI^9HMtKLl?EOP-D?j30000;mIXuN)?~KnBXY&X4jI#rma2pFBoTj`x z;Y!2H&+au66DD;`xO&M-(eRc-&hL7byRA1S8BIIWa+yJAX6KnVJPaRFyacz!uIvNa O%i!ti=d#Wzp$Pz+D?TUy literal 0 HcmV?d00001 diff --git a/src/assets/intel/pin.png b/src/assets/intel/pin.png new file mode 100644 index 0000000000000000000000000000000000000000..5585fef6184cdc04ffe46a2f2fa22ca5324d235d GIT binary patch literal 293 zcmV+=0owkFP)2JalSz@0jY;_f2HBL!rc4Th@@JItDU-6vplmYu2{xN=la$FI-THLS*KwSa zQzy^#)^p$2b3gARJSS$Lsz(9Sk>wf`~Oapho67X0U7Jvs}Reh~OngD0O zHn6E)kHkqffJ@+9-Dw3RnE>8^3w5_DOmYfr0aNN{+53?o$qeuU9I8h(z#^~=T&W*z zfLY)fI8cwf9B7;%$vSWX%>7{;0{iOCNLK|sbugFgp*mj&q)X)S?)p8vnxF)%6ry{e r?T!Bl=(I^wg5ICl?~I*tH;BFfP3&9|!Pg(^00000NkvXXu0mjfxC(tw literal 0 HcmV?d00001 diff --git a/src/assets/intel/powerManager.png b/src/assets/intel/powerManager.png new file mode 100644 index 0000000000000000000000000000000000000000..aab83b4f9f18d60a24495bca9461daf9b93bb8d6 GIT binary patch literal 574 zcmV-E0>S->P)$fyQ@% zMxjx#1qI>Ak-6NN`(f^lJLSxr^StNd_j_f)j|0w6Pk@MsJdj#DuJ(_a_`d-nVgk6T zer#@GCqP6j0`{wysx5D+AR;D#lfXlDv?kyhuoswAPc_Gs01>e%69eCYo$9@sfHB}U zumBiUA2s6s03sqkz5-SPN7ZxHa=XnSBF2G?c>@fqdC0$86cM|Cn+0S<{m~;}KJW}! z4UDPxtAHE8Zs3@Dww*rywME5poB?LkeN{k~mhD-uKJ61Q0%YtvHFK3(^aWT9EK@)C z2^a!C0bkW6RX`?P3Jj@p{a8puWPIkCQxjIIAU9|guvUH1CtwZm9C)qfuuFg$U=J{^ zUg#5W1UL^|S5tT;zz*OR@DfQPiUPQ!HiVgUd^8hZZKRc}LJm6r__l3Ze zdc33A6;))=X&@~-y(%~A3Xlf&z6f3cYz7VhxjA_erTR})57Y6&iHK}h2K`oKmnVBZ z(cG4<6*PY=95z8)fepa&;`;~S32+yfZ49*CA$qzp>kPd5&%gn{01H3f84Jg_7XSbN M07*qoM6N<$f)mv7zyJUM literal 0 HcmV?d00001 diff --git a/src/assets/intel/qr-code.png b/src/assets/intel/qr-code.png new file mode 100644 index 0000000000000000000000000000000000000000..5a56fb2ac583aee82d7068781e4d62c87b670767 GIT binary patch literal 865 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 R|N8&{002ovPDHLkV1k|_>Bj&7 literal 0 HcmV?d00001 diff --git a/src/assets/intel/shutdown.png b/src/assets/intel/shutdown.png new file mode 100644 index 0000000000000000000000000000000000000000..c367f685c24b784ae6257249ffe391db26d1c8c2 GIT binary patch literal 1065 zcmV+^1lIeBP)DMW1&^9*#uEl0O z1zggV?n!_*0~-rN>m=RY^`?C_*oyXbz`ezPt0np6N7h)th&UQ}AJ{)NHcMJPsaA#n z+D=5=1Kf}{e*hLr`n>I<3$QHc4}eAE(mJFD$CSk0z?(i1@|lmHi+ceh;uzo~!1dyz zWU-{TCq*wKGWaHm2*deIwXZ*bVx#U{ooX5 z1or{H1rAC-he`UX@!1Ak3Ott&Uy*cX4cu%}oVqUqjj!dB8itVwUIW~pP!CJGdX4}e z0DVq_PXH^;<0$5j%jhQ=#grE1Lyh z;3wBK3)Mu0U_`jfc1Pnt)in#5=BIrRr!BYmZnu7!2BJ$RBK8B^B`r*ut&$p_d(^u6 zL0{Mgt8Q3_!nYb$zq}h(H!3sMq?X@p&z*(z#Fl>uU_{)Sqi3V$Ij8hBIf9vH!#`U) zG}QoRT78W*t-jTK?yivJ8XQ@DW2a|gkDaF$njQhJ7`kzCLp%Z;SJ-+3I9HNqlCi61 zaQAHdcq@_~1zrN&g>3KY86(2I-Z{XP!1>wQHz1DOrILP}6#ZVnOt)tSfBJA!+H9Q0 zt{Yp=6`ulM=aI1h@MO5SxFH&OZwP+{^O-l}^U2^8w-s_6^~|!O6^)t_bd7EEi%Rls zRkf-jI0n>*`AWcJ;*1$$jMv?OzaBi=p4e&#rvfYu=Nxl9a5!*qKKl;v5dK~{U(^7# jWo8XHjng4I^B(X|mhiPWEfNKo00000NkvXXu0mjfh{)?b literal 0 HcmV?d00001 diff --git a/src/assets/intel/slide_unlock.png b/src/assets/intel/slide_unlock.png new file mode 100644 index 0000000000000000000000000000000000000000..02f1f6943250c2647fe5af359d7a3ce5595b0a7f GIT binary patch literal 408 zcmV;J0cZY+P)lJj0x^OAn}C_M1O33Wq=!a-)WQjv*#xix z%m7`%xB?skWl4`kU~&PNSvRl%tO3Jy3|@d^U|Z5v9+0{KW;PT|7lEFPg7-i#(2=os z=^((Fr1x4dRRCt@Ytz6wFa@-vEENso@izZ{?e>Stz7>=k%C3fMu3<=Oduu@6Ziz`L|q}2&b%uC0000w3y zga4Tr+ltZ?cK1(P=)u7F4`h%%nn4E{{xfwJX2tL5o4LT1@!$U{kQ%fwU_8U{?|*wi zR>JxTGv`_S|Id^IGK7)w-;{#%q!SZnE(l~`V60^L|K9{=IMzU9WMsI;z`)q~;l+bx zAcX=F3Q-IU|I7dXXV60T9)Vb3e8s>3jwCT$;X-*dQk)K`VE~0mnbOQeV)DgM4gi4^ VYtNIGdAtAs002ovPDHLkV1fhaj?Mr8 literal 0 HcmV?d00001 diff --git a/src/assets/intel/wechat.png b/src/assets/intel/wechat.png new file mode 100644 index 0000000000000000000000000000000000000000..c93885cef8b02da920e41ce37e65d8accfed054f GIT binary patch literal 402 zcmV;D0d4+?P)vvJz=;}LMAR;9M`k|9l^&2Xe`LqMkeTIv^<1r#vaQ9% wI))fA64+d*vdlahf&G$R+TIJC0&W4D3N0E4%#D(4 z2T?sRB0--64ZwZiecpv+R{-09d3pB**d?jS_A;PZQd=h#*03-MEfK)+K#HLC4DRDgRTHMi^!@fRdX9ClpZ~f}N5c76atGE@l^2ODYJU6HkxZ)OHxll3Hz#k4zKrYR)GdT_x#LcS5LWUE5v` zTnC0H&{triqB>As(G?uX^(}DDbdckbL}i zeN6Zax0M{10w(VZb^ddpocvsOdsX-j>DMOL-0#lfnZ85A|I|6j zS9?}%==_;j_eE{v3a;w(;2WzRhJHV>E^lWYW9a_dS9Q-OA66~<>z$bTXv!rS20n#Y V2?GI*&J3XK44$rjF6*2UngFU>M8E(5 literal 0 HcmV?d00001 diff --git a/src/assets/keyboard.svg b/src/assets/keyboard.svg new file mode 100644 index 0000000..79d0b1c --- /dev/null +++ b/src/assets/keyboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/lock.png b/src/assets/lock.png new file mode 100644 index 0000000000000000000000000000000000000000..ad2158634545462c789639d646562dca46567098 GIT binary patch literal 830 zcmV-E1Ht@>P)5Oa7=@qQYVE?-x>hQJA6qvnDt>`fP*+_jb|vk)#b4pxU(veOl}Z%_3o6=zh;0RJ zp{p(`tzFzoc_nfJ@p6-!n@M6Cb6}X{-pqN=$xP-W6Kd4ho$#Vmn^_iyeEy5TP2d7> z3^=I&)`73U8{i4>+7Ig-(2KOx9==hB)G)I#Gh4FcSW=mWOYFEi9%(csjslN>(<;6K zOaU{%XJ8H33mgWH09S$AD(B-Ja0mF7CVWvfdd1AvRENuEcGt{$5|r!FwaY5&b6m=@ zMzb_JY-UXfeQag}DLDF7*2HzaY(`I-*@gr@bbQVl;_$I4?ot@_o84Opn>I7g!DNs; zahbT2H2>Ue%U zO|eoNZFGMN{=yBNOanWPTh4<0 z+R<`O?c!`o)1)-UqEpj7q($+mlGofEa0^&XvrOwXvyU3A7tL%W<%%S)G$2ij(l}>k z9XqRbMkhqDU}l3QGT54YZC|8yB8|~m6&@+E$smnL>ui*txMwsNBM$%@UEir<(Jzfn z&-41aHCogdnHB!%^&)zsdK|49YNJ&{Z8QfA0Z+B$YU+5XsNh8oFVBE$Ex*0gmP8ha z9bSqw8caY2b^b%|g!WX;Xe(bX{Pl#P1R4rKZM16mZ;WQuwI{)LXm|;F+&s21+M>uB zeWiR|V<`F(r4STpbR2lDn*F8YdqoAW-!N1Swb81fHd-~r8QoSsunRozSN=2Vf;gie zl()VWC?kfYDe@D8IHOa_AB5ftlo5l{7}lH|{uZ3J;=画板 3 \ No newline at end of file diff --git a/src/assets/login-button.svg b/src/assets/login-button.svg new file mode 100644 index 0000000..22861a2 --- /dev/null +++ b/src/assets/login-button.svg @@ -0,0 +1 @@ +画板 2 \ No newline at end of file diff --git a/src/assets/logout.png b/src/assets/logout.png new file mode 100644 index 0000000000000000000000000000000000000000..cec5de0a1a3764dd6bfba15b1c42f40df9df2ccb GIT binary patch literal 562 zcmV-20?qx2P)tZU&$2AZ4ln=_tpPLuo&l`IBXQ*C6TlaMeF))b!M_Mm zXd>DWQBURQnTA$&biub7kvkTqA)-3-UCm<~{;$S%R7Hqr#mu$PZ5%74*JhO2l+e4! ztX8fPnKp*>8>L8}o5fP%tss0NqE{2bd0ve&R6CTQbeWy`)aH4vm{&rX!Ibb@b4l;> ze=p{(+-BG`4nWb2^MZYtxtbTbg=e^Ayp z+hHez@O^*Q$BzOj@;of&=fFGi;buTl?lY^%BMf`Gn%s$9cXsZ>#y#9pq5|;5zdxoI z@iw^ReBm-QdPnz136Y1-Hu`XeAZMbHgUQcq3W$-Ql(+<(^fqVVB~(4$h9rCl!KB5K z5UmAZ>{&fmBQqEu-d$)Bi`aD-t+-za?rqKvoZfUC5YBO93nA>KcCJ$i|H&M@r_)$S zLF2omg!iHzOK7XWdt(brXf48fb2q_tdtH3&_kcqX@q5H12>Lzb6NLR9vkE4D54sg& zpH~g8C}MHh<86rG*4J%fpEX)PUAd94zitEI8%2}|NyC&1R{#J207*qoM6N<$g4mn= Ap8x;= literal 0 HcmV?d00001 diff --git a/src/assets/password-login.png b/src/assets/password-login.png new file mode 100644 index 0000000000000000000000000000000000000000..9fc4338e9bd887eee4dde4bd03848f3f9c758359 GIT binary patch literal 188 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`Gj&7LlfAs(G?CpmH*HsE1V{~Pw1 z@%w*&4Zk|WgX{S1wwS1xX$A+mprtvm%!ks-Cx4y0>v`pf=TH>>BoT*t_5Q2t>b mbGB?&cF_x2G5J3t2iWv;_b0zN6lx804uhwwpUXO@geCyM2T1_{ literal 0 HcmV?d00001 diff --git a/src/assets/powerManager.png b/src/assets/powerManager.png new file mode 100644 index 0000000000000000000000000000000000000000..73089ad931255c194f7fffc2a4a7eca418487e3a GIT binary patch literal 501 zcmV%jUF{q7r4oIT_6}@TC1762EdTVmlPafh$qw>+Q(&r zzeJ=eA}b;?Drv_RYJEphRMpA6mRTtYnrk-ropG&1hSqvBlQ#ICajQh|HshTOe8ad~ zB6yGSHn##mJ)nX3_`1MpRby(#OJ0Pgv=%#cf>$lIZpg!0tmT6DR2EvEk&1O^#{ zK$bUHs|SIT&_O`EJ^FM2?DHjDrq`M4SffwN)(ZjaXWlh-@3Jv#<18)HLdV+{Z<$RI rS!Mo}jYoZ_7p`g49B&sk9}|%&YB6teDUM3=00000NkvXXu0mjfU{={U literal 0 HcmV?d00001 diff --git a/src/assets/reboot.png b/src/assets/reboot.png new file mode 100644 index 0000000000000000000000000000000000000000..5365f799ea8d47db745f222fd13c47463f292f53 GIT binary patch literal 1106 zcmV-Y1g-mtP)1t~lXcaspqng4FMiht`xFiQ^p#)v4Q30cU`Y&IIAuathRwY^<4Ju}rkJu)v8waj!?y-!z7Z*_ei z0}V9LKy^@(=Rr~oYwLh}fIEOqzzx7!;0j;}I1QWxegwV*J_p_hj<-3^oYh;?50Ydh zR&3B~fG2@Rfi0o_n@9%yHt;HNreK?8EM;JHgSt;mtNoQ<;?I=NWaWf-d?xa(tR8l z2Hxu@$dForI=$9Ad+`)ny6f!K=)5iA^L+)?#?qa%n=sdJkRgRYZ@2oB-r9?&5a=eW zzvwrhkV2q0T79&)_Tni7I%f6Begg_A^wdqa7ejWC)i3({=rc)@ty#bSC(zZv@6Kt2dVGEvhzHXW!*@8Up>s z>YM7#woIwlZ}YwkfqrK7T_u}~sk^PO!+Cv%p1SV;=&!cVb%CSbY7Xh%nHPY|f$Nhb z`6YWnvxm{VHQZ6Y&#txIf?Lk>3P@VKkG^5`Q}yFoq15ABZMCvcF!lhbql^__yQ@&djGY6N<~Jk6WOu0gR#a~k;FUjxYf=H;xUjoI(J0i{M^!(Ha# zqIp_zO}C)*I1r4DnQA(8T?|lq6xtuLvC^5GYIc1rP*-+jk~OxK2)WIBx;8Az-Ly?i zP;%05W+U&QCNk+aE*L#tq-|_aS5{k?eaXzE2~`hwm{2urZ4X&rvcC7(ScUq@Q>+xZ z>wrDLl$RoR&Qjz)&ZNls=TT#JiZO2QlXqrP?A*5QMd0Ul%v6!|=mJtt0FPNrwUeYr z_p0?K@H*}8T}@Sh`Wmw|Ru{+F?4_|@Y1b^;0r?NW*LJG$p?5IXu?i4z0lSh|()-bMibDH%rOU2DN&cMZ# anc?9|p2HvR9Q6a5$l&Sf=d#Wzp$P!;?I~aY literal 0 HcmV?d00001 diff --git a/src/assets/scrollbar_down_hover.png b/src/assets/scrollbar_down_hover.png new file mode 100644 index 0000000000000000000000000000000000000000..50da897d185e74200a2f242299abfa3e93bc58ac GIT binary patch literal 126 zcmeAS@N?(olHy`uVBq!ia0vp^{2&32-8QGdd@QN^!_kW&OWee;)Jw4Qk@rj#CZbG aGQ63>Gh8lE7-Efgxv`npbusoV*6cB1U?}qAJO0Ui^CF-r N44$rjF6*2UngD!+CAa_p literal 0 HcmV?d00001 diff --git a/src/assets/scrollbar_up_clicked.png b/src/assets/scrollbar_up_clicked.png new file mode 100644 index 0000000000000000000000000000000000000000..20c6e3c07ecceb07194a85b612a81fba80a540ae GIT binary patch literal 115 zcmeAS@N?(olHy`uVBq!ia0vp^{2 \ No newline at end of file diff --git a/src/assets/show-password.png b/src/assets/show-password.png new file mode 100644 index 0000000000000000000000000000000000000000..afaf744b986a594ee0c49057bb281749a00bd3ea GIT binary patch literal 582 zcmV-M0=fN(P)?UO)7A_07 zP?U{w%L3&OkcExc;{6Q|W{f?jPS4Z1e9w7)=lmZ2OI=#6)`;3@G+KWX*J`!EsG$l) z#Px`n@TX=XVnRe*AI?8|82eppjfj41#@Yh=jMunUnz}n2cvMMfM#TD>{Q|o29FwrE zJg1fS4(!F`h!|J_{1iTk*NSw-*Q ze(}l7GdP5WcsvLjT>#^86{j<^uU70Bj%8+V&DI+c9mTh{!22U&BCbZn5_}r$xDQV< z^XxB2*@a7`_`Qg@SM2ApsvcO1KP{}Ej^eA4r5eHCD*7F~4GSV-`!9jFv^Pa&F3ZfN z#omn@^}vYOhxyp@y@Rq9)A0a@Gqb-0&(j{(QRPTVp@UeSnJ-G*F73@&oS7f0s`>#g z0dsL1XEXCuv2|(>YGnc6L{DbEjED*cuoWvZ^9}q4E}$9y{(|&jCRS(W`$6B3;F2^3 zJ=lSM4B#2YVjk9E6Ph@ZnP2Mp!-8uCrl6sHBj*~4+jQ{`u literal 0 HcmV?d00001 diff --git a/src/assets/shutdown.png b/src/assets/shutdown.png new file mode 100644 index 0000000000000000000000000000000000000000..a7c9c6225aab6bb344d007f1b25e008fd54e99bb GIT binary patch literal 1157 zcmV;01bX|4P)mzmdh@11$M58i=~?~$4L=A1kCPG;sC zbkIQu?FW*n>}8U(B(Ldrl74*1e18m8j(}?b)+@}P05+uPB+Hh{YPHb%z~27L!YA~h z6dhF&v4pM#@G*ct06ZTd-VlIE#atI5o*y|r{6|P0B3a}ASRgslpng3<^>!#075k9- zDm)pL5jsHfM~C=Xl21RNO|wFcZ6$fese6&+rdA8xLvl_8rb)ikENF1a1Nqng<3o6v<-?aD|D}(rV;sW0g^F< zLKBl6S?_CHMyQzt{!s@93lfYa)Yu1VZ#Zp^CG=5}mlXJP334%o8v9%^E{S7lp$8T4 zYR-u#wa{*7Ha=2T=vtC@6l_;XVzGrnoG6ZlYhj_EDA3mtNTm>J>>I^0Tnh<(lH|N* z+3gh;x?LO%*QY{48!hVJ2G9lI9Dtvje@rch;r5-lz7^`ckl3;I3cs}K_U zto-*8$ry|Q_!q!!{f>zr6ze%(%!dOVZIhphOy)~j3RtdNeX&C$Iv_ujh*Sz#t^>Z< zA)$Tpf3=6k`>JY3`aWlNMXayQA$sdqZISbAJ=#M;WCU;vltgg z!*woKOXsPtb8?@}X3Sb?yTz?0u7!^~jU7<18Ir3jByyEF8NBb76%}eS&65f^Q6Zrd z;xt*G4KFIx*dWPU3VgHzB5nH6B$P6$&L^EykBY z%B;41ZHM{=}L{a8Yw?6>`9XCh_VLbl79U0Zrt!`h%T8ykET zc?C7Bm=;qcw=~l>wooY3d|1mbYAI&!8@)!dtAMyJwLPm?RO}=5S?#<+V_kYQH#Xk` zFsfaI!5y9Sn^eIs0Iq0Pl9;)_LDoD_VZfSeWY8VO`{dJJ9_}U3E&L?&lE@^ zQCqE(06s(BwX4wTYpiCpcIp!3W&j&?fVEQPx0PtR3gB0rYMhU8Xzml*K?faF7y$kQ Xw91=>Ug|;X00000NkvXXu0mjfzCIw! literal 0 HcmV?d00001 diff --git a/src/assets/suspend.png b/src/assets/suspend.png new file mode 100644 index 0000000000000000000000000000000000000000..63f2a32b7da50b62050547fa0f9045c9da874e85 GIT binary patch literal 817 zcmV-11J3-3P)9S%p+;%lTgPEd+IEC zG{%&3@Cbk_a7W+A;-vI|E8r^Wdr=Ywz!h-qLedW8Zk=x;Xf9xkIk0D0d!SDRgcUy` zc^zGT1tP{F8r&}2rLm2RPX$EUj8*0@eb+hTU{DMYUfJ*b-6-qrqmPB`+z{-YrUKsq z=SItfwVvsFk-e#7{ry`a-}m_;YfVUL<~)2)(@Gf@CaRrQ`1trGpDU#tXarwH(+h_@ z#VzGXsqo72VxH6*d#LZ>U5V-S{FA!av5JRZDzQsR3Zo!)u;o#r61O)UrRl5EEE#tf z%yzM(#fSwPu^SyFis<+tSsIZfzGqR!!)O009sNwu%_@#A9pri zQmd|cz`Zf%Re64~cyPR)q6oQ>cKw=yAMVq;18m`zS?>h^9*r@vhn`+#$kI!8AX-r? zf270G2ip-xGt3i|E2hZ{6=gH-_t%Xul}o`ItIt8P4 z(Y+m5Gx~x%ssILq*t77O(xV*io+&-PduB{43gESpCN1wDF}j%!R2^I4TB&daE_u>D vcn36)0QO@?E!MJqF5rrmEn1ljwEP))fo^Qk zuBeSxicmxlN<~HlHjbXS_FcJ+Gtc|InWryj4qUG9Ip@3QynEk!-~Ap+D4~Q{BSEqd z`sx9+0cZlS4?rb=1pr?GyaUh=;CcYB89;;Z3}yg~0T==B5Wsle3XRb`S{=y&u8iJ{ zF?@Lz1HuWdH6N2nlCvUYf#egC=Sl7%SxIsm$-N{mlH8wzmnPXyvfbfT%HQqszqI^z zR=BHb^onz}he_6k;0<~5YUQ^US60ht(iyaeWFiEwTfu|;w%g&WXI5*n&RKRnA$i?l zm!D&HR(sEC2eJ>4Vdaci-jI@)ZD#E<9*D|ly$mp~xF=m+TFJ}RYMlpEy)SQr;!V^h z{ZjywA$WsIUM@^ZEI0Ow&=_r!{*h8xv-FRZydcO1h{-u%^1RJZt zm*ATMCM2aZsFD=T7YxY@f=Ly>JK9!+W=8*O_!^}%8#|PS!}5Y)V|lL-&5Vvpe^4oE zO!@;#UJz`oF<*kl=rie0D@Bb+e@e*5k#}*dkmb&^vcqx9mZseK2Eb7O=^VUODR&mi z=(4!dM-n^?HbxXTHU=$9yO;HblF{{sTt*Y}$iEHXEr66fVBD4s6#$Y_DV)y2iKmnc>vpk>mQRbA|gzZud;D`h=4r*-5f2 z&$Ke1Rwf!!j*owG-II>VONxOy9Fr|;XfU(2IA%MnQF9eO%E=pwU=_|i*%>2~wsnd& zqp`xAI438jNN!OCvK#65YrI@@wKCCWkTegSGjj9Z0q|4t5NMUYcj<hA}nt;Q4L@UvLCgAy*}1Bh~nc>p|SLFY`y2Y zOW9CSCiR=vP)Fo2Mt{g4b;`l!q~9E4e>Kw28KckTpgn+)4HI(iSadq`hSfGxZe1ZzImvOytVtzn^88v>h*l$7 ztVyG>+y*vz;Z2W?@Jxn~-HzEfVq7#Xt1y_Ed_eNEr%^kzW*Lte?EnA(07*qoM6N<$f^{V5N&o-= literal 0 HcmV?d00001 diff --git a/src/assets/switchUser.png b/src/assets/switchUser.png new file mode 100644 index 0000000000000000000000000000000000000000..65b98eafa43a14a57bda2b0927a288f5e2b54916 GIT binary patch literal 424 zcmV;Z0ayNsP)sV!q>$#dVn1DLT{v;?3-LkIerM;1YNS-U53%26E@{Xo6B> z$NpYD0H3~)!_Qing)p~olr(K12A&%os!5ZV0?#=q2{V?`j z*DI587h~XdqY2zsl)kCRdFK~s8*`h \ No newline at end of file diff --git a/src/assets/ukui-loginopt-finger.svg b/src/assets/ukui-loginopt-finger.svg new file mode 100644 index 0000000..ed0d828 --- /dev/null +++ b/src/assets/ukui-loginopt-finger.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/ukui-loginopt-fingervein.svg b/src/assets/ukui-loginopt-fingervein.svg new file mode 100644 index 0000000..fda3187 --- /dev/null +++ b/src/assets/ukui-loginopt-fingervein.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/ukui-loginopt-iris.svg b/src/assets/ukui-loginopt-iris.svg new file mode 100644 index 0000000..64cbcf7 --- /dev/null +++ b/src/assets/ukui-loginopt-iris.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/ukui-loginopt-lose.svg b/src/assets/ukui-loginopt-lose.svg new file mode 100644 index 0000000..516a33a --- /dev/null +++ b/src/assets/ukui-loginopt-lose.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/ukui-loginopt-password.svg b/src/assets/ukui-loginopt-password.svg new file mode 100644 index 0000000..458d06f --- /dev/null +++ b/src/assets/ukui-loginopt-password.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/ukui-loginopt-qrcode.svg b/src/assets/ukui-loginopt-qrcode.svg new file mode 100644 index 0000000..0389ac3 --- /dev/null +++ b/src/assets/ukui-loginopt-qrcode.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/ukui-loginopt-smile.svg b/src/assets/ukui-loginopt-smile.svg new file mode 100644 index 0000000..6ff2371 --- /dev/null +++ b/src/assets/ukui-loginopt-smile.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/ukui-loginopt-voice.svg b/src/assets/ukui-loginopt-voice.svg new file mode 100644 index 0000000..176036a --- /dev/null +++ b/src/assets/ukui-loginopt-voice.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/ukui-qrcode-null.svg b/src/assets/ukui-qrcode-null.svg new file mode 100644 index 0000000..9bcffed --- /dev/null +++ b/src/assets/ukui-qrcode-null.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/unlock-button-hover.png b/src/assets/unlock-button-hover.png new file mode 100644 index 0000000000000000000000000000000000000000..39d20f1bca390ab3a7e5139f48872df736be5cdf GIT binary patch literal 548 zcmV+<0^9wGP)H%eIO>~v}Z#-d_hNn z8gLzm1c3Av(cYSR89@o40%St~+{&s~!UQS zkQ+8T9rr3b8T%`{26T6MGB9+ZaG0BSzM7UMJpbI&#%SWq8QezZ%w^T)6BQ&i)9ln_xvK#xZZ5p4>n1-uv`M7&%5 zw{Arbt)Y4MrTv@wn>|~|zMi^HsLJvCz^3!{yPviY)bkmx^Jp1p+B#UMsh+q&GHsXt z6RfFT&IG&f3^KgufqVLVgGzd&_kwykGTnmqB(@azG5|#kRO05@I({FEZ_iTmy@^Y&Ox^T0000XgAjxWT~`T_TS>7w~v5_ukcf$9tdW`?-7HyYF+c@=BKgodTADtH1(~ z(Q*g)1Z)CLV1Js;G|*+>A#gG1PE;M>KCoq4j2o^cf#*Ow1O%;ZYi}|Llm=SBEqU4y z5LYqnZK;irj#l^wBxM1+ZVRzTG-kchBqJmj|$6?RELXQG0AR> zer2a6f_?!5_%&6o8yk$ebMqvNrFq^ohrTrlP2ppqZ(k*l z(uE8Uo(<^kI(}u2fCf=PE~NhWBVUI0Z8xUNnH76oxUKFtdj0az#?$g k^8@)A*Z|fvVax*l06Hs|_{e@#xc~qF07*qoM6N<$g5tmQa{vGU literal 0 HcmV?d00001 diff --git a/src/assets/unlock-button.png b/src/assets/unlock-button.png new file mode 100644 index 0000000000000000000000000000000000000000..2a6d08ff2531a15753883b9834ef5cb06e7451b7 GIT binary patch literal 538 zcmV+#0_FXQP)dPJObRPnW$>O0kt8XiY}D@VVI2R|!N8@x$jo zI-f8^4FT;Y1$mIWl?Dfgr`<~T(%Rm82@%x<^rpuU(Q5(i0GmC8NcN7m>*n>+itKmW z2Q6Mzf7(LYdh7a4Rj%I`HqOYCLwfzZP{ z20YX68&~2H?{&@cW%>*H1>8~Krx+lq6@YmCH}MqO>vV%$)9!u?7zc&|KagL6_rN>t cFlGUN02!8;c24BEH2?qr07*qoM6N<$f*O|WZvX%Q literal 0 HcmV?d00001 diff --git a/src/assets/waiting.png b/src/assets/waiting.png new file mode 100644 index 0000000000000000000000000000000000000000..2136f8b4b1501094649c201498e3705d13444590 GIT binary patch literal 417 zcmV;S0bc%zP)Ofe&EDG6%1YG0gS4N-sbSvH}{uNNUCucz%Fuz2QPhy=2XppKXwn zOY#wO*%-dbyB+Z};Fr;1D%ZtkX&DXxyc&D#?|js4ebWJeru`Kzi%om-v!l;@6wDUs z?k0>5&^YIZ+a6MZp6@duDalo;ZuPz%sxE~Lgf#^%LlkYsjwpQL=e_BCdc=8k*M42J zC3vaoqTHBsIb~flyfwJ6TWf%)z9jq)7S){}8LQl5QJa00000 LNkvXXu0mjffrYpk literal 0 HcmV?d00001 diff --git a/src/assets/weather/0.png b/src/assets/weather/0.png new file mode 100644 index 0000000000000000000000000000000000000000..adf75615d90d91c569b7cdab36d662651c393fd2 GIT binary patch literal 16703 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 P00000NkvXXu0mjfc&2)k literal 0 HcmV?d00001 diff --git a/src/assets/weather/1.png b/src/assets/weather/1.png new file mode 100644 index 0000000000000000000000000000000000000000..edf4f22cb989d4d2bdfccef4fab72e35fffee2dc GIT binary patch literal 17559 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 RfuSl$<*BA(wY+)o{{Vhew+H|L literal 0 HcmV?d00001 diff --git a/src/assets/weather/15.png b/src/assets/weather/15.png new file mode 100644 index 0000000000000000000000000000000000000000..3d440787a1b857ac1bd5588aa6717ceb9eb41b28 GIT binary patch literal 15916 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 literal 0 HcmV?d00001 diff --git a/src/assets/weather/17.png b/src/assets/weather/17.png new file mode 100644 index 0000000000000000000000000000000000000000..cf5e92f6b576e185db03e05a2de4111315cbf057 GIT binary patch literal 19757 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 literal 0 HcmV?d00001 diff --git a/src/assets/weather/18.png b/src/assets/weather/18.png new file mode 100644 index 0000000000000000000000000000000000000000..2caad277bf1bb5777e723904246a5d95ef9fa817 GIT binary patch literal 12940 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 T8XWLnCXkwvwqlKfdC30&@DAsV literal 0 HcmV?d00001 diff --git a/src/assets/weather/19.png b/src/assets/weather/19.png new file mode 100644 index 0000000000000000000000000000000000000000..b8284494c6bd7c890ba3b28b79cc6743bf029713 GIT binary patch literal 18130 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 literal 0 HcmV?d00001 diff --git a/src/assets/weather/2.png b/src/assets/weather/2.png new file mode 100644 index 0000000000000000000000000000000000000000..2caad277bf1bb5777e723904246a5d95ef9fa817 GIT binary patch literal 12940 zcma)DWmHsMxIQ$}r6S#+(hS{5H;95Goihj`-Q6PHT|<9>N(lm@lt?om(%mA`4R?Eg z-ye6c1G88Q);VYIH=cNj*40)aCZHjJAc$B^RZ$;&2L1cO#|7UzWi0w3hzU|tlsE9o z+4V;z(Dk~nr@OnkKg$noCN;;sAN+ufN?sqsM7~*Q3G+!ITdQlOtopA27G|DXo=o z4RmYEniQXq#v;SB8H_34{Igy8-+FT~odu)tDkx zue?a*;$V0+?xg#ipUiF7`1rW&yc=4}6Qdj@b=nj%9|bX8nqDm5|8n-BSS9{ug9%^c zZj93Ep|?@m-K`deJIn>hEL-Y`@799+T2D7yc!jknAu^>zq7bNkuk+MD%z~@<9y2~> zF}&K+!{eU*k93bev*)CNM=F6w-Vky9Rao6IPk_}{EH-2*^{4H0{`T+46VhuPlS$Fz zlM|Noew>z?CurCpOe=ACL+??lF896g6|sMJBVv6RH6BQY{a1>#W~bOi-pK7D)ct42)})=cob2fU-7-US=`{! z-KCb-R1p@sRTF}>Z6`Dyzkls##{2)a3UsZIT(wLLsCXQdao!ux$oSL!8gIh>xQ&&S zRo-ERc&1T6P%vb7rYVM)RwPQH5HgvZ$snmm<*DMxQz_wh@;ejMP5t+M&E*?RYpbQ^ zum1u>CK1OPPcL~g^CB(v z12S4S$`KERS4Z#@B`0EFLHY#?!`y zPcIS6W_>b%Mj*weIgc5$tKW9Am%qEZ!tw`giP10t35mCTeQNSV8v~r%sGB>G@xlH3 z_XVV-iH8r9^f2tCOjHUIt3z-gMAp6Y_S)u}>=ZTg==?p&IM*U$-dlS&w=2MV37MJ$9tZ04pPf~PVr(3ena=1H&~8q6#(0`?}sz#z?`YHnZTL~0|%hd9xWv9K_Za6Rd5XxL^8Nhd#-HBl!&NJjAM7Fo6jzxj#C zg8n2sWg5vIENy)M9vVKpN=!^_n>2PEQ*=^rc9UDP8g$M@L8I=H^h`3Q8tkHqw${!u~EHA>l`OHAROtbwuKZch9$P znD_488=0B;eCogFyRgu{#^ZGUa4#}uWa04`J4OfN)mKKpon5EEOX4&eoO8cW^<*g<9CwH-e4IyZs7wZyKW|(HM;t^1WM$*0re3HGdzgqGN$F(g z<^3e;6nlf#620zwxp48*Y>^nkbX?OI+N<-}o4@{Q%xm`|?XI=p13lh!r9xd*>T$=6 zUbR$ZOa)K`t?;`^i8|#tlI2CR!);!GP3Y|Gq-JD{w0V)B&rupgA}D`kGI_kpbSR(2 zmfXjb+}Ghu2Ts(|(oz9@y!E2!|884n3G!N$$ZEsz%f&?GUi;-A(!y>(tn>ul^-gxk zsy~fq64Ya!{xMB-%FfSfN`1_duVKboz3Aazh$; z*4ea3VILB6C#bxv%+5D3@HXxj$t!0~nM8wBG1kxF0b5e;Qm23V>Ly`4dY7Q&ZSL%7 z&^w}nl=ZyaZHi>P`5XBqNCP`0 z1_z>-6B874ahJ&-#>5XJYjK-%! zA6*JDbjDmoo?8O03msltTU%7|OMUeUCA}~ZYqwy}F0Z6dXAh^3C(UjhjYCGiD&cgr zxbM`AxSJ3#qf+!oQg{VwvL|Z;$`)p6IU6)KChnjB6u7y$5kZ!19H^R2&xX<@=+i3( zXy+y;=z~lGon29nt%xhhxs(EIYLS!w&P%g}CZ~n3k84{G$aoL@zI9`Q`jI@mA&S1k z9OfXBy*ys=)KR~mG-(>eDz87 zR*xDVHJax5kvl}jW6kuOWK)e=C)7vA1SA#n0a;HndJK-Q>}#yt??WKlTS}4hv$M0m zVnydSuQyd>eW-bYo4J@ijdaQdr=?X`*G&~#eh@?Lw7|5Ug4#e-yjf6qSKN_JoSP^c z+bszX#h?9(@)2MDa3?>}S-VbjA(ViinPB^GUAOjGWSpC>KNqgWjEGA!9bGSZv#~Zj zt>pCh80Ye2_i~)4Y4#&IkL+Sm7lIdst9s8#Q%mc*_q1)#-;9Bter{PTZeZgO&YMo@ zr&&{ZUBCZn8y`~xTKsZ8@-8B<(UDUq*ey<|o%YbX`7yoPisZXTn>iFcKc$8V``27f zB4w{EmVSM(I$dNz1{2~y#6fM`h9#<1hyZH#zh>b*_s?@IB;=s6$^EP z9A}ucI%IkJLoBE(G@pN!TacVWypBW<{7`?CH>{RZ{Xn(f4$|lR2+#=2iKk+-F~emy zU%B9jbr;FJQ8PEI`F=?>CqX}PLT&w~>&d<-!q)KBTIrN}s zW9eaTc9Qs5gt22P+1AP-tOF&&vgX&cb1t0=yZ_#pFu_WdvzI!hlrb8Fp_!f4TgO=h8g;Mn@+ez>2}qQ9WOLcp@(_&;~B-a2}D$)P??VEyN^OMO#JxtPHZgrirPMhn$+vY=m?4e4=qL3kM zuT&@9F_>OfO_;V_{`q_efJ-)Mhmx;gW!xbdOu}qi35(2s`)U3~x_!gN9>BI&4kEAO z-o5*7^4a4~|3`>PZy$R9{(TVW@hc#YGjem4a+Eq<58-#ISQ67ftpFj#_8N4R4%lF$ z8WV49*YEmr(_RvB!oh-@Nn}w}?<22wp~~#_wjV|j-<6A*vj$5+gE?!DZ*FeZojgYN ze~ddmJ(X>C-}xE&4{QDX_3PIIA1PCnmZ|f9K;)u}Q)WNkc=FmMFz}1hOylU}WaO)w zX60Xv0CFYvujy7lcmDlLfKJ+n3XMkp^_R8aE-^1ry7jt8QsNnI5oOF9dAu`eb+|qV zn&cakCY}mZa;PUSRB$VygfsCfh@I>W^mp~Hx@O62xNY5?N%xYy%WtRGT=btv=L;AQ*1BsrRPk7z~O30@n{&%6t$;oX~4)?+pHpncQ3crFHB8UWBU zva>^1#BQ(t1x@~Zo9`*wfF~#Xg;(1@h$`v%=W2#D;BV;d6a1z71URmsyJ~A`-9f|l zXI+Pv`Z)7J>~HSwD#_&QQ0ETX{v%M__m;%8EmypIUEHS}MD(gA!0sj+)H2}%9>Xsw zN!ZE%=6J%;3iR7ICZ3sX8|&u6W35?-b2l17SV$&aj2hbq-M5DmLxMlarU8sOnSNF4 zoh{;cAgBIXHymnqLWQDzJexXKN z)8PKy8yC7B9>hF#6|0*VB{!+EmjU3r#364ZJa1!*Qzwr~{|DoO*Geqoe7pap+J6+- zwRY6y7*QgxXV3nCQ%=<6paiJKA3&s1e=t9I?e|?5F!SmuriP^giu<~ zoq38$?pByS5*y#BpfnYLD83X-InW&Yn8d}$U)l}jOu6nQ*HO6O#E- zhfpe%31Rgb;05g*v0yw#Ke9ca$pN$jm%_NcFcC2^duMxe^c{y^@0m064eJ8DSkFCE_bsS71VWUHKREa^8Wyj)JOD_6 zfFbb88L$sbxgr6F8Eui&0*U?K`PxLg9_^Imb>~+~({F^eLd&n8;0<#3L`GS$j`0&c zopfjH==lt=;0$z9a90+R2wF7uhcM($j*m_D6XX3>H?Xjv$3+7 z86$|oeL0C-OBH zag4im>YS7X>hC3{7jG+0-D#rV49ahOw=RFW{vrAC2)q|TL`r%YNy>P$>3=$RI=67y zw(yyh{wjD9D+p9wi~f&nkoUo=+6w^RTN5ckg!6+?D!m0mck)#iafkWV2cJE5zy1CDX4(HR!G)%C z^Yf4Kdfzku+@h36LE{G#GBzQsAxvc1qE2a~Qhz(_Fr=^xGM`7f;sjv;I!49aPf=G_ zHv-d2QD?WVd-31PCK4+vD@VQNRJQymAz7l%#z8tjXlP=UXJ};ej-OO4fKFUcZLMlK zj$rAgS|;Q6TTrPm0HF&G^B>Tjpn*Q6&IBh3T2+fm6<8YN@d|9T}a8V1@Sr%Q9rKPuYM z(IFUCiV-Xkdb?g3=&z6^;W{iG33kE4(h{>o|DhU>hLr*Q4&)BkHb?s15#{4`VrW(aj&|C)K0EiI+POwSg&>_gyf04OFF2m}{~ ze3?jPyB7TOxqt4*)Un_GqFPS^X9Yqmir~V*x zIKsH3jz*rCVJ;rN8t%p$S-yPd(kwNLMi)PbAkQp)h@i`D9C=y-J65Id?pFyX=WZ$q0;$vgA#N$}Q$Pz86 zLvr~3jOdj}s)bGSx5WFNQMDDHfdGAIHZ0l#uPB6>bW*p6Juay=iZXs-;qbw1QO;;CWKxYi4%DC4lPs;{ z`e;I;iX&|@S6YW+!d`efk!@?S4kw7C#2AR#l!hkm6DnXux|zfaX1*&pT7Rw4R_=l# znds5yERQX8MLknpR?DptNfK&KZd{ry3s#)lWPZy6n5>AFoNejzs-;8~hOb>+*n4M< zm(P$JM61WE3SU*8RtaDr;VSA6Q7J48FN7f@bD54QNDhhu{cpjwlUVes^X%+w-A{(k zDwTU2C(qBQ1T#n!{>&daL~Qm`DOqS%Xs`5-;TVMcvLz$aB&R1b)0l2^tbg;h@JZx* zt6mcISz}vMgUAHQ|BmPyb^%1}SC=%>R3czL@@WI<`vVDyKz!?E#ykMzoErun8{Rg+ zt8ezae$$J^6$8);8q*}_-(tCbST+ktjuoA-R#qqiO z)s;D@@;vD90LktIn%vX;3c(~S^A~oHX%ZK77Cx&<_ZKEuP^&YCd8`j)h6C>L6oJ9i z^#YQ^BfGg?xV^p~M<;$z2J{;Xk!+=AV-Zm1`9vLlF=><}EmHK+YBlEjetRsAFd(V; zQE!n{`S26Zp22I*?NVtjNNM5xdl=sfzVv=E_iZZeJgLk9rb;;-+WqoN)_sq^9@QwV zPcLlH^DOuw_(6XA-S_iP{R5v*x=RvuN>~cLr-0m`r45Jg!-qIJR5h}pZ&o3|BQP>8S4HoY z)3Vv^#pJJ#HiV!|3*yXaS@w02?;qw~J;>9@;5e6x5woxUL~(h#7l^6xpp!+WB_hNf zS{f8xxLN=lC0vL6FfuZ-yT;@*TlL{nR-3t2Lm6Dd2W28@G7}>;G(XpJ(ObIx#=F9; zGPxPwMH7Cy)2FJl-+6$gnpQvhT}jL2X~Q@5`jEDbhet(#?C}#dbVia+?g3|IIMU)% zIWp^Ui9=CCx4#Ny0S5l<@8O%@3$16%F}JV${ATO8`#}$*GBour;ie)8p_xR^{wJ8+ zYC1vnH=-msNHVfAVw0_5j_ylpyIL0b?o zF^P%f0R6D$4|Cq(YTZQ!ZK5#B{VP|Wrl8$Tw!9hINc(<$_T0O!*=fgRVtq-PU=)7m zy-vQ&^=(b_dKRF|j&m*U(?hwEHif!m3Q>16hXbp=8`f$(z25e27^pY$o|b;(E?Fx9 zx6f4#byiNiD1B%@O%cZe?oL^zo%;<~cEFhfI>dXM{(As_uxS=Ix*gz@2joHpf(z~@ zdbN{Jn8Vtg7TV8y83V6O>mve*nX%i}l`*&e@ixFD1NYZOzj$YpeFGB8x=p$Xhp&$h zHiYuNwC~kl8+V)>L$y*0jZqJ<8@OLQf9b)w{rn3IFZrhIp?$CO^!okrPInXFKuKL} z76Uc16Q~usyy-KgscN05bAn1nxq&io;z@U#x+yBaJbdyv?dTq$7bh0uKC)I#wZo=HPoAg2R1cO{7D z@8$-}^ea@n$hM6PErvI z5~uc}@hu)zwZw0L69c8CJ5$K+&6G_#yW>mb7e$*-oL0Lm+s8hum+gSvT}-y0H1sn1 z{{G-Z$gKI$=5DnV#CP)>A5e{dhDpkxt&P;(M?cmC!^kQS29lYP$O)2Nj#R};yQ=wN z`O!gU<3U$LLWo6%q3lNWaP=Ag;oXkbF1$Z5Vog-MjdLnp0le$L`zCSuTDrzVAz+$HH zCmPKvl&LAW`t%vU6TjAw+sDCHp=NqR>Bag{g(h;jveoE`tWcP`5LV@r@9gbQU+Di} zGh;FxN~Hs~1P~DTiLh$hFDj(~Dh1p&JVnq_0B`gT^dSge3k_R1-S0QMIT*3c$zkkDeO+_T}_yqV~s=Y(ztFVEbOH$)5Ef{s~!4i1`hvH^5Jqf7@b0nMDo zD@L!Gm5wZ6;ianUIj>?s5QWOWSpb}2+1snFNRa6jKmt7I<9ho@AxhKXa0d~4N(<3s zl{V{9;cP+>RyA>M6F@*(Xd=fMk@MNlkFLkfhpt{Xnzlt?F%M#_l0V6-DXFyFKB{|m z{PLWc<#$DLJ+2RAQz|$-X7%|6EycuQ%0siZ$}C2>8z-@Vk)S2P0!foh^d? zj}*uGJ$}rJGc2*GJ&UbYpRB8LyXd(XPS_$)gLtUax#$OT)Jmu5Ks)q3k{__Tj*ZJ_ zsSGoR2z&bi^ipVm#|;0G-v>jl(oed{v{_KKD_yq7Rl;@>HT+rpS!tP?$s+f|AAJH zD{N#mD_!7y%HR`udL7>GW3}ZCi~#{?e8dHsX}yI()X)GEe&xVV^;C;TLZ34u>*N0P z_{o$^-QhyYsnQC%sJn`wYqY9_zQ_`1%msB^M%FyF!Y9IJ$sSY(QaCc}fPT-bmjn%S z{U{u<@kHFxOWKy-?t(68>dX^I?iv1QN7Ct+cWIeH_6-t@c8RL>2|xARtH(2QC0di+ z4htFG8SF5>JkNF7pr5Vgoih+~GNdWqYW2~fL5%&_I6X)t3I;A0e*ec21gt2QR&*MW zVFq!x%@J~DMN@UPm)}(oQ#bfLr5A|ckXM)!e|$IJKO>riXo3a+88UMe$yTU0ea-gJ zB>E|*Z@Ea;?K=5a7*hM`ma(P5-~CysEGJ!b?dJ(S8*TXI*mm~w08XB=w0seVHI879 zE8z94yNH9;e1`{_r=s!EZj^Tmxv@SvMs)m3%FSA4qy_`($jZ$0F43>>0y&t;^%k|^ z4J_I*lxTIL{YGVO*80}rr)C0Zn@M;#^GBf`es1xmaTyHur|IvdK}40~nxnQWhh)Rc zYaO1@>`*aYCgM&qJt_-7KR;wL&jd+5 z&v4MBV0wItS^kEyuilxzqA?N}7@A~poHsD~jFAymE!qa|SBt$$nUzv*h>6`hM>z4$qDOaDZm#q}a{iU|?Zxqq=d-E_5O>r*JN|*+_}`&iCrGqj z<^1rnB*TN!1N0ZC}WhnhY2=7CcROmXzt*(cD<_0ce9#aE&{Nt*MC zkKA2P3@(Qq#SJ6<{u@Y-sbE^vueHY({-Zs-Xj3uPH+;BoHPzalGp4Bpe^Du-i@_b% zF*|F_PI3uHU@W#$9#`Iz?ZQkTSNO+-XP1{P&jDqd2ZawnN%PqIb97RvTYi}y(|G}Y zp~@n3_AhNqwe#HGZtLZax0F=8xRD&ZDj#@;(X(fmKNoGF?8;o|p8$G1RYc(#m>uz1 z+$zg&f85>>P5AeQ={4qeS0YqO4H?VnW^sIg-Sx#W?dwlV5q(orQyr)N()gH5m+kf3 z8h@MoD@3xeLNt8gRD9Kk;-M4!LhdLZdQMZ2q$cZaWk{R=dg+*rU7q0(~hz6c41`pSBv)8{?700}mQON4ev$Z{|o7kD~*}te5+q;yZ ziH-Pg!v3$nnWXcwqE|1yr(fZeL!NT~i5VGV5FgQ~n~5kYg#!-pV_0J#@JQ&y-hcX( zvGCra6Bi9bX1jDv2N|?1etv_h@V&`0nut4iCTjmtvFUDy{YEL0{ts$Dx~85jz5XLw zu0&g*WI{!{w*ikYK1qT$HllpOJ~2iAH;`U#SK?&@e2w!Z;2et~62Ta09JHI|#W*2v z_U79>rEjkeBmZgF|7#T%1F@lI2ehXs7NeFL!u30ZU)L$eUox#+%tD*G?V)9dgm^mn=IK`m^)$P%~WDF-mg114;E+HbG-0tq$rq-ZMhJ?&r8 zaFmQMyKWy9gFOZ_cllT6!-4I4BZW`Oz!uq1D}z}GY6X1xnWSz$LAkoAlzqw9)~!MM zU;wF@OPwQQY+T^rlMf!%L{3n;!!dH zq6$;SK3$*8{dGXe!NSE5ZVwcPgv&D;R9+v$p0)CP$s1 zBapKXi`;~g%p0t>;6q;RnXo~c17_xhno=|wPwf6|pdP(UO|@4ezo+0G?jJXpA4d^U z;o;%B_!d{IoU)mhL!RxfkKx(QIZxg89HOw~F!77HT)wg~YS0um&>B2Oc3~EPf{_XA z+*S`@LF8k6-)#A_dU&2=^TLBSy^gqZLiB4IS`oic76T%c{_i9Y_=2febuJR_o{VymB8HvGXh&8ygc9 zbBzTaSDAU|U;nndt=;ezLU>=(K^sT#PpPUV_sNRZ@J8N;nrh>76W^tudaIO;*rl;& z#Z{q>0A`E+6JNx~51;@YM9Lf$O1JOf=}~P7u$Cb7W)h;KG#K5P?C#l7c#j^bi__>- z(a7PvppJ0VQ@(MSC`27d4<`?NP5F?*nTh_5L(=~vZbp$Wkm7?kQ5abdT1VF8%lUft z7eOJR!&U^l$Cv@{;F$>(1o3cu&MJ*AKNit2N8-MHo^DN!7hA+7_yNTZt3M$}sd3;l z{|CH~;p6q@J zpZ``|cX#)FMJeX{r*&1xtosiuW;c?@PVfq%nST)IcXm3wK2NIp z4p5OUjVEI*kvby0Y1M=8^dC@ci)_M&!Dsm(O~gHT3mY3|HUeH2~O zxkEP(V#$V%aFoISXmsmonYMSMqhw3XD~Q4_LsJ*z&xdugyp8BzckVU7wPu=}%+_Me zu9ia=t^kd_%eNSolH_uJ55quMtEzi?u~jmtBgsdHmEO6C3r^uw+@SKFGH^ql_24ed zgN?-6k2s9%Cm2?!`2yU-1vnR#Ikhs?9`p#YkgLhTs)=?W#I9h)G)L|~#9#`zu0-*y zgYiD&$<(3-1zd$FF4JTnhl9C`;1#iu6MF6JHv}&)|8yQo{hMEGGmQ@;^J##0%U+{p zub1njeR{`kF*4E#SN7GP%uW*tWcOxc;5<>r?=L$Rt5OVgepKE+8=+a zLoea=chS{?dZ_1mtthp(x6bz-nce9kW}9+5EfiE@{PkTaa*FFMcYQ{F2E;AUJV^BZ z_F-j4Y;9~$Z*T1bO^j4%i**|BfYTmh*{*+$I>+goqv`eg@ zhZ_{m$wu#^1U^D^%weoA#ebgwWzF-?KcgAA4;-6H zBVh0lj_Scih$I=~GAPB6#wufgxjIhmoQ!!e64Y*&=e_2|lqWDBeJosg=Zgv(Wc6T|{He?vAb_SWn2Jj0NUnf!5l zXpASvo*%&rES}@6d(xEqWU0>=D4wWxj~$QWn&209uj$>i#gr(~80eTrPGEL)Qb6`m z>2JEfR9q2qoGaFD3>M~$aXiLkPs!xK5P>w*)rL^f&&yt#A1+wGTFJ-M*yCnmiSXSQ zLf$7L!6fEMNW8;pn^`1GTvPc9?y5qeL!m*)Z(JX$f&&F}JtW=jM0~uhf1A#+h@kfW znn{`?3!%uXjhCW{#w}8s<~t6^!yjDYwX&v9p^X|2jg|8_bslBidop(2b=8=t@w={k zOnra_$=*YWk58Se*-;5Gg(2*-)_Wv9b?5j94uc5^p@MFz`_){TM4bsVI#9tV2JwAL zo}i&hD)Y<%BTR@?+nIK*6e9r>Iw4{q;Z9O*zpBLOpj5!lDrKjUQ?E!C;w~A7f2jW{ zrh`v@hgspF4S=|$9Wh1Ull3pu;ipO^hih2>DXN)+1`yLt>G0lI5rq}>3hI*q4xP)< z^Ae2rY!&uX^ulsOQq-V?lhkA#1B9_CBl_1+*2%SB8D>gyF^ipX*TlmhlVGlH|_| T8XWLnCXkwvwqlKfdC30&@DAsV literal 0 HcmV?d00001 diff --git a/src/assets/weather/20.png b/src/assets/weather/20.png new file mode 100644 index 0000000000000000000000000000000000000000..850d97208b6c1038263ed383b604b934a583804c GIT binary patch literal 15156 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 literal 0 HcmV?d00001 diff --git a/src/assets/weather/29.png b/src/assets/weather/29.png new file mode 100644 index 0000000000000000000000000000000000000000..4cbe3b9bb048f6268840a531593d9c3414e0c800 GIT binary patch literal 14927 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 JPDHLkV1n%NB$)sJ literal 0 HcmV?d00001 diff --git a/src/assets/weather/30.png b/src/assets/weather/30.png new file mode 100644 index 0000000000000000000000000000000000000000..c1eb46a35f49681cc5a4919c15e59792cb056a52 GIT binary patch literal 20621 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 literal 0 HcmV?d00001 diff --git a/src/assets/weather/33.png b/src/assets/weather/33.png new file mode 100644 index 0000000000000000000000000000000000000000..dbfbf608885c01c368849520bdbbc92bfeea4e6e GIT binary patch literal 14324 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 O4^UCmd|CCvGVFgyWA-Ni literal 0 HcmV?d00001 diff --git a/src/assets/weather/35.png b/src/assets/weather/35.png new file mode 100644 index 0000000000000000000000000000000000000000..b86b58b2cb202e494c0020394f6f65df975770dd GIT binary patch literal 14008 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 G=KleuiC^>p literal 0 HcmV?d00001 diff --git a/src/assets/weather/36.png b/src/assets/weather/36.png new file mode 100644 index 0000000000000000000000000000000000000000..6e68f013c2ef8956bfac8269619fa0bb779b1181 GIT binary patch literal 19448 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 P00000NkvXXu0mjfrT|pK literal 0 HcmV?d00001 diff --git a/src/assets/weather/38.png b/src/assets/weather/38.png new file mode 100644 index 0000000000000000000000000000000000000000..30affeb2d16030bb77b2848b82513678acfab4ab GIT binary patch literal 19908 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 Hu0mjfyC|>% literal 0 HcmV?d00001 diff --git a/src/assets/weather/39.png b/src/assets/weather/39.png new file mode 100644 index 0000000000000000000000000000000000000000..9fbc83b708461262e4b6eb562f70520912d4cbd3 GIT binary patch literal 20592 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 Bf*=3@ literal 0 HcmV?d00001 diff --git a/src/assets/weather/4.png b/src/assets/weather/4.png new file mode 100644 index 0000000000000000000000000000000000000000..1ec73d2001fbdaadf026f5f0de1bf843c874f2f0 GIT binary patch literal 18118 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 literal 0 HcmV?d00001 diff --git a/src/assets/weather/41.png b/src/assets/weather/41.png new file mode 100644 index 0000000000000000000000000000000000000000..c8b27b38ef97231f45aa4799e94612db04d1abcf GIT binary patch literal 14595 zcmZu&by$;c)ZXY6q!onGj7Ctx(dp=c!~_(j8v$Xoq=1wXBHcfvyF(C1gCd~Pj1Z9= zO2hZ~{qz0v?RvKn7rf7&bKZ0AbKmzHW1y!=O>vU~0)bH9*HS})_t1Y&G7|9Gr(it{ zfxsa5)s&6>vbKXTHMdMN7I(8IXXh6$b8?#O<>k#O>ui3%rhaQH&Z!i|Yd~xr^-d?2 zyl4PbCf={Zm^$+HtIg#=60(>s*7(C$1EVA&3mYmzb{JK7JT(h5$@er{=lZ+abqoGS zI|1!ytBE_+i@u4c_x0<9ht*2oZ!P#fTI`y?aP8hb3fhqJaGtx_{HfCO|9?9Vk@#31 zL{R<3Y}{wd^jz32Y=5f?LRUWXAM~Rbo<$;flmR!R3)M19r%!2aX)#N{`NQuzoWfDF^q({CRJEzoYdTb85drEoLKP=?oAWGB(~<2kp?EiC3*8yXs%9UV7) zvi0n%=H2w8tz|xMddH%-kPPZ3EK#&7N^s)JJ1a6fgbpdgYYDU~fyF3UQ(>k=Dr#zZ zNjW+H)`r8evKll8h5pOa%F9GZEm==*h+%%QfNro&^G#Z5lT%pDO+I=@Wa`cGK+T&Zv7Z zz8Z4)4P&Oouzcsbpp`QPY|`{udnA@U;J53z{Lvigc=o*`nxMr+m)W~6vr)F@v?>mj zK14hO(`ZVF3KAYcOUu<7Sb67ov23Yv8m$d)OnIH65o!Gvu2nq~j+4>k9|jom{VhQ7!=Ih@qr^*`M#4LMsKe8aKxyxj{4mq{QTuH%AuGN^MU zJSA89)wY3$u5_DEetlHObeq@{h_H1 ze_EWvr1GCtHGen^W_vu9Ci3udW1%yk`|s&Y_oeqj>!J(J&J^LV9Ob%(WF^hU~0?rIxtd?(})QDCuMqN)u5=W{)6L270bvATzHNQ^pLmi6P zx$6)0N(mQ_Kuo>c=c9JdUgB7gWoBg8v7W-=FoStFW@{GF#8Sy#r?66a8NwW)L@DF`MmwVnJtIr*&jzihyL;ute$LGlkP=LTB1(9 zQv8&E=Vfm9alaV*?dmN%7U@C)vLcg%mKWhA5(=>eRAjHGCN+?RJjmWFW~}FPsvpq{zU`J36%~1(9e%ns z3l#`_QF*CFX6;z{*{%8BI08SiaW8u;uH#en=!eUos(jrxqM@B{-&s14H*_y5_un;ALoL(ptI84EvI@?jSFgLG-sG*(9(UmJc z)d9D_YWY%78B4MTl9f3TPbf0}l8P@8Yqi+jRSMg{=HVuUmdX6b$+_jMS}mBWwqjvq z-ldb*tyzX(WGahC&H;aapX{xVW)73mu%AQexbsr>qvA~^iOe9q+q2E%K?iSg&)=bb zjagOCFsleuHfCjKPt7*FGoz4){+$+5dLdrrL>}Y@h25ipiqXU!JZxl~x1Yo$U_;WQ zAxPHN4O*3+qoCuiM2*6HF^iT^DPgsX;76wA4}{3Vb}yzLT}-(@>hSZtE-k~;foAU^ zpkvHP^m&NJ7IaPjcy4JrhuO&XpKWsGqEbCPyMQX$Q^<`|PQ;gbJf=vC z_$b1p70LqsyT_w6K|fU&e|gLGsk{`A|4h5WVU=&^)(gE-oM3T*Kx_p_ z-K~qWBg)}Awd!W{!{y(%I!n9p5l_%UG{hLB686{8S^7tJJ(kb*j`7w?13gYiEvpCw z%?6tmdBd~^o2FSB@#0Z_qlwB(aNI5epy#*jf7UW_^@Z*!e(3i0^+`+K$Y&3GoOe1`p0JwjfH#GN~L4$Aa{ zei_2ou0gCB)Ta|}-8%$NqW+QJ5#MzAOkK>ULu)ouS0qAT;Wh%hZOt>(n`d437H`L} zSNU1MM^awflUYCm7snG8xBfgfXF?W#fe83mN57A`~rtP(S!!s}a4uAd_%r&GJK1QWD^;`LK%Ea9%nE@&p9b zT!-C|@Nthk{^(G#&fpFGQW>l3>ia>$##vX;79Lw$L$$TFL2ud%;!viw$tj-Q<{~5T zmb>LGmkl-ew)JOX`}t?5#yaQx8m@>uD-wUkPAh#vgHEgJM}nz#((Vh0^AXAMK-SQ> ztyiR0_lZ3qhYIJ*G*_Q@=G(6@&9mFim6d~o_vbvZx5BVKRYaXuVsV3ftoG~YC42Da z^nOJa7RZc#ugNuh(29%(XosCv*e_q`n3$Mq>gryb3u37qt#Zw!;Qz%O!&8eqK4PFK zw_e&#D;l)3(<6E${QLIvzOu5ifW~RDEQO%c!NV>q>4KlBCvKiPVu+t2Uyk*d6`7=GE+Cren_sKu&d z!xAMC`UMnu->Vgn6@n)9Vd?RtRm}usJf825J|_krDvphebTrRwMOw3csL!gl=F!1-x<0P5kSR|6WkYQb5;koH!Lgk*~V1HpZ-NZF#*$*YghJ=j8cwk3Gw_N2m#&VuN3;>*GG@o?>+zh;(2>#*vHwf3Ih*$MkTHo-Ib>tXWt$%QABQgvT3T2IqxoX zf{#Ja!;6tJXJ|F7g4iXvum5VGVP?=@9C-4uk_nU;USTmY`4SV~H}e zu=ogaAqC}ODUG|p3x;LAkZKJUJ%3V5R&31FE$cHncq_Qe&P4S)MW}GZ+f5-!lKjT$ zG+XjYAH?0eO}n5EDx%S7f2df!l}9IoQgP6DTn-ez7CQ*Q9-X7#eIKXeP-WkZ#mqjt0q zuzOMZ^yyPyP*FewZp7g2Kwko>NNA6sDY1SnC)IMc7N~f|{@|L(@X)h^IA*be$xSLV z9#h(9?QB`s_iz`atCdA5dkC7Ut%7;CLaRsFH?^vAogj8jL*-!!I7mfdX%&!tA%Amn*dPS&2D*SmK6>ME#;>5q%9t=QF*kK;+| zNFg_BRl6Swri$Ob9R?%2-Jpk5jkR8Z9x$<`Pe`;nm%6-^%<^_UY&QR+Mn@4r%Vmr- z_XV({GyyEeLl8?llf>9zK@4kaZg-xkI!a7TTx29t{<`&N)wOxP#C4X*yYOe&NXwYl zqx#ZZ{7xW88+>l%i>QSbTrw>3Kie6fWsKF|Gca47LCV>PS}}6bsImx-xic=6k(;NR0C#ftV>& z;BPs=Cs%F^>T*=7mZItXU%wjLVQhGtT>E@156^<6GIgbH#=oK(tS1`lkxh8BHc^Zs z?-5+d&CLZ9%#DG<-8Tleu!G`|&y~iF+^>{YY50B8($nh=o|sQVB8Ok_(?HoK4a`=` zL@k*u>Ho0SrHI{gKQJiDGd2-riYu7ij)G8}{oOwM3D%SgS>oI$I62uC%7buS1c$h| zxa@+#a3Q(ik*ut28MxT&M2%Cq)$|AoTeJvNCi57|K68P!&~Sa$X@42cb#!&n%hI{D+dc zy&5LtlGk^(zwN6tsH5S$fr~Q$9bY{zGd>mDejY$xPF=k9mq1Jt%0t&Y`J*%7=!_y# zW$@^t%XRke>Z%Q&0GxL$OE!G6RKIZEt*=g)Ax{0P(;b-v$q6ZCo@ElN=COm#lJeNcxj3&%xpJp7d1Z%VqEi@^&<)Ny%8D%lk4<@~LqkL`i zZYKMui(9S8yV&VFwZAUhi+hb~4a9frr!$$OcUC`Oz zW)6hko)Cnh1u5)NNdc>&xO`t)bg)aigAg9| zeMLQtf2KS|1Hr}R>ab%Zio`9X6wteu=dB%n>Mg$4qTy}cPg&H2xyJcC)>D2(!gPZI7SYq&OYrNDTng$2tLEhJ{o0}wfmGy6 zoJ={lM^%_)65C#~N|F!WimWa8fWn*VNBSxKPlVC6$Z7r_FfJ~F7SMDzbJR;~c6YiQ zGJ_<7^W+s?jrn%rabV5vlzrlLt`EmXM`P&u^_C);t$ILMqoSihu?2^OynK9XtE|gh z%k{NqCv@nyg^pdGU!p4(K`d^D64@8M5hIC}&Do%4$Q8&ka7-gowF)Ovs{QU`2|`51 zX&}rbS%X!jY_;>(=M25dRngBk7$8DYd`OLE4R@Wrag6IwelNwP%l&j5KeREPdv$Vt ze$H8xJ!8gy&rgIsw1?xa%Qx{3z=ruYp+!WB85+^LS+ZUv;0OI5KN1bC-3Dp6wDivVjbz$NQ8aM0|{WgqBGO z1ZHn%w{Y;ge*CggtE8x!mC7oPK&!~>^zZIsNIS~Oo()9jbYU3c~pdjZ#^?u#xcD}W-92Z5z(rO9v&F%6|nBj0ybnb3{Xlw#T%p0qlvYE|d#$oo|mnA*>BB4QQ5eZ)@|KK@_Vio zPp86@cyOmRgq`5!%tlzL-~Id-bML3+RF^3$8k7`Frp4qqeG^9@3$Z^mnI6F%edOQ8Q5;4K<)PwPgR(?H_lrZ+5tHUaliCP4y$ zK-`m(YOn=_CdPtT(r=VvNk-2?F!gyU^{d+HA2&HT#v>FZBwhf7W^;2mS;{o5qnEPz zT%+(sWMrhy&#bg!+-vm>II|dAa{IMD@nKklp3`BZ3a2(95DH0(5@`oq~pI83R1UIT#Wi3x+WMp{LsNZ1gcmP1E`E_=i; zatO)Foo&^bp-p>EwP{h3P&;bsC7LHp?elILY{~YJyy6u9Cr?)X_C?dg3B(FlM7^~rGjGsfi-{vqzno8ctqOFqffibX!(BPgE)qVhciL}EO@qTldr;4 zY4We)OgRy1w@$?KRWF|f^SKbbE+PZe;;0=x2<_XqZ+k{YsNPRG@$uxKUTgCU9R(>; z!i|0N7I8VTf@utG4ll}{w`W^EWKjRlSnx)WZof#C2SjU`=*CCz*oLrW3x{YUMs2YV zghFl2O@6%=v}Q?|Z4!x!ic%{x4%=#b`|jP)9Ft=;+{Nf;$zGv=N~hJhn^bGivv6Z( z0GMyx>OVgA(@5D{T-vvCj(2K&7a68k`er%8q?~vhZy=ZohCK;I#Y8}z{r*-<_^`=k zFePTrL$1sV=0+I)#%Pd`ZgkXMHY2{)4)c!W^VcliwQSX#4FD_ES)!f*wo5XU>vgI& z<2010+5{Rg{evJBRVG!Ea`Ty`si`2NvV=E?Jj7$oyleAxGc`gp$g{(qy+_ILrP4H@ zGY!h8L_O=h@zcF5#&6%7l(Vq14zK@f>n&B3Gd|!N_t;wk7|EyO@=rAfhYz!?d2TkL${vFj0w!nknMBuF8P63WbS2YH`)-#odnR&ByNf|kfQT*_ z_Czr8m_R(#wryUNB9cHbl|nETnKnlESl3nwdmV)YI3-$soRuQ%F=xWlUg-C_01VEF zA#|miSjjI(+QmE#K%*a?Y|EI1P~LYAA(eBvR+}Xr(gT!gSwhuQPH%H^#QClX*tT7v(5OvhY$-QbgEmoQw*xERyyI9--W-&^?&$b!Gu!6Qk|jkjNfL9UL5d z#djytMh~`zy}M6l&)X_g5(Lj)LrTfX#o{-{aWVWkJ%IX?HrQEf5RYV(BURuJeJF8} z%NIdAQA~@}{g2@azLmF#M~HDpKt8IFRi+3Q&4{F;ZpRi3+Yy*D5)`EXkwe?rm6Fq< zI7Q<}sm4;gXv!O=IXQeqPB;J7hJ{T0^78Ts#j)_Mg_Y6o3JM2^>lb%-d6zm#6L5JJ zqBKeO3kwTno~@DrouxM5Xou=MzUj`vHScR##6=Csf8z?s`kzS9EBNR@$ww_j)9bz8 z){&#w+w{A3?I(X1r`&}A1qU^>^&Y}CD&`7tW5cTOzCkpm+|GQ6oy|J_ae)}CDyD*v z5a|smnV6Ir*X_P3hK6rHb&h1id*RtecAx2gNCh;!arpzp$?6+WTU7xpi`CBJ1LVXF z?8Md|61(ef4!+zmEK!a9c@}K)r|3*;T7Nl-Rhtdlwz%tDoKhn^{K=`MgtlhAMUXfN z^~3tvOegrv@Y$wD*V!3qFTm2&Mt6^QN^bIO_?YZmie+* zf=N+-xGrJ?)9?xP%6sjzl#~TC4Gdyt%`#m+^=quTV@)K)dn4Po>pLD{Da^!}YIz?@ zR+JQ~V?C)`+`V~KBEG5dx{>zeb*a+U^|}*T{YAgyrSQq7kJ>a*Km=_HoP>Vq-YNkU zsMmOo{MBy}cg%3eLXCBwN15@LV$^*A%)nLyWIl4BD`Yj?`31(ANzp^8Wuv@mevs5H zdl3AF@B_kwjLL**kl09w*`DuS_O+a#zvdl(D&0pN&}}5oaNg8ell)8*S=ThWHFdce zeres%YJG|3(<_?X{Mn$uxw4vXisFCQp9*7+uhxVzgnDd@W#(LWU+&Q9fz!){f^_iM zUs3<3BT2^ib&XFN=DW+UvviD9kl!!Ly zbTU_A4-h{WGk_fYD?e`Z@BC*+yW>B)PTn-@9A=f$c2Erw4_Ow#|3jp7TO9@S5)Yis z8dK=4&|V<_T!Ymf0w{*%2thm*oh*%h?BXKqEKt0)dK6WW>0rfa!e4gam4`#=6NXmI zNW^lX1vT#8jOV3*wp?>v-r2`ty90q7EQ2ibc=imnGuy16j^yQx7)7pH4K)_xa zAAIfL_!83_+}itScKrfQ^NRrp3on2mITCMFspOnJYQ8#0{-R4Jf%H-= zVt^^tgDqMM76iEZH;_}VAT!1)L+BGo;cm#)S;!;g4X_KXo;VG82?@^7Da>)nA1@w# zqZXN;tyxJe6DObM6>nu~=c3!J2l3FSG)R{(j0$^ ze^b#d-#H0`(L#pA>1$lXoa3tO(eEP1Wx0BB<-%4JPVdaT^RAN%paj$DiIfnbVl@`Ns}|lw@-cIh&OLvqN!l@o%<) zp-t(Sv(m-29zgERZ+bVRphSg*v%3BES7do^Q+rUeC_!F=8r=kf*6uRP)c?dV;ngdL z*`~-F_|Z%~PY)3(eQAENH2k#S80&<+1uf!QRk6<22grJB<(jU;MC-mnS4YrhNETw{ zVfob1FWyAN0olTSg0Bta%{L8B6XrG&lk_6oEe*%FjMgkpvrT_M`X@$MfBWckp*TG_ zSTPFcQs>QPR0rZgmfU;~s8Cw`rmUY{1qhzwXf4MI@B(jJf z!sEyJaY_ix^aIjWQq?{Vz z_3}uSPFZbop-_O%kU<>@_W^@JA0;K_pt#MDg7N?LYUZ&Ehz~~@?4b-z$zrL z%g46wv)&SAFY~{#4YJH0=3e~&)_;hTmVOnsE9~5lgh*p?0Tpcvb9Qc z#DnHBl=hE*kSt@>1lk@yc05|hIwrFBY=7^WbPAs%a-8=)(^#h${c(7PEG`SakjFl zFj+Eb9{aPSe{P7`P45U=6{pHO*;)(nL-o%tTOD~3RbJ_1?f#o*2rAFjk#6l9FE^=m zpjDcN2X)CExGJCd^)tk-y;};`mysuE#bl2Jlwc;23QU}g;Pcj>Co?|%M*8wTt00ZL z2_A1nT*STFjvfA(MDWrZ&&13;E+Q=402u&VUp+IqPCbgJNN2kS39=avj|o#OUr&^9EGk#;gITM^5hEj0ajIkDLrW6GdW20 zjeCnz=;laTn+wj_DkSvh$KS!--5x6+V+zsE^MLRKny~&6nzCj;v)8?5b4Pa>-?-mO z2YCO{67Rm2yq_$A3*DRMElOgg3>H5zi@2H8QZk$G&hQw_BdtM9cBI^^&0wPp*=%9p zq=}`B3_}DS@0I0Voo^_r-@es>jT9>`QZJakMWE*2OQ3W-{OIyvU$2z=3&%9jzl^Mc zWrZX~ur8(Rk#vG-dniHo+!YEXA(w+S^hi=qYD_>1!q!ZC0Hd-o^KOHBnxGD&kwkj) zq$Fi1$KX={X&K%WSvL9gVIYom@z{~e|8CEk{(9d2j@?_wdBKBrKuqdE!S4%ntP-QD=zkqDgGPpU4ZM{sia?IWstGDjaxK9Y_(wE-dKkNW(aqrQE7P1Vu&1(A;FE;f< zxFS3nrbmtUF1#N$pKVak625GhPNr2MQTDhzTfDkl+y;Et#i*U+Ge9&uJ`PNEy=nJc zsds}tJYv*&{V<1v&`j4?hE|F(v_6c73Pmw?{q2qL4D+`td0rC4SYe4)PnTzc+x82j zRSf55=hD*$;zc!^_FPD&6((zzPhfM?IVR9&Dr81`rj?M`&NdY?edlcoP9*5g^1idK z&7F?8Pw{KwRNTcWUk3-i&U#u6x(A`aAY9 z#Q?%&K?46*g1Yw{v8#}L-JtB-(ZwrLzyg2&L#mu-R81R@Q`!X>h^vdEuA|OfEqL*C zTYS;s7U9p5T|$d~{|`kFx=`*2k0b#Vi3Hr<@jfmN40@f>OgaQGoXj-jBU)nORXaxI z5#r`ryvG^q^Uf%d(aT?izIup=&a&x2uKIg@Va`Pg^dR21Q1qM+4p}c54!RTT_YL^ZqI{HDOSm#tUy4@znp^2qeA^-3FcH@?#!Wp#>T^-|WV{KMODoB47r^k(xn z1dS4QsT#8}n0WuAUh}LvU_)t%Kx}|wN8;3Y(`ELbK+LITAqp2svp)8GXL&Tw_Lh4$3;dGv&s7drGXldh#(#)K~V+@E^yUI=AMjP0kr@1 zv$1*WpEr9!eCOr<7F6Qjt`|Wg6sOeBff~2H9sM6QPL>#rG_ualGq~mf?-w z(mN!By|SP3T>7%yHLJ6frIdw9)8?KU8b+5^Slkd`UC~$E{9gyB-+G4qe`*y_z_LGu z^8$lZ?!|BWZvUSX`Qa#}Py&vGUdCL$IM6ipthyMYMe|iVBvfCVhd9Rj_SRQM6`8Wz z12PHW8ik&*tkYP}5K}8!*+^lo$w-zMb2x3LL6vmI} z9V)#siVVs0uESUayL7QbNYlMX8q<37KcvP~`*r}=L^vr8C=E|-Y}B|#cAY94H!#1( zFMp4B?GJD#o|_>Zv7U2i)nlU5s{6-Em-MPS%motPo}{F*Hcsd3{d@}hK#@kAWe`GB%2|jDBu$*B`v+W1ky-9i5c*rGwG5BYGH2Z7R zkW9j?)|`B6i2oYWW2!bpo}!1J(SsP4J}G_Ih?_h11ZaTwaBVw3f%Y2}(3zuGiJEZV z^iHkq_z*e%w0;xixNq;jHaz;Sc%_4YHt}S=f}PF|Z~WWw$i+UvgYNZc%|}%?##DN* z`JddBF13E3Vc){|T5A>iClfMP1(|9|ODr!a z2eY25n;_F>T<$_)0inc?=EH?9=`%$^d%lFeipa#HjI`0qoiIJTjjm|FK-o{@JOAkqu;k-b0HCt^7 zow?!1l4mgCAO++IDC{B8J0?qE(0@-mR~=L8${|F_4_oUs}$l9YUEK6s$!&pR^kR|HSr^(qq?J`qw((F>x%En$^9zY zG``D!U5SKcbNwJ%aY92P9VHLRVAO`!Pc=I?qD{Oxp$9>kqkL&;X%080UoZ}imRTNJ zz8iC`qRjFa7m3J|W%4XWB?G5AjpF&#Hk+h%sW8@RbIZH{?RYEY4b4c!CSDUbs*Z?sYvzv z@TXW$wfAH)XwPd=8+m^R&e`%Z!mOcXRL7Wm$|5# z_J2&Rib?}$e29?@QVS*slk#U6Oe?;JkOpyH{^;Ttvlf&0Plw7a76!S_=QZeqwchSu zn*G(hBU_$SUS1Kj>Xkk>$|=NS)u2(6<7C`^DrC$t{K*jSS~IrqfO{ANCcV#JV^jx{xc_tR0KT3XnkXG;k?twa@VF8uhZ=gqhJ8Tg|H7JQIWP7B zuK5GX1mF-MyaseOBByzK=3ka5I4eJS^~A9 zmmV;hQ00)B#Y)oul`$>4LsOaZ1(@F3s^T_s0^BY?kaC=F=Bq~M5Kqvn9HN#pwQ@@X z=C<+3#o^eql02(2PG}3V45;3WJOupe{}qRr*1RWDudrjf_Ijc~WLR)?Y|IUKo@dig zWvfL4_nKTECQVGH=p>!lQZr`o=Ml5WE-IY|vSH0}aQOA+V1E`SkyrgV1ZrYPF3Xz@ z>tSj7qbXj}e(dYNnx$~E*GI$g>J6V>_WIH4g08PSoqWmKBk5(r>I#C1eaqoG3TqZR zzW=!PA&gDQ1O~C2HM)?AoQ(&-muuGytnHFgQmfbNOpu!l{5Jn7RLWX>q1MS)CX+A_ z298|vn<+O8J}He~E3$_c`%vkEfaV+R!7XMOA^+y7;(wePDM#?}a=0>W1q@mjq_LA! zGdsZVKSHmC5GnJj7yv+J~`FE)6$LmRSegpjPAh73_&27MvlJU>v{~Rdi+(7R* z(PYD>D4=%qiMa_9)P5OkTo=KKt}StmH<*aEUZt#l?syu*q6^>wPKE&TZnQ+fZ*QrL z<9vyflq2MPf1_921(x5Xn%&^BFH&8#sLplB$`!cuNdQ9qgozUEj3Rm?%q7+XJg4QO z@P>19o(K=%eQK}Ncj~zR1Hm_ znDA620=6}y#sx|NaDNfO^+1yW6IQ<_N6_I(G}wa)P|EeVuje8EJAa#W&-@M$)6+!t z_N$!3HimBiXLjfV(HB3T6r^uXQ4yyLKX;)8ZpsAmI0?PpNJ4j;eDRI~M-MTJ zaTwG|E+|!n4E{Ptb7AviCJJR~o-I^nnr^;4Djq1;BrB46r1h4)r{TEEbA(keaB1BD za`XF}K~KjJs3{4GVDhxO2*Ts-PqX?+(fuW7{zQOi;13mx_mBwa@h9c{?^xeHi!NC= zKH|I&#L@;qAK&lY!g+O7_ZB0SGR^CiPTzkQ@^EY!aE`wT78?qquDIJe`qq z(;$Y3R?0ksZuE!HvsA4?59YdKpgO!SJ#A@}dZj%f?mxuS-CCD-6x`=3D3rp52y#n% zEU$QBv?tRcg$M`@@nJ|6LOOEEaqc(xHIW_qxz%}hks>sFIz)se)Jk``I+9SQ#=(wV zdepB{0K&neB>K2G1?72Qfh(tmuABLi(WGW^Cahp41e^Gg zsK;67XW+RUTxmgEWS&9c)zJ^XTr7K+U1ZSz-7V;Ht|*!I;=M+jMIyMl5prK$PpwMj HaoGO=w{cX8 literal 0 HcmV?d00001 diff --git a/src/assets/weather/43.png b/src/assets/weather/43.png new file mode 100644 index 0000000000000000000000000000000000000000..9c1f2f9a93ec9f8a69657397e336c6a8095d4210 GIT binary patch literal 18639 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 literal 0 HcmV?d00001 diff --git a/src/assets/weather/47.png b/src/assets/weather/47.png new file mode 100644 index 0000000000000000000000000000000000000000..61e165d7324283c2ec3e03b631278791b8558634 GIT binary patch literal 20265 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 literal 0 HcmV?d00001 diff --git a/src/assets/weather/48.png b/src/assets/weather/48.png new file mode 100644 index 0000000000000000000000000000000000000000..8b576ea9925e9a0701e9c945044794ca99ead433 GIT binary patch literal 20389 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 literal 0 HcmV?d00001 diff --git a/src/assets/weather/49.png b/src/assets/weather/49.png new file mode 100644 index 0000000000000000000000000000000000000000..7c8f0cfbf3dded5f8a1f6635198ae5e1b42bbb03 GIT binary patch literal 20488 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 literal 0 HcmV?d00001 diff --git a/src/assets/weather/51.png b/src/assets/weather/51.png new file mode 100644 index 0000000000000000000000000000000000000000..623e19f6b6367c53aade31085f6ca0d2d2ee30a3 GIT binary patch literal 22200 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 literal 0 HcmV?d00001 diff --git a/src/assets/weather/52.png b/src/assets/weather/52.png new file mode 100644 index 0000000000000000000000000000000000000000..884723f8353078918bea6e3141e80e95748f0b66 GIT binary patch literal 16620 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 Hu0mjfQop_m literal 0 HcmV?d00001 diff --git a/src/assets/weather/55.png b/src/assets/weather/55.png new file mode 100644 index 0000000000000000000000000000000000000000..116ba1aa8214d708c83a4e3c834b77230a36a2e6 GIT binary patch literal 16391 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 IOvWVef1oEBwEzGB literal 0 HcmV?d00001 diff --git a/src/assets/weather/56.png b/src/assets/weather/56.png new file mode 100644 index 0000000000000000000000000000000000000000..3d8c6b407f940a0ca0fbf73fc33985c17ee1e2f3 GIT binary patch literal 18135 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 literal 0 HcmV?d00001 diff --git a/src/assets/weather/58.png b/src/assets/weather/58.png new file mode 100644 index 0000000000000000000000000000000000000000..1e75173eeba2e73012e483512f0766975cb9b27a GIT binary patch literal 15156 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 literal 0 HcmV?d00001 diff --git a/src/assets/weather/59.png b/src/assets/weather/59.png new file mode 100644 index 0000000000000000000000000000000000000000..db55fba30928e836def898061b92cc8eee3b5995 GIT binary patch literal 23187 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

TR^@$##;H-9-f#a1 z3un_@@z9Sf)GwNI@hoE3@G!yU?W#~wlWuOWt*wm$=0*;pWTaQ*0OvJ2==e|#;4N=5 z@Yq?WP1Dazyej{Iu-gF6A;2G;Dp|=%Ne0YwHll>B0332^^vAKY*d%=$MZ%)!zh~_x ziTC*~V!3x4?uF>8;VQDCWt_9o4+JECNb5^+(&J}9TWa_`%Z)d+0ClARQRf#O6QkgN z%EETNWtpR*qPBo2$-B?xWA#Gi-CR6E=JhG^nMq1X83reD5~`aHfCnY6jTTwLeL*%A z2l%vS!fURXx&=|vzt6{(>)v-(>mOc5^d$~t+RJA@j@bPa@!snnO{kbA(A%AyPbxmKERXB1f3iOJpiWx0T7oYpb^geo=2D8%Ndw*#YGsk zJIg>6!Hy~0KR)}Z44W2kw7`59|Gc9n?$?K({TOI2MU}L)mvD4IUb=Vx{)`-WRK*W8 z34FM(UcI^wXzowk+qcp?m3Xd$F>s*1l1S@EFfpNe?Ip&H${u^^>L%W$*;t*Zt5{8G zJ$!lyz>91cc?J26^8pN6g^Dupu6MJKXs?AnCEb5<*N!^HrYa^Yo zp)g`|fl;|d!$3goA||#Hlw=1$X)eyN`q>~U(`PZ2w&Zb{;Lm11G7Jg_B{DZFt2`wf z^7MU4j6*=Q3wPR+9J;_?No6h;POXVR-HiNbmfhAs=lRgNX45-uxJ^*q1yvGxHQbf%>BO)T~KriGYJ_`gFa-jTo6Ch-# zgPn>Xxn*z@w2Yg)nE+E_QN!8$Hn(uN%KmzroAjrR0t6n~JLCzIt|QNf^z6cf#`=VW zV3vs)o|o z2LxzBO)Z#bHVS9Uen9L0VG$BF>Tr~={Tr|wq*hi~Zj_~zUVP-|w>!(>9+~@N2Ry%4 zE+i^Sq7ZvcM2lnSPLn7(Am!IVWB+IibbnJ1z#mNbOC~RI0ZP*fZX_o*I(mozAXKP& zzOsr+_de(z+9;30DOIcPKC6eZXqQ}&U353Z2FEjP?p;a9i*vKyTIMlTfU@yxo@vRY z8ONM>w$jITBY+XeyP`R_LECeQ`{4Q^EcIm`^5fN56vcd57Jc3IjV@t}dPFeAth|di zX_Rm*^EaMhAo>xO>*?n~3O|=c1ENK;;GwNu!B^sj`~1R!14ROqdTvcsT|N4gy2paS z`yKx46&xqma0`$5R^{n`?xvv+BM$NQwTH zDuWwxApY_OGFa3AhpgkK(E+|@2SocEG(1eQgc+(?5r|hR?tJKj^`=DghnrMFR&2r$ zE8ax|5){aPl%xafAD{MR4p}a(w5$8|#QJ>I%Y+#jQ=G9I%%qJu#Q7^*_MDJ$!2Wk8 zWlR2ii-aqNpEhr4mOZ{+KegTlTH9TYAUoRt>*S`Lt?ht7gxEhtQyB{G_+3XxI4prr z{rP!EZC)=9b4E&wYNXm=EUJu~rYx7=G;Eb&WA?B}Fj`m+XG#}P$t zb~jBF$uwkzPaeGBj?wY%=@@gIR099bWLC2)w|QKFvgfrcJXp1-I8E`T>FAkM1KV)f z&~D{*PHE@@Cq6YfCFuItar@u*RXfqD>;EK3zW@CAVp>?5-X*(o7Vw4rzNV&gXS!Dk zj-ep1ae8Y8BNQWUu6fPkB^#RdB7<($4_7{;4>r_p*=16;4@(g7Pmy1fdlRAGs`Mfr~a literal 0 HcmV?d00001 diff --git a/src/assets/weather/6.png b/src/assets/weather/6.png new file mode 100644 index 0000000000000000000000000000000000000000..04ebaa580f241dbce7ef20d5fada81a820fd1699 GIT binary patch literal 18639 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 literal 0 HcmV?d00001 diff --git a/src/assets/weather/62.png b/src/assets/weather/62.png new file mode 100644 index 0000000000000000000000000000000000000000..a6ffa438c4ee2d8a1b65f6bd483645791a503ed0 GIT binary patch literal 21622 zcmXtAWnA3c)6JrbFH+o{;suHnSzsydw0LnZ#R|n)TuX7+2P-F@0|-*=uPlqXl!6qL8I**0;}AUHo{`=JxN4 z{VOYv06Dgt93^^MR2g+5P07&@L;@Dd!d5Hkf_*Mm_YCx3-oIZSDf2(NMBF`&>BZs; zh7huhJzcG=uI#`Vc{IRq|L2YezY;aK{*dZ4Dd;)rj^jf*-+lF>Y|9UThtLL%4Zub) z*1lyAe7tR6Ss5(I&9!N3Ya0!rJ2$QeZuItZudc4LOsl?(Cq&z{<;Ip;)A`Utc^IqS z0+#R_S8E2q*h$^;2aR&}jEFDCtYrfW2NCzf1$6G=$#uo=;Nk|8*|s-qO%@qm0!$Rk ze6OOuCY4#ljQvU!=nW|Ro2ZqljKNw8G5GgqM~EY=b2zx2g$Xc4^QVK9%m4VSFT8jv zm-PD0tYQ7$Rg%kPrvHb3=&{6lWGI$^$#*yE!5YTJNrw z%veMGAh09^7Na3WP=#VAY1~CAD2@iQqaTg?0M?+BLsc^YYu5qT5z(XWRajL71hc zIQF~p`CSI!Ve1gd09^nD2@4J~MS`Lz(=sCwb`seL25Kz+c^sjgLP7hT$)JPMEGg zYZM1ss5%uG^pWxp8Iqb3<(v2^=m7L5nF4aIDJyCa1(z+Z8S3!s`Jmrny%A*jwFnd? z24z%)Xw6to@a3B~U~I5_j7ChDu`~*^l(|}+#v1~(-+h6E9bHw}sU?WOSR@pDJCHK1 zI=YD*ae(PL^|BBe4Ze(P)!mPo6d%$ zIS3;8Kq+EHk!uqMYs7%?16x{1QwnyF;51zu+8OfP5ZuJ~Kgs+}afukf0_eGa)kwSP zG;#S{AQ)tH_`$DdhFp*_B-;Tg2r7)|4TONzSdxxU3I`>PEh=ct-x5=rb|Q)g#rrpe zj^Tndg(1w_SNpkYq{YB975 zv-V@|wi@#K((m{d3l+~-EE7sZtfP{QH~)nAq|ib~zzI_Z+9QcAZg?}tp5>K+D8(s+ ztOd2UHf-4AhWs}A|M&=F(eUkJEER~0Q{~t!%R!^q<)HE|z2A!wU6^YzUF~S->Q+S< zN3kH<|BNH#z;76>o!Vg0-~y;L4=tzEY_#}HGW!hTBV0o%T-@Q9Qfj(-*T8y2x|B4i z>d#tKkUQ)l_79pdX@Hx9h+Lr8T@#aoqt8lC03EIA@Nux`){>tS*y@wT77EDZp!U1Irnuwsd9Eg5R%QE`VPZ5exAOZ)4MGdDv{q9ztGMVcp*j~7ZQqy@%oeJX zd$aetoQzWjt2AqgdQQ06V{fF;r+xelK{Et@KDIYNo=}c2etTee*IgZdwE!}y#~W`- z|2u=UC$9!DW@g19hWev?j_@vK3;=o{P>5}YXSX>=>Z(Xdz#`33$S8Zm^nu0n#G6vo zp;@QeL@BdPcKQWkd>$UYk&OIh{T4br?9+a(Y*kH`S5Py;i5{*%CcNjX(z=^H!H;Wc zKeDhtV#X>?#NzT|{j#){sa}#`p*ef%Qdl$eSf{+7abS9vf+5tLx|L1jM6+Pe35s32D?=f_QFkGhU3A0Pe%oR^JVN`8_s5DE&T5A`yA@M z6*~U35j@M;=DLlcX>_*KNh=+~!59lvNsd5x!^4127MU4kWNCR6?B%)9I>)jR0AP(K zg%dBYAI(qp&l)&W)~Qw?yL`>g<5ZBPn$pScnRW0+CT^Hn7hE+&jugNo_!T}#oI}a0 zZ(I$A`2R;%p5lG=s^-ebZ6k1uq@yJb=)@y3;0`)wKYeQ8obr1ZTiw)NsW}{}8~>_e zXTyUOlq;zqPZbn`$Q{^{alft<%m^M^kNo(2_d28Yr?U1EG$i*wHM_edKT(Di&qtrX zojIi!@WqLyatGg>gez&L2Q@4SwQj6esJVquyZmG|o1h5%-hH+60pC&Z4-lvd6(GQh zVe!wPjhZ70K#^p}L6eRc$d~z?ne!LTDIX2r+LRy$0*!v^I}rUFUdX0Ke)em_s((pQ0N-rSK;y-={Fngm4ysq60$LDO#isUNTXYZj8ujX(W6tCK#eNB3(9 zajAX5uQv1MSX=C$oOhQbI7Flq-hD$4=& z&+Z2Rm|-;&fEuF!age4F@@-wV?@*gxmU6INQArefG<6hmi5XE!*4MWY!|lw1YM*uD zMD8gM4$0ropvr?WHeJx7l0?rm_j&9sHT%Ghw!eAud!gE7RQ9N(AwO{~dH9ABA&{UU zM{w@fKh-i{b>X>Y^~m&WbC&{g5TA>U$eZhL$NBNAr|%w~?vwupXuf0BO6yV6)Q=x! z_j6@3x(}8mS$+!KS?rD(NniDI-jMKJV?0Dx0BDC3yq|{*FZ{X1BicdQ;UbE0IqQN3 zHSQk${~?k?Lm%m})+NJus%5al1|kS}bztP3-YgX`BLUL>Fj6*7P? zOAHV(8I#6fTNmWUQ~XB6v(krVRRYl!W=M7jN2Kp@mFj0i8Bq0~d5#9a06r@>#$^cH z0t~@teZ>#}pTJTp8($Ckkx32W*=7if7c&k)kfSbk-YG9Pe!I2wzD*LCtLlh)H0+t1 z)$Dp&!Blgg18a>a=KV^NoB-rV)kFM&L0XgdwAaYW$L(K0S)OMA*Heqc-+LnwJQ}ZA zjQrYd+8J}Xzb#LPoDadz?uCcF!S;df;Y!^K zNKs{|ayF$9^=8#z@>fQ=Ekrp4w(hwhh}d@n_&rInf4=ka{Q~Ly%&ho}PYqfRooqAB zC2Yh*!$)07(Q^2EQ&4`>r}_zKehpPY(tz!!=m*gTv8`d+z;=7{apZAd0y^9z)afbR z(sZxZ)BCTVSUAq&-N_?&rmO}$5di_(FR!h5fuLMsx3M*-jrT#}tBc`xpG+oG%nsRv zZPbSgEs{rDW(UX;*Vl8RgB^A4crX;^90~f6VzqR80aA-6k6x<$vaWq|5CH%=>m9(C z{^)|Gg$j=Vbrwf2`Q${sV*e&x|0mBxgU|RZjc%;S~ELq{oKF~)0r#tUL-Slx(iq<;TMS%AO*v? z-t>rLIy6KvF=p*7Yf`?MAyMra_q%!X_L#KOp~Ho@j(66E__D`r{zdA9ahaRuYt#fp;&JF#T1Ein9?2m6GIvT;3n3(Pq zENk&X9v&pPB`r26Yyc@N?6%+LBdOCk%e6e19}w-RqPTCkqCa^R5!Xq;n3qp0gF&aV z^hJPTB;QPt@6#|wF9RV_N#1)W7RFm0It+9?+zplvZ`5EyOUa$SjgD=7aY6<>O3Ynn*5Nu1TV8#3{B3I+T z6+)ce)HoqvpfVIyPL9?T3*x1K6q5|!M`8UCKuVxxF&#pblM%!TM(qM)FtY8=-F$sX zr(I-#QzOQv>1sA-IM#%5qe5VK&es|F*Sx`H_(9l;n3M-DbOwDm4(UH!E3Zs;3h7#> zz5&ZVxJj9UI7Qj>(avl^(EQ5}_NpM^#0I6hG?1jDH%zP>r7JNK zf~Nr5L|{DL^#;ZpdiP#_$8aVH@zL1bcx~o=!lD<2)sqUD8+UC|8VvG1t^nx7CU!!^ z|99An%0s}_>4YSJ1nnlA*;hs)9OcIV@E@Q(c%>W60mIVWR@l$h>Q#4s>IBi_HREtmYCzee!I$Xz)+CS-1R{afTL7Hr%Y$iA;J)gG(Ph*;KhUd@@oK`^Q3tXDwhvez>h4ewAGpMa-Z07Yjz;kmE)g*>?h=-RzyXOh za#iL%{P+O{Z#WaDRTTA)OOICNK!PMqadzt@#*X_kaAIuOl7;ym!&?QF!OMaeBDSewyYG#MChwjNbe$TPqIq&vAt!30Kw3?-Q< zI=#$wL;M>=%6`bM1S4s6S_>bAKiH%$bsPqIrIGj;Vf2_3 zU@$iBVaELnAy~KKfPOeB=7~M^1Ob0oJEQXeWo%8bb%b||Jny<1xq?iP4A1ZHXh=M- z)7Jj|jE`6P@EJtk#rFZ7yA_&O!TTeN4sS~V9$>H4GZHzL67n+KdhrNfQ{jEKO%X;; z|AvF)WXWj^wJ8aS33rhBKE@DDs0(y5oD+U6i&OY*Mpq>ecE_;Ii4B#L>p|(v7pTeA zz5B?;A+&W>|1`U@Jl}d!)T?KUk0na%`4=f=ijo!wDN_Mk=A%}PI5P=` z__1g{zi`1?rLQk8m1a^LO%6gHL5$57Q-e*ak<&g_5>gS%2BBr6!$zLskt0W=n2Pzl z{h?;n&s5rG^%i${UCm}aG|lJtDO?ry`7s8+5BZA;DW_z0{uNXI2loqbSPvBE1~aUV z&$x3d<-r;!o$S-k(}!X7U*=!<9-uNkvjG!=o1hSA4%w?$x`4{`sDF~Ui~Zhx zu_fs*B3m?)uh zfWLOioK?o(W}j_qtm82Ld!@T&E_)ijtU5K6@V_-~gj>G++COmN5JoK;4IjqL$cZ!@ z)!_F}$NV-JbRfuIAxtx=Ze!(MaHHs(eH8ci`FJKkvl~Zv%55~Pug3MY!jC= z7CH;KZQYw^N9>>MbuP>>>}?1}MBG3Ve>T+?o{Lbk%go;J#BqziD3g*2;4>mK9ndQ( zn{Z1h8=>B&o$6{c+SQlHQ1o5^RE9IN)#1)K*xYYA~R1IT@x*(@Cy+$MQf81^PaWV zW?z1+lPpu^*(ju}&>l(Q7f{1&iBNAG7sW*WfR2zfvxFExd5Gx>5Wu(7 zjHBu64Fx#i?duSnQPhXU4kH!bE{1Y{NR%3{qUKaiQBHmo3fF&|%5NO>{BxlbM5@U$ zqEy-%u>bNqr?7U+COur%MLVhGskkjx*MjpUs%GRv7N;3%m#jnN9r+WXjwm zC`iq2_+-ocx>KgD+9?vkqa)2D3D<#}@^u$mYWcai(tqXW_?dTErG%jwp?D%4zNr9 z5vv#N{@Y5N6D37q1%>Si32UG7G^_*0&q3?((ntHLs0Cx>62k083O0glzL>y6rMS=;hN-wiPsHS2}P zHksPLn)?yxeaKZlRis+mTNG#Lj(GngjBnP$7mK_pG-p$W0NTf$0FHR$8uxRdqNFqK zvDmemQ)W{oh?aD4n2mm(`#+a_()r|yK!W6M_{Rx7$zs6wUl$J5SpcT!>U7yqQ)JsC<&T@f*W8SnWJf5V*o8p4_TW8YchN>W8PfLnwJ61j7ZRO%C4rjzdMh3c*Q( zbtu_Vz~68HTV+{u-#VedHq2B~{ozXGLrR`sf1f1^5h*A*%xJ3=$(ktUF9v&fjOMlw z2*B4y*EQCgsz zp8sce{&eGG?eM_JI9elIz3CV1DYmmv#a==px#lb+K@Uua>5;y_zt8587+HcZP-+HFbREr9q z=Y2H3IySa0YI}4K|0s3=k=9R05Qwthf4|XD46psT>)MBPRg(0^(NLSu#X;KV(azr9 zzVt`B#B=QQc>7uSQvffw(0y9`qNf6(fj*bT8959>Hfxsi!x>lzuC9O=F<4n%D5CKfXX3T zs{=772fqZ zp38DHmIu(iYdR!VJKjk;dl9WtJh29Zhc1@-zK(`!{o&w$2>}|ICZzP(J#zxA*5$%= z-+8cgWo6v6SkTbWCKeaRZsvSl$glo_%3BgmqFV10G%WYzyd}Ij<6Sicg zCy<0_F)OEiUAW!Jejcy4ud!d8nVJe9`oPs=q0^E{P{!XvNI;5sj2x=7!nsMMcu`f56UKX9t|l#$wHGh<&8Vs{A2^<3kWY~ z>2>rk`ZeB9Au5tk2LfpACa1>9^?`iQS~Hh$bdMw?C{f|}Qk5CHh=|CSr+=~%cdsZJ zxCa#Az@|ndDijiHq^&KuzhGDC^K~eJ%{5rcpG7$hhDFRl+rwi_+V@jMRpj+9d=nH% z3)jGu3UjKHFr1oYp-Mnxw{J-jqJ}829ph@kWxj z{QRUY+ap~gseFKA`&VcsLp+vnOIE4qIILNX#|46j|sZ@ElrC`zV|e zGK|YA0}5*7kpRolG}kXxI-B0&y4xj60-=3cJfbgw220ZsGA2P;jRPxBXRy-w|? z`j&CMS6bgqUomE$>v32wcq>+OHo;l`t%`gB!rA1)$z9=+U#^APRGhECUMcBp)R5z=zqp++KN(OVyTI>wH#rG zl~%{cyUSI*I@>?fR@pvRd_qzxJoo?@_`LYV&c0#^=8*{=4{|qK`T0K88X@zhZ#r&#XTKnUN0~6mf=a4(3NQ9mw~l2qaaG9&BhKlR zZJ@49>TXFdiEgowAspA%#*>_A{!kCWk{(IAzoIepx>Ej5JbRoWdt5VDZLPUkB(I`^ z>)_u%os0Q#->dkr+UH+Z(@J0egTavYv3mvzlqcP(;&1kQ@SV$5%Kr?Sf#@ix7)>la zi+^|8Vcf_qq?fb!_~^~mjg`9jZm;O_n8$yxfy~w8EY=sRvHr42GhL{)DbeBZT@Ru7 zV%>)iLm@^`?NL3$$1})q|H!jsB!Lo~b69j~+;C)yqtIWvb_HGo3P%m{0}qBzegl z`N+veAxG$Wn{x;zmSc6MROBGqs*e~z{wAd6y2SEqAI!`cGM$ImNSKqO=oVu|`i0+z zO?9FHb>EIxX8%T;l?$LsIu4-9{`Wx_r$lBCw?C?=jxG~*1QEYE+h1ZQ8gCfskiVG0 zY4*qPa1Mh*5pnRt)t6beQwlt^<*f6|xXWf^z*UQk8xIP=mK@Fcqp8wELPK9xIYuKw zY@DG$OJFkYD5jTu#@4-_VS3i`{9$)}T5rK9QqcUJNHM!%enM!ipuXxAnJq>$v1+kv zhaOiYg3o-PdOQN*Se3xR$;s&g8FcFont%aN1Nn-Y8kh5xHm~9G@^aVqW0#EVLaAJ} zX`>>6cWs%zofjf@S%lw}e`Hz-NQj*!Q+tyS*RotP_g6E7){3a7QRpF%MBD03>tEHKjtYzXDjuA=+n(p79C;v%CcHmw#YtSNsulb`>H{R#RtK3bM>DH*dPNW|Il_k(m^UIE658?I{(lGBI z+hItg4~b@%_7MJ+Ec-vg{YJkBPh(G-|NPSL!8v_k5%9t&SkMDzys#pUHZOSbnJ zee|af^gypV*`K?z!Xg1)`#nO5?3722TgCJ)*jh2~nfeByZnjvG!?@dLPcdSWl+I^r z4ehrsy2Ib|ldJv<)?3KhR><1va-wvml=aGDIqGKZC^A%8BXK3Wg1g**H}$;@b^J_x9iuLYX|XQ4s7r15RpI`4 zbFdDOx2NEe&ZrPE`@%@xexCc4*V5v}4j28e3A9ld$b$4zTUaX~vBU*7seNzOw_e`U zP}|7oQr~hU#pJHN@&z1xUSwXZ7oMDOCmyDw{b@mEYonw`VV8PZTR20Xx$8L!pXz28WE!7X5;52gyb9Y z-(PTc^oCkBw=j#OGh%a=oKV%licolWtb{2_&m$x#kP(Gb->;lRygFcz3&TShq3e%8 z^0=CFZ-8(4 z=pJNbGf__3`^?{-yeXP z6U6GiPV{{H0&09O_x@CVUTUmz8P5_+^!EofR@k9P8rzh!VM}W$gqu4VCL7FJ6c82v zlVF9wrTKZ%Z&Iz@h4;B@)MfQUySu)Sm*XWjXmb#s7uux3 zVE>~6yXV`ZrQaxJ7wb8{UQ~7N?OW=8i@98vr_4V3rAPOXKBjCfo(bQ#8?}1=H)FEe z@Q>N4hgz>ef`u|g#Q|r|2@9Y0Pio{aTCo+yshW<7R5j_L#2w39SMB1#ccTgRXQi>mzG8>kn3moIqOA zqy2q~z5RVfKl}H<&x?x->?Ow@oZ|^Sy8!?Reowti~Wvzw$4@;!HW&OCGu)#ArZw8RVXOIvW$(izn!)xlwgf^M>#| z{M~@9pIRFxGLDzRuwX+cv?Y64yylHt)sxWC?Bl z*76lt-IIq%&R?@{+`BZd(1MBZ<@=pBm%pvHZAHI3x=|(#5p$Yt!~lXI?HaE;QDQsY z0SGW7c3u9sv;Re?au7>c3m$~xTyG6v%Tr$U)!m0l!AO2S@j>`LVFPGCS}nOCx*;go ze_xW1&L?f{>-7S~K;`MOw>=vIOVeOa(PU6zTWBXk8E!n?eJ#pO*O3<1yS7HV%zz#?GNCG^?mScgq}H%>2D^V${p@A6 zfAO)cvPSzC(TD~l)&X!O;1>$}=O@D?9Dwxx6Nhq8cA1QA?5Q{g^o6!QEg$<{*Q~Fv zUuV2v-~N`0^i~?xmoQvz-`-4dd@`4zh~HR-Fx87z z5`1^(`2zg%0E!l{!V!K5-3uqFa@|L){4bGieJK4{ekxkYe$>N5+>V?>!2PN0k+7Zl z?TcUw)Saasl5l(UEYWk2)hd?=o)wyJy%W`1I0pTW@%TkB8>>R}VY-hE&@u$QUlHJ8 zy^B*21K7aEi3MmG9-pGD=S~#MF-M?YwZ_bCNWpd(Mt~7<%RctEv(~X zPay(?hVypEc7 zuG^3C4p!RI5idxjySl6ofk4zpM@L_=X%^PLFhl{tw{M9#hufSz09&)@;SsfTOVl_r0Xk`sQY;u;hLLhw_}ZEApzRk^ia*-!_Da*o zgm*HqRKzZ4EaI7!t4%roXeg)@rSKgO|2Z4afc#82-nlXK!Vk@wnVlsm({K3VldJYq zztM61!L*7sMc6~osQ>(Uu-;w)nO1s6Xv_kDUx=!Hnfqn3w&Hc{azHsv zU0&Y5k-1B2#xv8?c}tZW1W5i(j=NLvybDo@_^@eIHNh{*K>S)ySj?ibE%5NPc~__dZ1A}bAHl;_k5K460`SSK%$7n ze6@w*3uKto;~*{#KzU&wnS)qrLxL_tm@yi!2PI$Xm8^*h{|*jB8%=sbE-3`RczZZH zI!XX}<>mGZ|H(uqaTqk7+}KPPD%;rF#+i_IyDp>1aTIJ4t(CZ(rjQkAV7%Dw(C_aH zelV(e(IaTVcj2rwE@?0ZGJM`KP!5sJmQX$-43`vU-iOZ57fM;ME(T-nKrse7jav;R zdqD&floY^Qdtay3zEQ&lhJUTzyMvl~OgMY})CNt6;(?;`TLb4WXEMW3yFz275A2kn)COe`WEYePHPokmoHlY3y<_=9~{w4L3YbfLSShB$1-~XlUePyMqtE-(Btl)$$-`lh3BrB_x9)tr=ah?0~r>?ZL)b9QzPO|`; zSiw?NYOF+@!_}O_FZ;%J{5-?C$-s@$ij^1}(2)Y`+Z{S(B>%`78~~X(EFA|C;rE3l zDyjAraGO>Gh2aJJ}M`H=Okfg-%Q2zzl|DYjsd!h}s&jra} z_T{tnH|{ojLL?uI6ZyvPIv=i@NPK-yrx3q()?#AE)2(eQ98k}MEq9;~CxgiBu-l*% z9u2Oy1q-grxpRJBqK*?&@@mm`we$&~w0}E)7=DUi=ktN~CoeIyp)pqQQBUrCTTtnp)C0iAW zNL{`QCk8<9Y!ogaF$ik)r3usNc#3}PjO+JtSQCZvOCrCNOJZ% zaNZyCI;z}G)_(kL%m8u>ZnB`uK{Ec8o_Zdhs*a#6fq?|ef7i-bZYO)#+90&emy2CzUJ^VpJ5Z2!FE(=wTSkR4Emcx7PS056h$s3;bP*XIAai{!H_bHqU?RlKHoy ztebxYJFb0R+>n~m`TlkFekFlh9n^WUS#L#bHfobg$Bt9{BwtfA)%p&fsqL}VD z9G#vnU^Vvek|+xQNdy%xL#5(+ti4f*-?!t44k1BeS%OYX>yL}Z-u-nJ3Y64oIwKJS zJeh?!l})F<;{!A2RtnXS&SI#H*iMhULv>8gj7w0a{ zEiOi;WhywZw2V)+sO?0Vv#4JHTBu1Ea5@>KfEWTPlrSrEoAN-R$#~J}$x-1R6C_%$ z-OvixsD2?dKrJHpN@^ryxiHi-1m+`Nl&SG!uHoA(t(qJ&XF?cjm~vVmIWb&!TSaVt zgT{CNgVoIC1f~EfA4BOUJ^Cxhk zhM^2h*xD5wp=*_WKfkz#`JKwoW7CcVxlNy5u~XNdB}d%0i)BmhiT6dKW(FSOv&&Kh zzOC#WwcjmEw25dopaBh2w--GfUZy+4cJ+F{|D!8ZD&~7z?x)v!bC8sbo##ZV-Y-02 zkfec&L5BuhApeV;4Fuc-`P+8Sz8QYIHLxj}7}2d(b5NHy(`1Je@UiR^ChOhbX}Y?p zWV`&4t?(R2Jya6mw^t;haJIJooP}u|6V~7C78Q&JI(YS8+r~qO$9WUIs;7`SoqZ(E zBFv_-KZSoc-{7HD`g95p%KdIjv*T_8$3gdGJ2IJwMOFB`+ue^~FOfHHLxQ;Ki z%@$vMsyR!n>)HCNKqoZn{vpbZX0o=B5`U`}F=CM7*2Sc4BDLouOlcaq&uEWRbJ}O|LrO4=?T1_H}*6PF@I0s3MyVK@e zq5><-(zMivGhVgX^C`SWnZcoiO_Rc6jFvRt-P$qRZijr@AQ%*MPV9S2a{Rc4%Hr9+ zgR(F_YSehMSrLc_#3~9Gj(0_Cf5HO>Xr68c-#I)S$Y70T>~f0wPL(wn-KNVJJ?_=* z!}lciOxV5$Ko%AocIsNL4(DxUpG^kc!lN=Q$Q~>7%lYgzf)OHQMyw6WVmpRbpWHPx zg~8G$WYyOC*jsdJT==>bkaeE(0Wo|WHoJ(Y^#CbRc_}+8>vF*Oml@)>c82qqx7`31 zvQ4_B?e3CWzYZ2l$0et%&17fjZpCT zX=L}V=@XLLZ2K{yLm;y0+D*v;+p|T{ayTE2S7`1X4a54HpRRDk^XNxT6llk(^7GTJ zow(x_sv8}NP*&V;wCTC3jk@2(E~Egp-vj!6?N!U?mI>n&*UxF+KQFo=pPzJATl61R zsQXE+iNbg*k5W8_=BIZW_L@UPRqY)*N9M~1=Bd3@ntDIuu@~IeA|6ktit3Iu6)uBK zQaB#<_Cc`0cu7E~_5g=OC2M)$cLc zuCS@CZPWCrNp%)~$dy(zIw}S131hmgbZ#QvkNVP5M4}$8bR^*(93M69*=tjLTlT5x zlN)0oLsAM&6^KkI*>8||{7qF^;dS+wZ!C4*mP~q2^_#>~z&`1?yly7hQ1*TIvejj< zP%Nir%SiX@O;UoG5W9*`ZSDB@csZv>t{hv|AX-q+@UibHLvqIs&hM-^5)nO$eBGT$ zH2dZHa$#`?tvozxZ*y*V%j)fzUmDC@=iBn-cku#xWzh_0$MHg=)o)%s?;LJM(#sIr z^h#!P-g~FAk1IzNb;mx(vNy9GpVEIl^sphpKRtI3y`yQYd0euq@Cy`h)nEugze>Yf zx$lp|Y*h{xS&SH0MsXAhCr0YjJ7+{Zrf2(kZXtHEw=FyI;HDDl(Kyl{R~P*(HlBvL zyq<+QoHdl~9eA*kijf<7dkVN-v~^^zXQpoSXlj5C==oCOwLjAoKgZd^Z(+-@w~zVn z@T}2*oSfVI;<)`j-RsARFFA1K`emiz5ux1h#q(*frf_tm?(+J8+H<$5jU681WAg9E z7JL5`H4Yr@UeNye63_QEog~Y1bLPrza;DBtd;G4hO%-tSBaObp#m#h|nr*it@AiAT zt@QSd5{W&)ll~(w^^5MjP@_5YCvG9zp5Zsg{v^(hLxqK{mgX_8P3ya|k8bFpO-;E! zuMhqah%Jk?$#|`pj4p^LIr9TyBhQ?wwGZpz_BBQ0UwVwMb9lOz)Ho)ZchJE(8s10y z5e7Z{6agB?W3Jp4%gskJnaDxhur|=e-Ei`%)^HqSH_c*D-27V8BgL%vVw)T1SKOE^ z2r3q-+i~NDUBTu8VUo`w;#=$f1>TqWR=ng-p5vK<%NNlkeWeXWJGq&i-5qEmLhK4^ z_z9TJN;SS8AUOdkw0IY-o~w`F5I&|Wt%~JWSMO)Gf;`3#{kr_y+A1kuDrgwMQ?@e* z8=!gaMfHhPZQK1b1>MR|ty%2$Tx1S<)#TB$08F5tiaZ&m=g-yGy#lM&m-QtH=_a7h zO{CSEY|^kZF=41%`92oj9}n-8kBPN(+`2`%;bU)q2Z&QXR#B^SbwsDi+bu-6e?3l^ zZa*zJU-f=Kw;IpXBC8O(8Z&I+0#1*C>ft?%VFI!((hg*In-B_D}_+q^4Yxz1^eN>or0fO`Q^rq(D)m|Y;$Yw6L$t3a(U z13|o>u)byEgFWt74XR<^z9kOSV&8VVjMFUDxm>mhVUI2~I4nDA&o&1d8t&sBE#E6J zd9~)6O#WSWHZpvnYvXN=p3{oyh(n*kH=mCgG5;LQ^JT9#IM^4zh(p@KNup3H9yQOk zhU9hd<6_dQCIyqH|x{aWl487~G?U&^g)6dy>g1&cSE+Isj4v4K8=xodkon6nA zW1Ii`sKdWg8@KXg=VV@9&#kgUn$HIw%T}OjG9Z9I;UmsA6+QDPUl)7JdRv%j|J;8(KibNtR>jb~id)cbo?S02DmN$J?`yPO_9 zdw+cu8IyYM`u1?4#!An742B;FC?-NJ>z9f`m%yGLZj#{ln=I z-(Rno1c5x~F)Cp6#lrIG(4k|JkLTAGYd+T#GW|@aNK^hqn-8b6l0oQfnD1U zBpsJT?r-$k6a`kNAR--FO~{oh?DwXj?iH_%0{*mB{V$6Tjasj9+IPgBO)V>40XI7m zezph}ai_&#LQ&arjZf_l+lC#Fhk|YlL*7pf31&zDa4_fe{AsO8*5B$05WVhIeKGH*!req*x)BGLE%5?&+iB| zm3<`L<}v6$e(pqXH!0ksDzygYn(Qx+eV;}b8&(H@|zjir5-%KTEXB`fdF-JEQV#N9)m~>o}L##!I`sis#RlXD=Sxd=&=o8sKO&R=3$qhU?EKGLR{ZFOPh2>^ zZ=m7`)3D3KeJ97q$BTyk%Mv_?TmCx%AOPq$z2!eXe&H1-9T$RqCkRc$(mbWTSw8UK z%IN^o0FVIGl~?`f?G2L~KDJ=t)L{R?Az9yCz=n-mmI8PdhiZ)iKznvPwc>|2l`eRB z)uI!Zo!nm6xnte%J$F3$7+~)J>7>Ia?0nA)N`_$zrGl-!-Pj1!3DYzhf?%lkjyt|S z6hD)?`kJ4;bJC2KcQ&^b{hot^R-rCOjyE_1(M+GQn7ja>Kl0JrPk-p47j8cHoD=?U zduJYBMRoS^-#IgL=gwVnbCZw-vH)RIC>GEPMG>{Uip5fuwk|Ip`&xageOtkb(pO9M zeW}>K3T~y~R%z9OSU^bt0c8)MVGUUzAt4)to7|hbuQPK_|46Q0LK0GvP};ZO|8nP< zGiPR=Ip;a&S@P=2l@D#`Nx%A%zMcZ&k*%G<07$whJ~((}7Iy`rl*$q(lE957#>+~N zr*@Fn0a%(`!Ns@E7|UIr5QRvIJ;jWJhmPg9%XBtuQbK5IQ^WDX4_ExcG^=EwxCfzWS zJH25VjA?`m1RUX{C+01QyK_7ClK^19_{G{WJNHy?{NG>S{cnw@Tb}J{zxtBC9s#;t zUOoFi1>?@dc)ro5@KjwBWC|!onH2ABA<<|czAMSw`=x#Vo;UB#8uHbFMbavh7M$)q$vpK6; z)965eX#)n>su~-&)W+q0^=!e!Mt^LqE{i%OL9iy9xNMZbd5{(tSC|s+sUxJH$VVzpD&s! zSd26)J2~c+AXyay2aCe(Y77BVd@J|Ma4v6D`@!(5a=6AA(lV?t%SPfj?c#xeCou=0 z6Uq)mqa#uQ`*47nIj1}p3pV8Bj5yTLaJ;i~I(zwXBEjIEqX3QqKx5-M=jgkOPTn;A z{_B_}Nzo<3sbUbh7V&bw-x09LsqxV;v0VayPnz=L3p2krVI&7)DuZCL8q8&7Cq@9c zqzcd%Qve0PZn$yr(^=W}SMvs^JpaTKtFGzLJ_7)w94Owo@nDnljN6bo(8h-j%S5ZQ zl76wbqzaHj9pudrh0)nV(vX&BrWu(@7?fitjJ1Mk;Q+dSNM4|FeR_J*x{Qng+tbsN zHjf)Oc1=UW`ZJx#3INzW_q=)ERbPMV=#PK=?(`0Q?-qk3#$r*8X6L4`%t3ZOG(VFm zCKYa%*GWmot%9}{!_!Z%`$q2Y^aqEH%+$j%O(X(g$teoowd+7$yWAyP5%hKhpa9sn zXS{ZgIob5$z{j#DRm7Z5rLNW#{oT z4RkX=6E}GXfQYln*PyE`D=$A?HGm3=0i@u=lLXKmXbTtb=mCr)0EjdU`&7a@p<1RZ@j*B9)L}_@E6DcAQrA$zxqwrSgBJVz}B5rk3KNxTcpJo=3$)+(77%v1SLny(mIpxPkN&O1OR*fH=A+_HdidY z_kr>H=_U^`nHB12ZW`MzbIy%OlAKCN(0g!i(OU=i>M1~!0LB);C{d~h89S$Ky{vfa z>u>xGnxC`0h*p^<0hkloT>LC9QG#KL6?NyDgb|PRq=B_o-@rJ2&))_m-Wr241sfQ44TIKv7KDv6$upOrVr2x*iVY%P%Pi#QhX=SCAgVDoSJW(|`cOAVz`0WR%F~F((07C_(;`(3_x8_RO>EM;@+h`9XS43ahVk3Y>8!O9E6a zV8d65o{9Jr#r9b^?7RsuqywDlItc;p{dIK*rTnqpnd~kBz*Csw!7YnO-c|!+i zNs0-PK#N#eQSJe#8(-D^xg=by0#E>YRbA^H-t}AbdDO#Gr;OAZ8(XfQ|Kz6ZpZsKcBhIJIXW) z*It)v&&{=>nvn27>_hH6*eS@Q2^}z|5!2AAJd;5 zKVh(Ty2*VLJUya4idq5n3v8<|j>704Da9?Wxk}M)U%f^d~_YDKExx=*x0Pgifvy;*+ zhC#V0daDE?Q_2;YA`*6L0EwV;I{F?G39McF+wR=p8~~Otd(sb#)+9;NR8>8yX$mKV z%9QGoC|XbVFp#FtxL5^X08m%`#Rq?S#eH|wH#u*_$aFL?FI~H`ppi52kSJa--moV_ zU3_aPNGVIA5 zo7R1Nws_y^Lcr3o?-T&L>wC**ovLg7Fc=77;lhF|7c98F0vEMwS6Y&n_wClRXFbCR z5zU;(T+wJv87^TUNSt0x0Xl;wh$0IGg9k!E&H#l=l9Z%rLEoA+&-XavrxOGK`pi3i zfA8_~hRG*RJpUv*-^1qx1xM4CuRJg=HOodq-XN8raEnPHXX@PGJkr%rj_-bVe&$CX z6|`*MxN+I`O`EbrL8Y38(?F6+h!##L>)-a}|9tf~N>4cc^rHu^i&d002q!9QZUs<* z?nA@7q^@p5Biihfzbn1k3ii0Sl7y&Mv)HLM;6->Gys5~__C7!tyw;in8i>lwdclIQu@r;`p6a#HKdwN3q zvA?T7zqj<(7vR6{y6Xy5)%%&I1x{#Mpp1~{M&MA%MYcx(usL%-xK=jHZ`?I&f;N0) zmKKh9rnMjM>|uNNZr=Q1;mRWFX=#=e1wvKDUZHU9wlcubgs(=qctIurz$c%)+`{z8 z-dHRW24DqP5@%e{_11Ey^OJ^zbJ*B%3!nP_eG`Z4oU4K)NH)8LoOL*cw9E8s`gVWQ zYj5D5J#Tpc>}YR`GuG>6jA_^Ynz!fte43%TB@i%MLDoe%S{f zzh@njHh?P)we_`)wfa6ljF*AwBDu4&I5qV~r`sJKN~vfBq7F@~F2h$FSnSO=cW13! zU-HP%QJJi^riE%T9Z3cOk*L>>wz>tE*lzDz`Z6G-yJV2Wq6*#hg-@RH!rZ&=xpp+C zRE0n!gHZyZTEf>>ojBa(+Frxh{!cY+Pobt27vn2Ki~_*k;@X+l-Z;t@)EF~bWSTO-3X??y=OKFQ zEsvUe@LVoyE>%!yPYN0_02YJQ_|u`IvRJbxOv4(3$^nC{8$bQ*^RA+6{EUUmC>0b* zGFc_bq8JRaUDre5q@?td0I+oFj=^uQ*#DWVm{#0!>yn4s&+AcITvM}u>aa0`Sd!hu zGqY?Ycklpiw&*+*@&pJGE_F<0pVQw8Kmg#8M^;RI|NYN1FVv5pKDz3ru@i@7F@wNt zb`#ASWar7LM#NM%tE)R}Y?tk*NF1o%t7)xURMpv_sxEgd=BfokTYbLIY5`amzw+rv z|M~C@!)}`~UibMucXZKCrv!b`)>urZc|+28c5W&gHX@5N-9GbU z1OR;G70)a@Rp;5hXwk-Z+JEnCDj)z*e4uW8*5Fh%K;!~kfpq~(jiQnKALg6Nl&p_Sn1&!sj1P~m_K;fIkH_4dg`hB zzuZij^VkPdeKGdXpu7~#aoR0e%rcQ=KH$a0?`F(x?tufq*|W9L=8}VNR+oK# z&5&W%%vNWEx3b*54@hd!H1(2s?fQ%UPyh;mJvo2P_#+i(U%KtvWA(cFR&KG$#;TL2 z#sY{3b0iv!8~_>{j_%&Q|KPqPB{XTmwYhLviSWUSKf8P$ZMS3)dmPRkmnlBn@ZS&3 zod!p15W$#6M454uMUb~|D^5Y1f8l~{Q7W1M?suGNEO4CB1whmVAxY8o)>uKoi@n+G zx-94q1>gXL&D$$qdu;x#lFu8`L|H^inuQl_-!=k3Jof!Og27eR~tbBnHP=hPkS1_FRDd1-5Yaiw#LXjQlnQ3ZRN6=p>SNC@TS)jdfM0|3af z;Uo|nFU!dpoO6|PP(mQ+I;+Nob09B!`dR@f0QT>*|1i^@oBB=BD5*Y&DvE?^CW}Ol z9joZfIrHV;n#LlcXcQRJtC||!%2U9wGD*rBN2!6dRd8WCV`?edYWwvV zyWQk*HT&tnY`Y${8JX26v)1N1@pMCj6FjZUaI($_1d3_^G|rv-QZk^kmtUUe#wGUK z_ZNMx02?`U@^%k+SWqacjh{a6-*=q9NmYuuyWvU|I#=BG&MQw zO||=1FPc}|m_IUgm?-$Q%JQZo;I`6);!FSU=?^2&cVOqXwV!;L|Hzb^^J#8w23%)O zVr$`!8bE4nzh+N{;ENq%+v>X$N+~78>vrurwBNC-?T}F*B&w&R*j&{;tzq&{PJd*e zSb-6Dnk~b>lRs*}*ug_hrkWF#^<^cVot#IG0HtfuGX>}^3NZW*Sspl=F{T3Gf#=i@W>9-CMl4Q>?guyg$CRCdt1^ z=Hw)2@12v0P*(hkjzWk6006p-w1nz=3;AC`LU^zHg$&05fEgfB~LCI8eZK>mF3+~3rYb0 z_3iqtOrnXWo%>FaR<03pvS)}84%wwQzW#C}^>R{o2L=Z3e!L;WLDCJuGb${!qBf~= z{mZYfudxYRTb8~rzRW2}-vJB=6wE2$di-l$EWg(K-)@<2q~tOfB^)Wx0J|>QdaHH6 z)K0PKAfDUFMAv26_^Uah*Ph)eQ0qffC^8pZV%XGR3T6iWrAr{&GB;(oC0 zKUrmjk8kMPKPD$8u|G1km++N^m7B)xS3Hp}>XjTvjj?&Zv3q`v@pKCokp{>K6+4dB z6LS7}jv$G^moXWq`7!|>@&{jupfcioZHhd8HV1_qc;Uz?mLq&WX-@K%n?{?dMNwcV z;EM2Siwr0X;IMJAi=cFuFqE|>3wf;sQuJy*d+4!g*NP788<0HHhH=AY?WQN;Z{WNH`F35VG=(9yN?d<2mt8ECHJYn}jlwct`+t zL&sr9Cik8)eyl8@Ipu8K z4MOpRx_*lsf*4gPO_2Xg^MO!<%6q5+zoA8fYbU}j#tlMYI2$NJBL}^>D0O~gu(MP3 zC5E^C830qtmRYdToU;+J5t%LE{kG<%Z2C!{%!yV(kYA7Z;ougD0)!RkgeHnB3HK?| zKL{Om$ig-)hJq435F`qN=S1y<$C<<}MbK}EWl@D5rsX_h059Swv6n)K%zQblvimSNeF^lWLIxAo=4D!5AkI}Oov7V?vcnDuWdJ@MkWh_lLe{hGpD@E>fKN!{+ zjp4&g=K~`QP7*JWLR05{he1lZA{rtV5Ed#`T4jt)tB})cxQQ!*rK(!S8=S4JUoeF5 zkzACjUy^}K$sEhD$Uk-DqZ1keCi;gm(|nYL=+}Jn=h+bh6+J z!Uc$7Sp?@xm7+14E`dYmY{otmhenoHnpC$qzpm*zr!p^nBPE>@Xz-eZw`Epz`?pJb z`=Y$m%(bT~gE#x{SmtB*$z7=j267qQKRbiyy84H|7jl>auYQaC9&X~sqKu|=iA|I3 z)qjk}wrN_X1ut9qqgC;d^V8AUIkCf`@)HsUl(0XS_Gz%_ScLi3aR5357;=Cgwl!Uf zO}q8^pXrDRF#y$duNqw7(Hjnw9LGXHfIv{tA0KaLKWmUpfBVTb-^}B6zo6e8?PJVX z~X|da$sa+-%6zg=f3iP0Vz-%P@EBJ35DxK^?3|EDj+5b@6;S6W{uVS+j-s<|w47 z#fRyaS8H;)I3m!QsKocCrkRq5NV-0-@o=1eKXX$eTg=5oPjy8@cs4>6lcnmRcX+nJ z6YqaqHhJ39y4OZC{bJW2`;E2dB0`c;!zH<;%WJP zN9KP!I-L?W9viPpP*vq)Z0GR8>Hnpelhf(4f7Om1JbD#ZoJXO0G*so-jbA81-(;wI1atC>6!e3DA=sd*_@S0uCJaNpE)D) z47LM16ag(TUc}#DSStfLJG%-FYk%q_Pb~Hb`r1X!go==-Y^g$cR7fND<-4p*ca7)H zP>`~r2wJA7gsQAGJq2?Fv;v}lWQmQ&**62Nrb?u=pHfMM>;($a|Uam3DfkDKe0>Un(R&!+zxmE1tqwKo-I#+=#VtJ!GN z`Zz{3>Sn`Sv>Q59`EYZZ%hlUm_3>=E-Pnxw{9cN2e6mCDF6Sj1HdC+bES6u`QcDZv zOQ!2X-7xpnhVRyGe7fY%nVETZ`=KbQ3N&OkYz<_R2P^u(5)8k2AAM9v@KZVkwTSv| z?k*Ak%Tk%oZ5g+ks)TYpx<6tH)Kbiz zzDJ=uoko!VlSau_^V=&QhTC2LXgk2p6lSmrX+$Ib8oh!(GPT&B z7vJ@~eWgVcT__*-PF_Z>Qup%usVoK~e=L4FjPjXCg-nNNL9S zyP_*rwi^(xOxK#WupHdw?Ix-j9a*xO{8Z~=X|9$Q&NUkOfm4O>tR%lz6hAfu_UYDe zs*<*u37N7l6#Z$-8-@d&WU1L1$+wHD?vTc2#RZ5HYR5m}!8BHi6Pq@G7>yzhPL)aL|6>W$&1$&|FsNR}C(k z`+qRczOk*;x~V#L93t2l*p}4qB`|;H?0WuZrGS;83HlNz}0 z1Y(VQuf%ko^1nHbzv;yw+I5ipdBUXB9uXme4Ihw{zv03#N~A`LVk4Xihi>!Yh?&f+ ztjDIr(p%W&MyK44T3DWoELoLhYZM$r`tt z_dDi6>2)5XToyMV1ccBTUjs5l;WCnm=pe+fI%^GK_3A8G3W#xBcI@Xndvlevh(*0+ zv4(C3LfE{oq<@0Vb9JL+vWN3$7BEB!Q)tT3Bav{wG2#0oLqh<+hw&y0tFf&}#@MfA zyE|hQ>V2-vKAS6~Y;IT55h#jNjL8`IvBB~|qgca-VgtV^B6c@e# z!jMJnESv1sy1sA-k^eQF@5$=9sTsa`R#Ut!YD^cA{_{+MJCmp91vJt%RsV70Q`h5) z@aWVOt?F2K?wSjUHyHzS_mR2(kQPyEwZg{z5ZQe!KPeBydeZxV-;djLw$jzFY>tlU zA7zE`Sv|Qjj<(#=XVTbNk)^|=F>M)F8=x8l#SB=8rC)0H1@CF1bMct7JFBDW`MU}R z$_l5DaFPHQLquJzck9*11}u7W5gqP%sYuG>&W z5+u|}fLw}<6PHPDbH%zRZa8--&}Ut;jd*izToG!2{zymKV~l?LcPYx}8~)IthaEj> z(Y-JQFy2hg%>Hv-LP4_`p67%-bCNuFIzK-jA|?*=?PtqQl5^a!^RzAE*DERkQzxpX z79OYRbqbzw*pe(ivsK5o*sSi|0y3oHLa3UvGe%F(!PU>ME*Mpm_LJ(=`xE^hIoOon zZ7#DxSBgUWIWrYpC{bM4W)Y zyWDDX0QbP?o5shVZkmUO79i}cDgMQ+ch?1H@{qv+6S=|!s0S@`)zyfH#}yiZ>191V z!NTizgka`0ZhxUh45N-MWw=BGBpEEDN@Dx>$c7}Q5A@u zEPS_1N6Ptqf$$^%M7QJdVm?37Y#c3mJq&Q)bCa5n=ekuQmto z@J`74{QjgqXX`F7zVS94mH_S+7yrogQ2CMBRii6T-Ip+jRc zw*7Sl6^sk(q1^=;LE=)GLG}Y&CemV0JEW9Zo&deMhFhm9R*-QB7XVRPoXL0tjHZyg=M3 zessolSA3TCLw(1)KQ#m77moYiqb`O&=fZ`UqM<(_*Fno*q7Ymv_vNQuN~-KA3N9U; zxkDeLEbPZMiY3eN@=97HVy(Z??(|LaQWr}y+rno#t%GFOG{%1%o-+U2pL{ZGLm%8> z5xg4marRR_nZd@Pb$a5xy=t@Cr8yP(55?fuqte;VVKh_)Fu(&M(4i6rV6Mj2rB>zv zam-3Yz{2+8)LuW<^NKbg++k6d6&A^H-!#?Oz6Lse(hLK<KB>`cHJ=KC z!GFIxTf?Boz?*Oov{s{9ejnK*!9%h8z0_z^hAibWk=xa5y#6aD|NI~Xj}VTq!p(?y zA^f4P8K!(LzZ`$vh)@@l(i#w0pa}s$lZQlbAfxLO`qrD~1IMGiPl3w@ug}uY_Nxm) zB{^Os&HMyWiaG(h{C8W`9Sd7Ui65bkOcR+r$P-ZGID0AkX;S%YZ(=Qe{!I-A@^pe5 z`x5|yBhHqLqevr4mnz5mu+3?}hhN?KujBrA%`1wAzAuiC@Ic5%mWmZXO!+suglqh8 zIesx(nmFE91~35&1sbT|o#QW?!W?ppJy!Ms} zu}K39lQy)TKvgS2F0$N<0zp%=uXdIj8BD3TAR>vwkfT%44wIC%Jt+4(+#gO;nNoD4 z9R-XEk$^`sP$FZ~q`ar-bwnD@^%6LH8Z87Ll8HLY!E}@LIWn!ibZB6Qc4^lC1MOz$ zFnJBLdu;adUsNfJ%LblXw^oO2Jnx$VD5#3O2yR26=gA`K0=zdXtXPsXC$bQv(mu+h zud=*Hf+b=rphcZ4eWd>B&Gr+;fCI_Xs8;@kOfbAhyPlcUw{HmFN)!iwe$wwBDk-?E zWeN!Gj7gu*ya+n)#MIDt=ICR~2q7k|-W(zmL?91aj-rJ%kd_nEf?I{L8AkX|YIQ1W zqNss3!h%Cr*<_ZJLa!fdbtl3iZfaJGk8=pTo5aOS$a8@OAjv2L7#J`q@VqR&;Fwqf zPYZV(($2Tfoo}!uYBBlz=Q@bcp+4NWuX$)@tQirX&VP7pwN>%nT!+-!2IS|Xyn1YS zVKo`-*GoJ{A=!+U2dF6{guCWO1D=P@kvE*8E1+amd%Y-YyZq0|`Gew= zl(reFEoU4fA_2={19gKjgf)@W3#bKO#p(xayqJEf86fX9yT|z!_!(A93ZWA?jEbII$DCyVS;{m z`)Up!z*QC2uKR({LNGk!oEKGrM1cd*6Ns5J8u}s zP@p?gP<8`$s9v(ikj1r2+$OZw5E9VaD;q2q*-HmbE(n7~421!FeFyJ7=cEuM8r^DD z8H^wJ7!5jba-VnORI2Y=_m2~?8PKcHgY%9z>)8*FB98ydj|GW;jvkmm5|2s1#^iUu z*=ajy-dgO@i>lIg17Yzx{;uP_{406U1*9a)kA0OYN&->!%XAPt-J;__sf#D!yY9ve z6Ma9VtSVK~8y=U-iplHXcJ#@Z;9=Lbm7^GacKF2rq0iAZ;C1nTHj#ge;6NQ%P4_G))$guCjwI?ae}}`5~Qd+weXZF^(>d8BRUO%B*737$7>zxZR|pp#PGG& zIR|99Y0XG?|GzjEFiBm&Tr;^XG6iw~+2+7iYN4epzicrFL$}y6>M-#K*5Ct1Hh|}( zptuXR0iSum^4#;2(Cf{(^`>!+PM7rB7%%@>MrRI14%yz2+6#aeFm(-6^tHfs*g|162VA-o%YLd6ItihVhbhJ z@8Q?i`GroJZ>2hsRTl#vWPGfU)gnog&imQKSMWI=h=~g6zvr6Eo7Aa?M$O3ff?>y- zwoLvU5^#cP_XR?ER09_-GXPv$<`KMlERKKq$z)Jg4WmkqB@14@k15Q(wBJRunW!aQ zxDgbGq)dxIdr!Y1^0en510+!A55xs87gd_iZDMu3bV%_jkQ1$ued1OEA}Jy|;b1&C z`Z5%VNv@b`w?4+^e++C|RA``ky^tnkWTa$$(&oqhXR5vDPBN6kO2b=dWmiSM}~Nz!pI6;Rg9`07$KS5=W>w@873IqA>q?!0t1=T2{jCyz3xk#R>^;m zr^SA-uyn+WBE(Mth*1Lx#w^g#F(lTRI0MiNB$p9XQINg{YKmlH6&tduo73jh#C^;j z8`+Lkk3wa`1=Zjv;1R%5LY;PKv~2**83qW3YDHM0i27%aA*3#jcXe#?d)?E7vLC#J zO5V6Dd^jAOW8<2@Z4L6Jj#}pw+ogo~blC0l$WwgXWFCD^QX200fuJVnFS3ejXo26RzAqjI^G;3oq z;oTYjG0A*q^W7X#soR9p#;(VuhONf~J^U2rLbgqc6zsI#*)%pzI5_MEaR28z>F5Xw ztdnE490`AwNNY+K`N}n3GG&_5aNVBWtiQNeMrvrNqVbe%_?Px-5wyXjPSw#nuPNb1 zud2#7#LOy%CgQR-f5s2BkZNfRWC*+&LABJ4SxJS8@;SXU7yNRYi za{BFVmA+4lr8wVdA@Ok5X!%ruvT_4=Un5vl2U?5(>c!t1f#ue;#qy9O0DQ?yXhA}G z;j7k(B2W`8TU-E9c5^-Tf1Uk0Mh54EpNFK3qfCjzTnWem*yr8l%c=r#3QwMpAxDRYyo0XUmC?<%DnqVq`E>!?+5A}4eCe)&>k`Z)c7=`_k8Gw9ef)HnYL}PdyFHvwj_L* zD^$tb>_^pv2ww2Zp4C5}AL4M4BO4I}8v+|wDlDnWEUB$J)efbg&5;gFnWryO3bhQ_3gbi>cQXcFqcx2a2nY6KI?1 zsFzReGSPYEz;)xW6Wk1nSO>8oZ9WLe4pK%I<1i$nygyGlUqv-T&%FUGB#K)eJe155 zH#ecr=2KaSA6bRYmI=MOTua^@00)~6UCwE0oOxrQG5?rWYJg`bI9>jy@!-D?xnm|A zea*IRx*GD?Up_3`-~;8vsS_)bZy2`j+ru0{;pgZwc;^3##dq_aBCiJN*i9nS*x4#G zHWFmR!{yi^Oz9HH!#I5C@h)u~la~>{unG-iglopQNKe0krTCKtrp1d@Lk}K^;e_Bt zM+{4&Rb_(>rMu7juNdf($1Vggt4}I(KU?3jP77R3t zNN9Kd*a+i_9b#Eaa!MMv!lXA%PLt_oA4vWe@7yI|iL89?)#)^MH|MLgQj;VG4n^w6 z>OQAJ0?`%oqKVTL=egoJsLIwYWHc? zgU3H! zlpQI&H{ojMdR^{AjFsj^FMvsdn{SF^Bq z|AaAMs~RZ%qajBM1ac1Q4m{~n$JNTz)Ue>LhluP)2sq82$8Q$(-%N?&k&aa;BG8c} zyoL~z^*TK+T^DtnC*SZ<;Q!3bkef|rNH&;Fig9IIk-62T^Q{Zn#fD$p@Cp%Rd1WR2 zN>!H(gEi^eXjOjeis9pni9*QV*A?$$~x|QIA z9}6rc(kf`#t#b3lQgNiCnR5_ilm>}V-^L|f4Kq?rerOVz`EqP;q(eG)-|yfh7#MdD z^Q&Wc;=ljmQzXI~0%#dpnx`!z@L@BHl9+p0BB2GR9gHw&?j>&b^=TcVg zjI1Wm@>p>JvdLmBM?tlukI0sd;rh3J#lYodEy?o{QCc4+l zI6=Dg^4FnK zoTa;jV?YEzp1zFU&qkLlOJ%wJYv-p|0sEf_ub}Vt%9tewiB!7kU*NJkg>%L|irhqP zGmrPGO7xB;@8;G=XE~$P~tf!`?<`XR&C+DX# zjdtTEC^)WS4pwDaNeRr&z`mCWOa z=LkTVMmqZ;Y>Ah8wOK~?{eV%c@&luKmM`3OHXAHOJq#1zwbAX%UoeeF$UUSC4t3Qp zhlg%SShZj6I4V|6=hEu*z1)CAj-2Ms7h%5%8rPH3S^38y=4e5Fr?4lOlnp;L{h&Sa z9%D17fFmB>Dwa#laRIl-%7k5k=v+rg_}x17SW;Q1_neBF%r@@mbEn@mA7sPhvhWoT z5y2@LTp>Y)3|ha_yy9_^ILK(Dvy(J^#bT3`Mwig9z`W*at>phQ&mF8-b%#A3j=K_{ z6{D|q*VfmC$W+uE`nV_n-s9p9>&5CtOj3cvrsn2$L$DJT98w^JBYgtgSKQXXU3|WX zBP{4)si?5MWeNIO{B?y&W(=fEBUZ($_%q*H)S+lV8lfv^q-O7V_r-D9&GzkfGb%ogAZOz)5*Nwn#Z;(nC= zq7|n-gqL+Z;t{G>uS_ZEoQnNfpi^toZPPyZoW)oJ29> z7l<<3pE6liVqjuq&naD=_S=O(O&~I55H1K3zCGO~E_^k?8CR**bWEZutH$slHbUur zX@3EBjv9hJZg@EfS{wZofq4A4qu3HY;i{(-Y-(n~AMNvZvD6HyicL7=zST5Vg54T^a=$4cq3_dGsoi4tC-!QJ-(I~= zldIWw+{B17aa0W^9?eXUBtvI0^11P_<~yY;(%mQ1-w0pltjKl0Qf6Br*5XGu{m3It zu(FMEM68h#1Hhb)$th_!BI*@^7+Rc`fp-wanT)sB&dGTvJU763@GJh zal~3D{!(g@{9@zam^<-YFM98FdKP;kJe;n&b0sLk#mB2xTx;Xgot5vM33LvZN(QTF z{n_7^x@w(#ai@UG@fh2()BFZWgQ$%d6A5=2{$?eewBMrGF+^Z`UKA_*>8#^?E1Tb4 zh@v=Y2#gu_osI(=C^G#X%RVFu2ZV(?t+v>2q>P&EzvH-?KX<;i(G&n)jY+YWlqU1# zJY{#vCT`WEjL}xJZ5)h9l{Ht6VhzdtWkJvl@HwZBmsHeRG`{nj+^&};F}uF4vj8^S z#$B5Z%R4SRJ~_c{+3zvF z|1O`7GN@O5wyfgA|D$GaWG294FMw4P@nip$>vH}W0N-F7W77$i9^L*zW&14!o+q#P z54$P2;EgFAnR4G8b*dX`3oN)&HHLR^u>=-p?Yf=|@ILRlqm{^%e=dXv(w4i`ekbG12|$s z+pB6>_nLlByd;aUZ9w7tq~Bbc8B4829gOuO-44fi9xXqOHB1 zogF1*B$1iGw48bqZ4}}BV|n`$JB*lEbmK@(Hhcy8OYBD$l4;fI8*ZGug=*Hys&naZ zJ*BUU&Z3E2dY(JU>305oBJapIakZU%hR_qkMhheJ|53tv(v%`!=bu_YvA%iyqxS?%{_i~HW+xC6l% z>@dl5y-tYG+Fm5|*r|FWQ|es6p|{m3L?xt17$x|}0lZy? z)TZ~d7Z zz4~Ty>s*sijt!!lNRWyl05>-`r$;9ztCwre(=zm?;@`e~bC#2nv;JReY-}u)JAJ@3 zULPUNq~Gnc^{ea~2_@yC>nIraR8o$>wdqWUgY8IHN~SzUFq#Kq={<1wVkJOc2Rp|e zQ&hR~@a9#($6S#3Xce=}1)U%NYel0~HAt2^T*pwnwPlD7_1aH254CUGOUcC^^dDw> z<#Z)}yBzPwzLQQ3L7dE<_IWe*_k4!8RS3}XdM^89vod1{^QQ}F%6q)?PlwTzF!`h^ zYJGZhcXxhze!h`yg+`GP$#TBjaIpP9TJXmZ%uv;|Yr(or@#x=~K41d1uDbFXkh%8l zd$1%b!UvALV%WnfgXCKZN$(bAQ&v2rm;syBYHNGy%^1-^L$GS4*k_>4?Q~`PuKTVl zlOcI5VCe!C2$sMjH2#74wbS$+Q8@3nJ+Wpw&m}2wyI3^!XoRm7?YkK`QaRtTK#(?f zVI?Vm)O1_36!0HIllj9^?Cm8kY`&9)DsQ8AFgvbZsmX5Z;h{N9URoZgHLEAL*0@x? zH`gCNT`qd;xt_*sJ!%%+c&1>4(S+USB_dB`!H)FK7E$`ymT#gY`e=0QS8vnktUy?K zgkLv{bT;nLITIJOgsx}A5#JZYkUn12psfV8ks}#72LM}jV|V})3kRU(8A`}MtdAZL z3bpDZO)*fR+AOEH+2-F3$U#PcA;e8%7bU}OmK8^m!1TnJtI>e_9K9~D8(VaA^xQN) zXS1u3k#IA!^wJdSoOtfYIRrHvH$~ps6p<$NhV!R)Z+@po{{hrEOn*B@Sc@&Oi- z@8L5ZzWquH~3fSXQ= zbT)he=8P=mh-%rdmni+`?O_ca$3fR9eCA`OWLYh1g`qSn19~*^$d<3Yv2RuuryMue zyRu?gjnay^!a~lJFnw$$lz}_EyS$2G5-5SdvQ0Bepaf1)SD=dlErOQo*P8REm1f)J z-@pHBdv`L@oG-3c!|cPvoGlFv4OL}jGVjNkz<%1HqI-$5hD&7%?=pFz1z725NYboTQN}3 zdM;V}S$=}&UjYBkSXEI}WeuZ6-*>TQ+x{zockv~wG|GlsuKJ|UF^j$6%YRCsUqz%O za;y&nxJrLJ9`-ZOiHV8VV4&>>Io?k1+?gahJNujYL>jy8zunPyQkU^dlMc_R`b2iM zIYd>~JLe+?8XDU9dRK?{cnULrv(559sbbY51R%2ek8(tWzLd=aRsE|k?hE^IAqaTO zedA7iJNTft&P!J0mN2oOMgec@pE45s@&S-VVgU8=485N`8^J}~*ObFZSTq4xyt(vT zZ@b^vVEr8LS=Dj#Rjf$_o-g{{?bcc^oBpd%&u*|;uIEcJ%~vfWM*`t00SUINS@l_E z3m&r6B9WYYRZqrK!K$)soSr#hRoX3Q?+j-7owdtTQogSbD>lZ=N%d5{j_OPxHar}+ zpS|o;^CLXx_@55n_zyzlJVPn%cQ)85C{hpQyuxHnfUcc75LH3}c!>MJjiRVfK()k>Y4Bn~T){HvI7 z{`4M-s%+LfJ;!rjuS;Az+|So3i(U4ovO6q}=8DG;-eY`ASC=4TabU znlD~Z0HBNo_nzHV~y=Q-NbfPt{ zv6ScTpaNi;9uu*HZ1g_v^-jrm_MXP$ygzaD?z!m8HYvxGu+;Qj zywuCa!U6)(IXO82y6^hIa=YtE$~!o>VNRCyy<{Y)YFe*19Us?jw(+FNoo<X0>~(NGgbJAR3qDGNA?iY?=kN-I6Xa0ENN^~u(G$bw3;|@ z4$)yrt+Ulep$KLlf)mA3XZX;g$%JutbAx^@ar6$T3~JbEm-)?yT?cxZL?y)7Dokn< z!CthfCcv|pKLVk+n$2ZxoE@)LGBqaR1xVTXE_xH~|e+mk3ou{Q_#2BEHATGx) z8`|~uJGzu18nFb5gs_l9RHkakDet9^rO|r(r`{HSBJTiVyZ`)-rc#@Q{TH=FP@o#r zDDVzu2A$HhQo?KrMMXhd&xvnO<0X0nZqpF7uW*TO5m|k^>Oem^DNq>SNfsrKI-3;~ zWU;5Rx7i!C`c7Eyzfgiq+0`eN6^L-oWFVHfD)@lWS)?uv4y0^=tYm^xOdKB@P-xRR zV@)0J0@E0LiD=DMm%rCki^5`ifrcr;$OF&Y5xit2L)3=xn(v1mCTi5|!vOh!CXj>` zkQc0SvN4?bEIT3r&=KLiGwY`gO*dP~VJ-weQ8Fz?( z_j&2ZHG&8Te*mb9@X9dG{^f;y(S9X#A2fe7lUJeVb{FLzm77wDzdhs4_8M^+On_-( zsS6IzJE&vAOULP_q7gN4^5md}0vpNU{hjIpzm42Kzh=KR{_^(fw{7Z+g=xzjxR zIQ?>w`!+r-g^*|0vsc^%weEm%+A*UYh{H6588Xh91G%IE-xy(jc*y;JjC1ufqU%0V z8X)d9QQi=(A}MUjU2F$cjl)h9mmnhKtf+mu@?ekzZT3yn_*nHmIp80Jfa)f=IJ`{@ z&@sE}8aO;wt*}aZqB4oCt}{eT7$!>Oz3=w!{#51&8fDf5^Z+lcCZ{$)mGO7qAvDfaNxR=DZVFHxSSpUXo zG~OQK-X5bH7-YZ9Y27_HYly!so9}Fu7Oh`WLDTGJhNm?>S2AjnWfPYxo-b7(<%#^@ z4^NVmvTHp9@c#1yh_N;-c_16V-5s*Gq1^B+;s%Cwmw63@lU+FkLBV^ONc9SYg-97x zbZt0FQEHO?{FmrF7Xz$#p(Qnk5d-xd6c*^f+Oo^ogS(#h(SI?XHI);cGzSxBg5mKa zNuGZ<^kid$A@s3I5D`K)Sg7f+uFol^kmFQH_e%G&Bia2<8)xU-LKv`&aA@M`8u@Mb z_dJCN1=Mu^X!~03ZMsBJa4}p^P?bJ)X6Z5Z`yTc&t}6NKwRa_`2=$QCVe%-||L~K( z--h?r`sH|>j>oj_MteeOOKM?F5TXR$`;{Lv#pmZ;B1QWLPRFkx0Dvm+_*RjLyz#~% z1O7YO0(aHFJ6D>H_L`>W(Mw_aFV1vN?p!B3T^=4}_q6f|pt)!GYNQ^fC0KltYD`Cm z_I=bk8Iqr?MF=1-Wmoo{TlFVDl}J%QAHc&_7P<2%jUf}h;$^SIFaRCg66}{f$Mg0h z=L0>nzpz0_VjtILoWx2r#8{P^=9r^DFId5_F&Kt(rC};&yx8aVY-TK!Nvbo{7Aq5o zHCwLZnUT(xw+o4b`e^4uJw2bkpT8WnY`8|ax!)1~;qIEj_|h#I{b0J1c{_GCvVL9zppOSa-fx>c zZ%4;lg2U4%X_#nt0PE}HtVWENA1rylG-m%mLM3(eDTop^jr7O4;7i{Li*9l*&T-I_ zjp+B#KTBDNKXGOjiGrvVp8I70^i6~L#&e&ef|FJAVD(ag@gKBuBU}?Xa0Xx#rVFsR zEeQF2`Xvp%kC&x(z5Q5|w{`oQRA9ozk59*?=&bWbUXD;vm#>_xIqc(|H~ zX27Da=Dhx5?pVnA%s?x8=?w-$VzdmAHxn)0R8b1F}Z$XchZ0O<3`K9 zJihl>QM)*jHpdgSZJr3=1&om)0QAO@cu>jBrRKzS=SjpbDHiM9x{(FkLvA7_9|*CI zw1?>KN~&%gsUd6zzIl#gzt8o2a^`2uL&mtI$0rjoAQDA$KPCy`M09C+q4S>h+BPml_Gl6F2B-@BFYph8xtfwU{uJtDL0Xt}?%!CilboV01$ zN&mDrayPKoQ^O^oocUHQS)-AU)MsQQsdj;1suhebL$$2Q5?;xuzZjmeALM$}FZL3< z$R$`KiUdb;Bh-okK>#Ayx`-VRmykkAXnu@Y=Gsn^ruj|XjMd(WXykDo0kg?f`5gy8XqxuR?c|g9n zm?mi@uxCg~h(JfM#Gc~Cm0kT!Pm(&GcJj-4w!(NcyOcu|1?8xZL6Bk15)WFi5Tt%# zMO4D&V_}y5i?tt^(GU#z7p2(3Lpcz=6Ug?BJSBE3h=_TvzlN;yRowsJb$h`dQR=_K z-{v9bRzgGDMwsL3Frfx$f8iRxXvPqeen`ehjJ7giM7^bkoDHe*Lwj7-B@a{(KrCvW_`bF!T%z@nlsg!|7q|7 zX4ucuRf+Xlyhes!f7~TqWI1ug3~ZML;!-rV%aFxOiGb+R$P-rymb*0K0Q`s#^$kj& zxIq>Ud@Axp#j zjsHHU&mK+cW*I6=-V1KiZ^oWH{qC1S;Xe<5KY#kHl*!+=)MB4^9-}XWevQFE)2F5f zQWm&yTAW|a(RW*(z+IA#n(fgTvzQDDDwu5@Z;#_9K?l$Pm%H$zDo(Bj>{?wj zih$Qs^3qfB@BuT=tUKIM<>>}qYTc*a8pYQ=+YPV3i~ef9dv5WegC}R_8`E8EaYl*R+-4HWHL6^pLsnJlVsN=D$Do zpfGRaz1rZ7Y3Fsl+JiVI`P@_b7ElNa0St70-z+tMOwI28OH9645cAK*TmK_iv*qeU z|1JMX>FqlG5{NIN%4(|D9I>IjK0EnRdwJ&{dA7oxK$Vf<)Yab~=G%34@T>TD{O7IE zGgzV?kEei`??e~LrhMO_%rCpc{@Mu8C=Iv`fdiwb!eP>Wt0z*g#Z&$56igjS*f4eW zjFnX)p1(`8T@!A!3h+q?|4bSeAU-5vXLEHShYc-4SBpJsj}uvDc@(HNC-_E7KwH1v zsUQEGiW~%e+W+yjd;0AqM4OfGd}+$GdA_AV)$?{gR{+UoeSD--VG_=+D~_on4Q-a)XhHNtVJ94i?n`#yoJH!R@TgQAHR^1+3WSF1hFonqdB9m-Y z>9W4mX#N!@z^a<86W4T1JHL^>}6f;db6$vAE_tM*{v@9&7Qe z4gmP(|NAe%ucEir_2l8N|LMM+x1D!==g{UrMurPFM4XLurPn%9A6$6+w(9qMZOm9~ zQ#TxL{rRfN4|$ICn=be1^Ei_}7imxu zT!-&ly8Qmy)6}1-w!19n=|a04irF59f!)!KaRC>LX&%TmJHYsV0r3?M>fHt*6vgE<47~wIXa1PRtD&Mfko&J}q zs&|_PD3dUPxX|rsI~FLjX36W{dB+%*RL4Nz*H_`mt7amrr3+?EWogY|$EO>1j6X_w z2H;kP^I7ikOztca8M(|EBZ!0`GR>w~5?!C=XU^p77A@lZW90J-#R;o`9z+9yPYV)X z_U#*xmH_bFd3@$uD|nS2qur&&^qrpH`s!2x0AnWe@-z>woBuoUB7j6f5Mv5j(!p@DV7ub z$!X{DlQ-Og>(0Fxdd3Kvfo_LNekojdTKXs>0Lb$$=6lYcL3ds_gY^6V{U`TeWVMf@o)Kg{c!_T=@~4kgg= z$kr8imH&CvnKMpH&vm8M7wj$bS3JKNP*e9E0)U)z2cKON#)8RTX0xIh!kP)^{)3%U z0W|c(XK&e~^w*GLFeSjTJc}>9oNv2wCcXw6;$fD;lMC!USU9DJCV3Sw0B2V`KIBv1nQ;k3jK-pM;KU{Vll%`H_OqJ{&4qpG+h6(-fndlGs z^9SX{xg+`AdGq9{1JDi`hGGKrUj9sheDhE2jX3b&sb7U`2tx@Fla>i8Ew3D%py~Nk z0Pt1c;hAS&iLri$*=Vq6HkmaK@xJ{Bh9^BM84LhFy`Ac6w$|_RRSMzBWtlzRWJ4FC za(h_=P~Cx}Z#+#Y^+T%awrZNwscBZtYRgI|iU-d)V?tFDodSSwp3UF*(&acmY==>= zlhSN5)O12^y)SowK9YCe$LFr!N~>1p|yr>M$-_p2L9HY|7#F#5`vkyr>x(Kdy94P%lOP)`S7hYml0EjxF<>`Q%E zX$Q}|@IvAiliA`WVgN>n5ec-WCATN3kFex<&R(ztZ{PcKcE_V+IJ|To{=t_&o(KkW z0wf0C`N}eO%=!^EC;cV?pt6&{bwsDGG!Oq%IiKMk z%VgA$usfMa2&&jj1a8rj>+J(}q6#kF@RMc58^&gluvu7^H3|`w%=Rdb9FV^1(ShQP zvu8h6AcU5-c=5em=yv-i!vtW9yZ9^KDS)UaVYM-nAPQ6ikc|nu{}Dm5Pc6m58}Gy^ zojT&WWG${3qyO-V&;9W_>N`{l2&|J7GzvY2K7=;#pH zSz}H1^*A z&8aFa<*Kshooz)xNhA8Wd$Ddi?l|X4aAgSa6Ua|2g;V35I}T( z&3t(@6aSk%21b1iTeJ)k<+U{vduVzGiUhLTs^+ix?t6(*gM=eZ5w>=t@2yhm=C0H4U$>_}zE7Rbk6m*kCdUQh zol(+YGe^vu*P8e6!FR0n`{_dJRng&F5AskMY)(#ww<`DqQ(sD5_ zZ8*H?qglpcMADRO>O`^5uDBq7a20*(jFn9X5z}20ApP3>W5R7&XL&|Z> zG!4J1X4r({ivauUNVG*1)tv@V0}iju1iGY*muOb6L(`lN&0@1!ZCUAHJve2`*wh$8 z0RZP+!1GQSBi}>!>r#NeMK=ME4?ilWjXV>xM@@m*9Dy2ASZZ#lo3^YUH9*Ha#HYB(lcn=I}{|salDA6_riWY&=@< z%%J0#UM$oP2mt?bt~^U0jbCL=1GjXNFch$^kSN;d>%8bp_iJV4o0G(8eY=zv7qc@5 zP^@5~O4-(!n>&6(Fj(A&+qBJRnii5$w@Wj^rV(xh^uPIhpHw6f2>^2Hnf%B_SMV<< zOrlA{a~}D;xM*$x4yy#D&~rq6^w*c%gtJ<8NM8^lrbroRQE31AcRmP(J}X4u5`g-y z)8}XDh)6T)lxCEq83{56w(I)d1F5theE3AHrNqpY@vlvjY|%miMcKA28Yx))n^hLA zVYgw_*BoR24+>5+B+T4#kgphj3jr=N({osrk`e1NA4f3(KwkY_zSfq5N2g4qXjv(=K$tE< z_d9g^f&}1jP30MB6Ny9hEIcE?LL<@{+4vk>7=v=qY2Y;>PC?O@bk}e-pUq?_KaqZ#feN0LrS7pSIi{x$9jgI zQ_HPf#W~MBjjPtLr{(B7!Y8fCK=Cc<-+}-)0%<5Ejn}z&+feR__v|w|@G$YEl1@2}S_;g%@S+qrb(c zYu1^c-FTCJISz~8WZwDN*Kq&0W+FGpkReqn>;T$2eZz4$5koQ<0B*U38me|io6Hzd zX1;D_kB=J`JCVPl#&61+)HiPv0KBoBvmctrYxBnA#Y--ecO>!97ytcY>C#^WnB&_F zLotPtnJ$DkT~X4kQnnUVRQ#pC(6%ANiR=LY0D7#|TC%Mmz47yLS@j3)+Wz-CLtBvY{xsw-y6AZXg&1TEc*4SS?zA;;=sm0P^fhc-|$~V7jhw ztc%Ig5#cZO{GQOi3IKES0lsAH#mMSPBh#MA!s247M*zbJ8FjUlsr!#!+Z>K6dy?LXtQ zbEe{sai_!R&{l%4HqOnQcV{0W|vmWKx>ei>$Kj8&NilGb?U zoZCM=-s55_juw}2QgFf(pssFpt(4*QhThp~8ip|R7{y}^mFelZ+i`rC8W;f9@4!PB zUJI?d3urPytqho|bp80|R}zstz0Mnt8rWUFMF@u*O4eAz%$a0zI3Pq<{hlhJ^lm`= z8lX;=#tuDBrck^YLh*Vu&E=4?wX~t({kr6GZ-0opF8Lnp^)axINwk`_fBqtqzCHY$C~9x4v8-2ow)|SLg3R0Qe8j$Wg0Gc?NQc1LZ6z zouDg#sX*-Ab1>Nj%mC2d^6PDDe{=drsy^%TaTlmcmtR%ZKUq`(=TF^>R6~=Fbj|lfr`>_8YOG|DV0rwc}$!*=0?kr9F_O=cqo#Dbj>X?7+jR0IGHhQm3rw#U}3 zd|+d#GkfyLXs{!!hqsgh!Jwg2EQy}ceJ?-xS{8>wTN8RX^%5%q;Gf;arYDV97+8IfnG)GH2!+ro}Ef*iz#s z>P(I~WB@R9jwq@_C9$`@vw7LZczER=MT>z*4Zi#C{6rcS{=qE%_UY63(A8JVpY+s` zdiqIKRSW=GUhWFD3wC-;&d4)lX1-zA+>(PW4Xw;gC*5c>6d3FY&|m%<0f5I&kblS> z$!pQQgHA1nisIjI-u!$;UFkN}s@a9Gxhz`6Hbr#RzQ0?s^rofkonV_bJy&NMT?IrA zo1$iBD5}?E8q#Fe3eb~N=Y}5{7dLd^&HEqaF>N8b4c&u(v_MP-fL}Z;3irPA{znCC zR5cb<6_ysQbfY3FxBq#oYKffmBim45utUQ7f&##tcsf6R*_F7148Y$YuX<~_SdPO{ zu?zqWOus>qw2@?(*>fsuH{n#5}V`iXzlt)3PlcO|Ne5u2u(;-D0y@{Ly~{88Fmg;r#6LJTjo+ zsTtpeS?gy7PN>aoovF5Z1^`VHH7u#kYV&4j7D<{#QLR?5hdEyR$tQp8FGnG8c-NlD zO2YU4oX?&7Rk|TP52oK>rJW^P(*>_xw|R6@nE~~u<~Hhi^IKn=GoLB-;d0yUNh*jM~b&qumTjh6Kts z#A8kC^tiuPkNbV`SpELY%#j}>CD*{eDuz-8$X8#H*-Jj-A7o4-E-rx*Rta_$Fa@Vw z|Gmdt%intN2oGN{xTz9A;}3rDa3%nHe0J-@9&zSs8a&_5ZII>wuGg#^o za4ZtjeLkNZeY9V-$k z+5fFDR4RY~ARG}BougQ$k6?CZciZX}&E!aDV?b5oN7KOr*%bN%1E5=fNgFO||t0cL%nfCZ>J`7&gu@NnzCPRYk02 zMIowrukM&O?b511AQ(_pON*+az%UMM!N1A*eXwvcE+Ja&_VXsk<1`9j xcDvQG_V$gX`0on*FaC@F98f=h{}*2{{vWkWBnD}T^m70J002ovPDHLkV1n(cR|)_C literal 0 HcmV?d00001 diff --git a/src/assets/weather/66.png b/src/assets/weather/66.png new file mode 100644 index 0000000000000000000000000000000000000000..74d008777f39db9d66b9257e8371f0c186ab5628 GIT binary patch literal 27684 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∓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 literal 0 HcmV?d00001 diff --git a/src/assets/weather/67.png b/src/assets/weather/67.png new file mode 100644 index 0000000000000000000000000000000000000000..2c4746d9bd6100e5f29f209a4d943c9206dae7bc GIT binary patch literal 15474 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 Ye|tgeCR6nGB>(^b07*qoM6N<$f(@Dw>Hq)$ literal 0 HcmV?d00001 diff --git a/src/assets/weather/7.png b/src/assets/weather/7.png new file mode 100644 index 0000000000000000000000000000000000000000..a00cbff42a3ff3dc129032fbea45679d864f23a6 GIT binary patch literal 10363 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 egB_?#C$ + + + +
文件名网络图标本地图标
+ + \ No newline at end of file diff --git a/src/auth-pam.cpp b/src/auth-pam.cpp new file mode 100644 index 0000000..aa01e1c --- /dev/null +++ b/src/auth-pam.cpp @@ -0,0 +1,352 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#include "auth-pam.h" + +#include + +#include +#include +#include +#define PAM_SERVICE_NAME "ukui-screensaver-qt" + +//通信管道的文件描述符 +int toParent[2], toChild[2]; +void sigchld_handler(int signo); +static void writeData(int fd, const void *buf, ssize_t count); +static void writeString(int fd, const char *data); +static int readData(int fd, void *buf, size_t count); +static char * readString(int fd); +static int pam_conversation(int msgLength, const struct pam_message **msg, + PAM_RESPONSE **resp, void *appData); +void sigchld_handler(int signo); +AuthPAM::AuthPAM(QObject *parent) + : Auth(parent), + pid(0), + nPrompts(0), + _isAuthenticated(false), + _isAuthenticating(false) +{ +// signal(SIGCHLD, sigchld_handler); +} + +void AuthPAM::authenticate(const QString &userName) +{ + stopAuth(); + + if(pipe(toParent) || pipe(toChild)) + qDebug()<< "create pipe failed: " << strerror(errno); + if((pid = fork()) < 0) + { + qDebug() << "fork error: " << strerror(errno); + } + else if(pid == 0) + { + prctl(PR_SET_PDEATHSIG, SIGHUP); + close(toParent[0]); + close(toChild[1]); + int arg1_int = toParent[1]; + int arg2_int = toChild[0]; + char arg1[128]; + char arg2[128]; + sprintf(arg1,"%d",arg1_int); + sprintf(arg2,"%d",arg2_int); + execlp ("ukui-screensaver-checkpass", + "ukui-screensaver-checkpass", + arg1, arg2,userName.toLocal8Bit().data(), NULL); + _exit (EXIT_FAILURE); + } + else + { + close(toParent[1]); + close(toChild[0]); + _isAuthenticating = true; + notifier = new QSocketNotifier(toParent[0], QSocketNotifier::Read); + connect(notifier, &QSocketNotifier::activated, this, &AuthPAM::onSockRead); + } +} + +void AuthPAM::stopAuth() +{ + if(pid != 0) + { + messageList.clear(); + responseList.clear(); + _isAuthenticating = false; + _isAuthenticated = false; + nPrompts = 0; + ::kill(pid, SIGKILL); + + close(toParent[0]); + close(toChild[1]); + if(notifier){ + notifier->deleteLater(); + notifier = nullptr; + } + pid = 0; + } +} + +void AuthPAM::respond(const QString &response) +{ + nPrompts--; + responseList.push_back(response); + +// for(auto msg : messageList) +// qDebug() << msg.msg; +// qDebug() << responseList; +// qDebug() << nPrompts; + + if(nPrompts == 0) + { + //发送响应到子进程 + int j = 0; + PAM_RESPONSE *resp = (PAM_RESPONSE*)calloc(messageList.size(), sizeof(PAM_RESPONSE)); + //响应的数量和消息的数量一致,如果消息类型不是PROMPT,则响应是空的 + for(int i = 0; i < messageList.size(); i++) + { + struct pam_message message = messageList[i]; + PAM_RESPONSE *r = &resp[i]; + if(message.msg_style == PAM_PROMPT_ECHO_OFF + || message.msg_style == PAM_PROMPT_ECHO_ON) + { + int respLength = responseList[j].length() + 1; + r->resp = (char *)malloc(sizeof(char) * respLength); + memcpy(r->resp, responseList[j].toLocal8Bit().data(), respLength); + j++; + } + } + _respond(resp); + free(resp); + messageList.clear(); + responseList.clear(); + } +} + +bool AuthPAM::isAuthenticated() +{ + return _isAuthenticated; +} + +bool AuthPAM::isAuthenticating() +{ + return _isAuthenticating; +} + + +void AuthPAM::onSockRead() +{ +// qDebug() << "has message"; + int msgLength; + int authComplete = 0; + readData(toParent[0], &authComplete, sizeof(authComplete)); + + if(authComplete) + { + int authRet = -1; + if(readData(toParent[0], (void*)&authRet, sizeof(authRet)) <= 0) + qDebug() << "get authentication result failed: " << strerror(errno); + qDebug() << "result: " << authRet; + _isAuthenticated = (authRet == PAM_SUCCESS); + _isAuthenticating = false; + if(notifier){ + notifier->deleteLater(); + notifier = nullptr; + } + Q_EMIT authenticateComplete(); + + } + else + { + readData(toParent[0], &msgLength, sizeof(msgLength)); +// qDebug() << "message length: " << msgLength; + + for(int i = 0; i < msgLength; i++) + { + //读取message + struct pam_message message; + readData(toParent[0], &message.msg_style, sizeof(message.msg_style)); + message.msg = readString(toParent[0]); + + qDebug() << message.msg; + + messageList.push_back(message); + + switch (message.msg_style) + { + case PAM_PROMPT_ECHO_OFF: + nPrompts++; + Q_EMIT showPrompt(message.msg, Auth::PromptTypeSecret); + break; + case PAM_PROMPT_ECHO_ON: + nPrompts++; + Q_EMIT showPrompt(message.msg, Auth::PromptTypeQuestion); + break; + case PAM_ERROR_MSG: + Q_EMIT showMessage(message.msg, Auth::MessageTypeInfo); + break; + case PAM_TEXT_INFO: + Q_EMIT showMessage(message.msg, Auth::MessageTypeError); + break; + } + } + + if(nPrompts == 0) + { + //不需要响应,发送一个空的 + PAM_RESPONSE *response = (PAM_RESPONSE*)calloc(messageList.size(), sizeof(PAM_RESPONSE)); + _respond(response); + free(response); + messageList.clear(); + } + } +} + +static void +writeData(int fd, const void *buf, ssize_t count) +{ + if(write(fd, buf, count) != count) + qDebug() << "write to parent failed: " << strerror(errno); +} + +static void +writeString(int fd, const char *data) +{ + int length = data ? strlen(data) : -1; + writeData(fd, &length, sizeof(length)); + if(data) + writeData(fd, data, sizeof(char) * length); +} + +static int +readData(int fd, void *buf, size_t count) +{ + ssize_t nRead = read(fd, buf, count); + if(nRead < 0) + qDebug() << "read data failed: " << strerror(errno); + return nRead; +} + +static char * +readString(int fd) +{ + int length; + + if(readData(fd, &length, sizeof(length)) <= 0) + return NULL; + if(length <= 0) + length = 0; + + char *value = (char *)malloc(sizeof(char) * (length + 1)); + readData(fd, value, length); + value[length] = '\0'; + + return value; +} + +void AuthPAM::_authenticate(const char *userName) +{ + qDebug() << "authenticate " << userName; + + pam_handle_t *pamh = NULL; + char *newUser; + int ret; + int authRet; + struct pam_conv conv; + + conv.conv = pam_conversation; + conv.appdata_ptr = NULL; + + ret = pam_start(PAM_SERVICE_NAME, userName, &conv, &pamh); + if(ret != PAM_SUCCESS) + { + qDebug() << "failed to start PAM: " << pam_strerror(NULL, ret); + } + + authRet = pam_authenticate(pamh, 0); + + ret = pam_get_item(pamh, PAM_USER, (const void **)&newUser); + if(ret != PAM_SUCCESS) + { + pam_end(pamh, 0); + qDebug() << "failed to get username"; + } + if(authRet == PAM_SUCCESS) + ret = pam_acct_mgmt(pamh, 0); + + if(ret != PAM_SUCCESS) + { + qDebug() << "failed to acct mgmt " << pam_strerror(NULL, ret); + } + + free(newUser); + fprintf(stderr, "authentication result: %d\n", authRet); + + // 发送认证结果 + int authComplete = 1; + writeData(toParent[1], (const void*)&authComplete, sizeof(authComplete)); + writeData(toParent[1], (const void *)&authRet, sizeof(authRet)); + qDebug() << "--- 认证完成"; + _exit(EXIT_SUCCESS); +} + +void AuthPAM::_respond(const PAM_RESPONSE *response) +{ + for(int i = 0; i < messageList.size(); i++) + { + const PAM_RESPONSE *resp = &response[i]; + writeData(toChild[1], (const void *)&resp->resp_retcode, + sizeof(resp->resp_retcode)); + writeString(toChild[1], resp->resp); + } +} + + +static int +pam_conversation(int msgLength, const struct pam_message **msg, + PAM_RESPONSE **resp, void */*appData*/) +{ + PAM_RESPONSE *response = (PAM_RESPONSE*)calloc(msgLength,sizeof(PAM_RESPONSE)); + + int authComplete = 0; + writeData(toParent[1], (const void*)&authComplete, sizeof(authComplete)); + writeData(toParent[1], (const void*)&msgLength, sizeof(msgLength)); + //发送pam消息 + for(int i = 0; i < msgLength; i++) + { + const struct pam_message *m = msg[i]; + writeData(toParent[1], (const void *)&m->msg_style, sizeof(m->msg_style)); + writeString(toParent[1], m->msg); + } + //读取响应 + for(int i = 0; i < msgLength; i++) + { + PAM_RESPONSE *r = &response[i]; + readData(toChild[0], &r->resp_retcode, sizeof(r->resp_retcode)); + r->resp = readString(toChild[0]); + } + *resp = response; + return PAM_SUCCESS; +} + +void sigchld_handler(int signo) +{ + if(signo == SIGCHLD) + { + ::waitpid(-1, NULL, WNOHANG); + } +} diff --git a/src/auth-pam.h b/src/auth-pam.h new file mode 100644 index 0000000..3fba36c --- /dev/null +++ b/src/auth-pam.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#ifndef AUTHPAM_H +#define AUTHPAM_H +#include "auth.h" +#include +#include + +#include + +typedef struct pam_message PAM_MESSAGE; +typedef struct pam_response PAM_RESPONSE; + +class AuthPAM : public Auth +{ + Q_OBJECT +public: + AuthPAM(QObject *parent = nullptr); + + void authenticate(const QString &userName); + void stopAuth(); + void respond(const QString &response); + bool isAuthenticated(); + bool isAuthenticating(); + +private: + void _authenticate(const char *userName); + void _respond(const struct pam_response *response); + +private Q_SLOTS: + void onSockRead(); + +private: + QString userName; + pid_t pid; + QSocketNotifier *notifier; + int nPrompts; + QStringList responseList; + QList messageList; + bool _isAuthenticated; //认证结果 + bool _isAuthenticating; +}; + +#endif // AUTHPAM_H diff --git a/src/auth.h b/src/auth.h new file mode 100644 index 0000000..b2f8193 --- /dev/null +++ b/src/auth.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#ifndef AUTH_H +#define AUTH_H + +#ifndef QT_NO_KEYWORDS +#define QT_NO_KEYWORDS +#endif + +#include + +class Auth : public QObject +{ + Q_OBJECT + + Q_ENUMS(PromptType MessageType) +public: + explicit Auth(QObject *parent = nullptr) + : QObject(parent) + { + + } + + enum PromptType { + PromptTypeQuestion, + PromptTypeSecret + }; + enum MessageType { + MessageTypeInfo, + MessageTypeError + }; + + +Q_SIGNALS: + void showPrompt(const QString &prompt, Auth::PromptType type); + void showMessage(const QString &message, Auth::MessageType type); + void authenticateComplete(); + +public: + virtual void authenticate(const QString &userName) = 0; + virtual void stopAuth() = 0; + virtual void respond(const QString &response) = 0; + virtual bool isAuthenticating() = 0; + virtual bool isAuthenticated() = 0; +}; + +#endif // AUTH_H diff --git a/src/authdialog.cpp b/src/authdialog.cpp new file mode 100644 index 0000000..4852dcc --- /dev/null +++ b/src/authdialog.cpp @@ -0,0 +1,1078 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#include "authdialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "users.h" +#include "iconedit.h" +#include "biometricproxy.h" +#include "biometricauthwidget.h" +#include "biometricdeviceswidget.h" +#include "pam-tally.h" +#include "commonfunc.h" +#include "loginoptionswidget.h" + +AuthDialog::AuthDialog(const UserItem &user, QWidget *parent) : + QWidget(parent), + user(user), + auth(new AuthPAM(this)), + authMode(UNKNOWN), + m_deviceCount(-1), + m_biometricProxy(nullptr), + m_widgetLoginOpts(nullptr), + m_buttonsWidget(nullptr), + m_retryButton(nullptr), + isBioPassed(false), + m_bioTimer(nullptr), + m_timer(nullptr), + isLockingFlg(false), + m_nCurLockMin(0), + useFirstDevice(false) +{ + initUI(); + pam_tally_init(); //这里写函数声明 + + connect(auth, &Auth::showMessage, this, &AuthDialog::onShowMessage); + connect(auth, &Auth::showPrompt, this, &AuthDialog::onShowPrompt); + connect(auth, &Auth::authenticateComplete, this, &AuthDialog::onAuthComplete); + + useFirstDevice = getUseFirstDevice(); + m_failedTimes.clear(); +} + +void AuthDialog::startAuth() +{ + prompted = false; + unacknowledged_messages = false; + auth->authenticate(user.name); + + m_passwordEdit->clear(); + m_passwordEdit->readOnly(true); +} + +void AuthDialog::stopAuth() +{ + if(m_widgetLoginOpts) + { + if(m_bioTimer && m_bioTimer->isActive()) + m_bioTimer->stop(); + m_widgetLoginOpts->stopAuth(); + + m_widgetLoginOpts->hide(); + } + + clearMessage(); + // auth->stopAuth(); + m_passwordEdit->readOnly(true); +// if(m_passwdWidget) +// m_passwdWidget->hide(); +} + +QPixmap AuthDialog::PixmapToRound(const QPixmap &src, int radius) +{ + if (src.isNull()) { + return QPixmap(); + } + + QPixmap pixmapa(src); + QPixmap pixmap(radius*2,radius*2); + pixmap.fill(Qt::transparent); + QPainter painter(&pixmap); + painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + QPainterPath path; + path.addEllipse(0, 0, radius*2, radius*2); + painter.setClipPath(path); + painter.drawPixmap(0, 0, radius*2, radius*2, pixmapa); + return pixmap; +} + +void AuthDialog::initUI() +{ + if (scale < 0.5) { + setFixedWidth(360); + } else { + setFixedWidth(600); + } + + m_userWidget = new QWidget(this); + m_userWidget->setObjectName(QStringLiteral("userWidget")); + + /* 头像 */ + const QString SheetStyle = QString("border-radius: %1px; border:0px solid white;").arg(77*scale); + m_labelHeadImg = new QLabel(m_userWidget); + m_labelHeadImg->setObjectName(QStringLiteral("faceLabel")); + m_labelHeadImg->setFocusPolicy(Qt::NoFocus); + m_labelHeadImg->setStyleSheet(SheetStyle); + m_labelHeadImg->setAlignment(Qt::AlignCenter); + m_labelHeadImg->hide(); + + QPixmap facePixmap(user.icon); + facePixmap = facePixmap.scaled(154*scale,154*scale, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + facePixmap = PixmapToRound(facePixmap, 77*scale); + m_labelHeadImg->setAlignment(Qt::AlignCenter); + m_labelHeadImg->setPixmap(facePixmap); + + // 人脸识别 + m_labelFace = new QLabel(m_userWidget); + m_labelFace->setObjectName("faceLabel"); + m_labelFace->setAlignment(Qt::AlignCenter); + m_labelFace->hide(); + + // 二维码窗口 + m_labelLoginTypeTip = new QLabel(m_userWidget); + //m_labelLoginTypeTip->setStyleSheet("QToolTip{border-radius:4px;background-color:#FFFFFF;color:black;font-size:16px}"); + m_labelLoginTypeTip->setObjectName("loginTypeTipLabel"); + m_labelLoginTypeTip->setAlignment(Qt::AlignCenter); + QFont font = m_labelLoginTypeTip->font(); + font.setPixelSize(14); + m_labelLoginTypeTip->setFont(font); + m_labelQRCode = new QLabel(m_userWidget); + m_labelQRCode->setObjectName("qrCodeLabel"); + m_labelQRCode->setAlignment(Qt::AlignCenter); + m_labelQRCode->hide(); + QVBoxLayout *layoutQRCode = new QVBoxLayout(); + layoutQRCode->setAlignment(Qt::AlignCenter); + layoutQRCode->setSpacing(10); + m_labelQRCode->setLayout(layoutQRCode); + m_labelQRCodeTip = new QLabel(); + m_labelQRCodeTip->setFixedSize(22,22); + m_labelQRCodeTip->setPixmap(QIcon::fromTheme("ukui-dialog-warning").pixmap(QSize(22,22))); + layoutQRCode->addWidget(m_labelQRCodeTip, 0, Qt::AlignHCenter); + m_labelQRCodeMsg = new QLabel(); + m_labelQRCodeMsg->setFixedHeight(24); + font = m_labelQRCodeMsg->font(); + font.setPixelSize(16); + m_labelQRCodeMsg->setStyleSheet("QLabel{background-color:rgba(255,255,255,0);color:rgb(255,0,0)}"); + m_labelQRCodeMsg->setFont(font); + layoutQRCode->addWidget(m_labelQRCodeMsg, 0, Qt::AlignHCenter); + + /* 用户名 */ + m_nameLabel = new QLabel(m_userWidget); + m_nameLabel->setObjectName(QStringLiteral("login_nameLabel")); + m_nameLabel->setFocusPolicy(Qt::NoFocus); + m_nameLabel->setAlignment(Qt::AlignCenter); + m_nameLabel->setStyleSheet("QToolTip{border-radius:4px;background-color:#FFFFFF;color:black;font-size:16px}"); + //m_nameLabel->setText(user.realName.isEmpty() ? user.name : user.realName); + QString text = user.realName.isEmpty() ? user.name : user.realName; + font = m_nameLabel->font(); + font.setPixelSize(24); + QString str = ElideText(font,500,text); + if(text != str) + m_nameLabel->setToolTip(text); + if(text == str) + m_nameLabel->setToolTip(""); + m_nameLabel->setText(str); + + + /* 密码框所在窗口 */ + m_passwdWidget = new QWidget(this); + m_passwdWidget->setObjectName(QStringLiteral("passwordWidget")); + + /* 密码框 */ + m_passwordEdit = new IconEdit(m_passwdWidget); + connect(m_passwordEdit,&IconEdit::clickedPassword, + this,&AuthDialog::onM_passwordEditClicked); + + m_passwdWidget->setInputMethodHints(Qt::ImhNone); + m_passwordEdit->setObjectName(QStringLiteral("passwordEdit")); + m_passwordEdit->setIcon(QIcon(":/image/assets/login-button.svg")); + m_passwordEdit->setFocusPolicy(Qt::StrongFocus); + m_passwordEdit->installEventFilter(this); + m_passwordEdit->readOnly(true); + m_passwordEdit->setType(QLineEdit::Password); + setFocusProxy(m_passwordEdit); + connect(m_passwordEdit, SIGNAL(clicked(const QString&)), + this, SLOT(onRespond(const QString&))); + + //m_passwdWidget->hide(); + + /* 密码认证信息显示列表 */ + m_messageLabel = new QLabel(m_passwdWidget); + m_messageLabel->setObjectName(QStringLiteral("messageLabel")); + m_messageLabel->setAlignment(Qt::AlignLeft); + font = m_messageLabel->font(); + font.setPixelSize(14); + m_messageLabel->setFont(font); + m_messageLabel->setStyleSheet("QToolTip{border-radius:4px;background-color:#FFFFFF;color:black;font-size:16px}"); + + m_messageButton = new QPushButton(m_passwdWidget); + m_messageButton->setObjectName(QStringLiteral("messageButton")); + m_messageButton->hide(); +} + +void AuthDialog::unlock_countdown() +{ + int failed_count = 0; + int time_left = 0; + int deny = 0; + int fail_time =0; + int unlock_time = 0; + pam_tally_unlock_time_left(user.uid, &failed_count, &time_left, &deny,&fail_time,&unlock_time); + + // qDebug() << "failed_count:" << failed_count << "time_left:" <= 60)//请多少分钟后重试 + { + int nMinuteleft = time_left/60; + if (isLockingFlg == false){ + m_nCurLockMin = unlock_time/60;//获取当前需要锁定的分钟数 + } + + //如果当前设置的不是1min钟锁定,那么1min显示成2min,由2min直接跳到59s || 剩余分钟数小于当前设置的锁定时间,并且大于1min,自增+1 + if ((nMinuteleft == 1 && m_nCurLockMin != 1) || (nMinuteleft > 1 && nMinuteleft < m_nCurLockMin)) + { + nMinuteleft = nMinuteleft + 1; + } + + m_messageLabel->setText(tr("Please try again in %1 minutes.").arg(nMinuteleft)); + m_messageLabel->setToolTip(tr("Please try again in %1 minutes.").arg(nMinuteleft)); + m_passwordEdit->clearText(); + m_passwordEdit->readOnly(true); + m_passwordEdit->setLocked(true); + isLockingFlg = true; + return ; + } + else if(time_left > 0 && time_left < 60)//请多少秒后重试 + { + m_messageLabel->setText(tr("Please try again in %1 seconds.").arg(time_left%60)); + m_messageLabel->setToolTip(tr("Please try again in %1 seconds.").arg(time_left%60)); + m_passwordEdit->clearText(); + m_passwordEdit->readOnly(true); + m_passwordEdit->setLocked(true); + isLockingFlg = true; + return ; + } + else if (failed_count == 0xFFFF)//账号被永久锁定 + { + m_messageLabel->setText(tr("Account locked permanently.")); + m_messageLabel->setToolTip(tr("Account locked permanently.")); + m_passwordEdit->clearText(); + m_passwordEdit->readOnly(true); + m_passwordEdit->setLocked(true); + isLockingFlg = true; + return ; + } + else + { + if(m_passwordEdit){ + m_passwordEdit->readOnly(false); + m_passwordEdit->setFocus(); + } + if (isLockingFlg) + { + onShowMessage("",Auth::MessageTypeError); + isLockingFlg = false; + m_passwordEdit->setLocked(false); + } + + m_timer->stop(); + } + return ; +} + +void AuthDialog::root_unlock_countdown() +{ + int failed_count = 0; + int time_left = 0; + int deny = 0; + int fail_time =0; + int unlock_time = 0; + pam_tally_root_unlock_time_left(&failed_count, &time_left, &deny,&fail_time,&unlock_time); + if(time_left >= 60)//请多少分钟后重试 + { + int nMinuteleft = time_left/60; + if (isLockingFlg == false){ + m_nCurLockMin = unlock_time/60;//获取当前需要锁定的分钟数 + } + + + //如果当前设置的不是1min钟锁定,那么1min显示成2min,由2min直接跳到59s || 剩余分钟数小于当前设置的锁定时间,并且大于1min,自增+1 + if ((nMinuteleft == 1 && m_nCurLockMin != 1) || (nMinuteleft > 1 && nMinuteleft < m_nCurLockMin)) + { + nMinuteleft = nMinuteleft + 1; + } + + m_messageLabel->setText(tr("Please try again in %1 minutes.").arg(nMinuteleft)); + m_messageLabel->setToolTip(tr("Please try again in %1 minutes.").arg(nMinuteleft)); + m_passwordEdit->clearText(); + m_passwordEdit->readOnly(true); + isLockingFlg = true; + return ; + } + else if(time_left > 0 && time_left < 60)//请多少秒后重试 + { + m_messageLabel->setText(tr("Please try again in %1 seconds.").arg(time_left%60)); + m_messageLabel->setToolTip(tr("Please try again in %1 seconds.").arg(time_left%60)); + m_passwordEdit->clearText(); + m_passwordEdit->readOnly(true); + isLockingFlg = true; + return ; + } + else if (failed_count == 0xFFFF)//账号被永久锁定 + { + m_messageLabel->setText(tr("Account locked permanently.")); + m_messageLabel->setToolTip(tr("Account locked permanently.")); + m_passwordEdit->clearText(); + m_passwordEdit->readOnly(true); + isLockingFlg = true; + return ; + } + else + { + if(m_passwordEdit){ + m_passwordEdit->readOnly(false); + m_passwordEdit->setFocus(); + } + if (isLockingFlg) + { + onShowMessage("",Auth::MessageTypeError); + isLockingFlg = false; + } + + m_timer->stop(); + } + return ; +} + +void AuthDialog::resizeEvent(QResizeEvent *) +{ + setChildrenGeometry(); +} + +void AuthDialog::setChildrenGeometry() +{ + if(scale < 0.5) + setFixedWidth(500); + // 用户信息显示位置 + m_userWidget->setGeometry(0, 0, + width(), 376*scale); + m_labelLoginTypeTip->setGeometry(0,0,m_userWidget->width(), 24); + m_labelLoginTypeTip->setAlignment(Qt::AlignCenter); + m_labelHeadImg->setStyleSheet(QString("border-radius: %1px; border:0px solid white;").arg(77*scale)); + m_labelHeadImg->setGeometry((width() - 154*scale) / 2 , m_labelLoginTypeTip->geometry().bottom()+24*scale, 154*scale, 154*scale); + QPixmap facePixmap(user.icon); + facePixmap = facePixmap.scaled(154*scale,154*scale, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + facePixmap = PixmapToRound(facePixmap, 77*scale); + m_labelHeadImg->setPixmap(facePixmap); + + m_labelQRCode->setStyleSheet(QString("border-radius: %1px; border:0px solid white;background-color: rgba(255,255,255,100%);").arg(6*scale)); + m_labelQRCode->setGeometry((width() - 154*scale) / 2 , m_labelLoginTypeTip->geometry().bottom()+24*scale, 154*scale, 154*scale); + setQRCode(m_imgQRCode); + + m_labelFace->setStyleSheet(QString("border-radius: %1px; border:0px solid white;background-color: rgba(255,255,255,10%);").arg(77*scale)); + m_labelFace->setGeometry((width() - 154*scale) / 2 , m_labelLoginTypeTip->geometry().bottom()+24*scale, 154*scale, 154*scale); + QImage faceImg; + setFaceImg(faceImg); + + m_nameLabel->setGeometry(0, m_labelHeadImg->geometry().bottom() + 16*scale, + width(), 34); + + m_passwdWidget->setGeometry(0, m_nameLabel->geometry().bottom() + 24*scale, width(), 100); + m_passwordEdit->setGeometry((m_passwdWidget->width() - 240)/2, 0, 240, 40); + m_messageLabel->setGeometry((m_passwdWidget->width() - 240)/2, + m_passwordEdit->geometry().bottom() + 4, + width()-(m_passwdWidget->width() - 240)/2, 20); + + m_messageButton->setGeometry((m_passwdWidget->width() - 200)/2, 0, 200, 40); + + setBiometricWidgetGeometry(); +} + +void AuthDialog::switchLoginOptType(unsigned uLoginOptType) +{ + switch(uLoginOptType) { + case LOGINOPT_TYPE_PASSWORD: + { + m_labelHeadImg->show(); + m_labelQRCode->hide(); + m_labelFace->hide(); + } + break; + case LOGINOPT_TYPE_FACE: + { + m_labelHeadImg->hide(); + m_labelQRCode->hide(); + m_labelFace->show(); + } + break; + case LOGINOPT_TYPE_FINGERPRINT: + case LOGINOPT_TYPE_VOICEPRINT: + case LOGINOPT_TYPE_FINGERVEIN: + case LOGINOPT_TYPE_IRIS: + { + m_labelHeadImg->show(); + m_labelQRCode->hide(); + m_labelFace->hide(); + } + break; + case LOGINOPT_TYPE_QRCODE: + { + m_labelHeadImg->hide(); + setQRCodeMsg(""); + m_labelQRCode->show(); + m_labelFace->hide(); + } + break; + default: + return; + } + if (uLoginOptType != m_uCurLoginOptType || (m_deviceInfo && m_deviceInfo->id != m_nLastDeviceId)) { + clearMessage(); + switch(uLoginOptType) { + case LOGINOPT_TYPE_PASSWORD: + { + setLoginTypeTip(""); + } + break; + case LOGINOPT_TYPE_FACE: + { + setLoginTypeTip(tr("Verify face recognition or enter password to unlock")); + } + break; + case LOGINOPT_TYPE_FINGERPRINT: + { + setLoginTypeTip(tr("Press fingerprint or enter password to unlock")); + } + break; + case LOGINOPT_TYPE_VOICEPRINT: + { + setLoginTypeTip(tr("Verify voiceprint or enter password to unlock")); + } + break; + case LOGINOPT_TYPE_FINGERVEIN: + { + setLoginTypeTip(tr("Verify finger vein or enter password to unlock")); + } + break; + case LOGINOPT_TYPE_IRIS: + { + setLoginTypeTip(tr("Verify iris or enter password to unlock")); + } + break; + case LOGINOPT_TYPE_QRCODE: + { + setLoginTypeTip(tr("Use the bound wechat scanning code or enter the password to unlock")); + } + break; + default: + return; + } + } + if (m_deviceInfo) { + m_nLastDeviceId = m_deviceInfo->id; + } else { + m_nLastDeviceId = -1; + } + + m_uCurLoginOptType = uLoginOptType; +} + + +void AuthDialog::closeEvent(QCloseEvent *event) +{ + qDebug() << "AuthDialog::closeEvent"; + + if(auth && auth->isAuthenticating()) + { + auth->stopAuth(); + } + + //调用kylin的接口,重新初始化 + QDBusInterface interface("com.kylin.network", + "/com/kylin/network", + "com.kylin.network", + QDBusConnection::sessionBus()); + QDBusMessage result = interface.call("keyRingInit"); + + return QWidget::closeEvent(event); +} + + +void AuthDialog::onShowMessage(const QString &message, Auth::MessageType type) +{ + qDebug()<setMinimumHeight(36); + if (message.indexOf("account locked") != -1 || message.indexOf("账户已锁定") != -1 + || message.indexOf("Account locked") != -1 || message.indexOf("永久锁定") != -1) + { + if(!m_timer){ + m_timer = new QTimer(this); + m_timer->setInterval(800); + connect(m_timer, &QTimer::timeout, this, &AuthDialog::unlock_countdown); + } + m_timer->start(); + }else if (message.indexOf("No password received, please input password") != -1){ + m_messageLabel->setText(tr("Password cannot be empty")); + m_messageLabel->setToolTip(tr("Password cannot be empty")); + } + else{ + QString strText = message; + if (!m_preStrMessage.isEmpty() && m_preStrMessageType != type && m_preStrMessageType != -1) + { + strText = m_preStrMessage + "," + strText; + } + QFontMetrics font(m_messageLabel->font()); + QString strDisplay = font.elidedText(message, Qt::ElideRight, m_messageLabel->width()-8); + m_messageLabel->setText(strDisplay); + m_messageLabel->setToolTip(message); + } + unacknowledged_messages = true; + //stopWaiting(); +} + +void AuthDialog::pamBioSuccess() +{ + m_bioTimer->stop(); + if (!m_widgetLoginOpts) { + switchLoginOptType(LOGINOPT_TYPE_PASSWORD); + return ; + } + m_widgetLoginOpts->startAuth(m_deviceInfo, user.uid); + if (m_deviceInfo) { + switchLoginOptType(m_widgetLoginOpts->convertDeviceType(m_deviceInfo->deviceType)); + } else { + switchLoginOptType(LOGINOPT_TYPE_PASSWORD); + } +} + +void AuthDialog::startBioAuth(unsigned uTimeout) +{ + if (m_widgetLoginOpts) + m_widgetLoginOpts->stopAuth(); + if(!m_bioTimer){ + m_bioTimer = new QTimer(this); + connect(m_bioTimer, SIGNAL(timeout()), this, SLOT(pamBioSuccess())); + } + m_bioTimer->start(uTimeout); + +} + +void AuthDialog::setX11Focus() +{ + if(m_passwordEdit && m_passwordEdit->isVisible()){ + m_passwordEdit->setX11Focus(); + } +} + +void AuthDialog::onShowPrompt(const QString &prompt, Auth::PromptType type) +{ + qDebug() << "-------prompt: " << prompt; + QString text = prompt; + + if(text == BIOMETRIC_PAM || text == BIOMETRIC_PAM_DOUBLE || text == BIOMETRIC_PAM_QRCODE) { + if(isBioPassed){ + onRespond(BIOMETRIC_SUCCESS); + return ; + } + if(authMode == PASSWORD) { + skipBiometricAuth(); + } else { + performBiometricAuth(); + } + } else { + stopWaiting(); + if(!text.isEmpty()) { + m_passwordEdit->readOnly(false); + m_passwordEdit->show(); + } + + m_passwordEdit->setFocus(); + + prompted = true; + unacknowledged_messages = false; + m_preStrMessage = ""; + if (!m_deviceInfo) { + switchLoginOptType(LOGINOPT_TYPE_PASSWORD); + } + + if(text == "Password: " || text == "密码:"){ + text = tr("Password "); + } else if (text == "Input password" || text == "Input Password" || text == "输入密码") { + text = tr("Input Password"); + } + + m_passwordEdit->clear(); + m_passwordEdit->setPrompt(text); + m_passwordEdit->show(); + if(!m_timer){ + m_timer = new QTimer(this); + m_timer->setInterval(200); + connect(m_timer, &QTimer::timeout, this, &AuthDialog::unlock_countdown); + } + m_timer->start(); + } +} + +void AuthDialog::onAuthComplete() +{ + stopWaiting(); + qDebug()<<"--------------------"<isAuthenticated(); + if(auth->isAuthenticated()) { + if (m_widgetLoginOpts) { + m_widgetLoginOpts->stopAuth(); + } + + if((prompted && !unacknowledged_messages )||direct_login) { + direct_login = false; + Q_EMIT authenticateCompete(true); + } else { + qDebug()<<"prompted = "<show(); + m_passwordEdit->hide(); + m_messageButton->show(); + m_messageButton->setFocus(); + m_messageButton->setDefault(true); + + connect(m_messageButton, &QPushButton::clicked, + this, &AuthDialog::onMessageButtonClicked); + if(successful) + { + isretry = false; + m_messageButton->setText(tr("Login")); + switchLoginOptType(LOGINOPT_TYPE_PASSWORD); + } + else + { + isretry = true; + m_messageButton->setText(tr("Retry")); + } + +} + +void AuthDialog::onMessageButtonClicked() +{ + m_messageButton->setDefault(false); + if(!isretry) + { + Q_EMIT authenticateCompete(true); + } + else + { + m_messageButton->hide(); + authMode = PASSWORD; + + m_messageLabel->setText(""); + startAuth(); + } +} + +void AuthDialog::onRespond(const QString &text) +{ + unacknowledged_messages=false; + clearMessage(); + startWaiting(); + m_passwordEdit->readOnly(true); + auth->respond(text); +} + +void AuthDialog::onM_passwordEditClicked() +{ + Q_EMIT clickPassword(true); +} + +//void AuthDialog::onCapsLockChanged() +//{ +// m_passwordEdit->onCapsStateChanged(); +//} + +void AuthDialog::startWaiting() +{ + if(m_buttonsWidget) + { + m_buttonsWidget->setEnabled(false); + } +} + +void AuthDialog::stopWaiting() +{ + m_passwordEdit->stopWaiting(); + if(m_buttonsWidget) + { + m_buttonsWidget->setEnabled(true); + } +} + +void AuthDialog::clearMessage() +{ + m_messageLabel->clear(); + m_preStrMessage = ""; + m_preStrMessageType = -1; +} + +void AuthDialog::performBiometricAuth() +{ + if(!m_biometricProxy) + { + m_biometricProxy = new BiometricProxy(this); + isHiddenSwitchButton = GetHiddenSwitchButton(); + maxFailedTimes = GetFailedTimes(); + } + + //服务没启动,或者打开DBus连接出错 + if(!m_biometricProxy->isValid()) + { + qWarning() << "An error occurs when connect to the biometric DBus"; + skipBiometricAuth(); + if (m_deviceInfo) { + if (!m_widgetLoginOpts || !m_widgetLoginOpts->findDeviceById(m_deviceInfo->id) + || m_widgetLoginOpts->isDeviceDisable(m_deviceInfo->id)) { + m_deviceInfo = DeviceInfoPtr(); + } + } + return; + } + + //初始化生物识别认证UI + initBiometricWidget(); + + //初始化enableBiometriAuth + if(m_deviceCount <= 0) + { + m_deviceCount = m_widgetLoginOpts->getLoginOptCount(); + } + + //没有可用设备,不启用生物识别认证 + if(m_deviceCount < 1) + { + qWarning() << "No available devices"; + skipBiometricAuth(); + return; + } + + //获取默认设备 + if(m_deviceName.isEmpty()) + { + m_deviceName = GetDefaultDevice(user.name); + } + qDebug() << m_deviceName; + if (m_deviceInfo) { + if (!m_widgetLoginOpts || !m_widgetLoginOpts->findDeviceById(m_deviceInfo->id) + || m_widgetLoginOpts->isDeviceDisable(m_deviceInfo->id)) { + m_deviceInfo = DeviceInfoPtr(); + } + } + + //如果默认设备为空的话,第一次不启动生物识别认证 + if(m_deviceName.isEmpty() && !m_deviceInfo) + { + if(useFirstDevice == true) { + m_deviceInfo = m_widgetLoginOpts->getFirstDevInfo(); + } else { + skipBiometricAuth(); + return; + } + } + + //clearMessage(); + + if(!m_deviceInfo) + { + m_deviceInfo = m_widgetLoginOpts->findDeviceByName(m_deviceName); + if (!m_deviceInfo) + m_deviceInfo = m_widgetLoginOpts->getFirstDevInfo(); + } + if(!m_deviceInfo) + { + skipBiometricAuth(); + return; + } + switchLoginOptType(m_widgetLoginOpts->convertDeviceType(m_deviceInfo->deviceType)); + startBioAuth(); + skipBiometricAuth(); +} + +void AuthDialog::skipBiometricAuth() +{ + auth->respond(BIOMETRIC_IGNORE); +} + +void AuthDialog::initBiometricWidget() +{ + if(m_widgetLoginOpts) { + m_widgetLoginOpts->setUser(user.uid); + } else { + m_widgetLoginOpts = new LoginOptionsWidget(m_biometricProxy, user.uid, this); + connect(m_widgetLoginOpts, &LoginOptionsWidget::authComplete, + this, &AuthDialog::onBiometricAuthComplete); + connect(m_widgetLoginOpts, &LoginOptionsWidget::optionSelected, + this, &AuthDialog::onDeviceChanged); + connect(m_widgetLoginOpts, &LoginOptionsWidget::updateImage, + this, &AuthDialog::onLoginOptImage); + connect(m_widgetLoginOpts, &LoginOptionsWidget::notifyOptionsChange, + this, &AuthDialog::onLoginOptsCount); + connect(m_widgetLoginOpts, &LoginOptionsWidget::updateAuthMsg, + this, &AuthDialog::setLoginMsg); + } + + qDebug()<<"----------DeviceCount:"<getLoginOptCount(); + if (m_widgetLoginOpts->getLoginOptCount() < 1) + m_widgetLoginOpts->hide(); + else + m_widgetLoginOpts->show(); + m_widgetLoginOpts->setEnabled(true); + + setBiometricWidgetGeometry(); +} + +void AuthDialog::setBiometricWidgetGeometry() +{ + //生物识别 + if(m_widgetLoginOpts) + { + m_widgetLoginOpts->setGeometry(0, m_passwdWidget->geometry().bottom(), + width(), 84*scale); + qDebug()<<"LoginOptGeometry:"<geometry()<<","<geometry() + <<","<isHidden(); + } +} + +void AuthDialog::onDeviceChanged(unsigned uCurLoginOptType, const DeviceInfoPtr &deviceInfo) +{ + if (!deviceInfo) + return; + qDebug() << "device changed: " << *deviceInfo; + if(m_failedTimes.contains(deviceInfo->id) && + m_failedTimes[deviceInfo->id] >= maxFailedTimes){ + qDebug() << "Failed MAX:"<id; + return ; + } + if (deviceInfo == m_deviceInfo) { + return ; + } + m_deviceInfo = deviceInfo; + switchLoginOptType(uCurLoginOptType); + if(!isBioPassed) + startBioAuth(); +} + +void AuthDialog::onBiometricAuthComplete(bool result, int nStatus) +{ + if(!result) { + if (nStatus >= 2) { + if (m_deviceInfo) { + if (m_failedTimes.contains(m_deviceInfo->id)) { + m_failedTimes[m_deviceInfo->id] = m_failedTimes[m_deviceInfo->id] + 1; + } else { + m_failedTimes[m_deviceInfo->id] = 1; + } + qDebug()<<"Failed count:"<id]<<",Max:"<id; + if (m_deviceInfo->deviceType == DeviceType::Face) { + QImage imgFailed; + setFaceImg(imgFailed, 1); + } + if(m_failedTimes[m_deviceInfo->id] >= maxFailedTimes){ + if (m_deviceInfo->deviceType == REMOTE_QRCODE_TYPE) { + setLoginTypeTip(tr("Failed to verify %1, please enter password to unlock").arg(DeviceType::getDeviceType_tr(m_deviceInfo->deviceType))); + QImage nullImage; + setQRCode(nullImage); + } else { + setLoginTypeTip(tr("Unable to verify %1, please enter password to unlock").arg(DeviceType::getDeviceType_tr(m_deviceInfo->deviceType))); + } + if (m_widgetLoginOpts) + m_widgetLoginOpts->setDeviceDisable(m_deviceInfo->id, true); + return ; + } + setLoginTypeTip(tr("Failed to verify %1, you still have %2 verification opportunities") + .arg(DeviceType::getDeviceType_tr(m_deviceInfo->deviceType)) + .arg(maxFailedTimes-m_failedTimes[m_deviceInfo->id])); + } + } + + if(!isBioPassed) { + if (nStatus <= 0) { + qDebug()<<"Biometric dbus error:"<= 2 && m_deviceInfo) { + if (m_deviceInfo->deviceType == DeviceType::Face) { + QImage imgFailed; + setFaceImg(imgFailed, 1); + } + } + } + } else { + direct_login = true; + isBioPassed = true; + onBiometricButtonClicked(); + } +} + +void AuthDialog::onBiometricButtonClicked() +{ + //当前没有设备 + if (!m_deviceInfo) { + qWarning()<<"未选择生物设备--"; + return ; + } + authMode = BIOMETRIC; + startAuth(); +} + +void AuthDialog::onPasswordButtonClicked() +{ + skipBiometricAuth(); +} + +void AuthDialog::onOtherDevicesButtonClicked() +{ + if (m_widgetLoginOpts) + m_widgetLoginOpts->stopAuth(); +} + +void AuthDialog::onRetryButtonClicked() +{ + if (m_widgetLoginOpts) + m_widgetLoginOpts->startAuth(m_deviceInfo, user.uid); + //m_retryButton->setVisible(false); +} + +void AuthDialog::setQRCode(QImage& imgQRCode) +{ + if (imgQRCode.isNull()) { + m_imgQRCode.load(":/image/assets/ukui-qrcode-null.svg"); + } else { + m_imgQRCode = imgQRCode; + } + m_imgQRCode = m_imgQRCode.scaled(QSize(150*scale, 150*scale)); + m_labelQRCode->setAlignment(Qt::AlignCenter); + m_labelQRCode->setPixmap(QPixmap::fromImage(m_imgQRCode)); +} + +void AuthDialog::setQRCodeMsg(QString strMsg) +{ + if (strMsg.isEmpty()) { + m_labelQRCodeMsg->hide(); + m_labelQRCodeTip->hide(); + } else { + m_labelQRCodeMsg->setText(strMsg); + m_labelQRCodeMsg->show(); + m_labelQRCodeTip->show(); + } +} + +void AuthDialog::setFaceImg(QImage& imgFace, int nStatus) +{ + QPixmap faceImage; + m_labelFace->setFixedSize(154*scale,154*scale); + //如果头像文件不存在,则使用默认头像 + if(!imgFace.isNull()) { + faceImage = PixmapToRound(QPixmap::fromImage(imgFace),77*scale); + } else { + switch(nStatus){ + case 1: + faceImage = QPixmap(":/image/assets/ukui-loginopt-lose.svg"); + break; + default: + faceImage = QPixmap(":/image/assets/ukui-loginopt-smile.svg"); + break; + } + } + + m_labelFace->setAlignment(Qt::AlignCenter); + m_labelFace->setPixmap(faceImage); +} + +void AuthDialog::setLoginTypeTip(QString strLoginTypeTip) +{ + m_strLoginTypeTip = strLoginTypeTip; + if (!m_strLoginTypeTip.isEmpty()) { + QFontMetrics font(m_labelLoginTypeTip->font()); + QString strDisplay = font.elidedText(m_strLoginTypeTip, Qt::ElideRight, m_messageLabel->width()-8); + m_labelLoginTypeTip->setText(strDisplay); + m_labelLoginTypeTip->setToolTip(m_strLoginTypeTip); + m_labelLoginTypeTip->show(); + } else { + m_labelLoginTypeTip->hide(); + } +} + +void AuthDialog::setLoginMsg(QString strLoginMsg) +{ + setLoginTypeTip(strLoginMsg); +} + +void AuthDialog::onLoginOptsCount(unsigned uCount) +{ + qDebug()<<"----------------------onLoginOptsCount Count:"< 0) { + setBiometricWidgetGeometry(); + m_widgetLoginOpts->show(); + } else { + switchLoginOptType(LOGINOPT_TYPE_PASSWORD); + m_widgetLoginOpts->hide(); + } + + if (!m_deviceInfo || !m_widgetLoginOpts->findDeviceById(m_deviceInfo->id) + || m_widgetLoginOpts->isDeviceDisable(m_deviceInfo->id)) { + m_widgetLoginOpts->stopAuth(); + authMode = BIOMETRIC; + startAuth(); + } +} + +void AuthDialog::onLoginOptImage(QImage img) +{ +// if (img.isNull()) { +// return ; +// } + if (m_uCurLoginOptType == LOGINOPT_TYPE_FACE) { + setFaceImg(img); + } else if (m_uCurLoginOptType == LOGINOPT_TYPE_QRCODE) { + setQRCode(img); + } +} diff --git a/src/authdialog.h b/src/authdialog.h new file mode 100644 index 0000000..73ecd16 --- /dev/null +++ b/src/authdialog.h @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#ifndef AUTHDIALOG_H +#define AUTHDIALOG_H + +#ifndef QT_NO_KEYWORDS +#define QT_NO_KEYWORDS +#endif + +#include +#include +#include +#include "auth-pam.h" +#include "types.h" +#include "users.h" +#include "biometricdeviceinfo.h" +#include "pam-tally.h" + +namespace Ui { +class AuthDialog; +} + +class QLabel; +class QPushButton; +class IconEdit; +class Auth; +class BiometricProxy; +class BiometricAuthWidget; +class BiometricDevicesWidget; +class PamTally; +class LoginOptionsWidget; + +extern float scale; +class AuthDialog : public QWidget +{ + Q_OBJECT + +public: + explicit AuthDialog(const UserItem &user, QWidget *parent = 0); + void resizeEvent(QResizeEvent *event); + void closeEvent(QCloseEvent *event); + void setUserOfAuth(); + void setX11Focus(); +private: + void initUI(); + void startWaiting(); + void stopWaiting(); + void clearMessage(); + void performBiometricAuth(); + void skipBiometricAuth(); + void initBiometricWidget(); + void setChildrenGeometry(); + void setBiometricWidgetGeometry(); + QPixmap PixmapToRound(const QPixmap &src, int radius); + void startBioAuth(unsigned uTimeout = 1000); + void show_authenticated (bool successful = true); + void setLoginTypeTip(QString strLoginTypeTip); + +private Q_SLOTS: + void onShowMessage(const QString &message, Auth::MessageType type); + void onShowPrompt(const QString &prompt, Auth::PromptType type); + void onAuthComplete(); + void onRespond(const QString &text); +// void onBioAuthStart(); +// void onBioAuthStop(); +// void setBioMovieImage(); +// void updateIcon(); + void onDeviceChanged(unsigned uCurLoginOptType, const DeviceInfoPtr &deviceInfo); + void onBiometricAuthComplete(bool result, int nStatus); + void onBiometricButtonClicked(); + void onPasswordButtonClicked(); + void onOtherDevicesButtonClicked(); + void onRetryButtonClicked(); + void pamBioSuccess(); + void onMessageButtonClicked(); + void switchLoginOptType(unsigned uLoginOptType); + void onLoginOptsCount(unsigned uCount); + void onLoginOptImage(QImage img); + void setLoginMsg(QString strLoginMsg); + void setQRCode(QImage& imgQRCode); + void setFaceImg(QImage& imgFace, int nStatus = 0); + void onM_passwordEditClicked(); + void setQRCodeMsg(QString strMsg); + +public Q_SLOTS: +// void switchToBiometric(); +// void switchToPassword(); +// void switchToDevices(); +// void onCapsLockChanged(); + void startAuth(); + void stopAuth(); + +Q_SIGNALS: + void authenticateCompete(bool result); + void clickPassword(bool clicked); +private: + UserItem user; + Auth *auth; + + enum AuthMode { PASSWORD, BIOMETRIC, UNKNOWN }; + + AuthMode authMode; + + // biometric auth + int m_deviceCount; + QString m_deviceName; + DeviceInfoPtr m_deviceInfo = nullptr; + BiometricProxy *m_biometricProxy; + BiometricAuthWidget *m_biometricAuthWidget; + QWidget *m_buttonsWidget; + QPushButton *m_biometricButton; + QPushButton *m_passwordButton; + QPushButton *m_otherDeviceButton; + QPushButton *m_retryButton; + + LoginOptionsWidget *m_widgetLoginOpts = nullptr; + + // UI +// QPushButton *m_backButton; //返回用户列表 + QWidget *m_userWidget; //放置用户信息Label + QLabel *m_labelHeadImg = nullptr; //头像 + QLabel *m_nameLabel; //用户名 +// QLabel *m_isLoginLabel; //提示是否已登录 + + QWidget *m_passwdWidget; //放置密码输入框和信息列表 + IconEdit *m_passwordEdit; //密码输入框 + QLabel *m_messageLabel; //PAM消息显示 + QPushButton *m_messageButton; + + QLabel *m_labelFace = nullptr; + QLabel *m_labelLoginTypeTip = nullptr; // 登录类型提示 + QLabel *m_labelQRCode = nullptr; // 二维码图标 + QLabel *m_labelQRCodeMsg = nullptr; // 二维码状态消息提示 + QLabel *m_labelQRCodeTip = nullptr; + + bool isBioPassed; // 生物认证是否成功 + int maxFailedTimes; + bool isHiddenSwitchButton; + QMap m_failedTimes; + QTimer *m_bioTimer; + bool useFirstDevice; + bool isLockingFlg; //判断当前是否正在锁定倒计时 + int m_nCurLockMin; //当前锁定的分钟数 + bool prompted = false; + bool unacknowledged_messages = false; + bool direct_login = false; + bool isretry = true; + QString m_preStrMessage = ""; + int m_preStrMessageType = -1; + + void root_unlock_countdown(); + void unlock_countdown(); + QTimer *m_timer; + + QImage m_imgQRCode; + unsigned m_uCurLoginOptType = LOGINOPT_TYPE_PASSWORD; // 当前登录验证方式 + QString m_strLoginTypeTip = ""; + int m_nLastDeviceId = -1; +}; + +#endif // AUTHDIALOG_H diff --git a/src/common.h b/src/common.h new file mode 100644 index 0000000..b7ed8c8 --- /dev/null +++ b/src/common.h @@ -0,0 +1,61 @@ +#ifndef LOCKCOMMON_H +#define LOCKCOMMON_H + +enum SwitchPage{ + SwitchToPin = 0, + SwitchToWechat, + SwitchToCheck, + SwitchToResetPWD, + SwitchToConfigPWD +}; + +// 和后台规定好的 错误码固定值 +enum DBusMsgCode{ + + Error_QR_Get_Timeout = -8, // 二维码获取时间超时 + Error_NetWork = -7, // 网络错误 未能如期获得对应的数据 + Error_VerifyCode_Timeout = -6, // 验证码失效 + Error_RepeatedRequests = -5, // 重复请求二维码服务 + Error_NoReply = -4, // DBus接口连接失败导致的无回复错误 + Error_UnknownReason = -3, // 未知原因 + Error_ArgCnt = -2, // 参数个数错误 + Error_SetPsw_SameAsOriPsw = -1, // 设置新密码时 与原密码相同 + No_Error = 0, // 获取消息无错误 + Error_TelHaveBinded = 9009, // 手机号已绑定 -> 更换手机号后再试 + Error_VerifyCodeDiff = 9000, // 验证码错误 -> 请填写正确的验证码 + Error_AccessTokenInvalid = 9007,// Access Token 失效 -> 登录状态已过期,请重新扫码登录 + Error_ErrTimesOverMax = 9014, // 手机号登录时验证码错误次数超限 + // 账号密码登录时密码错误次数超限 + // 1小时内连续错10次触发 -> 1小时后再试 + Error_UserInfo = 9017, // 用户微信信息不存在 ->老用户,联系管理员处理 + Error_WechatHaveBinded = 9022, // 微信号已被其他账号绑定 -> 更换微信号再试 + Error_DeviceHaveBinded = 9027, // 当前设备已被绑定 -> 请联系管理员或班主任解绑 + Error_HaveBindOtherDevice = 9028, // 用户已绑定其他设备 -> 请联系管理员或班主任解绑 + + //以下错误码保留 + Error_NWUnused = 6, // 网络异常,请检查网络链接哦~ + Error_TencentUnused = 7, // 腾讯服务异常,请稍后再试~ + Error_NWDelay = 28, // 网络延迟大,请更换良好网络~ + Error_ResMalloc = 301, // 资源分配异常,请重启尝试 + Error_DBOpen = 101, // 数据库打开异常,请重试,或重启再试 + Error_DBGetKey = 102, // 获取数据库密钥失败,请重试 + Error_DBRetrieve = 103, // 数据库检索数据失败,请重试 + Error_DBDecryption = 105, // 数据库解密过程失败,请重试 +}; + + + +/*! + * \brief The QRCodeSwepState enum + * WaitingSwep 在DBus、SSOBackend、1.617 版本中未启用 + */ +enum QRCodeSwepState{ + WaitingSwep = 0, //等待用户扫码 + HaveSwep = 2, // 用户扫码、等待用户确认 + CancelSwep = 3, // 用户扫码后、取消确认 + ConfirmSuccess = 4, // 扫码确认成功 + QRCodeInvalid = 5, // 二维码失效 + QRCodeTimeout = 6, //二维码超时 +}; + +#endif // LOCKCOMMON_H diff --git a/src/configuration.cpp b/src/configuration.cpp new file mode 100644 index 0000000..ac964a0 --- /dev/null +++ b/src/configuration.cpp @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#include "configuration.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "commonfunc.h" +#define GSETTINGS_SCHEMA_SCREENSAVER "org.ukui.screensaver" +#define KEY_MODE "mode" +#define KEY_THEMES "themes" +#define KEY_IDLE_ACTIVATION_ENABLED "idle-activation-enabled" +#define KEY_LOCK_ENABLED "lock-enabled" +#define KEY_IMAGE_TRANSITION_EFFECT "image-transition-effect" +#define KEY_IMAGE_SWITCH_INTERVAL "image-switch-interval" +#define KEY_BACKGROUND "background" +#define XSCREENSAVER_DIRNAME "/usr/lib/xscreensaver" +#define KEY_IDLE_DELAY "idleDelay" +#define KEY_IDLE_LOCK "idleLock" + +Configuration* Configuration::instance_ = nullptr; + +Configuration::Configuration(QObject *parent) : QObject(parent) +{ + /* QGSettings for screensaver */ + gsettings = new QGSettings(GSETTINGS_SCHEMA_SCREENSAVER, "", this); + connect(gsettings, &QGSettings::changed, + this, &Configuration::onConfigurationChanged); + + /* Initiailization */ + mode = gsettings->get(KEY_MODE).toString(); + themes = gsettings->get(KEY_THEMES).toStringList(); + idleDelay = gsettings->get( + KEY_IDLE_DELAY).toInt(); + lockEnabled = gsettings->get(KEY_LOCK_ENABLED).toBool(); + idleLock = gsettings->get(KEY_IDLE_LOCK).toInt(); + imageSwitchInterval = gsettings->get(KEY_IMAGE_SWITCH_INTERVAL).toInt(); + imageTSEffect = gsettings->get(KEY_IMAGE_TRANSITION_EFFECT).toInt(); + background = gsettings->get(KEY_BACKGROUND).toString(); + + qDebug() << mode << themes; + qDebug() << imageSwitchInterval << imageTSEffect; + + int FileisExist = 0; + if(!background.isEmpty()) + { + QFileInfo file(background); + if(file.exists()==false) + FileisExist = 0; + else + FileisExist = 1; + } + + //如果org.ukui.screensaver background中的背景图片为空,则设为桌面背景 + if(background.isEmpty()||0==FileisExist) + { + QString currentDesktop = qgetenv("XDG_CURRENT_DESKTOP"); + if(currentDesktop == "UKUI" || currentDesktop == "MATE") + { + bgGsettings = new QGSettings("org.mate.background"); + background = bgGsettings->get("picture-filename").toString(); + } + else if(currentDesktop == "ubuntu:GNOME") + { + bgGsettings = new QGSettings("org.gnome.desktop.background"); + background = bgGsettings->get("picture-uri").toString(); + //去除前缀:file:///usr/share/background/xxx.png + background.remove(0, 7); + } + } + qDebug() << "background: " << background; + + if(themes.count() == 1 && themes[0] == "kyccss-personal-slideshow") + mode ="image"; +} + +Configuration* Configuration::instance(QObject *parent) +{ + if(instance_ == nullptr) + instance_ = new Configuration(parent); + return instance_; +} + +/* Update member value when GSettings changed */ +void Configuration::onConfigurationChanged(QString key) +{ + qDebug() << "GSettings value changed, key = " << key; + if (key == KEY_MODE) + mode = gsettings->get(KEY_MODE).toString(); + else if (key == KEY_THEMES) + themes = gsettings->get(KEY_THEMES).toStringList(); + else if (key == KEY_IDLE_DELAY) + idleDelay = gsettings->get(KEY_IDLE_DELAY).toInt(); + else if (key == KEY_IDLE_LOCK) + idleLock = gsettings->get(KEY_IDLE_LOCK).toInt(); + else if(key == KEY_IMAGE_TRANSITION_EFFECT) + imageTSEffect = gsettings->get(KEY_IMAGE_TRANSITION_EFFECT).toInt(); + else if(key == KEY_IMAGE_SWITCH_INTERVAL) + imageSwitchInterval = gsettings->get(KEY_IMAGE_SWITCH_INTERVAL).toInt(); +} + +/* + * Getter + */ + +/* Get the executable path of xscreensaver */ +ScreenSaver *Configuration::getScreensaver() +{ + QStringList modeStr{"blank-only", "random", "single", "image","default-ukui","default-ukui-custom"}; + ScreenSaver *saver = new ScreenSaver; + int index = modeStr.indexOf(mode); + saver->mode = SaverMode(index); + saver->interval = imageSwitchInterval; + saver->effect = TransitionEffect(imageTSEffect); + + switch(index){ + case SAVER_BLANK_ONLY: + break; + case SAVER_RANDOM: + { + if(themes.count()==0){ + saver->path = "/usr/lib/ukui-screensaver/ukui-screensaver-default"; + break; + } + qsrand((unsigned)time(0)); + int index = qrand() % themes.count(); + while(QString::compare(themes[index], "kyccss-personal-slideshow")==0) + { + index = qrand() % themes.count(); + } + saver->path = getXScreensaverPath(themes[index]); + break; + } + case SAVER_SINGLE: + if(themes.count()==0){ + saver->path = "/usr/lib/ukui-screensaver/ukui-screensaver-default"; + break; + } + saver->path = getXScreensaverPath(themes[0]); + break; + case SAVER_IMAGE: + { + QString lang = qgetenv("LANG"); + if (!lang.isEmpty()){ + qDebug()<<"lang = "<path = QDir::homePath() + "/图片"; + break; + } + } + saver->path = QDir::homePath() + "/" + QStandardPaths::displayName(QStandardPaths::PicturesLocation); + break; + } + case SAVER_DEFAULT: + saver->path = "/usr/lib/ukui-screensaver/ukui-screensaver-default"; + break; + case SAVER_DEFAULT_CUSTOM: + saver->path = "/usr/lib/ukui-screensaver/ukui-screensaver-default"; + break; + default: + break; + } + return saver; +} + +QString Configuration::getXScreensaverPath(const QString &theme) +{ + /* screensavers-ukui-binaryring => binaryring */ + QStringList strs = theme.split("-"); + QString str = strs.at(strs.size() - 1); + QString filePath = QString("%1/%2").arg(XSCREENSAVER_DIRNAME, str); + //除了判断gsetting值是否为空,还需要判断屏保文件是否存在,不存在就使用默认屏保。 + if(QFile(filePath).exists()) + return filePath; + else + return "/usr/lib/ukui-screensaver/ukui-screensaver-default"; +} + +QString Configuration::getBackground() +{ + if(ispicture(background)) + return background; + + return "/usr/share/backgrounds/1-warty-final-ubuntukylin.jpg"; +} + +int Configuration::xscreensaverActivatedWhenIdle() +{ + return idleDelay; +} + +bool Configuration::lockWhenXScreensaverActivated() +{ + return lockEnabled; +} diff --git a/src/configuration.h b/src/configuration.h new file mode 100644 index 0000000..a138775 --- /dev/null +++ b/src/configuration.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#ifndef CONFIGURATION_H +#define CONFIGURATION_H + +#ifndef QT_NO_KEYWORDS +#define QT_NO_KEYWORDS +#endif + +//#include "gsettings.h" + +#include +#include "screensaver.h" + +class QGSettings; + +class Configuration : public QObject +{ + Q_OBJECT +public: + explicit Configuration(QObject *parent = nullptr); + +public: + ScreenSaver *getScreensaver(); + static Configuration *instance(QObject *parent = nullptr); + QString getBackground(); + int xscreensaverActivatedWhenIdle(); + bool lockWhenXScreensaverActivated(); + +public Q_SLOTS: + void onConfigurationChanged(QString key); + +private: + QString getXScreensaverPath(const QString &theme); + +private: + QGSettings *gsettings; + QGSettings *bgGsettings; + QString mode; + QList themes; + QString background; + bool idleActivationEnabled; + bool lockEnabled; + int idleDelay; + int idleLock; + int imageTSEffect; + int imageSwitchInterval; + static Configuration *instance_; +}; + +#endif // CONFIGURATION_H diff --git a/src/digitalauthdialog.cpp b/src/digitalauthdialog.cpp new file mode 100644 index 0000000..6646352 --- /dev/null +++ b/src/digitalauthdialog.cpp @@ -0,0 +1,834 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Authors: ZHAI Kang-ning +**/ +#include "digitalauthdialog.h" +#include +#include +#include +#include +#include +#include + +#include "auth-pam.h" +#include "permissioncheck.h" +#include "eduplatforminterface.h" + +DigitalAuthDialog::DigitalAuthDialog(QWidget *parent) : QWidget(parent), + m_bgColor("#000000"), //整个wigidt背景颜色 + m_outCircleColor("#000000"), + m_tipLineColor("#ff0000"), + m_circleRingColor("#ddffff"), + m_radius(40), + m_margin(5), + m_pressed(false), + m_showText(true), + m_inputPassword(""), + m_circleColor("#ffffff"),//按钮的颜色 + m_statusRadius(8), + m_auth(new AuthPAM(this)), + m_isAuthing(false), + m_buttonWidth(96), + m_buttonHeight(64), + m_buttonRadius(16), + m_buttonStep(16), + m_resetPassword(""), + m_resetOrAuthFailure(new QLabel(this)), + m_digitalKeyBoard(new DigitalKeyBoard(this)) +{ + //获取键盘事件 此操作会使得其他控件无法获取键盘事件,使用releaseKeyboard可释放 +// grabKeyboard(); + m_posInCircle.isIn = false; +// qDebug() <<"-----------init width=" << width() << ",height=" << height(); + m_title = new QLabel(tr("LoginByUEdu"), this); + m_title->setStyleSheet("font-size:30px;color:#ffffff"); + m_title->adjustSize(); + + m_labelReset = new MyLabel(tr("ResetPWD?"), this); + m_labelReset->setStyleSheet("QLabel{font-size:16px;color:rgba(255, 255, 255, 45);}" + "QLabel:hover{color:rgba(255, 255, 255, 100);}"); + m_labelReset->adjustSize(); + + m_authMessage = new QLabel(this); + m_authMessage->setStyleSheet("font-size:14px;color:#ffffff"); + + //设置字体大小 + QFont f; + f.setPixelSize(m_radius * 24/40); + setFont(f); + + //调整适当的大小 + resize(350,600); + m_title->setGeometry((this->width() - m_title->width())/2, 6, m_title->width(), m_title->height()); + m_labelReset->setGeometry((this->width()- m_labelReset->width())/2, this->height() - 120, m_labelReset->width(),m_labelReset->height()); + + + setMouseTracking(true); + connect(m_digitalKeyBoard, &DigitalKeyBoard::numbersButtonPress, this, &DigitalAuthDialog::onNumerPress); + connect(m_auth, &Auth::showMessage, this, &DigitalAuthDialog::onShowMessage); + connect(m_auth, &Auth::showPrompt, this, &DigitalAuthDialog::onShowPrompt); + connect(m_auth, &Auth::authenticateComplete, this, &DigitalAuthDialog::onAuthComplete); + connect(m_labelReset, &MyLabel::onClick, this, [=]{ +// qDebug() << "-------------------onClick"; + m_title->setText(tr("SetNewUEduPWD")); + m_title->adjustSize(); + m_resetOrAuthFailure->clear(); + m_title->setGeometry((this->width() - m_title->width())/2, 5, m_title->width(), m_title->height()); + m_inputPassword.clear(); + m_loginType = LoginType::RESET; + m_labelReset->hide(); +// PermissionCheck *check = new PermissionCheck(this); +// QVBoxLayout *v = new QVBoxLayout(this); +// v->addWidget(check); +// this->hide(); + Q_EMIT requestPasswordReset(); + }); + +// m_timer = new QTimer(this); +// connect(m_timer,&QTimer::timeout,this,[=]{ +// if(m_isAuthing) +// { +// qDebug() << "Auth respond timeout , restart"; +// startAuth(); +// } +// }); + + m_digitalKeyBoard->move(9,143); +} + +DigitalAuthDialog::~DigitalAuthDialog() +{ + +} + +void DigitalAuthDialog::onNumerPress(int btn_id) +{ + qDebug() << btn_id; + //nothing + if(m_authMessage) + cleanMessage(); + if(m_resetOrAuthFailure) + m_resetOrAuthFailure->clear(); + m_pressed = true; + + if(m_isAuthing) + { + qWarning() << "authenticating"; + return; + } + + if(btn_id == 11) + { + //点击了清空按钮 + m_inputPassword.clear(); + } else if (btn_id == 10) + { + if (!m_inputPassword.isEmpty()) + m_inputPassword.remove(m_inputPassword.size() -1, 1); + } else { + int num = btn_id; + m_inputPassword.append(QString::number(num == 11 ? 0 : num)); + if (m_inputPassword.size() >= 6) + { + checkPassword(); + } + } + repaint(); +} + +void DigitalAuthDialog::paintEvent(QPaintEvent *event) +{ + int width = this->width(); + int height = this->height(); + int side = qMin(width, height); +// qDebug() <<"-----------width=" << width << ",height=" << height << ",side=" << side; + + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + painter.translate(width / 2, height / 2); + painter.scale(side / 200, side / 200); + +// drawBg(&painter); + drawStatusCircle(&painter); +//// drawOutCircle(&painter); +// drawFilledCircle(&painter); +//// drawTipLine(&painter); +// drawInCircle(&painter); +//// drawJoinLine(&painter); +// drawHoverCircle(&painter); +} + +//画背景颜色 +void DigitalAuthDialog::drawBg(QPainter *painter) +{ + painter->save(); + + int width = this->width(); + int height = this->height(); + + painter->setPen(Qt::NoPen); + m_bgColor.setAlpha(0); + painter->setBrush(m_bgColor); + painter->drawRect(-width / 2, -height / 2, width, height); + + painter->restore(); +} + +//绘制按钮的颜色 +void DigitalAuthDialog::drawFilledCircle(QPainter *painter) +{ + painter->save(); + m_circleColor.setAlphaF(0.15); + QPen pen(m_circleColor); + painter->setPen(pen); + painter->setBrush(m_circleColor); + + for (int i = 0; i < 4; i++) + { + for (int j = 0; j < 3; j++) + { + int x = -3 * (m_buttonWidth / 2) + j * m_buttonWidth + (j - 1) * m_buttonStep; + int y = -2 * m_buttonHeight + i * m_buttonHeight + m_buttonStep * i - 3 * (m_buttonStep / 2); + QRect rect(x, y, m_buttonWidth, m_buttonHeight); +// painter->drawEllipse(rect); + painter->drawRoundedRect(rect, m_buttonRadius, m_buttonRadius); + if (m_showText) + { + painter->save(); + painter->setPen(QColor("#ffffff")); + painter->setBrush(QColor("#ffffff")); + + int number = posToNumber(i, j); + if (number == 11) + { + number = 0; + } else if(number == 10) + { + // 数字10的地方是清空按钮 + x = (j - 1) * m_buttonWidth + (j - 1) * m_buttonStep;; + y = -3 * (m_buttonHeight / 2) + i * m_buttonHeight + m_buttonStep * i - 3 * (m_buttonStep / 2); + QString text = tr("clear"); + int textWidth = fontMetrics().width(text); + QFont font = painter->font(); + font.setPixelSize(m_radius * 20/40); + painter->setFont(font); + painter->drawText(x - textWidth / 2 + 3, y + textWidth / 2 - 18, text); + painter->restore(); + continue; + } + else if (number == 12) + { + // 画删除符号(3,2) + x = m_buttonWidth / 2 + m_buttonStep; + y = m_buttonHeight + 3 * (m_buttonStep / 2); + painter->drawPixmap(x + m_buttonWidth/2 - 15,y + m_buttonHeight/2 - 15, QPixmap(":/image/assets/intel/delete.svg")); + painter->restore(); + continue; + } + + QString text = QString::number(number); + x = (j - 1) * m_buttonWidth + (j - 1) * m_buttonStep;; + y = -3 * (m_buttonHeight / 2) + i * m_buttonHeight + m_buttonStep * i - 3 * (m_buttonStep / 2); + int textWidth = fontMetrics().width(text); + int textHeight = fontMetrics().height(); + painter->drawText(x - textWidth / 2, y + textWidth / 2 + 4, text); + painter->restore(); + } + } + } + painter->restore(); +} + +//画外圆 +void DigitalAuthDialog::drawOutCircle(QPainter *painter) +{ + painter->save(); + QPen pen(m_outCircleColor, 2); + painter->setPen(pen); + painter->setBrush(Qt::NoBrush); + + for (int i = 0; i < 4; i++) + { + for (int j = 0; j < 3; j++) + { + int x = -3 * (m_buttonWidth / 2) + j * m_buttonWidth + (j - 1) * m_buttonStep; + int y = -2 * m_buttonHeight + i * m_buttonHeight + m_buttonStep * i - 3 * (m_buttonStep / 2); + QRect rect(x, y, m_buttonWidth, m_buttonHeight); +// painter->drawEllipse(rect); + painter->drawRoundedRect(rect, m_buttonRadius, m_buttonRadius); +// painter->drawEllipse(x, y, m_radius * 2, m_radius * 2); + if (m_showText) + { + int number = posToNumber(i, j); + QString text = QString::number(number); + x = -2 * m_radius + j * 2 * m_radius + (j - 1) * 16; + y = -3 * m_radius + i * 2 * m_radius + 16 * i - 24; + int textWidth = fontMetrics().width(text); + int textHeight = fontMetrics().height(); + painter->drawText(x - textWidth / 2, y + textWidth / 2, text); + } + } + } + + painter->restore(); +} + +void DigitalAuthDialog::drawTipLine(QPainter *painter) +{ + painter->save(); + + int offset = 3; + + if (m_posInCircle.isIn) + { + if (!m_pressed) + { + painter->setPen(m_tipLineColor); + painter->setBrush(Qt::NoBrush); + int x = -3 * (m_buttonWidth / 2) + m_posInCircle.j * m_buttonWidth + (m_posInCircle.j - 1) * m_buttonStep - offset; + int y = -2 * m_buttonHeight + m_posInCircle.i * m_buttonHeight + m_buttonStep * m_posInCircle.i - 3 * (m_buttonStep / 2) - offset; + QRect rect(x, y, m_buttonWidth, m_buttonHeight); + painter->drawRoundedRect(rect, m_buttonRadius, m_buttonRadius); + } + } + + painter->restore(); +} + +//绘制点击时的颜色 +void DigitalAuthDialog::drawInCircle(QPainter *painter) +{ + painter->save(); + + if (m_pressed) + { + if (m_posInCircle.isIn) + { + m_circleColor.setAlphaF(0.05); + painter->setPen(m_circleColor); + painter->setBrush(m_circleColor); + int x = -3 * (m_buttonWidth / 2) + m_posInCircle.j * m_buttonWidth + (m_posInCircle.j - 1) * m_buttonStep; + int y = -2 * m_buttonHeight + m_posInCircle.i * m_buttonHeight + m_buttonStep * m_posInCircle.i - 3 * (m_buttonStep / 2); + int w = m_buttonWidth; + int h = m_buttonHeight; + QRect rect = QRect(x, y, w, h); + painter->drawRoundedRect(rect, m_buttonRadius, m_buttonRadius); + } + } + + painter->restore(); +} + +//绘制悬浮时的颜色 +void DigitalAuthDialog::drawHoverCircle(QPainter *painter) +{ + painter->save(); + if (!m_pressed) + { + if (m_posInCircle.isIn) + { + m_circleColor.setAlphaF(0.35); + painter->setPen(m_circleColor); + painter->setBrush(m_circleColor); + int x = -3 * (m_buttonWidth / 2) + m_posInCircle.j * m_buttonWidth + (m_posInCircle.j - 1) * m_buttonStep; + int y = -2 * m_buttonHeight + m_posInCircle.i * m_buttonHeight + m_buttonStep * m_posInCircle.i - 3 * (m_buttonStep / 2); + int w = m_buttonWidth; + int h = m_buttonHeight; + QRect rect = QRect(x, y, w, h); + painter->drawRoundedRect(rect, m_buttonRadius, m_buttonRadius); + } + + } + + painter->restore(); +} + + +void DigitalAuthDialog::drawJoinLine(QPainter *painter) +{ + painter->save(); + + QPen pen(m_outCircleColor, 3); + painter->setPen(pen); + //绘制连接线段 + for (int i = 0; i < m_vecInputPoints.count() - 1; i++) + { + painter->drawLine(m_vecInputPoints[i], m_vecInputPoints[i + 1]); + } + + int offset1 = 1;//圆环偏移量 + int offset2 = 3;//内圆偏移量 + //绘制连接圆 + for (int i = 0; i < m_vecInputPoints.count(); i++) + { + int x = m_vecInputPoints[i].x() - m_radius + offset1; + int y = m_vecInputPoints[i].y() - m_radius + offset1; + int w = 2 * (m_radius - offset1); + int h = 2 * (m_radius - offset1); + painter->setPen(m_circleRingColor); + painter->setBrush(m_circleRingColor); + painter->drawEllipse(x, y, w, h); + + x = m_vecInputPoints[i].x() - m_radius + offset2; + y = m_vecInputPoints[i].y() - m_radius + offset2; + w = 2 * (m_radius - offset2); + h = 2 * (m_radius - offset2); + painter->setPen(m_outCircleColor); + painter->setBrush(m_outCircleColor); + painter->drawEllipse(x, y, w, h); + } + //绘制跟随线 + if (m_pressed && m_vecInputPoints.count() < 9) + { + int width = this->width(); + int height = this->height(); + int side = qMin(width, height); + int gestureScale = side / 200; + int x = (m_followPoint.x() - width / 2) / gestureScale; + int y = (m_followPoint.y() - height / 2) / gestureScale; + if (m_vecInputPoints.count() > 0) + { + painter->setPen(pen); + painter->drawLine(m_vecInputPoints.last(), QPoint(x, y)); + } + } + + painter->restore(); +} + +void DigitalAuthDialog::mouseMoveEvent(QMouseEvent *event) +{ +// bool temp = false; +// //nothing +// int x = (event->x() - this->width()/2) / (qMin(width(), height())/200); +// int y = (event->y() - this->height()/2) / (qMin(width(), height())/200); + +// for (int i = 0; i < 4; i++) +// { +// for (int j = 0; j < 3; j++) +// { +// int x1 = -3 * (m_buttonWidth / 2) + j * m_buttonWidth + (j - 1) * m_buttonStep; +// int y1 = -2 * m_buttonHeight + i * m_buttonHeight + m_buttonStep * i - 3 * (m_buttonStep / 2); +// if (isInButtonArea(x, y, x1, y1)) +// { +// m_posInCircle.i = i; +// m_posInCircle.j = j; +// m_posInCircle.isIn = true; +// temp = true; +// repaint(); +// } +// } +// } + +// if (!temp) +// { +// m_posInCircle.isIn = false; +// repaint(); +// } + +} + +void DigitalAuthDialog::mousePressEvent(QMouseEvent *event) +{ + //nothing +// if(m_authMessage) +// cleanMessage(); +// if(m_resetOrAuthFailure) +// m_resetOrAuthFailure->clear(); +// m_pressed = true; +// int x = (event->x() - this->width()/2) / (qMin(width(), height())/200); +// int y = (event->y() - this->height()/2) / (qMin(width(), height())/200); + +// for (int i = 0; i < 4; i++) +// { +// for (int j = 0; j < 3; j++) +// { +// int x1 = -3 * (m_buttonWidth / 2) + j * m_buttonWidth + (j - 1) * m_buttonStep; +// int y1 = -2 * m_buttonHeight + i * m_buttonHeight + m_buttonStep * i - 3 * (m_buttonStep / 2); +// if (isInButtonArea(x, y, x1, y1)) +// { +// if(m_isAuthing) +// { +// qWarning() << "authenticating"; +// return; +// } +// m_posInCircle.i = i; +// m_posInCircle.j = j; +// m_posInCircle.isIn = true; +// if(posToNumber(i, j) == 10) +// { +// //点击了清空按钮 +// m_inputPassword.clear(); +// } else if (posToNumber(i, j) == 12) +// { +// if (!m_inputPassword.isEmpty()) +// m_inputPassword.remove(m_inputPassword.size() -1, 1); +// } else { +// int num = posToNumber(i,j); +// m_inputPassword.append(QString::number(num == 11 ? 0 : num)); +// if (m_inputPassword.size() >= 6) +// { +// checkPassword(); +// } +//// qDebug() << "---------------选中了"<< posToNumber(i,j); +// } +// repaint(); +// } +// } +// } +} + +void DigitalAuthDialog::mouseReleaseEvent(QMouseEvent *event) +{ +// //nothing +// m_pressed = false; +// //m_posInCircle.isIn = false; +// repaint(); +} + +////监听键盘输入 +void DigitalAuthDialog::keyPressEvent(QKeyEvent *event) +{ + qDebug() << "keyPressEvent " << event->key(); + if (m_isAuthing) + { + qWarning() << "authenticating"; + return; + } + if(event->key() == Qt::Key_Backspace) + { + if (!m_inputPassword.isEmpty()) + m_inputPassword.remove(m_inputPassword.size() -1, 1); + } else if (event->key() >= Qt::Key_0 && event->key() <= Qt::Key_9 ) + { + if(m_resetOrAuthFailure) + m_resetOrAuthFailure->clear(); + m_inputPassword.append(event->text()); + if (m_inputPassword.size() >= 6) + { + checkPassword(); + m_inputPassword.clear(); + } + } else { + return; + } + repaint(); +} + +////监听键盘输入 +//void DigitalAuthDialog::RecieveKey(int key) +//{ +// qDebug() << "keyPressEvent " << key; +// if (m_isAuthing) +// { +// qWarning() << "authenticating"; +// return; +// } +// if(key == 10) +// { +// if (!m_inputPassword.isEmpty()) +// m_inputPassword.remove(m_inputPassword.size() -1, 1); +// } else if (key >= 0 && key <= 9 ) +// { +// if(m_resetOrAuthFailure) +// m_resetOrAuthFailure->clear(); +// m_inputPassword.append(QString::number(key)); +// if (m_inputPassword.size() >= 6) +// { +// checkPassword(); +// m_inputPassword.clear(); +// } +// } else { +// return; +// } +// repaint(); +//} + +double DigitalAuthDialog::distance(int x1, int y1, int x2, int y2) +{ + //nothing + double x = x1 - x2; + double y = y1 - y2; + return sqrt(x*x + y*y); +} + +int DigitalAuthDialog::posToNumber(int i, int j) +{ + //nothing + return ((3*i) + j +1); +} + +void DigitalAuthDialog::setPassword(const QString password) +{ + //ToDo + Q_EMIT setPinCode(password); +} + +QString DigitalAuthDialog::getPassword() +{ + //nothing +} + +bool DigitalAuthDialog::checkPassword() +{ + if(m_loginType == LoginType::AUTHENTICATE) + { + m_password = m_inputPassword; + qDebug() << "Input finished" << m_password; + m_auth->respond(m_password); + m_isAuthing = true; +// m_timer->start(5000); +// onShowMessage(tr("now is authing, wait a moment"), Auth::MessageTypeInfo); + } else if (m_loginType == LoginType::RESET) + { + m_resetPassword = m_inputPassword; + m_title->setText(tr("ConfirmNewUEduPWD")); + m_title->adjustSize(); + m_title->setGeometry((this->width() - m_title->width())/2, 5, m_title->width(), m_title->height()); + m_inputPassword.clear(); + m_loginType = LoginType::SECONDCONFIRMATION; + Q_EMIT switchToReset(false); + } else if (m_loginType == LoginType::SECONDCONFIRMATION) + { + if (m_resetPassword == m_inputPassword) + { + setPassword(m_resetPassword); + } else { + m_resetOrAuthFailure->setText(tr("The two password entries are inconsistent, please reset")); + m_resetOrAuthFailure->setStyleSheet("font-size:14px;color:rgba(255, 255, 255, 255)"); + m_resetOrAuthFailure->adjustSize(); + m_resetOrAuthFailure->setGeometry((this->width()- m_resetOrAuthFailure->width())/2, 120, + m_resetOrAuthFailure->width(),m_resetOrAuthFailure->height()); + showResetPasswordPage(); + Q_EMIT switchToReset(true); + } + } + return false; +} + +//画密码状态的圆圈 +void DigitalAuthDialog::drawStatusCircle(QPainter *painter) +{ + painter->save(); + + m_circleColor.setAlphaF(0.45); + QPen pen(m_circleColor, 2); + painter->setPen(pen); + painter->setBrush(Qt::NoBrush); + + for(int i = 0; i < 6; i++) + { + int x = -11 * m_statusRadius + i * 4 * m_statusRadius; + int y = -2 * m_buttonHeight - (11 * m_buttonStep) / 2; + if (i < m_inputPassword.size()) { + painter->save(); + painter->setBrush(QColor("#ffffff")); + QRect rect(x, y, 2 * m_statusRadius, 2 * m_statusRadius); + painter->drawEllipse(rect); + painter->restore(); + } else { + painter->drawEllipse(x, y, 2 * m_statusRadius, 2 * m_statusRadius); + } + } + painter->restore(); +} + +void DigitalAuthDialog::onShowMessage(const QString &message, Auth::MessageType type) +{ + m_authMessage->setText(message); + m_authMessage->adjustSize(); + m_authMessage->setGeometry((this->width()-m_authMessage->width())/2, this->height() -20, m_authMessage->width(),m_authMessage->height()); +} + +void DigitalAuthDialog::cleanMessage() +{ + m_authMessage->clear(); +} + +void DigitalAuthDialog::onShowPrompt(const QString &prompt, Auth::PromptType type) +{ + qDebug() << "prompt: " << prompt; +} + +void DigitalAuthDialog::onAuthComplete() +{ + qDebug() << "Auth Complete"; +// m_timer->stop(); + DBusMsgCode errcode = EduPlatformInterface::getInstance()->CheckToken(getenv("USER")); + if(m_auth->isAuthenticated()) + { + /* + DBusMsgCode errcode = EduPlatformInterface::getInstance()->CheckToken(getenv("USER")); + + if(DBusMsgCode::Error_AccessTokenInvalid == errcode){ + m_resetOrAuthFailure->setText(tr("登录状态已过期,请重新扫码登录")); + m_resetOrAuthFailure->setStyleSheet("font-size:14px;color:rgba(255, 255, 255, 255)"); + m_resetOrAuthFailure->adjustSize(); + m_resetOrAuthFailure->setGeometry((this->width()- m_resetOrAuthFailure->width())/2, 120, + m_resetOrAuthFailure->width(),m_resetOrAuthFailure->height()); + + startAuth(); + m_inputPassword.clear(); + m_password.clear(); + m_isAuthing = false; + repaint(); + return; + } + */ + Q_EMIT authenticateCompete(true); + m_isAuthing = false; + } + else + { + qWarning() << "auth failed"; + if(m_loginType == LoginType::AUTHENTICATE) + { + m_resetOrAuthFailure->setText(tr("Password entered incorrectly, please try again")); + m_resetOrAuthFailure->setStyleSheet("font-size:14px;color:rgba(255, 255, 255, 255)"); + m_resetOrAuthFailure->adjustSize(); + m_resetOrAuthFailure->setGeometry((this->width()- m_resetOrAuthFailure->width())/2, 120, + m_resetOrAuthFailure->width(),m_resetOrAuthFailure->height()); + } + //认证失败,重新认证 + startAuth(); + } + m_inputPassword.clear(); + m_password.clear(); + m_isAuthing = false; + repaint(); +} + +void DigitalAuthDialog::startAuth() +{ + m_inputPassword.clear(); + m_password.clear(); + m_auth->authenticate(getenv("USER")); + m_isAuthing = false; + repaint(); +} + +void DigitalAuthDialog::stopAuth() +{ + m_isAuthing = false; +} + +void DigitalAuthDialog::startWaiting() +{ + +} + +void DigitalAuthDialog::stopWaiting() +{ + +} + +void DigitalAuthDialog::closeEvent(QCloseEvent *event) +{ + qDebug() << "DigitalAuthDialog::closeEvent"; + + if(m_auth && m_auth->isAuthenticating()) + { + m_auth->stopAuth(); + } + return QWidget::closeEvent(event); +} + +bool DigitalAuthDialog::isInButtonArea(int x1, int y1, int x2, int y2) +{ + if (x1 >= x2 && x1 <= (x2 + m_buttonWidth) && y1 >= y2 && y1 <= (y2 + m_buttonHeight)) + return true; + return false; +} + +void DigitalAuthDialog::reset() +{ + m_title->setText(tr("LoginByUEdu")); + m_title->adjustSize(); + m_title->setGeometry((this->width() - m_title->width())/2, 5, m_title->width(), m_title->height()); + + m_labelReset->show(); + m_inputPassword.clear(); + m_resetPassword.clear(); + m_loginType = LoginType::AUTHENTICATE; + repaint(); +} + +/** + * @brief DigitalAuthDialog::showResetPasswordPage + * 重新展示重置密码页 + */ +void DigitalAuthDialog::showResetPasswordPage() +{ + m_title->setText(tr("SetNewUEduPWD")); + m_title->adjustSize(); + m_title->setGeometry((this->width() - m_title->width())/2, 5, m_title->width(), m_title->height()); + m_loginType = LoginType::RESET; + m_resetPassword.clear(); + m_inputPassword.clear(); + repaint(); + Q_EMIT switchToReset(true); +} + +int DigitalAuthDialog::getStatus() +{ + return m_loginType; +} + +void DigitalAuthDialog::showErrorMessage(QString message) +{ + m_resetOrAuthFailure->setText(message); + m_resetOrAuthFailure->setStyleSheet("font-size:14px;color:rgba(255, 255, 255, 255)"); + m_resetOrAuthFailure->adjustSize(); + m_resetOrAuthFailure->setGeometry((this->width()- m_resetOrAuthFailure->width())/2, 120, + m_resetOrAuthFailure->width(),m_resetOrAuthFailure->height()); + + QTimer::singleShot(3000,[=](){ + m_resetOrAuthFailure->clear(); + }); +} + +MyLabel::MyLabel(const QString & text,QWidget *parent) : QLabel(parent) +{ + setText(text); +} + +MyLabel::MyLabel(QWidget *parent) : QLabel(parent) +{ + +} + +void MyLabel::mousePressEvent(QMouseEvent *event) +{ + //Qt::LeftButton + //Qt::RightButton + if(event->button()== Qt::LeftButton) + { + this->setStyleSheet("QLabel{font-size:16px;color:rgba(255, 255, 255, 0.45);}"); + } +} + +void MyLabel::mouseReleaseEvent(QMouseEvent *event) +{ + //Qt::LeftButton + //Qt::RightButton + if(event->button()== Qt::LeftButton) + { + this->setStyleSheet("QLabel{font-size:16px;color:rgba(255, 255, 255, 0.35);}" + "QLabel:hover{color:rgba(255, 255, 255, 1);}"); + Q_EMIT onClick(); + } +} diff --git a/src/digitalauthdialog.h b/src/digitalauthdialog.h new file mode 100644 index 0000000..630f9be --- /dev/null +++ b/src/digitalauthdialog.h @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Authors: ZHAI Kang-ning +**/ +#ifndef DIGITALAUTHDIALOG_H +#define DIGITALAUTHDIALOG_H + +#include +#include +#include +#include +#include +#include +#include "auth.h" +#include "digitalkeyboard.h" +#include + +class Auth; +class MyLabel; + +class DigitalAuthDialog : public QWidget +{ + Q_OBJECT +public: + struct PosInCircle + { + int i; //行 + int j; //列 + bool isIn; //是否在范围内 + }; + + enum LoginType{ + AUTHENTICATE = 0, + RESET, + SECONDCONFIRMATION, + }; + + explicit DigitalAuthDialog(QWidget *parent = nullptr); + ~DigitalAuthDialog(); + + void setPassword(const QString password); + QString getPassword(); + bool checkPassword(); + void reset(); + void showResetPasswordPage(); + int getStatus(); + void showErrorMessage(QString message); +// void RecieveKey(int key); +protected: + void paintEvent(QPaintEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void keyPressEvent(QKeyEvent *event) override; + void closeEvent(QCloseEvent *evert) override; + + void drawBg(QPainter *painter); + void drawOutCircle(QPainter *painter); + void drawFilledCircle(QPainter *painter); + void drawTipLine(QPainter *painter); + void drawInCircle(QPainter *painter); + void drawJoinLine(QPainter *painter); + void drawStatusCircle(QPainter *painter); + void drawHoverCircle(QPainter *painter); +private: + /** + * 圆形按钮,测量距离中心的距离 + * @brief distance + * @param x1 + * @param y1 + * @param x2 圆心x坐标 + * @param y2 圆心y坐标 + * @return + */ + double distance(int x1, int y1, int x2, int y2); + + bool isInButtonArea(int x1, int y1, int x2, int y2); + int posToNumber(int i, int j); + +// void onShowMessage(const QString &message); + void cleanMessage(); + + void startWaiting(); + void stopWaiting(); +Q_SIGNALS: + void correctPassword(bool isCorrect, QString password); + void authenticateCompete(bool result); + void requestPasswordReset(); + void setPinCode(QString pinCode); + void switchToReset(bool isReset); +private Q_SLOTS: + void onShowMessage(const QString &message, Auth::MessageType type); + void onShowPrompt(const QString &prompt, Auth::PromptType type); + void onAuthComplete(); +public Q_SLOTS: + void startAuth(); + void stopAuth(); + void onNumerPress(int btn_id); +private: + QColor m_bgColor; //背景色 + QColor m_outCircleColor; //外圆颜色 + QColor m_tipLineColor; //提示线颜色 + QColor m_circleRingColor; //圆环颜色 + QColor m_circleColor; //实心圆颜色 + int m_margin; //外边距 + int m_radius; //圆相对半径 + int m_buttonRadius; //按钮圆角半径 + int m_buttonWidth; //按钮宽度 + int m_buttonHeight; //按钮高度 + int m_buttonStep; //按钮间距 + PosInCircle m_posInCircle; //判断点是否在圆内 + bool m_pressed; //鼠标按下 + bool m_showText; //是否显示文字 + + QVector m_vecInputPoints; //鼠标移动选点 + QPoint m_followPoint; //鼠标跟随点 + QString m_password; //原密码 + QString m_inputPassword; //鼠标移动选点转换为密码 + + //label + QLabel *m_title; + MyLabel *m_labelReset; + + //密码状态更新布局 + int m_statusRadius; + + //认证中? + bool m_isAuthing; + + //数字键盘 + DigitalKeyBoard *m_digitalKeyBoard; + + /** + * 二次认证相关成员变量 + */ + int m_loginType = LoginType::AUTHENTICATE; + QString m_resetPassword; + QLabel *m_resetOrAuthFailure; + + QLabel *m_authMessage; + + //pam auth + Auth *m_auth; + +// QTimer *m_timer; +}; + +class MyLabel : public QLabel{ + Q_OBJECT +public: + MyLabel(const QString & text, QWidget *parent); + MyLabel(QWidget *parent); + +Q_SIGNALS: + void onClick(); + +protected: + void mouseReleaseEvent(QMouseEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; +}; + +#endif // DIGITALAUTHDIALOG_H diff --git a/src/digitalkeyboard.cpp b/src/digitalkeyboard.cpp new file mode 100644 index 0000000..77239ba --- /dev/null +++ b/src/digitalkeyboard.cpp @@ -0,0 +1,129 @@ +#include "digitalkeyboard.h" + +#include +#include +#include + +DigitalKeyBoard::DigitalKeyBoard(QWidget *parent): + QWidget(parent) +{ + initUI(); + initConnect(); + setQSS(); +} + +void DigitalKeyBoard::initUI() +{ + setFocusPolicy(Qt::NoFocus); + QGridLayout* mainLayout = new QGridLayout(this); + setLayout(mainLayout); + + for(int i = 1;i <= 9;i++) + { + m_pNumerPressBT[i] = new QPushButton(this); + m_pNumerPressBT[i]->setText(QString(QChar(i + '0'))); + m_pNumerPressBT[i]->setProperty("class", "numberPressBT"); + m_pNumerPressBT[i]->setFocusPolicy(Qt::NoFocus); + mainLayout->addWidget(m_pNumerPressBT[i], (i - 1) / 3, (i - 1) % 3); + } + m_pNumerPressBT[0] = new QPushButton(this); + m_pNumerPressBT[0]->setText(QString(QChar('0'))); + m_pNumerPressBT[0]->setFocusPolicy(Qt::NoFocus); + m_pNumerPressBT[0]->setProperty("class", "numberPressBT"); + mainLayout->addWidget(m_pNumerPressBT[0], 3, 1); + + m_pNumerPressBT[10] = new QPushButton(this); + m_pNumerPressBT[10]->setIcon(QPixmap(":/image/assets/intel/icon-backspace.png")); + m_pNumerPressBT[10]->setIconSize(QSize(30, 30)); + m_pNumerPressBT[10]->setFocusPolicy(Qt::NoFocus); + m_pNumerPressBT[10]->setProperty("class", "numberPressBT"); + mainLayout->addWidget(m_pNumerPressBT[10], 3, 2); + + m_pNumerPressBT[11] = new QPushButton(this); + m_pNumerPressBT[11]->setText("清空"); + m_pNumerPressBT[11]->setFocusPolicy(Qt::NoFocus); + m_pNumerPressBT[11]->setProperty("class", "numberPressBT"); + mainLayout->addWidget(m_pNumerPressBT[11], 3, 0); + + mainLayout->setSpacing(16); + mainLayout->setVerticalSpacing(16); +} + +void DigitalKeyBoard::initConnect() +{ + connect(m_pNumerPressBT[0], &QPushButton::clicked, this, [=](){ qDebug() << "DigitalKeyBoard press";emit numbersButtonPress(0); }); + connect(m_pNumerPressBT[1], &QPushButton::clicked, this, [=](){ emit numbersButtonPress(1); }); + connect(m_pNumerPressBT[2], &QPushButton::clicked, this, [=](){ emit numbersButtonPress(2); }); + connect(m_pNumerPressBT[3], &QPushButton::clicked, this, [=](){ emit numbersButtonPress(3); }); + connect(m_pNumerPressBT[4], &QPushButton::clicked, this, [=](){ emit numbersButtonPress(4); }); + connect(m_pNumerPressBT[5], &QPushButton::clicked, this, [=](){ emit numbersButtonPress(5); }); + connect(m_pNumerPressBT[6], &QPushButton::clicked, this, [=](){ emit numbersButtonPress(6); }); + connect(m_pNumerPressBT[7], &QPushButton::clicked, this, [=](){ emit numbersButtonPress(7); }); + connect(m_pNumerPressBT[8], &QPushButton::clicked, this, [=](){ emit numbersButtonPress(8); }); + connect(m_pNumerPressBT[9], &QPushButton::clicked, this, [=](){ emit numbersButtonPress(9); }); + connect(m_pNumerPressBT[10], &QPushButton::clicked, this, [=](){ emit numbersButtonPress(10); }); + connect(m_pNumerPressBT[11], &QPushButton::clicked, this, [=](){ emit numbersButtonPress(11); }); +} + +void DigitalKeyBoard::setQSS() +{ + setStyleSheet(".numberPressBT{" + "font-size:20px;" + "min-width:96px;" + "min-height:64px;" + "max-width:96px;" + "max-height:64px;" + "background:rgba(255, 255, 255, 15);" + "border-radius:16px;" + "}" + ".numberPressBT:hover{" + "background:rgba(255, 255, 255, 35);" + "}" + ".numberPressBT:pressed{" + "background:rgba(255, 255, 255, 5);" + "}"); +} + + +void DigitalKeyBoard::onKeyReleaseEvent(QKeyEvent *event) +{ +// switch(event->key()) +// { +// case Qt::Key_0: +// emit numbersButtonPress(0); +// break; +// case Qt::Key_1: +// emit numbersButtonPress(1); +// break; +// case Qt::Key_2: +// emit numbersButtonPress(2); +// break; +// case Qt::Key_3: +// emit numbersButtonPress(3); +// break; +// case Qt::Key_4: +// emit numbersButtonPress(4); +// break; +// case Qt::Key_5: +// emit numbersButtonPress(5); +// break; +// case Qt::Key_6: +// emit numbersButtonPress(6); +// break; +// case Qt::Key_7: +// emit numbersButtonPress(7); +// break; +// case Qt::Key_8: +// emit numbersButtonPress(8); +// break; +// case Qt::Key_9: +// emit numbersButtonPress(9); +// break; +// case Qt::Key_Backspace: +// emit numbersButtonPress(10); +// break; +// default: +// break; +// } +} + diff --git a/src/digitalkeyboard.h b/src/digitalkeyboard.h new file mode 100644 index 0000000..107a115 --- /dev/null +++ b/src/digitalkeyboard.h @@ -0,0 +1,31 @@ +#ifndef DIGITALKEYBOARD_H +#define DIGITALKEYBOARD_H + +#include +#include +#include + +class DigitalKeyBoard : public QWidget +{ + Q_OBJECT + +public: + explicit DigitalKeyBoard(QWidget *parent = nullptr); + +public Q_SLOTS: + void onKeyReleaseEvent(QKeyEvent *event); + +Q_SIGNALS: + void numbersButtonPress(int btn_id); + +private: + void initUI(); + void initConnect(); + void setQSS(); + +private: + QPushButton *m_pNumerPressBT[12]; // 0~9 是数字按键 10是删除键 11是清空键 +}; + +#endif // DIGITALKEYBOARD_H + diff --git a/src/displaymanager.cpp b/src/displaymanager.cpp new file mode 100644 index 0000000..abc9e0b --- /dev/null +++ b/src/displaymanager.cpp @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#include "displaymanager.h" +#include +#include +#include +#include +#include +#include "types.h" + +DisplayManager::DisplayManager(QObject *parent) : + QObject(parent), + _canSwitch(false), + _displayType(""), + process(nullptr) +{ + + if(getenv("XDG_SEAT_PATH")){ + _displayType = "lightdm"; + } + else if(process_is_running ("gdm") || process_is_running("gdm3") || process_is_running("gdm-binary")){ + _displayType = "gdm"; + process = new QProcess(this); + } + + if(_displayType == "lightdm"){ + char *seatPath = getenv("XDG_SEAT_PATH"); + qDebug() << seatPath; + + dmService = new QDBusInterface(DM_DBUS_SERVICE, + seatPath, + DBUS_PROP_INTERFACE, + QDBusConnection::systemBus()); + dmSeatService = new QDBusInterface(DM_DBUS_SERVICE, + seatPath, + DM_SEAT_INTERFACE, + QDBusConnection::systemBus()); + getProperties(); + }else if(_displayType == "gdm"){ + _canSwitch = true; + } +} + +bool DisplayManager::canSwitch() +{ + return _canSwitch; +} + +bool DisplayManager::hasGuestAccount() +{ + return _hasGuestAccount; +} + +QString DisplayManager::getDisplayType() +{ + return _displayType; +} + +void DisplayManager::switchToGreeter() +{ + qDebug()<<"111111111111111111111111111111111111111111111111111111"; + if(_displayType == "lightdm"){ + QDBusMessage ret = dmSeatService->call("SwitchToGreeter"); + handleDBusError(ret); + } + else if(_displayType == "gdm"){ + QString cmd = "gdmflexiserver"; + if(QFile("/usr/bin/gdmflexiserver").exists()){ + process->waitForFinished(3000); + process->start(cmd); + } + } +} + +void DisplayManager::switchToUser(const QString &userName) +{ + if(_displayType != "lightdm") + return ; + + QDBusMessage ret = dmSeatService->call("SwitchToUser", userName, ""); + + handleDBusError(ret); +} + +void DisplayManager::switchToGuest() +{ + if(_displayType != "lightdm") + return ; + + QDBusMessage ret = dmSeatService->call("SwitchToGuest", ""); + + handleDBusError(ret); +} + +void DisplayManager::getProperties() +{ + QDBusMessage ret = dmService->call("GetAll", DM_SEAT_INTERFACE); + handleDBusError(ret); + const QDBusArgument &arg = ret.arguments().at(0).value(); + qDebug() << arg.currentType(); + + arg.beginMap(); + while(!arg.atEnd()) + { + QString key; + QVariant value; + arg.beginMapEntry(); + arg >> key >> value; + arg.endMapEntry(); + if(key == "CanSwitch") + { + _canSwitch = value.toBool(); + } + else if(key == "HasGuestAccount") + { + _hasGuestAccount = value.toBool(); + } + } + arg.endMap(); +} + +bool DisplayManager::process_is_running(QString name) +{ + int num_processes; + QString command = "pidof " + name + " | wc -l"; + FILE *fp = popen(command.toLatin1().data(), "r"); + + if (fscanf(fp, "%d", &num_processes) != 1) + num_processes = 0; + + pclose(fp); + + if (num_processes > 0) { + return true; + } else { + return false; + } +} + +void DisplayManager::handleDBusError(const QDBusMessage &msg) +{ + if(msg.type() == QDBusMessage::ErrorMessage) + { + qWarning() << msg.errorMessage(); + } +} diff --git a/src/displaymanager.h b/src/displaymanager.h new file mode 100644 index 0000000..741be7d --- /dev/null +++ b/src/displaymanager.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#ifndef DISPLAYMANAGER_H +#define DISPLAYMANAGER_H + +#include +#include + +class QDBusInterface; +class QDBusMessage; + +class DisplayManager : public QObject +{ + Q_OBJECT +public: + explicit DisplayManager(QObject *parent = nullptr); + void switchToGreeter(); + void switchToUser(const QString &userName); + void switchToGuest(); + bool canSwitch(); + bool hasGuestAccount(); + + QString getDisplayType(); + +private: + void getProperties(); + void handleDBusError(const QDBusMessage &msg); + bool process_is_running(QString name); + +private: + bool _canSwitch; + bool _hasGuestAccount; + QString _displayType; + + QDBusInterface *dmService; + QDBusInterface *dmSeatService; + QProcess *process; +}; + +#endif // DISPLAYMANAGER_H diff --git a/src/eduplatforminterface.cpp b/src/eduplatforminterface.cpp new file mode 100644 index 0000000..47084ef --- /dev/null +++ b/src/eduplatforminterface.cpp @@ -0,0 +1,375 @@ +#include "eduplatforminterface.h" + +const QString STR_EDU_SERVICE = "cn.kylinos.SSOBackend"; +const QString STR_EDU_PATH = "/cn/kylinos/SSOBackend"; + +EduPlatformInterface* EduPlatformInterface::m_instance = nullptr; + +EduPlatformInterface::EduPlatformInterface(const QString &strService,const QString &strPath,\ + const QDBusConnection &connection, QObject *parent)\ + :QDBusAbstractInterface(strService,strPath,getInterfaceName(),connection,parent) +{ + initConnect(); +} + +EduPlatformInterface* EduPlatformInterface::getInstance() +{ + static QMutex mutex; + mutex.lock(); + if(m_instance == nullptr || !m_instance->isValid()) + m_instance = new EduPlatformInterface(STR_EDU_SERVICE, STR_EDU_PATH, QDBusConnection::systemBus()); + mutex.unlock(); + return m_instance; +} + +void EduPlatformInterface::initConnect() +{ + +} + +DBusMsgCode EduPlatformInterface::GetLoginQR(QPixmap &qrcode) +{ + QDBusMessage message = call("GetLoginQR"); + if(QDBusMessage::ErrorMessage == message.type()) + { + qDebug() << "info: [EduPlatformInterface][GetLoginQR]: DBus Connect Failed!"; + return DBusMsgCode::Error_NoReply; + } + QList argvs = message.arguments(); + if(argvs.size() != 2) + { + qDebug() << "info: [EduPlatformInterface][GetLoginQR]: DBus arguments error!"; + return DBusMsgCode::Error_ArgCnt; + } + int state = argvs.at(1).value(); + if(state != 0) + { + qDebug() << "info: [EduPlatformInterface][GetLoginQR]: DBus request failed!"; + //return static_cast(state); + return DBusMsgCode::Error_ArgCnt; + } + const QString url = argvs.at(0).value(); + + DBusMsgCode download_errcode = getQRCodeFromURL(url, qrcode); + if(download_errcode != DBusMsgCode::No_Error) return download_errcode; + qrcode = beautifyQRCode(qrcode); + return DBusMsgCode::No_Error; +} + +DBusMsgCode EduPlatformInterface::GetLoginQRUrl(QString &url) +{ + QDBusMessage message = call("GetLoginQR"); + if(QDBusMessage::ErrorMessage == message.type()) + { + qDebug() << "info: [EduPlatformInterface][GetLoginQR]: DBus Connect Failed!"; + return DBusMsgCode::Error_NoReply; + } + QList argvs = message.arguments(); + if(argvs.size() != 2) + { + qDebug() << "info: [EduPlatformInterface][GetLoginQR]: DBus arguments error!"; + return DBusMsgCode::Error_ArgCnt; + } + int state = argvs.at(1).value(); + if(state != 0) + { + qDebug() << "info: [EduPlatformInterface][GetLoginQR]: DBus request failed!"; + //return static_cast(state); + return DBusMsgCode::Error_ArgCnt; + } + url = argvs.at(0).value(); + return DBusMsgCode::No_Error; +} + +DBusMsgCode EduPlatformInterface::BindPhoneNum(const QString &username, const QString &phonenum, const QString &vcode) +{ + QDBusReply reply = call("BindPhoneNum", username, phonenum, vcode); + if(!reply.isValid()) + { + qDebug() << "info: [EduPlatformInterface][BindPhoneNum]: DBus Connect Failed!"; + return DBusMsgCode::Error_NoReply; + } + if(reply.value() != 0) + { + qDebug() << "info: [EduPlatformInterface][BindPhoneNum]: DBus request failed!"; + //return static_cast(reply.value()); + return DBusMsgCode::Error_NoReply; + } + + return DBusMsgCode::No_Error; +} + +DBusMsgCode EduPlatformInterface::GetVerifyCode(const QString &phonenum) +{ + QDBusReply reply = call("GetVerifyCode", phonenum); + if(!reply.isValid()) + { + qDebug() << "info: [EduPlatformInterface][GetVerifyCode]: DBus Connect Failed!"; + return DBusMsgCode::Error_NoReply; + } + // + if(reply.value() != 0) // 默认0为成功 + { + qDebug() << "info: [EduPlatformInterface][GetVerifyCode]: DBus request failed!"; + //return static_cast(reply.value()); + return DBusMsgCode::Error_NoReply; + } + + return DBusMsgCode::No_Error; +} + +DBusMsgCode EduPlatformInterface::SetPinCode(const QString &username, const QString &pincode) +{ + QDBusReply reply = call("SetPinCode", username, pincode); + if(!reply.isValid()) + { + qDebug() << "info: [][]: DBus connect failed!"; + return DBusMsgCode::Error_NoReply; + } + if(!reply.value()) + { + return DBusMsgCode::No_Error; + } + return DBusMsgCode::Error_NoReply; +} + +DBusMsgCode EduPlatformInterface::CheckToken(const QString &username) +{ + QDBusReply reply = call("CheckToken", username); + if(!reply.isValid()) + { + qDebug() << "info: [EduPlatformInterface][RefreshToken]: DBus connect failed!"; + return DBusMsgCode::Error_NoReply; + } +// if(reply.value()) +// { +// qDebug() << "info: [EduPlatformInterface][RefreshToken]: DBus return-result failed!"; +// } + switch (reply.value()) { + case 0: return DBusMsgCode::No_Error; + default: return static_cast(reply.value()); + } +} + +DBusMsgCode EduPlatformInterface::CheckPhoneNumBind(const QString &username, bool &isBind) +{ + QDBusReply reply = call("CheckPhoneNumBind", username); + if(!reply.isValid()) + { + qDebug() << "info: [EduPlatformInterface][CheckPhoneNumBind]: DBus connect failed!"; + return DBusMsgCode::Error_NoReply; + } + // TODO 需要改接口 接口有问题 + if(reply.value() == 0) + { + isBind = false; + return DBusMsgCode::No_Error; + } + if(reply.value() == 1) + { + isBind = true; + return DBusMsgCode::No_Error; + } + isBind = true; + return DBusMsgCode::Error_NoReply; +} + +DBusMsgCode EduPlatformInterface::CheckPincodeSet(const QString &username, bool &isBind) +{ + QDBusReply reply = call("CheckPincodeSet", username); + if(!reply.isValid()) + { + qDebug() << "info: [EduPlatformInterface][CheckPincodeSet]: DBus connect failed!"; + return DBusMsgCode::Error_NoReply; + } + + if(reply.value() == 0) + { + isBind = true; + return DBusMsgCode::No_Error; + } + if(reply.value() == 1) + { + isBind = false; + return DBusMsgCode::No_Error; + } + + return DBusMsgCode::Error_NoReply; +} + +DBusMsgCode EduPlatformInterface::CheckUserByPhone(const QString &username, const QString &phonenum, const QString &vcode) +{ + QDBusReply reply = call("CheckUserByPhone", username, phonenum, vcode); + if(!reply.isValid()) + { + qDebug() << "info: [EduPlatformInterface][CheckUserByPhone]: DBus connect failed!"; + return DBusMsgCode::Error_NoReply; + } + return static_cast(reply.value()); +} + +DBusMsgCode EduPlatformInterface::CheckUserByQR(QPixmap &qrcode) +{ + QDBusMessage message = call("CheckUserByQR"); + if(QDBusMessage::ErrorMessage == message.type()) + { + qDebug() << "info: [EduPlatformInterface][CheckUserByQR]: DBus Connect Failed!"; + return DBusMsgCode::Error_NoReply; + } + QList argvs = message.arguments(); + if(argvs.size() != 2) + { + qDebug() << "info: [EduPlatformInterface][CheckUserByQR]: DBus arguments error!"; + return DBusMsgCode::Error_ArgCnt; + } + int state = argvs.at(1).value(); + if(state != 0) + { + qDebug() << "info: [EduPlatformInterface][CheckUserByQR]: DBus request failed!"; + //return static_cast(state); + return static_cast(state); + } + const QString url = argvs.at(0).value(); + + DBusMsgCode download_errcode = getQRCodeFromURL(url, qrcode); + if(download_errcode != DBusMsgCode::No_Error) return download_errcode; + qrcode = beautifyQRCode(qrcode); + return DBusMsgCode::No_Error; +} + +DBusMsgCode EduPlatformInterface::CheckUserByQR(QString &url) +{ + QDBusMessage message = call("CheckUserByQR"); + if(QDBusMessage::ErrorMessage == message.type()) + { + qDebug() << "info: [EduPlatformInterface][CheckUserByQR]: DBus Connect Failed!"; + return DBusMsgCode::Error_NoReply; + } + QList argvs = message.arguments(); + if(argvs.size() != 2) + { + qDebug() << "info: [EduPlatformInterface][CheckUserByQR]: DBus arguments error!"; + return DBusMsgCode::Error_ArgCnt; + } + int state = argvs.at(1).value(); + if(state != 0) + { + qDebug() << "info: [EduPlatformInterface][CheckUserByQR]: DBus request failed!"; + //return static_cast(state); + return static_cast(state); + } + url = argvs.at(0).value(); + return DBusMsgCode::No_Error; +} + +DBusMsgCode EduPlatformInterface::getQRCodeFromURL(const QString &url, QPixmap &qrcode) +{ + // 从网络上下载二维码 并加载到qrcode + QNetworkRequest request; + QNetworkAccessManager networkManager; + request.setUrl(url); + QNetworkReply *reply = networkManager.get(request); + + // 超时 \ 同步处理 + QEventLoop loop; + QTimer timer; + connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); + connect(&timer, &QTimer::timeout, &timer, &QTimer::stop); + connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); + timer.start(3000); + loop.exec(); + + if(!timer.isActive()) + { + reply->abort(); + qDebug() << "info: [EduPlatformInterface][GetLoginQR]: Get QRCode timeout!"; + return DBusMsgCode::Error_QR_Get_Timeout; + } + timer.stop(); + + // 生成二维码 + if(reply->error() != QNetworkReply::NoError) + { + qDebug() << "info: [EduPlatformInterface][GetLoginQR]: Network error!" << reply->errorString(); + return DBusMsgCode::Error_NetWork; + } + QByteArray bytes = reply->readAll(); + qrcode.loadFromData(bytes); + return DBusMsgCode::No_Error; +} + +// 美化图片 +QPixmap EduPlatformInterface::beautifyQRCode(QPixmap &pixImg) +{ + // 去除白边 + QImage img = pixImg.toImage(); + int rect_x, rect_y, rect_width, rect_height; + for(int i = 0;i < img.size().width();i++) + { + bool isEnd = false; + for(int j = 0;j < img.size().height();j++) + { + if(img.pixel(i, j) != qRgb(255, 255, 255)) + { + rect_x = i - 1; + rect_y = j - 1; + rect_width = img.size().width() - 2 * rect_x; + rect_height = img.size().height() - 2 * rect_y; + isEnd = true; + break; + } + } + if(isEnd) break; + } + QPixmap dealImg = QPixmap::fromImage(img.copy(rect_x, rect_y, rect_width, rect_height)).scaledToWidth(150); + img = dealImg.toImage(); + + // 添加微信log +// QPixmap piximg_wechat(WECHAT_LOG_PATH); +// QImage img_wechat = piximg_wechat.scaledToWidth(150).toImage(); +// for(int i = 0;i < img.size().width();++i) +// { +// for(int j = 0;j < img.size().height(); ++j) +// { +// if(img.pixel(i, j) > qRgb(125, 125, 125)) +// { +// int r = qRed(img_wechat.pixel(i, j)); +// int g = qGreen(img_wechat.pixel(i, j)); +// int b = qBlue(img_wechat.pixel(i, j)); +// int alpha = qAlpha(img_wechat.pixel(i, j)); +// if(alpha == 0) r = 255, b = 255, g = 255; +// else r = 0x25, g = 0xAC, b = 0x36; +// img.setPixelColor(i, j, QColor(r, g, b, alpha)); +// //img.setPixel(i, j, qRgb(0, 255, 0)); +// } +// } +// } + dealImg = QPixmap::fromImage(img); + return dealImg; +} + +//void EduPlatformInterface::onScanState(QString username, QString password, int state) +//{ +// switch(state) +// { +// case 4: // 扫码并确认完成 +// qDebug() << "=================扫码并确认完成" << username << password; +// emit qrcodeScanState(QRCodeSwepState::ConfirmSuccess, username, password); +// break; +// case 2: // 用户扫码 +// qDebug() << "=================用户扫码" << username << password; +// emit qrcodeScanState(QRCodeSwepState::HaveSwep, "", ""); +// break; +// case 3: // 用户取消扫码 +// qDebug() << "=================用户取消扫码" << username << password; +// emit qrcodeScanState(QRCodeSwepState::CancelSwep, "", ""); +// break; +// case 5: // 二维码超时 +// qDebug() << "=================二维码超时" << username << password; +// emit qrcodeScanState(QRCodeSwepState::QRCodeInvalid, "", ""); +// break; +// default: +// break; +// } +//} + diff --git a/src/eduplatforminterface.h b/src/eduplatforminterface.h new file mode 100644 index 0000000..60e72dc --- /dev/null +++ b/src/eduplatforminterface.h @@ -0,0 +1,66 @@ +#ifndef EDUPLATFORMINTERFACE_H +#define EDUPLATFORMINTERFACE_H +#include +#include +#include +#include "common.h" +#include + +#include +#include +#include +#include +#include + +class EduPlatformInterface: public QDBusAbstractInterface +{ + Q_OBJECT +public: + static inline const char* getInterfaceName() + {return "cn.kylinos.SSOBackend.eduplatform";} +public: + EduPlatformInterface(const QString &strService,const QString &strPath,\ + const QDBusConnection &connection, QObject *parent = 0); + + static EduPlatformInterface* getInstance(); + + // 得到登录二维码地址 + DBusMsgCode GetLoginQRUrl(QString &url); + // 绑定手机号 + DBusMsgCode BindPhoneNum(const QString &username, const QString &phonenum, const QString &vcode); + // 得到登录二维码 + DBusMsgCode GetLoginQR(QPixmap &qrcode); + // 得到验证码 + DBusMsgCode GetVerifyCode(const QString &phonenum); + // 设置Pin码 + DBusMsgCode SetPinCode(const QString &username, const QString &pincode); + // 检查手机号绑定 + DBusMsgCode CheckPhoneNumBind(const QString &username, bool &isBind); + // 检查Pin码设置 + DBusMsgCode CheckPincodeSet(const QString &username, bool &isBind); + // 验证手机号正确性 + DBusMsgCode CheckUserByPhone(const QString &username, const QString &phonenum, const QString &vcode); + // 通过二维码检查用户身份 + DBusMsgCode CheckUserByQR(QPixmap &qrcode); + // 通过二维码检查用户身份:获取二维码地址 + DBusMsgCode CheckUserByQR(QString &url); + + // 通过url获取二维码图片数据 + DBusMsgCode getQRCodeFromURL(const QString &url, QPixmap &qrcode); + + // 获取用户登录状态 + DBusMsgCode CheckToken(const QString &username); +private: + void initConnect(); + QPixmap beautifyQRCode(QPixmap &); + + static EduPlatformInterface *m_instance; + +Q_SIGNALS: + void QRStatusChange(QString,QString,int); + +private: + +}; + +#endif // EDUPLATFORMINTERFACE_H diff --git a/src/fullbackgroundwidget.cpp b/src/fullbackgroundwidget.cpp new file mode 100644 index 0000000..c199287 --- /dev/null +++ b/src/fullbackgroundwidget.cpp @@ -0,0 +1,1154 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#include "fullbackgroundwidget.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "lockwidget.h" +#include "xeventmonitor.h" +#include "monitorwatcher.h" +#include "configuration.h" +#include "screensaver.h" +#include "screensaverwidget.h" +#include "grab-x11.h" +#include "tabletlockwidget.h" +#include "sessionwatcher.h" +// 实现键盘f1 - f2 的功能键 +#include "PhysicalDeviceSet/sounddeviceset.h" +#include "PhysicalDeviceSet/brightnessdeviceset.h" + +#include "config.h" + +enum { + SWITCH_TO_LINUX = 0, + SWITCH_TO_ANDROID = 1, + BACK_TO_DESKTOP = 2, + TEST_CONNECTION = 3, +}; + +QT_BEGIN_NAMESPACE +extern void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0); +QT_END_NAMESPACE + +#define BLUR_RADIUS 300 +#define GSETTINGS_SCHEMA_SCREENSAVER "org.ukui.screensaver" +#define KEY_IDLE_DELAY "idleDelay" +#define KEY_IDLE_LOCK "idleLock" +#define KEY_IDLE_LOCK_ENABLED "idleLockEnabled" + +QPixmap scaledPixmap(int width, int height, QString url) +{ + QFile imgFile(url); + if(!imgFile.exists()){ + qDebug()<< "not find the pixmap file"; + return QPixmap(); + } + QImage image(url); + QPixmap pixmap = QPixmap::fromImage(image); + if(pixmap.isNull()) { + qDebug() << "pixmap is null"; + QProcess exec; + QString program("file " + url); + exec.start(program); + exec.waitForFinished(1000); + QString output = exec.readAllStandardOutput(); + qDebug() << output; + if(output.contains("SVG")){ + qDebug() << "image format is SVG"; + QSvgRenderer render(url); + QImage image(width, height, QImage::Format_ARGB32); + image.fill(Qt::transparent); + QPainter painter(&image); + render.render(&painter, image.rect()); + pixmap.convertFromImage(image); + } else if(output.contains("TIFF")) { + qDebug() << "image format is TIFF"; + + } + } + + return pixmap.scaled(width, height, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); +} + +int connect_to_switch(const char* path) +{ + int ret; + int connect_fd; + struct sockaddr_un srv_addr; + + connect_fd = socket(PF_UNIX,SOCK_STREAM,0); + if(connect_fd < 0) { + + return -1; + } + + srv_addr.sun_family=AF_UNIX; + strcpy(srv_addr.sun_path, path); + + ret = connect(connect_fd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)); + if(ret < 0) { + perror("cannot connect to the server"); + close(connect_fd); + return -1; + } + + return connect_fd; +} + +int switch_to_linux(const char* container) +{ + int connect_fd; + int32_t switch_to = BACK_TO_DESKTOP; + + char path[1024] = {0}; + sprintf(path, "/var/lib/kydroid/%s/sockets/%s", container, "desktop-switch"); + printf("path = %s\n",path); + connect_fd = connect_to_switch(path); + + if(connect_fd < 0) + return -1; + + write(connect_fd, &switch_to, sizeof(switch_to)); + close(connect_fd); + return 0; +} + +void x11_get_screen_size(int *width,int *height) +{ + Display* display; + + display = XOpenDisplay(NULL); + if (display == NULL) { + fprintf(stderr, "Cannot connect to X server %s/n", "simey:0"); + exit (-1); + } + int screen_num; + + screen_num = DefaultScreen(display); + + *width = DisplayWidth(display, screen_num); + *height = DisplayHeight(display, screen_num); + XCloseDisplay(display); + +} + +QPixmap blurPixmap(QPixmap pixmap) +{ + QPainter painter(&pixmap); + QImage srcImg = pixmap.toImage(); + qt_blurImage(&painter, srcImg, BLUR_RADIUS, false, false); + + //在设置Qt::WA_TranslucentBackground属性后,模糊图片会导致锁屏界面透明 + //因此这里修改image图形的alpha值为255. + for (int y = 0;y < srcImg.height();++y) { + QRgb *row = (QRgb*)srcImg.scanLine(y); + for (int x = 0; x < srcImg.width(); ++x) { + ((unsigned char*)&row[x])[3] = 255; + } + } + + painter.end(); + return QPixmap::fromImage(srcImg); +} + +FullBackgroundWidget::FullBackgroundWidget(QWidget *parent) + : QWidget(parent), + lockWidget(nullptr), + xEventMonitor(new XEventMonitor(this)), + //monitorWatcher(new MonitorWatcher(this)), + configuration(Configuration::instance()), + isLocked(false), + isPassed(false), + lockState(false), + screenStatus(UNDEFINED), + isBlank(false), + m_delay(false) +{ + qDebug() << "init - screenStatus: " << screenStatus; + setMouseTracking(true); + // connect(monitorWatcher, &MonitorWatcher::monitorCountChanged, + // this, &FullBackgroundWidget::onScreenCountChanged); + QDesktopWidget *desktop = QApplication::desktop(); + + connect(desktop, &QDesktopWidget::resized, + this, &FullBackgroundWidget::onDesktopResized); + connect(desktop, &QDesktopWidget::workAreaResized, + this, &FullBackgroundWidget::onDesktopResized); + connect(desktop, &QDesktopWidget::primaryScreenChanged, + this, &FullBackgroundWidget::onDesktopResized); + connect(desktop, &QDesktopWidget::screenCountChanged, + this, &FullBackgroundWidget::onDesktopResized); + + QDBusInterface *iface = new QDBusInterface("org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + QDBusConnection::systemBus(), + this); + connect(iface, SIGNAL(PrepareForSleep(bool)), this, SLOT(onPrepareForSleep(bool))); + + QDBusInterface *interfaceScreensaver = new QDBusInterface( + SS_DBUS_SERVICE, + SS_DBUS_PATH, + SS_DBUS_INTERFACE, + QDBusConnection::sessionBus()); + + connect(interfaceScreensaver, SIGNAL(SessionIdle()), + this, SLOT(showScreensaver())); + + settings_delay = new QGSettings(GSETTINGS_SCHEMA_SCREENSAVER, "", this); + connect(settings_delay, &QGSettings::changed, + this, &FullBackgroundWidget::onConfigurationDelayChanged); + idleDelay = settings_delay->get("idle-delay").toInt(); + qDebug()<<"idleDelay="<get("idle-lock").toInt(); + +// lockEnabled_Key = new QGSettings(GSETTINGS_SCHEMA_SCREENSAVER, "", this); +// connect(lockEnabled_Key, &QGSettings::changed, +// this, &FullBackgroundWidget::lockEnabledChanged); +// lockEnabled = lockEnabled_Key->get("idle-lock-enabled").toBool(); +// qDebug()<, QStringList))); +#endif + + init(); + qApp->installNativeEventFilter(this); + installEventFilter(this); + QTimer::singleShot(500,this,SLOT(switchToLinux())); + + LogindIntegration *m_logind = new LogindIntegration(this); + + + connect(m_logind, &LogindIntegration::requestUnlock, this, + [this]() { + closeScreensaver(); + }); + +} + +#ifdef USE_INTEL +void FullBackgroundWidget::propertiesChangedSlot(QString property, QMap propertyMap, QStringList propertyList) +{ + Q_UNUSED(property); + Q_UNUSED(propertyList); + if (propertyMap.keys().contains("LidIsClosed")) { + qDebug() <<"LidIsClosed" << propertyMap.value("LidIsClosed").toBool(); +// if(!(propertyMap.value("LidIsClosed").toBool())) +// { +// m_isSuspend = true; +// } + } +} +#endif + +void FullBackgroundWidget::switchToLinux() +{ + struct passwd *pwd; + pwd = getpwuid(getuid()); + char *username = pwd->pw_name; + int uid = pwd->pw_uid; + char container[100]= {0}; + + sprintf(container,"kydroid2-%d-%s",uid,username); + + switch_to_linux(container); + +} + +void FullBackgroundWidget::laterActivate() +{ + activateWindow(); + raise(); + setFocus(); + if(lockWidget && lockWidget->isVisible()) + lockWidget->setFocus(); +} + +void FullBackgroundWidget::onConfigurationDelayChanged(QString key) +{ + if(key == KEY_IDLE_DELAY){ + idleDelay = settings_delay->get("idle-delay").toInt(); + } +} + +void FullBackgroundWidget::onConfigurationLockChanged(QString key) +{ + if(key == KEY_IDLE_LOCK){ + idleLock = settings_lock->get("idle-lock").toInt(); + } +} + +//void FullBackgroundWidget::lockEnabledChanged(QString key) +//{ +// if(key == KEY_IDLE_LOCK_ENABLED){ +// lockEnabled = lockEnabled_Key->get("idle-lock-enabled").toBool(); +// } +//} + +void FullBackgroundWidget::setLockState() +{ + if(lockState == true) + return ; + + lockState = true; + + QDBusInterface *interface = new QDBusInterface(SS_DBUS_SERVICE, + SS_DBUS_PATH, + SS_DBUS_INTERFACE); + QDBusMessage msg = interface->call("SetLockState"); + if(msg.type() == QDBusMessage::ErrorMessage) + qDebug() << msg.errorMessage(); + +} + +bool FullBackgroundWidget::eventFilter(QObject *obj, QEvent *event) +{ + if(event->type() == QEvent::WindowDeactivate){ + QTimer::singleShot(50,this,SLOT(laterActivate())); + }else if(event->type() == QEvent::WindowActivate){ + QTimer::singleShot(500,this,SLOT(setLockState())); + QTimer::singleShot(200,this,SLOT(killWindow())); + } + return false; +} + +void FullBackgroundWidget::killWindow() +{ + if(isStartup && !process){ + process = new QProcess(this); + process->start("killall screensaver-focus-helper"); + } +} + +void FullBackgroundWidget::setIsStartup(bool val) +{ + isStartup = val; +} + +void FullBackgroundWidget::paintEvent(QPaintEvent *event) +{ + if(future.isRunning()){ + qDebug()<<"------------------------------------------------------------等待加载背景完成1"; + future.waitForFinished(); + qDebug()<<"------------------------------------------------------------加载背景完成1"; + }else{ + if(!future.isFinished() && future.isStarted()){ + qDebug()<<"------------------------------------------------------------等待加载背景完成2"; + future.waitForFinished(); + qDebug()<<"------------------------------------------------------------加载背景完成2"; + } + } + + for(auto screen : QGuiApplication::screens()) + { + QPainter painter(this); + if(background.isNull()){ + QColor cor = "#035290"; + painter.setBrush(cor); + painter.drawRect(screen->geometry()); + } else { + painter.drawPixmap(screen->geometry(), background); + QPainterPath path; + painter.setOpacity(0.25); + painter.setRenderHint(QPainter::Antialiasing); // 反锯齿; + painter.setClipping(true); + painter.setPen(Qt::transparent); + path.addRect(this->rect()); + path.setFillRule(Qt::WindingFill); + painter.setBrush(QColor("#000000")); + painter.setPen(Qt::transparent); + painter.drawPath(path); + } + } + return QWidget::paintEvent(event); +} + +void FullBackgroundWidget::closeEvent(QCloseEvent *event) +{ + qDebug() << "FullBackgroundWidget::closeEvent"; + +#ifdef USE_INTEL + //蓝牙连接后 唤醒信号会有延迟 以防退出时未收到信号导致kwin compositor未resume + QDBusInterface *interface = new QDBusInterface("org.ukui.KWin", + "/Compositor", + "org.ukui.kwin.Compositing", + QDBusConnection::sessionBus(), + this); + + if (!interface->isValid()){ + syslog(LOG_DEBUG, "interface error"); + return; + } + QDBusMessage msg = interface->call("resume"); + syslog(LOG_DEBUG, "after close resume kwin "); +#endif + + for(auto obj: children()) + { + QWidget *widget = dynamic_cast(obj); + if(widget) + widget->close(); + } + closeGrab(); + + return QWidget::closeEvent(event); +} + +void FullBackgroundWidget::showEvent(QShowEvent *event) +{ + // XSetWindowAttributes top_attrs; + // top_attrs.override_redirect = False; + // XChangeWindowAttributes(QX11Info::display(), this->winId(), CWOverrideRedirect, &top_attrs); + // XRaiseWindow(QX11Info::display(), this->winId()); + // raise(); + + return QWidget::showEvent(event); +} + + +bool FullBackgroundWidget::nativeEventFilter(const QByteArray &eventType, void *message, long *result) +{ + if (qstrcmp(eventType, "xcb_generic_event_t") != 0) { + return false; + } + xcb_generic_event_t *event = reinterpret_cast(message); + const uint8_t responseType = event->response_type & ~0x80; + if (responseType == XCB_CONFIGURE_NOTIFY) { + xcb_configure_notify_event_t *xc = reinterpret_cast(event); + if(xc->window == winId()) + return false; + XWindowAttributes window_attributes; + XGetWindowAttributes (QX11Info::display(), xc->window,&window_attributes); + XClassHint ch; + ch.res_name = NULL; + ch.res_class = NULL; + XGetClassHint (QX11Info::display(), xc->window, &ch); + if(QString(ch.res_name) == "ukui-screensaver-dialog") + return false; + + laterActivate(); + }else if(responseType == XCB_MAP_NOTIFY){ + xcb_map_notify_event_t *xm = reinterpret_cast(event); + if(xm->window == winId()) + return false; + XWindowAttributes window_attributes; + XGetWindowAttributes (QX11Info::display(), xm->window,&window_attributes); + XClassHint ch; + ch.res_name = NULL; + ch.res_class = NULL; + XGetClassHint (QX11Info::display(), xm->window, &ch); + if(QString(ch.res_name) == "ukui-screensaver-dialog") + return false; + laterActivate(); + } + return false; +} + +void FullBackgroundWidget::mouseMoveEvent(QMouseEvent *e) +{ + onCursorMoved(QCursor::pos()); + return QWidget::mouseMoveEvent(e); +} + +void FullBackgroundWidget::mousePressEvent(QMouseEvent *e) +{ +#ifdef USE_INTEL + if(screenStatus == SCREEN_LOCK_AND_SAVER) + { + ScreenSaver *saver = configuration->getScreensaver(); + if(saver->path == "/usr/lib/ukui-screensaver/ukui-screensaver-default") + return ; + clearScreensavers(); + } +#endif +} + +void FullBackgroundWidget::init() +{ +/*捕获键盘,如果捕获失败,则可能是由于弹出菜单项已经捕获,那么模拟一次esc按键来退出菜单,如果仍捕获失败,则放弃锁屏,避免密码无法输入*/ + if(establishGrab()) + qDebug()<<"establishGrab : true"; + else { + qDebug()<<"establishGrab : false"; + XTestFakeKeyEvent(QX11Info::display(), XKeysymToKeycode(QX11Info::display(),XK_Escape), True, 1); + XTestFakeKeyEvent(QX11Info::display(), XKeysymToKeycode(QX11Info::display(),XK_Escape), False, 1); + XFlush(QX11Info::display()); + sleep(1); + if(!establishGrab()) + { + exit(1); + } + } + + qDebug()<<"----------------------------------------------------------------开始异步调用"; + future = QtConcurrent::run([=](){ + QImageReader reader; + reader.setFileName(configuration->getBackground()); + reader.setAutoTransform(true); + reader.setScaledSize(QApplication::primaryScreen()->size()); + background = QPixmap::fromImageReader(&reader); + + if(!background.isNull()){ + background = blurPixmap(background); + } + }); + + setWindowFlags(Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint + | Qt::X11BypassWindowManagerHint); + setAttribute(Qt::WA_TranslucentBackground); + + XWindowAttributes rootAttr; + XGetWindowAttributes(QX11Info::display(), QX11Info::appRootWindow(), &rootAttr); + XSelectInput( QX11Info::display(), QX11Info::appRootWindow(), + SubstructureNotifyMask|rootAttr.your_event_mask ); + + // 监听session信号 +// smInterface = new QDBusInterface(SM_DBUS_SERVICE, +// SM_DBUS_PATH, +// SM_DBUS_INTERFACE, +// QDBusConnection::sessionBus()); +// connect(smInterface, SIGNAL(StatusChanged(uint)), +// this, SLOT(onSessionStatusChanged(uint))); + + connect(xEventMonitor, SIGNAL(keyPress(const QString &)), + this, SLOT(onGlobalKeyPress(const QString &))); + connect(xEventMonitor, SIGNAL(keyRelease(const QString &)), + this, SLOT(onGlobalKeyRelease(const QString &))); + connect(xEventMonitor, SIGNAL(buttonDrag(int, int)), + this, SLOT(onGlobalButtonDrag(int, int))); + connect(xEventMonitor, SIGNAL(buttonPress(int, int)), + this, SLOT(onGlobalButtonPressed(int, int))); + + +// int totalWidth = 0; +// int totalHeight = 0; +// for(auto screen : QGuiApplication::screens()) +// { +// totalWidth += screen->geometry().width(); +// totalHeight += screen->geometry().height(); +// } +// setGeometry(0, 0, totalWidth, totalHeight); + QDesktopWidget *desktop = QApplication::desktop(); + setGeometry(desktop->geometry()); + + xEventMonitor->start(); + +#ifdef USE_INTEL + SoundDeviceSet::instance(); +#endif +} + +void FullBackgroundWidget::onCursorMoved(const QPoint &pos) +{ + if(!lockWidget) + { + return; + } + for(auto screen : QGuiApplication::screens()) + { +#ifdef USE_INTEL + if(screen == qApp->primaryScreen()){ + lockWidget->setGeometry(screen->geometry()); + break; + } +#else + if(screen->geometry().contains(pos)) + { + if(lockWidget->geometry() == screen->geometry()) + return ; + /*避免切换时闪烁*/ + qDebug()<geometry()<geometry(); + lockWidget->hide(); + lockWidget->setGeometry(screen->geometry()); + lockWidget->show(); + break; + } +#endif + } +} + +void FullBackgroundWidget::lock() +{ + showLockWidget(); + if(lockWidget){ + lockWidget->show(); + lockWidget->startAuth(); + } +#ifndef USE_INTEL + inhibit(); +#endif +} + +void FullBackgroundWidget::showLock() +{ + screenStatus = (ScreenStatus)(screenStatus | SCREEN_LOCK); + //qDebug() << "showLockWidget - screenStatus: " << screenStatus; +} + +void FullBackgroundWidget::showLockWidget() +{ +#ifdef USE_INTEL + screenStatus = /*(ScreenStatus)(screenStatus | SCREEN_LOCK)*/SCREEN_LOCK; +#else + screenStatus = (ScreenStatus)(screenStatus | SCREEN_LOCK); +#endif + qDebug() << "showLockWidget - screenStatus: " << screenStatus; + + if(!lockWidget) + { +#ifdef USE_INTEL + lockWidget = new TabletLockWidget(this); + connect(lockWidget, &TabletLockWidget::closed, + this, &FullBackgroundWidget::close); + connect(lockWidget, &TabletLockWidget::screenSaver, + this, [=] { + showScreensaver(); + }); + connect(lockWidget, &TabletLockWidget::blackSaver, + this, [=] { + onShowBlackBackGround(); + }); +#else + lockWidget = new LockWidget(this); + connect(lockWidget, &LockWidget::closed, + this, &FullBackgroundWidget::close); +#endif + } + onCursorMoved(QCursor::pos()); + lockWidget->setFocus(); + XSetInputFocus(QX11Info::display(),this->winId(),RevertToParent,CurrentTime); + activateWindow(); + repaint(); +} + +void FullBackgroundWidget::showScreensaver() +{ + if(screenStatus & SCREEN_SAVER){ + return ; + } +#ifdef USE_INTEL + screenStatus = /*(ScreenStatus)(screenStatus | SCREEN_SAVER)*/SCREEN_LOCK_AND_SAVER; +#else + screenStatus = (ScreenStatus)(screenStatus | SCREEN_SAVER); +#endif + qDebug() << "showScreensaver - screenStatus: " << screenStatus; + + for(auto screen : QGuiApplication::screens()) + { + ScreenSaver *saver = configuration->getScreensaver(); + ScreenSaverWidget *saverWidget = new ScreenSaverWidget(saver, this); + qDebug() << " new ScreenSaverWidget"; + widgetXScreensaverList.push_back(saverWidget); + //深色模式有一像素的白边,所以主屏幕向左,向右移一个像素点;这种操作后,外显上方仍旧会有一个像素的白边,暂时不对外显做偏移处理 + if(screen == qApp->primaryScreen()) { + saverWidget->setGeometry(screen->geometry().x()-1, screen->geometry().y()-1, + screen->geometry().width()+1, screen->geometry().height()+1); + } else { + saverWidget->setGeometry(screen->geometry()); + } + + } + setCursor(Qt::BlankCursor); + + //显示屏保时,停止认证(主要针对生物识别) + if(lockWidget) + { + lockWidget->stopAuth(); + lockWidget->hide(); + } +} + +void FullBackgroundWidget::clearScreensavers() +{ +#ifdef USE_INTEL + screenStatus = /*(ScreenStatus)(screenStatus & ~SCREEN_SAVER)*/SCREEN_LOCK; +#else + screenStatus = (ScreenStatus)(screenStatus & ~SCREEN_SAVER); +#endif + for(auto widget : widgetXScreensaverList) + { + widget->close(); + } + widgetXScreensaverList.clear(); + + qDebug() << "clearScreensavers - screenStatus: " << screenStatus; + + unsetCursor(); + if(screenStatus == UNDEFINED) + { + close(); + } + else + { + lock(); + } +} + +int FullBackgroundWidget::onSessionStatusChanged(uint status) +{ + qDebug() << "session status changed: " << status; + if(status != SESSION_IDLE) + { + //当前session没有处于空闲状态 + return -1; + } + qDebug() << "onSessionStatusChanged - screenStatus: " << screenStatus; + + if(!configuration->xscreensaverActivatedWhenIdle()) + { + return -1; + } +#ifdef USE_INTEL + if(screenStatus == SCREEN_LOCK_AND_SAVER) +#else + if(screenStatus & SCREEN_SAVER) +#endif + { + return -1; + } +#ifdef USE_INTEL + else if(screenStatus == SCREEN_LOCK) +#else + else if(screenStatus & SCREEN_LOCK) +#endif + { + showScreensaver(); + } + else if(screenStatus == UNDEFINED) + { +#ifdef USE_INTEL + //显示锁屏和屏保 + showLockWidget(); + showScreensaver(); +#else + if(configuration->xscreensaverActivatedWhenIdle() != -1 && configuration->lockWhenXScreensaverActivated()) + { + //显示锁屏和屏保 + showLockWidget(); + showScreensaver(); + } + else if(configuration->xscreensaverActivatedWhenIdle() != -1) + { + if( idleDelay == idleLock && idleLock != -1){ + //显示锁屏和屏保 + showLockWidget(); + showScreensaver(); + }else{ + //只显示屏保 + showScreensaver(); + } + } +#endif + } + return 0; +} + +void FullBackgroundWidget::onBlankScreensaver() +{ + showLockWidget(); + screenStatus = (ScreenStatus)(screenStatus | SCREEN_SAVER | SCREEN_LOCK); + qDebug() << "showScreensaver - screenStatus: " << screenStatus; + + for(auto screen : QGuiApplication::screens()) + { + ScreenSaver *saver = configuration->getScreensaver(); + saver->mode = SaverMode(SAVER_BLANK_ONLY); + ScreenSaverWidget *saverWidget = new ScreenSaverWidget(saver, this); + widgetXScreensaverList.push_back(saverWidget); + saverWidget->setGeometry(screen->geometry()); + } + setCursor(Qt::BlankCursor); + isBlank = true; +} + +void FullBackgroundWidget::onScreensaver() +{ + showLockWidget(); + showScreensaver(); +} + +void FullBackgroundWidget::onGlobalKeyPress(const QString &key) +{ +#ifdef USE_INTEL + qDebug() << "onGlobalKeyPress " << key << "screenStatus " << screenStatus; + + if(m_delay) + { + qDebug() << "it is delay time ,ignore"; + return; + } + + if(!key.isEmpty() && (screenStatus == SCREEN_LOCK_AND_SAVER)) + { + clearScreensavers(); + }/*else{ + lockWidget->startAuth(); + inhibit(); + }*/ +// if(screenStatus == SCREEN_LOCK) +// { +// checkNumLock(); +// int keyValue = numberMatch(key);; +// if (keyValue >= 0 && keyValue <= 10 ) +// { +// lockWidget->RecieveKey(keyValue); +// } +// } +#endif +} + +void FullBackgroundWidget::onGlobalKeyRelease(const QString &key) +{ +// if(key == "Caps_Lock") +// { +// lockWidget->capsLockChanged(); +// } +#ifdef USE_INTEL + + // 声音、亮度等调整 + // 取消声音快捷键设置,由settings-deamon控制 有一个bug,音量为零时再按静音键,静音指示灯会熄灭,可解决,但是不用锁屏做了 +// if(key == "XF86AudioRaiseVolume") +// { +// SoundDeviceSet::instance()->setValue(SoundDeviceSet::instance()->getValue() + 5); +// } +// else if(key == "XF86AudioLowerVolume") +// { +// SoundDeviceSet::instance()->setValue(SoundDeviceSet::instance()->getValue() - 5); +// } +// else if(key == "XF86AudioMute") +// { +// SoundDeviceSet::instance()->setMute(!(SoundDeviceSet::instance()->getIsMute())); +// } +// else + + if (key == "XF86MonBrightnessUp") // 亮度调整 + { + //qDebug() << "up"; + BrightnessDeviceSet* pBrightness = BrightnessDeviceSet::instance(); + pBrightness->setValue(pBrightness->getValue() + 5); + } + else if (key == "XF86MonBrightnessDown") + { + //qDebug() << "down"; + BrightnessDeviceSet* pBrightness = BrightnessDeviceSet::instance(); + pBrightness->setValue(pBrightness->getValue() - 5); + } + else if (key == "XF86RFKill") // 飞行模式 + { + // 键盘上的飞行模式实体键 生效,不需要在登录界面进行设置 + } + else if (key == "") // num_lock + { + // 键盘上的num_lock生效、不需要登录界面进行管理 + } +#else + if(key == "Escape" && screenStatus == SCREEN_LOCK) + { + bool canShow = true; + if (lockWidget && !lockWidget->exitSubWidget()) + canShow = false; + if (canShow) + showScreensaver(); + } + else if(screenStatus & SCREEN_SAVER && !isBlank) + { + clearScreensavers(); + } +#endif +} + +void FullBackgroundWidget::onGlobalButtonDrag(int xPos, int yPos) +{ +#ifdef USE_INTEL + if(screenStatus == SCREEN_LOCK_AND_SAVER) + { + ScreenSaver *saver = configuration->getScreensaver(); + if(saver->path == "/usr/lib/ukui-screensaver/ukui-screensaver-default") + return ; + clearScreensavers(); + } +#else + if(screenStatus & SCREEN_SAVER && !isBlank) + { + ScreenSaver *saver = configuration->getScreensaver(); + if(isPassed || saver->path != "/usr/lib/ukui-screensaver/ukui-screensaver-default"){ + clearScreensavers(); + } + isPassed = true; + } +#endif +} + +void FullBackgroundWidget::onGlobalButtonPressed(int xPos, int yPos) +{ +#ifndef USE_INTEL + if(screenStatus & SCREEN_SAVER && !isBlank) + { + clearScreensavers(); + } +#endif +} + +void FullBackgroundWidget::closeScreensaver() +{ + if(screenStatus & SCREEN_SAVER){ + clearScreensavers(); + } + + if(screenStatus & SCREEN_LOCK){ + if(lockWidget){ + lockWidget->stopAuth(); + lockWidget->close(); + } + } + + close(); +} + +void FullBackgroundWidget::onScreenCountChanged(int) +{ +#ifdef USE_INTEL + QDesktopWidget *desktop = QApplication::desktop(); + setGeometry(desktop->geometry()); + //repaint(); + update(); + clearScreensavers(); +#else + QDesktopWidget *desktop = QApplication::desktop(); + setGeometry(desktop->geometry()); + //repaint(); + if(screenStatus & SCREEN_SAVER) + { + clearScreensavers(); + } + update(); +#endif +} + + +void FullBackgroundWidget::onDesktopResized() +{ +#ifdef USE_INTEL + qDebug() << "[FullBackgroundWidget] [onDesktopResized]"; + QDesktopWidget *desktop = QApplication::desktop(); + if(NULL == desktop) + { + qWarning() << " get desktop size failed"; + return; + } + setGeometry(desktop->geometry()); + if(lockWidget) + onCursorMoved(cursor().pos()); +// clearScreensavers(); + //repaint(); + update(); + if(screenStatus == SCREEN_LOCK_AND_SAVER) + { + clearScreensavers(); + showScreensaver(); + } +#else + QDesktopWidget *desktop = QApplication::desktop(); + setGeometry(desktop->geometry()); + if(lockWidget) + onCursorMoved(QCursor::pos()); + if(screenStatus & SCREEN_SAVER) + { + clearScreensavers(); + } + //repaint(); + update(); +#endif +} + +void FullBackgroundWidget::laterInhibit(bool val) +{ + if(val){ + inhibit(); + }else{ + uninhibit(); + } +} + +void FullBackgroundWidget::laterStartAuth() +{ + if (lockWidget) + lockWidget->startAuth(); + inhibit(); +} + +void FullBackgroundWidget::onPrepareForSleep(bool sleep) +{ + ///系统休眠时,会关闭总线,导致设备不可用,发生错误 + ///在系统休眠之前停止认证,在系统唤醒后重新开始认证 + if(sleep) + { + if(lockWidget) + lockWidget->stopAuth(); + uninhibit(); + } + else + { + if(screenStatus & SCREEN_SAVER) + { + isBlank = false; + clearScreensavers(); + }else{ + repaint(); + if(lockWidget) + lockWidget->startAuth(); + inhibit(); + } + } +} + +void FullBackgroundWidget::inhibit() +{ + if (m_inhibitFileDescriptor.isValid()) { + return; + } + + QDBusMessage message = QDBusMessage::createMethodCall("org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + QStringLiteral("Inhibit")); + message.setArguments(QVariantList({QStringLiteral("sleep"), + "Screen Locker", + "Ensuring that the screen gets locked before going to sleep", + QStringLiteral("delay")})); + QDBusPendingReply reply = QDBusConnection::systemBus().call(message); + if (!reply.isValid()) { + return; + } + reply.value().swap(m_inhibitFileDescriptor); +} + +void FullBackgroundWidget::uninhibit() +{ + if (!m_inhibitFileDescriptor.isValid()) { + return; + } + m_inhibitFileDescriptor = QDBusUnixFileDescriptor(); +} + +/** + * @brief FullBackgroundWidget::getPaddingPixmap + * @param pixmap 需要填充的图像 + * @param width 容器宽度 + * @param height 容器高度 + * @return + */ +QPixmap FullBackgroundWidget::getPaddingPixmap(QPixmap pixmap, int width, int height) +{ + if (pixmap.isNull() || pixmap.width() == 0 || pixmap.height() == 0) + { + return QPixmap(); + } + + bool useHeight; + float scaled = 0.0; + QPixmap scaledPixmap; + QPixmap paddingPixmap; + qint64 rw = qint64(height) * qint64(pixmap.width()) / qint64(pixmap.height()); + + useHeight = (rw >= width); + if (useHeight) { + scaled = float(height) / float(pixmap.height()); + scaledPixmap = pixmap.scaled(pixmap.width() * scaled, height); + paddingPixmap = scaledPixmap.copy((pixmap.width() * scaled - width) / 2 , 0, width, height); + } else { + scaled = float(width) / float(pixmap.width()); + scaledPixmap = pixmap.scaled(width, pixmap.height() * scaled); + paddingPixmap = scaledPixmap.copy(0 , (pixmap.height() * scaled - height) / 2,width, height); + } + + return paddingPixmap; +} + +#ifdef USE_INTEL +void FullBackgroundWidget::onShowBlackBackGround() +{ + screenStatus = SCREEN_BLACK; + qDebug() << "showBlackBackGround - screenStatus: " << screenStatus; + + for(auto screen : QGuiApplication::screens()) + { +// BlackWidget *blackWidget = new BlackWidget(this); +// qDebug() << " new BlackWidget"; +// widgetBlackList.push_back(blackWidget); + ScreenSaver *saver = configuration->getScreensaver(); + saver->mode = SaverMode(SAVER_BLANK_ONLY); + ScreenSaverWidget *saverWidget = new ScreenSaverWidget(saver, this); + widgetBlackList.push_back(saverWidget); + saverWidget->setGeometry(screen->geometry()); + + //深色模式有一像素的白边,所以主屏幕向左,向右移一个像素点;这种操作后,外显上方仍旧会有一个像素的白边,暂时不对外显做偏移处理 + if(screen == qApp->primaryScreen()) { + saverWidget->setGeometry(screen->geometry().x()-1, screen->geometry().y()-1, + screen->geometry().width()+1, screen->geometry().height()+1); + } else { + saverWidget->setGeometry(screen->geometry()); + } + + + } + setCursor(Qt::BlankCursor); +} +#endif diff --git a/src/fullbackgroundwidget.h b/src/fullbackgroundwidget.h new file mode 100644 index 0000000..991e2be --- /dev/null +++ b/src/fullbackgroundwidget.h @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#ifndef FULLBACKGROUNDWIDGET_H +#define FULLBACKGROUNDWIDGET_H + +#ifndef QT_NO_KEYWORDS +#define QT_NO_KEYWORDS +#endif +#include +#include +#include +#include "types.h" +#include +#include +#include +#include "logind.h" +#include "config.h" + +void x11_get_screen_size(int *width,int *height); + +class LockWidget; +class XEventMonitor; +class MonitorWatcher; +class Configuration; +class QDBusInterface; +class TabletLockWidget; +class QProcess; +class FullBackgroundWidget : public QWidget , public QAbstractNativeEventFilter +{ + Q_OBJECT +public: + explicit FullBackgroundWidget(QWidget *parent = nullptr); + void paintEvent(QPaintEvent *event); + void closeEvent(QCloseEvent *event); + void showEvent(QShowEvent *event); + virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) override; + void mouseMoveEvent(QMouseEvent *e); + void mousePressEvent(QMouseEvent *e); + void onScreensaver(); + void onBlankScreensaver(); + void closeScreensaver(); + void setIsStartup(bool val); + +public Q_SLOTS: + void onCursorMoved(const QPoint &pos); + void lock(); + void showLockWidget(); + void showLock(); + void showScreensaver(); + int onSessionStatusChanged(uint status); + void inhibit(); + void uninhibit(); +#ifdef USE_INTEL + void propertiesChangedSlot(QString, QMap, QStringList); + void onShowBlackBackGround(); +#endif +private: + void init(); + void clearScreensavers(); + bool eventFilter(QObject *obj, QEvent *event); + + QPixmap getPaddingPixmap(QPixmap pixmap, int width, int height); +// void checkNumLock(); +// int numberMatch(const QString &key); + +private Q_SLOTS: + void onScreenCountChanged(int); + void onDesktopResized(); + void onGlobalKeyPress(const QString &key); + void onGlobalKeyRelease(const QString &key); + void onGlobalButtonDrag(int xPos, int yPos); + void onGlobalButtonPressed(int xPos, int yPos); + void onPrepareForSleep(bool sleep); + void switchToLinux(); + void laterActivate(); + void setLockState(); + void killWindow(); + void laterInhibit(bool val); + void laterStartAuth(); + void onConfigurationDelayChanged(QString key); + void onConfigurationLockChanged(QString key); +// void lockEnabledChanged(QString key); + +private: + QDBusInterface *smInterface; +#ifdef USE_INTEL + TabletLockWidget *lockWidget; +#else + LockWidget *lockWidget; +#endif + XEventMonitor *xEventMonitor; + MonitorWatcher *monitorWatcher; + Configuration *configuration; + QList widgetXScreensaverList; + QList widgetBlackList; + QList xscreensaverPidList; + bool isLocked; + bool lockState; + ScreenStatus screenStatus; + QPixmap background; + QDBusUnixFileDescriptor m_inhibitFileDescriptor; + bool isPassed; + bool m_delay; + int isBlank; + QGSettings *settings_delay; + QGSettings *settings_lock; + QGSettings *lockEnabled_Key; + QProcess *process = nullptr; + int idleDelay; + int idleLock; + bool lockEnabled; + bool isStartup = false; + QFuture future; +}; + +#endif // FULLBACKGROUNDWIDGET_H diff --git a/src/generalauthwidget.cpp b/src/generalauthwidget.cpp new file mode 100644 index 0000000..eaa2efa --- /dev/null +++ b/src/generalauthwidget.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#include "generalauthwidget.h" + +GeneralAuthWidget::GeneralAuthWidget(QWidget *parent) : QWidget(parent) +{ + +} diff --git a/src/generalauthwidget.h b/src/generalauthwidget.h new file mode 100644 index 0000000..47f2c57 --- /dev/null +++ b/src/generalauthwidget.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#ifndef GENERALAUTHWIDGET_H +#define GENERALAUTHWIDGET_H + +#include + +class GeneralAuthWidget : public QWidget +{ + Q_OBJECT +public: + explicit GeneralAuthWidget(QWidget *parent = nullptr); + +signals: + +public slots: +}; + +#endif // GENERALAUTHWIDGET_H \ No newline at end of file diff --git a/src/gestureauthdialog.cpp b/src/gestureauthdialog.cpp new file mode 100644 index 0000000..ca657dc --- /dev/null +++ b/src/gestureauthdialog.cpp @@ -0,0 +1,402 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Authors: ZHAI Kang-ning +**/ +#include "gestureauthdialog.h" + +#include +#include +#include +#include +#include +#include + +int gestureScale = 1; + +/** + * @author zhaikangning + * @date 2020/09/15 + * 手势密码页 + * 功能未完善 未对接pam + * 未来进化 所有登录均继承认证父类 该父类含有密码校验等基本功能 + */ + +GestureAuthDialog::GestureAuthDialog(QWidget *parent) : QWidget(parent), + m_bgColor("#000000"), + m_outCircleColor("#fff333"), + m_tipLineColor("#ff0000"), + m_circleRingColor("#ddffff"), + m_radius(20), + m_margin(5), + m_pressed(false), + m_showText(true), + m_inputPassword(""), + m_is_set_password(false), + m_is_check_password(false) +{ + m_posInCircle.isIn = false; + qDebug() <<"-----------init width=" << width() << ",height=" << height(); + + m_set_password = new QPushButton(this); + m_set_password->setGeometry(0,0,m_set_password->width(),m_set_password->height()); + m_set_password->setText("设置密码"); + connect(m_set_password, &QPushButton::clicked,this, [=] { + m_chech_result->setText("请输入密码"); + m_is_set_password = true; + }); + + + m_unlock = new QPushButton(this); + m_unlock->setText("解锁"); + m_unlock->setGeometry(0,m_set_password->height() + 2,m_unlock->width(),m_unlock->height()); + connect(m_unlock,&QPushButton::clicked, this, [=] { + m_chech_result->setText("密码校验"); + m_is_check_password = true; + }); + + m_chech_result = new QLabel("校验结果", this); + m_chech_result->setStyleSheet("color:#ffeeff;"); + m_chech_result->setGeometry(0,m_set_password->height() + m_unlock->height() +2, m_chech_result->width(), m_chech_result->height()); + + resize(640,480); +} + +GestureAuthDialog::~GestureAuthDialog() +{ + +} + +void GestureAuthDialog::paintEvent(QPaintEvent *event) +{ + int width = this->width(); + int height = this->height(); + int side = qMin(width, height); + qDebug() <<"-----------width=" << width << ",height=" << height << ",side=" << side; + + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + painter.translate(width / 2, height / 2); + painter.scale(side / 200, side / 200); + gestureScale = side/200; + + drawBg(&painter); + drawOutCircle(&painter); + drawTipLine(&painter); + drawInCircle(&painter); + drawJoinLine(&painter); +} +void GestureAuthDialog::drawBg(QPainter *painter) +{ + painter->save(); + + int width = this->width(); + int height = this->height(); + + painter->setPen(Qt::NoPen); + m_bgColor.setAlpha(100); + painter->setBrush(m_bgColor); + painter->drawRect(-width / 2, -height / 2, width, height); + + painter->restore(); +} + +void GestureAuthDialog::drawOutCircle(QPainter *painter) +{ + painter->save(); + QPen pen(m_outCircleColor, 2); + painter->setPen(pen); + painter->setBrush(Qt::NoBrush); + + for (int i = 0; i < 3; i++) + { + for (int j = 0; j < 3; j++) + { + int x = -5 * m_radius + j * 4 * m_radius; + int y = -5 * m_radius + i * 4 * m_radius; + painter->drawEllipse(x, y, m_radius * 2, m_radius * 2); +//qDebug() << "-------------x,y=" << x << ","<< y; + if (m_showText) + { + int number = posToNumber(i, j); + QString text = QString::number(number); + x = -4 * m_radius + j * 4 * m_radius; + y = -4 * m_radius + i * 4 * m_radius; + int textWidth = fontMetrics().width(text); + int textHeight = fontMetrics().height(); + painter->drawText(x - textWidth / 2, y + textWidth / 2, text); + } + } + } + + painter->restore(); +} + +void GestureAuthDialog::drawTipLine(QPainter *painter) +{ + painter->save(); + + int offset = 3; + + if (m_posInCircle.isIn) + { + if (!m_pressed) + { + painter->setPen(m_tipLineColor); + painter->setBrush(Qt::NoBrush); + + int x = -m_radius * 5 + m_posInCircle.j * 4 * m_radius - offset; + int y = -m_radius * 5 + m_posInCircle.i * 4 * m_radius - offset; + int w = (m_radius + offset) * 2; + int h = (m_radius + offset) * 2; + QRect rect(x, y, w, h); + painter->drawEllipse(rect); + } + } + + painter->restore(); +} + +void GestureAuthDialog::drawInCircle(QPainter *painter) +{ + painter->save(); + + if (m_pressed) + { + int offset1 = 1;//圆环偏移量 + int offset2 = 3;//内圆偏移量 + + if (m_posInCircle.isIn) + { + //绘制圆环 + painter->setPen(m_circleRingColor); + painter->setBrush(m_circleRingColor); + int x = -m_radius * 5 + m_posInCircle.j * 4 * m_radius + offset1; + int y = -m_radius * 5 + m_posInCircle.i * 4 * m_radius + offset1; + int w = (m_radius - offset1) * 2; + int h = (m_radius - offset1) * 2; + QRect rect(x, y, w, h); + painter->drawEllipse(rect); + + painter->setPen(m_outCircleColor); + painter->setBrush(m_outCircleColor); + x = -m_radius * 5 + m_posInCircle.j * 4 * m_radius + offset2; + y = -m_radius * 5 + m_posInCircle.i * 4 * m_radius + offset2; + w = (m_radius - offset2) * 2; + h = (m_radius - offset2) * 2; + rect = QRect(x, y, w, h); + painter->drawEllipse(rect); + + x = -m_radius * 4 + m_posInCircle.j * 4 * m_radius; + y = -m_radius * 4 + m_posInCircle.i * 4 * m_radius; + + if (!m_vecInputPoints.contains(QPoint(x, y))) + { + m_vecInputPoints.push_back(QPoint(x, y)); + int number = posToNumber(m_posInCircle.i, m_posInCircle.j); + m_inputPassword += QString::number(number); + } + } + } + + painter->restore(); +} + +void GestureAuthDialog::drawJoinLine(QPainter *painter) +{ + painter->save(); + + QPen pen(m_outCircleColor, 3); + painter->setPen(pen); + //绘制连接线段 + for (int i = 0; i < m_vecInputPoints.count() - 1; i++) + { + painter->drawLine(m_vecInputPoints[i], m_vecInputPoints[i + 1]); + } + + int offset1 = 1;//圆环偏移量 + int offset2 = 3;//内圆偏移量 + //绘制连接圆 + for (int i = 0; i < m_vecInputPoints.count(); i++) + { + int x = m_vecInputPoints[i].x() - m_radius + offset1; + int y = m_vecInputPoints[i].y() - m_radius + offset1; + int w = 2 * (m_radius - offset1); + int h = 2 * (m_radius - offset1); + painter->setPen(m_circleRingColor); + painter->setBrush(m_circleRingColor); + painter->drawEllipse(x, y, w, h); + + x = m_vecInputPoints[i].x() - m_radius + offset2; + y = m_vecInputPoints[i].y() - m_radius + offset2; + w = 2 * (m_radius - offset2); + h = 2 * (m_radius - offset2); + painter->setPen(m_outCircleColor); + painter->setBrush(m_outCircleColor); + painter->drawEllipse(x, y, w, h); + } + //绘制跟随线 + if (m_pressed && m_vecInputPoints.count() < 9) + { + int width = this->width(); + int height = this->height(); + int side = qMin(width, height); + int gestureScale = side / 200; + int x = (m_followPoint.x() - width / 2) / gestureScale; + int y = (m_followPoint.y() - height / 2) / gestureScale; + if (m_vecInputPoints.count() > 0) + { + painter->setPen(pen); + painter->drawLine(m_vecInputPoints.last(), QPoint(x, y)); + } + } + + painter->restore(); +} + +void GestureAuthDialog::mouseMoveEvent(QMouseEvent *event) +{ + //nothing + qDebug() << "--------mouseMoveEvent(" << event->x() << "," << event->y() << ")"; + // qDebug() << "-------------gestureScale=" << gestureScale; + int x = (event->x() - this->width()/2) / gestureScale; + int y = (event->y() - this->height()/2) / gestureScale; + // qDebug() << "--------mousePressEvent转化后 x,y(" << x << "," << y << ")"; + int jMax = 0; + if (x >=0) + jMax = 1; + + for (int i = 0; i < 3; i++) + { + for (int j = jMax; j < 3; j++) + { + + int x1 = -4 * m_radius + j * 4 * m_radius; + int y1 = -4 * m_radius + i * 4 * m_radius; + // qDebug() << "--------每个圆心的位置 x1,y1(" << x1 << "," << y1 << ")"; + if (distance(x , y, x1, y1) <= m_radius) + { + QString numStr = QString::number(posToNumber(i,j), 10); + if (m_inputPassword.contains(numStr)) + { + qWarning() << "已选过该区域"; + return; + } + m_inputPassword.append(numStr); + + m_posInCircle.i = i; + m_posInCircle.j = j; +// m_posInCircle.isIn = true; + qDebug() << "---------------选中了"<< posToNumber(i,j); + + + m_vecInputPoints.append(QPoint(x1, y1)); //鼠标移动选点 + m_followPoint = QPoint(x, y); + repaint(); + } + } + } +} + +void GestureAuthDialog::mousePressEvent(QMouseEvent *event) +{ + //nothing + qDebug() << "--------mousePressEvent(" << event->x() << "," << event->y() << ")"; +// qDebug() << "-------------gestureScale=" << gestureScale; + int x = (event->x() - this->width()/2) / gestureScale; + int y = (event->y() - this->height()/2) / gestureScale; +// qDebug() << "--------mousePressEvent转化后 x,y(" << x << "," << y << ")"; + + for (int i = 0; i < 3; i++) + { + for (int j = 0; j < 3; j++) + { + int x1 = -4 * m_radius + j * 4 * m_radius; + int y1 = -4 * m_radius + i * 4 * m_radius; +// qDebug() << "--------每个圆心的位置 x1,y1(" << x1 << "," << y1 << ")"; + if (distance(x , y, x1, y1) <= m_radius) + { + m_posInCircle.i = i; + m_posInCircle.j = j; + m_posInCircle.isIn = true; + + m_vecInputPoints.append(QPoint(x1, y1)); //鼠标移动选点 + m_followPoint = QPoint(x1, y1); + qDebug() << "---------------选中了"<< posToNumber(i,j); + repaint(); + } + } + } +} + +void GestureAuthDialog::mouseReleaseEvent(QMouseEvent *event) +{ + //nothing + qDebug() << "--------mouseReleaseEvent(" << event->x() << "," << event->y() << ")"; + m_posInCircle.isIn = false; + if (m_is_set_password && !m_inputPassword.isEmpty()) + { + m_password = m_inputPassword; + m_chech_result->setText("密码设置成功"); + qDebug() << "---------密码:" << m_password; + m_is_set_password = false; + } + + if (m_is_check_password) { + if (m_inputPassword == m_password) + { + m_chech_result->setText("密码正确"); + qDebug() << "---------密码:" << m_password << " 输入的是:" << m_inputPassword; + emit authenticateCompete(true); +// m_is_check_password = false; + } else { + m_chech_result->setText("密码错误"); + qDebug() << "---------密码:" << m_password << " 输入的是:" << m_inputPassword; + } + } + m_vecInputPoints.clear(); + m_inputPassword.clear(); + repaint(); +} + +double GestureAuthDialog::distance(int x1, int y1, int x2, int y2) +{ + //nothing + double x = x1 - x2; + double y = y1 - y2; + return sqrt(x*x + y*y); +} + +int GestureAuthDialog::posToNumber(int i, int j) +{ + //nothing + return ((3*i) + j +1); +} + +void GestureAuthDialog::setPassword(const QString password) +{ + //nothing +} + +QString GestureAuthDialog::getPassword() +{ + //nothing +} + +bool GestureAuthDialog::checkPassword() +{ + //nothing +} diff --git a/src/gestureauthdialog.h b/src/gestureauthdialog.h new file mode 100644 index 0000000..ad32bc7 --- /dev/null +++ b/src/gestureauthdialog.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Authors: ZHAI Kang-ning +**/ +#ifndef GESTUREAUTHDIALOG_H +#define GESTUREAUTHDIALOG_H + +#include +#include +#include +#include +#include +#include + +class GestureAuthDialog : public QWidget +{ + Q_OBJECT +public: + struct PosInCircle + { + int i; //行 + int j; //列 + bool isIn; //是否在范围内 + }; + explicit GestureAuthDialog(QWidget *parent = nullptr); + ~GestureAuthDialog(); + + void setPassword(const QString password); + QString getPassword(); + bool checkPassword(); +protected: + void paintEvent(QPaintEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void drawBg(QPainter *painter); + void drawOutCircle(QPainter *painter); + void drawTipLine(QPainter *painter); + void drawInCircle(QPainter *painter); + void drawJoinLine(QPainter *painter); +private: + double distance(int x1, int y1, int x2, int y2); + int posToNumber(int i, int j); +Q_SIGNALS: + void correctPassword(bool isCorrect, QString password); + void authenticateCompete(bool result); +public Q_SLOTS: +private: + QColor m_bgColor; //背景色 + QColor m_outCircleColor; //外圆颜色 + QColor m_tipLineColor; //提示线颜色 + QColor m_circleRingColor; //圆环颜色 + int m_margin; //外边距 + int m_radius; //圆相对半径 + PosInCircle m_posInCircle; //判断点是否在圆内 + bool m_pressed; //鼠标按下 + bool m_showText; //是否显示文字 + + QVector m_vecInputPoints; //鼠标移动选点 + QPoint m_followPoint; //鼠标跟随点 + QString m_password; //原密码 + QString m_inputPassword; //鼠标移动选点转换为密码 + + QPushButton *m_set_password; + bool m_is_set_password; + QPushButton *m_unlock; + bool m_is_check_password; + QLabel *m_chech_result; +}; + +#endif // GESTUREAUTHDIALOG_H diff --git a/src/grab-x11.cpp b/src/grab-x11.cpp new file mode 100644 index 0000000..a03d14d --- /dev/null +++ b/src/grab-x11.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#include "grab-x11.h" +#include +#include +#include + +class XServerGraber{ +public: + XServerGraber() { + xcb_grab_server(QX11Info::connection()); + } + ~XServerGraber() { + xcb_ungrab_server(QX11Info::connection()); + xcb_flush(QX11Info::connection()); + } +}; + +static bool grabKeyboard() +{ + int rv = XGrabKeyboard(QX11Info::display(), QX11Info::appRootWindow(), + True, GrabModeAsync, GrabModeAsync, CurrentTime); + return (rv == GrabSuccess); +} + +static bool grabMouse() +{ +#define GRABEVENTS ButtonPressMask | ButtonReleaseMask | PointerMotionMask | \ + EnterWindowMask | LeaveWindowMask | KeyPressMask | KeyReleaseMask + int rv = XGrabPointer(QX11Info::display(), QX11Info::appRootWindow(), + True, GRABEVENTS, GrabModeAsync, GrabModeAsync, None, + None, CurrentTime); +#undef GRABEVENTS + + return (rv == GrabSuccess); +} + +bool establishGrab() +{ + XSync(QX11Info::display(), False); + XServerGraber xserverGraber; + + Q_UNUSED(xserverGraber); + + if(!grabKeyboard()) + return false; + + if(!grabMouse()) { + XUngrabKeyboard(QX11Info::display(), CurrentTime); + XFlush(QX11Info::display()); + return false; + } + + return true; +} + +bool closeGrab() +{ + XSync(QX11Info::display(), False); + XServerGraber xserverGraber; + + Q_UNUSED(xserverGraber); + + XUngrabKeyboard(QX11Info::display(), CurrentTime); + XUngrabPointer(QX11Info::display(), CurrentTime); + XFlush(QX11Info::display()); + return true; +} diff --git a/src/grab-x11.h b/src/grab-x11.h new file mode 100644 index 0000000..2821f08 --- /dev/null +++ b/src/grab-x11.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#ifndef GRABX11_H +#define GRABX11_H + + +bool establishGrab(); +bool closeGrab(); + +#endif // GRABX11_H diff --git a/src/hoverwidget.cpp b/src/hoverwidget.cpp new file mode 100644 index 0000000..67f142d --- /dev/null +++ b/src/hoverwidget.cpp @@ -0,0 +1,60 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * Copyright (C) 2019 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include "hoverwidget.h" + +#include +#include + +#include +#define THEME_QT_SCHEMA "org.ukui.style" +#define MODE_QT_KEY "style-name" + +HoverWidget::HoverWidget(QString mname, QWidget *parent) : + QWidget(parent), + _name(mname) +{ + setAttribute(Qt::WA_DeleteOnClose); +} + +HoverWidget::~HoverWidget() +{ +} + +void HoverWidget::enterEvent(QEvent *event){ + emit enterWidget(_name); + + QWidget::enterEvent(event); +} + +void HoverWidget::leaveEvent(QEvent *event){ + emit leaveWidget(_name); + + QWidget::leaveEvent(event); +} + +//子类化一个QWidget,为了能够使用样式表,则需要提供paintEvent事件。 +//这是因为QWidget的paintEvent()是空的,而样式表要通过paint被绘制到窗口中。 +void HoverWidget::paintEvent(QPaintEvent *event){ + Q_UNUSED(event) + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} diff --git a/src/hoverwidget.h b/src/hoverwidget.h new file mode 100644 index 0000000..bcb811e --- /dev/null +++ b/src/hoverwidget.h @@ -0,0 +1,50 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * Copyright (C) 2019 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef HOVERWIDGET_H +#define HOVERWIDGET_H + +#include +#include +#include +#include +#include + +class HoverWidget : public QWidget +{ + Q_OBJECT + +public: + explicit HoverWidget(QString mname, QWidget *parent = 0); + ~HoverWidget(); + +public: + QString _name; + +protected: + virtual void enterEvent(QEvent * event); + virtual void leaveEvent(QEvent * event); + virtual void paintEvent(QPaintEvent * event); +Q_SIGNALS: + void enterWidget(QString name); + void leaveWidget(QString name); +}; + +#endif // HOVERWIDGET_H + diff --git a/src/iconedit.cpp b/src/iconedit.cpp new file mode 100644 index 0000000..a620826 --- /dev/null +++ b/src/iconedit.cpp @@ -0,0 +1,309 @@ +/* iconedit.cpp + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301, USA. +**/ +#include "iconedit.h" +#include "imageutil.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define GSETTINGS_SCHEMA_PERIPHERALS_KEYBOARD "org.ukui.peripherals-keyboard" +#define CAPSLOCK_STATUS "capslock_state" +/** + * @brief 判断大写键状态 + * @return true: 大写锁定 + */ +//bool checkCapsLockState() +//{ +// bool capsState = false; +// unsigned int n; +// XkbGetIndicatorState(QX11Info::display(), XkbUseCoreKbd, &n); +// capsState = (n & 0x01) == 1; + +// return capsState; +//} + + +IconEdit::IconEdit(QWidget *parent) + : QWidget(parent), + m_timer(nullptr) +{ + m_edit = new QLineEdit(this); + m_edit->setObjectName(QStringLiteral("passwdEdit")); + m_edit->setAttribute(Qt::WA_InputMethodEnabled, false); //禁用输入法 + m_edit->setContextMenuPolicy(Qt::NoContextMenu); //禁用右键菜单 + m_edit->installEventFilter(this); + + m_capsIcon = new QSvgWidget(this); + m_capsIcon->setObjectName(QStringLiteral("capsIconLabel")); + settings = new QGSettings(GSETTINGS_SCHEMA_PERIPHERALS_KEYBOARD, "", this); + connect(settings, &QGSettings::changed, + this, &IconEdit::onCapsChanged); + capsState = settings->get("capslock-state").toBool(); + m_capsIcon->setVisible(capsState); + m_capsIcon->load(QString(":/image/assets/capslock.svg")); + + m_iconButton = new QPushButton(this); + m_iconButton->setObjectName(QStringLiteral("loginButton")); + m_iconButton->setFocusPolicy(Qt::NoFocus); + m_iconButton->setCursor(QCursor(Qt::PointingHandCursor)); + m_iconButton->installEventFilter(this); + + m_modeButton = new QPushButton(this); + m_modeButton->setObjectName(QStringLiteral("echoModeButton")); + m_modeButton->setCheckable(true); + m_modeButton->setIcon(QIcon::fromTheme("ukui-eye-display-symbolic")); + m_modeButton->setFocusPolicy(Qt::NoFocus); + m_modeButton->setCursor(Qt::PointingHandCursor); + m_modeButton->installEventFilter(this); + connect(m_modeButton, &QPushButton::clicked, this, [&](bool checked){ + setType(checked ? QLineEdit::Normal : QLineEdit::Password); + }); + + QHBoxLayout *layout = new QHBoxLayout(this); + layout->setContentsMargins(1, 1, 8, 1); + layout->setSpacing(0); + layout->addStretch(); + layout->addWidget(m_capsIcon); + layout->addWidget(m_modeButton); + layout->addWidget(m_iconButton); + + connect(m_edit, &QLineEdit::returnPressed, this, &IconEdit::clicked_cb); + connect(m_iconButton, &QPushButton::clicked, this, &IconEdit::clicked_cb); + + setFocusProxy(m_edit); + //setCapsState(checkCapsState()); +} + +bool IconEdit::eventFilter(QObject *obj, QEvent *event) +{ + if(obj == m_edit){ + if(event->type() == 6){ //禁止复制粘贴功能。 + QKeyEvent *keyEvent = static_cast(event); + if(keyEvent->matches(QKeySequence::Copy) || keyEvent->matches(QKeySequence::Cut) || keyEvent->matches(QKeySequence::Paste)){ + event->ignore(); + return true; + }else if((keyEvent->modifiers() & Qt::MetaModifier) || (keyEvent->modifiers() & Qt::AltModifier)){//当meta或者alt键被按下时,忽略按键事件 + event->ignore(); + return true; + } + } + if(event->type() == 2) + { + Q_EMIT clickedPassword(true); + } + if(event->type() == 23) + { + XSetInputFocus(QX11Info::display(),this->winId(),RevertToParent,CurrentTime); + update(); + }else if(event->type() == QEvent::MouseButtonPress){ + XSetInputFocus(QX11Info::display(),this->winId(),RevertToParent,CurrentTime); + update(); + } + } + if(obj == m_iconButton){ + if(m_timer && m_timer->isActive()) + { + return false; + } + if(event->type() == 2) + { + Q_EMIT clickedPassword(true); + } + if(event->type() == QEvent::HoverEnter){ + setIcon(QIcon(":/image/assets/login-button-hover.svg")); + } + else if(event->type() == QEvent::HoverLeave){ + setIcon(QIcon(":/image/assets/login-button.svg")); + } + } + if(obj == m_modeButton){ + if(event->type() == 2) + { + Q_EMIT clickedPassword(true); + } + } + return false; +} + +void IconEdit::setType(QLineEdit::EchoMode type) +{ + m_edit->setEchoMode(type); + if(type == 0){ + m_modeButton->setChecked(true); + m_modeButton->setIcon(QIcon::fromTheme("ukui-eye-display-symbolic")); + } + else{ + m_modeButton->setChecked(false); + m_modeButton->setIcon(QIcon::fromTheme("ukui-eye-hidden-symbolic")); + } +} + +void IconEdit::resizeEvent(QResizeEvent *) +{ + // 设置输入框中文件输入区,不让输入的文字在被隐藏在按钮下 + int w = m_iconButton->width() + m_modeButton->width(); + m_edit->setTextMargins(1, 1, capsState ? w + m_capsIcon->width() + 8 : w, 1); + m_edit->setFixedSize(size()); +} + + +void IconEdit::setX11Focus() +{ + XSetInputFocus(QX11Info::display(),this->winId(),RevertToParent,CurrentTime); + update(); +} + +void IconEdit::clicked_cb() +{ + startWaiting(); + emit clicked(m_edit->text()); +} + +void IconEdit::onCapsChanged() +{ + capsState = settings->get("capslock-state").toBool(); + m_capsIcon->setVisible(capsState); + int w = m_iconButton->width() + m_modeButton->width(); + m_edit->setTextMargins(1, 1, capsState ? w + m_capsIcon->width() + 8 : w, 1); +} + +//void IconEdit::onCapsStateChanged() +//{ +// bool capsState = m_capsIcon->isHidden(); + +//} + +void IconEdit::setIcon(const QString &text) +{ + m_iconButton->setIcon(QIcon()); + m_iconButton->setText(text); + m_iconText = text; + m_icon = QIcon(); +} + +void IconEdit::setIcon(const QIcon &icon) +{ + m_iconButton->setIcon(icon); + m_iconButton->setText(""); + m_icon = icon; + m_iconText = ""; +} + +void IconEdit::clear() +{ + m_edit->setText(""); + setPrompt(""); +} + +void IconEdit::clearText() +{ + m_edit->setText(""); +} + +void IconEdit::setPrompt(const QString &prompt) +{ + m_edit->setPlaceholderText(prompt); + QPalette palette = m_edit->palette(); + palette.setColor(QPalette::Normal, QPalette::PlaceholderText, Qt::gray); + m_edit->setPalette(palette); +} + +const QString IconEdit::text() +{ + return m_edit->text(); +} + +void IconEdit::readOnly(bool enabled) +{ + m_edit->setEnabled(!enabled); + m_edit->setReadOnly(enabled); +} + +void IconEdit::setLocked(bool lock) +{ + m_iconButton->blockSignals(lock); +} + +void IconEdit::startWaiting() +{ + m_edit->setReadOnly(true); + //m_iconButton->setEnabled(false); + + if(!m_timer) + { + m_timer = new QTimer(this); + m_timer->setInterval(150); + connect(m_timer, &QTimer::timeout, this, &IconEdit::updatePixmap); + } + + //m_waitingPixmap.load(":/image/assets/ukui-loginopt-face.svg"); + +// m_iconButton->setProperty("useIconHighlightEffect", true); +// m_iconButton->setProperty("iconHighlightEffectMode", 1); +// m_waitingPixmap = ImageUtil::loadSvg(":/image/assets/ukui-loginopt-face.svg", "white", 24); +// m_iconButton->setIcon(QIcon(m_waitingPixmap)); + + QPixmap icon = QIcon::fromTheme("ukui-loading-symbolic-0").pixmap(16,16); + m_waitingPixmap = ImageUtil::drawSymbolicColoredPixmap(icon, "white"); + m_iconButton->setIcon(m_waitingPixmap); + m_timer->start(); +} + + +void IconEdit::stopWaiting() +{ + if(m_timer && m_timer->isActive()) + { + m_timer->stop(); + } + m_iconButton->setAttribute(Qt::WA_TransparentForMouseEvents, false); + m_edit->setReadOnly(false); + //m_iconButton->setEnabled(true); + if(!m_icon.isNull()) + m_iconButton->setIcon(m_icon); + else + m_iconButton->setText(m_iconText); +} + +void IconEdit::updatePixmap() +{ + m_iconButton->setEnabled(true); + m_iconButton->setAttribute(Qt::WA_TransparentForMouseEvents, true); + QMatrix matrix; + matrix.rotate(90.0); + m_waitingPixmap = m_waitingPixmap.transformed(matrix, Qt::FastTransformation); + m_iconButton->setIcon(QIcon(m_waitingPixmap)); +} +/* +void IconEdit::setCapsState(bool capsState) +{ + m_capsIcon->setVisible(capsState); + int w = m_iconButton->width() + m_modeButton->width(); + m_edit->setTextMargins(1, 1, capsState ? w + m_capsIcon->width() : w, 1); +} +*/ diff --git a/src/iconedit.h b/src/iconedit.h new file mode 100644 index 0000000..7a66db7 --- /dev/null +++ b/src/iconedit.h @@ -0,0 +1,80 @@ +/* iconedit.h + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301, USA. +**/ + +#ifndef ICONEDIT_H +#define ICONEDIT_H + +#include +#include +#include +#include +#include +#include +#include + +class IconEdit : public QWidget +{ + Q_OBJECT +public: + IconEdit(QWidget *parent = 0); + + void setIcon(const QString &text); + void setIcon(const QIcon &icon); + void clear(); + void clearText(); + void setPrompt(const QString &); + const QString text(); + void setType(QLineEdit::EchoMode type = QLineEdit::Password); + void startWaiting(); + void stopWaiting(); + void setX11Focus(); + void readOnly(bool enabled); + void setLocked(bool lock); + //void setEnabled(bool enabled); + +protected: + void resizeEvent(QResizeEvent *) Q_DECL_OVERRIDE; + bool eventFilter(QObject *obj, QEvent *event); + +private: + void updatePixmap(); + +Q_SIGNALS: + void clicked(const QString &); + void focusOut(); + void clickedPassword(bool clicked); +public Q_SLOTS: + void clicked_cb(); + void onCapsChanged(); +// void onCapsStateChanged(); + +private: + QLineEdit *m_edit; + QSvgWidget *m_capsIcon; + QPushButton *m_iconButton; + QPushButton *m_modeButton; + QTimer *m_timer; + QPixmap m_waitingPixmap; + QString m_iconText; //文字作为图标 + QIcon m_icon; + QGSettings *settings; + bool capsState = false; +}; + +#endif // ICONEDIT_H diff --git a/src/image.qrc b/src/image.qrc new file mode 100644 index 0000000..ca67c5d --- /dev/null +++ b/src/image.qrc @@ -0,0 +1,7 @@ + + + image/hide-password.png + image/show-password.png + image/warn.png + + diff --git a/src/imageutil.cpp b/src/imageutil.cpp new file mode 100644 index 0000000..928d63f --- /dev/null +++ b/src/imageutil.cpp @@ -0,0 +1,81 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * Copyright (C) 2019 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "imageutil.h" + +#include +#include +const QPixmap ImageUtil::loadSvg(const QString &path, const QString color, int size) +{ + int origSize = size; + const auto ratio = qApp->devicePixelRatio(); + if ( 2 == ratio) { + size += origSize; + } else if (3 == ratio) { + size += origSize; + } + QPixmap pixmap(size, size); + QSvgRenderer renderer(path); + pixmap.fill(Qt::transparent); + + QPainter painter; + painter.begin(&pixmap); + renderer.render(&painter); + painter.end(); + + pixmap.setDevicePixelRatio(ratio); + return drawSymbolicColoredPixmap(pixmap, color); +} + +QPixmap ImageUtil::drawSymbolicColoredPixmap(const QPixmap &source, QString cgColor) +{ + QImage img = source.toImage(); + for (int x = 0; x < img.width(); x++) { + for (int y = 0; y < img.height(); y++) { + auto color = img.pixelColor(x, y); + if (color.alpha() > 0) { + if ( "white" == cgColor) { + color.setRed(255); + color.setGreen(255); + color.setBlue(255); + img.setPixelColor(x, y, color); + } else if( "black" == cgColor) { + color.setRed(0); + color.setGreen(0); + color.setBlue(0); + img.setPixelColor(x, y, color); + } else if ("gray"== cgColor) { + color.setRed(152); + color.setGreen(163); + color.setBlue(164); + img.setPixelColor(x, y, color); + } else if ("blue" == cgColor){ + color.setRed(61); + color.setGreen(107); + color.setBlue(229); + img.setPixelColor(x, y, color); + } else { + return source; + } + } + } + } + return QPixmap::fromImage(img); +} diff --git a/src/imageutil.h b/src/imageutil.h new file mode 100644 index 0000000..92c09d8 --- /dev/null +++ b/src/imageutil.h @@ -0,0 +1,35 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * Copyright (C) 2019 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef IMAGEUTIL_H +#define IMAGEUTIL_H + +#include +#include +#include + +class ImageUtil +{ +public: + static const QPixmap loadSvg(const QString &path, const QString color, int size = 16); + static QPixmap drawSymbolicColoredPixmap(const QPixmap &source, QString cgColor); +}; + +#endif // IMAGEUTIL_H diff --git a/src/interface.cpp b/src/interface.cpp new file mode 100644 index 0000000..12d5b60 --- /dev/null +++ b/src/interface.cpp @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#include "interface.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +Interface::Interface(QObject *parent) + : QObject(parent), + m_timerCount(0), + settings(nullptr), + m_timer(nullptr) +{ + lockState = false; + slpState = false; + m_logind = new LogindIntegration(this); + connect(m_logind, &LogindIntegration::requestLock, this, + [this]() { + this->onShowScreensaver(); + } + ); + + connect(m_logind, &LogindIntegration::requestUnlock, this, + [this]() { +// process.terminate(); +// 因为fullbackgroundwidget.cpp会监听这个信号,因此这个信号没必要监听了 +// process.kill(); + } + ); + + connect(&process, static_cast(&QProcess::finished), + [=](int exitCode, QProcess::ExitStatus exitStatus){ + emitLockState(false); + }); + + settings = new QGSettings("org.ukui.screensaver","",this); + + QDBusInterface *iface = new QDBusInterface("org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + QDBusConnection::systemBus(), + this); + connect(iface, SIGNAL(PrepareForSleep(bool)), this, SLOT(onPrepareForSleep(bool))); + inhibit(); + +} + +bool Interface::GetSlpState() +{ + return ((process.state() != QProcess::NotRunning) && slpState); +} + +bool Interface::GetLockState() +{ + return ((process.state() != QProcess::NotRunning) && lockState); +} + +void Interface::SetLockState() +{ + lockState = true; +} + +void Interface::emitLockState(bool val) +{ + QDBusMessage message; + if(val){ + message = QDBusMessage::createSignal(SS_DBUS_PATH, + SS_DBUS_INTERFACE, + "lock"); + }else{ + message = QDBusMessage::createSignal(SS_DBUS_PATH, + SS_DBUS_INTERFACE, + "unlock"); + } + QDBusConnection::sessionBus().send(message); +} + +void Interface::UnLock() +{ + if(process.state() != QProcess::NotRunning) + process.kill(); +} + +void Interface::Lock() +{ + if(process.state() != QProcess::NotRunning) + return ; + qDebug() << "Lock requested"; + lockState = false; + QString cmd = QString("/usr/bin/ukui-screensaver-dialog --lock"); + qDebug() << cmd; + + process.start(cmd); + emitLockState(true); +} + +void Interface::onSessionIdleReceived() +{ + if(process.state() != QProcess::NotRunning) + return ; + + qDebug() << "emit SessionIdle"; + lockState = false; + QString cmd = QString("/usr/bin/ukui-screensaver-dialog --session-idle"); + qDebug() << cmd; + process.start(cmd); + emitLockState(true); +} + +void Interface::onShowBlankScreensaver() +{ + if(process.state() != QProcess::NotRunning) + return ; + + qDebug() << "lock and show screensaver"; + lockState = false; + slpState = false; + QString cmd = QString("/usr/bin/ukui-screensaver-dialog --blank"); + qDebug() << cmd; + + process.start(cmd); + emitLockState(true); +} + +void Interface::onShowScreensaver() +{ + if(process.state() != QProcess::NotRunning) + return ; + + slpState = true; + qDebug() << "lock and show screensaver"; + lockState = false; + QString cmd = QString("/usr/bin/ukui-screensaver-dialog --lock-screensaver"); + qDebug() << cmd; + + process.start(cmd); + emitLockState(true); +} + +bool Interface::checkExistChild() +{ + char cmd[128] = {0}; + char str[4]; + FILE *fp; + int num; + + sprintf(cmd, "ps -aux | grep ukui-screensaver-dialog | grep %s | grep -v grep | wc -l", getenv("USER")); + + fp = popen(cmd, "r"); + if(fgets(str, sizeof(str)-1, fp) == NULL) + qDebug() << "fgets: " << strerror(errno); + pclose(fp); + + num = atoi(str); + + qDebug() << (num > 0 ? "exist dialog running" : ""); + + return num > 0; +} + +void Interface::onNameLost(const QString &serviceName) +{ + if(serviceName == "cn.kylinos.ScreenSaver") + exit(0); +} + +void Interface::onPrepareForSleep(bool sleep) +{ + if(sleep) + { + if(!settings->get("sleep-activation-enabled").toBool()){ + uninhibit(); + return; + } + + if(GetLockState()){ + uninhibit(); + return; + } + + if(GetSlpState()){ + uninhibit(); + return; + } + + this->onShowBlankScreensaver(); + + if(!m_timer){ + m_timer = new QTimer(this); + connect(m_timer, &QTimer::timeout, this, [&]{ + m_timerCount+=1; + + if(GetLockState() || m_timerCount>20){ + m_timer->stop(); + m_timerCount = 0; + uninhibit(); + } + }); + } + m_timer->start(200); + } + else + { + inhibit(); + } +} + +void Interface::ShowScreensaver() +{ + if(process.state() != QProcess::NotRunning) + return ; + + qDebug() << "lock and show screensaver"; + lockState = false; + QString cmd = QString("/usr/bin/ukui-screensaver-dialog --screensaver"); + qDebug() << cmd; + + process.start(cmd); + emitLockState(true); +} + +void Interface::inhibit() +{ + if (m_inhibitFileDescriptor.isValid()) { + return; + } + + QDBusMessage message = QDBusMessage::createMethodCall("org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + QStringLiteral("Inhibit")); + message.setArguments(QVariantList({QStringLiteral("sleep"), + "Screen Locker Backend", + "Ensuring that the screen gets locked before going to sleep", + QStringLiteral("delay")})); + QDBusPendingReply reply = QDBusConnection::systemBus().call(message); + if (!reply.isValid()) { + return; + } + reply.value().swap(m_inhibitFileDescriptor); +} + +void Interface::uninhibit() +{ + if (!m_inhibitFileDescriptor.isValid()) { + return; + } + + m_inhibitFileDescriptor = QDBusUnixFileDescriptor(); +} diff --git a/src/interface.h b/src/interface.h new file mode 100644 index 0000000..411ce5c --- /dev/null +++ b/src/interface.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#ifndef INTERFACE_H +#define INTERFACE_H + +#include +#include +#include +#include +#include +#include "types.h" +#include "logind.h" + +class QGSettings; +class Interface : public QObject, protected QDBusContext +{ + Q_OBJECT + + Q_CLASSINFO("D-Bus Interface", SS_DBUS_SERVICE) + +public: + explicit Interface(QObject *parent = nullptr); + LogindIntegration *m_logind; +Q_SIGNALS: + void SessionIdle(); + +public Q_SLOTS: + /** + * Lock the screen + */ + void Lock(); + void UnLock(); + void ShowScreensaver(); + bool GetLockState(); + bool GetSlpState(); + void SetLockState(); + void onSessionIdleReceived(); + void onShowScreensaver(); + void onShowBlankScreensaver(); + void onNameLost(const QString&); + void onPrepareForSleep(bool sleep); + +private: + bool checkExistChild(); + void inhibit(); + void uninhibit(); + bool lockState; + bool slpState; + void emitLockState(bool); + int m_timerCount; + QGSettings *settings; + QTimer *m_timer; + QDBusUnixFileDescriptor m_inhibitFileDescriptor; + +private: + QProcess process; +}; + +#endif // INTERFACE_H diff --git a/src/lockwidget.cpp b/src/lockwidget.cpp new file mode 100644 index 0000000..19678ae --- /dev/null +++ b/src/lockwidget.cpp @@ -0,0 +1,987 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ + +#include "lockwidget.h" + +#include "powermanager.h" +#include "ui_lockwidget.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "authdialog.h" +#include "virtualkeyboard.h" +#include "users.h" +#include "displaymanager.h" +#include "config.h" +#include "commonfunc.h" +#include "hoverwidget.h" +#include "kylinnm.h" + +#include +#include +#include + +#define TIME_TYPE_SCHEMA "org.ukui.control-center.panel.plugins" +#define CONFIG_FILE "/etc/lightdm/ukui-greeter.conf" +float scale; +LockWidget::LockWidget(QWidget *parent) + : QWidget(parent), + ui(new Ui::LockWidget), + usersMenu(nullptr), + scrollArea(nullptr), + users(new Users(this)), + displayManager(new DisplayManager(this)), + timeType(24), + m_kylinNM(nullptr) +{ + scale = 1.0; + ui->setupUi(this); + // m_kylinNM->installEventFilter(this); + + UserItem user = users->getUserByName(getenv("USER")); + authDialog = new AuthDialog(user, this); + authDialog->installEventFilter(this); + connect(authDialog, &AuthDialog::authenticateCompete, + this, &LockWidget::closed); + connect(authDialog,&AuthDialog::clickPassword, + this,&LockWidget::hideNetManager); +// connect(this, &LockWidget::capsLockChanged, +// authDialog, &AuthDialog::onCapsLockChanged); + +// connect(m_kylinNM, &KylinNM::onConnectChanged, this,[=](){ +// updateNetIcon(m_kylinNM->getConnectStatus()); +// }); + + xEventMonitor = new XEventMonitor(this); + connect(xEventMonitor, SIGNAL(keyPress(const QString &)), this, + SLOT(onGlobalKeyPress(const QString &))); + connect(xEventMonitor, SIGNAL(keyRelease(const QString &)), this, + SLOT(onGlobalkeyRelease(const QString &))); + xEventMonitor->start(); + + this->installEventFilter(this); + initUI(); + + if(users->getUsers().count() < 2){ + ui->btnSwitchUser->hide(); + } +} + +LockWidget::~LockWidget() +{ + delete ui; +} + +void LockWidget::closeEvent(QCloseEvent *event) +{ + qDebug() << "LockWidget::closeEvent"; + authDialog->close(); + return QWidget::closeEvent(event); +} + +bool LockWidget::eventFilter(QObject *obj, QEvent *event) +{ + if (obj == m_kylinNM) + return true; + + if(event->type() == 2){ + if(obj == ui->btnPowerManager || obj == ui->btnSwitchUser || obj == ui->btnNetworkManager) + return false; + + if(scrollArea && scrollArea->isVisible()){ + scrollArea->hide(); + } + if(powermanager && powermanager->isVisible()){ + authDialog->setFocus(); + authDialog->show(); + powermanager->hide(); + } + if(m_kylinNM && m_kylinNM->isVisible()){ + m_kylinNM->hide(); + } + } + + if (obj == scrollContents ){ + if (event->type() == 76 ) { + usersMenu->show(); + //qDebug()<<"````````````````````````````````````````````"<type(); + } + } + + if(event->type() == QEvent::WindowActivate){ + if(!isNetFinished){ + isNetFinished = true; + QTimer::singleShot(2000, this, [=](){ + m_kylinNM = new KylinNM(this); + m_kylinNM->installEventFilter(this); + connect(m_kylinNM, &KylinNM::onConnectChanged, this,[=](){ + updateNetIcon(m_kylinNM->getConnectStatus()); + }); + m_kylinNM->hide(); + QtConcurrent::run([=](){ + updateNetIcon(getNetStatus()); + }); + }); + } + } + + return false; +} + +void LockWidget::onGlobalKeyPress(const QString &key) +{ +} + +void LockWidget::onGlobalkeyRelease(const QString &key) +{ + if (key == "Escape") { + + } else if (key == "Up" || key == "Down" || key == "Return" || key == "KP_Enter") { + qDebug()<<" key = "<isVisible()) + keyBdRelease(key); + } +} + +void LockWidget::keyBdRelease(QString key) +{ + //usersMenu->setFocus(); + QString focus = "focus"; + QString normal = "normal"; + if(key == "Up"){ +// if(nowAt == -1){ +// nowAt = 0; +// setButtonStyle(focus); +// } + for(int i = 0; i < list.count(); i++){ + if(nowAt == i){ + if(i == 0) + return ; + list.at(i)->setStyleSheet("HoverWidget{background-color:transparent;}" + "HoverWidget:hover:!pressed{background-color:rgb(255,255,255,15%);border-radius: 6px;}"); + list.at(i-1)->setStyleSheet("HoverWidget{background-color:rgb(255,255,255,40%);border-radius: 6px;}" + "HoverWidget:hover:!pressed{background-color:rgb(255,255,255,15%);border-radius: 6px;}"); + nowAt = i-1; + + /*else if(i == 0){ + qDebug()<<"enheng???111"<setStyleSheet("HoverWidget{background-color:rgb(255,255,255,40%);border-radius: 6px;}" + "HoverWidget:hover:!pressed{background-color:rgb(255,255,255,15%);border-radius: 6px;}"); + nowAt = list.count() -1; + }*/ + } + } + } else if(key == "Down") { + for(int i = list.count(); i >= 0; i--){ + if(nowAt == i){ + if(i == list.count() - 1) + return ; + list.at(i)->setStyleSheet("HoverWidget{background-color:transparent;}" + "HoverWidget:hover:!pressed{background-color:rgb(255,255,255,15%);border-radius: 6px;}"); + list.at(i+1)->setStyleSheet("HoverWidget{background-color:rgb(255,255,255,40%);border-radius: 6px;}" + "HoverWidget:hover:!pressed{background-color:rgb(255,255,255,15%);border-radius: 6px;}"); + nowAt = i+1; + /*else if(i == list.count() - 1){ + qDebug()<<"enheng???111"<setStyleSheet("HoverWidget{background-color:rgb(255,255,255,40%);border-radius: 6px;}" + "HoverWidget:hover:!pressed{background-color:rgb(255,255,255,15%);border-radius: 6px;}"); + nowAt = 0; + }*/ + } + } + } else if(key == "Return" || key == "KP_Enter"){ + if(authDialog) + { + authDialog->stopAuth(); + } + if(list.at(nowAt)->objectName() == "Guest") + { + displayManager->switchToGuest(); + } + else if(list.at(nowAt)->objectName() == "SwitchUser") + { + displayManager->switchToGreeter(); + } + else + { + displayManager->switchToUser(list.at(nowAt)->objectName()); + } + } +} + +void LockWidget::startAuth() +{ + if(authDialog) + { + authDialog->startAuth(); + } +} + +void LockWidget::stopAuth() +{ + if(authDialog) + { + authDialog->stopAuth(); + } +} + +void LockWidget::setX11Focus() +{ + if(authDialog){ + authDialog->setX11Focus(); + } +} + +void LockWidget::initUI() +{ + setFocusProxy(authDialog); + + if(QGSettings::isSchemaInstalled(TIME_TYPE_SCHEMA)){ + QGSettings *time_type = new QGSettings(TIME_TYPE_SCHEMA); + QStringList keys = time_type->keys(); + if (keys.contains("hoursystem")) { + timeType = time_type->get("hoursystem").toInt(); + } + if (keys.contains("date")) { + dateType = time_type->get("date").toString(); + } + } + + //显示系统时间 + timer = new QTimer(this); + connect(timer, &QTimer::timeout, this, [&]{ + if(timeType == 12) + ui->lblTime->setText(QDateTime::currentDateTime().toString("A hh:mm")); + else + ui->lblTime->setText(QDateTime::currentDateTime().toString("hh:mm")); + + if(dateType == "cn") + ui->lblDate->setText(QDate::currentDate().toString("yyyy/MM/dd ddd")); + else + ui->lblDate->setText(QDate::currentDate().toString("yyyy-MM-dd ddd")); + }); + + if(timeType == 12) + ui->lblTime->setText(QDateTime::currentDateTime().toString("A hh:mm")); + else + ui->lblTime->setText(QDateTime::currentDateTime().toString("hh:mm")); + + ui->lblTime->setStyleSheet("QLabel{color:white; font-size: 36px;}"); + ui->lblTime->setAlignment(Qt::AlignCenter); + ui->lblTime->adjustSize(); + timer->start(1000); + +// QString date = QDate::currentDate().toString("yyyy/MM/dd ddd"); +// qDebug() << "current date: " << date; +// ui->lblDate->setText(date); + if(dateType == "cn") + ui->lblDate->setText(QDate::currentDate().toString("yyyy/MM/dd ddd")); + else + ui->lblDate->setText(QDate::currentDate().toString("yyyy-MM-dd ddd")); + + ui->lblDate->setStyleSheet("QLabel{color:white; font-size: 18px;}"); + ui->lblDate->setAlignment(Qt::AlignCenter); + ui->lblDate->adjustSize(); + ui->widgetTime->adjustSize(); + + //电源管理 + ui->btnPowerManager->setIcon(QIcon(":/image/assets/powerManager.png")); + ui->btnPowerManager->setFixedSize(48,48); + ui->btnPowerManager->setIconSize(QSize(24,24)); + ui->btnPowerManager->setFocusPolicy(Qt::NoFocus); + ui->btnPowerManager->installEventFilter(this); + + connect(ui->btnPowerManager,&QPushButton::clicked + ,this,&LockWidget::showPowerManager); + + + QtConcurrent::run([=](){ + updateNetIcon(getNetStatus()); + }); + + ui->btnNetworkManager->setFixedSize(48,48); + ui->btnNetworkManager->setIconSize(QSize(24,24)); + ui->btnNetworkManager->setFocusPolicy(Qt::NoFocus); +// ui->btnNetworkManager->setStyleSheet("QPushButton:Hover{border-radius:24px;background-color:rgba(255, 255, 255, 0.15);}" +// "QPushButton:Pressed{border-radius:24px;background-color:rgba(255, 255, 255, 0.05);}"); + ui->btnNetworkManager->installEventFilter(this); + + connect(ui->btnNetworkManager,&QPushButton::clicked + ,this,&LockWidget::showNetManager); + + //虚拟键盘 + ui->btnKeyboard->setIcon(QIcon(":/image/assets/keyboard.svg")); + ui->btnKeyboard->setFixedSize(48, 48); + ui->btnKeyboard->setIconSize(QSize(24, 24)); + ui->btnKeyboard->setFocusPolicy(Qt::NoFocus); + ui->btnKeyboard->installEventFilter(this); +/* connect(ui->btnKeyboard, &QPushButton::clicked, + this, [&]{ + qDebug() << vKeyboard->isHidden(); + vKeyboard->setVisible(vKeyboard->isHidden()); + }); +*/ + connect(ui->btnKeyboard, &QPushButton::clicked, + this, &LockWidget::showVirtualKeyboard); + + //用户切换 + if(displayManager->canSwitch()) + { + //initUserMenu(); + ui->btnSwitchUser->setIcon(QIcon(":/image/assets/switchUser.png")); + ui->btnSwitchUser->setIconSize(QSize(24, 24)); + ui->btnSwitchUser->setFixedSize(48, 48); + ui->btnSwitchUser->setFocusPolicy(Qt::NoFocus); + ui->btnSwitchUser->raise(); + + connect(ui->btnSwitchUser, &QPushButton::clicked, + this, [&]{ + if(!usersMenu){ + initUserMenu(); + } + + if(scrollArea && scrollArea->isVisible()){ + scrollArea->hide(); + } + else{ + scrollArea->show(); + if(m_kylinNM && m_kylinNM->isVisible()) + m_kylinNM->hide(); + scrollArea->setFocus(); + } + }); + } + +} + +void LockWidget::showVirtualKeyboard() +{ + if(!vKeyboard){ + vKeyboard = new VirtualKeyboard(this); + vKeyboard->hide(); + + connect(vKeyboard, &VirtualKeyboard::aboutToClose, + vKeyboard, &VirtualKeyboard::hide); + } + + vKeyboard->setVisible(vKeyboard->isHidden()); + if(!vKeyboard->isHidden()){ + vKeyboard->raise(); + } + setVirkeyboardPos(); +} + +void LockWidget::showPowerManager() +{ + if(!powermanager){ + powermanager = new PowerManager(this); + sureWidget = new SureWindow(this); + powermanager->hide(); + connect(powermanager,SIGNAL(lock()) + ,this,SLOT(showPowerManager())); + connect(powermanager,SIGNAL(switchToUser()) + ,this,SLOT(switchToGreeter())); + connect(powermanager, SIGNAL(mulUsersLogined(int)), + this,SLOT(switchToSureDialog(int))); + sureWidget->adjustSize(); + sureWidget->move((width()-sureWidget->geometry().width())/2, (height()-sureWidget->geometry().height())/2); + sureWidget->hide(); + connect(sureWidget, SIGNAL(cantelButtonclicked()), + this, SLOT(hideSureDialog())); + connect(sureWidget, SIGNAL(confirmButtonclicked()), + this, SLOT(confirmClicked())); + } + + if(m_kylinNM && m_kylinNM->isVisible()) + m_kylinNM->hide(); + if(scrollArea && scrollArea->isVisible()) + scrollArea->hide(); + if(sureWidget && sureWidget->isVisible()) + sureWidget->hide(); + if(powermanager->isVisible()){ + authDialog->setFocus(); + authDialog->show(); + powermanager->hide(); + } + else{ + authDialog->hide(); + powermanager->show(); + powermanager->setFocus(); +#ifndef USE_INTEL + powermanager->showNormalSize(); +#endif + powermanager->setGeometry((width()-powermanager->width())/2, + (height()-powermanager->height())/2, + powermanager->width(),powermanager->height()); + } +} + +void LockWidget::switchToSureDialog(int type) +{ + powermanagerType = type; + if(m_kylinNM && m_kylinNM->isVisible()) + m_kylinNM->hide(); + if(scrollArea && scrollArea->isVisible()) + scrollArea->hide(); + + authDialog->hide(); + powermanager->hide(); + sureWidget->show(); + sureWidget->setFocus(); + ui->widgetTime->hide(); + ui->btnPowerManager->hide(); + ui->btnNetworkManager->hide(); + ui->btnSwitchUser->hide(); + ui->btnKeyboard->hide(); +} + +void LockWidget::hideSureDialog() +{ + sureWidget->hide(); + powermanager->show(); + ui->widgetTime->show(); + ui->btnPowerManager->show(); + ui->btnNetworkManager->show(); + ui->btnSwitchUser->show(); + ui->btnKeyboard->show(); +} + +void LockWidget::confirmClicked() +{ + if(m_kylinNM && m_kylinNM->isVisible()) + m_kylinNM->hide(); + if(scrollArea && scrollArea->isVisible()) + scrollArea->hide(); + sureWidget->hide(); + powermanager->hide(); + authDialog->show(); + ui->widgetTime->show(); + ui->btnPowerManager->show(); + ui->btnNetworkManager->show(); + ui->btnSwitchUser->show(); + ui->btnKeyboard->show(); + powermanager->doEvent(powermanagerType); +} + +void LockWidget::showNetManager() +{ + if(!m_kylinNM){ + return; + } + + if(m_kylinNM->isVisible()) + { + m_kylinNM->hide(); + } else { + m_kylinNM->show(); + if(scrollArea && scrollArea->isVisible()) + scrollArea->hide(); + m_kylinNM->setGeometry(this->width() - m_kylinNM->width() - 20, + this->height() - m_kylinNM->height() - 100, + m_kylinNM->width(), + m_kylinNM->height()); + m_kylinNM->updateWifiList(); + } +} + +void LockWidget::hideNetManager() +{ + if(!m_kylinNM){ + return; + } + + if(m_kylinNM && m_kylinNM->isVisible()) + m_kylinNM->hide(); + if(scrollArea && scrollArea->isVisible()) + scrollArea->hide(); +} + +int LockWidget::getNetStatus() +{ + int ret = -1; + QString actLanName = "--"; + QString actWifiName = "--"; + + activecon *act = kylin_network_get_activecon_info(); + int index = 0; + while (act[index].con_name != NULL) { + if (QString(act[index].type) == "ethernet" || QString(act[index].type) == "802-3-ethernet") { + actLanName = QString(act[index].con_name); + } + if (QString(act[index].type) == "wifi" || QString(act[index].type) == "802-11-wireless") { + actWifiName = QString(act[index].con_name); + } + index ++; + } + + // 设置图标 + if (actLanName != "--") { + ret = 0; + } else if (actWifiName != "--") { + ret = 1; + } else { + ret = -1; + } + qDebug() << "[KylinNM] [getConnectStatus] getConnectStatus = " << ret; + //KylinDBus::checkConnectivity(); + return ret; +} + +void LockWidget::updateNetIcon(int status) +{ + switch(status) { + case 0: + //有线 + ui->btnNetworkManager->setIcon(QIcon(":/image/assets/intel/icon-wired.png")); + break; + case 1: + //无线 + ui->btnNetworkManager->setIcon(QIcon(":/image/assets/intel/icon-wifi.png")); + break; + case 2: + //有线+无线 + ui->btnNetworkManager->setIcon(QIcon(":/image/assets/intel/icon-wired.png")); + break; + default: + //无连接 + ui->btnNetworkManager->setIcon(QIcon(":/image/assets/intel/icon-no-signal.png")); + } +} + +void LockWidget::switchToGreeter() +{ + if(authDialog) + { + authDialog->stopAuth(); + } + displayManager->switchToGreeter();; +} + +void LockWidget::setVirkeyboardPos() +{ + if(vKeyboard) + { + vKeyboard->setGeometry(0, + height() - height()/3, + width(), height()/3); + + } +} + + +void LockWidget::initUserMenu() +{ + scrollArea = new QScrollArea(this); + scrollArea->installEventFilter(this); +// scrollArea->setAttribute(Qt::WA_TranslucentBackground); //设置背景透明 +// scrollArea->viewport()->setAttribute(Qt::WA_TranslucentBackground); //设置背景透明 + scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); //屏蔽水平滚动条 + scrollArea->setStyleSheet("QScrollArea {background-color: rgba(255,255,255,15%);border-radius:8px;}"); + scrollArea->viewport()->setStyleSheet("background-color:transparent;"); + scrollArea->verticalScrollBar()->setProperty("drawScrollBarGroove", false); + scrollArea->verticalScrollBar()->setContextMenuPolicy(Qt::NoContextMenu); + scrollContents = new QWidget(scrollArea); + scrollContents->installEventFilter(this); + scrollArea->setWidget(scrollContents); + scrollArea->hide(); + + if(!usersMenu) + { + usersMenu = new QMenu(scrollContents); + usersMenu->setObjectName("usersMenu"); + usersMenu->setToolTipsVisible(true); +// usersMenu->setStyleSheet("QMenu{background-color:transparent;color:white;padding:5px 5px 5px 8px}"); + //QToolTip{border-radius:4px;background-color:rgb(255,255,255,40%);color:white;} + //如果没有设置x11属性,则由于弹出菜单受窗口管理器管理,而主窗口不受,在点击菜单又点回主窗口会闪屏。 + usersMenu->setWindowFlags(Qt::X11BypassWindowManagerHint); + //usersMenu->hide(); + connect(usersMenu, &QMenu::triggered, + this, &LockWidget::onUserMenuTrigged); + } + + if(displayManager->getDisplayType() == "gdm"){ +// QAction *action = new QAction(QIcon(users->getDefaultIcon()), +// tr("SwitchUser"), this); + QWidgetAction *action = new QWidgetAction(usersMenu); + HoverWidget *widget = new HoverWidget(""); + widget->setStyleSheet("HoverWidget:hover{background-color:rgb(255,255,255,15%);border-radius: 6px;}"); + widget->setObjectName("SwitchUser"); + list.append(widget); + widget->setFixedSize(240, 40); + QLabel *iconlabel =new QLabel(widget); + QPixmap p(users->getDefaultIcon()); + p = p.scaled(32*scale,32*scale, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + p = PixmapToRound(p, 16*scale); + iconlabel->setPixmap(p); + iconlabel->setGeometry(8,4,32,32); + QLabel *textlabel =new QLabel(widget); + textlabel->setStyleSheet("QToolTip{border-radius:4px;background-color:#FFFFFF;color:black;font-size:16px}"); + textlabel->setGeometry(48,8,190,24); + action->setToolTip("SwitchUser"); + action->setData("SwitchUser"); + usersMenu->addAction(action); + list.append(widget); + } + + else if(displayManager->getDisplayType() == "lightdm"){ + connect(users, &Users::userAdded, this, &LockWidget::onUserAdded); + connect(users, &Users::userDeleted, this, &LockWidget::onUserDeleted); + + for(auto user : users->getUsers()) + { + onUserAdded(user); + } + + if(displayManager->hasGuestAccount()) + { +// QAction *action = new QAction(QIcon(users->getDefaultIcon()), +// tr("Guest"), this); + QWidgetAction *action = new QWidgetAction(usersMenu); + HoverWidget *widget = new HoverWidget(""); + widget->setStyleSheet("HoverWidget:hover{background-color:rgb(255,255,255,15%);border-radius: 6px;}"); + widget->setObjectName("Guest"); + list.append(widget); + widget->setFixedSize(240, 40); + QLabel *iconlabel =new QLabel(widget); + QPixmap p(users->getDefaultIcon()); + p = p.scaled(32*scale,32*scale, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + p = PixmapToRound(p, 16*scale); + iconlabel->setPixmap(p); + iconlabel->setGeometry(8,4,32,32); + QLabel *textlabel =new QLabel(widget); + textlabel->setStyleSheet("QToolTip{border-radius:4px;background-color:#FFFFFF;color:black;font-size:16px}"); + textlabel->setGeometry(48,8,190,24); + action->setData("Guest"); + action->setToolTip("Guest"); + usersMenu->addAction(action); + list.append(widget); + } + } + UserItem currentUser = users->getUserByName(getenv("USER")); + for(int i = 0;i < list.count(); i++){ + if(list.at(i)->objectName() == currentUser.name){ + list.at(i)->setStyleSheet("HoverWidget{background-color:rgb(255,255,255,40%);border-radius: 6px;}" + "HoverWidget:hover:!pressed{background-color:rgb(255,255,255,40%);border-radius: 6px;}"); + nowAt = i; + } else { + list.at(i)->setStyleSheet("HoverWidget{background-color:transparent;}" + "HoverWidget:hover:!pressed{background-color:rgb(255,255,255,15%);border-radius: 6px;}"); + } + } + scrollContents->setFixedSize(usersMenu->width() - 14, usersMenu->height()); + //设置弹出菜单,设置弹出菜单的坐标为切换用户按钮的上方,中间保持一定间隔。 + if(scrollArea){ + if(scrollContents->height() < height()/2){ + scrollArea->setFixedSize(usersMenu->width(),scrollContents->height()+1); + }else{ + scrollArea->setFixedSize(usersMenu->width() + 4, height()/2); + } + scrollArea->move(ui->btnSwitchUser->geometry().x()- scrollArea->width()/2 + 24, \ + height() - 72 - scrollArea->height() - 5); + } + +} + +void LockWidget::keyReleaseEvent(QKeyEvent *e) +{ + if(e->key() == Qt::Key_K){ + if(e->modifiers() & Qt::ControlModifier) + showVirtualKeyboard(); + } +} + +/* lockscreen follows cursor */ +void LockWidget::resizeEvent(QResizeEvent *event) +{ + QSize size = event->size(); + //重新计算缩放比例 + scale = QString::number(size.width() / 1920.0, 'f', 1).toFloat(); + + //系统时间 + ui->widgetTime->move((width()-ui->widgetTime->geometry().width())/2, 59*scale); + if(sureWidget) + sureWidget->move((width()-sureWidget->geometry().width())/2, (height()-sureWidget->geometry().height())/2); + + //认证窗口 + //设置认证窗口左右居中 + if(scale >= 1) + authDialog->setGeometry((width()-authDialog->geometry().width())/2,height()/(4-0.65*scale), \ + authDialog->width(), (height()*3/4)); + else + authDialog->setGeometry((width()-authDialog->geometry().width())/2,height()/(4+0.65*scale), \ + authDialog->width(), (height()*3/4)); + + if(scale > 1) + scale = 1; + + //右下角按钮,x,y的初始值代表距离右下角的距离。 + int x=24,y=72; + x = x + ui->btnPowerManager->width(); + ui->btnPowerManager->move(width() - x,height() - y); + + x = x+ui->btnKeyboard->width()+16; + ui->btnKeyboard->move(width() - x, height() - y); + + x = x + ui->btnNetworkManager->width()+16; + ui->btnNetworkManager->move(width() - x, height() - y); + + x = x + ui->btnSwitchUser->width()+16; + ui->btnSwitchUser->move(width() - x, height() - y); + setVirkeyboardPos(); + + if(scrollContents && usersMenu) + scrollContents->setFixedSize(usersMenu->width() - 14, usersMenu->height()); + //设置弹出菜单,设置弹出菜单的坐标为切换用户按钮的上方,中间保持一定间隔。 + if(scrollArea){ + if(scrollContents->height() < height()/2){ + scrollArea->setFixedSize(usersMenu->width(),scrollContents->height()+1); + }else{ + scrollArea->setFixedSize(usersMenu->width() + 4, height()/2); + } + scrollArea->move(ui->btnSwitchUser->geometry().x()- scrollArea->width()/2 + 24, \ + height() - y - scrollArea->height() - 5); + } + + if(m_kylinNM){ + m_kylinNM->setGeometry(this->width() - m_kylinNM->width() - 20, + this->height() - m_kylinNM->height() - 100, + m_kylinNM->width(), + m_kylinNM->height()); + } + + if(powermanager){ +#ifndef USE_INTEL + if(width() < 1280) + powermanager->showSmallSize(); + else + powermanager->showNormalSize(); +#endif + powermanager->setGeometry((width()- powermanager->width())/2, + (height()-powermanager->height())/2, + powermanager->width(),powermanager->height()); + + } + XSetInputFocus(QX11Info::display(),this->winId(),RevertToParent,CurrentTime); +} + + +void LockWidget::onUserAdded(const UserItem &user) +{ +// QAction *action = new QAction(QIcon(user.icon), user.realName, this); +// action->setCheckable(true); + + QWidgetAction *action = new QWidgetAction(usersMenu); + HoverWidget *widget = new HoverWidget(""); + widget->setStyleSheet("HoverWidget:hover{background-color:rgb(255,255,255,15%);border-radius: 6px;}"); + widget->setObjectName(user.name); + list.append(widget); + widget->setFixedSize(240, 40); + QLabel *iconlabel =new QLabel(widget); + QPixmap p(user.icon); + p = p.scaled(32*scale,32*scale, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + p = PixmapToRound(p, 16*scale); + iconlabel->setPixmap(p); + //iconlabel->setStyleSheet("background-color:transparent;"); + iconlabel->setGeometry(8,4,32,32); + QLabel *textlabel =new QLabel(widget); + textlabel->setStyleSheet("QToolTip{border-radius:4px;background-color:#FFFFFF;color:black;font-size:16px}"); + textlabel->setGeometry(48,8,190,24); + if(getLoadStatus(user.name)){ + QLabel *statusIcon = new QLabel(widget); + QPixmap status(":/image/assets/selected.svg"); + status = status.scaled(14*scale,14*scale, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + p = PixmapToRound(status, 7*scale); + statusIcon->setPixmap(status); + statusIcon->setGeometry(32, 5, 14, 14); + } + action->setDefaultWidget(widget); + + QFont font; + font.setPixelSize(16); + QString str = ElideText(font,189,user.realName); + if(user.realName != str){ + textlabel->setToolTip(user.realName); + textlabel->setText(str); + } else { + textlabel->setText(user.realName); + } + action->setData(user.name); + usersMenu->addAction(action); + usersMenu->adjustSize(); +} + +QPixmap LockWidget::PixmapToRound(const QPixmap &src, int radius) +{ + if (src.isNull()) { + return QPixmap(); + } + + QPixmap pixmapa(src); + QPixmap pixmap(radius*2,radius*2); + pixmap.fill(Qt::transparent); + QPainter painter(&pixmap); + painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + QPainterPath path; + path.addEllipse(0, 0, radius*2, radius*2); + painter.setClipPath(path); + painter.drawPixmap(0, 0, radius*2, radius*2, pixmapa); + return pixmap; +} + +void LockWidget::onUserDeleted(const UserItem &user) +{ + for(auto action : usersMenu->actions()) + { + if(action->data() == user.name) + usersMenu->removeAction(action); + } +} + +bool LockWidget::getLoadStatus(const QString &name) +{ + QDBusInterface interface(LG_DBUS_SERVICE, + LG_DBUS_PATH, + LG_DBUS_INTERFACE, + QDBusConnection::systemBus()); + QDBusMessage result = interface.call("ListUsers"); + QList outArgs = result.arguments(); + QVariant first = outArgs.at(0); + QDBusArgument dbvFirst = first.value(); + QVariant vFirst = dbvFirst.asVariant(); + const QDBusArgument &dbusArgs = vFirst.value(); + + QVector loginedUsers; + + dbusArgs.beginArray(); + while (!dbusArgs.atEnd()) { + userInfo user; + dbusArgs >> user; + loginedUsers.push_back(user); + } + for (userInfo user : loginedUsers) { + QDBusInterface userPertyInterface("org.freedesktop.login1", + user.userPath.path(), + "org.freedesktop.DBus.Properties", + QDBusConnection::systemBus()); + QDBusReply reply = userPertyInterface.call("Get", "org.freedesktop.login1.User", "State"); + if (reply.isValid()) { + QString status = reply.value().toString(); + if ("closing" != status) { + m_loginedUser.append(user.userName); + } + } + } + int count = 0; + for(int i = 0 ;i 0) + return true; + else + return false; +} + +void LockWidget::onUserMenuTrigged(QAction *action) +{ + qDebug() << action->data().toString() << "selected"; + + if(authDialog) + { + authDialog->stopAuth(); + } + + QString userName = action->data().toString(); + for (int i =0; i < list.count(); i++) + { + if(list.at(i)->objectName() == userName){ + list.at(i)->setStyleSheet("HoverWidget{background-color:rgb(255,255,255,40%);border-radius: 6px;}" + "HoverWidget:hover:!pressed{background-color:rgb(255,255,255,40%);border-radius: 6px;}"); + } else { + list.at(i)->setStyleSheet("HoverWidget{background-color:transparent;}" + "HoverWidget:hover:!pressed{background-color:rgb(255,255,255,15%);border-radius: 6px;}"); + } + } + if(userName == "Guest") + { + displayManager->switchToGuest(); + } + else if(userName == "SwitchUser") + { + displayManager->switchToGreeter(); + } + else + { + displayManager->switchToUser(userName); + } +} + +bool LockWidget::exitSubWidget() +{ + bool allExited = true; + if(scrollArea && scrollArea->isVisible()){ + scrollArea->hide(); + allExited = false; + } + if(powermanager && powermanager->isVisible()){ + authDialog->setFocus(); + authDialog->show(); + powermanager->hide(); + allExited = false; + } + if(m_kylinNM && m_kylinNM->isVisible()){ + m_kylinNM->hide(); + allExited = false; + } + return allExited; +} + +QDBusArgument &operator <<(QDBusArgument &arg, const userInfo &usersInfo) +{ + arg.beginStructure(); + arg << usersInfo.userId + << usersInfo.userName + << usersInfo.userPath; + arg.endStructure(); + return arg; +} +const QDBusArgument &operator >>(const QDBusArgument &arg, userInfo &usersInfo) +{ + arg.beginStructure(); + arg >> usersInfo.userId + >> usersInfo.userName + >> usersInfo.userPath; + arg.endStructure(); + return arg; +} + + + diff --git a/src/lockwidget.h b/src/lockwidget.h new file mode 100644 index 0000000..c8352c3 --- /dev/null +++ b/src/lockwidget.h @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#ifndef LOCKWIDGET_H +#define LOCKWIDGET_H + +#ifndef QT_NO_KEYWORDS +#define QT_NO_KEYWORDS +#endif + +#include +#include +#include +#include "surewindow.h" +#include "xeventmonitor.h" + +namespace Ui { +class LockWidget; +} + + +struct userInfo { + int userId; + QString userName; + QDBusObjectPath userPath; +}; + +QDBusArgument &operator <<(QDBusArgument &arg, const userInfo &usersInfo); +const QDBusArgument &operator >>(const QDBusArgument &arg, userInfo &usersInfo); + +class VirtualKeyboard; +class PowerManager; +class AuthDialog; +class Users; +class UserItem; +class DisplayManager; +class QMenu; +class QScrollArea; +class KylinNM; + +class LockWidget : public QWidget +{ + Q_OBJECT + +public: + explicit LockWidget(QWidget *parent = 0); + ~LockWidget(); + void closeEvent(QCloseEvent *event); + void startAuth(); + void stopAuth(); + void setX11Focus(); + bool exitSubWidget(); +Q_SIGNALS: + void closed(); +// void capsLockChanged(); + +private: + void initUI(); + void initUserMenu(); + void setVirkeyboardPos(); + void updateNetIcon(int status); + bool getLoadStatus(const QString &name); + int getNetStatus(); + QPixmap PixmapToRound(const QPixmap &src, int radius); + void keyBdRelease(QString key); +private Q_SLOTS: + void onUserAdded(const UserItem &user); + void onUserDeleted(const UserItem &user); + void onUserMenuTrigged(QAction *action); + void showVirtualKeyboard(); + void showPowerManager(); + void showNetManager(); + void switchToGreeter(); + void switchToSureDialog(int type); + void hideSureDialog(); + void confirmClicked(); + void hideNetManager(); + void onGlobalKeyPress(const QString &key); + void onGlobalkeyRelease(const QString &key); +protected: + bool eventFilter(QObject *obj, QEvent *event); + void resizeEvent(QResizeEvent *event); + void keyReleaseEvent(QKeyEvent *e); + +private: + Ui::LockWidget *ui; + AuthDialog *authDialog; + VirtualKeyboard *vKeyboard = nullptr; + PowerManager *powermanager = nullptr; + SureWindow *sureWidget = nullptr; + QTimer *timer; + QMenu *usersMenu = nullptr; + QList list; + Users *users; + DisplayManager *displayManager; + int timeType; + QString dateType; + QScrollArea *scrollArea; + QWidget *scrollContents; + + KylinNM *m_kylinNM = nullptr; + QWidget *m_NetManagerWidget; + QStringList m_loginedUser; + bool isNetFinished = false; + int powermanagerType; + XEventMonitor *xEventMonitor; + int nowAt = -1; +}; + +#endif // LOCKWIDGET_H diff --git a/src/lockwidget.ui b/src/lockwidget.ui new file mode 100644 index 0000000..22caea6 --- /dev/null +++ b/src/lockwidget.ui @@ -0,0 +1,118 @@ + + + LockWidget + + + + 0 + 0 + 748 + 433 + + + + Form + + + + + 550 + 10 + 80 + 26 + + + + PointingHandCursor + + + Qt::NoFocus + + + + + + + + + 10 + 280 + 54 + 70 + + + + + + + Time + + + + + + + Date + + + + + + + + + 460 + 10 + 80 + 26 + + + + PointingHandCursor + + + Qt::NoFocus + + + + + + + + + 650 + 10 + 80 + 26 + + + + PointingHandCursor + + + + + + + + + 370 + 10 + 80 + 26 + + + + PointingHandCursor + + + Qt::NoFocus + + + + + + + + + diff --git a/src/logind.cpp b/src/logind.cpp new file mode 100644 index 0000000..34c00ed --- /dev/null +++ b/src/logind.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#include "logind.h" +#include +#include +#include +#include +#include + +const static QString login1Service = QStringLiteral("org.freedesktop.login1"); +const static QString login1Path = QStringLiteral("/org/freedesktop/login1"); +const static QString login1ManagerInterface = QStringLiteral("org.freedesktop.login1.Manager"); +const static QString login1SessionInterface = QStringLiteral("org.freedesktop.login1.Session"); + +LogindIntegration::LogindIntegration(QObject *parent) + : QObject(parent) +{ + QDBusInterface loginInterface(login1Service, + login1Path, + login1ManagerInterface, + QDBusConnection::systemBus()); + QDBusReply sessionPath = loginInterface.call("GetSessionByPID",(quint32) QCoreApplication::applicationPid()); + if(!sessionPath.isValid()){ + qWarning()<< "Get session error:" << sessionPath.error(); + } + else{ + + QString session = sessionPath.value().path(); + QDBusConnection::systemBus().connect(login1Service, + session, + login1SessionInterface, + QStringLiteral("Lock"), + this, + SIGNAL(requestLock())); + QDBusConnection::systemBus().connect(login1Service, + session, + login1SessionInterface, + QStringLiteral("Unlock"), + this, + SIGNAL(requestUnlock())); + } + return; +} + +LogindIntegration::~LogindIntegration() = default; + diff --git a/src/logind.h b/src/logind.h new file mode 100644 index 0000000..f1e27ed --- /dev/null +++ b/src/logind.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2020 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#ifndef LOGIND_H +#define LOGIND_H + +#include +#include + +class QDBusServiceWatcher; + +class LogindIntegration : public QObject +{ + Q_OBJECT +public: + explicit LogindIntegration(QObject *parent = nullptr); + ~LogindIntegration() override; + +Q_SIGNALS: + void requestLock(); + void requestUnlock(); + void connectedChanged(); + +private: + +}; + +#endif diff --git a/src/loginoptionswidget.cpp b/src/loginoptionswidget.cpp new file mode 100644 index 0000000..c35e09d --- /dev/null +++ b/src/loginoptionswidget.cpp @@ -0,0 +1,680 @@ +#include "loginoptionswidget.h" + +#include +#include + +#include +#include "giodbus.h" +#include "biometricdeviceinfo.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +LoginOptionsWidget::LoginOptionsWidget(BiometricProxy* proxy, int uid, QWidget *parent) + : QWidget(parent) + , m_biomericProxy(proxy) + , m_uid(uid) +{ + initUI(); + initConnections(); + m_mapDisableDev.clear(); +} + +LoginOptionsWidget::~LoginOptionsWidget() +{ + +} + +void LoginOptionsWidget::initUI() +{ + this->setFixedHeight(86); + // 初始化UI + m_layoutMain = new QVBoxLayout(); + m_layoutMain->setContentsMargins(0,0,0,0); + m_layoutMain->setSpacing(16); + m_layoutOptBtns = new QHBoxLayout(); + m_layoutOptBtns->setContentsMargins(0,0,0,0); + m_layoutOptBtns->setSpacing(16); + + m_labelOptTitle = new QLabel(); + m_labelOptTitle->setAlignment(Qt::AlignCenter); + m_labelOptTitle->setText(tr("Login Options")); + m_labelOptTitle->setFixedHeight(22); + m_layoutMain->addWidget(m_labelOptTitle); + + m_btnGroup = new QButtonGroup(this); + m_btnGroup->setExclusive(true); + + m_layoutOptBtns->setAlignment(Qt::AlignCenter); + m_layoutMain->addLayout(m_layoutOptBtns); + + m_layoutMain->addStretch(); + this->setLayout(m_layoutMain); +} + +void LoginOptionsWidget::initConnections() +{ + if(m_biomericProxy && m_biomericProxy->isValid()) + { + connect(m_biomericProxy, &BiometricProxy::StatusChanged, + this, &LoginOptionsWidget::onStatusChanged); + + connect(m_biomericProxy, &BiometricProxy::FrameWritten, + this, &LoginOptionsWidget::onFrameWritten); + connect(m_biomericProxy, &BiometricProxy::USBDeviceHotPlug, + this, &LoginOptionsWidget::onUSBDeviceHotPlug); + readDevicesInfo(); + } + connect(m_btnGroup, SIGNAL(buttonClicked(int)), this, SLOT(onOptionSelected(int))); +} + +bool LoginOptionsWidget::getCurLoginOpt(int& nLoginOptType, int& nDrvId) +{ + if (m_curDevInfo) { + nLoginOptType = convertDeviceType(m_curDevInfo->deviceType); + nDrvId = m_curDevInfo->id; + return true; + } + return false; +} + +unsigned LoginOptionsWidget::getLoginOptCount() +{ + return m_mapDevices.size(); +} + +DeviceInfoPtr LoginOptionsWidget::getFirstDevInfo() +{ + DeviceInfoPtr devInfo = nullptr; + int nDrvId = GetLastDevice(getpwuid(m_uid)->pw_name); + if (nDrvId >= 0) { + qDebug()<<"GetLastDevice:"<id == nDrvId) { + if (!isDeviceDisable(devinfo->id)) { + devInfo = devinfo; + break; + } + } + } + if (devInfo) { + break; + } + } + } + if (!devInfo) { + DeviceMap::iterator itDevInfo = m_mapDevices.begin(); + for (; itDevInfo != m_mapDevices.end(); itDevInfo++) { + for (auto devinfo : itDevInfo.value()) { + if (devinfo && !isDeviceDisable(devinfo->id)) { + devInfo = devinfo; + break; + } + } + if (devInfo) { + break; + } + } + } + return devInfo; +} + +void LoginOptionsWidget::addOptionButton(unsigned uLoginOptType, int nDrvId, QString strDrvName) +{ + if (m_mapOptBtns.contains(nDrvId)) { + return ; + } + QToolButton *newButton = new QToolButton(); + QVBoxLayout *layoutBtn = new QVBoxLayout(); + newButton->setLayout(layoutBtn); + QLabel *newLabel = new QLabel(); + layoutBtn->setAlignment(Qt::AlignCenter); + layoutBtn->addWidget(newLabel); + newButton->setCheckable(true); + newButton->setChecked(false); + newButton->setFocusPolicy(Qt::NoFocus); + if (nDrvId == -1) { + //newButton->setEnabled(false); + newButton->setChecked(true); + //屏蔽鼠标事件 + newButton->setAttribute(Qt::WA_TransparentForMouseEvents, true); + } else { + int nLength = m_btnGroup->buttons().length(); + m_btnGroup->addButton(newButton, nLength); + m_listDriveId.append(nDrvId); + } + QPixmap iconPixmap; + switch (uLoginOptType) { + case LOGINOPT_TYPE_PASSWORD: + iconPixmap = loadSvg(":/image/assets/ukui-loginopt-password.svg", "white", 16); + break; + case LOGINOPT_TYPE_FACE: + iconPixmap = loadSvg(":/image/assets/ukui-loginopt-face.svg", "white", 16); + break; + case LOGINOPT_TYPE_FINGERPRINT: + iconPixmap = loadSvg(":/image/assets/ukui-loginopt-finger.svg", "white", 16); + break; + case LOGINOPT_TYPE_IRIS: + iconPixmap = loadSvg(":/image/assets/ukui-loginopt-iris.svg", "white", 16); + break; + case LOGINOPT_TYPE_VOICEPRINT: + iconPixmap = loadSvg(":/image/assets/ukui-loginopt-voice.svg", "white", 16); + break; + case LOGINOPT_TYPE_FINGERVEIN: + iconPixmap = loadSvg(":/image/assets/ukui-loginopt-fingervein.svg", "white", 16); + break; + case LOGINOPT_TYPE_QRCODE: + iconPixmap = loadSvg(":/image/assets/ukui-loginopt-qrcode.svg", "white", 16); + break; + } + newLabel->setPixmap(iconPixmap); + newButton->setToolTip(strDrvName); + newButton->setStyleSheet("QToolTip{border-radius:4px;background-color:#FFFFFF;color:black;font-size:16px}"); + newButton->setFixedSize(48, 48); + if (isDeviceDisable(nDrvId)) { + newButton->setDisabled(true); + } else { + newButton->setDisabled(false); + } + + m_layoutOptBtns->addWidget(newButton); + m_mapOptBtns[nDrvId] = newButton; +} + +void LoginOptionsWidget::clearOptionButtons() +{ + QMap::iterator itMapBtn = m_mapOptBtns.begin(); + for ( ; itMapBtn != m_mapOptBtns.end(); itMapBtn++) { + if (itMapBtn.value()) { + m_btnGroup->removeButton(itMapBtn.value()); + m_layoutOptBtns->removeWidget(itMapBtn.value()); + itMapBtn.value()->deleteLater(); + } + } + m_listDriveId.clear(); + m_mapOptBtns.clear(); +} + +void LoginOptionsWidget::updateOptionButtons() +{ + clearOptionButtons(); + //addOptionButton(LOGINOPT_TYPE_PASSWORD, -1, tr("Password")); + DeviceMap::iterator itMapDev = m_mapDevices.begin(); + for ( ; itMapDev != m_mapDevices.end(); itMapDev++) { + for (DeviceInfoPtr devPtr : itMapDev.value()) { + if (devPtr) { + addOptionButton(itMapDev.key(), devPtr->id, DeviceType::getDeviceType_tr(devPtr->deviceType)); + } + } + } + qDebug()<<"m_mapOptBtns.size():"<hide(); + QMap::iterator itMapBtn = m_mapOptBtns.begin(); + for ( ; itMapBtn != m_mapOptBtns.end(); itMapBtn++) { + if (itMapBtn.value()) { + itMapBtn.value()->hide(); + } + } + } else { + m_labelOptTitle->show(); + QMap::iterator itMapBtn = m_mapOptBtns.begin(); + for ( ; itMapBtn != m_mapOptBtns.end(); itMapBtn++) { + if (itMapBtn.value()) { + itMapBtn.value()->show(); + } + } + } +} + +void LoginOptionsWidget::setUser(int uid) +{ + m_uid = uid; + readDevicesInfo(); +} + +void LoginOptionsWidget::readDevicesInfo() +{ + m_mapDevices.clear(); + bool isAuthEnable = GetAuthEnable(); + bool isQRCodeEnable = GetQRCodeEnable(); + DeviceList deviceList = m_biomericProxy->GetDevList(); + for(auto pDeviceInfo : deviceList) + { + qDebug()<<"BeginGetFeature------!"; + int nDevFeatureCount = m_biomericProxy->GetUserDevFeatureCount(m_uid,pDeviceInfo->id); + qDebug() << *pDeviceInfo << ",feautres:"< 0) { + if (!isAuthEnable && pDeviceInfo->deviceType <= DeviceType::VoicePrint) + continue; + if (!isQRCodeEnable && pDeviceInfo->deviceType == REMOTE_QRCODE_TYPE) + continue; + int nDevType = LOGINOPT_TYPE_OTHERS; + nDevType = convertDeviceType(pDeviceInfo->deviceType); + m_mapDevices[nDevType].push_back(pDeviceInfo); + } + } + updateOptionButtons(); +} + +void LoginOptionsWidget::startAuth(DeviceInfoPtr device, int uid) +{ + if(!m_biomericProxy) + { + qWarning() << "BiometricProxy doesn't exist."; + return; + } + if (!device) { + qWarning() << "Biometric Auth device invalid."; + return ; + } + + if(m_isInAuth) + { + qDebug() << "Identification is currently under way, stop it"; + stopAuth(); + } + qDebug()<<"deviceInfo:"<id; + this->m_curDevInfo = device; + this->m_uid = uid; + this->m_strUserName = getpwuid(uid)->pw_name; + this->m_isStopped = false; + this->m_curLoginOptType = convertDeviceType(this->m_curDevInfo->deviceType); + updateUIStatus(); + SetLastDevice(this->m_strUserName, this->m_curDevInfo->id); + startAuth_(); +} + +void LoginOptionsWidget::startAuth_() +{ + if (!m_curDevInfo) + return ; + qDebug().noquote() << QString("Identify:[drvid: %1, uid: %2]").arg(m_curDevInfo->id).arg(m_uid); + + m_isInAuth = true; + m_dupFD = -1; + + QDBusPendingCall call = m_biomericProxy->Identify(m_curDevInfo->id, m_uid); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, this); + connect(watcher, &QDBusPendingCallWatcher::finished, + this, &LoginOptionsWidget::onIdentifyComplete); +} + +void LoginOptionsWidget::stopAuth() +{ + m_isStopped = true; + if(!m_isInAuth || !m_curDevInfo) + { + return; + } + m_biomericProxy->StopOps(m_curDevInfo->id); + if(m_retrytimer&&m_retrytimer->isActive()){ + m_retrytimer->stop(); + delete m_retrytimer; + m_retrytimer = nullptr; + } + m_isInAuth = false; + Q_EMIT updateImage(QImage()); +} + +void LoginOptionsWidget::onIdentifyComplete(QDBusPendingCallWatcher *watcher) +{ + if(m_isStopped == true) + return ; + + QDBusPendingReply reply = *watcher; + if(reply.isError()) + { + qWarning() << "Identify error: " << reply.error().message(); + Q_EMIT authComplete(false, -1); + return; + } + int result = reply.argumentAt(0).toInt(); + int authUid = reply.argumentAt(1).toInt(); + qDebug() << result << authUid << m_uid; + + // 特征识别成功,而且用户id匹配 + if(result == DBUS_RESULT_SUCCESS && authUid == m_uid) { + qDebug() << "Identify success"; + Q_EMIT authComplete(true, 0); + } else if(result == DBUS_RESULT_NOTMATCH) { // 特征识别不匹配 + qDebug() << "Identify failed"; + Q_EMIT authComplete(false, 2); + } else if(result == DBUS_RESULT_ERROR) { //识别发生错误 + StatusReslut ret = m_biomericProxy->UpdateStatus(m_curDevInfo->id); + qDebug()<<"StatusReslut:"<id != drvid) || !m_isInAuth) { + return ; + } + if(m_dupFD == -1){ + m_dupFD = get_server_gvariant_stdout(drvid); + } + + if(m_dupFD <= 0) + return ; + + cv::Mat img; + lseek(m_dupFD, 0, SEEK_SET); + char base64_bufferData[1024*1024]; + int rc = read(m_dupFD, base64_bufferData, 1024*1024); + printf("rc = %d\n", rc); + + cv::Mat mat2(1, sizeof(base64_bufferData), CV_8U, base64_bufferData); + img = cv::imdecode(mat2, cv::IMREAD_COLOR); + if (!img.data) + return ; + cv::cvtColor(img,img,cv::COLOR_BGR2RGB); + + QImage srcQImage = QImage((uchar*)(img.data), img.cols, img.rows, QImage::Format_RGB888); + Q_EMIT updateImage(srcQImage); +} + +void LoginOptionsWidget::onStatusChanged(int drvid, int status) +{ + if(!m_isInAuth || !m_curDevInfo) + { + return; + } + if(drvid != m_curDevInfo->id) + { + return; + } + +// // 显示来自服务的提示信息 +// if(status == STATUS_NOTIFY) +// { +// QString notifyMsg = m_biomericProxy->GetNotifyMesg(drvid); +// Q_EMIT updateAuthMsg(notifyMsg); +// } +} + +void LoginOptionsWidget::setCurrentDevice(int drvid) +{ + DeviceInfoPtr pDeviceInfo = findDeviceById(drvid); + if(pDeviceInfo) + { + setCurrentDevice(pDeviceInfo); + } +} + +void LoginOptionsWidget::setCurrentDevice(const QString &deviceName) +{ + DeviceInfoPtr pDeviceInfo = findDeviceByName(deviceName); + if(pDeviceInfo) + { + setCurrentDevice(pDeviceInfo); + } +} + +void LoginOptionsWidget::setCurrentDevice(const DeviceInfoPtr &pDeviceInfo) +{ + this->m_curDevInfo = pDeviceInfo; +} + +DeviceInfoPtr LoginOptionsWidget::findDeviceById(int drvid) +{ + for(int type : m_mapDevices.keys()) + { + DeviceList &deviceList = m_mapDevices[type]; + auto iter = std::find_if(deviceList.begin(), deviceList.end(), + [&](DeviceInfoPtr ptr){ + return ptr->id == drvid; + }); + if(iter != deviceList.end()) + { + return *iter; + } + } + return DeviceInfoPtr(); +} + +DeviceInfoPtr LoginOptionsWidget::findDeviceByName(const QString &name) +{ + for(int type : m_mapDevices.keys()) + { + DeviceList &deviceList = m_mapDevices[type]; + auto iter = std::find_if(deviceList.begin(), deviceList.end(), + [&](DeviceInfoPtr ptr){ + return ptr->shortName == name; + }); + if(iter != deviceList.end()) + { + return *iter; + } + } + return DeviceInfoPtr(); +} + +void LoginOptionsWidget::onUSBDeviceHotPlug(int drvid, int action, int /*devNum*/) +{ + int savedDeviceId = (m_curDevInfo ? m_curDevInfo->id : -1); + int savedCount = 0; + for(int type : m_mapDevices.keys()) + savedCount += m_mapDevices.value(type).count(); + + switch(action) + { + case ACTION_ATTACHED: + { + //插入设备后,需要更新设备列表 + readDevicesInfo(); + break; + } + case ACTION_DETACHED: + { + DeviceInfoPtr pDeviceInfo = findDeviceById(drvid); + if(pDeviceInfo) + { + int nDevType = LOGINOPT_TYPE_PASSWORD; + nDevType = convertDeviceType(pDeviceInfo->deviceType); + if (savedDeviceId == drvid) { + if (m_isInAuth) { + Q_EMIT updateAuthMsg(tr("Identify device removed!")); + } + } + m_mapDevices[nDevType].removeOne(pDeviceInfo); + if(m_mapDevices[nDevType].isEmpty()) { + m_mapDevices.remove(nDevType); + } + } + break; + } + } + + int count = 0; + for(int type : m_mapDevices.keys()) + count += m_mapDevices.value(type).count(); + + //设备数量发生了变化 + if(count != savedCount) { + updateOptionButtons(); + Q_EMIT notifyOptionsChange(count); + } +} + +bool LoginOptionsWidget::getAuthDouble() +{ + QSettings settings("/etc/biometric-auth/ukui-biometric.conf", QSettings::IniFormat); + bool distribId = settings.value("DoubleAuth").toBool(); + return distribId; +} + +void LoginOptionsWidget::updateUIStatus() +{ + if (m_mapOptBtns.contains(-1)) { + QToolButton* btn = m_mapOptBtns[-1]; + if (btn) { + btn->setChecked(true); + } + } + if (m_curDevInfo) { + if (m_mapOptBtns.contains(m_curDevInfo->id)) { + QToolButton* btn = m_mapOptBtns[m_curDevInfo->id]; + if (btn) { + btn->setChecked(true); + } + } + } +} + +void LoginOptionsWidget::onOptionSelected(int nIndex) +{ + if (nIndex < 0) + return; + if (nIndex < m_listDriveId.size()) { + DeviceInfoPtr info = findDeviceById(m_listDriveId[nIndex]); + if (info && !isDeviceDisable(info->id)) { + Q_EMIT optionSelected(convertDeviceType(info->deviceType), info); + } + } +} + +int LoginOptionsWidget::convertDeviceType(int nDevType) +{ + int nLoginOptType = LOGINOPT_TYPE_OTHERS; + switch (nDevType) { + case BioT_FingerPrint: + nLoginOptType = LOGINOPT_TYPE_FINGERPRINT; + break; + case BioT_FingerVein: + nLoginOptType = LOGINOPT_TYPE_FINGERVEIN; + break; + case BioT_Iris: + nLoginOptType = LOGINOPT_TYPE_IRIS; + break; + case BioT_Face: + nLoginOptType = LOGINOPT_TYPE_FACE; + break; + case BioT_VoicePrint: + nLoginOptType = LOGINOPT_TYPE_VOICEPRINT; + break; + case UniT_Remote: + nLoginOptType = LOGINOPT_TYPE_QRCODE; + break; + default: + nLoginOptType = LOGINOPT_TYPE_OTHERS; + break; + } + return nLoginOptType; +} + +void LoginOptionsWidget::setDeviceDisable(int nDevId, bool bDisable) +{ + if (bDisable) { + m_mapDisableDev[m_uid][nDevId] = true; + QMap::iterator itMapBtn = m_mapOptBtns.begin(); + for ( ; itMapBtn != m_mapOptBtns.end(); itMapBtn++) { + if (itMapBtn.key() == nDevId && itMapBtn.value()) { + itMapBtn.value()->setDisabled(true); + break; + } + } + } else { + m_mapDisableDev[m_uid][nDevId] = false; + QMap::iterator itMapBtn = m_mapOptBtns.begin(); + for ( ; itMapBtn != m_mapOptBtns.end(); itMapBtn++) { + if (itMapBtn.key() == nDevId && itMapBtn.value()) { + itMapBtn.value()->setDisabled(false); + break; + } + } + } +} + +bool LoginOptionsWidget::isDeviceDisable(int nDevId) +{ + if (m_mapDisableDev[m_uid].contains(nDevId)) { + return m_mapDisableDev[m_uid][nDevId]; + } + return false; +} + +QPixmap LoginOptionsWidget::loadSvg(QString path, QString color, int size) +{ + int origSize = size; + const auto ratio = qApp->devicePixelRatio(); + if ( 2 == ratio) { + size += origSize; + } else if (3 == ratio) { + size += origSize; + } + QPixmap pixmap(size, size); + QSvgRenderer renderer(path); + pixmap.fill(Qt::transparent); + + QPainter painter; + painter.begin(&pixmap); + renderer.render(&painter); + painter.end(); + + pixmap.setDevicePixelRatio(ratio); + return drawSymbolicColoredPixmap(pixmap, color); +} + +QPixmap LoginOptionsWidget::drawSymbolicColoredPixmap(QPixmap &source, QString cgColor) +{ + QImage img = source.toImage(); + for (int x = 0; x < img.width(); x++) { + for (int y = 0; y < img.height(); y++) { + auto color = img.pixelColor(x, y); + if (color.alpha() > 0) { + if ( "white" == cgColor) { + color.setRed(255); + color.setGreen(255); + color.setBlue(255); + img.setPixelColor(x, y, color); + } else if( "black" == cgColor) { + color.setRed(0); + color.setGreen(0); + color.setBlue(0); + img.setPixelColor(x, y, color); + } else if ("gray"== cgColor) { + color.setRed(152); + color.setGreen(163); + color.setBlue(164); + img.setPixelColor(x, y, color); + } else if ("blue" == cgColor){ + color.setRed(61); + color.setGreen(107); + color.setBlue(229); + img.setPixelColor(x, y, color); + } else { + return source; + } + } + } + } + return QPixmap::fromImage(img); +} diff --git a/src/loginoptionswidget.h b/src/loginoptionswidget.h new file mode 100644 index 0000000..c53a0bc --- /dev/null +++ b/src/loginoptionswidget.h @@ -0,0 +1,120 @@ +#ifndef LOGINOPTIONSWIDGET_H +#define LOGINOPTIONSWIDGET_H + +#include +#include "biometricproxy.h" +#include "biometricdeviceinfo.h" + +class QLabel; +class QButtonGroup; +class QHBoxLayout; +class QVBoxLayout; +class QToolButton; +class QTimer; +class QPixmap; + +typedef enum { + BioT_FingerPrint, /** 指纹 **/ + BioT_FingerVein, /** 指静脉 **/ + BioT_Iris, /** 虹膜 **/ + BioT_Face, /** 人脸 **/ + BioT_VoicePrint, /** 声纹 **/ + + UniT_KCM, /** 安全管控 **/ + UniT_General_Ukey, /** 普通的Ukey **/ + UniT_Advanced_Ukey, /** 高阶的Ukey **/ + UniT_Remote, /** 远程账户 **/ +}BioType; + +class LoginOptionsWidget : public QWidget +{ + Q_OBJECT +public: + explicit LoginOptionsWidget(BiometricProxy* proxy, int uid, QWidget *parent = nullptr); + virtual ~LoginOptionsWidget(); + + bool getCurLoginOpt(int& nLoginOptType, int& nDrvId); + unsigned getLoginOptCount(); + DeviceInfoPtr getFirstDevInfo(); + int convertDeviceType(int nDevType); + + void setUser(int uid); + void setCurrentDevice(int drvid); + void setCurrentDevice(const QString &deviceName); + void setCurrentDevice(const DeviceInfoPtr &pDeviceInfo); + DeviceInfoPtr findDeviceById(int drvid); + DeviceInfoPtr findDeviceByName(const QString &name); + void setDeviceDisable(int nDevId, bool bDisable = true); + bool isDeviceDisable(int nDevId); + + /** + * @brief 进行生物识别认证 + * @param deviceInfo 使用的设备 + * @param uid 待认证的用户id + */ + void startAuth(DeviceInfoPtr device, int uid); + + /** + * @brief 终止生物识别认证 + */ + void stopAuth(); + + /** + * @brief 是否正在认证 + * @return + */ + bool isAuthenticating() { + return m_isInAuth; + } + +public Q_SLOTS: + void readDevicesInfo(); + void onIdentifyComplete(QDBusPendingCallWatcher *watcher); + void onStatusChanged(int drvid, int status); + void onFrameWritten(int drvid); + void onUSBDeviceHotPlug(int drvid, int action, int devNum); + void onOptionSelected(int nIndex); + +Q_SIGNALS: + void notifyOptionsChange(unsigned uOptionsCount); + void optionSelected(unsigned uLoginOptType, const DeviceInfoPtr &deviceInfo); + void updateImage(QImage img); + void authComplete(bool bResult, int nStatus); + void updateAuthMsg(QString strMsg); + +private: + void initUI(); + void initConnections(); + void addOptionButton(unsigned uLoginOptType, int nDrvId, QString strDrvName); + void clearOptionButtons(); + void updateOptionButtons(); + void startAuth_(); + bool getAuthDouble(); + void updateUIStatus(); + QPixmap loadSvg(QString path, QString color, int size); + QPixmap drawSymbolicColoredPixmap(QPixmap &source, QString cgColor); + +private: + BiometricProxy* m_biomericProxy = nullptr; + DeviceMap m_mapDevices; + unsigned m_curLoginOptType = LOGINOPT_TYPE_PASSWORD; + int m_uid; + QString m_strUserName; + DeviceInfoPtr m_curDevInfo = nullptr; // 当前选择的设备信息 + int m_dupFD = -1; // 透传的图像文件句柄 + bool m_isInAuth = false; // 是否正在验证 + bool m_isStopped = false; // 是否被强制终止 + QTimer *m_retrytimer = nullptr; // 重试定时器 + + // UI + QVBoxLayout *m_layoutMain = nullptr; + QHBoxLayout *m_layoutOptBtns = nullptr; + + QLabel *m_labelOptTitle = nullptr; + QButtonGroup *m_btnGroup = nullptr; + QList m_listDriveId; + QMap m_mapOptBtns; + QMap> m_mapDisableDev; +}; + +#endif // LOGINOPTIONSWIDGET_H diff --git a/src/monitorwatcher.cpp b/src/monitorwatcher.cpp new file mode 100644 index 0000000..60009ec --- /dev/null +++ b/src/monitorwatcher.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#include "monitorwatcher.h" +#include +#include +#include +#include + +#define DRM_DIR "/sys/class/drm/" + +MonitorWatcher::MonitorWatcher(QObject *parent) + : QThread(parent), + virtualSize(0, 0), + monitorCount(0), + firstDetect(true) +{ + +} + +MonitorWatcher::~MonitorWatcher() +{ + requestInterruption(); + terminate(); + wait(); +} + +void MonitorWatcher::run() +{ + QDir drmDir(DRM_DIR); + QStringList drms = drmDir.entryList(QDir::Dirs); + for(auto iter = drms.begin(); iter != drms.end(); ) { + if((*iter).indexOf("card") == 0 || iter->length() > 5) + iter++; + else + iter = drms.erase(iter); + } + + FILE * fp = popen("xrandr", "r"); + Q_UNUSED(fp) + + /* 每隔3秒遍历一次显卡接口的连接状态 */ + while(!isInterruptionRequested()) { + int width = 0; + int height = 0; + QMap tmpStatus; + for(const QString & drm : drms) { + QFile drmStatusFile(DRM_DIR + drm + "/status"); + if(drmStatusFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + QTextStream in(&drmStatusFile); + QString status = in.readLine(); + tmpStatus[drm] = status; + + if(status == "connected") { + QSize maxMode = getMonitorMaxSize(drm); + width += maxMode.width(); + height = height > maxMode.height() ? height : maxMode.height(); + } + } + } + if(drmStatus != tmpStatus) { + drmStatus = tmpStatus; + int count = 0; + for(auto &pair : tmpStatus) + if(pair == "connected") + count++; + qDebug() << "monitor count changed ---" << count; + if(monitorCount != count) { + monitorCount = count; + if(!firstDetect) { + firstDetect = false; + Q_EMIT monitorCountChanged(count); + } + } + FILE *fp = popen("xrandr", "r"); + Q_UNUSED(fp) + } + + virtualSize = QSize(width, height); + sleep(3); + } +} + +/** + * 获取显示器的最大分辨率 + */ +QSize MonitorWatcher::getMonitorMaxSize(const QString &drm) +{ + int width = 0, height = 0; + QFile drmModeFile(DRM_DIR + drm + "/modes"); + if(drmModeFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + QTextStream in2(&drmModeFile); + QString maxMode = in2.readLine(); + int xpos = maxMode.indexOf('x', 0); + width = maxMode.left(xpos).toInt(); + int ipos = maxMode.indexOf('i', 0); + if(ipos != -1) + height = maxMode.mid(xpos+1, ipos-xpos-1).toInt(); + else + height = maxMode.mid(xpos+1).toInt(); + } + return QSize(width, height); +} + +QSize MonitorWatcher::getVirtualSize() +{ + return virtualSize; +} + +int MonitorWatcher::getMonitorCount() +{ + return monitorCount; +} diff --git a/src/monitorwatcher.h b/src/monitorwatcher.h new file mode 100644 index 0000000..224aeac --- /dev/null +++ b/src/monitorwatcher.h @@ -0,0 +1,56 @@ +/* monitorwatcher.h + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301, USA. +**/ +#ifndef MONITORWATCHER_H +#define MONITORWATCHER_H + +#include +#include +#include + +/*! + * \brief The MonitorWatcher class + * 监控显示器的插拔 + */ +class MonitorWatcher : public QThread +{ + Q_OBJECT + +public: + MonitorWatcher(QObject *parent=nullptr); + ~MonitorWatcher(); + QSize getVirtualSize(); + int getMonitorCount(); + +Q_SIGNALS: + void monitorCountChanged(int newCount); + void virtualSizeChanged(const QSize &newVirtualSize); + +protected: + void run(); + +private: + QSize getMonitorMaxSize(const QString &drm); + + QMap drmStatus; + QSize virtualSize; + int monitorCount; + bool firstDetect; +}; + +#endif // MONITORWATCHER_H diff --git a/src/networkwatcher.cpp b/src/networkwatcher.cpp new file mode 100644 index 0000000..313eabb --- /dev/null +++ b/src/networkwatcher.cpp @@ -0,0 +1,93 @@ +#include +#include +#include +#include + +#include "networkwatcher.h" + +const int port = 80; + +NetWorkWatcher::NetWorkWatcher(QObject *parent) +{ + QDBusConnection::systemBus().connect(QString("org.freedesktop.NetworkManager"), + QString("/org/freedesktop/NetworkManager"), + QString("org.freedesktop.NetworkManager"), + QString("StateChanged"), this, SLOT(onNetworkStateChanged(uint))); + + network = new QDBusInterface("org.freedesktop.NetworkManager", + "/org/freedesktop/NetworkManager", + "org.freedesktop.NetworkManager", + QDBusConnection::systemBus()); +} + +void NetWorkWatcher::onNetworkStateChanged(uint state) +{ + qDebug() << "NetWork state already changed to" << state; + emit NetworkStateChanged(state); +} + +//网络是否可以连接 +void NetWorkWatcher::checkOnline() +{ + QHostInfo::lookupHost("www.baidu.com", this, SLOT(getIp(QHostInfo))); //异步非阻塞 + /*QHostInfo info = QHostInfo::fromName("www.baidu.com"); //阻塞 + if(info.error() != QHostInfo::NoError) + return false; + QString ipAddress = info.addresses().first().toString(); + + QTcpSocket tcpClient; + tcpClient.abort(); + tcpClient.connectToHost(ipAddress, port); + //150毫秒没有连接上则判断不在线 + return tcpClient.waitForConnected(150);*/ +} + +//是否连接网络 +bool NetWorkWatcher::isConnect() +{ + QDBusMessage message = network->call("state"); + if(QDBusMessage::ErrorMessage == message.type()) + { + qDebug() << "error: NetWorkManager DBus Connect Failed!"; + return false; + } + + QList argvs = message.arguments(); + uint state = argvs.at(0).value(); + + qDebug() << "NetWorkManager state:" << state; + + if(state == NM_STATE_CONNECTED_SITE || state == NM_STATE_CONNECTED_LOCAL || state == NM_STATE_CONNECTED_GLOBAL) + return true; + + return false; +} + +void NetWorkWatcher::getIp(QHostInfo info) +{ + if(!isConnect()) + { + emit NetworkStateChanged(NM_STATE_DISCONNECTED); + return; + } + + if(info.error() != QHostInfo::NoError) + { + emit NetworkStateChanged(NM_STATE_CONNECTED_LIMIT); + return; + } + + QString ipAddress = info.addresses().first().toString(); + + QTcpSocket tcpClient; + tcpClient.abort(); + tcpClient.connectToHost(ipAddress, port); + //150毫秒没有连接上则判断不在线 + if(!tcpClient.waitForConnected(150)) + { + emit NetworkStateChanged(NM_STATE_CONNECTED_LIMIT); + return; + } + emit NetworkStateChanged(NM_STATE_CONNECTED_GLOBAL); +} + diff --git a/src/networkwatcher.h b/src/networkwatcher.h new file mode 100644 index 0000000..71d3d28 --- /dev/null +++ b/src/networkwatcher.h @@ -0,0 +1,42 @@ +#ifndef NETWORKWATCHER_H +#define NETWORKWATCHER_H +#include +#include +#include + +enum NMState{ + NM_STATE_UNKNOWN = 0, + NM_STATE_ASLEEP = 10, + NM_STATE_DISCONNECTED = 20, + NM_STATE_DISCONNECTING = 30, + NM_STATE_CONNECTING = 40, + NM_STATE_CONNECTED_LOCAL = 50, + NM_STATE_CONNECTED_SITE = 60, + NM_STATE_CONNECTED_GLOBAL = 70, + NM_STATE_CONNECTED_LIMIT = 80 +}; + +class NetWorkWatcher : public QObject +{ + Q_OBJECT + +public: + NetWorkWatcher(QObject *parent=nullptr); + +Q_SIGNALS: + void NetworkStateChanged(uint state); + +private Q_SLOTS: + void onNetworkStateChanged(uint state); + void getIp(QHostInfo info); + +public: + void checkOnline(); + bool isConnect(); + +private: + QDBusInterface *network; + +}; + +#endif diff --git a/src/org.ukui.ScreenSaver.xml b/src/org.ukui.ScreenSaver.xml new file mode 100644 index 0000000..482c477 --- /dev/null +++ b/src/org.ukui.ScreenSaver.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/pam-tally.c b/src/pam-tally.c new file mode 100644 index 0000000..d32f578 --- /dev/null +++ b/src/pam-tally.c @@ -0,0 +1,478 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + **/ + +#include "pam-tally.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +char shm_tally_real[128]; + +#define FILE_MODE (S_IRUSR | S_IWUSR) + +#define CONFIG_FILE "/usr/share/lightdm/lightdm.conf.d/96-kylin-setting.conf" + +struct tallylog { + char fail_line[52]; /* rhost or tty of last failure */ + uint16_t reserved; /* reserved for future use */ + uint16_t fail_cnt; /* failures since last success */ + uint64_t fail_time; /* time of last failure */ +}; + + + static +int get_is_open_other_authentication() +{ + char buf[128]; + FILE *config_file; + + if( (config_file = fopen(CONFIG_FILE, "r")) == NULL) + { + //gs_debug("open %s failed", CONFIG_FILE); + return 0; + } + + int open_other_authentication = 0; + while(fgets(buf, sizeof(buf), config_file)) { + if(strlen(buf) == 0 || buf[0] == '#') + { + memset(buf, sizeof(buf), 0); + continue; + } + if(buf[strlen(buf)-1] == '\n') + buf[strlen(buf)-1] = '\0'; + + char *p = strchr(buf, '='); + if(!p) + continue; + *p = '\0'; + + size_t len = strlen(buf); + if(len == 0) + continue; + //去掉=之前的空格 + while(len--) + if(buf[len] == ' ' || buf[len] == '\t') + buf[len] = '\0'; + if(strcmp(buf, "open-other-authentication") != 0) + continue; + + p++; + len = strlen(p); + if(len == 0) + break; + //去掉等号之后的空格 + while(*p == ' ' || *p == '\t') + { + p++; + len--; + } + //去掉尾部空格 + while(len--) + if(*(p+len) == ' ' || *(p+len) == '\t') + *(p+len) = '\0'; + + if(*p == '0') + break; + if(*p == '1') + { + open_other_authentication = 1; + break; + } + } + fclose(config_file); + return open_other_authentication; +} + + static +int get_pam_tally(int *deny, int *unlock_time , int *root_unlock_time) +{ + char buf[128]; + FILE *auth_file; + + if( (auth_file = fopen("/etc/pam.d/common-auth", "r")) == NULL) + return -1; + + while(fgets(buf, sizeof(buf), auth_file)) { + if(strlen(buf) == 0 || buf[0] == '#') + continue; + if(!strstr(buf, "deny")) + continue; + + char *ptr = strtok(buf, " \t"); + while(ptr) { + if(strncmp(ptr, "deny=", 5)==0){ + sscanf(ptr, "deny=%d", deny); + // fprintf(stderr, "-------------------- deny=%d\n", *deny); + } + if(strncmp(ptr, "unlock_time=", 12)==0){ + sscanf(ptr, "unlock_time=%d", unlock_time); + // fprintf(stderr, "-------------------- unlock_time=%d\n", *unlock_time); + } + if(strncmp(ptr, "root_unlock_time=", 17)==0){ + sscanf(ptr, "root_unlock_time=%d", root_unlock_time); + // fprintf(stderr, "-------------------- root_unlock_time=%d\n", *root_unlock_time); + } + ptr = strtok(NULL, " \t"); + } + return 1; + } + return 0; +} + + static +void set_shm_tally_real() +{ + sprintf(shm_tally_real, "%s_%d", SHM_TALLY, getuid()); +} + +int pam_tally_init() +{ + int fd; + int deny = 0, unlock_time = 0 , root_unlock_time = 0; + pam_tally *tally_ptr; + + set_shm_tally_real(); + + printf("shm path =========== : %s\n", shm_tally_real); + + shm_unlink(shm_tally_real); + + if(get_is_open_other_authentication()) + { + return 0; + } + + if(!get_pam_tally(&deny, &unlock_time,&root_unlock_time)) + { + return 0; + } + + if(deny <= 0) + deny = 0; + if(unlock_time <= 0) + unlock_time = 0; + + if( (fd = shm_open(shm_tally_real, O_RDWR | O_CREAT, FILE_MODE)) == -1) + { + printf("shm_open error: %s\n", strerror(errno)); + return -1; + } + + ftruncate(fd, sizeof(pam_tally)); + + if( (tally_ptr = mmap(NULL, sizeof(pam_tally), + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) + { + close(fd); + return -1; + } + close(fd); + tally_ptr->deny = deny; + tally_ptr->unlock_time = unlock_time; + tally_ptr->failed = 0; + tally_ptr->lock_start_time = 0; + tally_ptr->root_unlock_time = root_unlock_time; + + return 1; +} + + static +pam_tally* pam_tally_memory() +{ + int fd; + pam_tally *tally_ptr; + + set_shm_tally_real(); + + if( (fd = shm_open(shm_tally_real, O_RDWR, FILE_MODE)) == -1) + { + return NULL; + } + + if( (tally_ptr = mmap(NULL, sizeof(pam_tally), + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) + { + close(fd); + return NULL; + } + close(fd); + return tally_ptr; +} + +int pam_tally_is_enbled() +{ + int fd; + + set_shm_tally_real(); + + if( (fd = shm_open(shm_tally_real, O_RDONLY, FILE_MODE)) == -1) + { + printf("shm_open error: %s\n", strerror(errno)); + close(fd); + return 0; + } + + pam_tally *tally_ptr; + + if((tally_ptr = pam_tally_memory()) == NULL){ + close(fd); + return -1; + } + + if(tally_ptr->deny == 0 || tally_ptr->unlock_time == 0){ + close(fd); + return 0; + } + close(fd); + + return 1; +} + +int pam_tally_add_failed() +{ + pam_tally *tally_ptr; + + if((tally_ptr = pam_tally_memory()) == NULL) + return -1; + + tally_ptr->failed++; + + //如果失败次数达到上限,开始计时 + if(tally_ptr->failed >= tally_ptr->deny) + tally_ptr->lock_start_time = time(NULL); + + return 0; +} + +int pam_tally_clear_failed() +{ + pam_tally *tally_ptr; + + if((tally_ptr = pam_tally_memory()) == NULL) + return -1; + + tally_ptr->failed = 0; + tally_ptr->lock_start_time = 0; + return 0; +} + +int pam_tally_failure_is_out() +{ + pam_tally *tally_ptr; + + if((tally_ptr = pam_tally_memory()) == NULL) + return -1; + + return (tally_ptr->failed >= tally_ptr->deny ? 1 : 0); +} + +int pam_tally_deny() +{ + pam_tally *tally_ptr; + + if((tally_ptr = pam_tally_memory()) == NULL) + return -1; + + return tally_ptr->deny; +} + +int pam_tally_failed_count() +{ + pam_tally *tally_ptr; + + if((tally_ptr = pam_tally_memory()) == NULL) + return -1; + + return tally_ptr->failed; +} + +int pam_tally_unlock_time() +{ + pam_tally *tally_ptr; + + if((tally_ptr = pam_tally_memory()) == NULL) + return -1; + + printf("########################### unlock time = %d\n", tally_ptr->unlock_time); + return tally_ptr->unlock_time; +} + +int pam_tally_is_canUnlock() +{ + pam_tally *tally_ptr; + + if((tally_ptr = pam_tally_memory()) == NULL) + return -1; + + if(tally_ptr->failed >= tally_ptr->deny && + time(NULL) - tally_ptr->lock_start_time < tally_ptr->unlock_time) + return 0; + return 1; +} + + int +pam_modutil_read(int fd, char *buffer, int count) +{ + int block, offset = 0; + + while (count > 0) { + block = read(fd, &buffer[offset], count); + + if (block < 0) { + if (errno == EINTR) continue; + return block; + } + if (block == 0) return offset; + + offset += block; + count -= block; + } + + return offset; +} + +void get_tally(uid_t uid, int *tfile, struct tallylog *tally) +{ + char filename[50]={0}; + sprintf(filename,"%s","/tmp/.tallylog"); + + void *void_tally = tally; + if ((*tfile = open(filename, O_RDONLY)) == -1){ + fprintf(stderr, "open tallylog failed \n"); + return ; + } + + if (lseek(*tfile, (off_t)uid*(off_t)sizeof(*tally), SEEK_SET) == (off_t)-1) { + fprintf(stderr, "lseek tallylog failed \n"); + close(*tfile); + return ; + } + + if (pam_modutil_read(*tfile, void_tally, sizeof(*tally)) != sizeof(*tally)) { + fprintf(stderr, "read tallylog failed \n"); + memset(tally, 0, sizeof(*tally)); + } + + close(*tfile); + tally->fail_line[sizeof(tally->fail_line)-1] = '\0'; +} + +int pam_tally_unlock_time_left(int uid, int *fail_cnt,int *left_time, int *deny, int *fail_time, int *unlock_time1) +{ + pam_tally *tally_ptr; + + if((tally_ptr = pam_tally_memory()) == NULL) + return 0; + + int unlock_time = tally_ptr->unlock_time; + *deny = tally_ptr->deny; + if(unlock_time == 0) + return 0; + + int tfile = -1; + struct tallylog tally; + tally.reserved = 0; + tally.fail_cnt = 0; + tally.fail_time = 0; + + get_tally(uid,&tfile,&tally); + + if(tally.fail_cnt<*deny) + return 0; + + //连续输错,时间累加 + if (tally.fail_cnt >= (*deny)*2 && tally.fail_cnt < (*deny)*3){ + unlock_time = unlock_time*2; + } + else if(tally.fail_cnt >= (*deny)*3 && tally.fail_cnt < (*deny)*4){ + unlock_time = unlock_time*8; + } + else if (tally.fail_cnt >= (*deny)*4){ + *fail_cnt = 0xFFFF;//永久锁定 + return 0; + } + + + int unlock_time_left = unlock_time - ((long)time(NULL) - tally.fail_time); + + *fail_time = tally.fail_time; + *unlock_time1 = unlock_time; + + *left_time = unlock_time_left > 0 ? unlock_time_left : 0; + *fail_cnt = tally.fail_cnt; + + return *left_time; +} + +int pam_tally_root_unlock_time_left(int *fail_cnt,int *left_time, int *deny1, int *fail_time, int *unlock_time1) +{ + + pam_tally *tally_ptr; + if((tally_ptr = pam_tally_memory()) == NULL){ + return 0; + } + int root_unlock_time = tally_ptr->root_unlock_time; + int deny = tally_ptr->deny; + if(root_unlock_time == 0) + return 0; + + int tfile = -1; + uid_t uid = getuid(); + struct tallylog tally; + tally.reserved = 0; + tally.fail_cnt = 0; + tally.fail_time = 0; + + get_tally(uid,&tfile,&tally); + if(tally.fail_cnt= (deny)*2 && tally.fail_cnt < (deny)*3){ + root_unlock_time = root_unlock_time*2; + } + else if(tally.fail_cnt >= (deny)*3 && tally.fail_cnt < (deny)*4){ + root_unlock_time = root_unlock_time*8; + } + else if (tally.fail_cnt >= (deny)*4){ + *fail_cnt = 0xFFFF;//永久锁定 + return 0; + } + + int root_unlock_time_left = root_unlock_time - ((long)time(NULL) - tally.fail_time); + + *fail_time = tally.fail_time; + *unlock_time1 = root_unlock_time; + + *left_time = root_unlock_time_left > 0 ? root_unlock_time_left : 0; + + *fail_cnt = tally.fail_cnt; + + return *left_time; +} + + diff --git a/src/pam-tally.cpp b/src/pam-tally.cpp new file mode 100644 index 0000000..ba82fec --- /dev/null +++ b/src/pam-tally.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301, USA. +**/ +#include "pam-tally.h" +#include +#include +#include +#include +#include +#define PAM_CONFIG_FILE "/etc/pam.d/common-auth" + +PamTally* PamTally::instance_ = nullptr; + +PamTally::PamTally(QObject *parent) + : QObject(parent) +{ + parsePamConfig(); +} + +PamTally* PamTally::instance(QObject *parent) +{ + if(instance_ == nullptr) + instance_ = new PamTally(parent); + return instance_; +} + +int PamTally::parsePamConfig() +{ + deny = 0; + unlock_time = 0; + root_unlock_time = 0; + + QFile file(PAM_CONFIG_FILE); + if(!file.open(QIODevice::ReadOnly|QIODevice::Text)){ + qDebug()<<"open /etc/pam.d/common-auth failed"; + return -1; + } + + QStringList list; + list.clear(); + + while(!file.atEnd()){ + QString line = (QString)file.readLine(); + line = line.trimmed(); + + if(line.startsWith("#") || !line.contains("unix",Qt::CaseSensitive)) + continue; + + list << line; + } + file.close(); + + if(list.size() > 0) + { + QString line = list.at(0); + QStringList strs = line.split(" "); + foreach (QString str, strs) { + if(str.contains("deny",Qt::CaseSensitive)) + { + deny = str.split("=").at(1).toUInt(); + } + if(str.contains("unlock_time",Qt::CaseSensitive)) + { + unlock_time = str.split("=").at(1).toULongLong(); + } + if(str.contains("root_unlock_time",Qt::CaseSensitive)) + { + root_unlock_time = str.split("=").at(1).toULongLong(); + } + } + } + qDebug()<<"deny = "<. + * +**/ +#include "pam.h" + +extern "C" { + #include + #include + #include +} + +#define TO_AUTHCHILD_RD_SHIFT 16 +#define TO_PARENT_WR_SHIFT 0 +#define PIPE_FD_MASK ((1 << TO_AUTHCHILD_RD_SHIFT) - 1) + +void authenticate(int toParent[2], int toAuthChild[2]) +{ + pam_handle_t *pamh = NULL; + char *username; + int retval; + int auth_status; + struct pam_conv conv; + unsigned long fd = 0; + fd = fd | toAuthChild[0] << TO_AUTHCHILD_RD_SHIFT + | toParent[1] << TO_PARENT_WR_SHIFT; + /* authentication child process */ + username=getenv("USER"); + conv.conv = pam_conversation; + conv.appdata_ptr = (void *)fd; + retval = pam_start("ukui-screensaver-qt", username, &conv, &pamh); + if(retval == PAM_SUCCESS) + qDebug("PAM started successfully."); + else + qDebug("PAM started unsuccessfully."); + qDebug("Invoke pam authentication."); + auth_status = pam_authenticate(pamh, 0); + qDebug("Complete pam authentication."); + if(pam_end(pamh, retval) != PAM_SUCCESS){ + qDebug("Failed to terminate PAM."); + _exit(1); + } + qDebug("PAM ended successfully."); + kill(getppid(), SIGUSR1); + char buffer[16]; + sprintf(buffer, "%d", auth_status); + PIPE_OPS_SAFE( + write(toParent[1], buffer, strlen(buffer) + 1) + ); + qDebug("Auth status has been written to pipe."); + ::close(toParent[1]); + ::close(toAuthChild[0]); + qDebug("Authenticate child process now exits."); + _exit(0); +} + +#define MAX_PASSWORD_LENGTH 1024 +int pam_conversation(int num_msg, const struct pam_message **msg, + struct pam_response **resp, void *appdata_ptr) +{ + unsigned long fd; + int read_from_parent; + int write_to_parent; + int count; + char *password; + struct pam_response *tmp_save; + + qDebug("Into pam_conversation"); + + fd = (unsigned long)appdata_ptr; + read_from_parent = (fd >> TO_AUTHCHILD_RD_SHIFT) & PIPE_FD_MASK; + write_to_parent = (fd >> TO_PARENT_WR_SHIFT) & PIPE_FD_MASK; + count = num_msg; + password = (char *)malloc(MAX_PASSWORD_LENGTH); + *resp = (struct pam_response *)malloc(num_msg * + sizeof(struct pam_response)); + tmp_save = (struct pam_response *)(*resp); + memset(*resp, 0, num_msg * sizeof(struct pam_response)); + qDebug("Resolve PAM messages."); + while(count-- >= 1){ + struct pam_message_object pam_msg_obj; + pam_msg_obj.msg_style = (*msg)->msg_style; + strncpy(pam_msg_obj.msg, (*msg)->msg, MAX_MSG_LENGTH); + PIPE_OPS_SAFE( + write(write_to_parent, &pam_msg_obj, sizeof(pam_msg_obj)); + ); + qDebug("PAM message has been written to pipe."); + kill(getppid(), SIGUSR1); + + if ((*msg)->msg_style == PAM_PROMPT_ECHO_OFF + || (*msg)->msg_style == PAM_PROMPT_ECHO_ON){ + int n; + PIPE_OPS_SAFE( + n = read(read_from_parent, password, MAX_PASSWORD_LENGTH); + ); + qDebug("%d bytes response received from pipe.", n); + (*resp)->resp = password; + (*resp)->resp_retcode = 0; + } else { + ; + } + if(count != 0){ + msg++; + (*resp)++; + } + } + + (*resp) = tmp_save; + + qDebug("Out pam_conversation."); + return PAM_SUCCESS; +} + diff --git a/src/pam.h b/src/pam.h new file mode 100644 index 0000000..317098d --- /dev/null +++ b/src/pam.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#ifndef PAM_H +#define PAM_H + +#include +#include +#include + +void authenticate(int toParent[2], int toAuthChild[2]); + +int pam_conversation(int num_msg, const struct pam_message **msg, + struct pam_response **resp, void *appdata_ptr); + +#define MAX_MSG_LENGTH 1024 +struct pam_message_object { + int msg_style; + char msg[MAX_MSG_LENGTH + 1]; +} __attribute__((packed)); + +#define PIPE_OPS_SAFE(statement) do { \ + int return_value = statement; \ + if (return_value == -1)\ + qWarning("PIPE write/read error: %s", strerror(errno)); \ +} while (0) + +#endif // PAM_H diff --git a/src/permissioncheck.cpp b/src/permissioncheck.cpp new file mode 100644 index 0000000..82ee79b --- /dev/null +++ b/src/permissioncheck.cpp @@ -0,0 +1,401 @@ +#include "permissioncheck.h" +#include +#include +#include +#include +#include "common.h" + +#include "wechatauthdialog.h" + +VerificationWidget::VerificationWidget(QWidget *parent) : QWidget(parent) +{ + qDebug() << "PermissionCheck"; + resize(1000,355); + QHBoxLayout *mainLayout = new QHBoxLayout(this); + setLayout(mainLayout); + + WeChatAuthDialog *m_weChatWidget; + PhoneAuthWidget *m_phoneAuthWidget; + + m_weChatWidget = new WeChatAuthDialog(1,this); + mainLayout->addWidget(m_weChatWidget, 1, Qt::AlignLeft); + + mainLayout->addSpacing(400); + + m_phoneAuthWidget = new PhoneAuthWidget(this); + mainLayout->addWidget(m_phoneAuthWidget, 1, Qt::AlignRight); + + connect(m_phoneAuthWidget, &PhoneAuthWidget::pageMessage, this, [=](SwitchPage s, QList list){ + Q_EMIT pageMessage(s, list); + }); +} + +void VerificationWidget::paintEvent(QPaintEvent *) +{ + int x1 = width() / 2; + int y1 = height() / 4; + int x2 = x1; + int y2 = height() / 3 * 2 + y1; + QLinearGradient linearGra(QPoint(x1, y1), QPoint(x2, y2)); + linearGra.setColorAt(0, QColor(238, 238, 238, 0)); + linearGra.setColorAt(0.5, QColor(255, 255, 255, 138)); + linearGra.setColorAt(1, QColor(216, 216, 216, 0)); + + QPainter painter(this); + QBrush brush(linearGra); + painter.setPen(Qt::transparent); + painter.setBrush(brush); + painter.drawRoundedRect(QRect(x1, y1, 4, height() / 3 * 2), 16, 16); +} + +InputInfos::InputInfos(QWidget *parent): + QWidget(parent) +{ + initUI(); + initConnect(); + setQSS(); +} + +void InputInfos::initUI() +{ + setFixedWidth(316); + QGridLayout *mainLayout = new QGridLayout(this); + setLayout(mainLayout); + mainLayout->setColumnStretch(1, 0); + mainLayout->setColumnStretch(2, 1); + mainLayout->setColumnStretch(3, 0); + + m_pPhoneIconLB = new QLabel(this); + m_pPhoneIconLB->setPixmap(QPixmap(":/images/icon-phone.png")); + mainLayout->addWidget(m_pPhoneIconLB, 0, 1, 1, 1, Qt::AlignLeft); + + m_pPhoneLE = new QLineEdit(this); + m_pPhoneLE->setPlaceholderText("手机号"); + m_pPhoneLE->setProperty("class", "InputLine"); + m_pPhoneLE->setValidator(0); + mainLayout->addWidget(m_pPhoneLE, 0, 2, 1, 2, Qt::AlignLeft); + + m_pVerCodeIconLB = new QLabel(this); + m_pVerCodeIconLB->setPixmap(QPixmap(":/images/icon-sms.png")); + mainLayout->addWidget(m_pVerCodeIconLB, 1, 1, 1, 1, Qt::AlignLeft); + + m_pVerCodeLE = new QLineEdit(this); + m_pVerCodeLE->setPlaceholderText("短信验证码"); + m_pVerCodeLE->setProperty("class", "InputLine"); + m_pVerCodeLE->setValidator(0); + mainLayout->addWidget(m_pVerCodeLE, 1, 2, 1, 1, Qt::AlignLeft); + + m_pGetVerCodeBT = new QPushButton(this); + m_pGetVerCodeBT->setText("获取验证码"); + m_pGetVerCodeBT->setEnabled(false); + m_pGetVerCodeBT->setFocusPolicy(Qt::NoFocus); + m_pGetVerCodeBT->setProperty("class", "GetCodeBT"); + m_pGetVerCodeBT->setCursor(QCursor(Qt::PointingHandCursor)); + mainLayout->addWidget(m_pGetVerCodeBT, 1, 3, 1, 1, Qt::AlignLeft); + + m_pNextGetVerCodeQTimer = new QTimer(this); + + m_curInputState = InputState::InputWaiting; +} + +void InputInfos::initConnect() +{ + connect(m_pPhoneLE, &QLineEdit::textChanged, this, &InputInfos::onPhoneTextChanged); + + connect(m_pVerCodeLE, &QLineEdit::textChanged, this, &InputInfos::onVerCodeTextChanged); + + connect(m_pNextGetVerCodeQTimer, &QTimer::timeout, this, &InputInfos::onNextGetVerCodeTimer); + + connect(m_pGetVerCodeBT, &QPushButton::clicked, this, &InputInfos::onGetVerCode); +} + +void InputInfos::setQSS() +{ + QString style_sheet = ".InputLine{" + "padding:10px 2px 10px 2px;" + "}" + ".GetCodeBT{" + "background:rgba(0,0,0,0);" + "color:rgba(38,38,38,115);" + "font-size:16px;" + "}"; + setStyleSheet(style_sheet); + adjustSize(); +} + +void InputInfos::paintEvent(QPaintEvent *) +{ + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + painter.setPen(Qt::white); + painter.setBrush(Qt::white); + painter.drawRoundedRect(rect(), 16, 16); + painter.setPen(QColor(226, 226, 226)); + painter.drawLine(QLineF(0, m_pPhoneIconLB->y() + m_pPhoneIconLB->height() + 1, + width(), m_pPhoneIconLB->y() + m_pPhoneIconLB->height() + 1)); +} + +void InputInfos::onPhoneTextChanged(const QString &text) +{ + if(text.length() == 11){ + m_pGetVerCodeBT->setEnabled(true); + m_pGetVerCodeBT->setFocusPolicy(Qt::NoFocus); + m_pGetVerCodeBT->setStyleSheet("color:#2FB3E8;"); + + if(m_pVerCodeLE->text().length() == 6){ + Q_EMIT InputStateChanged(InputState::InputFinish); + m_curInputState = InputState::InputFinish; + } + } + else + { + m_pGetVerCodeBT->setEnabled(false); + m_pGetVerCodeBT->setStyleSheet("color:rgba(38, 38, 38, 115);"); + if(InputState::InputFinish == m_curInputState) + { + m_curInputState = InputState::InputWaiting; + Q_EMIT InputStateChanged(InputState::InputWaiting); + } + } +} + +void InputInfos::onVerCodeTextChanged(const QString &text) +{ + if(text.length() == 6 && m_pPhoneLE->text().length() == 11) + { + Q_EMIT InputStateChanged(InputState::InputFinish); + m_curInputState = InputState::InputFinish; + } + else if(m_curInputState == InputState::InputFinish) + { + Q_EMIT InputStateChanged(InputState::InputWaiting); + m_curInputState = InputState::InputWaiting; + } +} + +void InputInfos::clearInfos() +{ + m_pPhoneLE->clear(); + m_pVerCodeLE->clear(); + if(m_curInputState == InputState::InputFinish) + { + m_curInputState = InputState::InputWaiting; + Q_EMIT InputStateChanged(m_curInputState); + } +} + +QString InputInfos::getVerificationCode() +{ + return m_pVerCodeLE->text(); +} + +QString InputInfos::getPhoneNumer() +{ + return m_pPhoneLE->text(); +} + +void InputInfos::onGetVerCode() +{ + m_pGetVerCodeBT->setEnabled(false); + m_pGetVerCodeBT->setStyleSheet("color:rgba(38, 38, 38, 115);"); + m_nextGetVerCodeTime = 60; + m_pGetVerCodeBT->setText("重新获取(60s)"); + m_pNextGetVerCodeQTimer->start(1000); +} + + +void InputInfos::onNextGetVerCodeTimer() +{ + --m_nextGetVerCodeTime; + m_pGetVerCodeBT->setText("重新获取(" + QString::number(m_nextGetVerCodeTime) + "s)"); + if(m_nextGetVerCodeTime == 0) + { + m_pNextGetVerCodeQTimer->stop(); + m_pGetVerCodeBT->setEnabled(true); + m_pGetVerCodeBT->setFocusPolicy(Qt::NoFocus); + m_pGetVerCodeBT->setText("获取验证码"); + m_pGetVerCodeBT->setStyleSheet("color:#2FB3E8;"); + return; + } + +} + +PhoneAuthWidget::PhoneAuthWidget(QWidget *parent) +{ + initUI(); + initConnect(); + setQSS(); +} + +void PhoneAuthWidget::initUI() +{ + QVBoxLayout *phoLayout = new QVBoxLayout(this); + phoLayout->setMargin(0); + + m_pPhoTitleLB = new QLabel(this); + m_pPhoTitleLB->setText(tr("Verification by phoneNum")); + m_pPhoTitleLB->setProperty("class", "titleLB"); + phoLayout->addWidget(m_pPhoTitleLB, 0, Qt::AlignHCenter); + + m_pPhoPromptMsgLB = new QLabel(this); + m_pPhoPromptMsgLB->setText(tr("「 Use bound Phone number to verification 」")); + m_pPhoPromptMsgLB->setProperty("class", "PromptText"); + phoLayout->addWidget(m_pPhoPromptMsgLB, 0, Qt::AlignHCenter); + + m_pPhoErrorMsgLB = new QLabel(this); + m_pPhoErrorMsgCloneLB = new QLabel(this); + m_pPhoErrorMsgLB->setProperty("class", "ErrorMsg"); + m_pPhoErrorMsgCloneLB->setProperty("class", "ErrorMsg"); + hidePhoneErrorMsg(); + phoLayout->addWidget(m_pPhoErrorMsgLB, 0, Qt::AlignHCenter); + phoLayout->addWidget(m_pPhoErrorMsgCloneLB, 0, Qt::AlignHCenter); + + + m_pPhoInputInfos = new InputInfos(this); + phoLayout->addWidget(m_pPhoInputInfos, 0, Qt::AlignHCenter); + + m_pPhoSubmitBT = new QPushButton(this); + m_pPhoSubmitBT->setFocusPolicy(Qt::NoFocus); + m_pPhoSubmitBT->setText(tr("commit")); + m_pPhoSubmitBT->setProperty("class", "BindBT"); + m_pPhoSubmitBT->setCursor(QCursor(Qt::PointingHandCursor)); + m_pPhoSubmitBT->setEnabled(false); + phoLayout->addSpacing(8); + phoLayout->addWidget(m_pPhoSubmitBT, 0, Qt::AlignHCenter); + phoLayout->addStretch(1); +} + +void PhoneAuthWidget::initConnect() +{ + connect(m_pPhoInputInfos, &InputInfos::InputStateChanged, this, &PhoneAuthWidget::onInputStateChanged); + + connect(m_pPhoSubmitBT, &QPushButton::clicked, this, &PhoneAuthWidget::onSubmitBTClick); + + connect(m_pPhoInputInfos, &InputInfos::getVerCode, this, &PhoneAuthWidget::onGetVerCode); +} + +QSize PhoneAuthWidget::sizeHint() const{ + return QWidget::sizeHint(); +} + +void PhoneAuthWidget::hidePhoneErrorMsg() +{ + m_pPhoErrorMsgCloneLB->setFixedHeight(m_pPhoErrorMsgLB->height()); + m_pPhoErrorMsgCloneLB->show(); + m_pPhoErrorMsgLB->hide(); +} + +void PhoneAuthWidget::showPhoneErrorMsg() +{ + m_pPhoErrorMsgLB->show(); + m_pPhoErrorMsgCloneLB->hide(); +} + +void PhoneAuthWidget::onInputStateChanged(InputInfos::InputState input_state) +{ + if(input_state == InputInfos::InputState::InputFinish) + { + m_pPhoSubmitBT->setEnabled(true); + m_pPhoSubmitBT->setFocusPolicy(Qt::NoFocus); + m_pPhoSubmitBT->setStyleSheet("background:#2FB3E8;"); + } + else + { + m_pPhoSubmitBT->setEnabled(false); + m_pPhoSubmitBT->setStyleSheet("background:rgba(255,255,255,115);"); + } +} + + +void PhoneAuthWidget::onSubmitBTClick() +{ + QString tel = m_pPhoInputInfos->getPhoneNumer(); + QString verCode = m_pPhoInputInfos->getVerificationCode(); + if (true) + { + //验证成功 + qDebug() << "-----------onSubmitBTClick"; + Q_EMIT pageMessage(SwitchPage::SwitchToResetPWD, QList()); + } +// DBusMsgCode msgCode = SSOP->m_pRetrievePwdInterface->CheckVerificationCode(tel, verCode); +// switch (msgCode) { +// case DBusMsgCode::No_Error: +// emit pageMessage(PageMessage::JumpToNewPswSet, QList()); +// break; +// // TODO 更新提交后的错误状态 +// default: +// break; +// } +} + +void PhoneAuthWidget::onGetVerCode() +{ +// DBusMsgCode msgCode = SSOP->m_pRetrievePwdInterface->GetVerificationCode(Cfg->getUsername(), m_pPhoInputInfos->getPhoneNumer()); +// switch (msgCode) { +// case DBusMsgCode::No_Error: +// // 验证码获取无错误 +// break; +// // TODO 更新错误状态 +// default: +// break; +// } +} + +void PhoneAuthWidget::onQRCodeStateChanged(QString username, QString password, int nState) +{ + QRCodeSwepState state = static_cast(nState); + switch (state) { + case QRCodeSwepState::WaitingSwep: + qDebug() << "info: [QRCodePhoneAuthWidget][onQRCodeStateChanged]: waiting user swep qrcode!"; + break; + case QRCodeSwepState::HaveSwep: + qDebug() << "info: [QRCodePhoneAuthWidget][onQRCodeStateChanged]: user has swep code!"; + break; + case QRCodeSwepState::CancelSwep: + qDebug() << "info: [QRCodePhoneAuthWidget][onQRCodeStateChanged]: user cancel swep code!"; + break; + case QRCodeSwepState::ConfirmSuccess: + { + qDebug() << "info: [QRCodePhoneAuthWidget][onQRCodeStateChanged]: user = [" << username << "] confirm success!"; +// if(username != Cfg->getUsername()) +// { +// resetQRCService(); +// // TODO 更新错误信息 +// } +// else +// emit pageMessage(PageMessage::JumpToNewPswSet, QList()); +// break; + } + case QRCodeSwepState::QRCodeInvalid: + { + qDebug() << "info: [QRCodePhoneAuthWidget][onQRCodeStateChanged]: qrcode invalid!"; +// resetQRCService(); + break; + } + default: + break; + } +} + +void PhoneAuthWidget::setQSS() +{ + QString style_sheet =".PromptText{" + "font-size:24px;" + "color: rgba(255,255,255,192);" + "}" + ".BindBT{" + "background:rgba(255,255,255,115);" + "width:316px;" + "height:64px;" + "border-radius:16px;" + "}" + ".ErrorMsg{" + "color:#FD625E;" + "font-size:14px;" + "}" + ".titleLB{" + "color:#FFFFFF;" + "font-size:32px;" + "}"; + setStyleSheet(style_sheet); +} diff --git a/src/permissioncheck.h b/src/permissioncheck.h new file mode 100644 index 0000000..94639e5 --- /dev/null +++ b/src/permissioncheck.h @@ -0,0 +1,108 @@ +#ifndef PERMISSIONCHECK_H +#define PERMISSIONCHECK_H + +#include +#include +#include +#include +#include "wechatauthdialog.h" +#include "common.h" + +class InputInfos; +class PhoneAuthWidget; + +class InputInfos : public QWidget +{ + Q_OBJECT +public: + enum InputState{ + InputWaiting + , InputFinish + }; + InputInfos(QWidget *parent = nullptr); + QString getPhoneNumer(); + QString getVerificationCode(); + void clearInfos(); + +Q_SIGNALS: + void getVerCode(); + void InputStateChanged(InputState input_state); + +protected: + void paintEvent(QPaintEvent *); + +private Q_SLOTS: + void onPhoneTextChanged(const QString &text); + void onVerCodeTextChanged(const QString &text); + void onGetVerCode(); + void onNextGetVerCodeTimer(); + +private: + void initUI(); + void initConnect(); + void setQSS(); + +private: + QLabel *m_pPhoneIconLB; + QLineEdit *m_pPhoneLE; + QLabel *m_pVerCodeIconLB; + QLineEdit *m_pVerCodeLE; + QPushButton *m_pGetVerCodeBT; + QTimer *m_pNextGetVerCodeQTimer; + int m_nextGetVerCodeTime; + + InputState m_curInputState; +}; + +class VerificationWidget : public QWidget +{ + Q_OBJECT +public: + explicit VerificationWidget(QWidget *parent = nullptr); + +Q_SIGNALS: + void pageMessage(SwitchPage id, QList argvs); +private: + QLabel *m_titleLB; + QLabel *m_phonePromptMsgLB; + WeChatAuthDialog *m_weChatWidget; +//public: + PhoneAuthWidget *m_phoneAuthWidget; + +protected: + void paintEvent(QPaintEvent *); +}; + +class PhoneAuthWidget : public QWidget +{ + Q_OBJECT +public: + PhoneAuthWidget(QWidget* parent = nullptr); + QSize sizeHint() const; + +private: + void initUI(); + void initConnect(); + void setQSS(); + + void showPhoneErrorMsg(); + void hidePhoneErrorMsg(); +Q_SIGNALS: + void pageMessage(SwitchPage id, QList argvs); +private Q_SLOTS: + void onInputStateChanged(InputInfos::InputState input_state); + void onSubmitBTClick(); + void onGetVerCode(); + void onQRCodeStateChanged(QString username, QString password, int nState); + +private: + // 手机号验证 + QLabel *m_pPhoTitleLB; + QLabel *m_pPhoPromptMsgLB; + QLabel *m_pPhoErrorMsgLB; + QLabel *m_pPhoErrorMsgCloneLB; // 控制格式 + QPushButton *m_pPhoSubmitBT; + InputInfos *m_pPhoInputInfos; + +}; +#endif // PERMISSIONCHECK_H diff --git a/src/powermanager.cpp b/src/powermanager.cpp new file mode 100644 index 0000000..7bc190d --- /dev/null +++ b/src/powermanager.cpp @@ -0,0 +1,801 @@ + /* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + **/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "powermanager.h" + +const static QString login1Service = QStringLiteral("org.freedesktop.login1"); +const static QString login1Path = QStringLiteral("/org/freedesktop/login1"); +const static QString login1ManagerInterface = QStringLiteral("org.freedesktop.login1.Manager"); + +#ifdef USE_INTEL +PowerManager::PowerManager(QWidget *parent) + : QWidget(parent), + lasttime(QTime::currentTime()) +{ + resize((ITEM_WIDTH*4 + ITEM_SPACING*3), ITEM_HEIGHT); + initUI(); + setQSS(); +} +#else +PowerManager::PowerManager(QWidget *parent) + : QListWidget(parent), + lasttime(QTime::currentTime()) +{ + // resize(ITEM_WIDTH*7, ITEM_HEIGHT); + setFlow(QListWidget::LeftToRight); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setSelectionMode(QListWidget::NoSelection); + + + //QObject::connect(this,SIGNAL(itemClicked(QListWidgetItem*)),this,SLOT(powerClicked(QListWidgetItem*))); +// QObject::connect(this, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), this,\ +// SLOT(keyboardPressed(QListWidgetItem*,QListWidgetItem*))); + + sessionInterface = new QDBusInterface("org.gnome.SessionManager", + "/org/gnome/SessionManager", + "org.gnome.SessionManager", + QDBusConnection::sessionBus(), + this); + + loginInterface = new QDBusInterface(login1Service, + login1Path, + login1ManagerInterface, + QDBusConnection::systemBus(), + this); + + xEventMonitor = new XEventMonitor(this); + connect(xEventMonitor, SIGNAL(keyPress(const QString &)), this, + SLOT(onGlobalKeyPress(const QString &))); + connect(xEventMonitor, SIGNAL(keyRelease(const QString &)), this, + SLOT(onGlobalkeyRelease(const QString &))); + xEventMonitor->start(); + + QDBusReply stateReply = loginInterface->call("CanSuspend"); + if(stateReply.isValid() && stateReply.value() == "yes"){ + canSuspend = true; + }else{ + canSuspend = false; + } + + QDBusReply stateReply1 = loginInterface->call("CanHibernate"); + if(stateReply1.isValid() && stateReply1.value() == "yes"){ + canHibernate = true; + }else{ + canHibernate = false; + } + + initUI(); + resize((ITEM_WIDTH+ITEM_SPACING*2)*this->count()-ITEM_SPACING*2, ITEM_HEIGHT+ITEM_SPACING*2); + //setCurrentRow(0); +} + + +void PowerManager::keyboardPressed(QListWidgetItem *item_c, QListWidgetItem *item_p) +{ +// if(item_p){ +// QString name = itemWidget(item_c)->objectName(); +// if(name == rebootWidget->objectName()){ +// rebootFace->setStyleSheet("background-color: rgba(255, 255, 255, 40%),\ +// QLabel:hover{background-color:rgb(255,255,255,40%)"); +// } else if(name == shutdownWidget->objectName()){ +// shutdownFace->setStyleSheet("background-color: rgba(255, 255, 255, 40%)"); +// } else if(suspendWidget && name == suspendWidget->objectName()){ +// suspendFace->setStyleSheet("background-color: rgba(255, 255, 255, 40%)"); +// } else if(hibernateWidget && name == hibernateWidget->objectName()){ +// hibernateFace->setStyleSheet("background-color: rgba(255, 255, 255, 40%)"); +// } +// QString name_p = itemWidget(item_p)->objectName(); +// if(name_p == rebootWidget->objectName()){ +// rebootFace->setStyleSheet("background-color: rgba(255, 255, 255, 20% QLabel:hover{background-color:rgb(255,255,255,40%))"); + +// } else if(name_p == shutdownWidget->objectName()){ +// shutdownFace->setStyleSheet("background-color: rgba(255, 255, 255, 20%\ +// QLabel:hover{background-color:rgb(255,255,255,40%))"); +// } else if(suspendWidget && name_p == suspendWidget->objectName()){ +// suspendFace->setStyleSheet("background-color: rgba(255, 255, 255, 20%\ +// QLabel:hover{background-color:rgb(255,255,255,40%))"); +// } else if(hibernateWidget && name_p == hibernateWidget->objectName()){ +// hibernateFace->setStyleSheet("background-color: rgba(255, 255, 255, 20%); QLabel:hover{background-color:rgb(255,255,255,40%))"); +// } +// } +} + + + +bool PowerManager::eventFilter(QObject *obj, QEvent *event) +{ + if(event->type() != QEvent::MouseButtonPress){ + return false; + } + + QString name = obj->objectName(); + + if(name == "switchFace") + switchWidgetClicked(); + else if(name == "logoutFace") + logoutWidgetCliced(); + else if(name == "rebootFace") + rebootWidgetClicked(); + else if(name == "shutdownFace") + shutdownWidgetClicked(); + else if(name == "suspendFace") + suspendWidgetClicked(); + else if(name == "hibernateFace") + hibernateWidgetClicked(); + + return false; +} + +void PowerManager::onGlobalKeyPress(const QString &key) +{ +} + +void PowerManager::onGlobalkeyRelease(const QString &key) +{ + if (key == "Escape") { + + } else if (key == "Left" || key == "Right" || key == "Return" || key == "KP_Enter") { + keyBdRelease(key); + } +} + +void PowerManager::keyBdRelease(QString key) +{ + QString focus = "focus"; + QString normal = "normal"; + if(key == "Right"){ + if(nowAt == -1){ + nowAt = 0; + setButtonStyle(focus); + } else if(nowAt == 0){ + setButtonStyle(normal); + nowAt = 1; + setButtonStyle(focus); + } else if(nowAt == 1){ + setButtonStyle(normal); + nowAt = 2; + setButtonStyle(focus); + } else if(nowAt == 2){ + setButtonStyle(normal); + nowAt = 3; + setButtonStyle(focus); + } else if(nowAt == 3){ + setButtonStyle(normal); + nowAt = 0; + setButtonStyle(focus); + } + } else if(key == "Left") { + if(nowAt == -1){ + nowAt = 3; + setButtonStyle(focus); + } else if(nowAt == 3){ + setButtonStyle(normal); + nowAt = 2; + setButtonStyle(focus); + } else if(nowAt == 2){ + setButtonStyle(normal); + nowAt = 1; + setButtonStyle(focus); + } else if(nowAt == 1){ + setButtonStyle(normal); + nowAt = 0; + setButtonStyle(focus); + } else if(nowAt == 0){ + setButtonStyle(normal); + nowAt = 3; + setButtonStyle(focus); + } + } else if(key == "Return" || key == "KP_Enter"){ + if(nowAt == 0 && this->isVisible()) + hibernateWidgetClicked(); + else if(nowAt == 1 && this->isVisible()) + suspendWidgetClicked(); + else if(nowAt == 2 && this->isVisible()) + rebootWidgetClicked(); + else if(nowAt == 3 && this->isVisible()) + shutdownWidgetClicked(); + } +} + + +void::PowerManager::setButtonStyle(QString Style) +{ + if(Style == "normal") + listLabel.at(nowAt)->setStyleSheet("background-color: rgba(255, 255, 255, 15%);QLabel:hover{background-color:rgb(255,255,255,40%);QLabel:pressed{background-color:rgb(255,255,255,30%))"); + else if(Style == "focus") + listLabel.at(nowAt)->setStyleSheet("background-color: rgba(255, 255, 255, 20%);border: 1px solid #296CD9; border-radius: 64px;"); +} + +QStringList PowerManager::getLoginedUsers() +{ + QStringList m_loginedUser; + QDBusMessage result = loginInterface->call("ListUsers"); + QList outArgs = result.arguments(); + QVariant first = outArgs.at(0); + QDBusArgument dbvFirst = first.value(); + QVariant vFirst = dbvFirst.asVariant(); + const QDBusArgument &dbusArgs = vFirst.value(); + + + QVector loginedUsers; + + dbusArgs.beginArray(); + while (!dbusArgs.atEnd()) { + usersInfo user; + dbusArgs >> user; + loginedUsers.push_back(user); + } + + for (usersInfo user: loginedUsers) { + + QDBusInterface userPertyInterface("org.freedesktop.login1", user.usersPath.path(), + "org.freedesktop.DBus.Properties", + QDBusConnection::systemBus()); + + QDBusReply reply = + userPertyInterface.call("Get", "org.freedesktop.login1.User", "State"); + if (reply.isValid()) { + QString status = reply.value().toString(); + if ("closing" != status) { + m_loginedUser.append(user.usersName); + } + } + } + return m_loginedUser; +} + +QDBusArgument &operator <<(QDBusArgument &arg, const usersInfo &userInfo) +{ + arg.beginStructure(); + arg << userInfo.usersId + << userInfo.usersName + << userInfo.usersPath; + arg.endStructure(); + return arg; +} +const QDBusArgument &operator >>(const QDBusArgument &arg, usersInfo &userInfo) +{ + arg.beginStructure(); + arg >> userInfo.usersId + >> userInfo.usersName + >> userInfo.usersPath; + arg.endStructure(); + return arg; +} + +void PowerManager::powerClicked(QListWidgetItem *item) +{ + int interval = lasttime.msecsTo(QTime::currentTime()); + if(interval < 200 && interval > -200) + return ; + lasttime = QTime::currentTime(); + + QString name = itemWidget(item)->objectName(); + printf("111111111111111111111111111 name = %s\n",name.toLatin1().data()); +// if(name == lockWidget->objectName()) +// lockWidgetClicked(); + if(switchWidget && name == switchWidget->objectName()) + switchWidgetClicked(); + else if(name == logoutWidget->objectName()) + logoutWidgetCliced(); + else if(name == rebootWidget->objectName()) + rebootWidgetClicked(); + else if(name == shutdownWidget->objectName()) + shutdownWidgetClicked(); + else if(suspendWidget && name == suspendWidget->objectName()) + suspendWidgetClicked(); + else if(hibernateWidget && name == hibernateWidget->objectName()) + hibernateWidgetClicked(); +} +#endif + +void PowerManager::lockWidgetClicked() +{ + emit lock(); +} + +void PowerManager::switchWidgetClicked() +{ + emit switchToUser(); +} + +#ifdef USE_INTEL +void PowerManager::shutdownWidgetClicked() +{ + QDBusInterface *interface = new QDBusInterface("org.gnome.SessionManager", + "/org/gnome/SessionManager", + "org.gnome.SessionManager", + QDBusConnection::sessionBus(), + this); + + QDBusMessage msg = interface->call("powerOff"); + qDebug() << "[PowerManager] [shutdownWidgetClicked]" << msg.errorMessage(); +} + +void PowerManager::rebootWidgetClicked() +{ + QDBusInterface *interface = new QDBusInterface("org.gnome.SessionManager", + "/org/gnome/SessionManager", + "org.gnome.SessionManager", + QDBusConnection::sessionBus(), + this); + + QDBusMessage msg = interface->call("reboot"); + qDebug() << "[PowerManager] [rebootWidgetClicked]" << msg.errorMessage(); +} + +void PowerManager::logoutWidgetCliced() +{ + QDBusInterface *interface = new QDBusInterface("org.gnome.SessionManager", + "/org/gnome/SessionManager", + "org.gnome.SessionManager", + QDBusConnection::sessionBus(), + this); + + QDBusMessage msg = interface->call("logout"); + qDebug() << "[PowerManager] [logoutWidgetCliced]" << msg.errorMessage(); +} + +void PowerManager::initUI() +{ + this->setContentsMargins(0,0,0,0); + QHBoxLayout *main_layout = new QHBoxLayout(this); + main_layout->setContentsMargins(0,0,0,0); + main_layout->setSpacing(0); + + lockWidget = new QWidget(this); + lockWidget->setContentsMargins(0,0,0,0); + lockWidget->setFixedSize(ITEM_WIDTH,ITEM_HEIGHT); + QPushButton *lockPB = new QPushButton(lockWidget); + QLabel *lockLabel = new QLabel(lockWidget); + lockPB->setProperty("class", "PowerManagerPB"); + lockPB->setIcon(QIcon(QPixmap(":/image/assets/intel/lock.png").scaled(40,40))); + lockLabel->setAlignment(Qt::AlignBottom | Qt::AlignHCenter); + lockLabel->setText(tr("lock")); + QVBoxLayout *locklayout = new QVBoxLayout(lockWidget); + locklayout->setContentsMargins(0,0,0,0); + locklayout->addWidget(lockPB); + locklayout->addWidget(lockLabel); + connect(lockPB, &QPushButton::clicked, this, [=]{ + qDebug() << "PowerManager lock clicked"; + Q_EMIT lock(); + }); + + logoutWidget = new QWidget(this); + logoutWidget->setContentsMargins(0,0,0,0); + logoutWidget->setFixedSize(ITEM_WIDTH,ITEM_HEIGHT); + QPushButton *logoutPB = new QPushButton(logoutWidget); + QLabel *logoutLabel = new QLabel(this); + logoutPB->setProperty("class", "PowerManagerPB"); + logoutPB->setIcon(QIcon(QPixmap(":/image/assets/intel/logout.png").scaled(40,40))); + logoutLabel->setText(tr("Log Out")); + logoutLabel->setAlignment(Qt::AlignBottom | Qt::AlignHCenter); + QVBoxLayout *logoutlayout = new QVBoxLayout(logoutWidget); + logoutlayout->setContentsMargins(0,0,0,0); + logoutlayout->addWidget(logoutPB); + logoutlayout->addWidget(logoutLabel); + connect(logoutPB, &QPushButton::clicked, this, [=]{ + qDebug() << "PowerManager logout clicked"; + logoutWidgetCliced();; + }); + + rebootWidget = new QWidget(this); + rebootWidget->setFixedSize(ITEM_WIDTH,ITEM_HEIGHT); + rebootWidget->setContentsMargins(0,0,0,0); + QPushButton *rebootPB = new QPushButton(rebootWidget); + QLabel *rebootLabel = new QLabel(this); + rebootPB->setProperty("class", "PowerManagerPB"); + rebootPB->setIcon(QIcon(QPixmap(":/image/assets/intel/reboot.png").scaled(40,40))); + rebootLabel->setText(tr("Restart")); + rebootLabel->setAlignment(Qt::AlignBottom | Qt::AlignHCenter); + + QVBoxLayout *rebootlayout = new QVBoxLayout(rebootWidget); + rebootlayout->setContentsMargins(0,0,0,0); + rebootlayout->addWidget(rebootPB); + rebootlayout->addWidget(rebootLabel); + connect(rebootPB, &QPushButton::clicked, this, [=]{ + qDebug() << "PowerManager reboot clicked"; + //rebootWidgetClicked(); + reboot(); + }); + + shutdownWidget = new QWidget(this); + shutdownWidget->setFixedSize(ITEM_WIDTH,ITEM_HEIGHT); + shutdownWidget->setObjectName("shutdownWidget"); + QPushButton *shutdownPB = new QPushButton(shutdownWidget); + QLabel *shutdownLabel = new QLabel(shutdownWidget); + shutdownPB->setProperty("class", "PowerManagerPB"); + shutdownPB->setIcon(QIcon(QPixmap(":/image/assets/intel/shutdown.png").scaled(40,40))); + shutdownLabel->setText(tr("Power Off")); + shutdownLabel->setAlignment(Qt::AlignBottom | Qt::AlignHCenter); + QVBoxLayout *shutdownlayout = new QVBoxLayout(shutdownWidget); + shutdownlayout->setContentsMargins(0,0,0,0); + shutdownlayout->addWidget(shutdownPB); + shutdownlayout->addWidget(shutdownLabel); + connect(shutdownPB, &QPushButton::clicked, this, [=]{ + qDebug() << "PowerManager shutdown clicked"; + //shutdownWidgetClicked(); + powerOff(); + }); + + main_layout->addWidget(lockWidget); + main_layout->addSpacing(ITEM_SPACING); + main_layout->addWidget(logoutWidget); + main_layout->addSpacing(ITEM_SPACING); + main_layout->addWidget(rebootWidget); + main_layout->addSpacing(ITEM_SPACING); + main_layout->addWidget(shutdownWidget); + adjustSize(); +} + +//息屏,休眠状态Hibernate/挂起Suspend +bool PowerManager::hibernate() +{ + const QString service = "org.freedesktop.login1"; + const QString path = "/org/freedesktop/login1"; + const QString interface = "org.freedesktop.login1.Manager"; + QString command = QLatin1String("Suspend"); + + QDBusInterface dbus(service, path, interface, QDBusConnection::systemBus()); + if (!dbus.isValid()) { + qWarning() << "dbusCall: QDBusInterface is invalid" << service<< path + << interface << "Suspend"; + return false; + } + QDBusMessage msg = dbus.call(command, QVariant(true)); + + if (!msg.errorName().isEmpty()) { + qWarning() << "Debus error: " << msg; + } + + if (msg.arguments().isEmpty() || msg.arguments().constFirst().isNull()) + return true; + + QString response = msg.arguments().constFirst().toString(); + qDebug() << "systemd:" << QLatin1String("Suspend") << "=" << response; + return response == QLatin1String("yes") || response == QLatin1String("challenge"); +} + +bool PowerManager::reboot() +{ + const QString service = "org.freedesktop.login1"; + const QString path = "/org/freedesktop/login1"; + const QString interface = "org.freedesktop.login1.Manager"; + QString command = QLatin1String("Reboot"); + + QDBusInterface dbus(service, path, interface, QDBusConnection::systemBus()); + if (!dbus.isValid()) { + qWarning() << "dbusCall: QDBusInterface is invalid" << service<< path + << interface << "Reboot"; + return false; + } + QDBusMessage msg = dbus.call(command, QVariant(true)); + + if (!msg.errorName().isEmpty()) { + qWarning() << "Debus error: " << msg; + } + + if (msg.arguments().isEmpty() || msg.arguments().constFirst().isNull()) + return true; + + QString response = msg.arguments().constFirst().toString(); + qDebug() << "systemd:" << QLatin1String("Reboot") << "=" << response; + return response == QLatin1String("yes") || response == QLatin1String("challenge"); +} + +bool PowerManager::powerOff() +{ + const QString service = "org.freedesktop.login1"; + const QString path = "/org/freedesktop/login1"; + const QString interface = "org.freedesktop.login1.Manager"; + QString command = QLatin1String("PowerOff"); + + QDBusInterface dbus(service, path, interface, QDBusConnection::systemBus()); + if (!dbus.isValid()) { + qWarning() << "dbusCall: QDBusInterface is invalid" << service<< path + << interface << "PowerOff"; + return false; + } + QDBusMessage msg = dbus.call(command, QVariant(true)); + + if (!msg.errorName().isEmpty()) { + qWarning() << "Debus error: " << msg; + } + + if (msg.arguments().isEmpty() || msg.arguments().constFirst().isNull()) + return true; + + QString response = msg.arguments().constFirst().toString(); + qDebug() << "systemd:" << QLatin1String("PowerOff") << "=" << response; + return response == QLatin1String("yes") || response == QLatin1String("challenge"); +} + +void PowerManager::setQSS() +{ + //设置电源管理按键样式 + QString style_sheet = ".PowerManagerPB{" + "background:rgba(255,255,255,38);" + "min-width: 128px;" + "max-width: 128px;" + "min-height: 128px;" + "max-height: 128px;" + "border-radius: 64px;" + "icon-size: 40px;" + "font-family: NotoSansCJKsc-Regular, NotoSansCJKsc;" + "}" + ".PowerManagerPB:hover{" + "background:rgba(255,255,255,89);" + "}" + ".PowerManagerPB:pressed{" + "background:rgba(255,255,255,12);" + "}"; + setStyleSheet(style_sheet); +} +#else + +void PowerManager::doEvent(int type) +{ + switch (type) { + case REBOOT: + sessionInterface->call("reboot"); + break; + case SHOTDOWN: + sessionInterface->call("powerOff"); + default: + break; + } +} + +void PowerManager::suspendWidgetClicked() +{ + loginInterface->call("Suspend",true); + emit lock(); +} + +void PowerManager::hibernateWidgetClicked() +{ + loginInterface->call("Hibernate",true); + emit lock(); +} + +void PowerManager::shutdownWidgetClicked() +{ + if(getLoginedUsers().count() > 1){ + Q_EMIT mulUsersLogined(SHOTDOWN); + return; + } + sessionInterface->call("powerOff"); +} + +void PowerManager::rebootWidgetClicked() +{ + if(getLoginedUsers().count() > 1){ + Q_EMIT mulUsersLogined(REBOOT); + return; + } + sessionInterface->call("reboot"); +} + +void PowerManager::logoutWidgetCliced() +{ + sessionInterface->call("logout"); +} + +void PowerManager::showSmallSize() +{ + for(int i = 0;iitem(i); + item->setSizeHint(QSize(ITEM_WIDTH*0.8,ITEM_HEIGHT)); + itemWidget(item)->setFixedSize(ITEM_WIDTH*0.8,ITEM_HEIGHT); + } + resize(ITEM_WIDTH*this->count()*0.8,ITEM_HEIGHT); +} + +void PowerManager::showNormalSize() +{ + for(int i = 0;iitem(i); + item->setSizeHint(QSize(ITEM_WIDTH,ITEM_HEIGHT)); + itemWidget(item)->setFixedSize(ITEM_WIDTH,ITEM_HEIGHT); + } + // resize(ITEM_WIDTH*this->count(),ITEM_HEIGHT); + // resize((ITEM_WIDTH+ITEM_SPACING)*this->count(), ITEM_HEIGHT+ITEM_SPACING); + resize((ITEM_WIDTH+ITEM_SPACING*2)*this->count()-ITEM_SPACING*2, ITEM_HEIGHT+ITEM_SPACING*2); +} + +void PowerManager::initUI() +{ + setSpacing(30); + +// actService = new QDBusInterface("org.freedesktop.Accounts", +// "/org/freedesktop/Accounts", +// "org.freedesktop.Accounts", +// QDBusConnection::systemBus()); + +// QDBusMessage ret = actService->call("ListCachedUsers"); +// QList outArgs = ret.arguments(); +// QVariant first = outArgs.at(0); +// const QDBusArgument &dbusArgs = first.value(); +// dbusArgs.beginArray(); +// QDBusObjectPath path; +// int userCount =0; +// while (!dbusArgs.atEnd()) +// { +// userCount++; +// dbusArgs >> path; +// } +// dbusArgs.endArray(); +// switchWidget=nullptr; +// if(userCount>1){ +// switchWidget = new QWidget(this); +// switchWidget->setObjectName("switchWidget"); +// QLabel *switchFace = new QLabel(switchWidget); +// QLabel *switchLabel = new QLabel(switchWidget); +// switchFace->setAlignment(Qt::AlignCenter); +// switchFace->setObjectName("switchFace"); +// switchFace->installEventFilter(this); +// switchLabel->setAlignment(Qt::AlignCenter); +// switchFace->setPixmap(QPixmap(":/image/assets/switchGreeter.png").scaled(58,58)); +// switchLabel->setText(tr("Switch User")); +// switchWidget->setFixedSize(ITEM_WIDTH,ITEM_HEIGHT); +// QVBoxLayout *switchlayout = new QVBoxLayout(switchWidget); +// switchlayout->addWidget(switchFace); +// switchlayout->addWidget(switchLabel); +// // switchWidget->installEventFilter(this); +// } +// logoutWidget = new QWidget(this); +// logoutWidget->setObjectName("logoutWidget"); +// QLabel *logoutFace = new QLabel(this); +// QLabel *logoutLabel = new QLabel(this); +// logoutFace->setAlignment(Qt::AlignCenter); +// logoutFace->setObjectName("logoutFace"); +// logoutFace->installEventFilter(this); +// logoutLabel->setAlignment(Qt::AlignCenter); +// logoutFace->setPixmap(QPixmap(":/image/assets/logout.png").scaled(48,48)); +// logoutLabel->setText(tr("Log Out")); +// logoutWidget->setFixedSize(ITEM_WIDTH,ITEM_HEIGHT); +// QVBoxLayout *logoutlayout = new QVBoxLayout(logoutWidget); +// logoutlayout->addWidget(logoutFace); +// logoutlayout->addWidget(logoutLabel); + //logoutWidget->installEventFilter(this); + + rebootWidget = new QWidget(this); + rebootWidget->setObjectName("rebootWidget"); + QWidget *rbLabelWidget = new QWidget(this); + rbLabelWidget->setFixedSize(130, 130); + rebootFace = new QLabel(rbLabelWidget); + rebootFace->setObjectName("rebootFace"); + QLabel *rebootLabel = new QLabel(this); + rebootFace->setAlignment(Qt::AlignCenter); + rebootFace->installEventFilter(this); + rebootLabel->setAlignment(Qt::AlignCenter); + rebootFace->setPixmap(QPixmap(":/image/assets/reboot.png").scaled(58,58)); + rebootLabel->setText(tr("Restart")); + rebootWidget->setFixedSize(ITEM_WIDTH,ITEM_HEIGHT); + QVBoxLayout *rebootlayout = new QVBoxLayout(rebootWidget); + rebootlayout->addWidget(rbLabelWidget); + rebootlayout->addWidget(rebootLabel); + //rebootWidget->installEventFilter(this); + + shutdownWidget = new QWidget(this); + shutdownWidget->setObjectName("shutdownWidget"); + QWidget *shLabelWidget = new QWidget(this); + shLabelWidget->setFixedSize(130, 130); + shutdownFace = new QLabel(shLabelWidget); + shutdownFace->setObjectName("shutdownFace"); + QLabel *shutdownLabel = new QLabel(this); + shutdownLabel->setAlignment(Qt::AlignCenter); + shutdownFace->setAlignment(Qt::AlignCenter); + shutdownFace->installEventFilter(this); + shutdownFace->setPixmap(QPixmap(":/image/assets/shutdown.png").scaled(58,58)); + shutdownLabel->setText(tr("Shut Down")); + shutdownWidget->setFixedSize(ITEM_WIDTH,ITEM_HEIGHT); + QVBoxLayout *shutdownlayout = new QVBoxLayout(shutdownWidget); + shutdownlayout->addWidget(shLabelWidget); + shutdownlayout->addWidget(shutdownLabel); + //shutdownWidget->installEventFilter(this); + +// if(userCount>1){ +// QListWidgetItem *item1 = new QListWidgetItem(); +// item1->setSizeHint(QSize(ITEM_WIDTH, ITEM_HEIGHT)); +// insertItem(this->count(), item1); +// setItemWidget(item1, switchWidget); +// } + hibernateWidget = nullptr; + if(canHibernate){ + hibernateWidget = new QWidget(this); + hibernateWidget->setObjectName("hibernateWidget"); + QWidget *hbLabelWidget = new QWidget(this); + hbLabelWidget->setFixedSize(130, 130); + hibernateFace = new QLabel(hbLabelWidget); + hibernateFace->setObjectName("hibernateFace"); + QLabel *hibernateLabel = new QLabel(this); + hibernateLabel->setAlignment(Qt::AlignCenter); + hibernateFace->setAlignment(Qt::AlignCenter); + hibernateFace->installEventFilter(this); + hibernateFace->setPixmap(QPixmap(":/image/assets/hibernate.png").scaled(48,48)); + hibernateLabel->setText(tr("Hibernate")); + hibernateWidget->setFixedSize(ITEM_WIDTH,ITEM_HEIGHT); + QVBoxLayout *hibernatelayout = new QVBoxLayout(hibernateWidget); + hibernatelayout->addWidget(hbLabelWidget); + hibernatelayout->addWidget(hibernateLabel); + //hibernateWidget->installEventFilter(this); + + QListWidgetItem *item5 = new QListWidgetItem(); + item5->setSizeHint(QSize(ITEM_WIDTH, ITEM_HEIGHT)); + insertItem(this->count(), item5); + setItemWidget(item5, hibernateWidget); + listLabel.append(hibernateFace); + } + + suspendWidget = nullptr; + if(canSuspend){ + suspendWidget = new QWidget(this); + suspendWidget->setObjectName("suspendWidget"); + QWidget *spLabelWidget = new QWidget(this); + spLabelWidget->setFixedSize(130, 130); + suspendFace = new QLabel(spLabelWidget); + suspendFace->setObjectName("suspendFace"); + QLabel *suspendLabel = new QLabel(this); + suspendLabel->setAlignment(Qt::AlignCenter); + suspendFace->setAlignment(Qt::AlignCenter); + suspendFace->installEventFilter(this); + suspendFace->setPixmap(QPixmap(":/image/assets/suspend.png").scaled(48,48)); + suspendLabel->setText(tr("Suspend")); + suspendWidget->setFixedSize(ITEM_WIDTH,ITEM_HEIGHT); + QVBoxLayout *suspendlayout = new QVBoxLayout(suspendWidget); + suspendlayout->addWidget(spLabelWidget); + suspendlayout->addWidget(suspendLabel); + //suspendWidget->installEventFilter(this); + + QListWidgetItem *item4 = new QListWidgetItem(); + item4->setSizeHint(QSize(ITEM_WIDTH, ITEM_HEIGHT)); + insertItem(this->count(), item4); + setItemWidget(item4, suspendWidget); + listLabel.append(suspendFace); + } + +// QListWidgetItem *item1 = new QListWidgetItem(); +// item1->setSizeHint(QSize(ITEM_WIDTH, ITEM_HEIGHT)); +// insertItem(this->count(), item1); +// setItemWidget(item1, logoutWidget); + + QListWidgetItem *item2 = new QListWidgetItem(); + item2->setSizeHint(QSize(ITEM_WIDTH, ITEM_HEIGHT)); + insertItem(this->count(), item2); + setItemWidget(item2, rebootWidget); + listLabel.append(rebootFace); + + QListWidgetItem *item3 = new QListWidgetItem(); + item3->setSizeHint(QSize(ITEM_WIDTH, ITEM_HEIGHT)); + insertItem(this->count(), item3); + setItemWidget(item3, shutdownWidget); + listLabel.append(shutdownFace); +} +#endif diff --git a/src/powermanager.h b/src/powermanager.h new file mode 100644 index 0000000..3a4a1ee --- /dev/null +++ b/src/powermanager.h @@ -0,0 +1,135 @@ + /* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + **/ + +#ifndef POWERMANAGER_H +#define POWERMANAGER_H +#include +#include +#include +#include +#include "config.h" +#include "xeventmonitor.h" +#ifdef USE_INTEL +#define ITEM_WIDTH 128 +#define ITEM_HEIGHT (ITEM_WIDTH + 40) +#define ITEM_SPACING 72 + +class QWidget; +class PowerManager:public QWidget +#else +#define ITEM_WIDTH 144 +#define ITEM_HEIGHT 200 +#define ITEM_SPACING 30 + +enum stateType { + REBOOT, + SHOTDOWN, + NOTHING +}; + +struct usersInfo { + int usersId; + QString usersName; + QDBusObjectPath usersPath; +}; + +QDBusArgument &operator <<(QDBusArgument &arg, const usersInfo &userInfo); +const QDBusArgument &operator >>(const QDBusArgument &arg, usersInfo &userInfo); + +class QLabel; +class QListWidget; +class QListWidgetItem; +class QDBusInterface; +class PowerManager:public QListWidget +#endif +{ + Q_OBJECT + +public: + PowerManager(QWidget *parent = 0); +#ifdef USE_INTEL + bool hibernate(); +#else + void showNormalSize(); + void showSmallSize(); + void doEvent(int type); +#endif +private: + void initUI(); + +#ifdef USE_INTEL + void setQSS(); + QWidget *list; +#else + QList listLabel; + int nowAt = -1; + QListWidget *list; + QWidget *suspendWidget; + QWidget *hibernateWidget; + QDBusInterface *sessionInterface; + QDBusInterface *loginInterface; + QDBusInterface *actService; + int login_Num; + + bool canSuspend; + bool canHibernate; + int m_count; +#endif + QWidget *lockWidget; + QWidget *switchWidget; + QWidget *logoutWidget; + QWidget *rebootWidget; + QWidget *shutdownWidget; + QTime lasttime; + XEventMonitor *xEventMonitor; + QLabel *rebootFace; + QLabel *shutdownFace; + QLabel *hibernateFace; + QLabel *suspendFace; + +private: + void lockWidgetClicked(); + void switchWidgetClicked(); + void logoutWidgetCliced(); + void rebootWidgetClicked(); + QStringList getLoginedUsers(); + void shutdownWidgetClicked(); + void keyBdRelease(QString key); + void setButtonStyle(QString Style); +#ifdef USE_INTEL + bool reboot(); + bool powerOff(); +#else + void suspendWidgetClicked(); + void hibernateWidgetClicked(); +#endif +private Q_SLOTS: +#ifndef USE_INTEL + void powerClicked(QListWidgetItem *item); + bool eventFilter(QObject *obj, QEvent *event); + void onGlobalKeyPress(const QString &key); + void onGlobalkeyRelease(const QString &key); + void keyboardPressed(QListWidgetItem *item,QListWidgetItem*); +#endif +Q_SIGNALS: + void switchToUser(); + void lock(); + void mulUsersLogined(int type); +}; + +#endif // POWERMANAGER_H diff --git a/src/screensaver.cpp b/src/screensaver.cpp new file mode 100644 index 0000000..744d33b --- /dev/null +++ b/src/screensaver.cpp @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#include "screensaver.h" +#include +#include +#include + +ScreenSaver::ScreenSaver(QObject *parent) + : QObject(parent), + imageIndex(0), + timer(nullptr) +{ + +} + +ScreenSaver::ScreenSaver(const ScreenSaver &screensaver) + : mode(screensaver.mode), + path(screensaver.path), + effect(screensaver.effect), + interval(screensaver.interval), + imageIndex(screensaver.imageIndex), + timer(screensaver.timer), + imagePaths(screensaver.imagePaths) +{ +} + + +ScreenSaver::ScreenSaver(ScreenSaver &&screensaver) noexcept + : mode(screensaver.mode), + path(screensaver.path), + effect(screensaver.effect), + interval(screensaver.interval), + imageIndex(screensaver.imageIndex), + timer(screensaver.timer), + imagePaths(screensaver.imagePaths) +{ +} + +bool ScreenSaver::exists() +{ + switch(mode) + { + case SAVER_BLANK_ONLY: + return true; + case SAVER_RANDOM: + case SAVER_SINGLE: + return QFile(path).exists(); + case SAVER_IMAGE: + return QDir(path).exists(); + case SAVER_DEFAULT: + return QDir(path).exists(); + case SAVER_DEFAULT_CUSTOM: + return QDir(path).exists(); + default: + return false; + } + return false; +} + +void ScreenSaver::startSwitchImages() +{ + qDebug() << "ScreenSaver::startSwitchImages"; + if(mode != SAVER_IMAGE) + return; + QFileInfo fileInfo(path); + if(fileInfo.isFile()) + return; + QList formats = QImageReader::supportedImageFormats(); + if(fileInfo.isDir()) { + QDir dir(path); + QStringList files = dir.entryList(QDir::Files | QDir::Readable); + for(QString file : files) { + fileInfo.setFile(file); + QString suffix = fileInfo.suffix(); + if(formats.contains(suffix.toUtf8())) + imagePaths.push_back(path + "/" + file); + } + if(!imagePaths.empty()) { + path = imagePaths[0]; + timer = new QTimer(this); + connect(timer, &QTimer::timeout, this, [&]{ + imageIndex = (imageIndex + 1) % imagePaths.size(); + path = imagePaths[imageIndex]; + lastPath = imagePaths[imageIndex - 1 < 0 ? imagePaths.size() - 1 : imageIndex - 1]; + Q_EMIT imagePathChanged(path); + }); + timer->start(interval * 1000); + Q_EMIT imagePathChanged(path); + } + } +} + +void ScreenSaver::stopSwitchImages() +{ + if(timer && timer->isActive()) + timer->stop(); +} + +bool ScreenSaver::timerStatus() +{ + return timer->isActive(); +} + +QDebug &operator<<(QDebug debug, const ScreenSaver &screensaver) +{ + QString modes[] = {"blank-only", "random", "single", "image","default-ukui","default-ukui-custom"}; + QString effects[] = {"none", "fade-in-out"}; + debug.nospace()<< "screensaver: "<< modes[screensaver.mode]; + switch(screensaver.mode) { + case SAVER_BLANK_ONLY: + break; + case SAVER_RANDOM: + case SAVER_SINGLE: + debug.nospace() << screensaver.path; + break; + case SAVER_IMAGE: + debug.nospace() << screensaver.path << effects[screensaver.effect] << screensaver.interval; + case SAVER_DEFAULT: + debug.nospace() << screensaver.path << effects[screensaver.effect] << screensaver.interval; + case SAVER_DEFAULT_CUSTOM: + debug.nospace() << screensaver.path << effects[screensaver.effect] << screensaver.interval; + default: + debug.nospace() <<"screensaver path not exists"; + } + + return debug.maybeSpace(); +} + diff --git a/src/screensaver.h b/src/screensaver.h new file mode 100644 index 0000000..d3f2ad3 --- /dev/null +++ b/src/screensaver.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#ifndef SCREENSAVER_H +#define SCREENSAVER_H + +#include +#include +#include + +enum SaverMode +{ + SAVER_BLANK_ONLY = 0, + SAVER_RANDOM, + SAVER_SINGLE, + SAVER_IMAGE, + SAVER_DEFAULT, + SAVER_DEFAULT_CUSTOM +}; + +enum TransitionEffect +{ + TRANSITION_NONE, + TRANSITION_FADE_IN_OUT +}; + +class ScreenSaver : public QObject +{ + Q_OBJECT +public: + SaverMode mode; + //path is a directory or a file path if mode is SAVER_IMAGE + QString path; + QString lastPath; + + //for images saver + TransitionEffect effect; + int interval; + +private: + int imageIndex; + QTimer *timer; + QStringList imagePaths; + +Q_SIGNALS: + void imagePathChanged(const QString &path); + +public: + explicit ScreenSaver( QObject *parent=nullptr); + explicit ScreenSaver(const ScreenSaver &screensaver); + ScreenSaver(ScreenSaver &&screensaver) noexcept; + bool exists(); + void startSwitchImages(); + void stopSwitchImages(); + bool timerStatus(); + friend QDebug &operator<<(QDebug stream, const ScreenSaver &screensaver); +}; + +Q_DECLARE_METATYPE(ScreenSaver) + +QDebug &operator<<(QDebug stream, const ScreenSaver &screensaver); + +#endif // SCREENSAVER_H diff --git a/src/screensaverwidget.cpp b/src/screensaverwidget.cpp new file mode 100644 index 0000000..e194c54 --- /dev/null +++ b/src/screensaverwidget.cpp @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#include "screensaverwidget.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +ScreenSaverWidget::ScreenSaverWidget(ScreenSaver *screensaver, QWidget *parent) + : QWidget(parent), + timer(nullptr), + screensaver(screensaver), + closing(false) +{ + qDebug() << "ScreenSaverWidget::ScreenSaverWidget"; + setMouseTracking(true); + setFocus(); + this->installEventFilter(this); + + QPalette pal(palette()); + pal.setColor(QPalette::Background, Qt::black); //设置背景黑色 + setAutoFillBackground(true); + setPalette(pal); + + switch(screensaver->mode) { + case SAVER_RANDOM: + case SAVER_SINGLE: + embedXScreensaver(screensaver->path); + break; + case SAVER_BLANK_ONLY: + break; + case SAVER_IMAGE: + { + setAutoFillBackground(true); + screensaver->startSwitchImages(); + + QPalette plt; + plt.setBrush(QPalette::Background, Qt::black); + setPalette(plt); + + connect(screensaver, &ScreenSaver::imagePathChanged, + this, &ScreenSaverWidget::onBackgroundChanged); + break; + } + case SAVER_DEFAULT: + embedXScreensaver(screensaver->path); + break; + case SAVER_DEFAULT_CUSTOM: + embedXScreensaver(screensaver->path); + break; + default: + break; + } + + show(); +} + +ScreenSaverWidget::~ScreenSaverWidget() +{ + +} + +void ScreenSaverWidget::closeEvent(QCloseEvent *event) +{ + qDebug() << "ScreenSaverWidget::closeEvent---beginStop"; + if(process.state() != QProcess::NotRunning) + process.kill(); + + if(!closing){ + closing = true; + screensaver->stopSwitchImages(); + delete screensaver; + if(timer && timer->isActive()) + timer->stop(); + } + qDebug() << "ScreenSaverWidget::closeEvent---endStop"; + return QWidget::closeEvent(event); +} + +void ScreenSaverWidget::paintEvent(QPaintEvent *event) +{ + if(!screensaver->exists()) + { + QPainter painter(this); + painter.fillRect(geometry(), Qt::black); + } + if(screensaver->mode == SAVER_IMAGE) { + switch(screensaver->effect) { + case TRANSITION_NONE: + { + QPixmap pixmap(screensaver->path); + pixmap.scaled(size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + QPainter painter(this); + painter.drawPixmap(geometry(), pixmap); + break; + } + case TRANSITION_FADE_IN_OUT: + { + QPainter painter(this); + QPixmap pixmap1(screensaver->lastPath); + pixmap1.scaled(size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + painter.setOpacity(opacity); + painter.drawPixmap(geometry(), pixmap1); + + QPixmap pixmap(screensaver->path); + pixmap.scaled(size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + painter.setOpacity(1 - opacity); + painter.drawPixmap(geometry(), pixmap); + break; + } + } + + + } + return QWidget::paintEvent(event); +} + +bool ScreenSaverWidget::eventFilter(QObject *obj, QEvent *event) +{ + if(event->type() == 23) + { + XSetInputFocus(QX11Info::display(),this->winId(),RevertToParent,CurrentTime); + } + return false; +} + +/* Embed xscreensavers */ +void ScreenSaverWidget::embedXScreensaver(const QString &path) +{ + QString cmd = path + " -window-id " + QString::number(winId()); + if(process.state() == QProcess::NotRunning) + process.start(cmd); +} + + + +void ScreenSaverWidget::onBackgroundChanged(const QString &/*path*/) +{ + switch(screensaver->effect) { + case TRANSITION_NONE: + repaint(); + break; + case TRANSITION_FADE_IN_OUT: + opacity = 1.0; + timer = new QTimer(this); + connect(timer, &QTimer::timeout, this, [&]{ + opacity -= 0.1; + if(opacity <= 0) + timer->stop(); + else + repaint(); + + }); + timer->start(50); + break; + } +} + diff --git a/src/screensaverwidget.h b/src/screensaverwidget.h new file mode 100644 index 0000000..a76d428 --- /dev/null +++ b/src/screensaverwidget.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#ifndef SCREENSAVERWIDGET_H +#define SCREENSAVERWIDGET_H + +#include +#include +#include "screensaver.h" + +class ScreenSaverWidget : public QWidget +{ + Q_OBJECT +public: + ScreenSaverWidget(ScreenSaver *screensaver, + QWidget *parent = nullptr); + ~ScreenSaverWidget() ; + +protected: + void closeEvent(QCloseEvent *); + void paintEvent(QPaintEvent *event); + bool eventFilter(QObject *obj, QEvent *event); +private: + void embedXScreensaver(const QString &path); + +private Q_SLOTS: + void onBackgroundChanged(const QString &path); + +private: + QTimer *timer; + ScreenSaver *screensaver; + bool closing; + float opacity; + QProcess process; +}; + +#endif // SCREENSAVERWIDGET_H diff --git a/src/sessionwatcher.cpp b/src/sessionwatcher.cpp new file mode 100644 index 0000000..c97d7b8 --- /dev/null +++ b/src/sessionwatcher.cpp @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#include "sessionwatcher.h" +#include +#include +#include +#include "types.h" + +#define GSETTINGS_SCHEMA_SCREENSAVER "org.ukui.screensaver" +#define KEY_IDLE_DELAY "idleDelay" +#define KEY_IDLE_LOCK "idleLock" +#define KEY_IDLE_ACTIVATION_ENABLED "idleActivationEnabled" +#define KEY_IDLE_LOCK_ENABLED "idleLockEnabled" +#define TIME_TYPE_SCHEMA "org.ukui.control-center.panel.plugins" + +SessionWatcher::SessionWatcher(QObject *parent) : QObject(parent) +{ + sessionPath = qgetenv("XDG_SESSION_PATH"); + + QDBusInterface *interface = new QDBusInterface( + SM_DBUS_SERVICE, + SM_DBUS_PATH, + SM_DBUS_INTERFACE, + QDBusConnection::sessionBus()); + + connect(interface, SIGNAL(StatusChanged(unsigned int)), + this, SLOT(onStatusChanged(unsigned int))); + + QDBusInterface *displayManagerInterface = new QDBusInterface( + DM_DBUS_SERVICE, + DM_DBUS_PATH, + DM_DBUS_INTERFACE, + QDBusConnection::systemBus()); + connect(displayManagerInterface, SIGNAL(SessionRemoved(QDBusObjectPath)), + this, SLOT(onSessionRemoved(QDBusObjectPath))); + + settings_delay = new QGSettings(GSETTINGS_SCHEMA_SCREENSAVER, "", this); + connect(settings_delay, &QGSettings::changed, + this, &SessionWatcher::onConfigurationDelayChanged); + idleDelay = settings_delay->get("idle-delay").toInt(); + + settings_lock = new QGSettings(GSETTINGS_SCHEMA_SCREENSAVER, "", this); + connect(settings_lock, &QGSettings::changed, + this, &SessionWatcher::onConfigurationLockChanged); + idleLock = settings_lock->get("idle-lock").toInt(); + + QString userName = getenv("USER"); + QString configPath; + configPath = QString("/var/lib/lightdm-data/%1/ukui-greeter.conf").arg(userName); + QFile fileConf(configPath); + if (!fileConf.exists()) { + QFile file(configPath); + file.setPermissions(QFile::WriteUser | QFile::ReadUser | QFile::WriteOther | QFile::ReadOther); + } + configSettings = new QSettings(configPath, QSettings::IniFormat, this); + if(QGSettings::isSchemaInstalled(TIME_TYPE_SCHEMA)) + timegsettings = new QGSettings(TIME_TYPE_SCHEMA,"",this); + int timeType = timegsettings->get("hoursystem").toInt(); + setValue("timeType",timeType); + QString dateType = timegsettings->get("date").toString(); + setValue("dateType",dateType); + connect(timegsettings, &QGSettings::changed, + this, &SessionWatcher::onConfigurationTimeTpChanged); + +// activationEnabled_Key = new QGSettings(GSETTINGS_SCHEMA_SCREENSAVER, "", this); +// connect(activationEnabled_Key, &QGSettings::changed, +// this, &SessionWatcher::activationEnabledChanged); +// idleActivationEnabled = activationEnabled_Key->get("idle-activation-enabled").toBool(); + +// lockEnabled_Key = new QGSettings(GSETTINGS_SCHEMA_SCREENSAVER, "", this); +// connect(lockEnabled_Key, &QGSettings::changed, +// this, &SessionWatcher::lockEnabledChanged); +// lockEnabled = lockEnabled_Key->get("idle-lock-enabled").toBool(); +// qDebug()<get("idle-delay").toInt(); + } +} + +void SessionWatcher::onConfigurationLockChanged(QString key) +{ + if(key == KEY_IDLE_LOCK){ + idleLock = settings_lock->get("idle-lock").toInt(); + } +} + +void SessionWatcher::onConfigurationTimeTpChanged(QString key) +{ + if(key == "hoursystem"){ + int timeType = timegsettings->get("hoursystem").toInt(); + setValue("timeType",timeType); + } + if(key == "date"){ + qDebug()<<"jinlaile~~~~~~~~~~~~~~key="<get("date").toString(); + qDebug()<<"lsadjfldsa"<beginGroup("Greeter"); + configSettings->setValue(key, value); + configSettings->endGroup(); +} + +//void SessionWatcher::activationEnabledChanged(QString key) +//{ +// if(key == KEY_IDLE_ACTIVATION_ENABLED){ +// idleActivationEnabled = activationEnabled_Key->get("idle-activation-enabled").toBool(); +// } +//} + +//void SessionWatcher::lockEnabledChanged(QString key) +//{ +// if(key == KEY_IDLE_LOCK_ENABLED){ +// lockEnabled = lockEnabled_Key->get("idle-lock-enabled").toBool(); +// } +//} + +void SessionWatcher::onStatusChanged(unsigned int status) +{ + if(status == SESSION_IDLE) { + if(idleDelay != -1){ + if(!m_timer){ + m_timer = new QTimer(this); + connect(m_timer, &QTimer::timeout, this, [&]{ + Q_EMIT sessionIdle(); + m_timer->stop(); + }); + } + int time = (idleDelay - 1)*60000; + if(time<0) + time = 0; + + m_timer->start(time); + } + + if(idleLock != -1){ + if(!m_timer2){ + m_timer2 = new QTimer(this); + connect(m_timer2, &QTimer::timeout, this, [&]{ + Q_EMIT sessionLockIdle(); + m_timer2->stop(); + }); + } + int lockTime = (idleLock - 1)*60000; + if(lockTime<0) + lockTime = 0; + + m_timer2->start(lockTime); + } + + + }else if(status == SESSION_AVAILABLE){ + if(m_timer && m_timer->isActive()){ + m_timer->stop(); + } + if(m_timer2 && m_timer2->isActive()){ + m_timer2->stop(); + } + } +} + +void SessionWatcher::onSessionRemoved(const QDBusObjectPath &objectPath) +{ + //如果session注销了,则结束进程 + if(objectPath.path() == sessionPath) + exit(0); +} diff --git a/src/sessionwatcher.h b/src/sessionwatcher.h new file mode 100644 index 0000000..9b202d1 --- /dev/null +++ b/src/sessionwatcher.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#ifndef SESSIONWATCHER_H +#define SESSIONWATCHER_H + +#include +#include +#include +#include +#include +#include + +class SessionWatcher : public QObject +{ + Q_OBJECT +public: + explicit SessionWatcher(QObject *parent = nullptr); + void setValue(const QString &key, const QVariant &value); +Q_SIGNALS: + void sessionIdle(); + void sessionLockIdle(); + +private Q_SLOTS: + void onStatusChanged(unsigned int status); + void onSessionRemoved(const QDBusObjectPath &objectPath); + void onConfigurationDelayChanged(QString key); + void onConfigurationLockChanged(QString key); + void onConfigurationTimeTpChanged(QString key); +// void activationEnabledChanged(QString key); +// void lockEnabledChanged(QString key); +private: + QString sessionPath; + QGSettings *settings_delay; + QGSettings *settings_lock; + QGSettings *activationEnabled_Key; + QGSettings *lockEnabled_Key; + QGSettings *timegsettings; + QSettings *configSettings; + bool idleActivationEnabled; + bool lockEnabled; + int idleDelay; + int idleLock; + QTimer *m_timer = nullptr; + QTimer *m_timer2 = nullptr; +}; + +#endif // SESSIONWATCHER_H diff --git a/src/surewindow.cpp b/src/surewindow.cpp new file mode 100644 index 0000000..7633f15 --- /dev/null +++ b/src/surewindow.cpp @@ -0,0 +1,32 @@ +#include "surewindow.h" +#include "ui_surewindow.h" +#include +SureWindow::SureWindow(QWidget *parent) : + QWidget(parent), + ui(new Ui::SureWindow) +{ + ui->setupUi(this); + ui->tipLabel->setText(tr("Multiple users are logged in at the same time.Are you sure " + "you want to reboot this system?")); + ui->tipLabel->setStyleSheet("color:white;font:14pt;"); + ui->cancelBtn->setStyleSheet("QPushButton{background: rgba(255, 255, 255, 0.2);border-radius: 8px;}" + "QPushButton:hover{background: rgba(255, 255, 255, 0.4);border-radius: 8px;}" + "QPushButton:pressed {background: rgba(255, 255, 255, 0.3);border-radius: 8px;}"); + ui->confirmBtn->setStyleSheet("QPushButton{background: rgba(255, 255, 255, 0.2);border-radius: 8px;}" + "QPushButton:hover{background: rgba(255, 255, 255, 0.25);border-radius: 8px;}" + "QPushButton:pressed {background: rgba(255, 255, 255, 0.3);border-radius: 8px;}"); + connect(ui->cancelBtn, &QPushButton::clicked, this, [&]() { emit cantelButtonclicked(); }); + connect(ui->confirmBtn, &QPushButton::clicked, this, [&]() { emit confirmButtonclicked(); }); +} + +SureWindow::~SureWindow() +{ + delete ui; + + +} + +void SureWindow::setText(const QString tips) +{ + ui->tipLabel->setText(tips); +} diff --git a/src/surewindow.h b/src/surewindow.h new file mode 100644 index 0000000..89871ff --- /dev/null +++ b/src/surewindow.h @@ -0,0 +1,28 @@ +#ifndef SUREWINDOW_H +#define SUREWINDOW_H + +#include + +namespace Ui { +class SureWindow; +} + +class SureWindow : public QWidget +{ + Q_OBJECT + +public: + explicit SureWindow(QWidget *parent = nullptr); + ~SureWindow(); + void setText(const QString tips); + +private: + Ui::SureWindow *ui; + void on_cancelBtn_Clicked(); + +Q_SIGNALS: + void cantelButtonclicked(); + void confirmButtonclicked(); +}; + +#endif // SUREWINDOW_H diff --git a/src/surewindow.ui b/src/surewindow.ui new file mode 100644 index 0000000..0f921d5 --- /dev/null +++ b/src/surewindow.ui @@ -0,0 +1,163 @@ + + + SureWindow + + + + 0 + 0 + 821 + 631 + + + + Form + + + + 46 + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + Noto Sans CJK SC + + + + TextLabel + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + 24 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 96 + 36 + + + + + 96 + 36 + + + + Cancel + + + + + + + + 96 + 36 + + + + + 96 + 36 + + + + Confirm + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/src/switchbuttongroup.cpp b/src/switchbuttongroup.cpp new file mode 100644 index 0000000..dcb085d --- /dev/null +++ b/src/switchbuttongroup.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Authors: ZHAI Kang-ning +**/ +#include "switchbuttongroup.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +SwitchButtonGroup::SwitchButtonGroup(QWidget *parent) : QWidget(parent), + m_checkedId(0) +{ + this->resize(264, 48); + m_bg_switch = new QButtonGroup(this); + m_pb_password = new QPushButton(tr("uEduPWD"), this); + m_pb_qrCode = new QPushButton(tr("Wechat"), this); + m_pb_password->setIcon(QIcon(":/image/assets/intel/pin.png")); + m_pb_qrCode->setIcon(QIcon(":/image/assets/intel/wechat.png")); + + m_pb_password->setCheckable(true); + m_pb_qrCode->setCheckable(true); + + m_pb_password->setStyleSheet("font-size:16px;color:#ffffff"); + m_pb_qrCode->setStyleSheet("font-size:16px;color:#ffffff"); + + m_bg_switch->addButton(m_pb_password, 0); + m_bg_switch->addButton(m_pb_qrCode, 1); + + QObject::connect(m_pb_password, &QPushButton::clicked, this, &SwitchButtonGroup::parseButtonStatus); + QObject::connect(m_pb_qrCode, &QPushButton::clicked, this, &SwitchButtonGroup::parseButtonStatus); + + initUI(); +} + +void SwitchButtonGroup::initUI() +{ + QHBoxLayout *hbLayout = new QHBoxLayout(this); + hbLayout->setContentsMargins(0, 0, 0, 0); + hbLayout->addSpacerItem(new QSpacerItem(40,20,QSizePolicy::Expanding, QSizePolicy::Maximum)); + hbLayout->addWidget(m_pb_password); + hbLayout->addSpacerItem(new QSpacerItem(64,20,QSizePolicy::Expanding, QSizePolicy::Maximum)); + + QLine *s = new QLine(this->width()/2, 0, this->width(), this->height()/2); +// hbLayout->addChildWidget(s); + + hbLayout->addWidget(m_pb_qrCode); + hbLayout->addSpacerItem(new QSpacerItem(40,20,QSizePolicy::Expanding, QSizePolicy::Maximum)); +} + +void SwitchButtonGroup::parseButtonStatus() +{ +// qDebug() << "---------------------------parseButtonStatus check id=" << m_bg_switch->checkedId(); + if (m_checkedId == m_bg_switch->checkedId()) + return; + m_checkedId = m_bg_switch->checkedId(); +// switch (m_checkedId) { +// case 0: +// m_pb_password->setStyleSheet("font-size:16px;color:#2fb4e8"); +// m_pb_qrCode->setStyleSheet("font-size:16px;color:#ffffff"); +// break; +// case 1: +// m_pb_qrCode->setStyleSheet("font-size:16px;color:#2fb4e8"); +// m_pb_password->setStyleSheet("font-size:16px;color:#ffffff"); +// break; +// default: +// break; +// } + repaint(); + emit onSwitch(m_checkedId); +} + +void SwitchButtonGroup::paintEvent(QPaintEvent *event) +{ +// Q_UNUSED(event); +// QPainter painter(this); //QWidget为绘图设备,创建一个画刷对象,主要用到设置颜色和填充模式,brush,setBrush +// int width=this->width(); //获取QWidget 窗口的宽度 +// int height=this->height();//获取QWidget 窗口的高度 + +// painter.setPen(QColor("#ffffff")); +// painter.drawLine(this->width()/2, 0, this->width()/2, this->height()/2); + +// QPainterPath drawtriangle; //单独画三角形 + +// QPushButton *checkedButton; +// if (m_checkedId == 0) +// { +// checkedButton = m_pb_password; +// } else { +// checkedButton = m_pb_qrCode; +// } + +// drawtriangle.moveTo(checkedButton->x() + checkedButton->geometry().width()/2 -6, height);//左下角,第一点坐标为(0,height); +// drawtriangle.lineTo(checkedButton->x() + checkedButton->geometry().width()/2, height -12);//第二点坐标为(width/4,height/2) +// drawtriangle.lineTo(checkedButton->x() + checkedButton->geometry().width()/2 + 6, height);//第三点坐标为(width/2,height) +// painter.setPen(Qt::NoPen); +// painter.setBrush(QColor("#2fb4e8")); //填充绿色 +// painter.drawPath(drawtriangle); //绘制出图形 + + //设计稿变化重新做界面 + QPainter painter(this); + QColor color("#ffffff"); + color.setAlphaF(0.15); + painter.setRenderHint(QPainter::Antialiasing); + QPen pen(color); + painter.setPen(pen); + painter.setBrush(color); + + if(m_checkedId == 0) { + QRect rect1(4, 0, 120, 48); + painter.drawRoundedRect(rect1, 24, 24); + } else { + QRect rect2(140, 0, 120, 48); + painter.drawRoundedRect(rect2, 24, 24); + } +} diff --git a/src/switchbuttongroup.h b/src/switchbuttongroup.h new file mode 100644 index 0000000..1a2ace7 --- /dev/null +++ b/src/switchbuttongroup.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Authors: ZHAI Kang-ning +**/ +#ifndef SWITCHBUTTONGROUP_H +#define SWITCHBUTTONGROUP_H + +#include +#include +#include + +class SwitchButtonGroup : public QWidget +{ + Q_OBJECT +public: + explicit SwitchButtonGroup(QWidget *parent = nullptr); + +Q_SIGNALS: + void onSwitch(int id); + +private Q_SLOTS: + void parseButtonStatus(); + +private: + void initUI(); + + int m_checkedId;//上一次选中的按钮id + QButtonGroup *m_bg_switch; + QPushButton *m_pb_password; + QPushButton *m_pb_qrCode; + +protected: + void paintEvent(QPaintEvent *event) override; +}; + +#endif // SWITCHBUTTONGROUP_H diff --git a/src/tabletlockwidget.cpp b/src/tabletlockwidget.cpp new file mode 100644 index 0000000..82e4932 --- /dev/null +++ b/src/tabletlockwidget.cpp @@ -0,0 +1,634 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Authors: ZHAI Kang-ning +**/ +#include +#include +#include +#include +//#include +//#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tabletlockwidget.h" +#include "authdialog.h" +#include "virtualkeyboard.h" +#include "users.h" +#include "displaymanager.h" +#include "weathermanager.h" +#include "powermanager.h" +#include "digitalauthdialog.h" +#include "switchbuttongroup.h" +#include "wechatauthdialog.h" +#include "verificationwidget.h" +#include "verticalVerificationwidget.h" +#include "common.h" +#include "eduplatforminterface.h" +#include "accountsinterface.h" +#include "kylinnm.h" + +#include + +float tablescale; +TabletLockWidget::TabletLockWidget(QWidget *parent) : + QWidget(parent), + displayManager(new DisplayManager(this)), + m_switchButtonGroup(new SwitchButtonGroup(this)), + m_digitalAuthDialog(new DigitalAuthDialog(this)), + m_weChatAuthDialog(new WeChatAuthDialog(this)), + m_pb_powerManager(new QPushButton(this)), + m_pb_networkManager(new QPushButton(this)), + m_vKeyboardPB(new QPushButton(this)), + m_vKeyboard(new VirtualKeyboard(this)), + m_cancelOrBack(new QPushButton(this)), + m_pb_skip(new QPushButton(this)), + m_verificationWidget(new VerificationWidget(this)), + m_verticalVerificationWidget(new VerticalVerificationWidget(this)), + m_authType(0), + m_eduPlatformInterface(nullptr), + m_kylinNM(new KylinNM(this)) +{ + tablescale = 1.0; + m_kylinNM->installEventFilter(this); + m_digitalAuthDialog->installEventFilter(this); + m_switchButtonGroup->installEventFilter(this); + m_vKeyboard->installEventFilter(this); + this->installEventFilter(this); + + powermanager = new PowerManager(this); + powermanager->hide(); + + initUI(); + initConnect(); + onSwitchPage(); +} + +TabletLockWidget::~TabletLockWidget() +{ +} + +void TabletLockWidget::closeEvent(QCloseEvent *event) +{ + qDebug() << "TabletLockWidget::closeEvent"; + m_digitalAuthDialog->close(); + m_weChatAuthDialog->close(); + m_switchButtonGroup->close(); + m_verificationWidget->close(); + m_verticalVerificationWidget->close(); + m_kylinNM->close(); + return QWidget::closeEvent(event); +} + + +bool TabletLockWidget::eventFilter(QObject *obj, QEvent *event) +{ + if (obj == m_vKeyboard || obj == m_kylinNM) + return true; + + if (event->type() == QEvent::MouseButtonPress && m_kylinNM->isVisible()) + { + m_kylinNM->hide(); + if(powermanager->isHidden()) //非电源会话界面则刷新,否则只网络弹窗 + onSwitchPage(); + } + + return QWidget::eventFilter(obj, event); +} + +void TabletLockWidget::startAuth() +{ + m_digitalAuthDialog->startAuth(); +} + +void TabletLockWidget::stopAuth() +{ + +} + +void TabletLockWidget::initUI() +{ + //电源管理 + m_pb_powerManager->setIcon(QIcon(":/image/assets/intel/powerManager.png")); + m_pb_powerManager->setFixedSize(48,48); + m_pb_powerManager->setIconSize(QSize(24,24)); + m_pb_powerManager->setFocusPolicy(Qt::NoFocus); + m_pb_powerManager->setStyleSheet("QPushButton:Hover{border-radius:24px;background-color:rgba(255, 255, 255, 0.15);}" + "QPushButton:Pressed{border-radius:24px;background-color:rgba(255, 255, 255, 0.05);}"); + m_pb_powerManager->installEventFilter(this); + + connect(m_pb_powerManager,&QPushButton::clicked + ,this,&TabletLockWidget::showPowerManager); + + //网络设置 + updateNetIcon(m_kylinNM->getConnectStatus()); + m_pb_networkManager->setFixedSize(48,48); + m_pb_networkManager->setIconSize(QSize(24,24)); + m_pb_networkManager->setFocusPolicy(Qt::NoFocus); + m_pb_networkManager->setStyleSheet("QPushButton:Hover{border-radius:24px;background-color:rgba(255, 255, 255, 0.15);}" + "QPushButton:Pressed{border-radius:24px;background-color:rgba(255, 255, 255, 0.05);}"); + m_pb_networkManager->installEventFilter(this); + + connect(m_pb_networkManager,&QPushButton::clicked + ,this,&TabletLockWidget::showNetManager); + + //虚拟键盘 + m_vKeyboard->hide(); + + connect(m_vKeyboard, &VirtualKeyboard::aboutToClose, + m_vKeyboard, &VirtualKeyboard::hide); + + m_vKeyboardPB->setIcon(QIcon(":/image/assets/intel/keyboard.png")); + m_vKeyboardPB->setFixedSize(48,48); + m_vKeyboardPB->setIconSize(QSize(24,24)); + m_vKeyboardPB->setFocusPolicy(Qt::NoFocus); + m_vKeyboardPB->installEventFilter(this); + m_vKeyboardPB->setStyleSheet("QPushButton:Hover{border-radius:24px;background-color:rgba(255, 255, 255, 0.15);}" + "QPushButton:Pressed{border-radius:24px;background-color:rgba(255, 255, 255, 0.05);}"); + + connect(m_vKeyboardPB,&QPushButton::clicked + ,this,&TabletLockWidget::showVirtualKeyboard); + + //取消或返回 + m_cancelOrBack->resize(88, 48); + m_cancelOrBack->setIcon(QIcon(":/image/assets/intel/cancel.png")); + m_cancelOrBack->setStyleSheet("QPushButton{font-size:16px;color:#ffffff;}" + "QPushButton:Hover{border-radius:24px;background-color:rgba(255, 255, 255, 0.15);}" + "QPushButton:Pressed{border-radius:24px;background-color:rgba(255, 255, 255, 0.05);}"); + m_cancelOrBack->setText(tr("Cancel")); + m_cancelOrBack->setIconSize(QSize(24,24)); + m_cancelOrBack->setFocusPolicy(Qt::NoFocus); + m_cancelOrBack->installEventFilter(this); + connect(m_digitalAuthDialog, &DigitalAuthDialog::requestPasswordReset, this, [=]{ + m_cancelOrBack->setIcon(QIcon(":/image/assets/intel/back.png")); + m_cancelOrBack->setText(tr("Back")); + m_pageType = pageType::check; + m_pb_skip->hide(); + onSwitchPage(); + }); + connect(m_cancelOrBack, &QPushButton::clicked, this, [=]{ + if(powermanager->isVisible()) + { + powermanager->hide(); + onSwitchPage(); + // m_digitalAuthDialog->reset(); + if(m_pageType != pageType::check) + { + m_cancelOrBack->setIcon(QIcon(":/image/assets/intel/cancel.png")); + m_cancelOrBack->setText(tr("Cancel")); + } + return; + } + + if(m_pageType == pageType::pinLogin && + m_digitalAuthDialog->getStatus() == DigitalAuthDialog::LoginType::SECONDCONFIRMATION) + { + // 二次确认页返回,则显示重置密码页 + m_digitalAuthDialog->showResetPasswordPage(); + return; + } + + if(m_pageType == pageType::check) { + m_pageType = pageType::pinLogin; + m_digitalAuthDialog->reset(); + + onSwitchPage(); + m_cancelOrBack->setIcon(QIcon(":/image/assets/intel/cancel.png")); + m_cancelOrBack->setText(tr("Cancel")); + return; + } + + if(m_digitalAuthDialog->getStatus() == 0) + { + //Todo 1、锁屏 -》息屏 + m_digitalAuthDialog->reset(); + Q_EMIT blackSaver(); +#ifdef USE_INTEL + powermanager->hibernate(); +#endif + } else { + m_digitalAuthDialog->reset(); + m_cancelOrBack->setIcon(QIcon(":/image/assets/intel/cancel.png")); + m_cancelOrBack->setText(tr("Cancel")); + } + }); + + // 重置密码时跳过按钮 + m_pb_skip->setText(tr("Skip")); + m_pb_skip->resize(88, 48); + m_pb_skip->setStyleSheet("QPushButton{font-size:16px;color:#ffffff;}" + "QPushButton:Hover{border-radius:24px;background-color:rgba(255, 255, 255, 0.15);}" + "QPushButton:Pressed{border-radius:24px;background-color:rgba(255, 255, 255, 0.05);}"); + m_pb_skip->hide(); + + //网络模块 + m_kylinNM->hide(); +} + +/** + * @brief TabletLockWidget::initConnect + * 建立信号与槽的绑定 + */ +void TabletLockWidget::initConnect() +{ + connect(m_digitalAuthDialog, &DigitalAuthDialog::authenticateCompete, + this, &TabletLockWidget::closed); + connect(m_digitalAuthDialog, &DigitalAuthDialog::setPinCode, this, [=](QString pinCode){ + //Todo + QString currentPinCode; + AccountsInterface::getInstance()->GetAccountPincode(getenv("USER"), currentPinCode); + qDebug() << "currentPinCode:" << currentPinCode << "--old pinCode:" << pinCode; + if(!currentPinCode.isEmpty() && currentPinCode == pinCode) + { + //与原密码相同 + m_digitalAuthDialog->showErrorMessage(tr("New password is the same as old")); + m_digitalAuthDialog->showResetPasswordPage(); + return; + } + + DBusMsgCode code = AccountsInterface::getInstance()->SetAccountPincode(getenv("USER"), pinCode); + if (code == DBusMsgCode::No_Error) { + qDebug() << "set pincode success"; + Q_EMIT closed(); + } else { + qWarning() << "设置失败" << code; + m_digitalAuthDialog->showErrorMessage(tr("Reset password error:%1").arg(code)); + } + }); + connect(m_weChatAuthDialog, &WeChatAuthDialog::authenticateCompete, this, &TabletLockWidget::closed); + connect(m_weChatAuthDialog, &WeChatAuthDialog::qRStatusChange, this, [=] (QString loginname, QString loginpwd, int curstatus){ + //QString loginname, QString loginpwd, int curstatus + if(getenv("USER") == loginname && m_pageType == pageType::wecharLogin) + { + Q_EMIT closed(); + } else { + qWarning() << "[error] Username does not match wechat<" << loginname << ">" << " local<" << getenv("USER")<<">"; + if (!loginname.isEmpty()){ + m_weChatAuthDialog->showErrorMessage(tr("Please scan by correct WeChat")); + m_weChatAuthDialog->onReset(); + } + } + }); + connect(m_switchButtonGroup, &SwitchButtonGroup::onSwitch, this, &TabletLockWidget::switchLoginType); + connect(m_verificationWidget, &VerificationWidget::pageMessage, this, [=](SwitchPage type, QList args) + { + m_pageType = pageType::pinLogin; + onSwitchPage(); + m_pb_skip->show(); + }); + connect(m_verticalVerificationWidget, &VerticalVerificationWidget::pageMessage, this, [=](SwitchPage type, QList args) + { + m_pageType = pageType::pinLogin; + onSwitchPage(); + m_pb_skip->show(); + }); + + connect(powermanager,SIGNAL(lock()) + ,this,SLOT(showPowerManager())); + + if(QGSettings::isSchemaInstalled("org.ukui.SettingsDaemon.plugins.tablet-mode")) + { + QGSettings *tm = new QGSettings("org.ukui.SettingsDaemon.plugins.tablet-mode", "", this); + //判断是否是平板模式 + if(tm->get("tablet-mode").toBool()){ + connect(m_kylinNM, &KylinNM::onLineEditClicked, this, [=](){ + //点击了输入密码的框框,需要弹出软键盘 + showVirtualKeyboard(true); + m_kylinNM->move(m_kylinNM->x(), height() - m_vKeyboard->height() - m_kylinNM->height() - 20); + }); + } + + //监听平板模式变化 + connect(tm, &QGSettings::changed, this, [=](QString key){ + if(key == "tablet-mode" || key == "tabletMode") { + disconnect(m_kylinNM, &KylinNM::onLineEditClicked, this, nullptr); + if(tm->get("tablet-mode").toBool()){ + connect(m_kylinNM, &KylinNM::onLineEditClicked, this, [=](){ + //点击了输入密码的框框,需要弹出软键盘 + showVirtualKeyboard(true); + m_kylinNM->move(m_kylinNM->x(), height() - m_vKeyboard->height() - m_kylinNM->height()); + }); + } + } + }); + } + + connect(m_kylinNM, &KylinNM::onConnectChanged, this,[=](){ + updateNetIcon(m_kylinNM->getConnectStatus()); + }); + + connect(m_vKeyboard, &VirtualKeyboard::aboutToClose, m_vKeyboard, [=](){ + if(m_vKeyboard->isHidden() && m_kylinNM->isVisible()) { + m_kylinNM->setGeometry(this->width() - m_kylinNM->width() - 20, + this->height() - m_kylinNM->height() - 100, + m_kylinNM->width(), + m_kylinNM->height()); + } + }); + + connect(m_pb_skip, &QPushButton::clicked, this, &TabletLockWidget::closed); + connect(m_digitalAuthDialog, &DigitalAuthDialog::switchToReset, this, [=](bool isReset){ + if(isReset){ + m_pb_skip->show(); + } else { + m_pb_skip->hide(); + } + }); +} + +void TabletLockWidget::switchLoginType(int position) +{ + if(position == 0) + { + //锁屏密码登录 + m_pageType = pageType::pinLogin; + } else if (position == 1) + { + m_pageType = pageType::wecharLogin; + //微信登录 + m_digitalAuthDialog->reset(); + m_cancelOrBack->setIcon(QIcon(":/image/assets/intel/cancel.png")); + m_cancelOrBack->setText(tr("Cancel")); + } + + onSwitchPage(); +} + +void TabletLockWidget::showPowerManager() +{ + if (powermanager->isVisible()) + { + powermanager->hide(); + onSwitchPage(); + if(m_pageType != pageType::check) + { + m_cancelOrBack->setIcon(QIcon(":/image/assets/intel/cancel.png")); + m_cancelOrBack->setText(tr("Cancel")); + } + Q_EMIT screenSaver(); + } else { + m_digitalAuthDialog->hide(); + m_weChatAuthDialog->hide(); + m_switchButtonGroup->hide(); + m_verificationWidget->hide(); + m_verticalVerificationWidget->hide(); + m_cancelOrBack->setIcon(QIcon(":/image/assets/intel/back.png")); + m_cancelOrBack->setText(tr("Back")); + m_pb_networkManager->hide(); + m_pb_powerManager->hide(); + m_vKeyboardPB->hide(); + m_vKeyboard->hide(); + powermanager->show(); + powermanager->setGeometry((width()-ITEM_WIDTH*4 - ITEM_SPACING*3)/2, + (height()-ITEM_HEIGHT)/2, + (ITEM_WIDTH*4 + ITEM_SPACING*3),ITEM_HEIGHT); + } +} + +void TabletLockWidget::showNetManager() +{ + if(m_kylinNM->isVisible()) + { + m_kylinNM->hide(); + } else { + m_digitalAuthDialog->releaseKeyboard(); + m_kylinNM->show(); + m_kylinNM->setGeometry(this->width() - m_kylinNM->width() - 20, + this->height() - m_kylinNM->height() - 100, + m_kylinNM->width(), + m_kylinNM->height()); + m_kylinNM->updateWifiList(); + } +} + +/* lockscreen follows cursor */ +void TabletLockWidget::resizeEvent(QResizeEvent *event) +{ + QSize size = event->size(); + //重新计算缩放比例 + tablescale = QString::number(size.width() / 1920.0, 'f', 1).toFloat(); + + if(tablescale > 1) + tablescale = 1; + + m_cancelOrBack->setGeometry(32, 40, m_cancelOrBack->width(), m_cancelOrBack->height()); + m_pb_skip->setGeometry(this->width() - m_pb_skip->width() - 32, 40, m_pb_skip->width(), m_pb_skip->height()); + + m_digitalAuthDialog->setGeometry((this->width()-m_digitalAuthDialog->width())/2, (this->height()-m_digitalAuthDialog->height())/2 + 40, + m_digitalAuthDialog->width(), m_digitalAuthDialog->height()); + m_weChatAuthDialog->setGeometry((this->width()-m_weChatAuthDialog->width())/2, (this->height()-m_digitalAuthDialog->height())/2 + 40, + m_weChatAuthDialog->width(), m_weChatAuthDialog->height()); + + m_switchButtonGroup->setGeometry((this->width()-m_switchButtonGroup->width())/2, this->height()-80, + m_switchButtonGroup->width(), m_switchButtonGroup->height()); + + if(isOrientation()) + m_verticalVerificationWidget->setGeometry((this->width()-m_verticalVerificationWidget->width())/2, (this->height()-m_verticalVerificationWidget->height())/2, + m_verticalVerificationWidget->width(), m_verticalVerificationWidget->height()); + else + m_verificationWidget->setGeometry((this->width()-m_verificationWidget->width())/2, (this->height()-m_verificationWidget->height())/2 - 65, + m_verificationWidget->width(), m_verificationWidget->height()); + if(m_verificationWidget->isVisible() || m_verticalVerificationWidget->isVisible()) + showVerificationPage(); + // XSetInputFocus(QX11Info::display(),this->winId(),RevertToParent,CurrentTime); + //右下角按钮,x,y的初始值代表距离右下角的距离。 + int x=19,y=76; + x = x + m_pb_powerManager->width(); + m_pb_powerManager->setGeometry(this->width() - x,this->height() - y, m_pb_powerManager->width(), m_pb_powerManager->height()); + m_pb_networkManager->setGeometry(this->width() - x - 64 * tablescale, this->height() - y, + m_pb_networkManager->width(),m_pb_networkManager->height()); + m_vKeyboardPB->setGeometry(this->width() - x - 128 * tablescale, this->height() - y, + m_vKeyboardPB->width(), m_vKeyboardPB->height()); + m_kylinNM->setGeometry(this->width() - m_kylinNM->width() - 20, + this->height() - m_kylinNM->height() - 100, + m_kylinNM->width(), + m_kylinNM->height()); + //更新软键盘位置 + setVirkeyboardPos(); + if(powermanager && powermanager->isVisible()) + { + powermanager->setGeometry((width()-ITEM_WIDTH*4 - ITEM_SPACING*3)/2, + (height()-ITEM_HEIGHT)/2, + (ITEM_WIDTH*4 + ITEM_SPACING*3),ITEM_HEIGHT); + } +} + +//重写重绘事件,比如动态更新文本位置等 +void TabletLockWidget::paintEvent(QPaintEvent *event) +{ +} + +void TabletLockWidget::onSwitchPage() +{ + bool isOnlyWeChatLogin = false; + EduPlatformInterface::getInstance()->CheckPincodeSet(getenv("USER"), isOnlyWeChatLogin); + if(isOnlyWeChatLogin && m_pageType == pageType::pinLogin) + { + m_pageType = pageType::wecharLogin; + } + + if(!m_pb_networkManager->isVisible()) + m_pb_networkManager->show(); + + if(!m_pb_powerManager->isVisible()) + m_pb_powerManager->show(); + + switch (m_pageType) { + case pageType::pinLogin: + m_digitalAuthDialog->show(); + m_digitalAuthDialog->grabKeyboard(); + m_switchButtonGroup->show(); + m_weChatAuthDialog->hide(); + m_verificationWidget->hide(); + m_verticalVerificationWidget->hide(); + m_vKeyboardPB->hide(); + m_vKeyboard->hide(); + break; + case pageType::wecharLogin: + m_digitalAuthDialog->hide(); + m_digitalAuthDialog->releaseKeyboard(); + m_weChatAuthDialog->show(); + if(isOnlyWeChatLogin) + { + m_switchButtonGroup->hide(); + } else { + m_switchButtonGroup->show(); + } + m_verificationWidget->hide(); + m_verticalVerificationWidget->hide(); + m_vKeyboardPB->hide(); + m_vKeyboard->hide(); + break; + case pageType::check: + m_digitalAuthDialog->hide(); + m_digitalAuthDialog->releaseKeyboard(); + m_weChatAuthDialog->hide(); + m_switchButtonGroup->hide(); + showVerificationPage(); + m_vKeyboardPB->show(); + break; + default: + break; + } +} + +QWidget* TabletLockWidget::getCurrentWidget() +{ + switch (m_pageType) { + case pageType::pinLogin: + return m_digitalAuthDialog; + case pageType::wecharLogin: + return m_weChatAuthDialog; + case pageType::check: + return m_verificationWidget; + default: + return m_digitalAuthDialog; + } +} + +void TabletLockWidget::showVirtualKeyboard(bool isShow) +{ + m_vKeyboard->setVisible(isShow || m_vKeyboard->isHidden()); + setVirkeyboardPos(); +} + +void TabletLockWidget::setVirkeyboardPos() +{ + if(m_vKeyboard) + { + m_vKeyboard->setGeometry(0 + 20, + height() - height()/3 + 10, + width() - 40 , height()/3 - 20); + m_vKeyboard->raise(); + m_vKeyboard->repaint(); + } +} + +bool TabletLockWidget::isOrientation() +{ + QRect wRect = QApplication::primaryScreen()->geometry(); + if(wRect.height() > wRect.width()) + return true; + return false; +} + +void TabletLockWidget::showVerificationPage() +{ + if(isOrientation()) + { + m_verificationWidget->hide(); + m_verticalVerificationWidget->show(); + } + else + { + m_verificationWidget->show(); + m_verticalVerificationWidget->hide(); + } +} + +//void TabletLockWidget::RecieveKey(int key) +//{ +// if(m_pageType == pageType::pinLogin ||m_pageType == pageType::check) +// m_digitalAuthDialog->RecieveKey(key); +//} + +void TabletLockWidget::updateNetIcon(int status) +{ + switch(status) { + case 0: + //有线 + m_pb_networkManager->setIcon(QIcon(":/image/assets/intel/icon-wired.png")); + break; + case 1: + //无线 + m_pb_networkManager->setIcon(QIcon(":/image/assets/intel/icon-wifi.png")); + break; + case 2: + //有线+无线 + m_pb_networkManager->setIcon(QIcon(":/image/assets/intel/icon-wired.png")); + break; + default: + //无连接 + m_pb_networkManager->setIcon(QIcon(":/image/assets/intel/icon-no-signal.png")); + } +} + +/** + * @Destoryed + * @brief TabletLockWidget::getEduPlatformInterface + * @return + */ +EduPlatformInterface* TabletLockWidget::getEduPlatformInterface() +{ + if(m_eduPlatformInterface == nullptr) + { + m_eduPlatformInterface = new EduPlatformInterface("cn.kylinos.SSOBackend", + "/cn/kylinos/SSOBackend", + QDBusConnection::systemBus(), + this); + } + + return m_eduPlatformInterface; +} diff --git a/src/tabletlockwidget.h b/src/tabletlockwidget.h new file mode 100644 index 0000000..12be613 --- /dev/null +++ b/src/tabletlockwidget.h @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Authors: ZHAI Kang-ning +**/ +#ifndef TABLETLOCKWIDGET_H +#define TABLETLOCKWIDGET_H + +#ifndef QT_NO_KEYWORDS +#define QT_NO_KEYWORDS +#endif + +#include +#include +#include +#include +#include +#include + +class VirtualKeyboard; +class PowerManager; +class AuthDialog; +class Users; +class UserItem; +class DisplayManager; +class QMenu; +class DigitalAuthDialog; +class SwitchButtonGroup; +class WeChatAuthDialog; +class VerificationWidget; +class VerticalVerificationWidget; +class EduPlatformInterface; +class KylinNM; + +class TabletLockWidget : public QWidget +{ + Q_OBJECT +public: + explicit TabletLockWidget(QWidget *parent = nullptr); + ~TabletLockWidget(); + + enum pageType{ + pinLogin = 0, + wecharLogin, + check + }; + + void closeEvent(QCloseEvent *event); + void startAuth(); + void stopAuth(); + bool exitSubWidget() { + return true; + }; +// void RecieveKey(int key); + +Q_SIGNALS: + void closed(); + void capsLockChanged(); + void screenSaver(); + void blackSaver(); + +private: + void initUI(); + void initConnect(); + + void onSwitchPage(); + QWidget* getCurrentWidget(); + EduPlatformInterface* getEduPlatformInterface(); + void updateNetIcon(int status); + + void paintEvent(QPaintEvent *event) override; + + bool isOrientation(); + void showVerificationPage(); + +private Q_SLOTS: + void showPowerManager(); + void showNetManager(); + void showVirtualKeyboard(bool isShow = false); + void setVirkeyboardPos(); + void switchLoginType(int position); + +protected: + bool eventFilter(QObject *obj, QEvent *event); + void resizeEvent(QResizeEvent *event); + +private: + AuthDialog *authDialog; + VirtualKeyboard *m_vKeyboard; + PowerManager *powermanager; + QTimer *timer; + QMenu *usersMenu; + Users *users; + DisplayManager *displayManager; + DigitalAuthDialog *m_digitalAuthDialog; + WeChatAuthDialog *m_weChatAuthDialog; + SwitchButtonGroup *m_switchButtonGroup; + QPushButton *m_pb_powerManager; + QPushButton *m_pb_networkManager; + QPushButton *m_cancelOrBack; + QPushButton *m_pb_skip; + QPushButton *m_vKeyboardPB; + VerificationWidget *m_verificationWidget; + VerticalVerificationWidget *m_verticalVerificationWidget; + EduPlatformInterface *m_eduPlatformInterface; + + KylinNM *m_kylinNM; + QWidget *m_NetManagerWidget; + + int m_authType; + int m_pageType = 0; +}; + +#endif // TABLETLOCKWIDGET_H diff --git a/src/test-accounts.cpp b/src/test-accounts.cpp new file mode 100644 index 0000000..4da700b --- /dev/null +++ b/src/test-accounts.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#include +#include "users.h" +#include + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + + Users users; + + qDebug() << users.getUserByName("kylin"); + + return 0; +} diff --git a/src/types.h b/src/types.h new file mode 100644 index 0000000..42d5522 --- /dev/null +++ b/src/types.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#ifndef TYPES_H +#define TYPES_H + +/* https://www.narf.ssji.net/~shtrom/wiki/projets/gnomescreensavernosession */ +enum SessionStatus +{ + SESSION_AVAILABLE = 0, + SESSION_INVISIBLE = 1, + SESSION_BUSY = 2, + SESSION_IDLE = 3 +}; + +enum ScreenStatus +{ + UNDEFINED = 0x00, + SCREEN_SAVER = 0x01, + SCREEN_LOCK = 0x02, + SCREEN_LOCK_AND_SAVER = 0x03, + SCREEN_BLACK = 0x04, +}; + +#define SM_DBUS_SERVICE "org.gnome.SessionManager" +#define SM_DBUS_PATH "/org/gnome/SessionManager/Presence" +#define SM_DBUS_INTERFACE "org.gnome.SessionManager.Presence" + +#define DM_DBUS_SERVICE "org.freedesktop.DisplayManager" +#define DM_DBUS_PATH "/org/freedesktop/DisplayManager" +#define DM_DBUS_INTERFACE "org.freedesktop.DisplayManager" +#define DM_SEAT_INTERFACE "org.freedesktop.DisplayManager.Seat" + +#define ACT_DBUS_SERVICE "org.freedesktop.Accounts" +#define ACT_DBUS_PATH "/org/freedesktop/Accounts" +#define ACT_DBUS_INTERFACE "org.freedesktop.Accounts" +#define ACT_USER_INTERFACE "org.freedesktop.Accounts.User" + +#define DBUS_PROP_INTERFACE "org.freedesktop.DBus.Properties" + +#define SS_DBUS_SERVICE "org.ukui.ScreenSaver" +#define SS_DBUS_PATH "/" +#define SS_DBUS_INTERFACE "org.ukui.ScreenSaver" + +#define BIO_ERROR -1 +#define BIO_FAILED 0 +#define BIO_SUCCESS 1 +#define BIO_IGNORE 2 + +#define BIOMETRIC_PAM "BIOMETRIC_PAM" +#define BIOMETRIC_IGNORE "BIOMETRIC_IGNORE" +#define BIOMETRIC_SUCCESS "BIOMETRIC_SUCCESS" +#define BIOMETRIC_FAILED "BIOMETRIC_FAILED" + +#define LG_DBUS_SERVICE "org.freedesktop.login1" +#define LG_DBUS_PATH "/org/freedesktop/login1" +#define LG_DBUS_INTERFACE "org.freedesktop.login1.Manager" + + + +#endif // TYPES_H diff --git a/src/ukui-screensaver-backend.cpp b/src/ukui-screensaver-backend.cpp new file mode 100644 index 0000000..63f6351 --- /dev/null +++ b/src/ukui-screensaver-backend.cpp @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#include +#include +#include +#include + +#include "interface.h" +#include "sessionwatcher.h" +#include "screensaveradaptor.h" +#include "types.h" + +#include +#include +#include +#include +#include + +#define POWER_TYPE_SCHENA "org.ukui.power-manager" + +void sig_chld(int /*signo*/) +{ + pid_t pid; + while( (pid = waitpid(-1, NULL, WNOHANG)) > 0) + qDebug() << "child" << pid << "terminated"; + return; +} + +int main(int argc, char *argv[]) +{ +// if(signal(SIGCHLD, sig_chld) == SIG_ERR) { +// perror("signal error"); +// exit(EXIT_FAILURE); +// } + initUkuiLog4qt("ukui-screensaver-backend"); + // 重启或关机时不被session关掉 + qunsetenv("SESSION_MANAGER"); + QCoreApplication a(argc, argv); + + // 检查该程序是否已经有实例在运行 + QDBusInterface *checkInterface = + new QDBusInterface("org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + QDBusConnection::sessionBus()); + QDBusReply ret = checkInterface->call("NameHasOwner", + "cn.kylinos.ScreenSaver"); + if(ret.value()) { + qDebug() << "There is an instance running"; + exit(EXIT_FAILURE); + } + + FILE *fp; + + // for PowerManager + fp = popen("xset s 0 0", "r"); + fclose(fp); +// Q_UNUSED(fp) + + QGSettings *powerSettings; + if(QGSettings::isSchemaInstalled(POWER_TYPE_SCHENA)){ + powerSettings = new QGSettings(POWER_TYPE_SCHENA,"",NULL); + QStringList keys = powerSettings->keys(); + if (keys.contains("lockSuspend")) { + bool ret = powerSettings->get("lockSuspend").toBool(); + if(ret){ + powerSettings->set("lock-suspend",false); + } + } + if (keys.contains("lockHibernate")) { + bool ret = powerSettings->get("lockHibernate").toBool(); + if(ret){ + powerSettings->set("lock-hibernate",false); + } + } + } + + // 注册DBus + Interface *interface = new Interface(); + ScreenSaverAdaptor adaptor(interface); + + QDBusConnection service = QDBusConnection::sessionBus(); + if(!service.registerService(SS_DBUS_SERVICE)) { + qDebug() << service.lastError().message(); + exit(EXIT_FAILURE); + } + if(!service.registerObject(SS_DBUS_PATH, SS_DBUS_SERVICE, &adaptor, + QDBusConnection::ExportAllSlots | + QDBusConnection::ExportAllSignals)) { + qDebug() << service.lastError().message(); + exit(EXIT_FAILURE); + } + qDebug() << service.baseService(); + + // 发送DBus信号 + SessionWatcher *watcher = new SessionWatcher; + QObject::connect(watcher, &SessionWatcher::sessionIdle, + interface, &Interface::onSessionIdleReceived); + + QObject::connect(watcher, &SessionWatcher::sessionLockIdle, + interface, &Interface::Lock); + + QObject::connect(watcher, &SessionWatcher::sessionIdle, + &a, [&]{ + QDBusMessage message = QDBusMessage::createSignal(SS_DBUS_PATH, + SS_DBUS_INTERFACE, + "SessionIdle"); + service.send(message); + //qDebug()<<"message="<start(3000); + + QObject::connect(checkInterface, SIGNAL(NameLost(QString)), + interface, SLOT(onNameLost(QString))); + + return a.exec(); +} diff --git a/src/ukui-screensaver-checkpass.cpp b/src/ukui-screensaver-checkpass.cpp new file mode 100644 index 0000000..6f48647 --- /dev/null +++ b/src/ukui-screensaver-checkpass.cpp @@ -0,0 +1,166 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +static int toParent = 0; +static int fromChild = 0; + +typedef struct pam_message PAM_MESSAGE; +typedef struct pam_response PAM_RESPONSE; + +static void +writeData(int fd, const void *buf, ssize_t count) +{ + if(write(fd, buf, count) != count) + printf("write to parent failed: %s\n",strerror(errno)); +} + +static void +writeString(int fd, const char *data) +{ + int length = data ? strlen(data) : -1; + writeData(fd, &length, sizeof(length)); + if(data) + writeData(fd, data, sizeof(char) * length); +} + +static int +readData(int fd, void *buf, size_t count) +{ + ssize_t nRead = read(fd, buf, count); + if(nRead < 0) + printf("read data failed: %s\n",strerror(errno)); + return nRead; +} + +static char * +readString(int fd) +{ + int length; + + if(readData(fd, &length, sizeof(length)) <= 0) + return NULL; + if(length <= 0) + length = 0; + char *value = (char *)malloc(sizeof(char) * (length + 1)); + readData(fd, value, length); + value[length] = '\0'; + + return value; +} + +static int +pam_conversation(int msgLength, const struct pam_message **msg, + PAM_RESPONSE **resp, void */*appData*/) +{ + PAM_RESPONSE *response = (PAM_RESPONSE*)calloc(msgLength,sizeof(PAM_RESPONSE)); + + int authComplete = 0; + writeData(toParent, (const void*)&authComplete, sizeof(authComplete)); + writeData(toParent, (const void*)&msgLength, sizeof(msgLength)); + //发送pam消息 + for(int i = 0; i < msgLength; i++) + { + const struct pam_message *m = msg[i]; + writeData(toParent, (const void *)&m->msg_style, sizeof(m->msg_style)); + writeString(toParent, m->msg); + } + //读取响应 + for(int i = 0; i < msgLength; i++) + { + PAM_RESPONSE *r = &response[i]; + readData(fromChild, &r->resp_retcode, sizeof(r->resp_retcode)); + r->resp = readString(fromChild); + } + *resp = response; + return PAM_SUCCESS; +} + +static void +_authenticate(const char *userName) +{ + printf("authenticate %s\n",userName); + + pam_handle_t *pamh = NULL; + char *newUser; + int ret; + int authRet; + struct pam_conv conv; + + conv.conv = pam_conversation; + conv.appdata_ptr = NULL; + + ret = pam_start("ukui-screensaver-qt", userName, &conv, &pamh); + if(ret != PAM_SUCCESS) + { + printf("failed to start PAM: = %s\n", pam_strerror(NULL, ret)); + } + + authRet = pam_authenticate(pamh, 0); + + ret = pam_get_item(pamh, PAM_USER, (const void **)&newUser); + if(ret != PAM_SUCCESS) + { + pam_end(pamh, 0); + printf("failed to get username\n"); + _exit(EXIT_FAILURE); + return ; + } + if(authRet == PAM_SUCCESS) + authRet = pam_acct_mgmt(pamh, 0); + if (authRet == PAM_NEW_AUTHTOK_REQD) + authRet = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK); + if(authRet != PAM_SUCCESS) + { + + } + + fprintf(stderr, "authentication result: %d\n", authRet); + + // 发送认证结果 + int authComplete = 1; + writeData(toParent, (const void*)&authComplete, sizeof(authComplete)); + writeData(toParent, (const void *)&authRet, sizeof(authRet)); + printf("--- 认证完成\n"); + + /* Stop if we didn't authenticated */ + if (authRet != PAM_SUCCESS) + { + pam_end(pamh, 0); + _exit(EXIT_FAILURE); + return ; + } + pam_end(pamh, 0); + pamh = NULL; + _exit(EXIT_SUCCESS); +} + +int main(int argc, char **argv) +{ + if (argc != 4) + { + return EXIT_FAILURE; + } + toParent = atoi (argv[1]); + fromChild = atoi (argv[2]); + if (toParent == 0 || fromChild == 0) + { + printf ("Invalid file descriptors %s %s\n", argv[2], argv[3]); + return EXIT_FAILURE; + } + + //mlockall (MCL_CURRENT | MCL_FUTURE); + fcntl (toParent, F_SETFD, FD_CLOEXEC); + fcntl (fromChild, F_SETFD, FD_CLOEXEC); + + + _authenticate(argv[3]); + + +} + diff --git a/src/ukui-screensaver-command.cpp b/src/ukui-screensaver-command.cpp new file mode 100644 index 0000000..1ce2d54 --- /dev/null +++ b/src/ukui-screensaver-command.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#include +#include +#include +#include +#include +#include +#include +#include "types.h" +#include + +#define WORKING_DIRECTORY "/usr/share/ukui-screensaver" + +int main(int argc, char **argv) +{ + initUkuiLog4qt("ukui-screensaver-command"); + QCoreApplication a(argc, argv); + + QString locale = QLocale::system().name(); + QTranslator translator; + QString qmFile = QString(WORKING_DIRECTORY"/i18n_qm/%1.qm").arg(locale); + translator.load(qmFile); + a.installTranslator(&translator); + + QCommandLineParser parser; + parser.setApplicationDescription(QCoreApplication::translate("main", "Start command for the ukui ScreenSaver.")); + parser.addHelpOption(); + parser.addVersionOption(); + + QCommandLineOption lockOption({"l", QStringLiteral("lock")}, + QCoreApplication::translate("main", "lock the screen immediately")); + + QCommandLineOption queryOption({"q", QStringLiteral("query")}, + QCoreApplication::translate("main", "query the status of the screen saver")); + + QCommandLineOption unlockOption({"u", QStringLiteral("unlock")}, + QCoreApplication::translate("main", "unlock the screen saver")); + QCommandLineOption screensaverOption({"s", QStringLiteral("screensaver")}, + QCoreApplication::translate("main", "show the screensaver")); + + parser.addOption(lockOption); + parser.addOption(queryOption); + parser.addOption(unlockOption); + parser.addOption(screensaverOption); + parser.process(a); + + if(!parser.isSet(lockOption) && !parser.isSet(queryOption) && !parser.isSet(unlockOption) && !parser.isSet(screensaverOption)) + return -1; + + QDBusInterface *interface = new QDBusInterface(SS_DBUS_SERVICE, + SS_DBUS_PATH, + SS_DBUS_INTERFACE); + + QDBusReply stateReply = interface->call("GetLockState"); + if(!stateReply.isValid()){ + qWarning()<< "Get state error:" << stateReply.error(); + return 0; + } + qInfo()<<"Command:"<call("Lock"); + if(msg.type() == QDBusMessage::ErrorMessage) + qDebug() << msg.errorMessage(); + }else if(parser.isSet(unlockOption)){ + QDBusMessage msg = interface->call("UnLock"); + if(msg.type() == QDBusMessage::ErrorMessage) + qDebug() << msg.errorMessage(); + }else if(parser.isSet(screensaverOption)){ + QDBusMessage msg = interface->call("ShowScreensaver"); + if(msg.type() == QDBusMessage::ErrorMessage) + qDebug() << msg.errorMessage(); + } + return 0; +} + diff --git a/src/ukui-screensaver-dialog.cpp b/src/ukui-screensaver-dialog.cpp new file mode 100644 index 0000000..bd7bb1e --- /dev/null +++ b/src/ukui-screensaver-dialog.cpp @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fullbackgroundwidget.h" +#include "configuration.h" +#define CACHE_DIR "/.cache/ukui-screensaver/" +#define DOUBLE 2 +#define MAX_FILE_SIZE 1024 * 1024 +#define LOG_FILE0 "screensaver_0.log" +#define LOG_FILE1 "screensaver_1.log" + +#define GSETTINGS_SCHEMA_SCREENSAVER "org.ukui.screensaver" +#define KEY_LOCK_ENABLED "lock-enabled" + +FullBackgroundWidget *window = NULL; +static void +messageOutput(QtMsgType type, const QMessageLogContext &context,const QString &msg); + +void checkIslivecd() +{ + char cmd[128] = {0}; + char str[1024]; + FILE *fp; + + int n = sprintf(cmd, "cat /proc/cmdline"); + Q_UNUSED(n) + + fp = popen(cmd, "r"); + while(fgets(str, sizeof(str)-1, fp)) { + if(strstr(str,"boot=casper")) + { + printf("is livecd\n"); + exit(0); + } + } + pclose(fp); + + QString filepath = QDir::homePath() + "/Desktop" + "/kylin-os-installer.desktop"; + QFileInfo file(filepath); + if(!file.exists()) + return; + if(getuid() != 999) + return; + exit(0); +} + +void checkIsRunning() +{ + int fd, len; + char buf[32]; + struct flock lock; + + const QString PID_DIR = QString("/var/run/user/%1").arg(QString::number(getuid())); + QString env = qgetenv("DISPLAY"); + const QString PID_FILE = PID_DIR + QString("/ukui-screensaver%1.pid").arg(env); + + qDebug() << PID_DIR; + QDir dir(PID_DIR); + if(!dir.exists()) { + if(!dir.mkdir(PID_DIR.toLocal8Bit().data())) { + perror("create pid directory failed"); + exit(1); + } + } + if( (fd = open(PID_FILE.toLocal8Bit().data(), + O_RDWR | O_CREAT, 0666)) == -1){ + perror("open pid file failed"); + exit(1); + } + + memset(&lock, 0, sizeof(struct flock)); + lock.l_type = F_WRLCK; + lock.l_whence = SEEK_SET; + + if(fcntl(fd, F_SETLK, &lock) < 0) { +// perror("fcntl F_SETLK failed"); + printf("There is already an instance running\n"); + exit(1); + } + + len = snprintf(buf, sizeof(buf), "%d", getpid()); + ftruncate(fd, 0); + if(write(fd, buf, len) != len) { + perror("write pid to lock file failed"); + exit(1); + } +} + +void handler(int signum) +{ + window->closeScreensaver(); +} + +#define WORKING_DIRECTORY "/usr/share/ukui-screensaver" +int main(int argc, char *argv[]) +{ + if(argc < 2) + return 0; + + initUkuiLog4qt("ukui-screensaver-dialog"); + // 重启或关机时不被session关掉 + qunsetenv("SESSION_MANAGER"); + checkIsRunning(); + checkIslivecd(); + + qputenv("QT_QPA_PLATFORMTHEME",QByteArray("ukui")); + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) + QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); +#endif +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); +#endif + + qunsetenv("QT_IM_MODULE"); + //signal(SIGTERM,handler); + QApplication a(argc, argv); + QApplication::setSetuidAllowed(true); + + + //命令行参数解析 + QCommandLineParser parser; + parser.setApplicationDescription(QCoreApplication::translate("main", "Dialog for the ukui ScreenSaver.")); + parser.addHelpOption(); + parser.addVersionOption(); + + QCommandLineOption lockOption(QStringLiteral("lock"), + QCoreApplication::translate("main", "lock the screen immediately")); + QCommandLineOption lstOption(QStringLiteral("lock-startup"), + QCoreApplication::translate("main", "lock the screen immediately")); + QCommandLineOption sessionIdleOption(QStringLiteral("session-idle"), + QCoreApplication::translate("main", "activated by session idle signal")); + QCommandLineOption lscreensaverOption(QStringLiteral("lock-screensaver"), + QCoreApplication::translate("main", "lock the screen and show screensaver immediately")); + QCommandLineOption screensaverOption(QStringLiteral("screensaver"), + QCoreApplication::translate("main", "show screensaver immediately")); + QCommandLineOption blankOption(QStringLiteral("blank"), + QCoreApplication::translate("main", "lock the screen and show screensaver immediately")); + parser.addOptions({lockOption, lstOption,sessionIdleOption , screensaverOption,blankOption,lscreensaverOption}); + parser.process(a); + + if(!parser.isSet(sessionIdleOption) + && !parser.isSet(lockOption) + && !parser.isSet(lstOption) + && !parser.isSet(screensaverOption) + && !parser.isSet(lscreensaverOption) + && !parser.isSet(blankOption)) + { + return 0; + } + + //qInstallMessageHandler(messageOutput); + + //加载翻译文件 + QString locale = QLocale::system().name(); + QTranslator translator; + QString qmFile = QString(WORKING_DIRECTORY"/i18n_qm/%1.qm").arg(locale); + translator.load(qmFile); + a.installTranslator(&translator); + qDebug() << "load translation file " << qmFile; + + window = new FullBackgroundWidget(); + + QFile qssFile(":/qss/assets/authdialog.qss"); + if(qssFile.open(QIODevice::ReadOnly)) { + a.setStyleSheet(qssFile.readAll()); + } + qssFile.close(); + + if(parser.isSet(blankOption)) + { + window->onBlankScreensaver(); + } + +#ifndef USE_INTEL + window->show(); + window->activateWindow(); +#endif + if(parser.isSet(lockOption)) + { + window->lock(); + } + + if(parser.isSet(lstOption)) + { + window->lock(); + window->setIsStartup(true); + } + + if(parser.isSet(sessionIdleOption)) + { + if(window->onSessionStatusChanged(SESSION_IDLE) == -1) + return 0; + } + + if(parser.isSet(lscreensaverOption)) + { + window->onScreensaver(); + } +/* + if(parser.isSet(blankOption)) + { + window->onBlankScreensaver(); + } +*/ + if(parser.isSet(screensaverOption)) + { + window->showScreensaver(); + } + +#ifdef USE_INTEL + window->show(); + window->activateWindow(); +#endif + + QString username = getenv("USER"); + int uid = getuid(); + QDBusInterface *interface = new QDBusInterface("cn.kylinos.Kydroid2", + "/cn/kylinos/Kydroid2", + "cn.kylinos.Kydroid2", + QDBusConnection::systemBus(), + window); + + QDBusMessage msg = interface->call(QStringLiteral("SetPropOfContainer"),username, uid, "is_kydroid_on_focus", "0"); + return a.exec(); +} + +static void messageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) +{ + Q_UNUSED(context) + QDateTime dateTime = QDateTime::currentDateTime(); + QByteArray time = QString("[%1] ").arg(dateTime.toString("MM-dd hh:mm:ss.zzz")).toLocal8Bit(); + QByteArray localMsg = msg.toLocal8Bit(); + + QString name[DOUBLE] = {LOG_FILE0, LOG_FILE1}; + FILE *log_file = nullptr; + QString logFilePath; + int fileSize; + static int i = 0; + QDir dir; + + if (dir.mkpath(QDir::homePath() + CACHE_DIR)) { + logFilePath = QDir::homePath() + CACHE_DIR + "/" + name[i]; + log_file = fopen(logFilePath.toLocal8Bit().constData(), "a+"); + } + + QString filePath(context.file); + int separator = filePath.lastIndexOf('/'); + QString fileName = filePath.right(filePath.length() - separator - 1); + const char *file = fileName.toLocal8Bit().data(); + + switch(type) { + case QtDebugMsg: + fprintf(log_file? log_file:stderr, "%s Debug: %s:%u: %s\n", time.constData(), file, context.line, localMsg.constData()); + break; + case QtInfoMsg: + fprintf(log_file? log_file:stderr, "%s Info: %s:%u: %s\n", time.constData(), file, context.line, localMsg.constData()); + break; + case QtWarningMsg: + fprintf(log_file? log_file:stderr, "%s Warnning: %s:%u: %s\n", time.constData(), file, context.line, localMsg.constData()); + break; + case QtCriticalMsg: + fprintf(log_file? log_file:stderr, "%s Critical: %s:%u: %s\n", time.constData(), file, context.line, localMsg.constData()); + break; + case QtFatalMsg: + fprintf(log_file? log_file:stderr, "%s Fatal: %s:%u: %s\n", time.constData(), file, context.line, localMsg.constData()); + abort(); + } + fflush(stderr); + + if (log_file) { + fileSize = ftell(log_file); + if (fileSize >= MAX_FILE_SIZE) { + i = (i + 1) % DOUBLE; + logFilePath = QDir::homePath() + logFilePath + "/" + name[i]; + if (QFile::exists(logFilePath)) { + QFile temp(logFilePath); + temp.remove(); + } + } + fclose(log_file); + } +} diff --git a/src/ukui-screensaver.pro b/src/ukui-screensaver.pro new file mode 100644 index 0000000..f8b4b43 --- /dev/null +++ b/src/ukui-screensaver.pro @@ -0,0 +1,80 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2018-04-09T11:40:40 +# +#------------------------------------------------- +QT += core gui dbus x11extras + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +include(BioAuth/bioauth.pri) +include(VirtualKeyboard/VirtualKeyboard.pri) + +TARGET = ukui-screensaver-dialog +TEMPLATE = app + +# The following define makes your compiler emit warnings if you use +# any feature of Qt which has been marked as deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS \ + QT_MESSAGELOGCONTEXT + +LIBS += -lpam -lpam_misc -lX11 -lXext -lXtst + +CONFIG += link_pkgconfig +PKGCONFIG += gio-2.0 x11 xcb xtst + +# You can also make your code fail to compile if you use deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +INCLUDEPATH += \ + VirtualKeyboard/src/ + BioAuth/include/ + +SOURCES += \ + ukui-screensaver-dialog.cpp \ + mainwindow.cpp \ + unixsignallistener.cpp \ + auth-pam.cpp \ + authdialog.cpp \ + gsettings.cpp \ + auxiliary.cpp \ + configuration.cpp \ + screensaverwidget.cpp \ + screensaver.cpp \ + event_monitor.cpp \ + monitorwatcher.cpp + +HEADERS += \ + mainwindow.h \ + unixsignallistener.h \ + auth-pam.h \ + auth.h \ + authdialog.h \ + gsettings.h \ + auxiliary.h \ + configuration.h \ + screensaverwidget.h \ + screensaver.h \ + event_monitor.h \ + monitorwatcher.h + +FORMS += \ + mainwindow.ui \ + authdialog.ui + +RESOURCES += \ + assets.qrc + +TRANSLATIONS = ../i18n_ts/zh_CN.ts \ + ../i18n_ts/ru.ts \ + ../i18n_ts/fr.ts \ + ../i18n_ts/pt.ts \ + ../i18n_ts/es.ts + +target.path = /usr/bin/ + +INSTALLS += target diff --git a/src/unixsignallistener.cpp b/src/unixsignallistener.cpp new file mode 100644 index 0000000..6cb1583 --- /dev/null +++ b/src/unixsignallistener.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#include "unixsignallistener.h" +#include +#include +#include +#include +#include + +int UnixSignalListener::sigusr1Fd[2]; + +UnixSignalListener::UnixSignalListener(QObject *parent) : QObject(parent) +{ + if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sigusr1Fd)) + qFatal("Couldn't create SIGUSR1 socketpair"); + snUsr1 = new QSocketNotifier(sigusr1Fd[1], QSocketNotifier::Read, this); + connect(snUsr1, &QSocketNotifier::activated, this, &UnixSignalListener::handleSigUsr1); +} + +void UnixSignalListener::usr1SignalAction(int sig, siginfo_t *siginfo, void *ucontext) +{ + (void)sig; + (void)ucontext; + char buffer[16]; + sprintf(buffer, "%d", siginfo->si_pid); + int ignore = ::write(sigusr1Fd[0], buffer, strlen(buffer) + 1); + (void)ignore; +} + +void UnixSignalListener::chldSignalAction(int /*sig*/, siginfo_t */*siginfo*/, void */*ucontext*/) +{ + ::waitpid(-1, NULL, 0); +} + +void UnixSignalListener::handleSigUsr1() +{ + snUsr1->setEnabled(false); + char buffer[16]; + int pid; + int ignore = ::read(sigusr1Fd[1], buffer, sizeof(buffer)); + sscanf(buffer, "%d", &pid); + (void)ignore; + + /* Do Qt stuff */ + Q_EMIT transition(pid); + + snUsr1->setEnabled(true); +} diff --git a/src/unixsignallistener.h b/src/unixsignallistener.h new file mode 100644 index 0000000..2a1da96 --- /dev/null +++ b/src/unixsignallistener.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#ifndef UNIXSIGNALLISTENER_H +#define UNIXSIGNALLISTENER_H + +#ifndef QT_NO_KEYWORDS +#define QT_NO_KEYWORDS +#endif + +#include +#include +#include + +class UnixSignalListener : public QObject +{ + Q_OBJECT +public: + explicit UnixSignalListener(QObject *parent = nullptr); + +public: + /* Unix signal handlers. */ + static void usr1SignalAction(int sig, siginfo_t *siginfo, void *ucontext); + static void chldSignalAction(int sig, siginfo_t *siginfo, void *ucontext); + +Q_SIGNALS: + /* pid is the sending process id */ + void transition(int pid); /* Finite State Machine Driven Signal */ + +public Q_SLOTS: + /* Qt signal handlers. */ + void handleSigUsr1(); + +private: + static int sigusr1Fd[2]; + QSocketNotifier *snUsr1; +}; + +#endif // UNIXSIGNALLISTENER_H diff --git a/src/users.cpp b/src/users.cpp new file mode 100644 index 0000000..03c87e6 --- /dev/null +++ b/src/users.cpp @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * +**/ +#include "users.h" + +#include +#include +#include +#include +#include +#include + +#include "types.h" +#include +#include + +QDebug operator<<(QDebug stream, const UserItem &user) +{ + stream << user.name << user.realName << user.uid + << user.icon << user.path; + return stream; +} + +Users::Users(QObject *parent) : QObject(parent) +{ + if (Utils::isCommunity()) { + defaultIcon = ":/image/assets/01-default-community.png"; + } else { + defaultIcon = ":/image/assets/01-default-commercial.png"; + } + loadUsers(); +} + +QList Users::getUsers() +{ + return users; +} + +UserItem Users::getUserByName(const QString &name) +{ + for(int i = 0 ; i < users.size() ; i++) { + if(users.at(i).name == name) + { + return users.at(i); + } + } + UserItem user; + if(name == "root") + { + user.icon = "/root/.face"; + if(!QFile(user.icon).exists()) + { + user.icon = defaultIcon; + } + } + else + { + user.icon = qgetenv("HOME")+"/.face"; + if(!QFile(user.icon).exists()) + { + user.icon = defaultIcon; + } + } + user.name = name; + user.path = ""; + user.realName = name; + user.uid = getuid(); + return user; +} + +QString Users::getDefaultIcon() +{ + return defaultIcon; +} + +bool compareBarData(const UserItem &user1, const UserItem &user2) +{ + if (user1.realName < user2.realName) + { + return true; + } + return false; +} + +//https://stackoverflow.com/questions/20206376/ +//how-do-i-extract-the-returned-data-from-qdbusmessage-in-a-qt-dbus-call +void Users::loadUsers() +{ + qDebug() << "loadUsers"; + actService = new QDBusInterface(ACT_DBUS_SERVICE, + ACT_DBUS_PATH, + ACT_DBUS_INTERFACE, + QDBusConnection::systemBus()); + + connect(actService, SIGNAL(UserAdded(const QDBusObjectPath&)), + this, SLOT(onUserAdded(const QDBusObjectPath&))); + connect(actService, SIGNAL(UserDeleted(const QDBusObjectPath&)), + this, SLOT(onUserDeleted(const QDBusObjectPath&))); + QDBusMessage ret = actService->call("ListCachedUsers"); + QList outArgs = ret.arguments(); //(QVariant(QDBusArgument,)) + QVariant first = outArgs.at(0); //QVariant(QDBusArgument,) + const QDBusArgument &dbusArgs = first.value(); + QDBusObjectPath path; + dbusArgs.beginArray(); + while (!dbusArgs.atEnd()) + { + dbusArgs >> path; + getUser(path.path()); + } + dbusArgs.endArray(); + qSort(users.begin(), users.end(), compareBarData); +} + +UserItem Users::getUser(const QString &path) +{ + QDBusInterface iface(ACT_DBUS_SERVICE, + path, + DBUS_PROP_INTERFACE, + QDBusConnection::systemBus()); + QDBusMessage ret = iface.call("GetAll", ACT_USER_INTERFACE); + QList outArgs = ret.arguments(); + QVariant first = outArgs.at(0); + const QDBusArgument &dbusArgs = first.value(); + UserItem user; + user.path = path; + dbusArgs.beginMap(); + while(!dbusArgs.atEnd()) + { + QString key; + QVariant value; + dbusArgs.beginMapEntry(); + dbusArgs >> key >> value; + if(key == "UserName") + { + user.name = value.toString(); + } + else if(key == "RealName") + { + user.realName = value.toString(); + } + else if(key == "IconFile") + { + user.icon = value.toString(); + if(!QFile(user.icon).exists()) + { + user.icon = defaultIcon; + } + } + else if(key == "Uid") + { + user.uid = value.toUInt(); + } + dbusArgs.endMapEntry(); + } + dbusArgs.endMap(); + if(user.realName.isEmpty()) + { + user.realName = user.name; + } + users.push_back(user); + return user; +} + +void Users::onUserAdded(const QDBusObjectPath& path) +{ + int index = findUserByPath(path.path()); + if(index >=0 &&index= 0 && index. + * +**/ +#ifndef USERS_H +#define USERS_H + +#include + + +struct UserItem +{ + QString name; + QString realName; + QString icon; + quint64 uid; + QString path; //accounts service path + friend QDebug operator<<(QDebug stream, const UserItem &user); +}; + +class QDBusInterface; +class QDBusObjectPath; +class Users : public QObject +{ + Q_OBJECT +public: + explicit Users(QObject *parent = nullptr); + QList getUsers(); + UserItem getUserByName(const QString &name); + QString getDefaultIcon(); + +private: + void loadUsers(); + UserItem getUser(const QString &path); + int findUserByPath(const QString &path); + +private Q_SLOTS: + void onUserAdded(const QDBusObjectPath& path); + void onUserDeleted(const QDBusObjectPath& path); + +Q_SIGNALS: + void userAdded(const UserItem &user); + void userDeleted(const UserItem &user); + +private: + QDBusInterface *actService; + QList users; + QString defaultIcon; +}; + +#endif // USERS_H diff --git a/src/utils.cpp b/src/utils.cpp new file mode 100644 index 0000000..e5b7f50 --- /dev/null +++ b/src/utils.cpp @@ -0,0 +1,20 @@ +#include "utils.h" +#include + + +namespace Utils { + +bool isCommunity() +{ + QString filename = "/etc/os-release"; + QSettings osSettings(filename, QSettings::IniFormat); + + QString versionID = osSettings.value("VERSION_ID").toString(); + + if (versionID.compare("22.04", Qt::CaseSensitive)) { + return false; + } + return true; +} + +} diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 0000000..4966654 --- /dev/null +++ b/src/utils.h @@ -0,0 +1,11 @@ +#ifndef UTILS_H +#define UTILS_H + +namespace Utils { + +// 是否为2204社区版本 +bool isCommunity(); + +} + +#endif // UTILS_H diff --git a/src/verificationwidget.cpp b/src/verificationwidget.cpp new file mode 100644 index 0000000..edbb92f --- /dev/null +++ b/src/verificationwidget.cpp @@ -0,0 +1,674 @@ +#include "verificationwidget.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" + +#include "wechatauthdialog.h" +#include "eduplatforminterface.h" +#include "accountsinterface.h" + +/** + * 忘记密码后,校验页面:分为二维码校验和手机号校验 + */ + +bool isDigitalString(QString s);//判断字符串是否全为数字 + +VerificationWidget::VerificationWidget(QWidget *parent) : QWidget(parent) +{ + qDebug() << "PermissionCheck"; + resize(1000,400); + QHBoxLayout *mainLayout = new QHBoxLayout(this); + setLayout(mainLayout); + + m_weChatWidget = new WeChatAuthDialog(1,this); + mainLayout->addWidget(m_weChatWidget, 1, Qt::AlignLeft); + m_weChatWidget->setFixedWidth(400); + + mainLayout->addSpacerItem(new QSpacerItem(0,200,QSizePolicy::Fixed,QSizePolicy::Expanding)); + + m_phoneAuthWidget = new PhoneAuthWidget(this); + mainLayout->addWidget(m_phoneAuthWidget, 1, Qt::AlignRight); + m_phoneAuthWidget->setFixedWidth(400); + + connect(m_phoneAuthWidget, &PhoneAuthWidget::pageMessage, this, [=](SwitchPage s, QList list){ + Q_EMIT pageMessage(s, list); + }); + + connect(m_weChatWidget, &WeChatAuthDialog::qRStatusChange, this, [=] (QString loginname, QString loginpwd, int curstatus){ + //QString loginname, QString loginpwd, int curstatus + if (loginname.isEmpty()) + { + // Todo?正在登陆???? + qDebug() << "wechar name is null"; + } else if(getenv("USER") == loginname) + { + Q_EMIT pageMessage(SwitchPage::SwitchToResetPWD, QList());; + } else { + qWarning() << "[error] Username does not match wechat<" << loginname << ">" << " local<" << getenv("USER")<<">"; + m_weChatWidget->showErrorMessage(tr("Please scan by bound WeChat")); + m_weChatWidget->onReset(); + } + }); +} + +VerificationWidget::~VerificationWidget() +{ + m_phoneAuthWidget->close(); + m_weChatWidget->close(); +} + +void VerificationWidget::paintEvent(QPaintEvent *) +{ + int x1 = width() / 2; + int y1 = height() / 4; + int x2 = x1; + int y2 = height() / 3 * 2 + y1; + QLinearGradient linearGra(QPoint(x1, y1), QPoint(x2, y2)); + linearGra.setColorAt(0, QColor(238, 238, 238, 0)); + linearGra.setColorAt(0.5, QColor(255, 255, 255, 138)); + linearGra.setColorAt(1, QColor(216, 216, 216, 0)); + + QPainter painter(this); + QBrush brush(linearGra); + painter.setPen(Qt::transparent); + painter.setBrush(brush); + painter.drawRoundedRect(QRect(x1, y1, 4, height() / 3 * 2), 16, 16); +} + +void VerificationWidget::reloadQR() +{ + m_weChatWidget->reloadQR(); +} + +InputInfos::InputInfos(QWidget *parent): + QWidget(parent) +{ + initUI(); + initConnect(); + setQSS(); +} + +void InputInfos::initUI() +{ + setFixedSize(316, 129); + QGridLayout *mainLayout = new QGridLayout(this); + setLayout(mainLayout); + mainLayout->setColumnStretch(1, 0); + mainLayout->setColumnStretch(2, 1); + mainLayout->setColumnStretch(3, 0); + + m_pPhoneIconLB = new QLabel(this); + m_pPhoneIconLB->setPixmap(QPixmap(":/image/assets/intel/phone.png")); + mainLayout->addWidget(m_pPhoneIconLB, 0, 1, 1, 1, Qt::AlignLeft); + + m_pPhoneLE = new QLineEdit(this); + m_pPhoneLE->setReadOnly(true); + //限制以下特殊符号在lineEdit中的输入 + /*需求变更,不需要用户输入手机号,直接获取已绑定的手机号显示给用户 + QRegExp phoneNumrx = QRegExp("^1([358][0-9]|4[579]|66|7[0135678]|9[89])[0-9]{8}$"); + QRegExpValidator *phoneNumValidator = new QRegExpValidator(phoneNumrx); + m_pPhoneLE->setValidator(phoneNumValidator); + */ + m_pPhoneLE->setText("正在获取手机号..."); + GetPhoneNumThread *workerThread = new GetPhoneNumThread(this); + connect(workerThread, &GetPhoneNumThread::resultReady, this, &InputInfos::onGetPhoneNum); + connect(workerThread, &GetPhoneNumThread::finished, workerThread, &QObject::deleteLater); + workerThread->start(); + + m_pPhoneLE->setPlaceholderText("手机号"); + m_pPhoneLE->setProperty("class", "InputLine"); + mainLayout->addWidget(m_pPhoneLE, 0, 2, 1, 2, Qt::AlignLeft); + + m_pVerCodeIconLB = new QLabel(this); + m_pVerCodeIconLB->setPixmap(QPixmap(":/image/assets/sms.png")); + mainLayout->addWidget(m_pVerCodeIconLB, 1, 1, 1, 1, Qt::AlignLeft); + + //限制以下特殊符号在lineEdit中的输入 + QRegExp verCoderx = QRegExp("^[0-9]{6}$"); + QRegExpValidator *verCodeValidator = new QRegExpValidator(verCoderx); + m_pVerCodeLE = new MyLineEdit(this); + m_pVerCodeLE->setValidator(verCodeValidator); + mainLayout->addWidget(m_pVerCodeLE, 1, 2, 1, 1, Qt::AlignLeft); + + m_pGetVerCodeBT = new QPushButton(this); + m_pGetVerCodeBT->setText(tr("Get code")); + m_pGetVerCodeBT->setEnabled(false); + m_pGetVerCodeBT->setFocusPolicy(Qt::NoFocus); + m_pGetVerCodeBT->setProperty("class", "GetCodeBT"); + m_pGetVerCodeBT->setCursor(QCursor(Qt::PointingHandCursor)); + mainLayout->addWidget(m_pGetVerCodeBT, 1, 3, 1, 1, Qt::AlignLeft); + + m_pNextGetVerCodeQTimer = new QTimer(this); + + m_curInputState = InputState::InputWaiting; + + m_networkWatcher = new NetWorkWatcher(this); +} + +void InputInfos::initConnect() +{ + connect(m_pPhoneLE, &QLineEdit::textChanged, this, &InputInfos::onPhoneTextChanged); + + connect(m_pVerCodeLE, &QLineEdit::textChanged, this, &InputInfos::onVerCodeTextChanged); + + connect(m_pNextGetVerCodeQTimer, &QTimer::timeout, this, &InputInfos::onNextGetVerCodeTimer); + + connect(m_pGetVerCodeBT, &QPushButton::clicked, this, &InputInfos::onGetVerCode); + + connect(m_networkWatcher, &NetWorkWatcher::NetworkStateChanged, this, &InputInfos::onNetworkStateChanged); +} + +void InputInfos::setQSS() +{ + QString style_sheet = ".InputLine{" + "padding:10px 2px 10px 2px;" + "background-color:rgb(255,255,255,0);" + "color:black;" + "}" + ".GetCodeBT{" + "background:rgba(0,0,0,0);" + "color:rgba(38,38,38,115);" + "font-size:16px;" + "}"; + setStyleSheet(style_sheet); + //adjustSize(); +} + +void InputInfos::paintEvent(QPaintEvent *) +{ + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + painter.setPen(Qt::white); + painter.setBrush(Qt::white); + painter.drawRoundedRect(rect(), 16, 16); + painter.setPen(QColor(226, 226, 226)); + painter.drawLine(QLineF(0, m_pPhoneIconLB->y() + m_pPhoneIconLB->height() + 1, + width(), m_pPhoneIconLB->y() + m_pPhoneIconLB->height() + 1)); +} + +void InputInfos::onPhoneTextChanged(const QString &text) +{ + if(text.length() == 11 && isDigitalString(text)){ + m_pGetVerCodeBT->setEnabled(true); + m_pGetVerCodeBT->setFocusPolicy(Qt::NoFocus); + m_pGetVerCodeBT->setStyleSheet("color:#2FB3E8;"); + + if(m_pVerCodeLE->text().length() == 6){ + Q_EMIT InputStateChanged(InputState::InputFinish); + m_curInputState = InputState::InputFinish; + } + } + else + { + m_pGetVerCodeBT->setEnabled(false); + m_pGetVerCodeBT->setStyleSheet("color:rgba(38, 38, 38, 115);"); + if(InputState::InputFinish == m_curInputState) + { + m_curInputState = InputState::InputWaiting; + Q_EMIT InputStateChanged(InputState::InputWaiting); + } + } +} + +void InputInfos::onNetworkStateChanged(uint state) +{ + if(NM_STATE_CONNECTED_LIMIT == state) + { + m_nextGetVerCodeTime = 0; + emit notGlobal(); + } + else if(NM_STATE_CONNECTED_GLOBAL != state) + { + m_nextGetVerCodeTime = 0; + emit offlineState(); + } + else + emit online(); +} + +void InputInfos::onVerCodeTextChanged(const QString &text) +{ + if(text.length() == 6 && m_pPhoneLE->text().length() == 11 && isDigitalString(m_pPhoneLE->text())) + { + Q_EMIT InputStateChanged(InputState::InputFinish); + m_curInputState = InputState::InputFinish; + } + else if(m_curInputState == InputState::InputFinish && text.length() > 0 && text.length() < 6) + { + Q_EMIT InputStateChanged(InputState::InputWaiting); + m_curInputState = InputState::InputWaiting; + } +} + +void InputInfos::clearInfos() +{ + m_pPhoneLE->clear(); + m_pVerCodeLE->clear(); + if(m_curInputState == InputState::InputFinish) + { + m_curInputState = InputState::InputWaiting; + Q_EMIT InputStateChanged(m_curInputState); + } +} + +QString InputInfos::getVerificationCode() +{ + return m_pVerCodeLE->text(); +} + +QString InputInfos::getPhoneNumer() +{ + return m_phoneNum; +} + +/** + * @brief InputInfos::onGetVerCode + * 获取手机验证码 + */ +void InputInfos::onGetVerCode() +{ + if(!m_networkWatcher->isConnect()) + { + emit offlineState(); + return; + } + + m_networkWatcher->checkOnline(); + + m_pGetVerCodeBT->setEnabled(false); + m_pGetVerCodeBT->setStyleSheet("color:rgba(38, 38, 38, 115);"); + m_nextGetVerCodeTime = 60; + m_pGetVerCodeBT->setText(tr("Recapture(60s)")); + m_pNextGetVerCodeQTimer->start(1000); + + QDBusReply reply = EduPlatformInterface::getInstance()->call("GetVerifyCode", getPhoneNumer()); + if(!reply.isValid()) + { + qDebug() << "info: [InputInfos][onGetVerCode]: DBus Connect Failed!"; + return; + } + // + if(reply.value() != 0) // 默认0为成功 + { + qDebug() << "info: [InputInfos][onGetVerCode]: DBus request failed!"; + return; + } +} + + +void InputInfos::onNextGetVerCodeTimer() +{ + if(m_nextGetVerCodeTime > 0) + --m_nextGetVerCodeTime; + if(m_nextGetVerCodeTime == 20 || m_nextGetVerCodeTime == 40) + m_networkWatcher->checkOnline(); + m_pGetVerCodeBT->setText(tr("Recapture(%1s)").arg(QString::number(m_nextGetVerCodeTime))); + if(m_nextGetVerCodeTime == 0) + { + m_pNextGetVerCodeQTimer->stop(); + m_pGetVerCodeBT->setEnabled(true); + m_pGetVerCodeBT->setFocusPolicy(Qt::NoFocus); + m_pGetVerCodeBT->setText(tr("Get code")); + m_pGetVerCodeBT->setStyleSheet("color:#2FB3E8;"); + return; + } + +} + +void InputInfos::onGetPhoneNum(int errorCode, const QString phoneNum) +{ + switch (errorCode) { + case DBusMsgCode::No_Error: + if(phoneNum != nullptr && phoneNum.size() == 11) + { + m_phoneNum = phoneNum; + m_pPhoneLE->setText(QString(phoneNum).replace(3,4,"****")); + } else { + qWarning() << "error: [InputInfos] onGetPhoneNum 获取到的手机号格式错误:" << phoneNum; + } + break; + case DBusMsgCode::Error_NoReply: + m_pPhoneLE->setText(tr("Service exception...")); + break; + case DBusMsgCode::Error_ArgCnt: + m_pPhoneLE->setText(tr("Invaild parameters...")); + break; + default: + m_pPhoneLE->setText(tr("Unknown fault:%1").arg(errorCode)); + break; + } + + if(DBusMsgCode::No_Error == errorCode && phoneNum != nullptr && phoneNum.size() == 11 && isDigitalString(phoneNum)) + { + + } else { + + qWarning() << "error: [InputInfos] onGetPhoneNum errorcode" << errorCode; + } +} + +void InputInfos::onClearInfo() +{ + m_pVerCodeLE->clear(); +} + +PhoneAuthWidget::PhoneAuthWidget(QWidget *parent) +{ + initUI(); + initConnect(); + setQSS(); +} + +void PhoneAuthWidget::initUI() +{ + QVBoxLayout *phoLayout = new QVBoxLayout(this); + phoLayout->setMargin(0); + + m_pPhoTitleLB = new QLabel(this); + m_pPhoTitleLB->setText(tr("Verification by phoneNum")); + m_pPhoTitleLB->setProperty("class", "titleLB"); + phoLayout->addWidget(m_pPhoTitleLB, 0, Qt::AlignHCenter); + + phoLayout->addSpacing(16); + m_pPhoPromptMsgLB = new QLabel(this); + m_pPhoPromptMsgLB->setText(tr("「 Use SMS to verification 」")); + m_pPhoPromptMsgLB->setProperty("class", "PromptText"); + phoLayout->addWidget(m_pPhoPromptMsgLB, 0, Qt::AlignHCenter); + + m_pPhoErrorMsgLB = new QLabel(this); + m_pPhoErrorMsgCloneLB = new QLabel(this); + m_pPhoErrorMsgLB->setProperty("class", "ErrorMsg"); + m_pPhoErrorMsgCloneLB->setProperty("class", "ErrorMsg"); + hidePhoneErrorMsg(); + phoLayout->addWidget(m_pPhoErrorMsgLB, 0, Qt::AlignHCenter); + phoLayout->addWidget(m_pPhoErrorMsgCloneLB, 0, Qt::AlignHCenter); + + + m_pPhoInputInfos = new InputInfos(this); + phoLayout->addWidget(m_pPhoInputInfos, 0, Qt::AlignHCenter); + + m_pPhoSubmitBT = new QPushButton(this); + m_pPhoSubmitBT->setFocusPolicy(Qt::NoFocus); + m_pPhoSubmitBT->setText(tr("commit")); + m_pPhoSubmitBT->setProperty("class", "BindBT"); + m_pPhoSubmitBT->setCursor(QCursor(Qt::PointingHandCursor)); + m_pPhoSubmitBT->setEnabled(false); + phoLayout->addSpacing(8); + phoLayout->addWidget(m_pPhoSubmitBT, 0, Qt::AlignHCenter); + phoLayout->addStretch(1); + + m_pPhoPromptMsgLB->setWordWrap(true); + + m_hideTimer = new QTimer(this); +} + +void PhoneAuthWidget::initConnect() +{ + connect(m_pPhoInputInfos, &InputInfos::InputStateChanged, this, &PhoneAuthWidget::onInputStateChanged); + + connect(m_pPhoSubmitBT, &QPushButton::clicked, this, &PhoneAuthWidget::onSubmitBTClick); + + connect(m_pPhoInputInfos, &InputInfos::getVerCode, this, &PhoneAuthWidget::onGetVerCode); + + connect(m_pPhoInputInfos, &InputInfos::offlineState, this, &PhoneAuthWidget::onOfflineState); + + connect(m_pPhoInputInfos, &InputInfos::notGlobal, this, &PhoneAuthWidget::onNotGlobal); + + connect(m_pPhoInputInfos, &InputInfos::online, this, &PhoneAuthWidget::hidePhoneErrorMsg); + + connect(m_hideTimer, &QTimer::timeout, this, &PhoneAuthWidget::hidePhoneErrorMsg); + + connect(this, &PhoneAuthWidget::clearInfo, m_pPhoInputInfos, &InputInfos::onClearInfo); +} + +QSize PhoneAuthWidget::sizeHint() const{ + return QWidget::sizeHint(); +} + +void PhoneAuthWidget::hidePhoneErrorMsg() +{ + m_pPhoErrorMsgCloneLB->setFixedHeight(m_pPhoErrorMsgLB->height()); + m_pPhoErrorMsgCloneLB->show(); + m_pPhoErrorMsgLB->hide(); +} + +void PhoneAuthWidget::showPhoneErrorMsg() +{ + if(m_hideTimer->isActive()) + m_hideTimer->stop(); + + m_pPhoErrorMsgLB->show(); + m_pPhoErrorMsgCloneLB->hide(); + + //QTimer::singleShot(5*1000, this,&PhoneAuthWidget::hidePhoneErrorMsg); + m_hideTimer->setSingleShot(true); + m_hideTimer->start(5*1000); +} + +void PhoneAuthWidget::onInputStateChanged(InputInfos::InputState input_state) +{ + if(m_pPhoErrorMsgLB->isVisible()) + hidePhoneErrorMsg(); + if(input_state == InputInfos::InputState::InputFinish) + { + m_pPhoSubmitBT->setEnabled(true); + m_pPhoSubmitBT->setFocusPolicy(Qt::NoFocus); + m_pPhoSubmitBT->setStyleSheet("background:#2FB3E8;"); + } + else + { + m_pPhoSubmitBT->setEnabled(false); + m_pPhoSubmitBT->setStyleSheet("background:rgba(255,255,255,115);"); + } +} + +void PhoneAuthWidget::onSubmitBTClick() +{ + QString tel = m_pPhoInputInfos->getPhoneNumer(); + QString verCode = m_pPhoInputInfos->getVerificationCode(); + + DBusMsgCode code = EduPlatformInterface::getInstance()->CheckUserByPhone(getenv("USER"), tel, verCode); + + switch (code) { + case DBusMsgCode::No_Error: + Q_EMIT pageMessage(SwitchPage::SwitchToResetPWD, QList()); + break; + case DBusMsgCode::Error_NetWork: + m_pPhoErrorMsgLB->setText(tr("Network not connected~")); + break; + case DBusMsgCode::Error_VerifyCode_Timeout: + m_pPhoErrorMsgLB->setText(tr("Verification Code invalid!")); + break; + case DBusMsgCode::Error_VerifyCodeDiff: + m_pPhoErrorMsgLB->setText(tr("Verification Code incorrect.Please retry!")); + break; + case Error_ErrTimesOverMax: + m_pPhoErrorMsgLB->setText(tr("Failed time over limit!Retry after 1 hour!")); + break; + default: + qWarning() << "[ERROR] verification error code is" << code; + m_pPhoErrorMsgLB->setText(tr("verifaction failed!")); + break; + } + if( code != DBusMsgCode::No_Error) + { + showPhoneErrorMsg(); + emit clearInfo(); + m_pPhoSubmitBT->setEnabled(false); + m_pPhoSubmitBT->setStyleSheet("background:rgba(255,255,255,115);"); + } +} + +void PhoneAuthWidget::onGetVerCode() +{ +// DBusMsgCode msgCode = SSOP->m_pRetrievePwdInterface->GetVerificationCode(Cfg->getUsername(), m_pPhoInputInfos->getPhoneNumer()); +// switch (msgCode) { +// case DBusMsgCode::No_Error: +// // 验证码获取无错误 +// break; +// // TODO 更新错误状态 +// default: +// break; +// } +} + +void PhoneAuthWidget::onOfflineState() +{ + m_pPhoErrorMsgLB->setText(tr("Network not connected~")); + showPhoneErrorMsg(); +} + +void PhoneAuthWidget::onNotGlobal() +{ + m_pPhoErrorMsgLB->setText(tr("Network unavailable~")); + showPhoneErrorMsg(); +} + +void PhoneAuthWidget::onQRCodeStateChanged(QString username, QString password, int nState) +{ + QRCodeSwepState state = static_cast(nState); + switch (state) { + case QRCodeSwepState::WaitingSwep: + qDebug() << "info: [QRCodePhoneAuthWidget][onQRCodeStateChanged]: waiting user swep qrcode!"; + break; + case QRCodeSwepState::HaveSwep: + qDebug() << "info: [QRCodePhoneAuthWidget][onQRCodeStateChanged]: user has swep code!"; + break; + case QRCodeSwepState::CancelSwep: + qDebug() << "info: [QRCodePhoneAuthWidget][onQRCodeStateChanged]: user cancel swep code!"; + break; + case QRCodeSwepState::ConfirmSuccess: + { + qDebug() << "info: [QRCodePhoneAuthWidget][onQRCodeStateChanged]: user = [" << username << "] confirm success!"; +// if(username != Cfg->getUsername()) +// { +// resetQRCService(); +// // TODO 更新错误信息 +// } +// else +// emit pageMessage(PageMessage::JumpToNewPswSet, QList()); +// break; + } + case QRCodeSwepState::QRCodeInvalid: + { + qDebug() << "info: [QRCodePhoneAuthWidget][onQRCodeStateChanged]: qrcode invalid!"; +// resetQRCService(); + break; + } + default: + break; + } +} + +void PhoneAuthWidget::setQSS() +{ + QString style_sheet =".PromptText{" + "font-size:24px;" + "color: rgba(255,255,255,192);" + "}" + ".BindBT{" + "background:rgba(255,255,255,115);" + "width:316px;" + "height:64px;" + "border-radius:16px;" + "}" + ".ErrorMsg{" + "color:#FD625E;" + "font-size:14px;" + "}" + ".titleLB{" + "color:#FFFFFF;" + "font-size:32px;" + "}"; + setStyleSheet(style_sheet); +} + +MyLineEdit::MyLineEdit(QWidget *parent) +{ + this->setStyleSheet("padding:10px 2px 10px 2px;" + "background-color:rgb(255,255,255,0);" + "color:grey;" + "font-size:16px"); + setText(tr("Verification code")); +} + +void MyLineEdit::focusInEvent(QFocusEvent *e) +{ + QLineEdit::focusInEvent(e); + if(text() == tr("Verification code")) + this->clear(); + this->setStyleSheet("padding:10px 2px 10px 2px;" + "background-color:rgb(255,255,255,0);" + "color:black;" + "font-size:16px"); +} + +void MyLineEdit::focusOutEvent(QFocusEvent *e) +{ + QLineEdit::focusOutEvent(e); + if(text().isEmpty()) + { + this->setStyleSheet("padding:10px 2px 10px 2px;" + "background-color:rgb(255,255,255,0);" + "color:grey;" + "font-size:16px"); + setText(tr("Verification code")); + } + +} + + + +GetPhoneNumThread::GetPhoneNumThread(QObject *parent) : QThread(parent) +{ + //获取手机号的dbus理论上也是耗时操作,故放到线程里面去请求 +} + +void GetPhoneNumThread::run() +{ + QString phoneNum = ""; + DBusMsgCode ret = DBusMsgCode::Error_UnknownReason; + /**极其不良的重试策略,如果没有服务或者网络问题,会导致不停地重试,应该做优化 + * 1、需要后台服务支持,如果不存在dbus服务,应该直接报错,当前绑定失败不意味着服务不存在,判断逻辑需要研究 + * 2、监听网络变化,网络状态变化时,断网时及时提醒用户网络未连接;联网时,如未获取到已绑定的手机号,需要重新获取; + * 网络异常时,固定次数的重试,都失败后提示用户网络请求异常,并提供可手动刷新的策略 + * */ + while (DBusMsgCode::No_Error != ret) + { + ret = AccountsInterface::getInstance()->GetUserPhone(getenv("USER"), phoneNum); + Q_EMIT resultReady(ret, phoneNum); + sleep(5); + } +} + + +bool isDigitalString(QString s) +{ + for(int i = 0; i< s.length(); i++) + { + if(s.at(i) > '9' || s.at(i) < '0') + { + qDebug() << "phoneNum error at" << i+1 << "position"; + return false; + } + if(i == 2) + i += 4; + } + + for(int i = 3; i< 7; i++) + { + if(s.at(i) != '*') + { + qDebug() << "phoneNum error at" << i+1 << "position unencrypted"; + return false; + } + } + return true; +} diff --git a/src/verificationwidget.h b/src/verificationwidget.h new file mode 100644 index 0000000..cbc13e7 --- /dev/null +++ b/src/verificationwidget.h @@ -0,0 +1,155 @@ +#ifndef VERIFICATIONWIDGET_H +#define VERIFICATIONWIDGET_H + +#include +#include +#include +#include +#include +#include "wechatauthdialog.h" +#include "common.h" +#include "networkwatcher.h" + +class InputInfos; +class PhoneAuthWidget; +class MyLineEdit; + +class InputInfos : public QWidget +{ + Q_OBJECT +public: + enum InputState{ + InputWaiting + , InputFinish + }; + InputInfos(QWidget *parent = nullptr); + QString getPhoneNumer(); + QString getVerificationCode(); + void clearInfos(); + +Q_SIGNALS: + void getVerCode(); + void InputStateChanged(InputState input_state); + void offlineState(); + void notGlobal(); + void online(); + +protected: + void paintEvent(QPaintEvent *); + +private Q_SLOTS: + void onPhoneTextChanged(const QString &text); + void onVerCodeTextChanged(const QString &text); + void onGetVerCode(); + void onNextGetVerCodeTimer(); + void onGetPhoneNum(int errorCode, const QString phoneNum); + void onNetworkStateChanged(uint state); + +public Q_SLOTS: + void onClearInfo(); + +private: + void initUI(); + void initConnect(); + void setQSS(); + +private: + QLabel *m_pPhoneIconLB; + QLineEdit *m_pPhoneLE; + QLabel *m_pVerCodeIconLB; + MyLineEdit *m_pVerCodeLE; + QPushButton *m_pGetVerCodeBT; + QTimer *m_pNextGetVerCodeQTimer; + int m_nextGetVerCodeTime; + QString m_phoneNum = nullptr; + + InputState m_curInputState; + NetWorkWatcher *m_networkWatcher; +}; + +class VerificationWidget : public QWidget +{ + Q_OBJECT +public: + explicit VerificationWidget(QWidget *parent = nullptr); + ~VerificationWidget(); + + void reloadQR(); + +Q_SIGNALS: + void pageMessage(SwitchPage id, QList argvs); +private: + QLabel *m_titleLB; + QLabel *m_phonePromptMsgLB; + WeChatAuthDialog *m_weChatWidget; +//public: + PhoneAuthWidget *m_phoneAuthWidget; + +protected: + void paintEvent(QPaintEvent *); +}; + +class PhoneAuthWidget : public QWidget +{ + Q_OBJECT +public: + PhoneAuthWidget(QWidget* parent = nullptr); + QSize sizeHint() const; + +private: + void initUI(); + void initConnect(); + void setQSS(); + + void showPhoneErrorMsg(); + void hidePhoneErrorMsg(); +Q_SIGNALS: + void pageMessage(SwitchPage id, QList argvs); + void clearInfo(); +private Q_SLOTS: + void onInputStateChanged(InputInfos::InputState input_state); + void onSubmitBTClick(); + void onGetVerCode(); + void onQRCodeStateChanged(QString username, QString password, int nState); + void onOfflineState(); + void onNotGlobal(); + + +private: + // 手机号验证 + QLabel *m_pPhoTitleLB; + QLabel *m_pPhoPromptMsgLB; + QLabel *m_pPhoErrorMsgLB; + QLabel *m_pPhoErrorMsgCloneLB; // 控制格式 + QPushButton *m_pPhoSubmitBT; + InputInfos *m_pPhoInputInfos; + QTimer *m_hideTimer; +}; + +class MyLineEdit : public QLineEdit +{ + Q_OBJECT +public: + explicit MyLineEdit(QWidget *parent = nullptr); + +/*public Q_SLOTS: + void onTextChanged(const QString &text);*/ + +protected: + void focusInEvent(QFocusEvent *) override; + void focusOutEvent(QFocusEvent *) override; +}; + +class GetPhoneNumThread : public QThread +{ + Q_OBJECT +public: + GetPhoneNumThread(QObject *parent); + +protected: + void run() override; + +Q_SIGNALS: + void resultReady(int errorCode, const QString phoneNum); +}; +#endif // VERIFICATIONWIDGET_H diff --git a/src/verticalVerificationwidget.cpp b/src/verticalVerificationwidget.cpp new file mode 100644 index 0000000..1dd912a --- /dev/null +++ b/src/verticalVerificationwidget.cpp @@ -0,0 +1,87 @@ +#include "verticalVerificationwidget.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" + +#include "wechatauthdialog.h" +#include "eduplatforminterface.h" +#include "accountsinterface.h" + +/** + * 忘记密码后,校验页面:分为二维码校验和手机号校验 + */ + +VerticalVerificationWidget::VerticalVerificationWidget(QWidget *parent) : QWidget(parent) +{ + qDebug() << "PermissionCheck"; + resize(600,1000); + QVBoxLayout *mainLayout = new QVBoxLayout(this); + setLayout(mainLayout); + + m_weChatWidget = new WeChatAuthDialog(1,this); + mainLayout->addWidget(m_weChatWidget, 1, Qt::AlignCenter); + + mainLayout->addSpacerItem(new QSpacerItem(0, 250, QSizePolicy::Fixed, QSizePolicy::Expanding)); + + m_phoneAuthWidget = new PhoneAuthWidget(this); + mainLayout->addWidget(m_phoneAuthWidget, 1, Qt::AlignCenter); + + connect(m_phoneAuthWidget, &PhoneAuthWidget::pageMessage, this, [=](SwitchPage s, QList list){ + Q_EMIT pageMessage(s, list); + }); + + connect(m_weChatWidget, &WeChatAuthDialog::qRStatusChange, this, [=] (QString loginname, QString loginpwd, int curstatus){ + //QString loginname, QString loginpwd, int curstatus + if (loginname.isEmpty()) + { + // Todo?正在登陆???? + qDebug() << "wechar name is null"; + } else if(getenv("USER") == loginname) + { + Q_EMIT pageMessage(SwitchPage::SwitchToResetPWD, QList());; + } else { + qWarning() << "[error] Username does not match wechat<" << loginname << ">" << " local<" << getenv("USER")<<">"; + m_weChatWidget->showErrorMessage(tr("Please scan by bound WeChat")); + m_weChatWidget->onReset(); + } + }); +} + +VerticalVerificationWidget::~VerticalVerificationWidget() +{ + m_phoneAuthWidget->close(); + m_weChatWidget->close(); +} + +void VerticalVerificationWidget::paintEvent(QPaintEvent *) +{ + int x1 = width() / 3; + int y1 = height() / 2; + int x2 = width() / 3 * 2 ; + int y2 = y1; + QLinearGradient linearGra(QPoint(x1, y1), QPoint(x2, y2)); + linearGra.setColorAt(0, QColor(238, 238, 238, 0)); + linearGra.setColorAt(0.5, QColor(255, 255, 255, 138)); + linearGra.setColorAt(1, QColor(216, 216, 216, 0)); + + QPainter painter(this); + QBrush brush(linearGra); + painter.setPen(Qt::transparent); + painter.setBrush(brush); + painter.drawRoundedRect(QRect(x1, y1, width() / 3 * 2, 4), 16, 16); +} + +void VerticalVerificationWidget::reloadQR() +{ + m_weChatWidget->reloadQR(); +} + + diff --git a/src/verticalVerificationwidget.h b/src/verticalVerificationwidget.h new file mode 100644 index 0000000..749dadc --- /dev/null +++ b/src/verticalVerificationwidget.h @@ -0,0 +1,34 @@ +#ifndef VERTICALVERIFICATIONWIDGET_H +#define VERTICALVERIFICATIONWIDGET_H + +#include +#include +#include +#include +#include + +#include "common.h" + +class VerticalVerificationWidget : public QWidget +{ + Q_OBJECT +public: + explicit VerticalVerificationWidget(QWidget *parent = nullptr); + ~VerticalVerificationWidget(); + + void reloadQR(); + +Q_SIGNALS: + void pageMessage(SwitchPage id, QList argvs); +private: + QLabel *m_titleLB; + QLabel *m_phonePromptMsgLB; + WeChatAuthDialog *m_weChatWidget; +//public: + PhoneAuthWidget *m_phoneAuthWidget; + +protected: + void paintEvent(QPaintEvent *); +}; + +#endif // VERTICALVERIFICATIONWIDGET_H diff --git a/src/weathermanager.cpp b/src/weathermanager.cpp new file mode 100644 index 0000000..e1738f2 --- /dev/null +++ b/src/weathermanager.cpp @@ -0,0 +1,323 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Authors: ZHAI Kang-ning +**/ +#include "weathermanager.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const QString weatherServerAddr = "http://service.ubuntukylin.com:8001/weather/api/3.0/heweather_data_s6/"; +const QString defaultCityId = "101030100"; //"101030100" 默认天津 +const int weatherReqInterval = 1000 * 60 * 20; //定时更新天气,和麒麟天气保持一致 + +const QByteArray schemaWeather = "org.china-weather-data.settings"; + +static const QMap weatherMap { + {"NA", "55"}, + {"晴", "0"}, + {"多云", "1"}, + {"阴", "2"}, + {"阵雨", "3"}, + {"雷阵雨", "4"}, + {"小雨", "7"}, + {"中雨", "8"}, + {"大雨", "9"}, + {"中到大雨", "9"}, + {"雪", "13"}, +}; + +WeatherManager::WeatherManager(QObject *parent) : QObject(parent) +{ + //初始化m_net_manager请求天气 + m_net_manager = new QNetworkAccessManager(this); + QObject::connect(m_net_manager, SIGNAL(finished(QNetworkReply*)), + this, SLOT(replyFinished(QNetworkReply*))); + + m_timer = new QTimer(this); + m_timer->setInterval(weatherReqInterval); + + m_local_weather_info = new LocalWeatherInfo(this); + + connect(m_timer, &QTimer::timeout, this, &WeatherManager::weatherRequest); +} + +void WeatherManager::getWeather() +{ + weatherRequest(); + if (m_timer != NULL) + { + m_timer->start(); + } + return; +} + +void WeatherManager::weatherRequest() +{ + if (updateLocation()) + return; + QNetworkRequest request(QUrl(weatherServerAddr + m_city_id + "/")); + m_net_manager->get(request); +} + +bool WeatherManager::updateLocation() +{ + //更新城市信息 未实现 麒麟天气提供gsettings,完成后对接 + if (QGSettings::isSchemaInstalled(schemaWeather)) { + m_settings = new QGSettings(schemaWeather,"",this); //org/ukui/indicator-china-weather path + if(getLogcalWeather()) + { + emit onWeatherUpdate(m_local_weather_info->getCityName(), + m_local_weather_info->getCondText(), + m_local_weather_info->getTemperature()); + return true; + } + m_city_id = getLogcalCityId(); + } + + if (m_city_id.isEmpty()) + m_city_id = defaultCityId; + + return false; +} + +bool WeatherManager::getLogcalWeather() +{ + if (m_settings != nullptr) + { + //"1920-08-27 10:17:42,101310204,澄迈,小雨,95%,25℃,北风,1级," 时间,城市编码,城市名称,天气,湿度,温度,风向,风力 + QString weather = m_settings->get("weather").toString(); + QStringList weatherInfoList = weather.split(","); + if (weatherInfoList.size() < 9) + return false; + m_local_weather_info->setTime(weatherInfoList.at(0)); + if(!m_local_weather_info->isTimeValid()) + return false; + + m_local_weather_info->setCityId(weatherInfoList.at(1)); + m_local_weather_info->setCityName(weatherInfoList.at(2)); + m_local_weather_info->setCondText(weatherInfoList.at(3)); + m_local_weather_info->setAirHumidity(weatherInfoList.at(4)); + m_local_weather_info->setTemperature(weatherInfoList.at(5)); + m_local_weather_info->setWindDirection(weatherInfoList.at(6)); + m_local_weather_info->setWindForce(weatherInfoList.at(7)); + return true; + } + + return false; +} + +QString WeatherManager::getLogcalCityId() +{ + if (m_settings != nullptr) { + QString citys = m_settings->get("citylist").toString();//"101010100," + if (citys.isEmpty()) + return ""; + + QStringList cityList = citys.split(","); + if (cityList.size() >= 1) + { + QString s = cityList.at(0); + qDebug() << "local city id = " << s; + return s; + } + } + return ""; +} + +void WeatherManager::replyFinished(QNetworkReply *reply) +{ + //注:天气信息只解析了锁屏需要展示的部分 + QByteArray BA; + QJsonDocument JD; + QJsonParseError JPE; + + BA = reply->readAll(); + // QTextCodec *codec = QTextCodec::codecForName("UTF-8"); + // QString all = codec->toUnicode(BA); + // qDebug() << "reply is:" << all; + + JD = QJsonDocument::fromJson(BA, &JPE); + if (JPE.error == QJsonParseError::NoError) + { + if (JD.isObject()) + { + QJsonObject kylinWeatherObj = JD.object().value("KylinWeather").toObject(); + + QString nowWeather = kylinWeatherObj.value("weather").toObject().value("now").toString(); + m_city_name = kylinWeatherObj.value("weather").toObject().value("location").toString(); + QStringList nowList = nowWeather.split(","); + for(QString now : nowList) + { + if(now.contains("cond_txt")) { + m_cond_txt = now.mid(9); + } + + if (now.contains("tmp")){ + m_temperature = now.mid(4); + } + } + emit onWeatherUpdate(m_city_name, m_cond_txt, m_temperature); + } + } else { + qWarning() << "get weather info error : " << JPE.errorString(); + } + + reply->deleteLater(); +} + +QPixmap WeatherManager::getWeatherIcon() +{ + return getWeatherIcon(m_cond_txt); +} + +QPixmap WeatherManager::getWeatherIcon(QString cond) +{ + if (cond.isEmpty()) + { + qWarning() << "cond info is unknown"; + return QPixmap(":/image/assets/weather/55.png").scaled(32,32); + } + + //根据m_cond_txt + QString numStr = weatherMap.value(cond); + if (!numStr.isEmpty()) { + qDebug() << "----------------numStr=" + numStr; + return QPixmap(":/image/assets/weather/" + numStr +".png").scaled(32,32); + } + + return QPixmap(":/image/assets/weather/55.png").scaled(32,32); +} + +LocalWeatherInfo::LocalWeatherInfo(QObject *parent) +{ +} + +bool LocalWeatherInfo::isTimeValid() +{ + if (m_update_time != nullptr && !m_update_time.isEmpty()) + { + QString strBuffer; + QDateTime time = QDateTime::fromString(m_update_time, "yyyy-MM-dd hh:mm:ss"); + QDateTime currentTime = QDateTime::currentDateTime(); + + if (!time.isValid()) + return false; + + uint timeInterval = currentTime.toTime_t() - time.toTime_t(); + + if((timeInterval <= 21 * 60) + && (timeInterval > 0)) + { + //麒麟天气更新时间为20分钟,加入1分钟容错机制 + return true; + } + } + return false; +} + +void LocalWeatherInfo::setTime(QString time) +{ + m_update_time = time; +} + +QString LocalWeatherInfo::getTime() +{ + return m_update_time; +} + +void LocalWeatherInfo::setCityId(QString cityId) +{ + m_city_id = cityId; +} + +QString LocalWeatherInfo::getCityId() +{ + return m_city_id; +} + +void LocalWeatherInfo::setCityName(QString cityName) +{ + m_city_name = cityName; +} + +QString LocalWeatherInfo::getCityName() +{ + return m_city_name; +} + +void LocalWeatherInfo::setCondText(QString condText) +{ + m_cond_text = condText; +} + +QString LocalWeatherInfo::getCondText() +{ + return m_cond_text; +} + +void LocalWeatherInfo::setAirHumidity(QString airHumidity) +{ + m_air_humidity = airHumidity; +} + +QString LocalWeatherInfo::getAirHumidity() +{ + return m_air_humidity; +} + +void LocalWeatherInfo::setTemperature(QString temperature) +{ + m_temperature = temperature; +} + +QString LocalWeatherInfo::getTemperature() +{ + return m_temperature; +} + +void LocalWeatherInfo::setWindDirection(QString windDirection) +{ + m_wind_direction = windDirection; +} + +QString LocalWeatherInfo::getWindDirection() +{ + return m_wind_direction; +} + +void LocalWeatherInfo::setWindForce(QString windForce) +{ + m_wind_force = windForce; +} + +QString LocalWeatherInfo::getWindForce() +{ + return m_wind_force; +} diff --git a/src/weathermanager.h b/src/weathermanager.h new file mode 100644 index 0000000..b1d2783 --- /dev/null +++ b/src/weathermanager.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Authors: ZHAI Kang-ning +**/ +#ifndef WEATHERMANAGER_H +#define WEATHERMANAGER_H + +#include +#include +#include +#include + +class QNetworkAccessManager; +class QNetworkReply; +class LocalWeatherInfo; + +class WeatherManager : public QObject +{ + Q_OBJECT +public: + explicit WeatherManager(QObject *parent = nullptr); + +Q_SIGNALS: + void onWeatherUpdate(QString city, QString cond, QString temperature); + +private Q_SLOTS: + void replyFinished(QNetworkReply *); + +public: + void getWeather(); + QPixmap getWeatherIcon(); + QPixmap getWeatherIcon(QString cond); + +private: + bool updateLocation();//更新位置,从用户设置获取城市信息,如有多个,只取第一个,未对接 + void weatherRequest(); + + bool getLogcalWeather(); + QString getLogcalCityId(); + +private: + QString m_city_id; // "101030100" 默认天津 + QString m_city_name; + QString m_cond_txt; //天气条件 晴、阴等 + QString m_temperature;//温度 10、20等 + + QNetworkAccessManager *m_net_manager; + QTimer *m_timer; + QGSettings *m_settings; + + LocalWeatherInfo *m_local_weather_info; +}; + +class LocalWeatherInfo : QObject +{ + //"1920-08-27 10:17:42,101310204,澄迈,小雨,95%,25℃,北风,1级," 时间,城市编码,城市名称,天气,湿度,温度,风向,风力 + Q_OBJECT +public: + explicit LocalWeatherInfo(QObject *parent = nullptr); + +private: + QString m_update_time; + QString m_city_id; + QString m_city_name; + QString m_cond_text; + QString m_air_humidity; + QString m_temperature; + QString m_wind_direction; + QString m_wind_force; +public: + bool isTimeValid(); + + void setTime(QString time); + QString getTime(); + + void setCityId(QString cityId); + QString getCityId(); + + void setCityName(QString cityName); + QString getCityName(); + + void setCondText(QString condText); + QString getCondText(); + + void setAirHumidity(QString airHumidity); + QString getAirHumidity(); + + void setTemperature(QString temperature); + QString getTemperature(); + + void setWindDirection(QString windDirection); + QString getWindDirection(); + + void setWindForce(QString windForce); + QString getWindForce(); +}; + +#endif // WEATHERMANAGER_H diff --git a/src/wechatauthdialog.cpp b/src/wechatauthdialog.cpp new file mode 100644 index 0000000..8deb3ed --- /dev/null +++ b/src/wechatauthdialog.cpp @@ -0,0 +1,364 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Authors: ZHAI Kang-ning +**/ +#include "wechatauthdialog.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "eduplatforminterface.h" + +WeChatAuthDialog::WeChatAuthDialog(QWidget *parent) : QWidget(parent), + m_networkManager(new QNetworkAccessManager(this)), + m_loginQR(new QRLabel(this)), + m_title(new QLabel(this)), + m_guide(new QLabel(this)), + m_errorMsg(new QLabel(this)), + m_networkWatcher(new NetWorkWatcher(this)), + m_timer(new QTimer(this)) +{ + initUI(); +} + +WeChatAuthDialog::WeChatAuthDialog(int type, QWidget *parent) : QWidget(parent), + m_networkManager(new QNetworkAccessManager(this)), + m_loginQR(new QRLabel(this)), + m_title(new QLabel(this)), + m_guide(new QLabel(this)), + m_errorMsg(new QLabel(this)), + m_networkWatcher(new NetWorkWatcher(this)), + m_timer(new QTimer(this)) +{ + m_authType = type; + initUI(); +} + +void WeChatAuthDialog::initUI() +{ + /** + * ************标题*************** + * **********提示信息************** + * **********二维码**************** + * **********错误信息*************** + */ + + this->setContentsMargins(0, 0, 0, 0); + QVBoxLayout *vbLayout = new QVBoxLayout(this); + vbLayout->setContentsMargins(0, 0, 0, 0); + + if (m_authType == authType::login) + { + m_title->setText(tr("Login by wechat")); + m_guide->setText(tr("「 Use registered WeChat account to login 」")); + } else if (m_authType == authType::check) + { + m_title->setText(tr("Verification by wechat")); + m_guide->setText(tr("「 Use bound WeChat account to verification 」")); + } + + m_errorMsg->setText(""); + m_title->setStyleSheet("font-size:32px;color:#ffffff"); + m_guide->setStyleSheet("font-size:24px;color: rgba(255,255,255,192);"); + m_errorMsg->setStyleSheet("font-size:14px;color:#FD625E;"); + m_guide->setWordWrap(true); + m_title->adjustSize(); + m_guide->adjustSize(); + + QPixmap pixmap(":/image/assets/intel/icon-wechat-noqrcode.png"); + m_loginQR->setPixmap(pixmap.scaled(188, 188, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); + m_loginQR->setStyleSheet("QLabel{border-radius: 16px;background:rgb(255,255,255);color:white}"); + //m_loginQR->resize(208,208); + + vbLayout->addWidget(m_title, 0, Qt::AlignCenter); + vbLayout->addSpacerItem(new QSpacerItem(0,16,QSizePolicy::Fixed,QSizePolicy::Preferred)); + vbLayout->addWidget(m_guide, 0, Qt::AlignCenter); + vbLayout->addSpacerItem(new QSpacerItem(0,30,QSizePolicy::Fixed,QSizePolicy::Preferred)); + vbLayout->addWidget(m_loginQR, 0, Qt::AlignCenter); + vbLayout->addWidget(m_errorMsg, 0 ,Qt::AlignCenter); + vbLayout->addStretch(1); + this->adjustSize(); + + + +// //监听网络状态变化 +// QDBusConnection::systemBus().connect(QString("org.freedesktop.NetworkManager"), +// QString("/org/freedesktop/NetworkManager"), +// QString("org.freedesktop.NetworkManager"), +// QString("StateChanged"), this, SLOT(onNetworkStateChanged(uint))); + + connect(m_networkWatcher, &NetWorkWatcher::NetworkStateChanged, this, &WeChatAuthDialog::onNetworkStateChanged); + + connect(m_timer, &QTimer::timeout, this, [=]{ + m_errorMsg->clear(); + }); + +} + +void WeChatAuthDialog::onNetworkStateChanged(uint state) +{ + if(isHidden()) + return; + + if(NM_STATE_CONNECTED_GLOBAL == state) + showErrorMessage(" ", true); + else + showErrorMessage(tr("Network not connected~"), true); + +} + +void WeChatAuthDialog::onReset() +{ + if (m_authType == authType::login) + { + m_title->setText(tr("Login by wechat")); + m_guide->setText(tr("「 Use registered WeChat account to login 」")); + } else if (m_authType == authType::check) + { + m_title->setText(tr("Verification by wechat")); + m_guide->setText(tr("「 Use bound WeChat account to verification 」")); + } + m_guide->show(); + + QPixmap pixmap(":/image/assets/intel/icon-wechat-noqrcode.png"); + m_loginQR->setPixmap(pixmap.scaled(188, 188, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); + m_loginQR->setStyleSheet("QLabel{border-radius: 16px;background:rgb(255,255,255);color:white}"); + //m_loginQR->resize(208,208); + reloadQR(); +} + +void WeChatAuthDialog::initConnect() +{ + QObject::connect(m_networkManager, &QNetworkAccessManager::finished, + this, &WeChatAuthDialog::replyFinished); +} + +void WeChatAuthDialog::getLoginQR() +{ + if(!m_requestQRThread) + { + qDebug() << "[WeChatAuthDialog] getLoginQR create new requestQRThread"; + m_requestQRThread = new RequestQRThread(this); + connect(m_requestQRThread, &RequestQRThread::resultReady, this, &WeChatAuthDialog::onQRUrlReady); + } + + if(!m_isThreadWorking){ + qDebug() << "[WeChatAuthDialog] getLoginQR requestQRThread is not running"; + m_requestQRThread->start(); + m_isThreadWorking = true; + } else { + qDebug() << "[WeChatAuthDialog] getLoginQR requestQRThread is running"; + } +} + +void WeChatAuthDialog::replyFinished(QNetworkReply *reply) +{ + if(!reply->isFinished()) + { + //超时 + reply->abort(); + qWarning() << "error: [WeChatAuthDialog][replyFinished]: network timeout"; + showErrorMessage(tr("Network not connected~")); + reloadQR(); + return; + } + + if (reply->error() == QNetworkReply::NoError) + { + QByteArray bytes = reply->readAll(); + + QSize size(188, 188); + QBitmap mask(size); + QPainter painter(&mask); + painter.setRenderHint(QPainter::Antialiasing); + painter.setRenderHint(QPainter::SmoothPixmapTransform); + painter.fillRect(0, 0, size.width(), size.height(), Qt::white); + painter.setBrush(QColor(0, 0, 0)); + painter.drawRoundedRect(0, 0, size.width(), size.height(), 16, 16); + + QPixmap pixmap; + pixmap.loadFromData(bytes); + QPixmap pixmap_ = pixmap.scaled(188, 188, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + + pixmap_.setMask(mask); + m_loginQR->setPixmap(pixmap_); + //m_loginQR->resize(208,208); + } + else + { + qWarning() << "网络错误" << reply->error(); + reloadQR(); + // 错误处理-显示错误信息,或显示上一次缓存的图片或叉图。 + } +} + +void WeChatAuthDialog::qRStatusChangeSolt(QString loginname, QString loginpwd, int curstatus) +{ + qDebug() << "---loginname:" << loginname << "---loginpwd:" << loginpwd << "---curstatus:" << curstatus; + QRCodeSwepState state = static_cast(curstatus); + switch (state) { + case QRCodeSwepState::WaitingSwep: + qDebug() << "info: [WeChatAuthDialog][qRStatusChangeSolt]: waiting user swep qrcode!"; + break; + case QRCodeSwepState::HaveSwep: + qDebug() << "info: [WeChatAuthDialog][qRStatusChangeSolt]: user has swep code!"; + m_title->setText(tr("Scan code successfully")); + m_guide->setText(" "); + m_errorMsg->setText(""); + break; + case QRCodeSwepState::CancelSwep: + qDebug() << "info: [WeChatAuthDialog][qRStatusChangeSolt]: user cancel swep code!"; + onReset(); + m_errorMsg->setText(""); + break; + case QRCodeSwepState::ConfirmSuccess: + { + //Todo 判断是否是该用户 + qDebug() << "info: [WeChatAuthDialog][qRStatusChangeSolt]: user = [" << loginname << "] confirm success!"; + Q_EMIT qRStatusChange(loginname, loginpwd, curstatus); + } + case QRCodeSwepState::QRCodeInvalid: + { + qDebug() << "info: [WeChatAuthDialog][onQRCodeStateChanged]: qrcode invalid!"; + onReset(); + break; + }case QRCodeSwepState::QRCodeTimeout: + { + qDebug() << "info: [UserSecondQRCodeLogin][onQRCodeState]: qrcode timeout!"; + onReset(); + // 更新错误信息 + showErrorMessage(tr("Timeout!Try again!")); + break; + } + default: + break; + } +} + + +void WeChatAuthDialog::reloadQR() +{ + if(this->parentWidget()->isHidden() || isHidden()) + return; + qDebug() << "info: [WeChatAuthDialog] [reloadQR] reloadQR"; + getLoginQR(); +} + +void WeChatAuthDialog::showErrorMessage(QString message, bool isLong) +{ + if (m_timer->isActive()) + m_timer->stop(); + onReset(); + m_errorMsg->setText(message); + m_errorMsg->adjustSize(); + m_errorMsg->setGeometry((this->width() - m_errorMsg->width())/2, + this->height() - m_errorMsg->height(), + m_errorMsg->width(), + m_errorMsg->height()); +// if(isLong) +// return; +// QTimer *timer = new QTimer(this); +// connect(timer, &QTimer::timeout, this, [=]{ +// m_errorMsg->setText(" "); +// }); +// timer->start(3000); + m_timer->setSingleShot(true); + m_timer->start(10000); +} + +void WeChatAuthDialog::onQRUrlReady(int errorCode, const QString url) +{ + m_isThreadWorking = false; + if (DBusMsgCode(errorCode) == DBusMsgCode::No_Error) + { + // 从网络上下载二维码 并加载到qrcode + QNetworkRequest request; + request.setUrl(url); + QNetworkReply *reply = m_networkManager->get(request); + QTimer::singleShot(2*1000, this, [=](){ + replyFinished(reply); + }); + } else if(DBusMsgCode(errorCode) == DBusMsgCode::Error_NetWork) + { + qWarning() << "[error] [WeChatAuthDialog] DBus(cn.kylinos.SSOBackend.eduplatform) get url network error"; + QTimer::singleShot(2 * 1000, this, [=]{ + if(this->isVisible() && !m_requestQRThread->isRunning()) + reloadQR(); + }); + } + else { + qWarning() << "[error] connect form DBus(cn.kylinos.SSOBackend.eduplatform)"; + QTimer::singleShot(2 * 1000, this, [=](){ reloadQR(); }); + } +} + +void WeChatAuthDialog::closeEvent(QCloseEvent *event) +{ + if(m_requestQRThread) { + qDebug() << "[WeChatAuthDialog] requestQRThread delete"; + m_requestQRThread->quit(); + m_requestQRThread->wait(); + m_requestQRThread->deleteLater(); + } +} + +void WeChatAuthDialog::showEvent(QShowEvent *event) +{ + m_networkWatcher->checkOnline(); + + onReset(); + connect(EduPlatformInterface::getInstance(),&EduPlatformInterface::QRStatusChange, + this, &WeChatAuthDialog::qRStatusChangeSolt); +} + +void WeChatAuthDialog::hideEvent(QHideEvent *event) +{ + disconnect(EduPlatformInterface::getInstance(),&EduPlatformInterface::QRStatusChange, + this, &WeChatAuthDialog::qRStatusChangeSolt); +} + +QRLabel::QRLabel(QWidget *parent) : QLabel(parent) +{ +} + +void QRLabel::mousePressEvent(QMouseEvent *event) +{ + if(event->button()== Qt::LeftButton) + { + Q_EMIT clicked(); + } +} + +RequestQRThread::RequestQRThread(QObject *parent) : QThread(parent) +{ + // +} + +void RequestQRThread::run() +{ + QString url; + DBusMsgCode ret = EduPlatformInterface::getInstance()->CheckUserByQR(url); + Q_EMIT resultReady(ret, url.toLocal8Bit()); +} diff --git a/src/wechatauthdialog.h b/src/wechatauthdialog.h new file mode 100644 index 0000000..57b183a --- /dev/null +++ b/src/wechatauthdialog.h @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Authors: ZHAI Kang-ning +**/ +#ifndef WECHATAUTHDIALOG_H +#define WECHATAUTHDIALOG_H + +#include +#include +#include +#include +#include + +#include +#include "networkwatcher.h" + +class QRLabel; +class RequestQRThread; + +/** + * @brief The WeChatAuthDialog class + * 微信登录、校验界面 + * 一、标题状态改变: + * 成功 事例: 1、微信登录->扫描成功->正在登录··· + * 微信不相符: 2、微信登录->登录失败 + */ +class WeChatAuthDialog : public QWidget +{ + Q_OBJECT +public: + explicit WeChatAuthDialog(QWidget *parent = nullptr); + explicit WeChatAuthDialog(int type, QWidget *parent = nullptr); + enum authType{ + login = 0, + check, + }; + + void reloadQR(); + void showErrorMessage(QString message, bool isLong = false); +// void updateStatus(int status); + void onReset(); + +protected: + void closeEvent(QCloseEvent *event) override; + void hideEvent(QHideEvent *event) override; + void showEvent(QShowEvent *event) override; + +Q_SIGNALS: + void authenticateCompete(bool result); + void qRStatusChange(QString loginname, QString loginpwd, int curstatus); + +private: + void initUI(); + void initConnect(); + + int m_authType = 0; + QNetworkAccessManager *m_networkManager; + QRLabel *m_loginQR; + QLabel *m_title; //标题 + QLabel *m_guide; //提示 + QLabel *m_errorMsg; //错误信息 + RequestQRThread *m_requestQRThread = nullptr; + bool m_isThreadWorking = false; + NetWorkWatcher *m_networkWatcher; + QTimer *m_timer; + + void getLoginQR(); + +private Q_SLOTS: + void replyFinished(QNetworkReply *); + void qRStatusChangeSolt(QString loginname, QString loginpwd, int curstatus); + void onQRUrlReady(int errorCode, const QString url); + void onNetworkStateChanged(uint state); +}; + +class QRLabel : public QLabel +{ + Q_OBJECT +public: + explicit QRLabel(QWidget *parent = nullptr); + +Q_SIGNALS: + void clicked(); + +protected: + void mousePressEvent(QMouseEvent *event) override; +}; + +class RequestQRThread : public QThread +{ + Q_OBJECT +public: + RequestQRThread(QObject *parent); + +protected: + void run() override; + +Q_SIGNALS: + void resultReady(int errorCode, const QString url); +}; + +#endif // WECHATAUTHDIALOG_H diff --git a/src/xeventmonitor.cpp b/src/xeventmonitor.cpp new file mode 100644 index 0000000..3d2feb4 --- /dev/null +++ b/src/xeventmonitor.cpp @@ -0,0 +1,208 @@ +/* -*- Mode: C++; indent-tabs-mode: nil; tab-width: 4 -*- + * -*- coding: utf-8 -*- + * + * Copyright (C) 2011 ~ 2017 Deepin, Inc. + * 2011 ~ 2017 Wang Yong + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * Author: Wang Yong + * Maintainer: Wang Yong + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include "xeventmonitor.h" +#include +#include +#include +#include +#include + +// Virtual button codes that are not defined by X11. +#define Button1 1 +#define Button2 2 +#define Button3 3 +#define WheelUp 4 +#define WheelDown 5 +#define WheelLeft 6 +#define WheelRight 7 +#define XButton1 8 +#define XButton2 9 + +class XEventMonitorPrivate +{ +public: + XEventMonitorPrivate(XEventMonitor *parent); + virtual ~XEventMonitorPrivate(); + void run(); + +protected: + XEventMonitor *q_ptr; + + bool filterWheelEvent(int detail); + static void callback(XPointer trash, XRecordInterceptData* data); + void handleRecordEvent(XRecordInterceptData *); + void emitButtonSignal(const char *member, xEvent *event); + void emitKeySignal(const char *member, xEvent *event); + +private: + Q_DECLARE_PUBLIC(XEventMonitor) +}; + +XEventMonitorPrivate::XEventMonitorPrivate(XEventMonitor *parent) + : q_ptr(parent) +{ + +} + +XEventMonitorPrivate::~XEventMonitorPrivate() +{ + +} + +void XEventMonitorPrivate::emitButtonSignal(const char *member, xEvent *event) +{ + int x = event->u.keyButtonPointer.rootX; + int y = event->u.keyButtonPointer.rootY; + QMetaObject::invokeMethod(q_ptr, member, + Qt::DirectConnection, + Q_ARG(int, x), + Q_ARG(int, y)); +} + +void XEventMonitorPrivate::emitKeySignal(const char *member, xEvent *event) +{ + if(QX11Info::display()){ + int keyCode = event->u.u.detail; + KeySym keySym = XkbKeycodeToKeysym(QX11Info::display(), event->u.u.detail, 0, 0); + char *keyStr = XKeysymToString(keySym); + QMetaObject::invokeMethod(q_ptr, member, + Qt::AutoConnection, + Q_ARG(int, keyCode)); + QMetaObject::invokeMethod(q_ptr, member, + Qt::AutoConnection, + Q_ARG(QString, keyStr)); + } +} + +void XEventMonitorPrivate::run() +{ + Display* display = XOpenDisplay(0); + if (display == 0) { + fprintf(stderr, "unable to open display\n"); + return; + } + + // Receive from ALL clients, including future clients. + XRecordClientSpec clients = XRecordAllClients; + XRecordRange* range = XRecordAllocRange(); + if (range == 0) { + fprintf(stderr, "unable to allocate XRecordRange\n"); + return; + } + + // Receive KeyPress, KeyRelease, ButtonPress, ButtonRelease and MotionNotify events. + memset(range, 0, sizeof(XRecordRange)); + range->device_events.first = KeyPress; + range->device_events.last = MotionNotify; + + // And create the XRECORD context. + XRecordContext context = XRecordCreateContext(display, 0, &clients, 1, &range, 1); + if (context == 0) { + fprintf(stderr, "XRecordCreateContext failed\n"); + return; + } + XFree(range); + + XSync(display, True); + + Display* display_datalink = XOpenDisplay(0); + if (display_datalink == 0) { + fprintf(stderr, "unable to open second display\n"); + return; + } + if (!XRecordEnableContext(display_datalink, context, callback, (XPointer) this)) { + fprintf(stderr, "XRecordEnableContext() failed\n"); + return; + } + +} + +void XEventMonitorPrivate::callback(XPointer ptr, XRecordInterceptData* data) +{ + ((XEventMonitorPrivate *) ptr)->handleRecordEvent(data); +} + +void XEventMonitorPrivate::handleRecordEvent(XRecordInterceptData* data) +{ + + if (data->category == XRecordFromServer) { + xEvent * event = (xEvent *)data->data; + switch (event->u.u.type) + { + case ButtonPress: + if (filterWheelEvent(event->u.u.detail)) { + emitButtonSignal("buttonPress", event); + } + break; + case MotionNotify: + emitButtonSignal("buttonDrag", event); + break; + case ButtonRelease: + if (filterWheelEvent(event->u.u.detail)) { + emitButtonSignal("buttonRelease", event); + } + break; + case KeyPress: + emitKeySignal("keyPress", event); + break; + case KeyRelease: + emitKeySignal("keyRelease", event); + break; + default: + break; + } + } + fflush(stdout); + XRecordFreeData(data); +} + +bool XEventMonitorPrivate::filterWheelEvent(int detail) +{ + return detail != WheelUp && detail != WheelDown && detail != WheelLeft && detail != WheelRight; +} + + +XEventMonitor::XEventMonitor(QObject *parent) + : QThread(parent), + d_ptr(new XEventMonitorPrivate(this)) +{ + Q_D(XEventMonitor); +} + +XEventMonitor::~XEventMonitor() +{ + requestInterruption(); + quit(); + wait(); +} + +void XEventMonitor::run() +{ + if(!isInterruptionRequested()) + { + d_ptr->run(); + } +} diff --git a/src/xeventmonitor.h b/src/xeventmonitor.h new file mode 100644 index 0000000..b30099d --- /dev/null +++ b/src/xeventmonitor.h @@ -0,0 +1,58 @@ +/* -*- Mode: C++; indent-tabs-mode: nil; tab-width: 4 -*- + * -*- coding: utf-8 -*- + * + * Copyright (C) 2011 ~ 2017 Deepin, Inc. + * 2011 ~ 2017 Wang Yong + * Copyright (C) 2018 Tianjin KYLIN Information Technology Co., Ltd. + * + * Author: Wang Yong + * Maintainer: Wang Yong + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef XEVENTMONITOR_H +#define XEVENTMONITOR_H + +#include +#include +#include + +class XEventMonitorPrivate; +class XEventMonitor : public QThread +{ + Q_OBJECT + +public: + XEventMonitor(QObject *parent = 0); + ~XEventMonitor(); + +Q_SIGNALS: + void buttonPress(int x, int y); + void buttonDrag(int x, int y); + void buttonRelease(int x, int y); + void keyPress(int keyCode); + void keyRelease(int keyCode); + void keyPress(const QString &key); + void keyRelease(const QString &key); + +protected: + void run(); + +private: + XEventMonitorPrivate *d_ptr; + Q_DECLARE_PRIVATE(XEventMonitor) +}; + +#endif

(uGX}q7{uI^2V&I`C=g6EQ3+P=SkoL_w?~wL{wgWH(xFQm*nx5cRY3z zP`;BDeiVchEw}K(xdh&!UMQD#2|(VKOAySwt?9yudknPX5=6yF=6gx0M}ctQ zZZWd+07+p}iLDDUu`_N}RDxInP|lN(n1kOf&w!;KK`P+N8~aH50{d06h~5@>U?kPu zl>ND-Sq%0G_^=fCx33-2A6xs?1>E4?_cndTAH6qh!U&>qp-(oDl z6{@cvE~V)7WtdBV=E@Tu>Tfp;{y0OXo%caEVFF0>)_@(R0An1|O$A0hfhZT#e zlu$5G8cd_9;?rMmwM|6$6UF$Qp2Uam9?;{L-&L0jz>EHI-+i1tu=Dj-k>b~vjMo!z z{l(-GgzbLQfV4gmBpG4pKnB$G2EKxkBT2XZ(q8iew93c@g9rf15RJ&90akoek(vZC zx|uEnqk+J&>-s3bn3$b}0DT?;A0>&8ZVvD#w-HS*ze_F`fR}FPa36Qn?l*5MS>3^e z8wqqHLtO)++y@Q)E?`zJflLT18+n6$WA1Da39yCanSxxydS)HR#6G4gASRTgJQg7B z1GpbVpCzR}n`ZJ;R~{Jt#oxP|_AbB5mkYq<$0d6F^sjRF?743qQ;JT2=^%(#nXm@p zRZ>{%FDI*=ezHuA4h!U@IAbX?V$K=JG%%?q4B8h&6DT!-5c>e{0XR!UXF&86L!BWK xPZ9Wxg7_vBJ$$%7{Myg_2<={e6<)lL{|}ek;g~_4l&1gy002ovPDHLkV1jp&74`rC literal 0 HcmV?d00001 diff --git a/src/assets/01-default-community.png b/src/assets/01-default-community.png new file mode 100644 index 0000000000000000000000000000000000000000..82ede8561964da89c78c73493286e8b07fa0024d GIT binary patch literal 63677 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