From 7d25c106d7aa1728ef4babb11872147a481f7725 Mon Sep 17 00:00:00 2001 From: dawidpieper Date: Thu, 24 Jan 2019 15:55:24 +0100 Subject: [PATCH 1/3] Tolk Module for Ruby Language --- README.md | 2 + build_ruby.bat | 7 ++++ clean.bat | 1 + src/ruby/extconf.rb | 24 ++++++++++++ src/ruby/tolk.cpp | 95 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 129 insertions(+) create mode 100644 build_ruby.bat create mode 100644 src/ruby/extconf.rb create mode 100644 src/ruby/tolk.cpp diff --git a/README.md b/README.md index c0780e0..448fdea 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ There are APIs for the following languages: * Python * AutoIt * PureBasic +* Ruby ## License @@ -118,3 +119,4 @@ The root directory and `examples` directories contain various batch files as a s * Leonard de Ruijter * Axel Vugts * QuentinC, who has developed [Universal Speech](https://github.com/QuentinC-Github/UniversalSpeech), another great screen reader library +* Dawid Pieper diff --git a/build_ruby.bat b/build_ruby.bat new file mode 100644 index 0000000..e7a5ef0 --- /dev/null +++ b/build_ruby.bat @@ -0,0 +1,7 @@ +@echo off + +cd src\ruby +ruby extconf.rb +make +cd ..\.. +move src\ruby\tolk.so bin\tolk.so diff --git a/clean.bat b/clean.bat index df40e21..fcb0ebc 100644 --- a/clean.bat +++ b/clean.bat @@ -4,3 +4,4 @@ del bin\TolkDotNet.res del bin\x64\*.obj bin\x64\Tolk.res del bin\x86\*.obj bin\x86\Tolk.res del src\java\com\davykager\tolk\Tolk.class +del src\ruby\makefile \ No newline at end of file diff --git a/src/ruby/extconf.rb b/src/ruby/extconf.rb new file mode 100644 index 0000000..25c8140 --- /dev/null +++ b/src/ruby/extconf.rb @@ -0,0 +1,24 @@ +### + # Product: Tolk + # File: extconf.rb + # Description: Tolk Ruby Library makefile generator + # Copyright: (c) 2014-2018, Davy Kager + # Ruby Tolk module written by Dawid Pieper + # License: LGPLv3 + ## + +require "mkmf-rice" +abort "Ole32 is missing." unless find_library('Ole32','OleRun') +abort "OleAut32 is missing." unless find_library('OleAut32','SysAllocString') +abort "Imm32 is missing." unless find_library('Imm32','ImmGetContext') +abort "WinMM is missing." unless find_library('WinMM','timeGetTime') +# sapi is not the part of MinGW build-tools included with Ruby +# Therefore the code tries to locate it both in standard LIB DIRs and in the current location +unless find_library('SAPI','_CLSID_SpVoice') +if FileTest.exists?("sapi.lib") +$LIBS+=" sapi.lib" +else +abort "SAPI is missing." +end +end +create_makefile 'tolk' \ No newline at end of file diff --git a/src/ruby/tolk.cpp b/src/ruby/tolk.cpp new file mode 100644 index 0000000..482bfab --- /dev/null +++ b/src/ruby/tolk.cpp @@ -0,0 +1,95 @@ +/** + * Product: Tolk + * File: Tolk.cpp + * Description: Ruby C++ Native Extension + * Copyright: (c) 2014-2018, Davy Kager + * Ruby Tolk module written by Dawid Pieper + * License: LGPLv3 + */ + +// Ruby Tolk module builds using Tolk code so it is not needed to include 'tolk.dll' library with the project +#ifndef UNICODE +#define _UNICODE +#define UNICODE +#endif +#define _EXPORTING +#include +#include "../fsapi.c" +#include "../wineyes.c" +#include "../zt.c" +#include "../ScreenReaderDriver.h" +#include "../ScreenReaderDriverJAWS.cpp" +#include "../ScreenReaderDriverSA.cpp" +#include "../ScreenReaderDriverSAPI.cpp" +#include "../ScreenReaderDriverSNova.cpp" +#include "../ScreenReaderDriverWE.cpp" +#include "../ScreenReaderDriverZT.cpp" +#include "../java/com_davykager_tolk_Tolk.cpp" +#include +#include "../ScreenReaderDriverNVDA.cpp" +#include "../tolk.cpp" +#include "rice/Data_Type.hpp" +#include "rice/Constructor.hpp" + +using namespace Rice; + +// Ruby Rice gem uses std::string to represent Ruby Strings while Tolk uses wchar_t, so it is needed to write some wrapper functions converting Unicoded wchar_t to std::string + +BOOL rb_Tolk_Output(std::string str, BOOL interrupt) { +int sz = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0); +std::wstring widestr( sz, 0 ); +MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &widestr[0], sz); +return Tolk_Output(widestr.c_str(), interrupt); +} + +BOOL rb_Tolk_Speak(std::string str, BOOL interrupt) { +int sz = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0); +std::wstring widestr( sz, 0 ); +MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &widestr[0], sz); +return Tolk_Speak(widestr.c_str(), interrupt); +} + +BOOL rb_Tolk_Braille(std::string str) { +int sz = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0); +std::wstring widestr( sz, 0 ); +MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &widestr[0], sz); +return Tolk_Braille(widestr.c_str()); +} + +std::string rb_Tolk_DetectScreenReader() { +const wchar_t *t = Tolk_DetectScreenReader(); +// Ruby Rice library does not support returning of NULL values in std::string methods +if(t==NULL) { +std::string s(""); +return s; +} +std::wstring ws(t); +int sz = WideCharToMultiByte(CP_UTF8, 0, &ws[0], ws.size(), NULL, 0, NULL, NULL); +std::string str(sz, 0); +WideCharToMultiByte(CP_UTF8, 0, &ws[0], (int)ws.size(), &str[0], sz, NULL, NULL); +return str; +} + +void rb_tolk_define() { +Module mod = define_module("Tolk"); +mod.define_module_function("load", &Tolk_Load); +mod.define_module_function("isLoaded", &Tolk_IsLoaded); +mod.define_module_function("unload", &Tolk_Unload); +mod.define_module_function("trySAPI", &Tolk_TrySAPI); +mod.define_module_function("preferSAPI", &Tolk_PreferSAPI); +mod.define_module_function("detectScreenReader", &rb_Tolk_DetectScreenReader); +mod.define_module_function("hasSpeech", &Tolk_HasSpeech); +mod.define_module_function("hasBraille", &Tolk_HasBraille); +mod.define_module_function("output", &rb_Tolk_Output); +mod.define_module_function("speak", &rb_Tolk_Speak); +mod.define_module_function("braille", &rb_Tolk_Braille); +mod.define_module_function("isSpeaking", &Tolk_IsSpeaking); +mod.define_module_function("silence", &Tolk_Silence); + +} + +extern "C" { +void Init_tolk(void) { +rb_tolk_define(); +} +} \ No newline at end of file From 98fa605f3799369a0efcad131510933b52c525ee Mon Sep 17 00:00:00 2001 From: dawidpieper Date: Thu, 24 Jan 2019 19:07:33 +0100 Subject: [PATCH 2/3] Fixes and extension of extconf --- src/ruby/extconf.rb | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/src/ruby/extconf.rb b/src/ruby/extconf.rb index 25c8140..676dbd4 100644 --- a/src/ruby/extconf.rb +++ b/src/ruby/extconf.rb @@ -7,18 +7,43 @@ # License: LGPLv3 ## -require "mkmf-rice" +require "rubygems" +begin +puts("Generating Tolk makefile for Ruby Version #{RUBY_VERSION} and platform #{RUBY_PLATFORM}") +require 'mkmf-rice' +rescue LoadError +# if 'Rice' is not installed, try to install it +Gem.install("rice") +require 'mkmf-rice' +end abort "Ole32 is missing." unless find_library('Ole32','OleRun') abort "OleAut32 is missing." unless find_library('OleAut32','SysAllocString') abort "Imm32 is missing." unless find_library('Imm32','ImmGetContext') abort "WinMM is missing." unless find_library('WinMM','timeGetTime') # sapi is not the part of MinGW build-tools included with Ruby # Therefore the code tries to locate it both in standard LIB DIRs and in the current location -unless find_library('SAPI','_CLSID_SpVoice') -if FileTest.exists?("sapi.lib") -$LIBS+=" sapi.lib" -else -abort "SAPI is missing." +unless find_library('SAPI',nil) +# Try to locate the library in common Directories +suc=false +pf=nil +paths=[ +'C:\Program Files (X86)\Microsoft SDKs\Windows', +'C:\Program Files\Microsoft SDKs\Windows' +] +paths.each {|d| pf=d if pf==nil and FileTest.exists?(d) } +if pf!=nil +pf.delete(".") +pf.delete("..") +for d in Dir.entries(pf) +if ![".",".."].include?(d) +pth=pf+"\\"+d+"\\lib" +if FileTest.exists?(pth+"\\sapi.lib") +suc=find_library("SAPI",nil,"\"#{pth.gsub("\\","/")}\"") +break +end +end +end end +abort "SAPI is missing." if !suc end create_makefile 'tolk' \ No newline at end of file From ce5724245e7cfe16c97188853cd7900b3aa67c3d Mon Sep 17 00:00:00 2001 From: dawidpieper Date: Mon, 28 Jan 2019 08:21:09 +0100 Subject: [PATCH 3/3] Written a Ruby Tolk example based on Python example --- examples/ruby/ConsoleApp.rb | 49 +++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 examples/ruby/ConsoleApp.rb diff --git a/examples/ruby/ConsoleApp.rb b/examples/ruby/ConsoleApp.rb new file mode 100644 index 0000000..b3d23fa --- /dev/null +++ b/examples/ruby/ConsoleApp.rb @@ -0,0 +1,49 @@ +### + # Product: Tolk + # File: ConsoleApp.rb + # Description: Ruby console application example. + # Copyright: (c) 2014, Davy Kager + # License: LGPLv3 + # Depends: Tolk.so + ## + +## See README.md for general information. +## See Tolk.h for full documentation of all functions. +## Note that for Python the functions are provided in module Tolk, +## that the prefix Tolk_ has been removed from the names, +## and that the first letters of names are lowercase. + +require("tolk") + +puts("Tolk -- Ruby Console App Example") + +puts("Initializing Tolk...") +## Tolk will also initialize COM +## if it has not been initialized on the calling thread +Tolk.load + +puts("Querying for the active screen reader driver...") +name = Tolk.detectScreenReader +if name!="" + puts("The active screen reader driver is: " + name) +else + puts("None of the supported screen readers is running\n") +end + +if Tolk.hasSpeech + puts("This screen reader driver supports speech") +end +if Tolk.hasBraille + puts("This screen reader driver supports braille") +end + +puts("Let's output some text...") +if !Tolk.output("Hello, World!", 0) + puts("Failed to output text") +end + +puts("Finalizing Tolk...") +## Tolk will also try to uninitialize COM +Tolk.unload + +puts("Done!")