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;

PACL pDACL = NULL;
PACL pOldDACL = NULL;
PSECURITY_DESCRIPTOR pSD = NULL;
EXPLICIT_ACCESS ea;
PSID pUsersSID = NULL;

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;
  cleanup();
  return EReturnCode_Error;
}

ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));

SID_IDENTIFIER_AUTHORITY SIDAuthNT = {SECURITY_NT_AUTHORITY};
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;
  cleanup();
  return EReturnCode_Error;
}

ea.grfAccessPermissions = FILE_GENERIC_WRITE | FILE_GENERIC_READ;
ea.grfAccessMode = SET_ACCESS;
ea.grfInheritance = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE;
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;
  cleanup();
  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;
  cleanup();
  return EReturnCode_Error;
}

cleanup();

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";
PACL pDACL = NULL;    
PACL pOldDACL = NULL;
PSECURITY_DESCRIPTOR pSD = NULL;
EXPLICIT_ACCESS ea;

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

ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));

char groupUsersName[6] = {"USERS"};
ea.grfAccessPermissions = FILE_GENERIC_WRITE | FILE_GENERIC_READ;
ea.grfAccessMode = SET_ACCESS;
ea.grfInheritance = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE;
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, 
                     DACL_SECURITY_INFORMATION, NULL, NULL, pDACL, NULL);

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=""
export DEPENDENCY_SERVER=""
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:

sdk-configure-project

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.

build-config-button

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

sdk-build-type-debug

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
  • BUILD_UNIT_TEST Boolean ON
  • BUILD_VALIDATION_TEST Boolean ON

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!

sdk-build-config

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.

sdk-build-steps

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:

LD_LIBRARY_PATH=/opt/openvibe/dist/sdk-debug/lib
OV_PATH_ROOT=/opt/openvibe/dist/sdk-debug

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:

LD_LIBRARY_PATH=/opt/openvibe/dist/sdk-release/lib
OV_PATH_ROOT=/opt/openvibe/dist/sdk-release

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.

designer-build-config

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:

LD_LIBRARY_PATH=/opt/openvibe/dist/designer-debug/lib
OV_PATH_ROOT=/opt/openvibe/dist/designer-debug

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.

extras-build-config

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:

LD_LIBRARY_PATH=/opt/openvibe/dist/openvibe-debug/lib
OV_PATH_ROOT=/opt/openvibe/dist/openvibe-debug

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.

projects

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!

run-config-button

Click on Add -> Custom Executable

run-config-add-button

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)

debug-designer

DTerm and Fish shell, together

Fish shell does not seem to like to live within the DTerm terminal emulator. Sadly, DTerm seems in a state of abandon.

What I do now is to force bash as the shell used by DTerm. I do this by launching:

env SHELL=/bin/bash open /Applications/DTerm.app

Possible future of the Mac Mini

The current Mac Mini has many uses and most of them are not for desktop users. So, what would you want to use a small “cheap” computer running macOS for?

  • A media center connected to a TV
  • A Facebook machine with iTunes music on it
  • A build/test server for developers
  • A server that one can put into a rack (like at Mac Mini Colo)

None of these things need a x86 processor.

I bet that whatever replaces the Mac Mini will be the first ARM based Mac. And it will be glorious.

MiKTeX 2.9 and Windows 10 package installation woes

Today my MiKTeX installation stopped installing packages on the fly. After trying to install packages through Package Manager I found out that this did not work either. The error that I have obtained is this:

The executed process did not succeed.
fileName="C:\Program Files (x86)\MiKTeX 2.9\MiKTeX\bin\initexmf.exe", arguments=" --mklinks --mkmaps", exitCode="1"

I googled around and found out that this might be a problem with the installation folder not being accessible and the suggested solution was to install MiKTeX somewhere else (or just for the local user). This was not an option for me as a toolchain that does not depend only on me requires the latex binary to be in the default folder.

My solution was to go to the C:\Program Files (x86)\MiKTeX 2.9 folder properties and changing the Users permissions to Full Control. Not extremely secure but at least now I can build my documentation.

Unprotect password protected Word documents

It is always annoying when people expect you to collaborate with them and they send you locked files.

This trick only works for documents that are protected for Review. Not when you can not even open them.

So, let us assume you have a Protected.docx file that looks something like this. Notice the disabled Delete Comment, Accept Changes and so buttons.

Sad grey icons

Step 1: Open the box

A good thing to know is that .docx (and .xlsx) files are just zipped folders.

Rename the Protected.docx file to Protected.zip and unzip it. You will obtain something that looks like this:

Docx folder contents

Step 2: Tinker with the contents

Open the file word/settings.xml. Its contents will look somewhat like this (except there will be more of it and everything will be on the same line):

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:settings xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" xmlns:sl="http://schemas.openxmlformats.org/schemaLibrary/2006/main" mc:Ignorable="w14 w15">
  <w:zoom w:percent="100"/>
  <w:proofState w:spelling="clean" w:grammar="clean"/>
  <w:trackRevisions/>
  <w:documentProtection w:edit="trackedChanges" w:enforcement="1" w:cryptProviderType="rsaFull" w:cryptAlgorithmClass="hash" w:cryptAlgorithmType="typeAny" w:cryptAlgorithmSid="4" w:cryptSpinCount="100000" w:hash="1+kEXOhPYpKExv2F8dIx9VCN5Ps=" w:salt="aOdV7QoW4ZJW3KVDxd7jBg=="/>
  <w:defaultTabStop w:val="720"/>
  <w:characterSpacingControl w:val="doNotCompress"/>
  <w:savePreviewPicture/>
  <w:compat>
    <w:useFELayout/>
 ...

Notice the

<w:documentProtection w:edit="trackedChanges" w:enforcement="1" w:cryptProviderType="rsaFull" w:cryptAlgorithmClass="hash" w:cryptAlgorithmType="typeAny" w:cryptAlgorithmSid="4" w:cryptSpinCount="100000" w:hash="1+kEXOhPYpKExv2F8dIx9VCN5Ps=" w:salt="aOdV7QoW4ZJW3KVDxd7jBg=="/>

this is the source of our woes. Now, simply remove the characters between <w:documentProtection and the first /> you encounter after it. Save the file.

Step 3: Put the box back together

Now, zip the folder back again. It is important that you compress the files as they were: the folders word, _rels, docProps and the file [Content_Types].xml are on the top level of the zip (i.e.: not in a subfolder that was probably created when you unzipped the original file). Let us call the new file Unprotected.zip

Finally, rename the file back to Unprotected.docx.

Step 4: Look into the box

And voilà:

Sad grey icons

Note: Word might complain about the document being corrupted, simply choose Open Anyway and re-save the file again.

It is interesting that when you move, suddenly everybody you talk to has a cousin with a moving company.

And then they are disappointed that you have went with a professional rather than gamble on a small, family-held, business.

Centre a one line text in a UITextView

I had a problem. First I have set up an UITextView with these parameters:

view.backgroundColor = UIColor.blueColor()
view.text = "An example text"
view.textColor = UIColor.whiteColor()
view.textContainerInset.top = 0
view.textContainerInset.bottom = 0

I wanted to be able to centre the text in the view vertically. This should be easy, one just needs to position the top of the text according to the formula: top = (container.height - content.height) / 2. However, when you look at the actual look of the UITextView you will see this:

view.font = UIFont.boldSystemFontOfSize(100.0)

view.font = UIFont(name: "DINCondensed-Bold", size: 100.0)

Ouch, there is a semi-randomly placed offset at the top of the text. Turns out the point size of a font is not always the one you have demanded. However it is possible to get the actual height. An UIFont class provides a bunch of useful metrics:

view.font = UIFont.boldSystemFontOfSize(100.0)
view.font.capHeight // 70.5
view.font.pointSize // 100.0
view.font.ascender // 95.2
view.font.lineHeight // 119.3

view.font = UIFont(name: "DINCondensed-Bold", size: 100.0)
view.font.capHeight // 71.2
view.font.pointSize // 100.0
view.font.ascender // 71.2
view.font.lineHeight // 100.0

On a graph this would look like this:

Finally, the method you can now use is:

extension UITextView {
  func centerTextVertically() {
    guard let ascender = view.font?.ascender else {
      return
    }
    guard let capHeight = view.font?.capHeight else {
      return
    }
    view.textContainerInset.top = (view.bounds.height - ascender - (ascender - capHeight)) / CGFloat(2.0)
  }
}

Note that this will only for one liners and only if you know the height of the UITextView (i.e.: the height was given to it on creation)