Revert "feat: hotkeys rework and MIDI support (#242)"

This reverts commit ad5fa403
This commit is contained in:
D3SOX 2022-05-27 18:48:25 +02:00
parent e78f17fa6e
commit 085abaa50b
No known key found for this signature in database
GPG Key ID: 39EC1673FC37B048
23 changed files with 368 additions and 715 deletions

3
.gitmodules vendored
View File

@ -41,6 +41,3 @@
[submodule "lib/guardpp"]
path = lib/guardpp
url = https://github.com/Soundux/guardpp
[submodule "lib/libremidi"]
path = lib/libremidi
url = https://github.com/Soundux/libremidi

View File

@ -78,7 +78,6 @@ add_subdirectory(lib/nativefiledialog-extended EXCLUDE_FROM_ALL)
add_subdirectory(lib/tiny-process-library EXCLUDE_FROM_ALL)
add_subdirectory(lib/backward-cpp EXCLUDE_FROM_ALL)
add_subdirectory(lib/traypp EXCLUDE_FROM_ALL)
add_subdirectory(lib/libremidi)
add_subdirectory(lib/guardpp)
add_subdirectory(lib/lockpp)
@ -94,7 +93,7 @@ set(HTTPLIB_REQUIRE_OPENSSL ON)
add_subdirectory(lib/cpp-httplib EXCLUDE_FROM_ALL)
target_include_directories(soundux SYSTEM PRIVATE "lib/cpp-httplib")
target_link_libraries(soundux PRIVATE webview nfd tiny-process-library tray guardpp httplib lockpp libremidi)
target_link_libraries(soundux PRIVATE webview nfd tiny-process-library tray guard httplib lockpp)
if (${EMBED_PATH} STREQUAL "OFF")
message("Web-content will not be embedded")

@ -1 +0,0 @@
Subproject commit e2213b724d8426366235f3de70db25fb231a0b07

View File

@ -33,9 +33,9 @@ namespace Soundux
inline Objects::Queue gQueue;
inline Objects::Config gConfig;
inline Objects::YoutubeDl gYtdl;
inline Objects::Hotkeys gHotKeys;
inline Objects::Settings gSettings;
inline std::unique_ptr<Objects::Window> gGui;
inline std::shared_ptr<Objects::Hotkeys> gHotKeys;
inline std::shared_ptr<guardpp::guard> gGuard;

View File

@ -1,10 +1,6 @@
#include "hotkeys.hpp"
#include "keys.hpp"
#include "linux/x11.hpp"
#include "windows/windows.hpp"
#include <core/global/globals.hpp>
#include <cstdint>
#include <fancy.hpp>
namespace Soundux
{
@ -23,117 +19,38 @@ namespace Soundux
} // namespace traits
namespace Objects
{
std::shared_ptr<Hotkeys> Hotkeys::createInstance()
void Hotkeys::init()
{
std::shared_ptr<Hotkeys> rtn;
#if defined(__linux__)
rtn = std::shared_ptr<X11>(new X11()); // NOLINT
#elif defined(_WIN32)
rtn = std::shared_ptr<WindowsHotkeys>(new WindowsHotkeys()); // NOLINT
#endif
rtn->setup();
return rtn;
listener = std::thread([this] { listen(); });
}
void Hotkeys::setup()
{
try
{
midi.open_port();
midi.set_callback([this](const libremidi::message &message) {
if (message.size() < 3)
{
Fancy::fancy.logTime().failure()
<< "Midi Message contains less than 3 bytes, can't parse information";
return;
}
auto byte0 = message[0];
auto byte1 = message[1];
auto byte2 = message[2];
MidiKey key;
key.byte0 = byte0;
key.key = byte1;
key.byte2 = byte2;
key.type = Enums::KeyType::Midi;
if (byte0 == 144)
{
onKeyDown(key);
}
else if (byte0 == 128)
{
onKeyUp(key);
}
else if (byte0 == 176)
{
if (shouldNotifyKnob)
{
Globals::gGui->onHotKeyReceived({key}); // NOLINT
}
else
{
auto newVolume = static_cast<int>((static_cast<float>(byte2) / 127.f) * 100);
if (Globals::gSettings.localVolumeKnob && key == Globals::gSettings.localVolumeKnob)
{
Globals::gSettings.localVolume = newVolume;
Globals::gGui->onVolumeChanged();
Globals::gQueue.push([=]() { Globals::gGui->onLocalVolumeChanged(newVolume); });
}
else if (Globals::gSettings.remoteVolumeKnob && key == Globals::gSettings.remoteVolumeKnob)
{
Globals::gSettings.remoteVolume = newVolume;
Globals::gGui->onVolumeChanged();
Globals::gQueue.push([=]() { Globals::gGui->onRemoteVolumeChanged(newVolume); });
}
}
}
});
midi.ignore_types(false, false, false);
}
catch (const libremidi::midi_exception &e)
{
Fancy::fancy.logTime().failure() << "Failed to initialize libremidi: " << e.what() << std::endl;
}
}
void Hotkeys::notify(bool state)
void Hotkeys::shouldNotify(bool status)
{
pressedKeys.clear();
shouldNotify = state;
notify = status;
}
void Hotkeys::requestKnob(bool state)
void Hotkeys::onKeyUp(int key)
{
shouldNotifyKnob = state;
}
void Hotkeys::onKeyUp(const Key &key)
{
if (std::find(pressedKeys.begin(), pressedKeys.end(), key) != pressedKeys.end())
if (notify && !pressedKeys.empty() &&
std::find(pressedKeys.begin(), pressedKeys.end(), key) != pressedKeys.end())
{
if (shouldNotify)
{
Globals::gGui->onHotKeyReceived(pressedKeys);
pressedKeys.clear();
}
else
{
pressedKeys.erase(std::remove_if(pressedKeys.begin(), pressedKeys.end(),
[&](const auto &keyItem) { return key == keyItem; }),
pressedKeys.end());
}
Globals::gGui->onHotKeyReceived(pressedKeys);
pressedKeys.clear();
}
else
{
pressedKeys.erase(std::remove_if(pressedKeys.begin(), pressedKeys.end(),
[key](const auto &item) { return key == item; }),
pressedKeys.end());
}
}
bool isCloseMatch(const std::vector<Key> &pressedKeys, const std::vector<Key> &keys)
bool isCloseMatch(const std::vector<int> &pressedKeys, const std::vector<int> &keys)
{
if (pressedKeys.size() >= keys.size())
{
bool allMatched = true;
for (const auto &key : keys)
{
if (std::find(pressedKeys.begin(), pressedKeys.end(), key) == pressedKeys.end()) // NOLINT
if (std::find(pressedKeys.begin(), pressedKeys.end(), key) == pressedKeys.end())
{
allMatched = false;
}
@ -142,14 +59,13 @@ namespace Soundux
}
return false;
}
template <typename T> std::optional<Sound> getBestMatch(const T &list, const std::vector<Key> &pressedKeys)
template <typename T> std::optional<Sound> getBestMatch(const T &list, const std::vector<int> &pressedKeys)
{
std::optional<Sound> rtn;
for (const auto &_sound : list)
{
const auto &sound = [&]() constexpr
{
const auto &sound = [&] {
if constexpr (traits::is_pair<std::decay_t<decltype(_sound)>>::value)
{
return _sound.second.get();
@ -158,13 +74,12 @@ namespace Soundux
{
return _sound;
}
}
();
}();
if (sound.hotkeys.empty())
continue;
if (pressedKeys == sound.hotkeys)
if (sound.hotkeys == pressedKeys)
{
rtn = sound;
break;
@ -182,81 +97,78 @@ namespace Soundux
}
return rtn;
}
void Hotkeys::onKeyDown(const Key &key)
void Hotkeys::onKeyDown(int key)
{
if (std::find(pressedKeys.begin(), pressedKeys.end(), key) != pressedKeys.end())
if (std::find(keysToPress.begin(), keysToPress.end(), key) != keysToPress.end())
{
return;
}
if (std::find(pressedKeys.begin(), pressedKeys.end(), key) == pressedKeys.end())
{
pressedKeys.emplace_back(key);
}
else
{
return;
}
pressedKeys.emplace_back(key);
if (!shouldNotify)
if (notify)
{
if (!Globals::gSettings.stopHotkey.empty() &&
(Globals::gSettings.stopHotkey == pressedKeys ||
isCloseMatch(pressedKeys, Globals::gSettings.stopHotkey)))
{
Globals::gGui->stopSounds();
return;
}
return;
}
std::optional<Sound> bestMatch;
if (!Globals::gSettings.stopHotkey.empty() && (pressedKeys == Globals::gSettings.stopHotkey ||
isCloseMatch(pressedKeys, Globals::gSettings.stopHotkey)))
{
Globals::gGui->stopSounds();
return;
}
if (Globals::gSettings.tabHotkeysOnly)
std::optional<Sound> bestMatch;
if (Globals::gSettings.tabHotkeysOnly)
{
if (Globals::gData.isOnFavorites)
{
if (Globals::gData.isOnFavorites)
{
auto sounds = Globals::gData.getFavorites();
bestMatch = getBestMatch(sounds, pressedKeys);
}
else
{
auto tab = Globals::gData.getTab(Globals::gSettings.selectedTab);
if (tab)
{
bestMatch = getBestMatch(tab->sounds, pressedKeys);
}
}
auto sounds = Globals::gData.getFavorites();
bestMatch = getBestMatch(sounds, pressedKeys);
}
else
{
auto scopedSounds = Globals::gSounds.scoped();
bestMatch = getBestMatch(*scopedSounds, pressedKeys);
}
if (bestMatch)
{
auto pSound = Globals::gGui->playSound(bestMatch->id);
if (pSound)
auto tab = Globals::gData.getTab(Globals::gSettings.selectedTab);
if (tab)
{
Globals::gGui->onSoundPlayed(*pSound);
bestMatch = getBestMatch(tab->sounds, pressedKeys);
}
}
}
}
std::string Hotkeys::getKeyName(const Key &key)
{
if (key.type == Enums::KeyType::Midi)
else
{
return "MIDI_" + std::to_string(key.key);
auto scopedSounds = Globals::gSounds.scoped();
bestMatch = getBestMatch(*scopedSounds, pressedKeys);
}
return "";
}
std::string Hotkeys::getKeySequence(const std::vector<Key> &keys)
{
std::string rtn;
for (auto it = keys.begin(); it != keys.end(); ++it)
if (bestMatch)
{
rtn += getKeyName(*it);
if (std::distance(it, keys.end()) > 1)
auto pSound = Globals::gGui->playSound(bestMatch->id);
if (pSound)
{
rtn += " + ";
Globals::gGui->onSoundPlayed(*pSound);
}
}
return rtn;
}
std::string Hotkeys::getKeySequence(const std::vector<int> &keys)
{
std::string rtn;
for (const auto &key : keys)
{
rtn += getKeyName(key) + " + ";
}
if (!rtn.empty())
{
return rtn.substr(0, rtn.length() - 3);
}
return "";
}
} // namespace Objects
} // namespace Soundux

View File

@ -1,15 +1,7 @@
#pragma once
#pragma push_macro("max")
#pragma push_macro("min")
#undef min
#undef max
#include <libremidi/libremidi.hpp>
#pragma pop_macro("min")
#pragma pop_macro("max")
#include "keys.hpp"
#include <atomic>
#include <string>
#include <thread>
#include <vector>
namespace Soundux
@ -18,32 +10,33 @@ namespace Soundux
{
class Hotkeys
{
libremidi::midi_in midi;
std::thread listener;
std::atomic<bool> kill = false;
std::atomic<bool> notify = false;
protected:
Hotkeys() = default;
virtual void setup();
std::vector<int> pressedKeys;
std::vector<int> keysToPress;
#if defined(_WIN32)
std::thread keyPressThread;
std::atomic<bool> shouldPressKeys = false;
#endif
protected:
std::vector<Key> pressedKeys;
std::atomic<bool> shouldNotify = false;
std::atomic<bool> shouldNotifyKnob = false;
private:
void listen();
public:
static std::shared_ptr<Hotkeys> createInstance();
void init();
void stop();
void shouldNotify(bool);
public:
virtual void notify(bool);
virtual void requestKnob(bool);
void onKeyUp(int);
void onKeyDown(int);
virtual void onKeyUp(const Key &);
virtual void onKeyDown(const Key &);
void pressKeys(const std::vector<int> &);
void releaseKeys(const std::vector<int> &);
virtual void pressKeys(const std::vector<Key> &) = 0;
virtual void releaseKeys(const std::vector<Key> &) = 0;
virtual std::string getKeyName(const Key &);
virtual std::string getKeySequence(const std::vector<Key> &);
std::string getKeyName(const int &);
std::string getKeySequence(const std::vector<int> &);
};
} // namespace Objects
} // namespace Soundux

View File

@ -1,9 +0,0 @@
#include "keys.hpp"
namespace Soundux::Objects
{
bool Key::operator==(const Key &other) const
{
return other.key == key && other.type == type;
}
} // namespace Soundux::Objects

View File

@ -1,33 +0,0 @@
#pragma once
#include <cstdint>
namespace Soundux
{
namespace Enums
{
enum class KeyType : std::uint8_t
{
Keyboard,
Mouse,
Midi
};
} // namespace Enums
namespace Objects
{
struct Key
{
int key;
Enums::KeyType type;
virtual ~Key() = default;
bool operator==(const Key &) const;
};
struct MidiKey : public Key
{
int byte0; // Action
int byte2; // Knob value / Press strength
~MidiKey() override = default;
};
} // namespace Objects
} // namespace Soundux

View File

@ -1,26 +1,25 @@
#if defined(__linux__)
#include "x11.hpp"
#if defined(__linux__) && __has_include(<X11/Xlib.h>)
#include "../hotkeys.hpp"
#include <X11/X.h>
#include <X11/XKBlib.h>
#include <X11/Xlib.h>
#include <X11/extensions/XI2.h>
#include <X11/extensions/XInput2.h>
#include <X11/extensions/XTest.h>
#include <chrono>
#include <core/hotkeys/keys.hpp>
#include <cstdlib>
#include <fancy.hpp>
#include <thread>
using namespace std::chrono_literals;
namespace Soundux::Objects
{
void X11::setup()
Display *display;
void Hotkeys::listen()
{
Hotkeys::setup();
listener = std::thread([this] { listen(); });
}
void X11::listen()
{
auto *displayEnv = std::getenv("DISPLAY"); // NOLINT
auto *x11Display = XOpenDisplay(displayEnv);
auto *displayenv = std::getenv("DISPLAY"); // NOLINT
auto *x11Display = XOpenDisplay(displayenv);
if (!x11Display)
{
@ -28,18 +27,20 @@ namespace Soundux::Objects
if (!(x11Display = XOpenDisplay(":0")))
{
Fancy::fancy.logTime().failure() << "Could not open X11 Display" << std::endl;
return;
}
}
else
{
Fancy::fancy.logTime().message() << "Using DISPLAY " << displayEnv << std::endl;
Fancy::fancy.logTime().message() << "Using DISPLAY " << displayenv << std::endl;
}
display = x11Display;
int event_rtn = 0, ext_rtn = 0;
int major_op = 0, event_rtn = 0, ext_rtn = 0;
if (!XQueryExtension(display, "XInputExtension", &major_op, &event_rtn, &ext_rtn))
{
Fancy::fancy.logTime().failure() << "Failed to find XInputExtension" << std::endl;
return;
}
Window root = DefaultRootWindow(display); // NOLINT
@ -66,106 +67,78 @@ namespace Soundux::Objects
XNextEvent(display, &event);
auto *cookie = reinterpret_cast<XGenericEventCookie *>(&event.xcookie);
if (XGetEventData(display, cookie) && cookie->type == GenericEvent && cookie->extension == major_op)
if (XGetEventData(display, cookie) && cookie->type == GenericEvent && cookie->extension == major_op &&
(cookie->evtype == XI_RawKeyPress || cookie->evtype == XI_RawKeyRelease ||
cookie->evtype == XI_RawButtonPress || cookie->evtype == XI_RawButtonRelease))
{
if (cookie->evtype == XI_RawKeyPress || cookie->evtype == XI_RawKeyRelease)
auto *data = reinterpret_cast<XIRawEvent *>(cookie->data);
auto key = data->detail;
if (key == 1)
continue;
if (cookie->evtype == XI_RawKeyPress || cookie->evtype == XI_RawButtonPress)
{
auto *data = reinterpret_cast<XIRawEvent *>(cookie->data);
auto key = data->detail;
Key pressedKey;
pressedKey.key = key;
pressedKey.type = Enums::KeyType::Keyboard;
if (cookie->evtype == XI_RawKeyPress)
{
onKeyDown(pressedKey);
}
else
{
onKeyUp(pressedKey);
}
onKeyDown(key);
}
else if (cookie->evtype == XI_RawButtonPress || cookie->evtype == XI_RawButtonRelease)
else if (cookie->evtype == XI_RawKeyRelease || cookie->evtype == XI_RawButtonRelease)
{
auto *data = reinterpret_cast<XIRawEvent *>(cookie->data);
auto button = data->detail;
if (button != 1)
{
Key pressedButton;
pressedButton.key = button;
pressedButton.type = Enums::KeyType::Mouse;
if (cookie->evtype == XI_RawButtonPress)
{
onKeyDown(pressedButton);
}
else
{
onKeyUp(pressedButton);
}
}
onKeyUp(key);
}
}
}
else
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::this_thread::sleep_for(100ms);
}
}
}
std::string X11::getKeyName(const Key &key)
std::string Hotkeys::getKeyName(const int &key)
{
if (!Hotkeys::getKeyName(key).empty())
// TODO(curve): There is no Keysym for the mouse buttons and I couldn't find any way to get the name for the
// mouse buttons so they'll just be named KEY_1 (1 is the Keycode). Maybe someone will be able to help me but I
// just can't figure it out
KeySym s = XkbKeycodeToKeysym(display, key, 0, 0);
if (s == NoSymbol)
{
return Hotkeys::getKeyName(key);
return "KEY_" + std::to_string(key);
}
if (key.type == Enums::KeyType::Keyboard)
auto *str = XKeysymToString(s);
if (str == nullptr)
{
KeySym keySym = XkbKeycodeToKeysym(display, key.key, 0, 0);
if (keySym == NoSymbol)
{
return "KEY_" + std::to_string(key.key);
}
auto *str = XKeysymToString(keySym);
if (!str)
{
return "KEY_" + std::to_string(key.key);
}
return str;
return "KEY_" + std::to_string(key);
}
if (key.type == Enums::KeyType::Mouse)
{
return "MOUSE_" + std::to_string(key.key);
}
return "";
return str;
}
void X11::pressKeys(const std::vector<Key> &keys)
{
for (const auto &key : keys)
{
XTestFakeKeyEvent(display, key.key, True, 0);
}
}
void X11::releaseKeys(const std::vector<Key> &keys)
{
for (const auto &key : keys)
{
XTestFakeKeyEvent(display, key.key, False, 0);
}
}
X11::~X11()
void Hotkeys::stop()
{
kill = true;
listener.join();
}
void Hotkeys::pressKeys(const std::vector<int> &keys)
{
keysToPress = keys;
for (const auto &key : keys)
{
XTestFakeKeyEvent(display, key, True, 0);
}
}
void Hotkeys::releaseKeys(const std::vector<int> &keys)
{
keysToPress.clear();
for (const auto &key : keys)
{
XTestFakeKeyEvent(display, key, False, 0);
}
}
} // namespace Soundux::Objects
#endif

View File

@ -1,33 +0,0 @@
#pragma once
#if defined(__linux__)
#include <core/hotkeys/hotkeys.hpp>
#include <thread>
struct _XDisplay; // NOLINT
using Display = _XDisplay;
namespace Soundux
{
namespace Objects
{
class X11 : public Hotkeys
{
int major_op;
Display *display;
std::thread listener;
std::atomic<bool> kill = false;
private:
void listen();
void setup() override;
public:
~X11();
std::string getKeyName(const Key &key) override;
void pressKeys(const std::vector<Key> &keys) override;
void releaseKeys(const std::vector<Key> &keys) override;
};
} // namespace Objects
} // namespace Soundux
#endif

View File

@ -1,90 +1,88 @@
#if defined(_WIN32)
#include "windows.hpp"
#include "../hotkeys.hpp"
#include <Windows.h>
#include <chrono>
#include <core/global/globals.hpp>
#include <winuser.h>
using namespace std::chrono_literals;
namespace Soundux::Objects
{
HHOOK WindowsHotkeys::oMouseProc;
HHOOK WindowsHotkeys::oKeyboardProc;
HHOOK oKeyBoardProc;
HHOOK oMouseProc;
void WindowsHotkeys::setup()
{
Hotkeys::setup();
oMouseProc = SetWindowsHookEx(WH_MOUSE_LL, mouseProc, nullptr, NULL);
oKeyboardProc = SetWindowsHookEx(WH_KEYBOARD_LL, keyBoardProc, nullptr, NULL);
listener = std::thread([this] { listen(); });
keyPresser = std::thread([this] { presser(); });
}
LRESULT CALLBACK WindowsHotkeys::keyBoardProc(int nCode, WPARAM wParam, LPARAM lParam)
LRESULT CALLBACK keyBoardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HC_ACTION)
{
auto *info = reinterpret_cast<PKBDLLHOOKSTRUCT>(lParam); // NOLINT
if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)
{
Key key;
key.type = Enums::KeyType::Keyboard;
key.key = static_cast<int>(info->vkCode);
Globals::gHotKeys->onKeyDown(key);
auto *info = reinterpret_cast<PKBDLLHOOKSTRUCT>(lParam);
Globals::gHotKeys.onKeyDown(info->vkCode);
}
else if (wParam == WM_KEYUP || wParam == WM_SYSKEYUP)
{
Key key;
key.type = Enums::KeyType::Keyboard;
key.key = static_cast<int>(info->vkCode);
Globals::gHotKeys->onKeyUp(key);
auto *info = reinterpret_cast<PKBDLLHOOKSTRUCT>(lParam);
Globals::gHotKeys.onKeyUp(info->vkCode);
}
}
return CallNextHookEx(oKeyboardProc, nCode, wParam, lParam);
return CallNextHookEx(oKeyBoardProc, nCode, wParam, lParam);
}
LRESULT CALLBACK WindowsHotkeys::mouseProc(int nCode, WPARAM wParam, LPARAM lParam)
LRESULT CALLBACK mouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HC_ACTION)
{
// TODO(curve): How would I tell if XButton1 or XButton2 is pressed? Is there a nicer way to do this?
switch (wParam)
{
case WM_RBUTTONUP: {
Key key;
key.key = VK_RBUTTON;
key.type = Enums::KeyType::Mouse;
Soundux::Globals::gHotKeys->onKeyUp(key);
}
break;
case WM_RBUTTONDOWN: {
Key key;
key.key = VK_RBUTTON;
key.type = Enums::KeyType::Mouse;
Soundux::Globals::gHotKeys->onKeyDown(key);
}
break;
case WM_MBUTTONUP: {
Key key;
key.key = VK_MBUTTON;
key.type = Enums::KeyType::Mouse;
Soundux::Globals::gHotKeys->onKeyUp(key);
}
break;
case WM_MBUTTONDOWN: {
Key key;
key.key = VK_RBUTTON;
key.type = Enums::KeyType::Mouse;
Soundux::Globals::gHotKeys->onKeyDown(key);
}
break;
case WM_RBUTTONUP:
Globals::gHotKeys.onKeyUp(VK_RBUTTON);
break;
case WM_RBUTTONDOWN:
Globals::gHotKeys.onKeyDown(VK_RBUTTON);
break;
case WM_MBUTTONDOWN:
Globals::gHotKeys.onKeyDown(VK_MBUTTON);
break;
case WM_MBUTTONUP:
Globals::gHotKeys.onKeyUp(VK_MBUTTON);
break;
}
}
return CallNextHookEx(oMouseProc, nCode, wParam, lParam);
}
void WindowsHotkeys::listen()
void Hotkeys::listen()
{
oKeyBoardProc = SetWindowsHookEx(WH_KEYBOARD_LL, keyBoardProc, GetModuleHandle(nullptr), NULL);
oMouseProc = SetWindowsHookEx(WH_MOUSE_LL, mouseProc, GetModuleHandle(nullptr), NULL);
keyPressThread = std::thread([this] {
while (!kill)
{
//* Yes, this is absolutely cursed. I tried to implement this by just sending the keydown event once but
//* it does not work like that on windows, so I have to do this, thank you Microsoft, I hate you.
if (shouldPressKeys)
{
for (const auto &key : keysToPress)
{
keybd_event(key, 0, 1, 0);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
else
{
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
}
});
MSG message;
while (!GetMessage(&message, nullptr, 0, 0))
while (!GetMessage(&message, nullptr, NULL, NULL))
{
if (kill)
{
@ -94,82 +92,68 @@ namespace Soundux::Objects
DispatchMessage(&message);
}
}
void WindowsHotkeys::presser()
{
std::unique_lock lock(keysToPressMutex);
while (!kill)
{
cv.wait(lock, [&]() { return !keysToPress.empty() || kill; });
//* Yes, this is absolutely cursed. I tried to implement this by just sending the keydown event once but
//* it does not work like that on windows, so I have to do this, thank you Microsoft, I hate you.
for (const auto &key : keysToPress)
{
keybd_event(key.key, 0, 1, 0);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
}
std::string WindowsHotkeys::getKeyName(const Key &key)
{
if (!Hotkeys::getKeyName(key).empty())
{
return Hotkeys::getKeyName(key);
}
if (key.type == Enums::KeyType::Keyboard)
{
char name[128];
auto result = GetKeyNameTextA(MapVirtualKey(key.key, MAPVK_VK_TO_VSC) << 16, name, 128);
if (result == 0)
{
return "KEY_" + std::to_string(key.key);
}
return name;
}
if (key.type == Enums::KeyType::Mouse)
{
return "MOUSE_" + std::to_string(key.key);
}
return "";
}
void WindowsHotkeys::pressKeys(const std::vector<Key> &keys)
{
std::unique_lock lock(keysToPressMutex);
keysToPress = keys;
}
void WindowsHotkeys::releaseKeys(const std::vector<Key> &keys)
{
std::unique_lock lock(keysToPressMutex);
for (const auto &key : keys)
{
for (auto it = keysToPress.begin(); it != keysToPress.end();)
{
if (*it == key)
{
it = keysToPress.erase(it);
}
else
{
++it;
}
}
}
}
WindowsHotkeys::~WindowsHotkeys()
void Hotkeys::stop()
{
kill = true;
PostThreadMessage(GetThreadId(listener.native_handle()), WM_QUIT, 0, 0);
listener.join();
cv.notify_all();
keyPresser.join();
UnhookWindowsHookEx(oMouseProc);
UnhookWindowsHookEx(oKeyboardProc);
UnhookWindowsHookEx(oKeyBoardProc);
PostThreadMessage(GetThreadId(listener.native_handle()), WM_QUIT, 0, 0);
listener.join();
keyPressThread.join();
}
std::string Hotkeys::getKeyName(const int &key)
{
auto scanCode = MapVirtualKey(key, MAPVK_VK_TO_VSC);
CHAR name[128];
int result = 0;
switch (key)
{
case VK_LEFT:
case VK_UP:
case VK_RIGHT:
case VK_DOWN:
case VK_RCONTROL:
case VK_RMENU:
case VK_LWIN:
case VK_RWIN:
case VK_APPS:
case VK_PRIOR:
case VK_NEXT:
case VK_END:
case VK_HOME:
case VK_INSERT:
case VK_DELETE:
case VK_DIVIDE:
case VK_NUMLOCK:
scanCode |= KF_EXTENDED;
default:
result = GetKeyNameTextA(scanCode << 16, name, 128);
}
if (result == 0)
{
return "KEY_" + std::to_string(key);
}
return name;
}
void Hotkeys::pressKeys(const std::vector<int> &keys)
{
keysToPress = keys;
shouldPressKeys = true;
}
void Hotkeys::releaseKeys([[maybe_unused]] const std::vector<int> &keys)
{
shouldPressKeys = false;
keysToPress.clear();
for (const auto &key : keys)
{
keybd_event(key, 0, 2, 0);
}
}
} // namespace Soundux::Objects
#endif

View File

@ -1,43 +0,0 @@
#pragma once
#if defined(_WIN32)
#include <core/hotkeys/hotkeys.hpp>
#include <mutex>
#include <thread>
#include <windows.h>
namespace Soundux
{
namespace Objects
{
class WindowsHotkeys : public Hotkeys
{
std::thread listener;
std::thread keyPresser;
std::atomic<bool> kill = false;
std::condition_variable cv;
std::mutex keysToPressMutex;
std::vector<Key> keysToPress;
private:
void listen();
void presser();
void setup() override;
private:
static HHOOK oMouseProc;
static HHOOK oKeyboardProc;
static LRESULT CALLBACK mouseProc(int, WPARAM, LPARAM);
static LRESULT CALLBACK keyBoardProc(int, WPARAM, LPARAM);
public:
~WindowsHotkeys();
std::string getKeyName(const Key &key) override;
void pressKeys(const std::vector<Key> &keys) override;
void releaseKeys(const std::vector<Key> &keys) override;
};
} // namespace Objects
} // namespace Soundux
#endif

View File

@ -8,7 +8,7 @@ namespace Soundux
{
namespace Objects
{
struct Key;
struct AudioDevice;
struct Sound
{
@ -17,7 +17,7 @@ namespace Soundux
std::string path;
bool isFavorite = false;
std::vector<Key> hotkeys;
std::vector<int> hotkeys;
std::uint64_t modifiedDate;
std::optional<int> localVolume;

View File

@ -1,7 +1,5 @@
#pragma once
#include <core/enums/enums.hpp>
#include <core/hotkeys/keys.hpp>
#include <optional>
#include <string>
#include <vector>
@ -16,18 +14,15 @@ namespace Soundux
Enums::Theme theme = Enums::Theme::System;
std::optional<std::string> language;
std::vector<Key> pushToTalkKeys;
std::vector<Key> stopHotkey;
std::optional<Key> remoteVolumeKnob;
std::optional<Key> localVolumeKnob;
std::vector<int> pushToTalkKeys;
std::vector<int> stopHotkey;
std::vector<std::string> outputs;
std::uint32_t selectedTab = 0;
bool syncVolumes = false;
int remoteVolume = 100;
int localVolume = 50;
bool syncVolumes = false;
bool allowMultipleOutputs = false;
bool useAsDefaultDevice = false;

View File

@ -1,69 +1,11 @@
#pragma once
#include <core/global/globals.hpp>
#include <core/hotkeys/keys.hpp>
#include <helper/audio/windows/winsound.hpp>
#include <helper/version/check.hpp>
#include <nlohmann/json.hpp>
namespace Soundux
{
namespace traits
{
template <typename T> struct is_optional
{
private:
static std::uint8_t test(...);
template <typename O> static auto test(std::optional<O> *) -> std::uint16_t;
public:
static const bool value = sizeof(test(reinterpret_cast<std::decay_t<T> *>(0))) == sizeof(std::uint16_t);
};
} // namespace traits
} // namespace Soundux
namespace nlohmann
{
template <typename T> struct adl_serializer<std::optional<T>>
{
static void to_json(json &j, const std::optional<T> &obj)
{
if (obj)
{
j = *obj;
}
else
{
j = nullptr;
}
}
static void from_json(const json &j, const std::optional<T> &obj)
{
if (!j.is_null())
{
obj = j.get<T>();
}
}
}; // namespace nlohmann
template <> struct adl_serializer<Soundux::Objects::Key>
{
static void to_json(json &j, const Soundux::Objects::Key &obj)
{
j = {{"key", obj.key}, {"type", obj.type}};
}
static void from_json(const json &j, Soundux::Objects::Key &obj)
{
if (j.find("type") != j.end())
{
j.at("key").get_to(obj.key);
j.at("type").get_to(obj.type);
}
else
{
j.get_to(obj.key);
obj.type = Soundux::Enums::KeyType::Keyboard;
}
}
}; // namespace nlohmann
template <> struct adl_serializer<Soundux::Objects::Sound>
{
static void to_json(json &j, const Soundux::Objects::Sound &obj)
@ -72,14 +14,29 @@ namespace nlohmann
{"name", obj.name},
{"hotkeys", obj.hotkeys},
{"hotkeySequence",
Soundux::Globals::gHotKeys->getKeySequence(obj.hotkeys)}, //* For frontend and config readability
Soundux::Globals::gHotKeys.getKeySequence(obj.hotkeys)}, //* For frontend and config readability
{"id", obj.id},
{"path", obj.path},
{"isFavorite", obj.isFavorite},
{"localVolume", obj.localVolume},
{"remoteVolume", obj.remoteVolume},
{"modifiedDate", obj.modifiedDate},
};
if (obj.localVolume)
{
j["localVolume"] = *obj.localVolume;
}
else
{
j["localVolume"] = nullptr;
}
if (obj.remoteVolume)
{
j["remoteVolume"] = *obj.remoteVolume;
}
else
{
j["remoteVolume"] = nullptr;
}
}
static void from_json(const json &j, Soundux::Objects::Sound &obj)
{
@ -163,8 +120,6 @@ namespace nlohmann
{"pushToTalkKeys", obj.pushToTalkKeys},
{"tabHotkeysOnly", obj.tabHotkeysOnly},
{"minimizeToTray", obj.minimizeToTray},
{"localVolumeKnob", obj.localVolumeKnob},
{"remoteVolumeKnob", obj.remoteVolumeKnob},
{"allowOverlapping", obj.allowOverlapping},
{"muteDuringPlayback", obj.muteDuringPlayback},
{"useAsDefaultDevice", obj.useAsDefaultDevice},
@ -176,22 +131,9 @@ namespace nlohmann
{
if (j.find(key) != j.end())
{
if constexpr (Soundux::traits::is_optional<T>::value)
if (j.at(key).type_name() == nlohmann::basic_json(T{}).type_name())
{
if (j.at(key).type_name() == nlohmann::basic_json(typename T::value_type{}).type_name())
{
if (!j.at(key).is_null())
{
member = j.at(key).get<typename T::value_type>();
}
}
}
else
{
if (j.at(key).type_name() == nlohmann::basic_json(T{}).type_name())
{
j.at(key).get_to(member);
}
j.at(key).get_to(member);
}
}
}
@ -212,8 +154,6 @@ namespace nlohmann
get_to_safe(j, "pushToTalkKeys", obj.pushToTalkKeys);
get_to_safe(j, "minimizeToTray", obj.minimizeToTray);
get_to_safe(j, "tabHotkeysOnly", obj.tabHotkeysOnly);
get_to_safe(j, "localVolumeKnob", obj.localVolumeKnob);
get_to_safe(j, "remoteVolumeKnob", obj.remoteVolumeKnob);
get_to_safe(j, "allowOverlapping", obj.allowOverlapping);
get_to_safe(j, "useAsDefaultDevice", obj.useAsDefaultDevice);
get_to_safe(j, "muteDuringPlayback", obj.muteDuringPlayback);
@ -337,5 +277,22 @@ namespace nlohmann
};
}
};
template <> struct adl_serializer<std::optional<Soundux::Objects::RecordingDevice>>
{
static void to_json(json &j, const std::optional<Soundux::Objects::RecordingDevice> &obj)
{
if (obj)
{
j = {
{"name", obj->getName()},
{"guid", obj->getGUID()},
};
}
else
{
j = "null";
}
}
};
#endif
} // namespace nlohmann

View File

@ -5,12 +5,7 @@
#include <filesystem>
#include <fstream>
#include <optional>
#pragma push_macro("UNICOCDE")
#undef UNICODE
#include <process.hpp>
#pragma pop_macro("UNICOCDE")
#include <regex>
#include <system_error>

View File

@ -10,33 +10,29 @@ namespace Soundux::Objects
cv.wait(lock, [&]() { return !queue.empty() || stop; });
while (!queue.empty())
{
auto front = queue.begin();
front->function();
queue.erase(front);
auto front = std::move(*queue.begin());
lock.unlock();
front.second();
lock.lock();
queue.erase(front.first);
}
}
}
void Queue::push_unique(std::uint64_t id, std::function<void()> function)
{
{
std::lock_guard lock(queueMutex);
if (std::find_if(queue.begin(), queue.end(),
[&id](const auto &entry) { return entry.id && *entry.id == id; }) != queue.end())
if (queue.find(id) != queue.end())
{
return;
}
}
std::unique_lock lock(queueMutex);
queue.emplace_back(Call{std::move(function), id});
lock.unlock();
cv.notify_one();
}
void Queue::push(std::function<void()> function)
{
std::unique_lock lock(queueMutex);
queue.emplace_back(Call{std::move(function), std::nullopt});
queue.emplace(id, std::move(function));
lock.unlock();
cv.notify_one();

View File

@ -3,7 +3,6 @@
#include <condition_variable>
#include <functional>
#include <map>
#include <optional>
#include <queue>
#include <thread>
@ -13,14 +12,8 @@ namespace Soundux
{
class Queue
{
struct Call
{
std::function<void()> function;
std::optional<std::uint64_t> id;
};
std::map<std::uint64_t, std::function<void()>> queue;
std::mutex queueMutex;
std::vector<Call> queue;
std::condition_variable cv;
std::atomic<bool> stop;
@ -33,7 +26,6 @@ namespace Soundux
Queue();
~Queue();
void push(std::function<void()>);
void push_unique(std::uint64_t, std::function<void()>);
};
} // namespace Objects

View File

@ -1,12 +1,7 @@
#pragma once
#include <json.hpp>
#include <optional>
#pragma push_macro("UNICOCDE")
#undef UNICODE
#include <process.hpp>
#pragma pop_macro("UNICOCDE")
#include <regex>
#include <string>

View File

@ -9,7 +9,6 @@
#include <helper/systeminfo/systeminfo.hpp>
#include <helper/version/check.hpp>
#include <helper/ytdl/youtube-dl.hpp>
#include <javascript/function.hpp>
#ifdef _WIN32
#include "../../assets/icon.h"
@ -123,14 +122,12 @@ namespace Soundux::Objects
webview->expose(Webview::Function("stopSounds", [this]() { stopSounds(); }));
webview->expose(Webview::Function("changeSettings",
[this](const Settings &newSettings) { return changeSettings(newSettings); }));
webview->expose(Webview::Function("requestHotkey", [](bool state) { Globals::gHotKeys->notify(state); }));
webview->expose(Webview::Function("requestHotkey", [](bool state) { Globals::gHotKeys.shouldNotify(state); }));
webview->expose(Webview::Function(
"setHotkey", [this](std::uint32_t id, const std::vector<Key> &keys) { return setHotkey(id, keys); }));
webview->expose(Webview::Function("getHotkeySequence", [this](const std::vector<Key> &keys) {
return Globals::gHotKeys->getKeySequence(keys);
"setHotkey", [this](std::uint32_t id, const std::vector<int> &keys) { return setHotkey(id, keys); }));
webview->expose(Webview::Function("getHotkeySequence", [this](const std::vector<int> &keys) {
return Globals::gHotKeys.getKeySequence(keys);
}));
webview->expose(
Webview::Function("getKeyName", [this](const Key &key) { return Globals::gHotKeys->getKeyName(key); }));
webview->expose(Webview::Function("removeTab", [this](std::uint32_t id) { return removeTab(id); }));
webview->expose(Webview::Function("refreshTab", [this](std::uint32_t id) { return refreshTab(id); }));
webview->expose(Webview::Function(
@ -172,8 +169,6 @@ namespace Soundux::Objects
return setCustomRemoteVolume(id, volume);
}));
webview->expose(Webview::Function("toggleSoundPlayback", [this]() { return toggleSoundPlayback(); }));
webview->expose(
Webview::Function("requestKnob", [this](bool state) { Globals::gHotKeys->requestKnob(state); }));
#if !defined(__linux__)
webview->expose(Webview::Function("getOutputs", [this]() { return getOutputs(); }));
@ -392,10 +387,15 @@ namespace Soundux::Objects
}
Fancy::fancy.logTime().message() << "UI exited" << std::endl;
}
void WebView::onHotKeyReceived(const std::vector<Key> &keys)
void WebView::onHotKeyReceived(const std::vector<int> &keys)
{
webview->callFunction<void>(
Webview::JavaScriptFunction("window.hotkeyReceived", Globals::gHotKeys->getKeySequence(keys), keys));
std::string hotkeySequence;
for (const auto &key : keys)
{
hotkeySequence += Globals::gHotKeys.getKeyName(key) + " + ";
}
webview->callFunction<void>(Webview::JavaScriptFunction(
"window.hotkeyReceived", hotkeySequence.substr(0, hotkeySequence.length() - 3), keys));
}
void WebView::onSoundFinished(const PlayingSound &sound)
{
@ -448,12 +448,4 @@ namespace Soundux::Objects
webview->callFunction<void>(
Webview::JavaScriptFunction("window.getStore().commit", "setAdministrativeModal", true));
}
void WebView::onLocalVolumeChanged(int volume)
{
webview->callFunction<void>(Webview::JavaScriptFunction("window.getStore().commit", "setLocalVolume", volume));
}
void WebView::onRemoteVolumeChanged(int volume)
{
webview->callFunction<void>(Webview::JavaScriptFunction("window.getStore().commit", "setRemoteVolume", volume));
}
} // namespace Soundux::Objects

View File

@ -28,12 +28,10 @@ namespace Soundux
void setup() override;
void mainLoop() override;
void onSoundFinished(const PlayingSound &sound) override;
void onHotKeyReceived(const std::vector<Key> &keys) override;
void onHotKeyReceived(const std::vector<int> &keys) override;
void onAdminRequired() override;
void onSettingsChanged() override;
void onLocalVolumeChanged(int volume) override;
void onRemoteVolumeChanged(int volume) override;
void onSwitchOnConnectDetected(bool state) override;
void onError(const Enums::ErrorCode &error) override;
void onSoundPlayed(const PlayingSound &sound) override;

View File

@ -15,8 +15,7 @@ namespace Soundux::Objects
void Window::setup()
{
NFD::Init();
Globals::gHotKeys = Hotkeys::createInstance();
Globals::gHotKeys.init();
for (auto &tab : Globals::gData.getTabs())
{
tab.sounds = getTabContent(tab);
@ -26,6 +25,7 @@ namespace Soundux::Objects
Window::~Window()
{
NFD::Quit();
Globals::gHotKeys.stop();
}
std::vector<Sound> Window::getTabContent(const Tab &tab) const
{
@ -225,7 +225,7 @@ namespace Soundux::Objects
}
if (!Globals::gSettings.pushToTalkKeys.empty())
{
Globals::gHotKeys->pressKeys(Globals::gSettings.pushToTalkKeys);
Globals::gHotKeys.pressKeys(Globals::gSettings.pushToTalkKeys);
}
auto playingSound = Globals::gAudio.play(*sound);
@ -297,7 +297,7 @@ namespace Soundux::Objects
}
if (!Globals::gSettings.pushToTalkKeys.empty())
{
Globals::gHotKeys->pressKeys(Globals::gSettings.pushToTalkKeys);
Globals::gHotKeys.pressKeys(Globals::gSettings.pushToTalkKeys);
}
if (Globals::gSettings.outputs.empty() && !Globals::gSettings.useAsDefaultDevice)
@ -579,33 +579,30 @@ namespace Soundux::Objects
onError(Enums::ErrorCode::FailedToSetCustomVolume);
return std::nullopt;
}
void Window::onVolumeChanged()
{
for (const auto &playingSound : Globals::gAudio.getPlayingSounds())
{
int newVolume = 0;
const auto &sound = playingSound.sound;
if (playingSound.playbackDevice.isDefault)
{
newVolume = sound.localVolume ? *sound.localVolume : Globals::gSettings.localVolume;
}
else
{
newVolume = sound.remoteVolume ? *sound.remoteVolume : Globals::gSettings.remoteVolume;
}
playingSound.raw.device.load()->masterVolumeFactor = static_cast<float>(newVolume) / 100.f;
}
}
Settings Window::changeSettings(Settings settings)
{
auto oldSettings = Globals::gSettings;
Globals::gSettings = settings;
if ((settings.localVolume != oldSettings.localVolume || settings.remoteVolume != oldSettings.remoteVolume))
if ((settings.localVolume != oldSettings.localVolume || settings.remoteVolume != oldSettings.remoteVolume) &&
!Globals::gAudio.getPlayingSounds().empty())
{
onVolumeChanged();
for (const auto &playingSound : Globals::gAudio.getPlayingSounds())
{
int newVolume = 0;
const auto &sound = playingSound.sound;
if (playingSound.playbackDevice.isDefault)
{
newVolume = sound.localVolume ? *sound.localVolume : Globals::gSettings.localVolume;
}
else
{
newVolume = sound.remoteVolume ? *sound.remoteVolume : Globals::gSettings.remoteVolume;
}
playingSound.raw.device.load()->masterVolumeFactor = static_cast<float>(newVolume) / 100.f;
}
}
#if defined(__linux__)
@ -717,9 +714,9 @@ namespace Soundux::Objects
#endif
return Globals::gSettings;
}
void Window::onHotKeyReceived([[maybe_unused]] const std::vector<Key> &keys)
void Window::onHotKeyReceived([[maybe_unused]] const std::vector<int> &keys)
{
Globals::gHotKeys->notify(false);
Globals::gHotKeys.shouldNotify(false);
}
std::optional<Tab> Window::refreshTab(const std::uint32_t &id)
{
@ -756,7 +753,7 @@ namespace Soundux::Objects
onError(Enums::ErrorCode::TabDoesNotExist);
return std::nullopt;
}
std::optional<Sound> Window::setHotkey(const std::uint32_t &id, const std::vector<Key> &hotkeys)
std::optional<Sound> Window::setHotkey(const std::uint32_t &id, const std::vector<int> &hotkeys)
{
auto sound = Globals::gData.getSound(id);
if (sound)
@ -954,7 +951,7 @@ namespace Soundux::Objects
{
if (!Globals::gSettings.pushToTalkKeys.empty())
{
Globals::gHotKeys->releaseKeys(Globals::gSettings.pushToTalkKeys);
Globals::gHotKeys.releaseKeys(Globals::gSettings.pushToTalkKeys);
}
#if defined(__linux__)
@ -992,7 +989,7 @@ namespace Soundux::Objects
{
if (!Globals::gSettings.pushToTalkKeys.empty())
{
Globals::gHotKeys->pressKeys(Globals::gSettings.pushToTalkKeys);
Globals::gHotKeys.pressKeys(Globals::gSettings.pushToTalkKeys);
}
}
void Window::setIsOnFavorites(bool state)

View File

@ -77,7 +77,6 @@ namespace Soundux
virtual std::optional<Tab> setSortMode(const std::uint32_t &, Enums::SortMode);
protected:
virtual void onVolumeChanged();
virtual bool toggleSoundPlayback();
virtual void stopSounds(bool = false);
virtual bool stopSound(const std::uint32_t &);
@ -89,7 +88,7 @@ namespace Soundux
virtual std::optional<PlayingSound> repeatSound(const std::uint32_t &, bool);
virtual std::optional<PlayingSound> seekSound(const std::uint32_t &, std::uint64_t);
virtual std::optional<Sound> setHotkey(const std::uint32_t &, const std::vector<Key> &);
virtual std::optional<Sound> setHotkey(const std::uint32_t &, const std::vector<int> &);
virtual std::optional<Sound> setCustomLocalVolume(const std::uint32_t &, const std::optional<int> &);
virtual std::optional<Sound> setCustomRemoteVolume(const std::uint32_t &, const std::optional<int> &);
@ -101,13 +100,11 @@ namespace Soundux
virtual void onAdminRequired() = 0;
virtual void onSettingsChanged() = 0;
virtual void onLocalVolumeChanged(int) = 0;
virtual void onRemoteVolumeChanged(int) = 0;
virtual void onSwitchOnConnectDetected(bool) = 0;
virtual void onSoundPlayed(const PlayingSound &);
virtual void onError(const Enums::ErrorCode &) = 0;
virtual void onSoundFinished(const PlayingSound &);
virtual void onHotKeyReceived(const std::vector<Key> &);
virtual void onHotKeyReceived(const std::vector<int> &);
virtual void onSoundProgressed(const PlayingSound &) = 0;
virtual void onDownloadProgressed(float, const std::string &) = 0;
};