激光键盘软件源代码编译和修改指南

English Version: http://www.robopeak.net/blog/?p=332

I. 简介

RoboPeak 开源虚拟激光投影键盘设计的所有源代码已经发布在Github上。该源代码遵循LGPL许可。只要遵循LGPL许可的规范,您可以随意的使用或者修改本源代码。

本文旨在介绍如何在Windows以及MacOS平台上编译RoboPeak 开源虚拟激光投影键盘的信号处理软件。此外,也介绍了一些技术细节问题,帮助您理解代码并开发新的酷功能。

快速参考:

  • 如何在Windows上编译运行代码
  • 如何在MacOS上编译运行代码
  • 更多细节

II.  从源代码编译的理由

首先,从源代码编译允许您体验到我们为激光键盘开发的最新功能,这些功能并不会立刻在稳定版本中发布。您可以成为最早体验这些功能的人。

其次,如果您需要对我们的软件做出修改或者做改进。能编译源代码就是必经之路了。

此外,如果我们的信号处理软件无法在您的系统运行,那么在我们团队做出修正前,您可先尝试自己编译源代码来解决此问题。

III. 先要准备的

  • Windows

Windows XP 以及更高版本

Visual Studio 2010 (带有VC++包) 或者 Visual C++ 2010 express (更高的版本也可工作,但我们暂不作保证)

Git工具支持 (例如: msysgit)

  • MacOS

OS10.7 或更高版本 (我们使用OS10.8)

XCode 4.5 或者带有gcc支持的更高版本 (我们不使用 llvm-gcc进行编译)

GIT 工具(您可使用 macport or homebrew 来安装它)

除了上述的系统环境需求外,我们默认您具有基本的软件开发基础,例如:了解如何在Windows/MacOS上开发编译C++的程序,知道OpenCV是什么,GIT是什么。此外,您需要知道如何从github clone源代码

IV. 在Windows上编译运行源代码

STEP1: 从Github clone源代码

我们推荐您使用tortoise-git clone源代码到您本地的文件夹中,如下图例子那样;
tutorisegit_clone 或者直接基于如下命令行:

git clone https://github.com/robopeak/laserkbd.git

在Cl0ne完毕后,您应该看到如下的文件结构: cloned_source

STEP2: 在VS2010中编译

进入laser_kbd_neo 文件夹并打开VS项目文档:laser_kbd.sln:

vs2010

您只需要选择Release编译配置并开始编译即可。编译完毕后,您可以在 laser_kbd_neoRelease文件夹中找到编译好的可执行文件:laser_kbd.exe

vs_compiled

STEP3: 将必要的依赖文件复制并打包

上述编译产生的可执行文件需要其他依赖文件才能执行,并且这些依赖文件需要与可执行文件位于同一个目录。因此您需要将这些文件复制过来。我们推荐您创建一个新的目录,例如在代码根目录下建立一个output这样的目录,并将laser_kbd.exe以及下列文件夹、文件复制进来:

  • laser_kbd_neores 文件夹
  • sdkpackrefdll*.dll

最终的软件包应该看起来像这样: vs_released

大功告成!

现在您自己编译的信号处理软件已经可以工作了! win32_done

V. 在MacOS下编译源代码

STEP#1 Clone源代码

使用如下命令将Github的源代码clone至您本地的文件系统:

$ git clone https://github.com/robopeak/laserkbd.git

在clone完毕后您应该看到如下的文件结构

Shikais-MacBook-Pro:laserkbd csk$ ls
README.md laser_kbd_neo sdkpack

STEP#2 在XCode中编译运行

进入目录laser_kbd_neo/xcode, 打开项目文件LaserKeyborad.xcodeproj.

xcode_compile

您可以直接点击运行按钮就可以编译出可以执行的软件包了(Debug版本)

macos_compiled

VI. 实现细节浅析

1. 文件组织

本源代码文件按照如下的结构进行组织

<source root>
      +---- laser_kbd_neo/            <--- 所有的源代码、IDE用到的工程文档
      |          +---- ref/           <--- 第三方库的源代码
      |          +---- res/           <--- 软件所用到的图片、数据等文件
      |          +---- src/           <--- 信号处理软件自身的源代码
      |          |      +---- port/      <--- 平台相关的源代码
      |          +---- xcode/         <--- MacOS/xcode相关的源代码和项目文档
      |          |---- laser_kbd.sln  <--- Visual Studio 2010项目文档
      +---- sdkpack
                 +---- license_and_copyrights/   <--- 第三方库的许可协议和README文档
                 +---- ref/
                        +---- dll/               <--- 第三方库的预编译dll (win32)
                        +---- lib/               <--- 第三方库的预编译lib (win32)
                        +---- inc/               <--- 第三方库的头文件
                        +---- dylib/             <--- 第三方库的预编译dll (MacOS)

 

2. 如何将代码移植到其他平台下?

本信号处理软件在开发初期就考虑了高度可移植性,表现在:

1) 所有的核心处理逻辑都是平台无关的代码

2) 所有平台相关的代码都放在了目录 src/port 里

3) 在平台相关/平台无关代码之间有明确的接口作桥梁

4) 所有依赖到的第三方库也是高度跨平台的,例如: OpenCV

如果您打算将源代码移植到新的平台下,您需要为这个新平台实现一个新的移植层(port layer)。这个移植层至少需要提供如下功能:

  • 按键事件注入

当“键盘”按下后,往OS注入对应的键盘事件

  • 摄像头视频捕捉已经曝光控制

由于OpenCV本身不允许用户代码控制摄像头的曝光参数,因此移植层需要实现这个机制。

所有对平台相关代码的调用接口都定义在了src/port/common文件夹里面。如果您确定要开始移植工作,则应该从这些接口定义开始入手。

此外,您也可以参考目前Windows和MacOS上的移植层。

3. 如何修改键盘布局?kbd_layout

位于src/keyboard_emu/layout_provider.cpp 的代码控制信号处理软件内部的键盘布局信息。 (实际的虚拟键盘投射元件也需要做出修改).

通过修改 _key_mapper[] 描述数组,可以实现对软件中键盘布局的修改。

4. 如何实现在“按键”时播放声音提示?

该功能已经在Windows平台中实现.

由于播放音频的方法是与具体操作系统相关的,因此推荐将这个功能放在移植层里面实现。这里将举例说明如何在Windows上实现这个功能。

我们使用如下的Win32 api实现播放一段制定的wav音频:

BOOL sndPlaySound(
 LPCTSTR lpszSound,
 UINT fuSound
 );

在这个例子里,我们使用的音频文件叫做type.wav. 当用户“按键”后,windows就会播放这个声音:

在移植层中定义了接口OSKeyInjector::injectKeyEvents用于操作用户“按键”以后的事情. 可以从这里入手加入播放声音的功能:

如下是Windows平台中该接口的原始实现代码 (keyinjector_win32.cpp)

    virtual bool injectKeyEvents( const std::vector<KeyEventDesc> & intputlist)
    {
        if (!intputlist.size()) return false;

        INPUT * inputs = new INPUT[intputlist.size()];

        do
        {
            for (int pos=0; pos<intputlist.size(); ++pos)
            {
                inputs[pos].type = INPUT_KEYBOARD;
                inputs[pos].ki.wVk = intputlist[pos].keyval;

                if (intputlist[pos].type == KEY_EVENT_PRESSED) {
                    hasinputs = true;
                    inputs[pos].ki.dwFlags =  0;
                } else {
                    inputs[pos].ki.dwFlags =  KEYEVENTF_KEYUP;
                }

                inputs[pos].ki.time = 0;
            }

            SendInput(intputlist.size(), inputs, sizeof(INPUT));

        }while(0);

        delete [] inputs;

        return true;
    }

我们将它修改如下:

    virtual bool injectKeyEvents( const std::vector<KeyEventDesc> & intputlist)
    {
        if (!intputlist.size()) return false;

+        bool hasinputs = false;

        INPUT * inputs = new INPUT[intputlist.size()];

        do
        {
            for (int pos=0; pos<intputlist.size(); ++pos)
            {
                inputs[pos].type = INPUT_KEYBOARD;
                inputs[pos].ki.wVk = intputlist[pos].keyval;

                if (intputlist[pos].type == KEY_EVENT_PRESSED) {
+                    hasinputs = true;
                    inputs[pos].ki.dwFlags =  0;
                } else {
                    inputs[pos].ki.dwFlags =  KEYEVENTF_KEYUP;
                }

                inputs[pos].ki.time = 0;
            }

            SendInput(intputlist.size(), inputs, sizeof(INPUT));

        }while(0);

        delete [] inputs;

+        if (hasinputs && g_config_bundle.playsound) {
+            // play sound feedback
+            std::string soundfile = FILEPATH_RESOURCE_SOUND_FOLDER;
+            soundfile += "type.wav";
+            ::sndPlaySoundA( soundfile.c_str(), SND_ASYNC);
+        }

        return true;
    }

这样按键声音反馈功能就实现好了。您可以自行编译github的源代码体验一下:)

Tips for Compiling the Laser Keyboard Software Source Code

I. Introduction

Currently, the full source code of RoboPeak open source laser projection keyboard design has been published on Github. The released source code applies the LGPL license. You can freely use the code and modify it as long as you follow the terms described in LGPL. This article describes how to compile the source code of the  signal processing software for RoboPeak Laser Projection Keyboard on Windows and MacOS systems. Besides, some in-depths implementation details are provided to help you understand the source code and make cool modifications.

Quick Ref:

  • Compile the source on Windows
  • Compile the source on MacOS
  • More details

II.  Reasons to compile the source code

Firstly, compiling the source code allows you to experience the latest features which hasn’t been included in the stable release version yet. You can be the very first personal to trail these cool features.

Secondly, if you want to make some modifications/improvements to the current design, compiling the source code is the first step.

Thirdly, if you find our released signal processing software cannot work on your system, compiling the source code by yourself may be a quick solution before RoboPeak team release bug fix to your issues.

III. Prerequisite

  • Windows

Windows XP or higher version

Visual Studio 2010 (with VC++ package installed) or Visual C++ 2010 express (higher version may work as well, but they haven’t been tested yet)

Git Tool support (e.g. msysgit)

  • MacOS

OS10.7 or higher (we use OS10.8)

XCode 4.5 or higher with GCC support (we don’t use llvm-gcc)

GIT tool (you can install it using macport or homebrew)

Besides the above requirements, we also assume you have some basic ideas related to software developement: e.g. how to compile a C++ program on Windows/MacOS, what OpenCV is , what GIT is , etc Also, you need to clone the latest source on github.

IV. Compile the source code on Windows

STEP1: clone the source code from github

We recommend you to use tortoise-git to clone the source code to your local folder. The following is an example: tutorisegit_clone or using the following command:

git clone https://github.com/robopeak/laserkbd.git

You should get the following file structure after the clone operation has been finished: cloned_source

STEP2: Compile the source using VS2010

Enter the laser_kbd_neo folder and open the VS solution file laser_kbd.sln:

vs2010

To build the source code, simply using the Release configuration and start build. You will find the compiled binary file laser_kbd.exe under the folder: laser_kbd_neoRelease:

vs_compiled

STEP3: Copy the essential files to build the working package

The generated executable requires its dependencies to be present in the same folder. You need to copy these dependencies to make the compiled software work. It is recommended to create a clean folder like output under the root of the source code: i.e.   laserkbd_source_rootoutput Copy the above laser_kbd.exe into this folder, and the following files as well:

  • laser_kbd_neores folder
  • sdkpackrefdll*.dll files

The final package should look like the following: vs_released

Done!

The signal processing software build by your own is ready to go now! win32_done

V. Compile the source code on MacOS

STEP#1 Clone the source code

Clone the laser keyboard source code to your local folder using the following command:

$ git clone https://github.com/robopeak/laserkbd.git

You should find the following files/folders in the cloned source folder:

Shikais-MacBook-Pro:laserkbd csk$ ls
README.md laser_kbd_neo sdkpack

STEP#2 Compile the source using XCode

Enter the folder laser_kbd_neo/xcode, open the project file: LaserKeyborad.xcodeproj.

xcode_compile

You can execute the final workable software package simply click the Run button (for debug version)

macos_compiled

VI. Implementation tips

1. file organization

The source code tree is organized based on the following structure:

<source root>
      +---- laser_kbd_neo/            <--- All the source code/IDE project files/3rd party libs src
      |          +---- ref/           <--- source code of the 3rd party libs
      |          +---- res/           <--- image/data required by the software
      |          +---- src/           <--- source code of the signal processing software
      |          |      +---- port/      <--- target platform dependent code
      |          +---- xcode/         <--- MacOS/xcode related code and the project file
      |          |---- laser_kbd.sln  <--- Visual Studio 2010 solution file
      +---- sdkpack
                 +---- license_and_copyrights/   <--- license/readme files of the 3rd party libs
                 +---- ref/
                        +---- dll/               <--- prebuilt dll binaries of the 3rd party libs (win32)
                        +---- lib/               <--- prebuilt static lib binaries of the 3rd party libs (win32)
                        +---- inc/               <--- header source of the 3rd party libs
                        +---- dylib/             <--- prebuilt dll binaries of the 3rd party libs (MacOS)

 

2. How to port the code to other platforms?

The signal processing software is inherently designed with highly portability:

1) all the core logic is implemented as platform independent

2) all the target specific code. is located under the src/port folder

3) there is a clean interface between the platform independent code and the target specific code.

4) all the 3rd libs required by the software are highly portable as well, e.g. OpenCV

If you want to port the code to a new platform, you should implement a new port layer for that platform. The port layer should provide at least the  following functions:

  • Key event injection

Inject key events to the target OS when virtual keys is pressed

  • Camera video capture and exposure control

As openCV doesn’t provide interface to allow user code to control a camera’s exposure value. The port layer should implement it.

The interfaces to  the target specific code are defined in the header files under the src/port/common folder. If you really want to do the port work, you should start from that place.

Also, the existing Windows and MacOS port layers are good reference for you.

3. how to change the keyboard layout?kbd_layout

The code inside the file src/keyboard_emu/layout_provider.cpp controls the keyboard layout used by the software (the actual keyboard layout projector should be changed as well).

You can modify the _key_mapper[] array to change the layout.

4. how to add sound feedback when a “key” has been pressed?

This feature has been implemented on Windows target.

As the way to playing a sound is target specific, it is recommended to implement the code inside the port layer. Here is the example to show you how to do this on Windows platform.

We use the following Win32 API to play a sound from a given wav file:

BOOL sndPlaySound(
 LPCTSTR lpszSound,
 UINT fuSound
 );

In the example, we choose a wav file called type.wav. When user press “keys”, Windows will play the sound.

The interface OSKeyInjector::injectKeyEvents defined in the port  layer will be invoked when user pressed “keys”. It should be a good place for us to added the sound feedback.

Here is a snapshot of the injectKeyEvents implementation on Windows platform (keyinjector_win32.cpp)

    virtual bool injectKeyEvents( const std::vector<KeyEventDesc> & intputlist)
    {
        if (!intputlist.size()) return false;

        INPUT * inputs = new INPUT[intputlist.size()];

        do
        {
            for (int pos=0; pos<intputlist.size(); ++pos)
            {
                inputs[pos].type = INPUT_KEYBOARD;
                inputs[pos].ki.wVk = intputlist[pos].keyval;

                if (intputlist[pos].type == KEY_EVENT_PRESSED) {
                    hasinputs = true;
                    inputs[pos].ki.dwFlags =  0;
                } else {
                    inputs[pos].ki.dwFlags =  KEYEVENTF_KEYUP;
                }

                inputs[pos].ki.time = 0;
            }

            SendInput(intputlist.size(), inputs, sizeof(INPUT));

        }while(0);

        delete [] inputs;

        return true;
    }

We changed the above code to the following:

    virtual bool injectKeyEvents( const std::vector<KeyEventDesc> & intputlist)
    {
        if (!intputlist.size()) return false;

+        bool hasinputs = false;

        INPUT * inputs = new INPUT[intputlist.size()];

        do
        {
            for (int pos=0; pos<intputlist.size(); ++pos)
            {
                inputs[pos].type = INPUT_KEYBOARD;
                inputs[pos].ki.wVk = intputlist[pos].keyval;

                if (intputlist[pos].type == KEY_EVENT_PRESSED) {
+                    hasinputs = true;
                    inputs[pos].ki.dwFlags =  0;
                } else {
                    inputs[pos].ki.dwFlags =  KEYEVENTF_KEYUP;
                }

                inputs[pos].ki.time = 0;
            }

            SendInput(intputlist.size(), inputs, sizeof(INPUT));

        }while(0);

        delete [] inputs;

+        if (hasinputs && g_config_bundle.playsound) {
+            // play sound feedback
+            std::string soundfile = FILEPATH_RESOURCE_SOUND_FOLDER;
+            soundfile += "type.wav";
+            ::sndPlaySoundA( soundfile.c_str(), SND_ASYNC);
+        }

        return true;
    }

Now the sound feedback feature has been implemented. You may checkout the latest source to have a try:)