Eventail 2.0

After a few months of development, I am proud to announce a new version of my iOS Calendar Widget App Eventail.

I am really happy how it turned out and invite you to try it, it is free.

Eventail Screenshots

Find Eventail on the App Store

Zoom out any page on mobile Safari

Just now I wanted to publish a new version of my app to testers. This is to be done on the iTunes connect website. I only had my phone, but according to Steve “iOS has full web experience” so this should not be a problem.

However, iTunes connect does not work exactly well on mobile. This is because the zoom level of the page is fixed on the “background” while the dialogs are in front. The result is that the button Submit is inaccessible.

After some amount of looking for a solution I have found several bookmarklets that should unlock the zoom. None of them worked. So I made my own solution.

Bookmark this page in Safari and change its URL to this:

javascript:(function(){document.getElementsByTagName("body")[0].style.zoom =0.5;})()

Then, on a page which has elements outside of the viewport, launch the bookmark from the bookmark menu. The page will be twice as small, but more of it will fit!

10 Laws of UX, digested

After reading the article on 10 Laws of UX and the associated comments on Hacker News, I have decided to try a shot at reformatting the page to a more legible format. In the form of a blog post—because I do not think that every idea on the internet needs its own domain.

I provide my own interpretation of the laws in the titles, they are not in the original article.

1. If a function needs to be accessed often and/or quickly, make the button big

The time to acquire a target is a function of the distance to and size of the target.

– Paul Fitts

Fitt’s Law on Wikipedia

2. Only provide choices when a good default does not exist

The time it takes to make a decision increases with the number and complexity of choices.

– William Edmund Hick and Ray Hyman

Hick’s Law on Wikipedia

3. Respect the platform’s conventions and interface guidelines

Users spend most of their time on other sites. This means that users prefer your site to work the same way as all the other sites they already know.

– Jakob Nielsen

Jakob’s Law on Nielsen Norman Group

4. Do not stuff too much detail into a small space

Law of Prägnanz:

People will perceive and interpret ambiguous or complex images as the simplest form possible, because it is the interpretation that requires the least cognitive effort of us.

The Laws of Figure/Ground, Prägnanz, Closure, and Common Fate on Interaction Design

5. Put actions that do similar things together

Law of Proximity:

Objects that are near, or proximate to each other, tend to be grouped together.

N.B.: The recent missile alert fiasco is a good example of why this is important

6. Reduce the number of things your users have to remember

The average person can only keep 7 (plus or minus 2) items in their working memory.

– George Miller

Miller’s Law on Wikipedia

7. Make tasks short, simple and with set deadlines

Any task will inflate until all of the available time is spent.

– Cyril Northcote Parkinson

Parkinson’s Law on Wikipedia

8. Put the important things at the beginning or at the end

Users have a propensity to best remember the first and last items in a series.

Serial Position Effect on Wikipedia

9. Removing features from your product may result in users not being able to achieve some goals

Tesler’s Law, also known as The Law of Conservation of Complexity, states that for any system there is a certain amount of complexity which cannot be reduced.

Larry Tesler’s Law of Conservation of Complexity on Wikipedia

10. Make the most important thing stand out

The Von Restorff effect, also known as The Isolation Effect, predicts that when multiple similar objects are present, the one that differs from the rest is most likely to be remembered.

Von Restorff Effect on Wikipedia

Alternatively: If you want to stand out from competition, find a feature which is always the same and make it different.

Bonus law

Do not put a leading zero on numbers that do not have to be aligned.

How I discovered how iOS calendar app chooses colors by digging into the icloud.com JavaScript code

While making an iOS calendar app, I needed to find out how Apple calculates background and text colors when displaying events in the Calendar app. I wanted to use the same algorithm for my display, in order to integrate well with the OS.


I was hoping for some displayBacgkroundColor property on EKCalendar object or something similar, but it does not seem to exist. I almost decided to start approximating their algorithm while I realized one thing—the icloud.com site has a calendar “application”. The event colors there seem to follow the same logic as those in the Calendar app. They are not strictly the same but it could provide some insight into how the color switching works.


iCloud.com being a modern web application, it runs a bunch of client side JavaScript. Since it is possible to add events, change calendar colors and so on, I assumed that the code which calculates the text and background colors for events must be somewhere in the publicly available JavaScript. There was a small possibility that they pre-computed the colors on the server but this seemed unlikely.

Homing in on the function

I decided to use Safari for this. It seems kind of appropriate and I am quite familiar with its web developer tools. I open the iCloud website, open the calendar page and look into what kind of JavaScript files are loaded.

Among others, this page loads mainly two big JavaScript files, both called javascript-packed.js. The other files are all called javascript-strings so I assume these load the localization files or something like that.

My first hypothesis was that the function is called when a color of an already present event is changed. This might happen when, for example, the calendar to which the event belongs is changed.

Luckily Safari can display minified JavaScript files in indented mode. However searching for functions for strings like “update” gave me too many results.

Looking at the HTML code of an event, I can observe that its color is inside an inline style attribute and that it changes when I change the calendar. This is good news, as the inspector lets me to put a breakpoint on that.

In the inspector I find the event element, right click on the div in the tree structure and set a breakpoint on Attribute Modified


Now I click on the event in the app, change the calendar… and voilà.


Walking up the call stack

At the very end of the call stack I can see that a property is assigned a value of #a66110. This is actually the text color for this calendar (I checked this in the inspector). My hope is that it will be calculated somewhere up the stack.

1 style: Assign #a66110 as as the color property of a CSS declaration
2 anonymous: Function that that applies the CSS style when called
3 access: This function is a sort of trampoline
4 css: This function generates the anonymous function in step 2, it does get the #a66110 as a parameter 'n'
5 updateProperty: calls the css function, and also gets the #a66110 as a parameter 'r'

When I get to the 6th level I finally hit something interesting. This is where the value #a66110 is created (inside a function called _update:

6 _update: This function seems to update the whole event, its title, state and most importantly, colors

p = t.get("textColor")

The get method seems to be doing something akin to the following (I did not really dig into this):

object.prototype.get = function(propName) {
    return call(this, this.properties[propName]);

I any case, the color value is derived from object t and its property textColor.

Finding the ‘Event’ object

I look into the t object’s properties and see this:

A cursory glance at the properties confirms to me that this is an ‘Event’ object. It has the usual ones such as owner, isAccepted and so on.

Intuitively this line then, in the _update function, does what we were searching for: Get the text color for an event and change the elements css representation to reflect that.

p = t.get("textColor"), this.updateProperty(n, o, ".text", p, "color")

I dig into this textColor property. I search the JavaScript file for it and look over the few results I get.

Within a piece of code which starts with Cal.Collection = CoreCal.Collection.extend({ I find this (among the other event properties):

textColor: function() {
    if (this.get("isBirthdayCalendar")) return "#4d5765";
    var e = this.get("_hexColor");
    return Cal.colorController.computeTextColor(e)
backgroundColor: function() {
    if (this.get("isBirthdayCalendar")) return "#cfd3d9";
    var e = this.get("_hexColor");
    return Cal.colorController.computeBackgroundColor(e)

Cal.colorController.computeTextColor(e) and Cal.colorController.computeBackgroundColor(e) and in both cases e is a color… finally I have struck gold!

Compute text and background color

The function iCloud uses to compute the text color is this:

computeTextColor: function(r) {
    r || CW.fatalError("Cannot provide a text color without a starting color"), r = r.toLowerCase();
    var i = this.get("specialColors"),
        s = SC.convertHexToHsv(r),
        o = s[0],
        u = s[1],
        a = s[2],
    if (i.isLight(o, u, a)) f = i.light.text;
    else if (i.isDark(o, u, a)) f = i.dark.text;
    else {
        var l = a - n,
            c = i.isGray(o, u, a),
            h = c ? 0 : Math.max(u, e);
        f = SC.convertHsvToHex(o, h, Math.max(l, t))
    return f

By putting a breakpoint on the f = ... line I can deduce the constants n = 0.35, e = 0.5 and t = 0.3.

As for the background color:

computeBackgroundColor: function(e) {
    if (!e) {
        SC.error("Cannot provide a background color without a starting color");
    e = e.toLowerCase();
    var t = this.get("specialColors"),
        n = t.backgrounds[e];
    if (n) return n;
    var r = SC.convertHexToHsv(e),
        i = r[0],
        o = r[1],
        u = r[2];
    if (t.isLight(i, o, u)) n = t.light.background;
    else if (t.isDark(i, o, u)) n = t.dark.background;
    else {
        var a = t.isGray(i, o, u) ? 0 : s;
        n = SC.convertHsvToHex(i, a, t.bgBrightness(i, o, u))
    return n

And the contents of the specialColors are here (comments are from my investigation of constants):

specialColors: {
    light: {
        main: "#dddddd",
        text: "#a8a8a8",
        background: "#f8f8f8"
    dark: {
        main: "#1a1a1a",
        text: "#000000",
        background: "#bababa"
    titles: {
        "#cc73e1": "#b14bc9",
        "#65db39": "#49bf1f",
        "#ffcc00": "#e0ac00",
        "#ff9500": "#ff7f00"
    backgrounds: {
        "#a2845e": "#e0d3c1"
    isLight: function(e, t, n) {
        var s = Cal.colorController;
        return n > r && t < i // r = 0.6, i = 0.1
    isDark: function(e, n, r) {
        return r < t // t = 0.3
    isGray: function(e, t, n) {
        return t < i // i = 0.1
    bgBrightness: function(e, t, n) {
        return Math.min(n * 1.5, o)

Then there is a bunch of color conversion functions that work in a pretty standard way.

  • convertHsvToHex takes a R, G, B triplet and returns a [H, S, V]
  • convertHexToHsv takes an array of three elements [H, S, V] and returns an array [R, G, B]
  • two regexes to check if a colors is in the HTML rgb(r, g, b) or #RRGGBB format
  • parseColor which converts either of the two formats and returns a #RRGGBB representation
  • expandColor which takes the #RRGGBB color and returns an array of [R, G, B]
  • toColorPart which takes a number in 0-255 range and transforms it to hex representation

I am not going to detail these functions here, but I have uploaded them here if you would like to look:

Original Apple Conversion Functions

Rewriting the functions into more human readable format

These functions were of course at least partly generated and minified. So I have rewritten them into a digestible format.

First, I have written a colorTools object which has the properties from the specialColors object on iCloud.

const colorTools = {
  light: {
    text: "#a8a8a8",
    background: "#f8f8f8"
  dark: {
    text: "#000000",
    background: "#bababa"
  backgrounds: {
    "#a2845e": "#e0d3c1"
  isLight: function(h, s, v) {
    return v > 0.6 && s < 0.1
  isDark: function(h, s, v) {
    return v < 0.3
  isGray: function(h, s, v) {
    return s < 0.1
  bgBrightness: function(h, s, v) {
    return Math.min(v * 1.5, 1)
  /* color space conversion removed for brevity */

This object is then used as a helper for the two functions I was actually searching for in the beginning.

Text Color

If the color is too light or too dark then use a pre-computed text color. Otherwise cap the saturation to 0.5 (for grayish colors just set it at 0) and reduce the value by 0.35.

function computeTextColor(rgb) {
  const [h, s, v] = colorTools.convertHexToHsv(rgb);  
  if (colorTools.isLight(h, s, v)) {
    return colorTools.light.text;
  } else if (colorTools.isDark(h, s, v)) {
    return colorTools.dark.text;
  } else {
    const rs = colorTools.isGray(h, s, v) ? 0 : Math.max(s, 0.5);
    const rv = v - 0.35;
    return colorTools.convertHsvToHex(h, rs, Math.max(rv, 0.3))

Background color

In a similar fashion, use a pre-computed colors if the background is too light or dark. Set the saturation to 0 for grayish colors.

function computeBackgroundColor(rgb) {
  if (colorTools.backgrounds[rgb]) {
    return colorTools.backgrounds[rgb];
  const [h, s, v] = colorTools.convertHexToHsv(rgb);
  if (colorTools.isLight(h, s, v)) {
    return colorTools.light.background;
  } else if (colorTools.isDark(h, s, v)) {
    return colorTools.dark.background;
  } else {
    let rs = colorTools.isGray(h, s, v) ? 0 : s;
    return colorTools.convertHsvToHex(h, rs, colorTools.bgBrightness(h, s, v));

This was fun. Now, the colors on iCloud website are not strictly the same than in the iOS app, but that requires just a bit of tweaking with the constants.

You can download the complete JavaScript file I have extracted here:

Apple Calendar Colors

Some final words

There you have it. In the end I was actually surprised that it was pretty easy to extract these functions from the code. I expected the minified/obfuscated code to be quite hard to read. Interestingly, most function names did not get mangled. I suppose this is due to the fact that they are called through strings in some part of the framework Apple uses.

Of course, later I have converted these functions into Swift UIColor extensions so I can use them in my application.

I might publish them here sometime later, but if you are interested ask me on Twitter.

Thoughts about spam notifications in iOS

I try to keep my notifications minimal. Only those which are important should get through.

But when I receive notifications like this my blood boils:

Spam notification from Prime Video, for an episode I have already watched.

Amazon is not the sole culprit. When I received several notifications from The Fork I have confronted them on Twitter. They have promised to do something about it, and then a few days later I was spammed again… with the same notification.

The Fork is guilty too

Although this is forbidden by Apple App Store Review Guidelines, Design section 4.5.3, big firms do not care.

I think the main problem comes from the fact that these cost nothing and are invisible to apple. I think we should be able to report these to Apple, in the same way it is done for spam e-mail.

Where to put the report button?

Putting a button on each notification would be an overkill, it would be easy to hit it with a fat thumb.

It could go into the slide over or 3D touch menu in the notification center. But the problem is that if a notification pops up while the phone is unlocked, and you tap it by reflex, it gets a free pass. These are maddening.

My idea would be to put a section inside the Privacy tab. This view would display last few notifications you have received, with an option to report them to Apple and an option to ignore notifications with the same text from this application. At first, this would not have much impact, but over time Apple could use this data to pressure App publishers and maybe even start training Siri to act as a spam filter.

Pure CSS solution to retina images with a single file

Problem: You have an image and want it to display well in browsers on a high DPI display. There are many ways to do this, some of them more complicated than others.

My requirements were pretty simple:

  • I want to be able to publish “retina” images with default markdown syntax
  • I prefer images to be blurry on non-high DPI screens, they are blurry anyway so what’s the harm.
  • I do not care about wasted bandwidth by sending retina assets to clients with standard definition screens. Not having ads or huge javascript on this site makes up for this.
  • I do not want to use any javascript or server side hacks

So what’s the solution?

  1. Name @2x assets with @2x in their filename, for example image@2x.png
  2. Add this small snippet into my CSS file (thanks, Internet Explorer!)
img[src*='@2x'] {

For an example usage look at my article about Qt Creator

Qt Creator Hidden Gems

When it comes to C++ IDEs I always reach for Qt Creator. I love its streamlined user interface which nevertheless exposes all screws and knobs if necessary.

But mostly I like how it manages to surprise me with every update.

Most recently I have updated to version 4.5.1 (from the 4.5 RC) and as I am hacking on the keyboard I see this:

QtCreator detects output parameters

Finally a work around the terrible decision to not explicitly mark function parameters used as a reference in C++.

What’s more, Qt Creator does support “relative” text style for this highlight. One saturation +0.30 later I have:

QtCreator can change text style relatively

Can’t wait for what the 4.6 will bring!

Setting File Permissions on Windows With C

Edit 2018-01-19

In my original article I have naively supposed that the group names are not localized. This is false. Always use the Well Known Security IDs when manipulating permissions on Windows.

I’ve spent good part of my morning trying to find a way how to set permissions to a particular group.

Finally while digging though Stack Overflow I have found this question which lead to another because of deprecation errors.

My final version of the code to add file read and write permissions to the group Users is this:

#include <Windows.h>
#include <AccCtrl.h>
#include <Aclapi.h>

const char* fileName = "C:/path/to/your_file.txt";

DWORD result = 0;


const auto cleanup = [pDACL, pOldDACL, pSD, pUsersSID](){
  if (pOldDACL) LocalFree(pOldDACL);
  if (pDACL) LocalFree(pDACL);
  if (pSD) LocalFree(pSD);
  if (pUsersSID) FreeSid(pUsersSID);

result = GetNamedSecurityInfo(static_cast<LPTSTR>(fileName), SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, &pOldDACL, NULL, &pSD);
if (result != ERROR_SUCCESS) {
  std::cerr << "Failed to get the security info [" << result << "]" << std::endl;
  return EReturnCode_Error;

ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));

if (!AllocateAndInitializeSid(&SIDAuthNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_USERS, 0, 0, 0, 0, 0, 0, &pUsersSID)) {
  std::cerr << "Failed to allocate SID" << std::endl;
  return EReturnCode_Error;

ea.grfAccessMode = SET_ACCESS;
ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
ea.Trustee.ptstrName = reinterpret_cast<LPTSTR>(pUsersSID);

result = SetEntriesInAcl(1, &ea, pOldDACL, &pDACL);
if (result != ERROR_SUCCESS) {
  std::cerr << "Failed to set entries in the access control list [" << result << "]" << std::endl;
  return EReturnCode_Error;

result = SetNamedSecurityInfo(static_cast<LPTSTR>(fileName), SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, pDACL, NULL);
if (result != ERROR_SUCCESS) {
  std::cerr << "Failed to set the security info [" << result << "]" << std::endl;
  return EReturnCode_Error;


Note: You will also need to link your program with Advapi32.lib.

My original code follows, do not use this:

const char* fileName = "C:/path/to/your_file.txt";

GetNamedSecurityInfo(static_cast<LPTSTR>(fileName), SE_FILE_OBJECT, 
                     &pOldDACL, NULL, &pSD);

ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));

char groupUsersName[6] = {"USERS"};
ea.grfAccessMode = SET_ACCESS;
ea.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
ea.Trustee.TrusteeType = TRUSTEE_IS_GROUP;
ea.Trustee.ptstrName = groupUsersName;

SetEntriesInAcl(1, &ea, pOldDACL, &pDACL);

SetNamedSecurityInfo(static_cast<LPTSTR>(fileName), SE_FILE_OBJECT, 

Parallels 12 and Windows 7 Slow Cursor

After updating my macOS, Parallels 12 has broken loading a VM from the Bootcamp partition. This has forced me to use my Windows 7 virtual machine and I have discovered a weird problem:

The mouse cursor inside the virtual machine is really slow if an application is loading something or doing some activity.

At first I thought this was because the machine was hogged, but the problem is actually simpler than that: Windows 7, in Parallels 12, is really slow when rendering an animated cursor. I have changed my cursor theme to an old non-aero one and everything is fine now.

Use QtCreator to develop OpenViBE in 2017 on Linux

Note: This tutorial does not use the meta repository. It uses the latest OpenViBE 2.0.0 repositories with the multi-repository build. It is based on QtCreator 4.4.1 (the currently latest stable release). It will work on the 4.5 version, however this one seems to not currently work very well on Linux.

It is possible to simplify this installation a LOT if you only wish to build the OpenViBE component and wish to use the default SDK (Kernel, Modules, base plugins..) and Designer. In that case only take the part concerning the extras repo and do not specify custom OPENVIBE_SDK_PATH and DESIGNER_SDK_PATH.

This tutorial also does not use the build scripts!

This post assumes that your final configuration will look like this:

  • openvibe
    • sdk git repository for OpenViBE SDK
    • designer git repository for OpenViBE Designer
    • extras git repository for OpenViBE Extras
    • test git repository for integration tests
    • build contains all of the build folders populated by CMake
      • sdk-debug
      • sdk-release
      • designer-debug
      • designer-release
      • extras-debug
      • extras-release
    • dist contains all of the folders created during CMake INSTALL phase
    • sdk-debug
      • sdk-release
      • designer-debug
      • designer-release
      • openvibe-debug this tis the final destination of the complete Debug build
      • openvibe-release and the release build

In my examples I am putting all of these files under /opt, if you want them somewhere else you should change all instances of /opt in this tutorial to something else.

Clone the repositories

Get to the folder where you want to have your openvibe root folder and do:

cd /opt
mkdir openvibe
cd openvibe
mkdir build
cd build
mkdir sdk-debug sdk-release designer-debug designer-release openvibe-debug openvibe-release
cd ..
mkdir dist

git clone git@gitlab.inria.fr:openvibe/sdk.git sdk
git clone git@gitlab.inria.fr:openvibe/designer.git designer
git clone git@gitlab.inria.fr:openvibe/extras.git extras
git clone git@gitlab.inria.fr:openvibe/test.git test

Install dependencies

Some dependencies are needed, this will install all linux native packages + some extras such as test.

# if you want test data you will need the values for these variables
export PROXYPASS=""
perl sdk/scripts/linux-install_dependencies.pl --manifest-dir sdk/scripts/ --dependencies-dir dependencies
perl sdk/scripts/linux-install_dependencies.pl --manifest-dir designer/scripts/ --dependencies-dir dependencies
perl sdk/scripts/linux-install_dependencies.pl --manifest-dir extras/scripts/ --dependencies-dir dependencies

cd /opt/openvibe/sdk
./unix-get-dependencies.sh --manifest tests-data.txt --out /opt/openvibe/dependencies --cache /opt/openvibe/dependencies/arch

Configure the QtCreator

This step will have to be reproduced for all of the other repositories with minor changes.

It is important to know how CMake actually works, a quick summary:

  • CMake creates a build folder during configuration step. This folder contains all of the configuration files (CMakeCache.txt is the most important), ninja file (this tells the compiler in what order it should build the project) and the built binary files.
  • When you configure the project all of the variables you specify at configure are saved in the build folder.
  • When you build the project, the binary files are kept in the build folder.
  • When you INSTALL the project, only the files that are necessary for the final application are copied into the dist folder (specified by CMAKE_INSTALL_PREFIX. On Linux, the default dist folder is /usr/local and on Windows it is C:\Program Files (x86)\PROJECT_NAME. We do not want to use the default folders!

Session configuration

Launch QtCreator, create a new session

SDK configuration

Go to Welcome -> Open Project and navigate to the openvibe/sdk/CMakeLists.txt file, a dialog like this will open:


Note that I have already changed all paths to what they should be.

Click on the Configure Project button.

CMake variables

You will see a lot of errors printed in the General Messages tab on the bottom. Go to the Projects pane on the right and select build under GCC.


Check that the Edit build configuration dropdown is set to Debug on the top of this window.


Now you will need to (sadly, blindly) add a few variables to the lot. This is because the configuration by default is not what we want.

Add these variables:

  • CMAKE_INSTALL_PREFIX String /opt/openvibe/dist/sdk-debug
  • OV_CUSTOM_DEPENDENCIES_PATH String /opt/openvibe/dependencies

The test variables are not strictly necessary and you can turn them OFF if you want.

Click on Apply Configuration. Note that Qt Creator does not save this configuration if you do not use Apply!


Install and clean

In order to actually run the INSTALL step after each build (this is not how normal projects work but here it is necessary).

In the Build Steps and Clean Steps choose the install and clean option respectively.


Run environment

OpenViBE requires some variables on runtime to work. Also, as we are not installing into a default prefix we need to tell Linux where to find the libraries.

In the Build Environment click on Batch Edit… and insert the following string:


You are now ready to build OpenViBE SDK in Debug mode. Give it a spin, either right click on the OpenVIBE project in the Edit view or use the hammer button on the bottom left.

Release configuration

Redo the same thing for Release configuration on the top.

In this case you will need to change the CMAKE_INSTALL_PREFIX to /opt/openvibe/dist/sdk-release/. And choose the appropriate environment variables like so:


Designer configuration

This step is very similar to the SDK. First open the project from the Welcome screen.

On the configure project page select:

  • Debug build folder is /opt/openvibe/build/designer-debug
  • Release build folder is /opt/openvibe/build/designer-release

Do not forget to disable all other build types.

CMake variables

You will need some additional CMake variable specified:

  • OPENVIBE_SDK_PATH String /opt/openvibe/dist/sdk-debug
  • LIST_DEPENDENCIES_PATH String /opt/openvibe/dependencies
  • CMAKE_INSTALL_PREFIX String /opt/openvibe/dist/designer-debug

This tells the designer to find the SDK built in the previous step. Otherwise the designer will use SDK from its dependencies.


Click on Apply Configuration.

Install & Clean

Check the install and clean options in Clean and Build steps like in SDK.

Run environment

We also want to be able to run a “pure” designer installation from Qt Creator.

In the Build Environment click on Batch Edit… and insert the following string:


You are now ready to build OpenViBE Designer in Debug mode. Give it a spin, either right click on the OpenVIBE project in the Edit view or use the hammer button on the bottom left.

Extras configuration

Once again (and for the last time).

On the configure project page select:

  • Debug build folder is /opt/openvibe/build/openvibe-debug
  • Release build folder is /opt/openvibe/build/opevibe-release

Do not forget to disable all other build types.

CMake variables

You will need some additional CMake variable specified:

  • OPENVIBE_SDK_PATH String /opt/openvibe/dist/sdk-debug
  • DESIGNER_SDK_PATH String /opt/openvibe/dist/designer-debug
  • LIST_DEPENDENCIES_PATH String /opt/openvibe/dependencies
  • CMAKE_INSTALL_PREFIX String /opt/openvibe/dist/openvibe-debug

This tells the designer to find the SDK built in the previous step. Otherwise the designer will use SDK from its dependencies.


Click on Apply Configuration.

Install & Clean

Check the install and clean options in Clean and Build steps like in SDK.

Run environment

Finally, we want to be able to run the complete installation.

In the Build Environment click on Batch Edit… and insert the following string:


You are now ready to build the Full OpenViBE Designer in Debug mode. Give it a spin, either right click on the OpenVIBE project in the Edit view or use the hammer button on the bottom left.

You should now see all of the various OpenViBE projects in the build menu.


Running the OpenViBE Designer with the full feature set

The Designer project is built in a separate project from extras. If you want to run the designer (or debug it) with all of the plugins, you need to create a new run configuration.

Go to Projects -> OpenViBE -> Run in the left pane. Be careful to choose the OpenViBE Project which corresponds to extras!


Click on Add -> Custom Executable


Now select the /opt/openvibe/dist/openvibe-debug/bin/openvibe-designer as Executable.

Rename this configuration as Debug Designer. You can now choose it from the projects menu and run it using the play button (or debug it using the play button with a little bug on it)