diff -Nru ckb-next-0.4.1/CHANGELOG.md ckb-next-0.4.2/CHANGELOG.md --- ckb-next-0.4.1/CHANGELOG.md 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/CHANGELOG.md 2019-10-08 12:56:16.000000000 +0000 @@ -1,5 +1,24 @@ # Change Log +## [v0.4.2](https://github.com/ckb-next/ckb-next/tree/v0.4.2) (2019-10-08) +[Full Changelog](https://github.com/ckb-next/ckb-next/compare/v0.4.1...v0.4.2) + +This update fixes ckb-next for macOS Catalina + +Support for new devices: +- Harpoon RGB Pro +- Ironclaw RGB + +Important bugfixes: + +- ckb-next-daemon now correctly requests permission on macOS Catalina +- Music visualiser is now included again in macOS packages +- GUI no longer crashes if an animation has no keys set + +Notes: + +- Included quazip was updated to fix deprecation warnings + ## [v0.4.1](https://github.com/ckb-next/ckb-next/tree/v0.4.1) (2019-08-27) [Full Changelog](https://github.com/ckb-next/ckb-next/compare/v0.4.0...v0.4.1) diff -Nru ckb-next-0.4.1/debian/changelog ckb-next-0.4.2/debian/changelog --- ckb-next-0.4.1/debian/changelog 2019-09-08 18:57:41.000000000 +0000 +++ ckb-next-0.4.2/debian/changelog 2019-10-08 14:46:43.000000000 +0000 @@ -1,3 +1,9 @@ +ckb-next (0.4.2-1~eoan) eoan; urgency=low + + * 0.4.2 release + + -- Tasos Sahanidis Tue, 08 Oct 2019 17:46:43 +0300 + ckb-next (0.4.1-1~eoan) eoan; urgency=low * v0.4.1 release diff -Nru ckb-next-0.4.1/debian/rules ckb-next-0.4.2/debian/rules --- ckb-next-0.4.1/debian/rules 2019-09-08 18:57:41.000000000 +0000 +++ ckb-next-0.4.2/debian/rules 2019-10-08 13:49:40.000000000 +0000 @@ -5,4 +5,4 @@ override_dh_auto_configure: dh_auto_configure -- \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_LIBEXECDIR="lib" -DFORCE_INIT_SYSTEM="systemd" -DCMAKE_INSTALL_LIBDIR="lib" + -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_LIBEXECDIR="lib" -DFORCE_INIT_SYSTEM="systemd" -DCMAKE_INSTALL_LIBDIR="lib" -DDISABLE_UPDATER=1 diff -Nru ckb-next-0.4.1/macos/daemon-restart.sh ckb-next-0.4.2/macos/daemon-restart.sh --- ckb-next-0.4.1/macos/daemon-restart.sh 1970-01-01 00:00:00.000000000 +0000 +++ ckb-next-0.4.2/macos/daemon-restart.sh 2019-10-08 12:56:16.000000000 +0000 @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +echo "Please enter your password to restart ckb-next-daemon" +sudo launchctl unload /Library/LaunchDaemons/org.ckb-next.daemon.plist +sudo launchctl load -w /Library/LaunchDaemons/org.ckb-next.daemon.plist +echo "Finished!" diff -Nru ckb-next-0.4.1/macos/org.ckb-next.daemon_agent.plist ckb-next-0.4.2/macos/org.ckb-next.daemon_agent.plist --- ckb-next-0.4.1/macos/org.ckb-next.daemon_agent.plist 1970-01-01 00:00:00.000000000 +0000 +++ ckb-next-0.4.2/macos/org.ckb-next.daemon_agent.plist 2019-10-08 12:56:16.000000000 +0000 @@ -0,0 +1,19 @@ + + + + + Label + org.ckb-next.daemon + Disabled + + KeepAlive + + ProgramArguments + + /Library/Application Support/ckb-next-daemon + --request-hid-permission-because-it-doesnt-work-as-root-thanks-apple + + StandardErrorPath + /tmp/org.ckb-next.daemon_agent.log + + diff -Nru ckb-next-0.4.1/macos/pkgproj/ckb-next.pkgproj ckb-next-0.4.2/macos/pkgproj/ckb-next.pkgproj --- ckb-next-0.4.1/macos/pkgproj/ckb-next.pkgproj 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/macos/pkgproj/ckb-next.pkgproj 2019-10-08 12:56:16.000000000 +0000 @@ -436,6 +436,8 @@ PAYLOAD_TYPE 0 + PRESERVE_EXTENDED_ATTRIBUTES + SHOW_INVISIBLE SPLIT_FORKS @@ -478,7 +480,7 @@ REFERENCE_PATH RELOCATABLE - + USE_HFS+_COMPRESSION VERSION @@ -697,7 +699,24 @@ CHILDREN - + + + CHILDREN + + GID + 0 + PATH + /Library/LaunchAgents/org.ckb-next.daemon_agent.plist + PATH_TYPE + 0 + PERMISSIONS + 420 + TYPE + 3 + UID + 0 + + GID 0 PATH @@ -967,6 +986,8 @@ PAYLOAD_TYPE 0 + PRESERVE_EXTENDED_ATTRIBUTES + SHOW_INVISIBLE SPLIT_FORKS @@ -1016,7 +1037,7 @@ REFERENCE_PATH RELOCATABLE - + USE_HFS+_COMPRESSION VERSION @@ -1050,7 +1071,17 @@ PROJECT_PRESENTATION BACKGROUND - + + APPAREANCES + + DARK_AQUA + + LIGHT_AQUA + + + SHARED_SETTINGS_FOR_ALL_APPAREANCES + + INSTALLATION TYPE HIERARCHIES @@ -1060,6 +1091,8 @@ LIST + CHILDREN + DESCRIPTION OPTIONS @@ -1071,18 +1104,16 @@ PACKAGE_UUID 32EBDDCD-400C-4E72-BA5F-AA4CD941B262 - REQUIREMENTS - TITLE - TOOLTIP - TYPE 0 UUID 90CC6C15-D584-4F5C-B27F-46B549D316EC + CHILDREN + DESCRIPTION OPTIONS @@ -1094,12 +1125,8 @@ PACKAGE_UUID 9B1AB7CE-075D-4AE0-B1B3-E87B644E9F9C - REQUIREMENTS - TITLE - TOOLTIP - TYPE 0 UUID diff -Nru ckb-next-0.4.1/macos/uninstall.sh ckb-next-0.4.2/macos/uninstall.sh --- ckb-next-0.4.1/macos/uninstall.sh 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/macos/uninstall.sh 2019-10-08 12:56:16.000000000 +0000 @@ -14,6 +14,7 @@ sudo rm -f /Library/LaunchDaemons/ckb-next-daemon.plist sudo rm -f /Library/LaunchDaemons/com.ckb.daemon.plist sudo rm -f /Library/LaunchDaemons/org.ckb-next.daemon.plist +sudo rm -f /Library/LaunchAgents/org.ckb-next.daemon_agent.plist echo "Removing GUI, daemon and support files" sudo rm -rf /Applications/ckb{,-next}.app diff -Nru ckb-next-0.4.1/scripts/mac_build/build_mac_legacy.sh ckb-next-0.4.2/scripts/mac_build/build_mac_legacy.sh --- ckb-next-0.4.1/scripts/mac_build/build_mac_legacy.sh 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/scripts/mac_build/build_mac_legacy.sh 2019-10-08 12:56:16.000000000 +0000 @@ -1,5 +1,10 @@ #!/bin/bash +if [[ "$PATH" == *"/usr/local/opt/qt/bin"* ]]; then + echo "Brew's Qt is in your PATH (/usr/local/opt/qt/bin). Please remove it and try again." + exit +fi + mkdir build cd build -Qt5_DIR=~/Qt5.6.2/5.6/clang_64/ cmake -DCMAKE_BUILD_TYPE=Release -DSAFE_INSTALL=ON -DSAFE_UNINSTALL=ON -DMAC_LEGACY=1 .. -DUSE_BREW_QUAZIP=0 -DUSE_BREW_QT5=0 -DUSE_PORTAUDIO=1 +Qt5_DIR=~/Qt5.6.3/5.6.3/clang_64/ cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.10 -DCMAKE_BUILD_TYPE=Release -DSAFE_INSTALL=ON -DSAFE_UNINSTALL=ON -DMAC_LEGACY=1 .. -DUSE_BREW_QUAZIP=0 -DUSE_BREW_QT5=0 -DUSE_PORTAUDIO=1 -DWITH_MVIZ=1 make -j4 macos-package diff -Nru ckb-next-0.4.1/scripts/mac_build/build_mac.sh ckb-next-0.4.2/scripts/mac_build/build_mac.sh --- ckb-next-0.4.1/scripts/mac_build/build_mac.sh 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/scripts/mac_build/build_mac.sh 2019-10-08 12:56:16.000000000 +0000 @@ -1,5 +1,5 @@ #!/bin/bash mkdir build cd build -Qt5_DIR=/usr/local/opt/qt cmake -DCMAKE_BUILD_TYPE=Release -DSAFE_INSTALL=ON -DSAFE_UNINSTALL=ON -DMAC_LEGACY=0 .. -DUSE_BREW_QUAZIP=0 -DUSE_BREW_QT5=1 -DUSE_PORTAUDIO=1 +Qt5_DIR=/usr/local/opt/qt cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.12 -DCMAKE_BUILD_TYPE=Release -DSAFE_INSTALL=ON -DSAFE_UNINSTALL=ON -DMAC_LEGACY=0 .. -DUSE_BREW_QUAZIP=0 -DUSE_BREW_QT5=1 -DUSE_PORTAUDIO=1 -DWITH_MVIZ=1 make -j4 macos-package diff -Nru ckb-next-0.4.1/src/daemon/CMakeLists.txt ckb-next-0.4.2/src/daemon/CMakeLists.txt --- ckb-next-0.4.1/src/daemon/CMakeLists.txt 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/daemon/CMakeLists.txt 2019-10-08 12:56:16.000000000 +0000 @@ -99,6 +99,7 @@ os.h profile.h protocol.h + request_hid_mac.h structures.h usb.h) endif () @@ -111,6 +112,7 @@ input_mac_legacy.c input_mac_mouse_legacy.c extra_mac.c + request_hid_mac.c keymap_mac.h) elseif (LINUX) target_sources( @@ -220,13 +222,18 @@ message(STATUS "launchd detected") set(CKB_NEXT_INIT_SYSTEM "launchd" CACHE INTERNAL "") - # Import plist + # Import plists message(STATUS "Importing org.ckb-next.daemon.plist (${CKB_NEXT_INIT_SYSTEM})") configure_file( "${ckb-next_SOURCE_DIR}/macos/org.ckb-next.daemon.plist" "${CMAKE_CURRENT_BINARY_DIR}/service/org.ckb-next.daemon.plist" COPYONLY) + configure_file( + "${ckb-next_SOURCE_DIR}/macos/org.ckb-next.daemon_agent.plist" + "${CMAKE_CURRENT_BINARY_DIR}/service/org.ckb-next.daemon_agent.plist" + COPYONLY) + if (SAFE_INSTALL) execute_process( COMMAND sudo launchctl list @@ -417,6 +424,13 @@ PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ + WORLD_READ) + install( + FILES "${CMAKE_CURRENT_BINARY_DIR}/service/org.ckb-next.daemon_agent.plist" + DESTINATION "/Library/LaunchAgents" + PERMISSIONS + OWNER_READ OWNER_WRITE + GROUP_READ WORLD_READ) elseif ("${CKB_NEXT_INIT_SYSTEM}" STREQUAL "systemd") install( diff -Nru ckb-next-0.4.1/src/daemon/input_linux.c ckb-next-0.4.2/src/daemon/input_linux.c --- ckb-next-0.4.1/src/daemon/input_linux.c 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/daemon/input_linux.c 2019-10-08 12:56:16.000000000 +0000 @@ -129,14 +129,12 @@ } // Generate SYN reports to synchronize device -static void isync(usbdevice* kb){ +static void isync(int fd){ struct input_event event; memset(&event, 0, sizeof(event)); event.type = EV_SYN; event.code = SYN_REPORT; - if(write(kb->uinput_kb - 1, &event, sizeof(event)) <= 0) - ckb_warn("uinput write failed: %s\n", strerror(errno)); - if(write(kb->uinput_mouse - 1, &event, sizeof(event)) <= 0) + if(write(fd, &event, sizeof(event)) <= 0) ckb_warn("uinput write failed: %s\n", strerror(errno)); } @@ -159,32 +157,34 @@ event.value = down; is_mouse = !!(scancode & SCAN_MOUSE); } - if(write((is_mouse ? kb->uinput_mouse : kb->uinput_kb) - 1, &event, sizeof(event)) <= 0) - ckb_warn("uinput write failed: %s\n", strerror(errno)); + int fd = (is_mouse ? kb->uinput_mouse : kb->uinput_kb) - 1; + if(write(fd, &event, sizeof(event)) > 0) + isync(fd); else - isync(kb); + ckb_warn("uinput write failed: %s\n", strerror(errno)); } void os_mousemove(usbdevice* kb, int x, int y){ struct input_event event; memset(&event, 0, sizeof(event)); event.type = EV_REL; - if(x != 0){ + int fd=kb->uinput_mouse - 1; + //send X + if(x!=0){ event.code = REL_X; event.value = x; - if(write(kb->uinput_mouse - 1, &event, sizeof(event)) <= 0) + if(write(fd, &event, sizeof(event)) <= 0) ckb_warn("uinput write failed: %s\n", strerror(errno)); - else - isync(kb); } - if(y != 0){ + //send Y + if(y!=0){ event.code = REL_Y; event.value = y; - if(write(kb->uinput_mouse - 1, &event, sizeof(event)) <= 0) + if(write(fd, &event, sizeof(event)) <= 0) ckb_warn("uinput write failed: %s\n", strerror(errno)); - else - isync(kb); } + //send SYN + isync(fd); } void* _ledthread(void* ctx){ diff -Nru ckb-next-0.4.1/src/daemon/keymap.c ckb-next-0.4.2/src/daemon/keymap.c --- ckb-next-0.4.1/src/daemon/keymap.c 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/daemon/keymap.c 2019-10-08 12:56:16.000000000 +0000 @@ -242,6 +242,11 @@ { "thumb10", -1, KEY_CORSAIR }, { "thumb11", -1, KEY_CORSAIR }, { "thumb12", -1, KEY_CORSAIR }, + { 0, -1, KEY_NONE }, + { 0, -1, KEY_NONE }, + { 0, -1, KEY_NONE }, + { 0, -1, KEY_NONE }, + { "profswitch", -1, KEY_CORSAIR }, // Extended mouse buttons (wheel is an axis in HW, 6-8 are recognized by the OS but not present in HW) { "wheelup", -1, SCAN_MOUSE | BTN_WHEELUP }, @@ -351,6 +356,8 @@ // Corsair Mouse Input else if(firstbyte == CORSAIR_IN) corsair_mousecopy(kb->input.keys, buffer); + else if(firstbyte == 0x05 && urblen == 21) // Seems to be on the Ironclaw RGB only + corsair_extended_mousecopy(kb->input.keys, buffer); else ckb_err("Unknown mouse data received in input thread %02x from endpoint %02x\n", firstbyte, ep); } else { @@ -531,6 +538,22 @@ } } +void corsair_extended_mousecopy(unsigned char* kbinput, const unsigned char* urbinput){ + // This handles the ironclaw 0x05 packets. + // So far only two possible values exist. In the future this may need to be rewritten + // in a similar fashion as corsair_mousecopy but we currently do not have enough data to do so. + + if(urbinput[6] & 0b01) + SET_KEYBIT(kbinput, MOUSE_BUTTON_FIRST + 4); + else + CLEAR_KEYBIT(kbinput, MOUSE_BUTTON_FIRST + 4); + + if(urbinput[6] & 0b10) + SET_KEYBIT(kbinput, MOUSE_BUTTON_FIRST + 3); + else + CLEAR_KEYBIT(kbinput, MOUSE_BUTTON_FIRST + 3); +} + void m95_mouse_translate(unsigned char* kbinput, short* xaxis, short* yaxis, int length, const unsigned char* urbinput){ if(length != 7) return; diff -Nru ckb-next-0.4.1/src/daemon/keymap.h ckb-next-0.4.2/src/daemon/keymap.h --- ckb-next-0.4.1/src/daemon/keymap.h 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/daemon/keymap.h 2019-10-08 12:56:16.000000000 +0000 @@ -34,8 +34,8 @@ // Mousepad zone count #define N_MOUSEPAD_ZONES 15 // Mouse buttons -#define N_BUTTONS_HW 20 -#define N_BUTTONS_EXTENDED 25 +#define N_BUTTONS_HW 25 +#define N_BUTTONS_EXTENDED 30 #define MOUSE_BUTTON_FIRST (N_KEYS_HW + N_KEY_ZONES + N_KEYS_EXTRA + N_GENERIC_ZONES) #define MOUSE_EXTRA_FIRST (MOUSE_BUTTON_FIRST + N_BUTTONS_HW) // Number of keys that generate input @@ -77,6 +77,7 @@ // Copies input from Corsair reports void corsair_kbcopy(unsigned char* kbinput, const unsigned char* urbinput); void corsair_mousecopy(unsigned char* kbinput, const unsigned char* urbinput); +void corsair_extended_mousecopy(unsigned char* kbinput, const unsigned char* urbinput); void m95_mouse_translate(unsigned char* kbinput, short* xaxis, short* yaxis, int length, const unsigned char* urbinput); #endif // KEYMAP_H diff -Nru ckb-next-0.4.1/src/daemon/keymap_patch.c ckb-next-0.4.2/src/daemon/keymap_patch.c --- ckb-next-0.4.1/src/daemon/keymap_patch.c 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/daemon/keymap_patch.c 2019-10-08 12:56:16.000000000 +0000 @@ -28,12 +28,12 @@ #define M95PATCH_LEN sizeof(m95patch)/sizeof(*m95patch) keypatches mappatches[] = { - { P_K68, k63patch, K63PATCH_LEN }, - { P_K68_NRGB, k63patch, K63PATCH_LEN }, - { P_K65, k65patch, K65PATCH_LEN }, - { P_K65_LEGACY, k65patch, K65PATCH_LEN }, - { P_K63_NRGB, k63patch, K63PATCH_LEN }, - { P_M95, m95patch, M95PATCH_LEN }, + { V_CORSAIR, P_K68, k63patch, K63PATCH_LEN }, + { V_CORSAIR, P_K68_NRGB, k63patch, K63PATCH_LEN }, + { V_CORSAIR, P_K65, k65patch, K65PATCH_LEN }, + { V_CORSAIR, P_K65_LEGACY, k65patch, K65PATCH_LEN }, + { V_CORSAIR, P_K63_NRGB, k63patch, K63PATCH_LEN }, + { V_CORSAIR, P_M95, m95patch, M95PATCH_LEN }, }; #define KEYPATCHES_LEN sizeof(mappatches)/sizeof(*mappatches) @@ -47,7 +47,7 @@ memcpy(kb->keymap, keymap, sizeof(keymap)); // Iterate through the patches for all devices for(size_t pos = 0; pos < KEYPATCHES_LEN; pos++){ - if(mappatches[pos].product == kb->product){ + if(mappatches[pos].vendor == kb->vendor && mappatches[pos].product == kb->product){ // Iterate through the keys in the selected patch for(size_t i = 0; i < mappatches[pos].patchlen; i++){ keypatch* curpatch = mappatches[pos].patch; diff -Nru ckb-next-0.4.1/src/daemon/keymap_patch.h ckb-next-0.4.2/src/daemon/keymap_patch.h --- ckb-next-0.4.1/src/daemon/keymap_patch.h 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/daemon/keymap_patch.h 2019-10-08 12:56:16.000000000 +0000 @@ -8,7 +8,8 @@ // Collection of keypatches typedef struct { - short product; + ushort product; + ushort vendor; keypatch* patch; unsigned patchlen; } keypatches; diff -Nru ckb-next-0.4.1/src/daemon/main.c ckb-next-0.4.2/src/daemon/main.c --- ckb-next-0.4.1/src/daemon/main.c 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/daemon/main.c 2019-10-08 12:56:16.000000000 +0000 @@ -3,6 +3,7 @@ #include "input.h" #include "led.h" #include "notify.h" +#include "request_hid_mac.h" #include // usb.c @@ -166,6 +167,11 @@ } } +#ifdef OS_MAC + if(argc == 2 && getuid() != 0 && !(strcmp(argv[1], "--request-hid-permission-because-it-doesnt-work-as-root-thanks-apple"))) + return request_hid_access_mac(); +#endif + // Check PID, quit if already running char pidpath[strlen(devpath) + 6]; snprintf(pidpath, sizeof(pidpath), "%s0/pid", devpath); diff -Nru ckb-next-0.4.1/src/daemon/request_hid_mac.c ckb-next-0.4.2/src/daemon/request_hid_mac.c --- ckb-next-0.4.1/src/daemon/request_hid_mac.c 1970-01-01 00:00:00.000000000 +0000 +++ ckb-next-0.4.2/src/daemon/request_hid_mac.c 2019-10-08 12:56:16.000000000 +0000 @@ -0,0 +1,29 @@ +#include "request_hid_mac.h" + +extern CFMutableDictionaryRef create_hid_device_dict(); + +hid_req_ret request_hid_access_mac(){ + CFMutableDictionaryRef match_dict = create_hid_device_dict(); + + io_service_t device = IOServiceGetMatchingService(kIOMasterPortDefault, match_dict); + if(!device){ + // Free? + return REQUEST_ERROR; + } + + IOHIDDeviceRef ref = IOHIDDeviceCreate(kCFAllocatorDefault, device); + + // Try to open the device + IOReturn ret = IOHIDDeviceOpen(ref, kIOHIDOptionsTypeNone); + if(ret == kIOReturnNotPermitted){ + // Is there a way to check if our request even succeeded? + return REQUEST_SUCCEEDED; + } else if(ret == kIOReturnSuccess || ret == kIOReturnExclusiveAccess) { + IOHIDDeviceClose(ref, kIOHIDOptionsTypeNone); + return REQUEST_ALREADY_ALLOWED; + } + + // If we get here, an unknown error occurred. It may or may not be fatal, we can't really know. + fprintf(stderr, "[E] IOHIDDeviceOpen returned %x\n", ret); + return REQUEST_ERROR; +} diff -Nru ckb-next-0.4.1/src/daemon/request_hid_mac.h ckb-next-0.4.2/src/daemon/request_hid_mac.h --- ckb-next-0.4.1/src/daemon/request_hid_mac.h 1970-01-01 00:00:00.000000000 +0000 +++ ckb-next-0.4.2/src/daemon/request_hid_mac.h 2019-10-08 12:56:16.000000000 +0000 @@ -0,0 +1,15 @@ +#ifndef REQUEST_HID_MAC_H +#define REQUEST_HID_MAC_H +#include "os.h" + +#ifdef OS_MAC +typedef enum { + REQUEST_STATE_NOT_SET = -1, + REQUEST_SUCCEEDED = 0, + REQUEST_ALREADY_ALLOWED, + REQUEST_ERROR +} hid_req_ret; + +hid_req_ret request_hid_access_mac(); +#endif // OS_MAC +#endif // REQUEST_HID_MAC_H diff -Nru ckb-next-0.4.1/src/daemon/usb.c ckb-next-0.4.2/src/daemon/usb.c --- ckb-next-0.4.1/src/daemon/usb.c 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/daemon/usb.c 2019-10-08 12:56:16.000000000 +0000 @@ -24,58 +24,66 @@ { P_SCIMITAR_PRO, 16000 }, { P_SABRE_O2, 6400 }, { P_HARPOON, 6000 }, + { P_HARPOON_PRO, 12000 }, { P_KATAR, 8000 }, + { P_IRONCLAW, 18000 }, { 0, 0 }, // Keep last and do not remove }; -ushort models[N_MODELS] = { +// WARNING: macOS doesn't support iVendor != 0x1b1c at the moment +device_desc models[] = { // Keyboards - P_K55, - P_K63_NRGB, - P_K65, - P_K65_LEGACY, - P_K65_LUX, - P_K65_RFIRE, - P_K66, - P_K68, - P_K68_NRGB, - P_K70, - P_K70_LEGACY, - P_K70_LUX, - P_K70_LUX_NRGB, - P_K70_RFIRE, - P_K70_RFIRE_NRGB, - P_K70_MK2, - P_K70_MK2SE, - P_K70_MK2LP, - P_K90_LEGACY, - P_K95, - P_K95_LEGACY, - P_K95_PLATINUM, - P_STRAFE, - P_STRAFE_NRGB, - P_STRAFE_NRGB_2, - P_STRAFE_MK2, + { V_CORSAIR, P_K55, }, + { V_CORSAIR, P_K63_NRGB, }, + { V_CORSAIR, P_K65, }, + { V_CORSAIR, P_K65_LEGACY, }, + { V_CORSAIR, P_K65_LUX, }, + { V_CORSAIR, P_K65_RFIRE, }, + { V_CORSAIR, P_K66, }, + { V_CORSAIR, P_K68, }, + { V_CORSAIR, P_K68_NRGB, }, + { V_CORSAIR, P_K70, }, + { V_CORSAIR, P_K70_LEGACY, }, + { V_CORSAIR, P_K70_LUX, }, + { V_CORSAIR, P_K70_LUX_NRGB, }, + { V_CORSAIR, P_K70_RFIRE, }, + { V_CORSAIR, P_K70_RFIRE_NRGB, }, + { V_CORSAIR, P_K70_MK2, }, + { V_CORSAIR, P_K70_MK2SE, }, + { V_CORSAIR, P_K70_MK2LP, }, + { V_CORSAIR, P_K90_LEGACY, }, + { V_CORSAIR, P_K95, }, + { V_CORSAIR, P_K95_LEGACY, }, + { V_CORSAIR, P_K95_PLATINUM, }, + { V_CORSAIR, P_STRAFE, }, + { V_CORSAIR, P_STRAFE_NRGB, }, + { V_CORSAIR, P_STRAFE_NRGB_2, }, + { V_CORSAIR, P_STRAFE_MK2, }, // Mice - P_M65, - P_M65_PRO, - P_M65_RGB_ELITE, - P_M95, - P_GLAIVE, - P_SABRE_O, - P_SABRE_L, - P_SABRE_N, - P_SCIMITAR, - P_SCIMITAR_PRO, - P_SABRE_O2, - P_HARPOON, - P_KATAR, + { V_CORSAIR, P_M65, }, + { V_CORSAIR, P_M65_PRO, }, + { V_CORSAIR, P_M65_RGB_ELITE, }, + { V_CORSAIR, P_M95, }, + { V_CORSAIR, P_GLAIVE, }, + { V_CORSAIR, P_SABRE_O, }, + { V_CORSAIR, P_SABRE_L, }, + { V_CORSAIR, P_SABRE_N, }, + { V_CORSAIR, P_SCIMITAR, }, + { V_CORSAIR, P_SCIMITAR_PRO, }, + { V_CORSAIR, P_SABRE_O2, }, + { V_CORSAIR, P_HARPOON, }, + { V_CORSAIR, P_HARPOON_PRO, }, + { V_CORSAIR, P_KATAR, }, + { V_CORSAIR, P_IRONCLAW, }, // Mousepads - P_POLARIS, + { V_CORSAIR, P_POLARIS, }, // Headset stands - P_ST100, + { V_CORSAIR, P_ST100, }, }; +size_t N_MODELS = sizeof(models) / sizeof(device_desc); + + /// brief . /// /// \brief reset_stop is boolean: Reset stopper for when the program shuts down. @@ -160,12 +168,14 @@ return "sabre"; if(product == P_SCIMITAR || product == P_SCIMITAR_PRO) return "scimitar"; - if(product == P_HARPOON) + if(product == P_HARPOON || product == P_HARPOON_PRO) return "harpoon"; if(product == P_GLAIVE) return "glaive"; if(product == P_KATAR) return "katar"; + if(product == P_IRONCLAW) + return "ironclaw"; if(product == P_POLARIS) return "polaris"; if(product == P_ST100) diff -Nru ckb-next-0.4.1/src/daemon/usb.h ckb-next-0.4.2/src/daemon/usb.h --- ckb-next-0.4.1/src/daemon/usb.h 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/daemon/usb.h 2019-10-08 12:56:16.000000000 +0000 @@ -42,7 +42,6 @@ /// Currently known for this are \b usb_linux.c and \b usb_mac.c /// #define V_CORSAIR 0x1b1c -#define V_CORSAIR_STR "1b1c" #define P_K55 0x1b3d #define IS_K55(kb) ((kb)->vendor == V_CORSAIR && (kb)->product == P_K55) @@ -105,7 +104,8 @@ #define IS_SCIMITAR(kb) ((kb)->vendor == V_CORSAIR && ((kb)->product == P_SCIMITAR || (kb)->product == P_SCIMITAR_PRO)) #define P_HARPOON 0x1b3c -#define IS_HARPOON(kb) ((kb)->vendor == V_CORSAIR && (kb)->product == P_HARPOON) +#define P_HARPOON_PRO 0x1b75 +#define IS_HARPOON(kb) ((kb)->vendor == V_CORSAIR && ((kb)->product == P_HARPOON || (kb)->product == P_HARPOON_PRO)) #define P_GLAIVE 0x1b34 #define IS_GLAIVE(kb) ((kb)->vendor == V_CORSAIR && (kb)->product == P_GLAIVE) @@ -113,14 +113,23 @@ #define P_KATAR 0x1b22 #define IS_KATAR(kb) ((kb)->vendor == V_CORSAIR && (kb)->product == P_KATAR) +#define P_IRONCLAW 0x1b5d +#define IS_IRONCLAW(kb) ((kb)->vendor == V_CORSAIR && (kb)->product == P_IRONCLAW) + #define P_POLARIS 0x1b3b #define IS_POLARIS(kb) ((kb)->vendor == V_CORSAIR && ((kb)->product == P_POLARIS)) #define P_ST100 0x0a34 #define IS_ST100(kb) ((kb)->vendor == V_CORSAIR && ((kb)->product == P_ST100)) -#define N_MODELS 41 -extern ushort models[]; +extern size_t N_MODELS; + +typedef struct _device_desc { + ushort idVendor; + ushort idProduct; +} device_desc; + +extern device_desc models[]; /// /// Uncomment to see USB packets sent to the device @@ -169,7 +178,7 @@ #define IS_FULLRANGE(kb) (!IS_LEGACY((kb)->vendor, (kb)->product) && (kb)->product != P_K65 && (kb)->product != P_K70 && (kb)->product != P_K95 && (kb)->product != P_STRAFE_NRGB) /// Mouse vs keyboard test -#define IS_MOUSE(vendor, product) ((vendor) == (V_CORSAIR) && ((product) == (P_M65) || (product) == (P_M65_PRO) || (product) == (P_M65_RGB_ELITE) || (product) == (P_M95) || (product) == (P_SABRE_O) || (product) == (P_SABRE_L) || (product) == (P_SABRE_N) || (product) == (P_SCIMITAR) || (product) == (P_SCIMITAR_PRO) || (product) == (P_SABRE_O2) || (product) == (P_GLAIVE) || (product) == (P_HARPOON) || (product) == (P_KATAR))) +#define IS_MOUSE(vendor, product) ((vendor) == (V_CORSAIR) && ((product) == (P_M65) || (product) == (P_M65_PRO) || (product) == (P_M65_RGB_ELITE) || (product) == (P_M95) || (product) == (P_SABRE_O) || (product) == (P_SABRE_L) || (product) == (P_SABRE_N) || (product) == (P_SCIMITAR) || (product) == (P_SCIMITAR_PRO) || (product) == (P_SABRE_O2) || (product) == (P_GLAIVE) || (product) == (P_HARPOON) || (product) == (P_HARPOON_PRO) || (product) == (P_KATAR) || (product) == (P_IRONCLAW))) /// For calling with a usbdevice*, vendor and product are extracted and IS_MOUSE() is returned. #define IS_MOUSE_DEV(kb) IS_MOUSE((kb)->vendor, (kb)->product) @@ -184,7 +193,7 @@ /// Used for new devices that come with V3 firmware endpoint configuration out of the factory, but have fwversion < 0x300. /// Note: only the RGB variant of the K68 needs a v3 override. /// Note: P_K70_MK2 doesn't seem to require this, but it was added as a precaution -#define IS_V3_OVERRIDE(kb) ((kb)->product == P_K68 || (kb)->product == P_K70_MK2 || (kb)->product == P_K70_MK2SE || (kb)->product == P_STRAFE_MK2) +#define IS_V3_OVERRIDE(kb) ((kb)->product == P_K68 || (kb)->product == P_K70_MK2 || (kb)->product == P_K70_MK2SE || (kb)->product == P_STRAFE_MK2 || (kb)->product == P_IRONCLAW) /// Used when a device has a firmware with a low version number that uses the new endpoint configuration. #define IS_V2_OVERRIDE(kb) (IS_V3_OVERRIDE(kb) || IS_PLATINUM(kb) || IS_K63(kb) || IS_K68(kb) || IS_HARPOON(kb) || IS_GLAIVE(kb) || IS_KATAR(kb) || (kb)->product == P_STRAFE_NRGB_2 || IS_POLARIS(kb) || IS_ST100(kb) || (kb)->product == P_SCIMITAR_PRO || (kb)->product == P_K66) @@ -193,10 +202,10 @@ #define IS_SINGLE_EP(kb) (IS_POLARIS(kb) || IS_ST100(kb)) /// Used for devices that use a file-based hardware animation system. -#define USES_FILE_HWSAVE(kb) ((kb)->product == P_K95_PLATINUM || (kb)->product == P_K70_MK2 || (kb)->product == P_K70_MK2SE || (kb)->product == P_STRAFE_MK2 || (kb)->product == P_GLAIVE || (kb)->product == P_SCIMITAR_PRO || (kb)->product == P_K70_MK2LP || (kb)->product == P_M65_RGB_ELITE) +#define USES_FILE_HWSAVE(kb) ((kb)->product == P_K95_PLATINUM || (kb)->product == P_K70_MK2 || (kb)->product == P_K70_MK2SE || (kb)->product == P_STRAFE_MK2 || (kb)->product == P_GLAIVE || (kb)->product == P_SCIMITAR_PRO || (kb)->product == P_K70_MK2LP || (kb)->product == P_M65_RGB_ELITE || (kb)->product == P_IRONCLAW || (kb)->product == P_HARPOON_PRO) /// Devices here support setting the pollrate through software -#define SUPPORTS_ADJRATE(kb) ((kb)->product == P_K63_NRGB || (kb)->product == P_K66 || (kb)->product == P_K68 || (kb)->product == P_K68_NRGB || (kb)->product == P_K70_MK2 || (kb)->product == P_K70_MK2SE || (kb)->product == P_K70_MK2LP || (kb)->product == P_K95_PLATINUM || (kb)->product == P_STRAFE || (kb)->product == P_STRAFE_NRGB || (kb)->product == P_STRAFE_NRGB_2 || (kb)->product == P_STRAFE_MK2 || (kb)->product == P_M65 || (kb)->product == P_M65_PRO || (kb)->product == P_M65_RGB_ELITE || (kb)->product == P_M95 || (kb)->product == P_SABRE_O || (kb)->product == P_SABRE_L || (kb)->product == P_SABRE_N || (kb)->product == P_SABRE_O2 || (kb)->product == P_SCIMITAR || (kb)->product == P_SCIMITAR_PRO || (kb)->product == P_HARPOON || (kb)->product == P_GLAIVE || (kb)->product == P_KATAR) +#define SUPPORTS_ADJRATE(kb) ((kb)->product == P_K63_NRGB || (kb)->product == P_K66 || (kb)->product == P_K68 || (kb)->product == P_K68_NRGB || (kb)->product == P_K70_MK2 || (kb)->product == P_K70_MK2SE || (kb)->product == P_K70_MK2LP || (kb)->product == P_K95_PLATINUM || (kb)->product == P_STRAFE || (kb)->product == P_STRAFE_NRGB || (kb)->product == P_STRAFE_NRGB_2 || (kb)->product == P_STRAFE_MK2 || (kb)->product == P_M65 || (kb)->product == P_M65_PRO || (kb)->product == P_M65_RGB_ELITE || (kb)->product == P_M95 || (kb)->product == P_SABRE_O || (kb)->product == P_SABRE_L || (kb)->product == P_SABRE_N || (kb)->product == P_SABRE_O2 || (kb)->product == P_SCIMITAR || (kb)->product == P_SCIMITAR_PRO || (kb)->product == P_HARPOON || (kb)->product == P_HARPOON_PRO || (kb)->product == P_GLAIVE || (kb)->product == P_KATAR) /// USB delays for when the keyboards get picky about timing /// That was the original comment, but it is used anytime. diff -Nru ckb-next-0.4.1/src/daemon/usb_linux.c ckb-next-0.4.2/src/daemon/usb_linux.c --- ckb-next-0.4.1/src/daemon/usb_linux.c 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/daemon/usb_linux.c 2019-10-08 12:56:16.000000000 +0000 @@ -709,18 +709,24 @@ /// call usbadd() with the model number. static int usb_add_device(struct udev_device* dev){ const char* vendor = udev_device_get_sysattr_value(dev, "idVendor"); - if(vendor && !strcmp(vendor, V_CORSAIR_STR)){ - const char* product = udev_device_get_sysattr_value(dev, "idProduct"); - if(product){ - ushort pid; - if(sscanf(product, "%04hx", &pid) && pid){ - for(size_t c = 0; c < N_MODELS; c++){ - if(models[c] == pid) - return usbadd(dev, V_CORSAIR, models[c]); - } - } - } + const char* product = udev_device_get_sysattr_value(dev, "idProduct"); + if(vendor == NULL || product == NULL) + return 1; + + ushort pid, vid; + pid = vid = 0; + + if(!(sscanf(vendor, "%04hx", &vid) == 1 && vid)) + return 1; + + if(!(sscanf(product, "%04hx", &pid) == 1 && pid)) + return 1; + + for(size_t c = 0; c < N_MODELS; c++){ + if(models[c].idVendor == vid && models[c].idProduct == pid) + return usbadd(dev, models[c].idVendor, models[c].idProduct); } + return 1; } @@ -764,7 +770,6 @@ static void udev_enum(){ struct udev_enumerate* enumerator = udev_enumerate_new(udev); udev_enumerate_add_match_subsystem(enumerator, "usb"); - udev_enumerate_add_match_sysattr(enumerator, "idVendor", V_CORSAIR_STR); udev_enumerate_scan_devices(enumerator); struct udev_list_entry* devices, *dev_list_entry; devices = udev_enumerate_get_list_entry(enumerator); diff -Nru ckb-next-0.4.1/src/daemon/usb_mac.c ckb-next-0.4.2/src/daemon/usb_mac.c --- ckb-next-0.4.1/src/daemon/usb_mac.c 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/daemon/usb_mac.c 2019-10-08 12:56:16.000000000 +0000 @@ -970,6 +970,24 @@ exithandler(type); } +CFMutableDictionaryRef create_hid_device_dict(){ + int vendor = V_CORSAIR; + CFMutableDictionaryRef match = IOServiceMatching(kIOHIDDeviceKey); + CFNumberRef cfvendor = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &vendor); + CFDictionarySetValue(match, CFSTR(kIOHIDVendorIDKey), cfvendor); + CFRelease(cfvendor); + CFMutableArrayRef cfproducts = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + for(size_t c = 0; c < N_MODELS; c++){ + int product = models[c].idProduct; + CFNumberRef cfproduct = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &product); + CFArrayAppendValue(cfproducts, cfproduct); + CFRelease(cfproduct); + } + CFDictionarySetValue(match, CFSTR(kIOHIDProductIDArrayKey), cfproducts); + CFRelease(cfproducts); + return match; +} + int usbmain(){ int vendor = V_CORSAIR; @@ -992,7 +1010,7 @@ CFRelease(cfvendor); CFMutableArrayRef cfproducts = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); for(size_t c = 0; c < N_MODELS; c++){ - int product = models[c]; + int product = models[c].idProduct; CFNumberRef cfproduct = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &product); CFArrayAppendValue(cfproducts, cfproduct); CFRelease(cfproduct); @@ -1012,19 +1030,7 @@ // Now move on to HID devices // It is in fact necessary to recreate the matching dictionary, as the keys are different - match = IOServiceMatching(kIOHIDDeviceKey); - cfvendor = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &vendor); - CFDictionarySetValue(match, CFSTR(kIOHIDVendorIDKey), cfvendor); - CFRelease(cfvendor); - cfproducts = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); - for(size_t c = 0; c < N_MODELS; c++){ - int product = models[c]; - CFNumberRef cfproduct = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &product); - CFArrayAppendValue(cfproducts, cfproduct); - CFRelease(cfproduct); - } - CFDictionarySetValue(match, CFSTR(kIOHIDProductIDArrayKey), cfproducts); - CFRelease(cfproducts); + match = create_hid_device_dict(); io_iterator_t iterator_hid = 0; res = IOServiceAddMatchingNotification(notify, kIOMatchedNotification, match, iterate_devices_hid, 0, &iterator_hid); diff -Nru ckb-next-0.4.1/src/gui/animscript.cpp ckb-next-0.4.2/src/gui/animscript.cpp --- ckb-next-0.4.1/src/gui/animscript.cpp 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/gui/animscript.cpp 2019-10-08 12:56:16.000000000 +0000 @@ -308,9 +308,9 @@ process->write("end params\n"); } -void AnimScript::begin(quint64 timestamp){ +int AnimScript::begin(quint64 timestamp){ if(!initialized) - return; + return 1; end(); stopped = firstFrame = readFrame = readAnyFrame = inFrame = false; // Determine the upper left corner of the given keys @@ -331,7 +331,7 @@ if(keysCopy.isEmpty()){ // If the key list is empty, don't actually start the animation but pretend it's running anyway firstFrame = readFrame = readAnyFrame = true; - return; + return 1; } process = new QProcess(this); process->setReadChannel(QProcess::StandardOutput); @@ -352,6 +352,7 @@ // Begin animating process->write("begin run\n"); lastFrame = timestamp; + return 0; } void AnimScript::retrigger(quint64 timestamp, bool allowPreempt){ @@ -360,28 +361,26 @@ if(allowPreempt && _info.preempt && repeatMsec > 0) // If preemption is wanted, trigger the animation 1 duration in the past first retrigger(timestamp - repeatMsec); - if(!process) - begin(timestamp); + if(!process && begin(timestamp)) + return; advance(timestamp); - if(process) - process->write("start\n"); + process->write("start\n"); } void AnimScript::stop(quint64 timestamp){ if(!initialized) return; - if(!process) - begin(timestamp); + if(!process && begin(timestamp)) + return; advance(timestamp); - if(process) - process->write("stop\n"); + process->write("stop\n"); } void AnimScript::keypress(const QString& key, bool pressed, quint64 timestamp){ if(!initialized) return; - if(!process) - begin(timestamp); + if(!process && begin(timestamp)) + return; int kpMode = _info.kpMode; if(_paramValues.value("kpmode", 0).toInt() != 0) // Disable KP mode according to user preferences @@ -474,17 +473,20 @@ if(!process) begin(timestamp); - advance(timestamp); - if((readFrame || !firstFrame) && process) + if(process){ + advance(timestamp); + // Don't ask for a new frame if the animation hasn't delivered the last one yet - process->write("frame\n"); + if(readFrame || !firstFrame) + process->write("frame\n"); + } firstFrame = true; readFrame = false; } void AnimScript::advance(quint64 timestamp){ - if(timestamp <= lastFrame || !process) - // Don't do anything if the time hasn't actually advanced. + // Don't do anything if the time hasn't actually advanced. + if(timestamp <= lastFrame) return; double delta = (timestamp - lastFrame) / (double)durationMsec; if(!_info.absoluteTime){ diff -Nru ckb-next-0.4.1/src/gui/animscript.h ckb-next-0.4.2/src/gui/animscript.h --- ckb-next-0.4.1/src/gui/animscript.h 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/gui/animscript.h 2019-10-08 12:56:16.000000000 +0000 @@ -147,7 +147,7 @@ // Helper functions void setDuration(); void printParams(); - void begin(quint64 timestamp); + int begin(quint64 timestamp); void advance(quint64 timestamp); // Global script list diff -Nru ckb-next-0.4.1/src/gui/animsettingdialog.cpp ckb-next-0.4.2/src/gui/animsettingdialog.cpp --- ckb-next-0.4.1/src/gui/animsettingdialog.cpp 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/gui/animsettingdialog.cpp 2019-10-08 12:56:16.000000000 +0000 @@ -7,6 +7,7 @@ #include "ui_animsettingdialog.h" #include "colorbutton.h" #include "gradientbutton.h" +#include "qoverloadlegacy.h" // QDial shows angles upside down (180° = top, 0° = bottom), so flip it static inline int angleFlip(int angle){ @@ -26,10 +27,6 @@ ui->animName->setText(anim->name()); const AnimScript* script = anim->script(); - connect(&updateMapper, SIGNAL(mapped(QString)), this, SLOT(updateParam(QString))); - connect(&angleDialMapper, SIGNAL(mapped(QString)), this, SLOT(angleDialChanged(QString))); - connect(&angleSpinnerMapper, SIGNAL(mapped(QString)), this, SLOT(angleSpinnerChanged(QString))); - // Build settings UI int row = 1; ui->settingsGrid->addItem(new QSpacerItem(0, 10, QSizePolicy::Fixed, QSizePolicy::Fixed), row++, 6); @@ -61,7 +58,9 @@ widget = new QCheckBox(param.prefix, this); ((QCheckBox*)widget)->setChecked(value.toBool()); colSpan = 4; - connect(widget, SIGNAL(stateChanged(int)), &updateMapper, SLOT(map())); + connect((QCheckBox*)widget, &QCheckBox::stateChanged, [=] () { + emit updateParam(param.name); + }); break; case AnimScript::Param::LONG: widget = new QSpinBox(this); @@ -72,7 +71,9 @@ ((QSpinBox*)widget)->setSuffix(postfix); postfix = ""; } - connect(widget, SIGNAL(valueChanged(int)), &updateMapper, SLOT(map())); + connect((QSpinBox*)widget, OVERLOAD_PTR(int, QSpinBox, valueChanged), [=] () { + emit updateParam(param.name); + }); break; case AnimScript::Param::DOUBLE: widget = new QDoubleSpinBox(this); @@ -84,13 +85,17 @@ ((QDoubleSpinBox*)widget)->setSuffix(postfix); postfix = ""; } - connect(widget, SIGNAL(valueChanged(double)), &updateMapper, SLOT(map())); + connect((QDoubleSpinBox*)widget, OVERLOAD_PTR(double, QDoubleSpinBox, valueChanged), [=] () { + emit updateParam(param.name); + }); break; case AnimScript::Param::RGB: widget = new ColorButton(this); ((ColorButton*)widget)->color(QColor("#" + value.toString())); colSpan = 3; - connect(widget, SIGNAL(colorChanged(QColor)), &updateMapper, SLOT(map())); + connect((ColorButton*)widget, &ColorButton::colorChanged, [=] () { + emit updateParam(param.name); + }); break; case AnimScript::Param::ARGB:{ widget = new ColorButton(this, true); @@ -103,20 +108,26 @@ color = "#" + val; ((ColorButton*)widget)->color(color); colSpan = 3; - connect(widget, SIGNAL(colorChanged(QColor)), &updateMapper, SLOT(map())); + connect((ColorButton*)widget, &ColorButton::colorChanged, [=] () { + emit updateParam(param.name); + }); break; } case AnimScript::Param::GRADIENT: widget = new GradientButton(this); ((GradientButton*)widget)->fromString(value.toString()); colSpan = 3; - connect(widget, SIGNAL(gradientChanged()), &updateMapper, SLOT(map())); + connect((GradientButton*)widget, &GradientButton::gradientChanged, [=] () { + emit updateParam(param.name); + }); break; case AnimScript::Param::AGRADIENT: widget = new GradientButton(this, true); ((GradientButton*)widget)->fromString(value.toString()); colSpan = 3; - connect(widget, SIGNAL(gradientChanged()), &updateMapper, SLOT(map())); + connect((GradientButton*)widget, &GradientButton::gradientChanged, [=] () { + emit updateParam(param.name); + }); break; case AnimScript::Param::ANGLE: widget = new QDial(this); @@ -132,14 +143,17 @@ ((QDial*)widget)->setWrapping(true); ((QDial*)widget)->setInvertedAppearance(true); ((QDial*)widget)->setValue(angleFlip(value.toInt())); - angleDialMapper.setMapping(widget, param.name); - connect(widget, SIGNAL(valueChanged(int)), &angleDialMapper, SLOT(map())); + connect((QDial*)widget, &QDial::valueChanged, [=] () { + emit angleDialChanged(param.name); + }); break; case AnimScript::Param::STRING: widget = new QLineEdit(this); ((QLineEdit*)widget)->setText(value.toString()); colSpan = 3; - connect(widget, SIGNAL(textEdited(const QString&)), &updateMapper, SLOT(map())); + connect((QLineEdit*)widget, &QLineEdit::textEdited, [=] () { + emit updateParam(param.name); + }); break; case AnimScript::Param::LABEL: widget = new QLabel(this); @@ -149,8 +163,6 @@ default: break; } - if(widget) - updateMapper.setMapping(widget, param.name); if(param.type == AnimScript::Param::BOOL || param.type == AnimScript::Param::LABEL){ // Boolean values are placed on the left with no prefix or postfix settingWidgets[param.name] = widget; @@ -170,8 +182,9 @@ spinner->setSuffix("°"); spinner->setValue(value.toInt()); angleSpinners[param.name] = spinner; - angleSpinnerMapper.setMapping(spinner, param.name); - connect(spinner, SIGNAL(valueChanged(int)), &angleSpinnerMapper, SLOT(map())); + connect(spinner, OVERLOAD_PTR(int, QSpinBox, valueChanged), [=] () { + emit angleSpinnerChanged(param.name); + }); ui->settingsGrid->addWidget(spinner, row, 4); colSpan = 2; } @@ -208,15 +221,17 @@ check->setChecked(anim->parameter("trigger").toBool()); ui->settingsGrid->addWidget(check, row, 3, 1, 4); settingWidgets["trigger"] = check; - connect(check, SIGNAL(stateChanged(int)), &updateMapper, SLOT(map())); - updateMapper.setMapping(check, "trigger"); + connect(check, &QCheckBox::stateChanged, [=] () { + emit updateParam("trigger"); + }); row++; check = new QCheckBox("Start with key press", this); check->setChecked(anim->parameter("kptrigger").toBool()); ui->settingsGrid->addWidget(check, row, 3, 1, 2); settingWidgets["kptrigger"] = check; - connect(check, SIGNAL(stateChanged(int)), &updateMapper, SLOT(map())); - updateMapper.setMapping(check, "kptrigger"); + connect(check, &QCheckBox::stateChanged, [=] () { + emit updateParam("kptrigger"); + }); // Add an option allowing the user to select keypress mode QComboBox* combo = new QComboBox(this); int selected = anim->parameter("kpmode").toInt(); @@ -236,8 +251,9 @@ combo->setCurrentIndex(selected); ui->settingsGrid->addWidget(combo, row, 5, 1, 2); settingWidgets["kpmode"] = combo; - connect(combo, SIGNAL(activated(int)), &updateMapper, SLOT(map())); - updateMapper.setMapping(combo, "kpmode"); + connect(combo, OVERLOAD_PTR(int, QComboBox, activated), [=] () { + emit updateParam("kpmode"); + }); row++; // Add horizontal spacer to compress content to left @@ -252,8 +268,10 @@ ui->kpDelayBox->setValue(anim->parameter("kpdelay").toDouble()); settingWidgets["kpmodestop"] = ui->kpModeStopBox; ui->kpModeStopBox->setChecked(anim->parameter("kpmodestop").toBool()); - connect(ui->kpModeStopBox, SIGNAL(clicked(bool)), &updateMapper, SLOT(map())); - updateMapper.setMapping(ui->kpModeStopBox, "kpmodestop"); + connect(ui->kpModeStopBox, &QCheckBox::clicked, [=] () { + emit updateParam("kpmodestop"); + }); + settingWidgets["kprelease"] = ui->kpReleaseBox; ui->kpReleaseBox->setChecked(anim->parameter("kprelease").toBool()); if(anim->hasParameter("repeat")){ @@ -270,8 +288,9 @@ spinner->setMaximum(1000000); spinner->setValue(anim->parameter("stop").toInt()); settingWidgets["stop"] = spinner; - connect(spinner, SIGNAL(valueChanged(int)), &updateMapper, SLOT(map())); - updateMapper.setMapping(spinner, "stop"); + connect(spinner, OVERLOAD_PTR(int, QSpinBox, valueChanged), [=] () { + emit updateParam("stop"); + }); ui->timeGrid->addWidget(spinner, 4, 3); ui->timeGrid->addWidget(new QLabel("times", this), 4, 4); // KP repeat @@ -281,8 +300,9 @@ spinner->setMaximum(1000000); spinner->setValue(anim->parameter("kpstop").toInt()); settingWidgets["kpstop"] = spinner; - connect(spinner, SIGNAL(valueChanged(int)), &updateMapper, SLOT(map())); - updateMapper.setMapping(spinner, "kpstop"); + connect(spinner, OVERLOAD_PTR(int, QSpinBox, valueChanged), [=] () { + emit updateParam("kpstop"); + }); ui->timeGrid->addWidget(spinner, 12, 3); ui->timeGrid->addWidget(new QLabel("times", this), 12, 4); // Infinite repeat toggles @@ -315,8 +335,9 @@ else spinner->setValue(stop); settingWidgets["stop"] = spinner; - connect(spinner, SIGNAL(valueChanged(double)), &updateMapper, SLOT(map())); - updateMapper.setMapping(spinner, "stop"); + connect(spinner, OVERLOAD_PTR(double, QDoubleSpinBox, valueChanged), [=] () { + emit updateParam("stop"); + }); ui->timeGrid->addWidget(spinner, 4, 3); ui->timeGrid->addWidget(new QLabel("seconds", this), 4, 4); // KP stop time @@ -330,8 +351,9 @@ else spinner->setValue(kpstop); settingWidgets["kpstop"] = spinner; - connect(spinner, SIGNAL(valueChanged(double)), &updateMapper, SLOT(map())); - updateMapper.setMapping(spinner, "kpstop"); + connect(spinner, OVERLOAD_PTR(double, QDoubleSpinBox, valueChanged), [=] () { + emit updateParam("kpstop"); + }); ui->timeGrid->addWidget(spinner, 12, 3); ui->timeGrid->addWidget(new QLabel("seconds", this), 12, 4); // Infinite run toggles diff -Nru ckb-next-0.4.1/src/gui/animsettingdialog.h ckb-next-0.4.2/src/gui/animsettingdialog.h --- ckb-next-0.4.1/src/gui/animsettingdialog.h 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/gui/animsettingdialog.h 2019-10-08 12:56:16.000000000 +0000 @@ -4,9 +4,9 @@ #include #include #include -#include #include #include "kbanim.h" +#include namespace Ui { class AnimSettingDialog; @@ -32,10 +32,8 @@ KbAnim* _anim; double lastDuration; QMap settingWidgets; - QSignalMapper updateMapper; QMap angleSpinners; - QSignalMapper angleDialMapper, angleSpinnerMapper; QFrame* hLine(); diff -Nru ckb-next-0.4.1/src/gui/CMakeLists.txt ckb-next-0.4.2/src/gui/CMakeLists.txt --- ckb-next-0.4.1/src/gui/CMakeLists.txt 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/gui/CMakeLists.txt 2019-10-08 12:56:16.000000000 +0000 @@ -199,6 +199,7 @@ modeselectdialog.h mperfwidget.h nowheelcombobox.h + qoverloadlegacy.h rebindwidget.h rlistwidget.h settingswidget.h @@ -446,6 +447,13 @@ "${Appindicator_DEFINITIONS}" "${CKB_NEXT_COMMON_COMPILE_FLAGS}") +if (CMAKE_BUILD_TYPE STREQUAL "Debug") + target_compile_options( + ckb-next + PRIVATE + "-DQT_DEPRECATED_WARNINGS") +endif () + # Add sanitizers after all target information is known add_sanitizers(ckb-next) @@ -476,6 +484,15 @@ OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ) + + # Install daemon restart script + install( + PROGRAMS "${PROJECT_SOURCE_DIR}/macos/daemon-restart.sh" + DESTINATION "ckb-next.app/Contents/Resources" + PERMISSIONS + OWNER_READ OWNER_WRITE OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE) elseif (LINUX) install( FILES "${CMAKE_CURRENT_BINARY_DIR}/ckb-next.png" diff -Nru ckb-next-0.4.1/src/gui/keyaction.cpp ckb-next-0.4.2/src/gui/keyaction.cpp --- ckb-next-0.4.1/src/gui/keyaction.cpp 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/gui/keyaction.cpp 2019-10-08 12:56:16.000000000 +0000 @@ -67,9 +67,9 @@ return "$lock:0"; // DPI buttons if(key == "dpiup"){ - if(model == KeyMap::HARPOON || model == KeyMap::GLAIVE) + if(model == KeyMap::HARPOON || model == KeyMap::GLAIVE || model == KeyMap::IRONCLAW) return "$dpi:-4"; - return "$dpi:-2"; + return "$dpi:-2"; } if(key == "dpidn") return "$dpi:-1"; diff -Nru ckb-next-0.4.1/src/gui/keymap.cpp ckb-next-0.4.2/src/gui/keymap.cpp --- ckb-next-0.4.1/src/gui/keymap.cpp 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/gui/keymap.cpp 2019-10-08 12:56:16.000000000 +0000 @@ -393,6 +393,36 @@ }; #define KEYCOUNT_ST100 (sizeof(ST100Zones) / sizeof(Key)) +// Mouse map - Ironclaw +static const Key IronclawKeys[] = { + // primary keys + {0, "Left Mouse", "mouse1", 12, 0, 12, 28, false, true }, + {0, "Right Mouse", "mouse2", 31, 0, 12, 28, false, true }, + + // center column keys + {0, "Wheel Up", "wheelup", 23, 3, 8, 7, false, true }, + {0, "Middle Mouse", "mouse3", 23, 7, 8, 6, false, true }, + {0, "Wheel Down", "wheeldn", 23, 12, 8, 7, false, true }, + {0, "Profile Cycle", "profswitch", 23, 18, 9, 9, false, true }, + {0, "DPI Cycle", "dpiup", 23, 26, 8, 9, false, true }, + + // left side forward/back keys + {0, "Forward", "mouse5", 6, 20, 5, 12, false, true }, + {0, "Back", "mouse4", 7, 32, 5, 12, false, true }, + + // zones for LEDs + {0, "Logo", "back", 21, 50, NS, true, false }, + {0, "Wheel", "wheel", 23, 3, 8, 14, true, false }, + + // need to add DPI LED, even if not directly configurable for indicator to work + {0, "DPI", "dpi", 10, 10, 8, 8, true, false } + +}; +#define KEYCOUNT_IRONCLAW (sizeof(IronclawKeys) / sizeof(Key)) + +#define IRONCLAW_WIDTH 52 +#define IRONCLAW_HEIGHT 67 + // Map getter. Each model/layout pair only needs to be constructed once; after that, future KeyMaps can copy the existing maps. #define N_MODELS KeyMap::_MODEL_MAX #define N_LAYOUTS KeyMap::_LAYOUT_MAX @@ -847,6 +877,19 @@ } break; } + case KeyMap::IRONCLAW:{ + // M65 isn't a keyboard; all mouse maps are unique. + for(const Key* key = IronclawKeys; key < IronclawKeys + KEYCOUNT_IRONCLAW; key++){ + // Keyboard keys are written from the center because that's where the LEDs are, but the mouse buttons are odd shapes so they're + // written from the upper left + Key translatedKey = *key; + translatedKey.x += translatedKey.width / 2; + translatedKey.y += translatedKey.height / 2; + map[key->name] = translatedKey; + } + // Mice also have no layout patches - no other changes necessary + break; + } default:; // <- stop GCC from complaining } // Map is finished, return result @@ -1081,6 +1124,8 @@ return M65E; if(lower == "m95") return M95; + if(lower == "ironclaw") + return IRONCLAW; return NO_MODEL; } @@ -1128,6 +1173,8 @@ return "m65e"; case M95: return "m95"; + case IRONCLAW: + return "ironclaw"; default: return ""; } @@ -1170,6 +1217,7 @@ case POLARIS: case ST100: case M95: + case IRONCLAW: return M65_WIDTH; default: return 0; @@ -1201,6 +1249,7 @@ case POLARIS: case ST100: case M95: + case IRONCLAW: return M65_HEIGHT; default: return 0; @@ -1275,6 +1324,9 @@ map = KeyMap(HARPOON, layout); if(map.contains(key)) return map[key].friendlyName(); + map = KeyMap(IRONCLAW, layout); + if(map.contains(key)) + return map[key].friendlyName(); // Not found at all return ""; diff -Nru ckb-next-0.4.1/src/gui/keymap.h ckb-next-0.4.2/src/gui/keymap.h --- ckb-next-0.4.1/src/gui/keymap.h 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/gui/keymap.h 2019-10-08 12:56:16.000000000 +0000 @@ -73,6 +73,7 @@ K66, M65E, M95, + IRONCLAW, _MODEL_MAX }; // Key layouts (ordered alphabetically by name) @@ -117,7 +118,7 @@ // Type of device inline static bool isKeyboard(Model model) { return !isMouse(model) && !isMousepad(model) && !isHeadsetStand(model) && model != NO_MODEL; } inline bool isKeyboard() const { return isKeyboard(keyModel); } - inline static bool isMouse(Model model) { return model == M65 || model == SABRE || model == SCIMITAR || model == HARPOON || model == GLAIVE || model == KATAR || model == M65E || model == M95; } + inline static bool isMouse(Model model) { return model == M65 || model == SABRE || model == SCIMITAR || model == HARPOON || model == GLAIVE || model == KATAR || model == M65E || model == M95 || model == IRONCLAW; } inline bool isMouse() const { return isMouse(keyModel); } inline static bool isMousepad(Model model) { return model == POLARIS; } inline bool isMousepad() const { return isMousepad(keyModel); } diff -Nru ckb-next-0.4.1/src/gui/keywidget.cpp ckb-next-0.4.2/src/gui/keywidget.cpp --- ckb-next-0.4.1/src/gui/keywidget.cpp 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/gui/keywidget.cpp 2019-10-08 12:56:16.000000000 +0000 @@ -9,7 +9,7 @@ static const int KEY_SIZE = 12; -static QImage* m65Overlay = 0, *sabOverlay = 0, *scimOverlay = 0, *harpOverlay = 0, *glaiveOverlay = 0, *polarisOverlay = 0, *katarOverlay = 0, *m95Overlay = 0; +static QImage* m65Overlay = 0, *sabOverlay = 0, *scimOverlay = 0, *harpOverlay = 0, *glaiveOverlay = 0, *polarisOverlay = 0, *katarOverlay = 0, *m95Overlay = 0, *ironclawOverlay = 0; // KbLight.cpp extern QRgb monoRgb(float r, float g, float b); @@ -157,6 +157,11 @@ m95Overlay = new QImage(":/img/overlay_m95.png"); overlay = m95Overlay; xpos = 2.f; + } else if(model == KeyMap::IRONCLAW){ + if(!ironclawOverlay) + ironclawOverlay = new QImage(":/img/overlay_ironclaw.png"); + overlay = ironclawOverlay; + xpos = 2.f; } if(!overlay){ @@ -387,7 +392,7 @@ drawLogo(&key, &decPainter, offX , offY, scale); else if ((model == KeyMap::K70MK2 || model == KeyMap::STRAFE_MK2) && key.friendlyName() == "Logo 2") decPainter.drawRect(QRectF((key.x + offX - key.width / 2.f - 2.f) * scale, y * scale, (key.width + 4.f) * scale, h * scale)); - else if(model == KeyMap::M95) + else if(model == KeyMap::M95 || (model == KeyMap::IRONCLAW && !strcmp(key.name, "back"))) drawLogo(&key, &decPainter, offX , offY, scale); else decPainter.drawEllipse(QRectF(x * scale, y * scale, w * scale, h * scale)); diff -Nru ckb-next-0.4.1/src/gui/kperfwidget.cpp ckb-next-0.4.2/src/gui/kperfwidget.cpp --- ckb-next-0.4.1/src/gui/kperfwidget.cpp 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/gui/kperfwidget.cpp 2019-10-08 12:56:16.000000000 +0000 @@ -2,6 +2,7 @@ #include "ui_kperfwidget.h" #include "modeselectdialog.h" #include +#include "qoverloadlegacy.h" /// /// \brief KPerfWidget::KPerfWidget sets up the UI for Keyboard Performace panel @@ -35,23 +36,27 @@ } // Map signals if(indicators[i].enable){ - connect(indicators[i].enable, SIGNAL(clicked(bool)), &updateMapper, SLOT(map())); - updateMapper.setMapping(indicators[i].enable, i); + connect(indicators[i].enable, &QCheckBox::clicked, [=] () { + emit uiUpdated(i); + }); } if(indicators[i].hwEnable){ - connect(indicators[i].hwEnable, SIGNAL(activated(int)), &updateMapper, SLOT(map())); - updateMapper.setMapping(indicators[i].hwEnable, i); + connect(indicators[i].hwEnable, OVERLOAD_PTR(int, QComboBox, activated), [=] () { + emit uiUpdated(i); + }); } - connect(indicators[i].color1, SIGNAL(colorChanged(QColor)), &updateMapper, SLOT(map())); - updateMapper.setMapping(indicators[i].color1, i); - connect(indicators[i].color2, SIGNAL(colorChanged(QColor)), &updateMapper, SLOT(map())); - updateMapper.setMapping(indicators[i].color2, i); + connect(indicators[i].color1, &ColorButton::colorChanged, [=] () { + emit uiUpdated(i); + }); + connect(indicators[i].color2, &ColorButton::colorChanged, [=] () { + emit uiUpdated(i); + }); if(indicators[i].color3){ - connect(indicators[i].color3, SIGNAL(colorChanged(QColor)), &updateMapper, SLOT(map())); - updateMapper.setMapping(indicators[i].color3, i); + connect(indicators[i].color3, &ColorButton::colorChanged, [=] () { + emit uiUpdated(i); + }); } } - connect(&updateMapper, SIGNAL(mapped(int)), this, SLOT(uiUpdated(int))); k95Widgets << ui->modeBox << ui->modeColorOn << ui->modeColorOff << ui->macroBox << ui->macroColorOn << ui->macroColorOff << ui->k95Label1 << ui->k95Label2 << ui->k95Label3 << ui->k95Label4 << ui->k95Label5 << ui->k95Label6 << ui->k95Line << ui->k95Spacer; } diff -Nru ckb-next-0.4.1/src/gui/kperfwidget.h ckb-next-0.4.2/src/gui/kperfwidget.h --- ckb-next-0.4.1/src/gui/kperfwidget.h 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/gui/kperfwidget.h 2019-10-08 12:56:16.000000000 +0000 @@ -4,7 +4,6 @@ #include #include #include -#include #include "kbperf.h" #include "kbprofile.h" #include "colorbutton.h" @@ -59,8 +58,6 @@ IndicatorUi indicators[I_COUNT]; QList k95Widgets; - QSignalMapper updateMapper; - private slots: void uiUpdated(int index); void on_intensityBox_valueChanged(int arg1); diff -Nru ckb-next-0.4.1/src/gui/mainwindow.cpp ckb-next-0.4.2/src/gui/mainwindow.cpp --- ckb-next-0.4.1/src/gui/mainwindow.cpp 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/gui/mainwindow.cpp 2019-10-08 12:56:16.000000000 +0000 @@ -1,3 +1,4 @@ + #include "ckbsettings.h" #include "kbmanager.h" #include "kbfirmware.h" @@ -72,6 +73,88 @@ } #endif +#if defined(Q_OS_MACOS) && !defined(OS_MAC_LEGACY) +bool is_catalina_or_higher(){ + // Get macOS version. If Catalina or higher, start the daemon agent as the current user to request for HID permission. + QString macOSver = QSysInfo::productVersion(); + // Split major/minor + QVector verVector = macOSver.splitRef('.'); + // Check if Catalina or greater + return (verVector.count() == 2 && verVector.at(0) == QString("10") && verVector.at(1).toInt() >= 15); +} + +void MainWindow::appleRequestHidTimer(){ + // Destroy the timer immediately if we have devices connected + if(KbManager::devices().count()){ + catalinaTimer->stop(); + catalinaTimer->deleteLater(); + catalinaTimer = nullptr; + return; + } + + QProcess launchctl; + launchctl.setProgram("launchctl"); + // Start the agent only if the state on the previous run wasn't "running" or pre exec + if(!catalinaAgentStarted){ + // Start the service + launchctl.setArguments(QStringList() << "start" << "org.ckb-next.daemon"); + launchctl.start(); + launchctl.waitForFinished(); + qDebug() << "Launchctl start returned" << launchctl.exitCode(); + catalinaAgentStarted = true; + } + + // Get EUID + uid_t euid = geteuid(); + + // This will most likely block the UI thread, but hopefully it won't be too bad + QThread::msleep(1500); + + // Call launchctl + launchctl.setArguments(QStringList() << "print" << QString("gui/%1/org.ckb-next.daemon").arg(euid)); + launchctl.start(); + launchctl.waitForFinished(); + if(launchctl.exitCode() == 0){ + QString str(launchctl.readAllStandardOutput()); + + int statestart = str.indexOf("state = ") + 8; + int statelen = str.indexOf('\n', statestart) - statestart; + if(statelen > 20) + return; + + // Extract the state from the output + QStringRef agentState(&str, statestart, statelen); + if(agentState == QString("running") || agentState == QString("spawned (pre-exec)")){ + catalinaAgentStarted = true; + return; + } + catalinaAgentStarted = false; + + // Extract the daemon's return code + int statusstart = str.lastIndexOf("last exit code = ") + 17; + int statuslen = str.indexOf('\n', statusstart) - statusstart; + if(statuslen < 3 && statuslen > 0){ + hid_req_ret status = (hid_req_ret)QStringRef(&str, statusstart, statuslen).toInt(); + // We do not need to do anything if the request succeeds, other than wait for the loop to run again + if(status == REQUEST_ALREADY_ALLOWED){ + qDebug() << "We have HID access!"; + catalinaTimer->stop(); + catalinaTimer->deleteLater(); + catalinaTimer = nullptr; + // Ask user to restart daemon only if we first had to request permission + if(prevHidRet == REQUEST_SUCCEEDED) + QProcess::execute("open", QStringList() << "-a" << "Terminal" << "/Applications/ckb-next.app/Contents/Resources/daemon-restart.sh"); + } + else + qDebug() << "HID agent encountered an unknown error"; + prevHidRet = status; + } else if (statuslen == 14) { + qDebug() << "Agent was never started. Something went wrong."; + } + } +} +#endif + MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) @@ -204,6 +287,27 @@ connect(settingsWidget, &SettingsWidget::checkForUpdates, this, &MainWindow::checkForCkbUpdates); #endif + +#if defined(Q_OS_MACOS) && !defined(OS_MAC_LEGACY) + if(is_catalina_or_higher()){ + // Load the agent as it'll be unloaded on first installation + { + QProcess launchctl; + launchctl.setProgram("launchctl"); + launchctl.setArguments(QStringList() << "load" << "/Library/LaunchAgents/org.ckb-next.daemon_agent.plist"); + launchctl.start(); + launchctl.waitForFinished(); + qDebug() << "Launchctl load returned" << launchctl.exitCode(); + } + + // Start it + catalinaTimer = new QTimer(this); + connect(catalinaTimer, &QTimer::timeout, this, &MainWindow::appleRequestHidTimer); + catalinaTimer->setInterval(11000); + catalinaTimer->start(); + QTimer::singleShot(2000, this, &MainWindow::appleRequestHidTimer); + } +#endif } void MainWindow::handleTrayScrollEvt(bool up){ @@ -281,6 +385,8 @@ QString kextstatOut(kextstat.readAll()); if(kextstatOut.isEmpty()) daemonWarning.append(tr("
Warning: System Extension by \"Fumihiko Takayama\" is not allowed in Security & Privacy. Please allow it and then unplug and replug your devices.")); + if(is_catalina_or_higher()) + daemonWarning.append(tr("
Warning: Make sure ckb-next-daemon is allowed in Security & Privacy -> Input monitoring.
Please allow for up to 10 seconds for the daemon restart prompt to show up after allowing input monitoring.")); #elif defined(Q_OS_LINUX) if(!(QFileInfo("/dev/uinput").exists() || QFileInfo("/dev/input/uinput").exists())){ QProcess modprobe; diff -Nru ckb-next-0.4.1/src/gui/mainwindow.h ckb-next-0.4.2/src/gui/mainwindow.h --- ckb-next-0.4.1/src/gui/mainwindow.h 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/gui/mainwindow.h 2019-10-08 12:56:16.000000000 +0000 @@ -62,9 +62,21 @@ QMenu* trayIconMenu; CkbSystemTrayIcon* trayIcon; - void closeEvent(QCloseEvent *event); +#if defined(Q_OS_MACOS) && !defined(OS_MAC_LEGACY) + QTimer* catalinaTimer; + typedef enum { + REQUEST_STATE_NOT_SET = -1, + REQUEST_SUCCEEDED = 0, + REQUEST_ALREADY_ALLOWED, + REQUEST_ERROR + } hid_req_ret; + hid_req_ret prevHidRet = REQUEST_STATE_NOT_SET; + bool catalinaAgentStarted = false; + +#endif + public slots: void showWindow(); void stateChange(Qt::ApplicationState state); @@ -89,6 +101,9 @@ void showFwUpdateNotification(QWidget* widget, float version); void QSignalHandler(); void checkedForNewVer(QString ver, QString changelog); +#if defined(Q_OS_MACOS) && !defined(OS_MAC_LEGACY) + void appleRequestHidTimer(); +#endif private: Ui::MainWindow *ui; diff -Nru ckb-next-0.4.1/src/gui/mperfwidget.cpp ckb-next-0.4.2/src/gui/mperfwidget.cpp --- ckb-next-0.4.1/src/gui/mperfwidget.cpp 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/gui/mperfwidget.cpp 2019-10-08 12:56:16.000000000 +0000 @@ -2,6 +2,7 @@ #include "ui_mperfwidget.h" #include "modeselectdialog.h" #include +#include "qoverloadlegacy.h" const static QString xyLinkPath = "UI/DPI/UnlinkXY"; @@ -32,37 +33,33 @@ stages[i].indicator->bigIcons(true); stages[i].indicator->allowAlpha(true); // Map signals - connect(stages[i].indicator, SIGNAL(clicked(bool)), &buttonMapper1, SLOT(map())); - connect(stages[i].indicator, SIGNAL(colorChanged(QColor)), &buttonMapper2, SLOT(map())); - connect(stages[i].xSlider, SIGNAL(valueChanged(int)), &sliderXMapper, SLOT(map())); - connect(stages[i].ySlider, SIGNAL(valueChanged(int)), &sliderYMapper, SLOT(map())); - connect(stages[i].xBox, SIGNAL(valueChanged(int)), &boxXMapper, SLOT(map())); - connect(stages[i].yBox, SIGNAL(valueChanged(int)), &boxYMapper, SLOT(map())); + connect(stages[i].indicator, &ColorButton::clicked, [=] (){ + emit colorClicked(i); + }); + connect(stages[i].indicator, &ColorButton::colorChanged, [=] () { + emit colorChanged(i); + }); + connect(stages[i].xSlider, &QSlider::valueChanged, [=] () { + emit sliderXMoved(i); + }); + connect(stages[i].ySlider, &QSlider::valueChanged, [=] () { + emit sliderYMoved(i); + }); + connect(stages[i].xBox, OVERLOAD_PTR(int, QSpinBox, valueChanged), [=] () { + emit boxXChanged(i); + }); + connect(stages[i].yBox, OVERLOAD_PTR(int, QSpinBox, valueChanged), [=] () { + emit boxYChanged(i); + }); if(stages[i].enableCheck) // Sniper has no enable - connect(stages[i].enableCheck, SIGNAL(stateChanged(int)), &enableMapper, SLOT(map())); - // Set names - buttonMapper1.setMapping(stages[i].indicator, i); - buttonMapper2.setMapping(stages[i].indicator, i); - sliderXMapper.setMapping(stages[i].xSlider, i); - sliderYMapper.setMapping(stages[i].ySlider, i); - boxXMapper.setMapping(stages[i].xBox, i); - boxYMapper.setMapping(stages[i].yBox, i); - if(stages[i].enableCheck) - enableMapper.setMapping(stages[i].enableCheck, i); + connect(stages[i].enableCheck, &QCheckBox::stateChanged, [=] () { + emit enableChanged(i); + }); // Hide indicator arrows stages[i].indicatorLabel->setVisible(false); } ui->iLabelO->setVisible(false); - - // Connect to slots - connect(&buttonMapper1, SIGNAL(mapped(int)), this, SLOT(colorClicked(int))); - connect(&buttonMapper2, SIGNAL(mapped(int)), this, SLOT(colorChanged(int))); - connect(&sliderXMapper, SIGNAL(mapped(int)), this, SLOT(sliderXMoved(int))); - connect(&sliderYMapper, SIGNAL(mapped(int)), this, SLOT(sliderYMoved(int))); - connect(&boxXMapper, SIGNAL(mapped(int)), this, SLOT(boxXChanged(int))); - connect(&boxYMapper, SIGNAL(mapped(int)), this, SLOT(boxYChanged(int))); - connect(&enableMapper, SIGNAL(mapped(int)), this, SLOT(enableChanged(int))); } MPerfWidget::~MPerfWidget(){ diff -Nru ckb-next-0.4.1/src/gui/mperfwidget.h ckb-next-0.4.2/src/gui/mperfwidget.h --- ckb-next-0.4.1/src/gui/mperfwidget.h 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/gui/mperfwidget.h 2019-10-08 12:56:16.000000000 +0000 @@ -6,7 +6,6 @@ #include #include #include -#include #include #include "kbperf.h" #include "kbprofile.h" @@ -46,11 +45,6 @@ bool _xyLink; bool colorLink; - QSignalMapper buttonMapper1, buttonMapper2; - QSignalMapper sliderXMapper, sliderYMapper; - QSignalMapper boxXMapper, boxYMapper; - QSignalMapper enableMapper; - // Hack: prevent recursive slot calls bool isSetting; diff -Nru ckb-next-0.4.1/src/gui/qoverloadlegacy.h ckb-next-0.4.2/src/gui/qoverloadlegacy.h --- ckb-next-0.4.1/src/gui/qoverloadlegacy.h 1970-01-01 00:00:00.000000000 +0000 +++ ckb-next-0.4.2/src/gui/qoverloadlegacy.h 2019-10-08 12:56:16.000000000 +0000 @@ -0,0 +1,9 @@ +#ifndef QOVERLOADLEGACY_H +#define QOVERLOADLEGACY_H +#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) +#include +#define OVERLOAD_PTR(type, cl, func) (QOverload::of(&cl::func)) +#else +#define OVERLOAD_PTR(type, cl, func) (static_cast(&cl::func)) +#endif +#endif // QOVERLOADLEGACY_H diff -Nru ckb-next-0.4.1/src/gui/rebindwidget.ui ckb-next-0.4.2/src/gui/rebindwidget.ui --- ckb-next-0.4.1/src/gui/rebindwidget.ui 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/gui/rebindwidget.ui 2019-10-08 12:56:16.000000000 +0000 @@ -1478,9 +1478,6 @@ - - 1 - Binary files /tmp/tmpADwR6T/VW_jbDBTxJ/ckb-next-0.4.1/src/gui/resources/overlay_ironclaw.png and /tmp/tmpADwR6T/_eXUH3f3O8/ckb-next-0.4.2/src/gui/resources/overlay_ironclaw.png differ diff -Nru ckb-next-0.4.1/src/gui/resources/overlay_ironclaw.svg ckb-next-0.4.2/src/gui/resources/overlay_ironclaw.svg --- ckb-next-0.4.1/src/gui/resources/overlay_ironclaw.svg 1970-01-01 00:00:00.000000000 +0000 +++ ckb-next-0.4.2/src/gui/resources/overlay_ironclaw.svg 2019-10-08 12:56:16.000000000 +0000 @@ -0,0 +1,132 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + diff -Nru ckb-next-0.4.1/src/libs/quazip/CMakeLists.txt ckb-next-0.4.2/src/libs/quazip/CMakeLists.txt --- ckb-next-0.4.1/src/libs/quazip/CMakeLists.txt 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/libs/quazip/CMakeLists.txt 2019-10-08 12:56:16.000000000 +0000 @@ -58,8 +58,8 @@ quazipfile.cpp quazipfileinfo.cpp quazipnewinfo.cpp + minizip_crypt.h JlCompress.h - crypt.h ioapi.h quaadler32.h quachecksum32.h @@ -91,3 +91,9 @@ PROPERTIES CXX_STANDARD 11) +if (CMAKE_BUILD_TYPE STREQUAL "Debug") + target_compile_options( + quazip + PRIVATE + "-DQT_DEPRECATED_WARNINGS") +endif () diff -Nru ckb-next-0.4.1/src/libs/quazip/crypt.h ckb-next-0.4.2/src/libs/quazip/crypt.h --- ckb-next-0.4.1/src/libs/quazip/crypt.h 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/libs/quazip/crypt.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,135 +0,0 @@ -/* crypt.h -- base code for crypt/uncrypt ZIPfile - - - Version 1.01e, February 12th, 2005 - - Copyright (C) 1998-2005 Gilles Vollant - - This code is a modified version of crypting code in Infozip distribution - - The encryption/decryption parts of this source code (as opposed to the - non-echoing password parts) were originally written in Europe. The - whole source package can be freely distributed, including from the USA. - (Prior to January 2000, re-export from the US was a violation of US law.) - - This encryption code is a direct transcription of the algorithm from - Roger Schlafly, described by Phil Katz in the file appnote.txt. This - file (appnote.txt) is distributed with the PKZIP program (even in the - version without encryption capabilities). - - If you don't need crypting in your application, just define symbols - NOCRYPT and NOUNCRYPT. - - This code support the "Traditional PKWARE Encryption". - - The new AES encryption added on Zip format by Winzip (see the page - http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong - Encryption is not supported. -*/ - -#include "quazip_global.h" - -#define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8)) - -/*********************************************************************** - * Return the next byte in the pseudo-random sequence - */ -static int decrypt_byte(unsigned long* pkeys, const z_crc_t FAR * pcrc_32_tab UNUSED) -{ - //(void) pcrc_32_tab; /* avoid "unused parameter" warning */ - unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an - * unpredictable manner on 16-bit systems; not a problem - * with any known compiler so far, though */ - - temp = ((unsigned)(*(pkeys+2)) & 0xffff) | 2; - return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); -} - -/*********************************************************************** - * Update the encryption keys with the next byte of plain text - */ -static int update_keys(unsigned long* pkeys,const z_crc_t FAR * pcrc_32_tab,int c) -{ - (*(pkeys+0)) = CRC32((*(pkeys+0)), c); - (*(pkeys+1)) += (*(pkeys+0)) & 0xff; - (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1; - { - register int keyshift = (int)((*(pkeys+1)) >> 24); - (*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift); - } - return c; -} - - -/*********************************************************************** - * Initialize the encryption keys and the random header according to - * the given password. - */ -static void init_keys(const char* passwd,unsigned long* pkeys,const z_crc_t FAR * pcrc_32_tab) -{ - *(pkeys+0) = 305419896L; - *(pkeys+1) = 591751049L; - *(pkeys+2) = 878082192L; - while (*passwd != '\0') { - update_keys(pkeys,pcrc_32_tab,(int)*passwd); - passwd++; - } -} - -#define zdecode(pkeys,pcrc_32_tab,c) \ - (update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab))) - -#define zencode(pkeys,pcrc_32_tab,c,t) \ - (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c)) - -#ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED - -#define RAND_HEAD_LEN 12 - /* "last resort" source for second part of crypt seed pattern */ -# ifndef ZCR_SEED2 -# define ZCR_SEED2 3141592654UL /* use PI as default pattern */ -# endif - -static int crypthead(passwd, buf, bufSize, pkeys, pcrc_32_tab, crcForCrypting) - const char *passwd; /* password string */ - unsigned char *buf; /* where to write header */ - int bufSize; - unsigned long* pkeys; - const z_crc_t FAR * pcrc_32_tab; - unsigned long crcForCrypting; -{ - int n; /* index in random header */ - int t; /* temporary */ - int c; /* random byte */ - unsigned char header[RAND_HEAD_LEN-2]; /* random header */ - static unsigned calls = 0; /* ensure different random header each time */ - - if (bufSize> 7) & 0xff; - header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t); - } - /* Encrypt random header (last two bytes is high word of crc) */ - init_keys(passwd, pkeys, pcrc_32_tab); - for (n = 0; n < RAND_HEAD_LEN-2; n++) - { - buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t); - } - buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t); - buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t); - return n; -} - -#endif diff -Nru ckb-next-0.4.1/src/libs/quazip/ioapi.h ckb-next-0.4.2/src/libs/quazip/ioapi.h --- ckb-next-0.4.1/src/libs/quazip/ioapi.h 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/libs/quazip/ioapi.h 2019-10-08 12:56:16.000000000 +0000 @@ -44,7 +44,7 @@ #include #include -#include "zlib.h" +#include #if defined(USE_FILE32API) #define fopen64 fopen diff -Nru ckb-next-0.4.1/src/libs/quazip/JlCompress.cpp ckb-next-0.4.2/src/libs/quazip/JlCompress.cpp --- ckb-next-0.4.1/src/libs/quazip/JlCompress.cpp 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/libs/quazip/JlCompress.cpp 2019-10-08 12:56:16.000000000 +0000 @@ -92,7 +92,7 @@ if (dir != origDir) { QuaZipFile dirZipFile(zip); if (!dirZipFile.open(QIODevice::WriteOnly, - QuaZipNewInfo(origDirectory.relativeFilePath(dir) + "/", dir), 0, 0, 0)) { + QuaZipNewInfo(origDirectory.relativeFilePath(dir) + QLatin1String("/"), dir), 0, 0, 0)) { return false; } dirZipFile.close(); @@ -103,7 +103,12 @@ if (recursive) { // Per ogni sotto cartella QFileInfoList files = directory.entryInfoList(QDir::AllDirs|QDir::NoDotAndDotDot|filters); - Q_FOREACH (QFileInfo file, files) { + for (int index = 0; index < files.size(); ++index ) { + const QFileInfo & file( files.at( index ) ); +#if QT_VERSION < QT_VERSION_CHECK(4, 7, 4) + if (!file.isDir()) + continue; +#endif // Comprimo la sotto cartella if(!compressSubDir(zip,file.absoluteFilePath(),origDir,recursive,filters)) return false; } @@ -111,7 +116,8 @@ // Per ogni file nella cartella QFileInfoList files = directory.entryInfoList(QDir::Files|filters); - Q_FOREACH (QFileInfo file, files) { + for (int index = 0; index < files.size(); ++index ) { + const QFileInfo & file( files.at( index ) ); // Se non e un file o e il file compresso che sto creando if(!file.isFile()||file.absoluteFilePath()==zip->getZipName()) continue; @@ -142,7 +148,7 @@ // Controllo esistenza cartella file risultato QDir curDir; - if (fileDest.endsWith('/')) { + if (fileDest.endsWith(QLatin1String("/"))) { if (!curDir.mkpath(fileDest)) { return false; } @@ -157,7 +163,7 @@ return false; QFile::Permissions srcPerm = info.getPermissions(); - if (fileDest.endsWith('/') && QFileInfo(fileDest).isDir()) { + if (fileDest.endsWith(QLatin1String("/")) && QFileInfo(fileDest).isDir()) { if (srcPerm != 0) { QFile(fileDest).setPermissions(srcPerm); } @@ -236,7 +242,8 @@ // Comprimo i file QFileInfo info; - Q_FOREACH (QString file, files) { + for (int index = 0; index < files.size(); ++index ) { + const QString & file( files.at( index ) ); info.setFile(file); if (!info.exists() || !compressFile(&zip,file,info.fileName())) { QFile::remove(fileCompressed); @@ -357,8 +364,9 @@ if(!zip.open(QuaZip::mdUnzip)) { return QStringList(); } - - QDir directory(dir); + QString cleanDir = QDir::cleanPath(dir); + QDir directory(cleanDir); + QString absCleanDir = directory.absolutePath(); QStringList extracted; if (!zip.goToFirstFile()) { return QStringList(); @@ -366,7 +374,10 @@ do { QString name = zip.getCurrentFileName(); QString absFilePath = directory.absoluteFilePath(name); - if (!extractFile(&zip, "", absFilePath)) { + QString absCleanPath = QDir::cleanPath(absFilePath); + if (!absCleanPath.startsWith(absCleanDir + QLatin1String("/"))) + continue; + if (!extractFile(&zip, QLatin1String(""), absFilePath)) { removeFile(extracted); return QStringList(); } diff -Nru ckb-next-0.4.1/src/libs/quazip/minizip_crypt.h ckb-next-0.4.2/src/libs/quazip/minizip_crypt.h --- ckb-next-0.4.1/src/libs/quazip/minizip_crypt.h 1970-01-01 00:00:00.000000000 +0000 +++ ckb-next-0.4.2/src/libs/quazip/minizip_crypt.h 2019-10-08 12:56:16.000000000 +0000 @@ -0,0 +1,135 @@ +/* crypt.h -- base code for crypt/uncrypt ZIPfile + + + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + This code is a modified version of crypting code in Infozip distribution + + The encryption/decryption parts of this source code (as opposed to the + non-echoing password parts) were originally written in Europe. The + whole source package can be freely distributed, including from the USA. + (Prior to January 2000, re-export from the US was a violation of US law.) + + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + + If you don't need crypting in your application, just define symbols + NOCRYPT and NOUNCRYPT. + + This code support the "Traditional PKWARE Encryption". + + The new AES encryption added on Zip format by Winzip (see the page + http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong + Encryption is not supported. +*/ + +#include "quazip_global.h" + +#define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8)) + +/*********************************************************************** + * Return the next byte in the pseudo-random sequence + */ +static int decrypt_byte(unsigned long* pkeys, const z_crc_t FAR * pcrc_32_tab QUAZIP_UNUSED) +{ + //(void) pcrc_32_tab; /* avoid "unused parameter" warning */ + unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an + * unpredictable manner on 16-bit systems; not a problem + * with any known compiler so far, though */ + + temp = ((unsigned)(*(pkeys+2)) & 0xffff) | 2; + return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); +} + +/*********************************************************************** + * Update the encryption keys with the next byte of plain text + */ +static int update_keys(unsigned long* pkeys,const z_crc_t FAR * pcrc_32_tab,int c) +{ + (*(pkeys+0)) = CRC32((*(pkeys+0)), c); + (*(pkeys+1)) += (*(pkeys+0)) & 0xff; + (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1; + { + register int keyshift = (int)((*(pkeys+1)) >> 24); + (*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift); + } + return c; +} + + +/*********************************************************************** + * Initialize the encryption keys and the random header according to + * the given password. + */ +static void init_keys(const char* passwd,unsigned long* pkeys,const z_crc_t FAR * pcrc_32_tab) +{ + *(pkeys+0) = 305419896L; + *(pkeys+1) = 591751049L; + *(pkeys+2) = 878082192L; + while (*passwd != '\0') { + update_keys(pkeys,pcrc_32_tab,(int)*passwd); + passwd++; + } +} + +#define zdecode(pkeys,pcrc_32_tab,c) \ + (update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab))) + +#define zencode(pkeys,pcrc_32_tab,c,t) \ + (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c)) + +#ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED + +#define RAND_HEAD_LEN 12 + /* "last resort" source for second part of crypt seed pattern */ +# ifndef ZCR_SEED2 +# define ZCR_SEED2 3141592654UL /* use PI as default pattern */ +# endif + +static int crypthead(passwd, buf, bufSize, pkeys, pcrc_32_tab, crcForCrypting) + const char *passwd; /* password string */ + unsigned char *buf; /* where to write header */ + int bufSize; + unsigned long* pkeys; + const z_crc_t FAR * pcrc_32_tab; + unsigned long crcForCrypting; +{ + int n; /* index in random header */ + int t; /* temporary */ + int c; /* random byte */ + unsigned char header[RAND_HEAD_LEN-2]; /* random header */ + static unsigned calls = 0; /* ensure different random header each time */ + + if (bufSize> 7) & 0xff; + header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t); + } + /* Encrypt random header (last two bytes is high word of crc) */ + init_keys(passwd, pkeys, pcrc_32_tab); + for (n = 0; n < RAND_HEAD_LEN-2; n++) + { + buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t); + } + buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t); + buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t); + return n; +} + +#endif diff -Nru ckb-next-0.4.1/src/libs/quazip/qioapi.cpp ckb-next-0.4.2/src/libs/quazip/qioapi.cpp --- ckb-next-0.4.1/src/libs/quazip/qioapi.cpp 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/libs/quazip/qioapi.cpp 2019-10-08 12:56:16.000000000 +0000 @@ -11,8 +11,8 @@ #include #include #include +#include -#include "zlib.h" #include "ioapi.h" #include "quazip_global.h" #include diff -Nru ckb-next-0.4.1/src/libs/quazip/quaadler32.cpp ckb-next-0.4.2/src/libs/quazip/quaadler32.cpp --- ckb-next-0.4.1/src/libs/quazip/quaadler32.cpp 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/libs/quazip/quaadler32.cpp 2019-10-08 12:56:16.000000000 +0000 @@ -25,7 +25,7 @@ #include "quaadler32.h" -#include "zlib.h" +#include QuaAdler32::QuaAdler32() { diff -Nru ckb-next-0.4.1/src/libs/quazip/quacrc32.cpp ckb-next-0.4.2/src/libs/quazip/quacrc32.cpp --- ckb-next-0.4.1/src/libs/quazip/quacrc32.cpp 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/libs/quazip/quacrc32.cpp 2019-10-08 12:56:16.000000000 +0000 @@ -24,7 +24,7 @@ #include "quacrc32.h" -#include "zlib.h" +#include QuaCrc32::QuaCrc32() { diff -Nru ckb-next-0.4.1/src/libs/quazip/quagzipfile.cpp ckb-next-0.4.2/src/libs/quazip/quagzipfile.cpp --- ckb-next-0.4.1/src/libs/quazip/quagzipfile.cpp 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/libs/quazip/quagzipfile.cpp 2019-10-08 12:56:16.000000000 +0000 @@ -57,13 +57,13 @@ char modeString[2]; modeString[0] = modeString[1] = '\0'; if ((mode & QIODevice::Append) != 0) { - error = QuaGzipFile::trUtf8("QIODevice::Append is not " + error = QuaGzipFile::tr("QIODevice::Append is not " "supported for GZIP"); return false; } if ((mode & QIODevice::ReadOnly) != 0 && (mode & QIODevice::WriteOnly) != 0) { - error = QuaGzipFile::trUtf8("Opening gzip for both reading" + error = QuaGzipFile::tr("Opening gzip for both reading" " and writing is not supported"); return false; } else if ((mode & QIODevice::ReadOnly) != 0) { @@ -71,13 +71,13 @@ } else if ((mode & QIODevice::WriteOnly) != 0) { modeString[0] = 'w'; } else { - error = QuaGzipFile::trUtf8("You can open a gzip either for reading" + error = QuaGzipFile::tr("You can open a gzip either for reading" " or for writing. Which is it?"); return false; } gzd = open(id, modeString); if (gzd == NULL) { - error = QuaGzipFile::trUtf8("Could not gzopen() file"); + error = QuaGzipFile::tr("Could not gzopen() file"); return false; } return true; diff -Nru ckb-next-0.4.1/src/libs/quazip/quaziodevice.cpp ckb-next-0.4.2/src/libs/quazip/quaziodevice.cpp --- ckb-next-0.4.1/src/libs/quazip/quaziodevice.cpp 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/libs/quazip/quaziodevice.cpp 2019-10-08 12:56:16.000000000 +0000 @@ -30,9 +30,10 @@ /// \cond internal class QuaZIODevicePrivate { friend class QuaZIODevice; - QuaZIODevicePrivate(QIODevice *io); + QuaZIODevicePrivate(QIODevice *io, QuaZIODevice *q); ~QuaZIODevicePrivate(); QIODevice *io; + QuaZIODevice *q; z_stream zins; z_stream zouts; char *inBuf; @@ -43,11 +44,13 @@ int outBufSize; bool zBufError; bool atEnd; + bool flush(int sync); int doFlush(QString &error); }; -QuaZIODevicePrivate::QuaZIODevicePrivate(QIODevice *io): +QuaZIODevicePrivate::QuaZIODevicePrivate(QIODevice *io, QuaZIODevice *q): io(io), + q(q), inBuf(NULL), inBufPos(0), inBufSize(0), @@ -86,7 +89,45 @@ if (inBuf != NULL) delete[] inBuf; if (outBuf != NULL) - delete[] outBuf; + delete[] outBuf; +} + +bool QuaZIODevicePrivate::flush(int sync) +{ + QString error; + if (doFlush(error) < 0) { + q->setErrorString(error); + return false; + } + // can't flush buffer, some data is still waiting + if (outBufPos < outBufSize) + return true; + Bytef c = 0; + zouts.next_in = &c; // fake input buffer + zouts.avail_in = 0; // of zero size + do { + zouts.next_out = (Bytef *) outBuf; + zouts.avail_out = QUAZIO_OUTBUFSIZE; + int result = deflate(&zouts, sync); + switch (result) { + case Z_OK: + case Z_STREAM_END: + outBufSize = (char *) zouts.next_out - outBuf; + if (doFlush(error) < 0) { + q->setErrorString(error); + return false; + } + if (outBufPos < outBufSize) + return true; + break; + case Z_BUF_ERROR: // nothing to write? + return true; + default: + q->setErrorString(QString::fromLocal8Bit(zouts.msg)); + return false; + } + } while (zouts.avail_out == 0); + return true; } int QuaZIODevicePrivate::doFlush(QString &error) @@ -124,7 +165,7 @@ QuaZIODevice::QuaZIODevice(QIODevice *io, QObject *parent): QIODevice(parent), - d(new QuaZIODevicePrivate(io)) + d(new QuaZIODevicePrivate(io, this)) { connect(io, SIGNAL(readyRead()), SIGNAL(readyRead())); } @@ -144,24 +185,24 @@ bool QuaZIODevice::open(QIODevice::OpenMode mode) { if ((mode & QIODevice::Append) != 0) { - setErrorString(trUtf8("QIODevice::Append is not supported for" + setErrorString(tr("QIODevice::Append is not supported for" " QuaZIODevice")); return false; } if ((mode & QIODevice::ReadWrite) == QIODevice::ReadWrite) { - setErrorString(trUtf8("QIODevice::ReadWrite is not supported for" + setErrorString(tr("QIODevice::ReadWrite is not supported for" " QuaZIODevice")); return false; } if ((mode & QIODevice::ReadOnly) != 0) { if (inflateInit(&d->zins) != Z_OK) { - setErrorString(d->zins.msg); + setErrorString(QString::fromLocal8Bit(d->zins.msg)); return false; } } if ((mode & QIODevice::WriteOnly) != 0) { if (deflateInit(&d->zouts, Z_DEFAULT_COMPRESSION) != Z_OK) { - setErrorString(d->zouts.msg); + setErrorString(QString::fromLocal8Bit(d->zouts.msg)); return false; } } @@ -172,13 +213,13 @@ { if ((openMode() & QIODevice::ReadOnly) != 0) { if (inflateEnd(&d->zins) != Z_OK) { - setErrorString(d->zins.msg); + setErrorString(QString::fromLocal8Bit(d->zins.msg)); } } if ((openMode() & QIODevice::WriteOnly) != 0) { - flush(); + d->flush(Z_FINISH); if (deflateEnd(&d->zouts) != Z_OK) { - setErrorString(d->zouts.msg); + setErrorString(QString::fromLocal8Bit(d->zouts.msg)); } } QIODevice::close(); @@ -283,38 +324,7 @@ bool QuaZIODevice::flush() { - QString error; - if (d->doFlush(error) < 0) { - setErrorString(error); - return false; - } - // can't flush buffer, some data is still waiting - if (d->outBufPos < d->outBufSize) - return true; - Bytef c = 0; - d->zouts.next_in = &c; // fake input buffer - d->zouts.avail_in = 0; // of zero size - do { - d->zouts.next_out = (Bytef *) d->outBuf; - d->zouts.avail_out = QUAZIO_OUTBUFSIZE; - switch (deflate(&d->zouts, Z_SYNC_FLUSH)) { - case Z_OK: - d->outBufSize = (char *) d->zouts.next_out - d->outBuf; - if (d->doFlush(error) < 0) { - setErrorString(error); - return false; - } - if (d->outBufPos < d->outBufSize) - return true; - break; - case Z_BUF_ERROR: // nothing to write? - return true; - default: - setErrorString(QString::fromLocal8Bit(d->zouts.msg)); - return false; - } - } while (d->zouts.avail_out == 0); - return true; + return d->flush(Z_SYNC_FLUSH); } bool QuaZIODevice::isSequential() const diff -Nru ckb-next-0.4.1/src/libs/quazip/quaziodevice.h ckb-next-0.4.2/src/libs/quazip/quaziodevice.h --- ckb-next-0.4.1/src/libs/quazip/quaziodevice.h 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/libs/quazip/quaziodevice.h 2019-10-08 12:56:16.000000000 +0000 @@ -39,6 +39,7 @@ example. */ class QUAZIP_EXPORT QuaZIODevice: public QIODevice { + friend class QuaZIODevicePrivate; Q_OBJECT public: /// Constructor. diff -Nru ckb-next-0.4.1/src/libs/quazip/quazip.cpp ckb-next-0.4.2/src/libs/quazip/quazip.cpp --- ckb-next-0.4.1/src/libs/quazip/quazip.cpp 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/libs/quazip/quazip.cpp 2019-10-08 12:56:16.000000000 +0000 @@ -28,6 +28,8 @@ #include "quazip.h" +#define QUAZIP_OS_UNIX 3u + /// All the internal stuff for the QuaZip class. /** \internal @@ -42,9 +44,9 @@ Q_DISABLE_COPY(QuaZipPrivate) /// The pointer to the corresponding QuaZip instance. QuaZip *q; - /// The codec for file names. + /// The codec for file names (used when UTF-8 is not enabled). QTextCodec *fileNameCodec; - /// The codec for comments. + /// The codec for comments (used when UTF-8 is not enabled). QTextCodec *commentCodec; /// The archive file name. QString zipName; @@ -70,6 +72,10 @@ bool zip64; /// The auto-close flag. bool autoClose; + /// The UTF-8 flag. + bool utf8; + /// The OS code. + uint osCode; inline QTextCodec *getDefaultFileNameCodec() { if (defaultFileNameCodec == NULL) { @@ -89,7 +95,9 @@ zipError(UNZ_OK), dataDescriptorWritingEnabled(true), zip64(false), - autoClose(true) + autoClose(true), + utf8(false), + osCode(defaultOsCode) { unzFile_f = NULL; zipFile_f = NULL; @@ -108,7 +116,9 @@ zipError(UNZ_OK), dataDescriptorWritingEnabled(true), zip64(false), - autoClose(true) + autoClose(true), + utf8(false), + osCode(defaultOsCode) { unzFile_f = NULL; zipFile_f = NULL; @@ -117,7 +127,7 @@ } /// The constructor for the corresponding QuaZip constructor. inline QuaZipPrivate(QuaZip *q, QIODevice *ioDevice): - q(q), + q(q), fileNameCodec(getDefaultFileNameCodec()), commentCodec(QTextCodec::codecForLocale()), ioDevice(ioDevice), @@ -126,7 +136,9 @@ zipError(UNZ_OK), dataDescriptorWritingEnabled(true), zip64(false), - autoClose(true) + autoClose(true), + utf8(false), + osCode(defaultOsCode) { unzFile_f = NULL; zipFile_f = NULL; @@ -145,9 +157,11 @@ QHash directoryCaseInsensitive; unz64_file_pos lastMappedDirectoryEntry; static QTextCodec *defaultFileNameCodec; + static uint defaultOsCode; }; QTextCodec *QuaZipPrivate::defaultFileNameCodec = NULL; +uint QuaZipPrivate::defaultOsCode = QUAZIP_OS_UNIX; void QuaZipPrivate::clearDirectoryMap() { @@ -280,6 +294,8 @@ flags |= ZIP_AUTO_CLOSE; if (p->dataDescriptorWritingEnabled) flags |= ZIP_WRITE_DATA_DESCRIPTOR; + if (p->utf8) + flags |= ZIP_ENCODING_UTF8; p->zipFile_f=zipOpen3(ioDevice, mode==mdCreate?APPEND_STATUS_CREATE: mode==mdAppend?APPEND_STATUS_CREATEAFTER: @@ -341,9 +357,9 @@ case mdCreate: case mdAppend: case mdAdd: - p->zipError=zipClose(p->zipFile_f, - p->comment.isNull() ? NULL - : p->commentCodec->fromUnicode(p->comment).constData()); + p->zipError=zipClose(p->zipFile_f, p->comment.isNull() ? NULL : isUtf8Enabled() + ? p->comment.toUtf8().constData() + : p->commentCodec->fromUnicode(p->comment).constData()); break; default: qWarning("QuaZip::close(): unknown mode: %d", (int)p->mode); @@ -409,7 +425,9 @@ if((fakeThis->p->zipError=unzGetGlobalComment(p->unzFile_f, comment.data(), comment.size())) < 0) return QString(); fakeThis->p->zipError = UNZ_OK; - return p->commentCodec->toUnicode(comment); + unsigned flags = 0; + return (unzGetFileFlags(p->unzFile_f, &flags) == UNZ_OK) && (flags & UNZ_ENCODING_UTF8) + ? QString::fromUtf8(comment) : p->commentCodec->toUnicode(comment); } bool QuaZip::setCurrentFile(const QString& fileName, CaseSensitivity cs) @@ -544,8 +562,8 @@ info->diskNumberStart=info_z.disk_num_start; info->internalAttr=info_z.internal_fa; info->externalAttr=info_z.external_fa; - info->name=p->fileNameCodec->toUnicode(fileName); - info->comment=p->commentCodec->toUnicode(comment); + info->name=(info->flags & UNZ_ENCODING_UTF8) ? QString::fromUtf8(fileName) : p->fileNameCodec->toUnicode(fileName); + info->comment=(info->flags & UNZ_ENCODING_UTF8) ? QString::fromUtf8(comment) : p->commentCodec->toUnicode(comment); info->extra=extra; info->dateTime=QDateTime( QDate(info_z.tmu_date.tm_year, info_z.tmu_date.tm_mon+1, info_z.tmu_date.tm_mday), @@ -565,10 +583,13 @@ } if(!isOpen()||!hasCurrentFile()) return QString(); QByteArray fileName(MAX_FILE_NAME_LENGTH, 0); - if((fakeThis->p->zipError=unzGetCurrentFileInfo64(p->unzFile_f, NULL, fileName.data(), fileName.size(), + unz_file_info64 file_info; + if((fakeThis->p->zipError=unzGetCurrentFileInfo64(p->unzFile_f, &file_info, fileName.data(), fileName.size(), NULL, 0, NULL, 0))!=UNZ_OK) return QString(); - QString result = p->fileNameCodec->toUnicode(fileName.constData()); + fileName.resize(file_info.size_filename); + QString result = (file_info.flag & UNZ_ENCODING_UTF8) + ? QString::fromUtf8(fileName) : p->fileNameCodec->toUnicode(fileName); if (result.isEmpty()) return result; // Add to directory map @@ -583,7 +604,17 @@ void QuaZip::setFileNameCodec(const char *fileNameCodecName) { - p->fileNameCodec=QTextCodec::codecForName(fileNameCodecName); + p->fileNameCodec=QTextCodec::codecForName(fileNameCodecName); +} + +void QuaZip::setOsCode(uint osCode) +{ + p->osCode = osCode; +} + +uint QuaZip::getOsCode() const +{ + return p->osCode; } QTextCodec *QuaZip::getFileNameCodec()const @@ -774,6 +805,16 @@ setDefaultFileNameCodec(QTextCodec::codecForName(codecName)); } +void QuaZip::setDefaultOsCode(uint osCode) +{ + QuaZipPrivate::defaultOsCode = osCode; +} + +uint QuaZip::getDefaultOsCode() +{ + return QuaZipPrivate::defaultOsCode; +} + void QuaZip::setZip64Enabled(bool zip64) { p->zip64 = zip64; @@ -784,6 +825,16 @@ return p->zip64; } +void QuaZip::setUtf8Enabled(bool utf8) +{ + p->utf8 = utf8; +} + +bool QuaZip::isUtf8Enabled() const +{ + return p->utf8; +} + bool QuaZip::isAutoClose() const { return p->autoClose; diff -Nru ckb-next-0.4.1/src/libs/quazip/quazipdir.cpp ckb-next-0.4.2/src/libs/quazip/quazipdir.cpp --- ckb-next-0.4.1/src/libs/quazip/quazipdir.cpp 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/libs/quazip/quazipdir.cpp 2019-10-08 12:56:16.000000000 +0000 @@ -55,7 +55,7 @@ QuaZipDir::QuaZipDir(QuaZip *zip, const QString &dir): d(new QuaZipDirPrivate(zip, dir)) { - if (d->dir.startsWith('/')) + if (d->dir.startsWith(QLatin1String("/"))) d->dir = d->dir.mid(1); } @@ -86,24 +86,24 @@ bool QuaZipDir::cd(const QString &directoryName) { - if (directoryName == "/") { - d->dir = ""; + if (directoryName == QLatin1String("/")) { + d->dir = QLatin1String(""); return true; } QString dirName = directoryName; - if (dirName.endsWith('/')) + if (dirName.endsWith(QLatin1String("/"))) dirName.chop(1); - if (dirName.contains('/')) { + if (dirName.contains(QLatin1String("/"))) { QuaZipDir dir(*this); - if (dirName.startsWith('/')) { + if (dirName.startsWith(QLatin1String("/"))) { #ifdef QUAZIP_QUAZIPDIR_DEBUG qDebug("QuaZipDir::cd(%s): going to /", dirName.toUtf8().constData()); #endif - if (!dir.cd("/")) + if (!dir.cd(QLatin1String("/"))) return false; } - QStringList path = dirName.split('/', QString::SkipEmptyParts); + QStringList path = dirName.split(QLatin1String("/"), QString::SkipEmptyParts); for (QStringList::const_iterator i = path.constBegin(); i != path.end(); ++i) { @@ -119,15 +119,15 @@ d->dir = dir.path(); return true; } else { // no '/' - if (dirName == ".") { + if (dirName == QLatin1String(".")) { return true; - } else if (dirName == "..") { + } else if (dirName == QLatin1String("..")) { if (isRoot()) { return false; } else { - int slashPos = d->dir.lastIndexOf('/'); + int slashPos = d->dir.lastIndexOf(QLatin1String("/")); if (slashPos == -1) { - d->dir = ""; + d->dir = QLatin1String(""); } else { d->dir = d->dir.left(slashPos); } @@ -138,7 +138,7 @@ if (isRoot()) d->dir = dirName; else - d->dir += "/" + dirName; + d->dir += QLatin1String("/") + dirName; return true; } else { return false; @@ -149,7 +149,7 @@ bool QuaZipDir::cdUp() { - return cd(".."); + return cd(QLatin1String("..")); } uint QuaZipDir::count() const @@ -247,10 +247,10 @@ QString QuaZipDirComparator::getExtension(const QString &name) { - if (name.endsWith('.') || name.indexOf('.', 1) == -1) { - return ""; + if (name.endsWith(QLatin1String(".")) || name.indexOf(QLatin1String("."), 1) == -1) { + return QLatin1String(""); } else { - return name.mid(name.lastIndexOf('.') + 1); + return name.mid(name.lastIndexOf(QLatin1String(".")) + 1); } } @@ -277,9 +277,9 @@ & (QDir::Name | QDir::Time | QDir::Size | QDir::Type); if ((sort & QDir::DirsFirst) == QDir::DirsFirst || (sort & QDir::DirsLast) == QDir::DirsLast) { - if (info1.name.endsWith('/') && !info2.name.endsWith('/')) + if (info1.name.endsWith(QLatin1String("/")) && !info2.name.endsWith(QLatin1String("/"))) return (sort & QDir::DirsFirst) == QDir::DirsFirst; - else if (!info1.name.endsWith('/') && info2.name.endsWith('/')) + else if (!info1.name.endsWith(QLatin1String("/")) && info2.name.endsWith(QLatin1String("/"))) return (sort & QDir::DirsLast) == QDir::DirsLast; } bool result; @@ -325,7 +325,7 @@ { QString basePath = simplePath(); if (!basePath.isEmpty()) - basePath += "/"; + basePath += QLatin1String("/"); int baseLength = basePath.length(); result.clear(); QuaZipDirRestoreCurrent saveCurrent(zip); @@ -351,8 +351,8 @@ continue; bool isDir = false; bool isReal = true; - if (relativeName.contains('/')) { - int indexOfSlash = relativeName.indexOf('/'); + if (relativeName.contains(QLatin1String("/"))) { + int indexOfSlash = relativeName.indexOf(QLatin1String("/")); // something like "subdir/" isReal = indexOfSlash == relativeName.length() - 1; relativeName = relativeName.left(indexOfSlash + 1); @@ -390,7 +390,11 @@ == Qt::CaseInsensitive) srt |= QDir::IgnoreCase; QuaZipDirComparator lessThan(srt); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)) + std::sort(list.begin(), list.end(), lessThan); +#else qSort(list.begin(), list.end(), lessThan); +#endif } QuaZipDir_convertInfoList(list, result); return true; @@ -448,12 +452,12 @@ bool QuaZipDir::exists(const QString &filePath) const { - if (filePath == "/" || filePath.isEmpty()) + if (filePath == QLatin1String("/") || filePath.isEmpty()) return true; QString fileName = filePath; - if (fileName.endsWith('/')) + if (fileName.endsWith(QLatin1String("/"))) fileName.chop(1); - if (fileName.contains('/')) { + if (fileName.contains(QLatin1String("/"))) { QFileInfo fileInfo(fileName); #ifdef QUAZIP_QUAZIPDIR_DEBUG qDebug("QuaZipDir::exists(): fileName=%s, fileInfo.fileName()=%s, " @@ -464,9 +468,9 @@ QuaZipDir dir(*this); return dir.cd(fileInfo.path()) && dir.exists(fileInfo.fileName()); } else { - if (fileName == "..") { + if (fileName == QLatin1String("..")) { return !isRoot(); - } else if (fileName == ".") { + } else if (fileName == QLatin1String(".")) { return true; } else { QStringList entries = entryList(QDir::AllEntries, QDir::NoSort); @@ -482,11 +486,11 @@ #endif Qt::CaseSensitivity cs = QuaZip::convertCaseSensitivity( d->caseSensitivity); - if (filePath.endsWith('/')) { + if (filePath.endsWith(QLatin1String("/"))) { return entries.contains(filePath, cs); } else { return entries.contains(fileName, cs) - || entries.contains(fileName + "/", cs); + || entries.contains(fileName + QLatin1String("/"), cs); } } } @@ -524,7 +528,7 @@ QString QuaZipDir::relativeFilePath(const QString &fileName) const { - return QDir("/" + d->dir).relativeFilePath(fileName); + return QDir(QLatin1String("/") + d->dir).relativeFilePath(fileName); } void QuaZipDir::setCaseSensitivity(QuaZip::CaseSensitivity caseSensitivity) @@ -545,12 +549,12 @@ void QuaZipDir::setPath(const QString &path) { QString newDir = path; - if (newDir == "/") { - d->dir = ""; + if (newDir == QLatin1String("/")) { + d->dir = QLatin1String(""); } else { - if (newDir.endsWith('/')) + if (newDir.endsWith(QLatin1String("/"))) newDir.chop(1); - if (newDir.startsWith('/')) + if (newDir.startsWith(QLatin1String("/"))) newDir = newDir.mid(1); d->dir = newDir; } diff -Nru ckb-next-0.4.1/src/libs/quazip/quazipfile.cpp ckb-next-0.4.2/src/libs/quazip/quazipfile.cpp --- ckb-next-0.4.1/src/libs/quazip/quazipfile.cpp 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/libs/quazip/quazipfile.cpp 2019-10-08 12:56:16.000000000 +0000 @@ -26,6 +26,8 @@ using namespace std; +#define QUAZIP_VERSION_MADE_BY 0x1Eu + /// The implementation class for QuaZip. /** \internal @@ -112,7 +114,7 @@ { zip=new QuaZip(zipName); this->fileName=fileName; - if (this->fileName.startsWith('/')) + if (this->fileName.startsWith(QLatin1String("/"))) this->fileName = this->fileName.mid(1); this->caseSensitivity=cs; } @@ -232,7 +234,7 @@ return; } p->fileName=fileName; - if (p->fileName.startsWith('/')) + if (p->fileName.startsWith(QLatin1String("/"))) p->fileName = p->fileName.mid(1); p->caseSensitivity=cs; } @@ -339,14 +341,22 @@ zipSetFlags(p->zip->getZipFile(), ZIP_WRITE_DATA_DESCRIPTOR); else zipClearFlags(p->zip->getZipFile(), ZIP_WRITE_DATA_DESCRIPTOR); - p->setZipError(zipOpenNewFileInZip3_64(p->zip->getZipFile(), - p->zip->getFileNameCodec()->fromUnicode(info.name).constData(), &info_z, + p->setZipError(zipOpenNewFileInZip4_64(p->zip->getZipFile(), + p->zip->isUtf8Enabled() + ? info.name.toUtf8().constData() + : p->zip->getFileNameCodec()->fromUnicode(info.name).constData(), + &info_z, info.extraLocal.constData(), info.extraLocal.length(), info.extraGlobal.constData(), info.extraGlobal.length(), - p->zip->getCommentCodec()->fromUnicode(info.comment).constData(), + p->zip->isUtf8Enabled() + ? info.comment.toUtf8().constData() + : p->zip->getCommentCodec()->fromUnicode(info.comment).constData(), method, level, (int)raw, windowBits, memLevel, strategy, - password, (uLong)crc, p->zip->isZip64Enabled())); + password, (uLong)crc, + (p->zip->getOsCode() << 8) | QUAZIP_VERSION_MADE_BY, + 0, + p->zip->isZip64Enabled())); if(p->zipError==UNZ_OK) { p->writePos=0; setOpenMode(mode); diff -Nru ckb-next-0.4.1/src/libs/quazip/quazip_global.h ckb-next-0.4.2/src/libs/quazip/quazip_global.h --- ckb-next-0.4.1/src/libs/quazip/quazip_global.h 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/libs/quazip/quazip_global.h 2019-10-08 12:56:16.000000000 +0000 @@ -48,9 +48,9 @@ #endif // QUAZIP_STATIC #ifdef __GNUC__ -#define UNUSED __attribute__((__unused__)) +#define QUAZIP_UNUSED __attribute__((__unused__)) #else -#define UNUSED +#define QUAZIP_UNUSED #endif #define QUAZIP_EXTRA_NTFS_MAGIC 0x000Au diff -Nru ckb-next-0.4.1/src/libs/quazip/quazip.h ckb-next-0.4.2/src/libs/quazip/quazip.h --- ckb-next-0.4.1/src/libs/quazip/quazip.h 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/libs/quazip/quazip.h 2019-10-08 12:56:16.000000000 +0000 @@ -227,6 +227,14 @@ * Equivalent to calling setFileNameCodec(QTextCodec::codecForName(codecName)); **/ void setFileNameCodec(const char *fileNameCodecName); + /// Sets the OS code (highest 8 bits of the “version made by” field) for new files. + /** There is currently no way to specify this for each file individually, + except by calling this function before opening each file. If this function is not called, + then the default OS code will be used. The default code is set by calling + setDefaultOsCode(). The default value at the moment of QuaZip creation will be used. */ + void setOsCode(uint osCode); + /// Returns the OS code for new files. + uint getOsCode() const; /// Returns the codec used to encode/decode comments inside archive. QTextCodec* getFileNameCodec() const; /// Sets the codec used to encode/decode comments inside archive. @@ -502,6 +510,28 @@ * \sa setZip64Enabled() */ bool isZip64Enabled() const; + /// Enables the use of UTF-8 encoding for file names and comments text. + /** + * @param utf8 If \c true, the UTF-8 mode is enabled, disabled otherwise. + * + * Once this is enabled, the names of all new files and comments text (until + * the mode is disabled again) will be encoded in UTF-8 encoding, and the + * version to extract will be set to 6.3 (63) in ZIP header. By default, + * the UTF-8 mode is off due to compatibility reasons. + * + * Note that when extracting ZIP archives, the UTF-8 mode is determined from + * ZIP file header, not from this flag. + * + * \sa isUtf8Enabled() + */ + void setUtf8Enabled(bool utf8); + /// Returns whether the UTF-8 encoding mode is enabled. + /** + * @return \c true if and only if the UTF-8 mode is enabled. + * + * \sa setUtf8Enabled() + */ + bool isUtf8Enabled() const; /// Returns the auto-close flag. /** @sa setAutoClose() @@ -563,9 +593,19 @@ /** * @overload * Equivalent to calling - * setDefltFileNameCodec(QTextCodec::codecForName(codecName)). + * setDefaultFileNameCodec(QTextCodec::codecForName(codecName)). */ static void setDefaultFileNameCodec(const char *codecName); + /// Sets default OS code. + /** + * @sa setOsCode() + */ + static void setDefaultOsCode(uint osCode); + /// Returns default OS code. + /** + * @sa getOsCode() + */ + static uint getDefaultOsCode(); }; #endif diff -Nru ckb-next-0.4.1/src/libs/quazip/quazipnewinfo.cpp ckb-next-0.4.2/src/libs/quazip/quazipnewinfo.cpp --- ckb-next-0.4.1/src/libs/quazip/quazipnewinfo.cpp 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/libs/quazip/quazipnewinfo.cpp 2019-10-08 12:56:16.000000000 +0000 @@ -121,7 +121,7 @@ void QuaZipNewInfo::setPermissions(QFile::Permissions permissions) { - QuaZipNewInfo_setPermissions(this, permissions, name.endsWith('/')); + QuaZipNewInfo_setPermissions(this, permissions, name.endsWith(QLatin1String("/"))); } void QuaZipNewInfo::setFileNTFSTimes(const QString &fileName) @@ -134,7 +134,11 @@ } setFileNTFSmTime(fi.lastModified()); setFileNTFSaTime(fi.lastRead()); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) + setFileNTFScTime(fi.birthTime()); +#else setFileNTFScTime(fi.created()); +#endif } static void setNTFSTime(QByteArray &extra, const QDateTime &time, int position, diff -Nru ckb-next-0.4.1/src/libs/quazip/quazipnewinfo.h ckb-next-0.4.2/src/libs/quazip/quazipnewinfo.h --- ckb-next-0.4.1/src/libs/quazip/quazipnewinfo.h 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/libs/quazip/quazipnewinfo.h 2019-10-08 12:56:16.000000000 +0000 @@ -70,7 +70,7 @@ */ quint32 externalAttr; /// File comment. - /** Will be encoded using QuaZip::getCommentCodec(). + /** Will be encoded in UTF-8 encoding. **/ QString comment; /// File local extra field. @@ -148,8 +148,9 @@ /** * If the file doesn't exist, a warning is printed to the stderr and nothing * is done. Otherwise, all three times, as reported by - * QFileInfo::lastModified(), QFileInfo::lastRead() and QFileInfo::created(), - * are written to the NTFS extra field record. + * QFileInfo::lastModified(), QFileInfo::lastRead() and + * QFileInfo::birthTime() (>=Qt5.10) or QFileInfo::created(), are written to + * the NTFS extra field record. * * The NTFS record is written to * both the local and the global extra fields, updating the existing record diff -Nru ckb-next-0.4.1/src/libs/quazip/unzip.c ckb-next-0.4.2/src/libs/quazip/unzip.c --- ckb-next-0.4.1/src/libs/quazip/unzip.c 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/libs/quazip/unzip.c 2019-10-08 12:56:16.000000000 +0000 @@ -15,6 +15,8 @@ For more info read MiniZip_info.txt + Modifications for static code analysis report + Copyright (C) 2016 Intel Deutschland GmbH ------------------------------------------------------------------------------------ Decryption code comes from crypt.c by Info-ZIP but has been greatly reduced in terms of @@ -28,7 +30,7 @@ If, for some reason, all these files are missing, the Info-ZIP license also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html - crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h] + crypt.c (full version) by Info-ZIP. Last revised: [see minizip_crypt.h] The encryption/decryption parts of this source code (as opposed to the non-echoing password parts) were originally written in Europe. The @@ -71,7 +73,7 @@ #include #include -#include "zlib.h" +#include #if (ZLIB_VERNUM < 0x1270) typedef uLongf z_crc_t; #endif @@ -197,7 +199,7 @@ #ifndef NOUNCRYPT -#include "crypt.h" +#include "minizip_crypt.h" #endif /* =========================================================================== @@ -856,6 +858,17 @@ pglobal_info32->size_comment = s->gi.size_comment; return UNZ_OK; } + +extern int ZEXPORT unzGetFileFlags (unzFile file, unsigned* pflags) +{ + unz64_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + *pflags = s->flags; + return UNZ_OK; +} + /* Translate date/time from Dos format to tm_unz (readable more easilty) */ @@ -1198,6 +1211,8 @@ &s->cur_file_info_internal, NULL,0,NULL,0,NULL,0); s->current_file_ok = (err == UNZ_OK); + if (s->cur_file_info.flag & UNZ_ENCODING_UTF8) + unzSetFlags(file, UNZ_ENCODING_UTF8); return err; } @@ -1594,6 +1609,7 @@ pfile_in_zip_read_info->stream_initialised=Z_DEFLATED; else { + TRYFREE(pfile_in_zip_read_info->read_buffer); TRYFREE(pfile_in_zip_read_info); return err; } diff -Nru ckb-next-0.4.1/src/libs/quazip/unzip.h ckb-next-0.4.2/src/libs/quazip/unzip.h --- ckb-next-0.4.1/src/libs/quazip/unzip.h 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/libs/quazip/unzip.h 2019-10-08 12:56:16.000000000 +0000 @@ -53,7 +53,7 @@ #endif #ifndef _ZLIB_H -#include "zlib.h" +#include #endif #ifndef _ZLIBIOAPI_H @@ -87,6 +87,7 @@ #define UNZ_AUTO_CLOSE 0x01u #define UNZ_DEFAULT_FLAGS UNZ_AUTO_CLOSE +#define UNZ_ENCODING_UTF8 0x0800u /* tm_unz contain date/time info */ typedef struct tm_unz_s @@ -227,6 +228,8 @@ extern int ZEXPORT unzGetGlobalInfo64 OF((unzFile file, unz_global_info64 *pglobal_info)); + +extern int ZEXPORT unzGetFileFlags OF((unzFile file, unsigned* pflags)); /* Write info about the ZipFile in the *pglobal_info structure. No preparation of the structure is needed diff -Nru ckb-next-0.4.1/src/libs/quazip/zip.c ckb-next-0.4.2/src/libs/quazip/zip.c --- ckb-next-0.4.1/src/libs/quazip/zip.c 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/libs/quazip/zip.c 2019-10-08 12:56:16.000000000 +0000 @@ -12,6 +12,9 @@ Modifications for QIODevice support and other QuaZIP fixes Copyright (C) 2005-2014 Sergey A. Tachenov + Fixing static code analysis issues + Copyright (C) 2016 Intel Deutschland GmbH + Changes Oct-2009 - Mathias Svensson - Remove old C style function prototypes Oct-2009 - Mathias Svensson - Added Zip64 Support when creating new file archives @@ -29,7 +32,8 @@ #include #include #include -#include "zlib.h" + +#include #if (ZLIB_VERNUM < 0x1270) typedef uLongf z_crc_t; #endif @@ -192,7 +196,7 @@ #ifndef NOCRYPT #define INCLUDECRYPTINGCODE_IFCRYPTALLOWED -#include "crypt.h" +#include "minizip_crypt.h" #endif local linkedlist_datablock_internal* allocate_new_datablock() @@ -527,13 +531,14 @@ if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) break; - for (i=(int)uReadSize-3; (i--)>0;) + for (i=(int)uReadSize-3; (i--)>0;){ if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) { uPosFound = uReadPos+i; break; } + } if (uPosFound!=0) break; @@ -988,7 +993,9 @@ if (err==ZIP_OK) { - if(zi->ci.zip64) + if(zi->ci.flag & ZIP_ENCODING_UTF8) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)63,2);/* Version 6.3 is required for Unicode support */ + else if(zi->ci.zip64) err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2);/* version needed to extract */ else err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)version_to_extract,2); @@ -1146,6 +1153,8 @@ } zi->ci.flag = flagBase; + if (zi->flags & ZIP_ENCODING_UTF8) + zi->ci.flag |= ZIP_ENCODING_UTF8; if ((level==8) || (level==9)) zi->ci.flag |= 2; if (level==2) @@ -1171,6 +1180,9 @@ zi->ci.size_centralExtraFree = 32; /* Extra space we have reserved in case we need to add ZIP64 extra info data */ zi->ci.central_header = (char*)ALLOC((uInt)zi->ci.size_centralheader + zi->ci.size_centralExtraFree); + if(!zi->ci.central_header) { + return (Z_MEM_ERROR); + } zi->ci.size_centralExtra = size_extrafield_global; zip64local_putValue_inmemory(zi->ci.central_header,(uLong)CENTRALHEADERMAGIC,4); @@ -1648,8 +1660,7 @@ /*version Made by*/ zip64local_putValue_inmemory(zi->ci.central_header+4,(uLong)45,2); /*version needed*/ - zip64local_putValue_inmemory(zi->ci.central_header+6,(uLong)45,2); - + zip64local_putValue_inmemory(zi->ci.central_header+6,(uLong)((zi->ci.flag & ZIP_ENCODING_UTF8) ? 63 : 45),2); } zip64local_putValue_inmemory(zi->ci.central_header+16,crc32,4); /*crc*/ @@ -1843,7 +1854,7 @@ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2); if (err==ZIP_OK) /* version needed */ - err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2); + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)((zi->ci.flag & ZIP_ENCODING_UTF8) ? 63 : 45),2); if (err==ZIP_OK) /* number of this disk */ err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); @@ -2025,6 +2036,9 @@ return ZIP_PARAMERROR; pNewHeader = (char*)ALLOC(*dataLen); + if(!pNewHeader) { + return Z_MEM_ERROR; + } pTmp = pNewHeader; while(p < (pData + *dataLen)) diff -Nru ckb-next-0.4.1/src/libs/quazip/zip.h ckb-next-0.4.2/src/libs/quazip/zip.h --- ckb-next-0.4.1/src/libs/quazip/zip.h 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/src/libs/quazip/zip.h 2019-10-08 12:56:16.000000000 +0000 @@ -53,7 +53,7 @@ //#define HAVE_BZIP2 #ifndef _ZLIB_H -#include "zlib.h" +#include #endif #ifndef _ZLIBIOAPI_H @@ -85,6 +85,7 @@ #define ZIP_WRITE_DATA_DESCRIPTOR 0x8u #define ZIP_AUTO_CLOSE 0x1u #define ZIP_SEQUENTIAL 0x2u +#define ZIP_ENCODING_UTF8 0x0800u #define ZIP_DEFAULT_FLAGS (ZIP_AUTO_CLOSE | ZIP_WRITE_DATA_DESCRIPTOR) #ifndef DEF_MEM_LEVEL diff -Nru ckb-next-0.4.1/VERSION.cmake ckb-next-0.4.2/VERSION.cmake --- ckb-next-0.4.1/VERSION.cmake 2019-08-27 13:36:13.000000000 +0000 +++ ckb-next-0.4.2/VERSION.cmake 2019-10-08 12:56:16.000000000 +0000 @@ -1,7 +1,7 @@ -set(ckb-next_VERSION "0.4.1") +set(ckb-next_VERSION "0.4.2") set(ckb-next_VERSION_MAJOR 0) set(ckb-next_VERSION_MINOR 4) -set(ckb-next_VERSION_PATCH 1) +set(ckb-next_VERSION_PATCH 2) # This should be set to TRUE _only_ in archive/tarball releases set(ckb-next_VERSION_IS_RELEASE TRUE)