Skip to content

Commit

Permalink
Add incomplete SRAL_Register/UnregisterKeyboardHooks to controle inte…
Browse files Browse the repository at this point in the history
…rrupt and pause speech by Ctrl and Shift.
  • Loading branch information
m1maker committed Aug 31, 2024
1 parent 01d893b commit 5f6d5ea
Show file tree
Hide file tree
Showing 13 changed files with 208 additions and 10 deletions.
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ elseif (APPLE)
set(CMAKE_CXX_COMPILER clang++)
target_link_libraries(${PROJECT_NAME} "-framework Foundation" "-framework AVFoundation")
target_link_libraries(${PROJECT_NAME}_test "-framework Foundation" "-framework AVFoundation")
else()
find_package(X11 REQUIRED)
target_link_libraries(${PROJECT_NAME} ${X11_LIBRARIES})
target_link_libraries(${PROJECT_NAME}_test ${X11_LIBRARIES})

endif()


Expand Down
2 changes: 1 addition & 1 deletion Examples/C/SRALExample.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ int main(void) {
printf("Failed to initialize SRAL library.\n");
return 1;
}

SRAL_RegisterKeyboardHooks();
// Speak some text
if (SRAL_GetEngineFeatures(0) & SUPPORTS_SPEECH) {
printf("Enter the text you want to be spoken:\n");
Expand Down
11 changes: 10 additions & 1 deletion Include/SRAL.h
Original file line number Diff line number Diff line change
Expand Up @@ -358,9 +358,18 @@ extern "C" {



SRAL_API bool SRAL_RegisterKeyboardHooks(void);




SRAL_API void SRAL_UnregisterKeyboardHooks(void);




#ifdef __cplusplus
}// extern "C"
#endif


#endif // SRAL_H_
4 changes: 4 additions & 0 deletions SRC/AVSpeech.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ class AVSpeech : public Engine {
uint64_t GetVoiceCount()override;
const char* GetVoiceName(uint64_t index)override;
bool SetVoice(uint64_t index)override;
int GetKeyFlags()override {
return HANDLE_NONE;
}

private:
AVSpeechSynthesizerWrapper* obj;
};
8 changes: 8 additions & 0 deletions SRC/Engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
#define SCREENREADER_H_
#pragma once
#include <stdint.h>
enum KeyboardFlags {
HANDLE_NONE = 0,
HANDLE_INTERRUPT = 2,
HANDLE_PAUSE_RESUME = 4
};

class Engine {
public:
virtual bool Speak(const char* text, bool interrupt) = 0;
Expand All @@ -22,5 +28,7 @@ class Engine {
virtual uint64_t GetVoiceCount() = 0;
virtual const char* GetVoiceName(uint64_t index) = 0;
virtual bool SetVoice(uint64_t index) = 0;
virtual int GetKeyFlags() = 0;
bool paused;
};
#endif
5 changes: 3 additions & 2 deletions SRC/Jaws.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#ifndef JAWS_H_
#define JAWS_H_
#ifdef _WIN32
#pragma once
#include "../Dep/fsapi.h"
#include "../Include/SRAL.h"
Expand Down Expand Up @@ -35,9 +34,11 @@ class Jaws : public Engine {
bool SetVoice(uint64_t index)override {
return false;
}
int GetKeyFlags()override {
return HANDLE_NONE;
}

private:
IJawsApi* JawsAPI = nullptr;
};
#endif
#endif
4 changes: 4 additions & 0 deletions SRC/NVDA.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ class NVDA : public Engine {
return false;
}

int GetKeyFlags()override {
return HANDLE_NONE;
}

private:
HINSTANCE lib = nullptr;
typedef error_status_t(__stdcall* NVDAController_speakText)(const wchar_t*);
Expand Down
8 changes: 8 additions & 0 deletions SRC/SAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,11 @@ bool SAPI::Speak(const char* text, bool interrupt) {
PCMData dat = { 0, 0 };
dat.data = (unsigned char*)final;
dat.size = bytes;
if (this->paused) {
this->paused = false;
if (!interrupt)
player->resume();
}
g_dataQueue.push_back(dat);
return true;
}
Expand All @@ -148,12 +153,15 @@ bool SAPI::StopSpeech() {
if (player == nullptr)return false;
g_dataQueue.clear();
player->stop();
this->paused = false;
return true;
}
bool SAPI::PauseSpeech() {
paused = true;
return SUCCEEDED(player->pause());
}
bool SAPI::ResumeSpeech() {
paused = false;
return SUCCEEDED(player->resume());
}
void SAPI::SetVolume(uint64_t value) {
Expand Down
3 changes: 3 additions & 0 deletions SRC/SAPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ class SAPI : public Engine {
uint64_t GetVoiceCount()override;
const char* GetVoiceName(uint64_t index)override;
bool SetVoice(uint64_t index)override;
int GetKeyFlags()override {
return HANDLE_INTERRUPT | HANDLE_PAUSE_RESUME;
}

private:
blastspeak* instance = nullptr;
Expand Down
135 changes: 133 additions & 2 deletions SRC/SRAL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#include "AVSpeech.h"
#else
#include "SpeechDispatcher.h"
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#endif
#include <vector>
#include <string>
Expand Down Expand Up @@ -53,7 +55,6 @@ struct QueuedOutput {
int time;
Engine* engine;
};

std::vector<QueuedOutput> g_delayedOutputs;
bool g_delayOperation = false;
bool g_outputThreadRunning = false;
Expand Down Expand Up @@ -85,6 +86,136 @@ static void output_thread() {
g_outputThreadRunning = false;
}



bool g_keyboardHookThread = false;
#if defined(_WIN32)
static HHOOK g_keyboardHook;
bool g_shiftPressed = false;
static LRESULT CALLBACK KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode >= 0) {
KBDLLHOOKSTRUCT* pKeyInfo = (KBDLLHOOKSTRUCT*)lParam;
for (uint64_t i = 0; i < g_engines.size(); ++i) {
if (g_engines[i] == nullptr || !g_engines[i]->GetActive()) continue;

if (wParam == WM_KEYDOWN) {
if ((pKeyInfo->vkCode == VK_LCONTROL || pKeyInfo->vkCode == VK_RCONTROL) && g_engines[i]->GetKeyFlags() & HANDLE_INTERRUPT) {
g_engines[i]->StopSpeech();
}
else if ((pKeyInfo->vkCode == VK_LSHIFT || pKeyInfo->vkCode == VK_RSHIFT) && g_engines[i]->GetKeyFlags() & HANDLE_PAUSE_RESUME && g_shiftPressed == false) {
if (g_engines[i]->paused)
g_engines[i]->ResumeSpeech();
else
g_engines[i]->PauseSpeech();
g_shiftPressed = true;
}
}
else if (wParam == WM_KEYUP) {
if (pKeyInfo->vkCode == VK_LSHIFT || pKeyInfo->vkCode == VK_RSHIFT) {
g_shiftPressed = false;
}
}
}
}
return CallNextHookEx(g_keyboardHook, nCode, wParam, lParam);
}
static void hook_thread() {
g_keyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProc, GetModuleHandle(NULL), 0);
if (g_keyboardHook == nullptr)return;
MSG msg;

while (g_keyboardHookThread) {
if (GetMessageW(&msg, nullptr, 0, 0)) {
DispatchMessageW(&msg);
TranslateMessage(&msg);
}
}
UnhookWindowsHookEx(g_keyboardHook);
}
extern "C" SRAL_API bool SRAL_RegisterKeyboardHooks(void) {
if (g_keyboardHookThread)return g_keyboardHookThread;
g_keyboardHookThread = true;
std::thread t(hook_thread);
t.detach();
Timer timer;
while (timer.elapsed() < 3000) {
Sleep(5);
if (g_keyboardHook != nullptr) {
return true;
}
}
return false; // Timeout: Hook is not set
}

extern "C" SRAL_API void SRAL_UnregisterKeyboardHooks(void) {
PostMessage(0, WM_KEYUP, 0, 0);
g_keyboardHookThread = false;
}
#elif defined(__APPLE__)
extern "C" SRAL_API bool SRAL_RegisterKeyboardHooks(void) {
return false;
}
extern "C" SRAL_API void SRAL_UnregisterKeyboardHooks(void) {
return;
}
#else
Display* g_display = nullptr;
static void hook_thread() {

g_display = XOpenDisplay(nullptr);

if (g_display == nullptr)return;
int num_screens = ScreenCount(g_display);
for (int screen = 0; screen < num_screens; ++screen) {
Window root = RootWindow(g_display, screen);
XSelectInput(g_display, root, KeyPressMask | KeyReleaseMask);
XGrabKeyboard(g_display, root, False, GrabModeAsync, GrabModeAsync, CurrentTime);
}
while (g_keyboardHookThread) {
XEvent event;

XNextEvent(g_display, &event);
for (uint64_t i = false; i < g_engines.size(); i += true) {
if (g_engines[i] == nullptr || !g_engines[i]->GetActive()) continue;

if (event.type == KeyPress) {
if ((event.xkey.keycode == XK_Control_L || event.xkey.keycode == XK_Control_R) && g_engines[i]->GetKeyFlags() & HANDLE_INTERRUPT) {
g_engines[i]->StopSpeech();
}
else if ((event.xkey.keycode == XK_Shift_L || event.xkey.keycode == XK_Shift_R) && g_engines[i]->GetKeyFlags() & HANDLE_PAUSE_RESUME) {
if (g_engines[i]->paused)
g_engines[i]->ResumeSpeech();
else
g_engines[i]->PauseSpeech();
}
}
}
}
XCloseDisplay(g_display);
}
extern "C" SRAL_API bool SRAL_RegisterKeyboardHooks(void) {
if (g_keyboardHookThread)return g_keyboardHookThread;
g_keyboardHookThread = true;
std::thread t(hook_thread);
t.detach();
Timer timer;
while (timer.elapsed() < 3000) {
usleep(5000);
if (g_display != nullptr) {
return true;
}
}
return false; // Timeout: Hook is not set
}

extern "C" SRAL_API void SRAL_UnregisterKeyboardHooks(void) {
g_keyboardHookThread = false;
}

#endif



extern "C" SRAL_API bool SRAL_Initialize(int engines_exclude) {
if (g_initialized)return true;
#if defined(_WIN32)
Expand Down Expand Up @@ -170,7 +301,7 @@ static void speech_engine_update() {
break;
}
}
}
}
#ifdef _WIN32
}
#endif
Expand Down
21 changes: 20 additions & 1 deletion SRC/SpeechDispatcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
#define spd_get_voice_rate (*spd_get_voice_rate)
#define spd_set_volume (*spd_set_volume)
#define spd_get_volume (*spd_get_volume)
#define spd_pause_all (*spd_pause_all)
#define spd_resume_all (*spd_resume_all)

#include "SpeechDispatcher.h"
#undef spd_get_default_address
#undef spd_open2
Expand All @@ -19,6 +22,8 @@
#undef spd_get_voice_rate
#undef spd_set_volume
#undef spd_get_volume
#undef spd_pause_all
#undef spd_resume_all


#include <dlfcn.h> // For dlopen, dlsym, dlclose
Expand All @@ -38,7 +43,8 @@ bool SpeechDispatcher::Initialize() {
*(void**)&spd_get_voice_rate = dlsym(Lib, "spd_get_voice_rate");
*(void**)&spd_set_volume = dlsym(Lib, "spd_set_volume");
*(void**)&spd_get_volume = dlsym(Lib, "spd_get_volume");

*(void**)&spd_pause_all = dlsym(Lib, "spd_pause_all");
*(void**)&spd_resume_all = dlsym(Lib, "spd_resume_all");
const auto* address = spd_get_default_address(nullptr);
if (address == nullptr) {
return false;
Expand Down Expand Up @@ -67,6 +73,7 @@ bool SpeechDispatcher::Speak(const char* text, bool interrupt) {
spd_stop(Speech);
spd_cancel(Speech);
}
this->paused = false;
return spd_say(Speech, interrupt ? SPD_IMPORTANT : SPD_TEXT, text);
}
bool SpeechDispatcher::StopSpeech() {
Expand All @@ -75,6 +82,18 @@ bool SpeechDispatcher::StopSpeech() {
spd_cancel(Speech);
return true;
}
bool SpeechDispatcher::PauseSpeech() {
if (!GetActive())return false;
this->paused = true;
return spd_pause_all(Speech) == 0;
}
bool SpeechDispatcher::ResumeSpeech() {
if (!GetActive())return false;
this->paused = false;
return spd_resume_all(Speech) == 0;
}


void SpeechDispatcher::SetVolume(uint64_t value) {
if (!GetActive())return;
spd_set_volume(Speech, value);
Expand Down
9 changes: 6 additions & 3 deletions SRC/SpeechDispatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ class SpeechDispatcher : public Engine {
bool Speak(const char* text, bool interrupt)override;
bool Braille(const char* text)override { return false; }
bool StopSpeech()override;
bool PauseSpeech()override { return false; }
bool ResumeSpeech()override { return false; }
bool PauseSpeech()override;
bool ResumeSpeech()override;

int GetNumber()override {
return ENGINE_SPEECH_DISPATCHER;
Expand All @@ -20,7 +20,7 @@ class SpeechDispatcher : public Engine {
bool Initialize()override;
bool Uninitialize()override;
int GetFeatures()override {
return SUPPORTS_SPEECH | SUPPORTS_SPEECH_RATE | SUPPORTS_SPEECH_VOLUME;
return SUPPORTS_SPEECH | SUPPORTS_SPEECH_RATE | SUPPORTS_SPEECH_VOLUME | SUPPORTS_PAUSE_SPEECH;
}
void SetVolume(uint64_t)override;
uint64_t GetVolume()override;
Expand All @@ -35,6 +35,9 @@ class SpeechDispatcher : public Engine {
bool SetVoice(uint64_t index)override {
return false;
}
int GetKeyFlags()override {
return HANDLE_INTERRUPT | HANDLE_PAUSE_RESUME;
}

private:
SPDConnection* Speech = nullptr;
Expand Down
Loading

0 comments on commit 5f6d5ea

Please sign in to comment.