In this article we are going to add in the Windows platform target to our existing workspace. This article almost never got written - I didn’t have a Windows machine that I used for development during most of the research for these articles. As I mentioned in part 1 of this series, in April 2019 I bought myself an Acer Predator Helios 500 Windows gaming laptop.
I bought it for a few reasons:
I still spend almost 100% of my day job using a Mac, and most of my home development is still done on my MacBook Air 2012, but I am really enjoying having the Helios 500 handy for not only gaming but as a very capable alternate development platform. And let’s face it - Visual Studio on Windows is pretty much the gold standard in terms of C++ development IDEs, so I very much had this series of articles in mind when deciding to buy the Windows machine.
The purpose of that ramble was to highlight that I would probably never have folded in the Windows platform into these articles if I hadn’t bought my Windows laptop. So let’s jump in and get our engine running on Windows!!
I am going to be making a couple of assumptions about your Windows developer machine, specifically:
Before continuing further you will need to install Microsoft Visual Studio 2019 - Community Edition. If you are a Microsoft developer you might have the full edition of Visual Studio which is fantastic, though only the community edition is necessary for what we are doing. Visit this link to download it: https://visualstudio.microsoft.com/vs/community. We will actually be using CMake via Visual Studio, so be sure to install 2019 or later - I’m not sure if or how well earlier versions of Visual Studio support CMake projects.
When installing Visual Studio, make sure to choose the following Workloads
:
Desktop development with C++
Linux development with C++
CRITICAL: Do not forget to install
Linux development with C++
!!! The option is somewhat hidden in the installer wizard - I missed it initially and was bashing my head against a wall for days trying to work out why Visual Studio would simply freeze on starting with a CMake project open. I took one for the team on this one - you have been warned!
Once you’ve installed Visual Studio with the workloads listed above we are ready to start creating our Windows platform.
To get started, create a new folder named windows
as a sibling to our other platform targets:
root
+ project
+ windows
We will keep the theme of automated scripting going with our Windows platform however we no longer have a rich MacOS terminal at our disposal. If you have installed Git for Windows you will probably have Git Bash
, however it is not a fully featured Linux environment. We will instead use the native scripting tools that ship with Windows itself. Historically we might have used Windows batch files (they end with .bat
) but modern versions of Windows ship with another scripting environment provisioned through the PowerShell
application.
CMake loves you!
You may have noticed that in the Visual Studio setup I stressed that the Linux development with C++
workload had to be installed. This may seem a bit odd considering we are on Windows now. There is a very good reason - it allows Visual Studio to open a folder containing a CMakeLists.txt
file, which it then treats as a fully functional CMake
project - allowing us to define our Windows project just like some of our CMake powered platforms. A lovely aspect of this approach is that it avoids the monolithic GUI driven project structure that a normal .sln
Visual Studio project would have. By using a CMakeLists.txt
file we never have to manually perform any kind of file sync when code is added to our main source folder - CMake
will simply see any changes automatically. The integration into Visual Studio feels very similar to the experience in Visual Studio Code for our Mac console application.
Create a new empty CMakeLists.txt
file in the windows
folder and enter the following:
cmake_minimum_required(VERSION 3.4.1)
project(a-simple-triangle)
set(THIRD_PARTY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../third-party")
set(MAIN_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../main/src")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/out)
set(LIB_SDL2 ${THIRD_PARTY_DIR}/sdl-windows/lib/x64/SDL2.lib)
set(LIB_SDL2_MAIN ${THIRD_PARTY_DIR}/sdl-windows/lib/x64/SDL2main.lib)
set(LIB_GLEW ${THIRD_PARTY_DIR}/glew/lib/Release/x64/glew32s.lib)
include_directories(${THIRD_PARTY_DIR}/sdl-windows/include)
include_directories(${THIRD_PARTY_DIR}/glew/include)
file(GLOB_RECURSE CPP_HEADERS ${MAIN_SOURCE_DIR}/*.hpp)
file(GLOB_RECURSE CPP_SOURCES ${MAIN_SOURCE_DIR}/*.cpp)
# If we are generating a release build, then request that we produce a Windows desktop
# application executable by specifying WIN32 as an executable option. This avoids the
# console window from appearing when we launch the .exe file. However for debug builds,
# the console window makes it really easy to view logging output so we'll allow it by
# NOT specifying the WIN32 executable option.
if(CMAKE_BUILD_TYPE MATCHES "Release")
set(EXECUTABLE_TYPE WIN32)
endif()
add_executable(
a-simple-triangle ${EXECUTABLE_TYPE}
${CPP_HEADERS}
${CPP_SOURCES}
)
set_property(TARGET a-simple-triangle PROPERTY CXX_STANDARD 17)
set_property(TARGET a-simple-triangle PROPERTY CXX_STANDARD_REQUIRED ON)
target_link_libraries(
a-simple-triangle
opengl32
${LIB_GLEW}
${LIB_SDL2}
${LIB_SDL2_MAIN}
)
Quite a lot of this CMakeLists.txt
file should feel very familiar - large parts of it are the same as for some of our other CMake platforms. Let’s step through it and I’ll highlight some of the more interesting parts.
Note: We haven’t yet written our
setup
script to download the third party dependencies - we’ll do that after we create theCMakeLists.txt
file.
The first section is very similar to the other platforms, really we are just defining a couple of variables (THIRD_PARTY_DIR
and MAIN_SOURCE_DIR
) and declaring that we’d like the build output to appear in an out
folder.
cmake_minimum_required(VERSION 3.4.1)
project(a-simple-triangle)
set(THIRD_PARTY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../third-party")
set(MAIN_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../main/src")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/out)
The next part is a bit interesting - for Windows builds we need to link .lib
files when consuming external libraries. Often the .lib
file is a stub representing another .dll
file that is loaded at runtime, and other times the .lib
file is a static library that is compiled into our code. To include the SDL2
third party library in our Windows platform we will actually need to use a Windows specific variant of SDL2 which includes the .lib
and .dll
files we need. The following two lines simply declare where in the file system to locate the .libs
needed for SDL2:
set(LIB_SDL2 ${THIRD_PARTY_DIR}/sdl-windows/lib/x64/SDL2.lib)
set(LIB_SDL2_MAIN ${THIRD_PARTY_DIR}/sdl-windows/lib/x64/SDL2main.lib)
Note: For this series of articles, I’ll be using the 64 bit versions of third party libraries. You can see an example above where we are creating paths to the
x64
variants of.lib
files. If you need or want to perform 32 bit builds, you will have to use the 32 bit.lib
files and include some sort of conditional compilation code forx64
vsx86
.
There is one more third party library that we must link with for our Windows platform which is not used on our other platforms - the OpenGL Extension Wrangler library, or GLEW for short. For the Windows platform, we will use the static GLEW library, so the library code is compiled directly into our application instead of loading at runtime from a .dll
.
set(LIB_GLEW ${THIRD_PARTY_DIR}/glew/lib/Release/x64/glew32s.lib)
We will add our include
folders to our third party library sources. For Windows we will use the sdl-windows
variant of SDL2 and additionally include the headers for the GLEW
library. We will also add all our C++ source code via the file
commands - similar to our other CMake platforms.
include_directories(${THIRD_PARTY_DIR}/sdl-windows/include)
include_directories(${THIRD_PARTY_DIR}/glew/include)
file(GLOB_RECURSE CPP_HEADERS ${MAIN_SOURCE_DIR}/*.hpp)
file(GLOB_RECURSE CPP_SOURCES ${MAIN_SOURCE_DIR}/*.cpp)
The next part is not strictly necessary but I found that showing a console window while the app is running can be very helpful to easily see logging output messages. Luckily this is the default behaviour however if you actually wanted to run the .exe
file without a hideous console window popping up, we need to tell CMake about what EXECUTABLE_TYPE
to produce. The value of WIN32
means that it should be presented as a desktop application, not a console application. So, if the current build is a "Release"
build, we will set its type to produce a desktop application, otherwise leave it alone.
if(CMAKE_BUILD_TYPE MATCHES "Release")
set(EXECUTABLE_TYPE WIN32)
endif()
The next couple of properties configure the C++ version we want to target.
set_property(TARGET a-simple-triangle PROPERTY CXX_STANDARD 17)
set_property(TARGET a-simple-triangle PROPERTY CXX_STANDARD_REQUIRED ON)
Finally, we define what to link into the executable - notice we are referring to the previously specified .lib
references.
target_link_libraries(
a-simple-triangle
opengl32
${LIB_GLEW}
${LIB_SDL2}
${LIB_SDL2_MAIN}
Close the CMakeLists.txt
file and create a new text file in the windows
folder named setup.ps1
. This will be our setup script that we will run through Windows PowerShell. We will write a set of commands to check for the required third party libraries and fetch them if they don’t exist. Conceptually it is the same thing we’ve done for the other platforms via the setup.sh
and shared-scripts.sh
files, though for Windows we won’t be sharing any scripts as there are no other Windows platforms in our solution (though we do have the Android on Windows setup scripts).
Edit the setup.ps1
file in text editor and enter the following:
# Don't allow our script to continue if any errors are observed
$ErrorActionPreference = "Stop"
# Check that we have a 'third-party' folder
Push-Location -Path "..\..\"
if (!(Test-Path "third-party")) {
New-Item -ItemType Directory -Path "third-party"
}
Pop-Location
# Check that we have the SDL2 third party Windows dev library
if (!(Test-Path "..\..\third-party\sdl-windows")) {
Write-Host "Downloading SDL Windows Dev library into third party folder sdl-windows ..."
$WebClient = New-Object System.Net.WebClient
$WebClient.DownloadFile("https://www.libsdl.org/release/SDL2-devel-2.0.9-VC.zip", "..\..\third-party\SDL2-devel-2.0.9-VC.zip")
Push-Location -Path "..\..\third-party"
Write-Host "Unzipping SDL Windows Dev library into third-party\sdl-windows ..."
cmd.exe /c 'tar -xf SDL2-devel-2.0.9-VC.zip'
Move-Item -Path SDL2-2.0.9 -Destination sdl-windows
Remove-Item -Path SDL2-devel-2.0.9-VC.zip
Pop-Location
}
# Check that we have the GLEW third party library
if (!(Test-Path "..\..\third-party\glew")) {
Write-Host "Downloading GLEW into third party folder glew ..."
$WebClient = New-Object System.Net.WebClient
$WebClient.DownloadFile("https://github.com/nigels-com/glew/releases/download/glew-2.1.0/glew-2.1.0-win32.zip", "..\..\third-party\glew-2.1.0-win32.zip")
Push-Location -Path "..\..\third-party"
Write-Host "Unzipping GLEW library into third-party\glew ..."
cmd.exe /c 'tar -xf glew-2.1.0-win32.zip'
Move-Item -Path glew-2.1.0 -Destination glew
Remove-Item -Path glew-2.1.0-win32.zip
Pop-Location
}
So let’s take a look at the components in our setup script. First we will make sure that if any script errors happen that we stop - the default behaviour is to keep going (no idea why that’s the default…).
$ErrorActionPreference = "Stop"
Next we use the push / pop concept to navigate down and see if we have a third-party
folder and if not, create one.
Push-Location -Path "..\..\"
if (!(Test-Path "third-party")) {
New-Item -ItemType Directory -Path "third-party"
}
Pop-Location
The first third party library we will look for and fetch if necessary is SDL2. We will be fetching the Windows Developer version as it comes bundled with the necessary .lib
and .dll
files that we’ll need for linking and runtime. The System.Net.WebClient
request behaves similarly to our wget
commands for the other platforms and was explained in the Android for Windows article:
if (!(Test-Path "..\..\third-party\sdl-windows")) {
Write-Host "Downloading SDL Windows Dev library into third party folder sdl-windows ..."
$WebClient = New-Object System.Net.WebClient
$WebClient.DownloadFile("https://www.libsdl.org/release/SDL2-devel-2.0.9-VC.zip", "..\..\third-party\SDL2-devel-2.0.9-VC.zip")
Push-Location -Path "..\..\third-party"
Write-Host "Unzipping SDL Windows Dev library into third-party\sdl-windows ..."
cmd.exe /c 'tar -xf SDL2-devel-2.0.9-VC.zip'
Move-Item -Path SDL2-2.0.9 -Destination sdl-windows
Remove-Item -Path SDL2-devel-2.0.9-VC.zip
Pop-Location
}
Then we need to fetch the GLEW
library if needed. Notice also that once these libraries have been fetched and unpacked into the third-party
folder, their locations line up to the linked libraries we specified in the CMakeLists.txt
file.
if (!(Test-Path "..\..\third-party\glew")) {
Write-Host "Downloading GLEW into third party folder glew ..."
$WebClient = New-Object System.Net.WebClient
$WebClient.DownloadFile("https://github.com/nigels-com/glew/releases/download/glew-2.1.0/glew-2.1.0-win32.zip", "..\..\third-party\glew-2.1.0-win32.zip")
Push-Location -Path "..\..\third-party"
Write-Host "Unzipping GLEW library into third-party\glew ..."
cmd.exe /c 'tar -xf glew-2.1.0-win32.zip'
Move-Item -Path glew-2.1.0 -Destination glew
Remove-Item -Path glew-2.1.0-win32.zip
Pop-Location
}
Close the setup.ps1
file, then start PowerShell and navigate into the windows
folder. Try to run your setup script in PowerShell:
.\setup.ps1
Downloading SDL Windows Dev library into third party folder sdl-windows ...
Unzipping SDL Windows Dev library into third-party\sdl-windows ...
Downloading GLEW into third party folder glew ...
Unzipping GLEW library into third-party\glew ...
As I mentioned in the earlier Android article there is a fair chance that if you have not run a PowerShell script on your computer before, you will get an execution policy error. You can change the execution policy in PowerShell, but the other way to get around this problem is to enable Developer mode
on your computer. To find the Developer mode
option, go to your Windows settings
-> Update & Security
-> For developers
. Enable the Developer mode
radio choice then reboot your computer. After doing this your script should be able to run successfully.
When you have successfully run the setup.ps1
script, check that you have a third-party
folder with the following children:
root
+ third-party
+ glew
+ sdl-windows
Now that we have the required third party libraries thanks to our setup script, and we have our CMakeLists.txt
file ready to go, we can open Visual Studio and try to run our project. Start Visual Studio, then select Open a local folder
.
Browse to our windows
folder and click Select Folder
. Visual Studio will open the folder and detect our CMake project - check the CMake
output pane to see that our project has been synced.
We can switch the project view to the CMake
view to show our C++ code base. Tap the small Toggle between Solution and Folder views
button, next to the icon that looks like a house and choose CMake Targets View
.
After toggling to the CMake Targets View
your project pane should look a bit like this:
To run our project we need to choose a target. Tap the small drop down arrow on the Select Startup Item
button and choose the a-simple-triangle.exe (....)
option. Now tap the green play button to start building the application.
Our project will fail its build with a few different errors. A few of these errors will feel familiar as they are related to us not having added the windows
enumeration to our platform
code.
For example, one of the first errors I get looks like this:
... main\src\core\platform.cpp(17): error C4716: 'ast::getCurrentPlatform': must return a value
To fix this error we need to add our new windows
platform to our platform.hpp
and platform.cpp
files. You can edit these files directly in Visual Studio if you like.
Open platform.hpp
and update it like this:
#pragma once
namespace ast
{
enum class Platform
{
mac,
ios,
android,
emscripten,
windows
};
Platform getCurrentPlatform();
} // namespace ast
Then edit platform.cpp
and update it to include a clause to return the windows
enum:
#include "platform.hpp"
ast::Platform ast::getCurrentPlatform()
{
#if defined(__EMSCRIPTEN__)
return ast::Platform::emscripten;
#elif __APPLE__
#include "TargetConditionals.h"
#if TARGET_OS_IPHONE
return ast::Platform::ios;
#else
return ast::Platform::mac;
#endif
#elif __ANDROID__
return ast::Platform::android;
#elif WIN32
return ast::Platform::windows;
#endif
}
Cool, now run the project again to get the next batch of errors. For me I have a series of OpenGL related errors, starting with one that looks like this:
... main\src\main.cpp(16): error C3861: 'glClearColor': identifier not found
You might suspect the problem - we need to update our graphics-wrapper.hpp
to include a Windows specific condition:
#pragma once
#if defined(__EMSCRIPTEN__)
#include <GLES2/gl2.h>
#elif __APPLE__
#define GL_SILENCE_DEPRECATION
#include "TargetConditionals.h"
#if TARGET_OS_IPHONE
#include <OpenGLES/ES2/gl.h>
#else
#include <OpenGL/gl3.h>
#endif
#elif __ANDROID__
#include <GLES2/gl2.h>
#elif WIN32
#define GLEW_STATIC
#include <GL/glew.h>
#endif
The important part is the new WIN32
condition, where we will include the GLEW
library which helps us a great deal on Windows to get the OpenGL functionality working. The #define GLEW_STATIC
is important because it tells the GLEW library that we will be using the static .lib instead of a .dll.
Run the app again - this time we shouldn’t get compilation errors, but we will get a dirty big runtime error that looks like this:
The reason for this error is that our a-simple-triangle.exe
is compiled and generated into the out
folder, and because we linked to the SDL2 library in our CMakeLists.txt
file like this:
set(LIB_SDL2 ${THIRD_PARTY_DIR}/sdl-windows/lib/x64/SDL2.lib)
our executable is searching the out
folder for the accompanying SDL2.dll
to dynamically load. Obviously the SDL2.dll
file is not there because we haven’t done anything to cause it to be there, hence the runtime crash. This problem can be solved by adding a post build step to our CMakeLists.txt
file. We did the same thing for the Mac console platform to run some script after a build.
Create a new text file named cmake-post-build.ps1
in the windows
folder and open it with a text editor with the following content:
$ErrorActionPreference = "Stop"
Push-Location -Path "out"
if (!(Test-Path "SDL2.dll")) {
Copy-Item -Path "..\..\..\third-party\sdl-windows\lib\x64\SDL2.dll"
}
Pop-Location
This post build script navigates into the out
folder then checks if there is a file named SDL2.dll
. If the file isn’t there, it copies it from our third party sdl-windows
folder. Save and close the script and edit the CMakeLists.txt
file again so we can add in the post build command to run the new script:
add_custom_command(
TARGET a-simple-triangle
POST_BUILD
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
COMMAND PowerShell -File cmake-post-build.ps1
)
As you can see, we are just invoking the command PowerShell -File cmake-post-build.ps1
as a CMake post build step. Save and close the CMakeLists.txt
. If you edited the CMakeLists.txt
file in Visual Studio you will notice that upon saving it, the CMake project is automatically synced, however our post build script won’t be triggered until the next time a build happens due to a code change.
To ensure our post build script will run, select Build
-> Rebuild All
in Visual Studio to force it to recompile, therefore triggering our post build step. You should observe there is now an SDL2.dll
file in the out
folder.
Re-run our application in Visual Studio and we will be greeted with yet another error like so:
This error is related to the GLEW
library. To use it correctly we have to not only include the library, but we must initialise it at the correct point in time. Because we haven’t initialised it yet, our OpenGL calls aren’t routing through it correctly. Initialising it is pretty straight forward - it must be done after we have obtained an OpenGL context but before we make any subsequent OpenGL invocations. Edit the main.cpp
file and add a conditional block of code which invokes glewInit();
like so:
context = SDL_GL_CreateContext(window);
#ifdef WIN32
glewInit();
#endif
glClearDepthf(1.0f);
We need the conditional guard because we only included the GLEW
library for the Windows platform. Run the application again and …
Hooray! We have our Windows application running successfully.
Since we have a shared C++ code base that we could be editing on either Mac with Visual Studio Code, or Windows with Visual Studio we would like the auto formatting rules to be the same across both. We can instruct Visual Studio on Windows to use Clang formatting based on the same kind of rules we configured for Visual Studio Code earlier. If Visual Studio finds a file named .clang-format
or _clang-format
within the same folder structure as the source code, it will use it when formatting code. I managed to get this working but it required me to place a _clang-format
file in the main/src
folder otherwise Visual Studio did not detect it.
Create a new text file in the main/src
folder named _clang-format
with the following text.
Note: Be sure to remove the
.txt
file extension or it won’t work
BasedOnStyle: LLVM
AccessModifierOffset: -4
DerivePointerAlignment: false
PointerAlignment: Left
UseTab: Never
IndentWidth: 4
BreakBeforeBraces: Allman
AllowShortIfStatementsOnASingleLine: false
IndentCaseLabels: true
ColumnLimit: 0
TabWidth: 4
NamespaceIndentation: All
Save and close the file, then open Visual Studio and within a source file, type the chords CTRL+K
, followed by CTRL+D
to trigger a format. Visual Studio will confirm that it found our code formatting rules and will use them.
If you are like me and spend most of your time working on a Mac, you will have grown very used to the typical standard keyboard navigation commands. For example here are a few common commands that you would use on auto pilot:
Command | Result |
---|---|
CMD + Left / Right |
Jump to beginning or end of current line |
CMD + Up / Down |
Jump to top or bottom of the current document (depends on application sometimes) |
OPTION + Left / Right |
Jump a word at a time left or right |
CMD + Tab |
Cycle through running applications |
CTRL + Left / Right |
Slide back and forth between virtual desktops |
CMD + C |
Copy |
CMD + V |
Paste |
CMD + S |
Save |
Additionally, the physical placement of the special keys is different on a Windows keyboard vs a Mac keyboard. On Windows the keys - at least on my machine - are ordered like this:
Ctrl - Fn - Windows - Alt
Whereas on a Mac they are like this:
Fn - Ctrl - Alt - Cmd
Jumping from my Mac onto my Windows laptop became really annoying as my muscle memory wanted to always press the Mac style keys. So I found myself constantly resizing windows or opening drop down menus when I was probably trying to simply move my cursor to the end of a line of code.
I also discovered that on my new Windows laptop the Home
and End
keys that you would normally use in Windows to jump to the start or end of a line of text were tucked at the top right of my keyboard and needed to be pressed with the Fn
key held down. Although different Windows keyboards will have different configurations I suspect that most Windows laptops won’t place the Home
and End
keys in a sensible position for someone who does a lot of text entry - like a programmer. Needless to say it made programming somewhat infuriating and I felt a level of rage building inside me. So here is how I fixed this problem by morphing the keyboard on my Windows machine into something that feels a lot more like my Mac keyboard. After applying these tweaks my keyboard experience on my Windows machine is now surprisingly pleasant and I can switch back and forth to my Mac and not have to cope with much difference.
Step 1 - Key remapping at the registry level
We will use a program to remap some of our Windows keys like this:
Physical key | Remapped behaviour |
---|---|
Ctrl |
Triggers Windows key |
Windows |
Triggers Alt key |
Alt |
Triggers Ctrl key |
By doing this, we can press Alt
+ C
etc in Windows, which mimics the same physical action as pressing CMD
+ C
in Mac OS.
This stage of remapping needs to be done by editing the Windows registry. I used a tool named SharpKeys
to do this. You can get SharpKeys
here: https://github.com/randyrants/sharpkeys. The configuration you need to apply in this tool should look exactly like this:
Once you add those mapping rules in SharpKeys
you will need to Write to Registry
then reboot your computer for the changes to apply.
Step 2 - Key remapping at the macro level
Now that our physical keys have been remapped, we need to add some additional mapping to achieve the Alt
+ Left
/ Right
and a few other behaviours.
To do this we will use another tool named AutoHotKey
which you can find here: https://www.autohotkey.com. I’ve actually used AutoHotKey
for years to remap keys while playing games. For example, in the original Dawn of War
https://en.wikipedia.org/wiki/Warhammer_40,000:_Dawn_of_War - which is an awesome game - the keyboard controls to scroll the battle map were the arrow keys. I found that to be really awkward so I used AutoHotKey
to remap the W, A, S, D
keys to mimic the arrow keys. I was then able to scroll around using WASD
instead.
The nice thing about AutoHotKey
is that you don’t need to reboot your computer to apply its scripts. You can simply create or edit scripts and load / unload them at will in real time. We will create a new script that you can load whenever you intend to do programming work - though to be honest its probably useful all the time.
After you’ve installed AutoHotKey
, create a new text file somewhere handy - we’ll name it MacKeys.ahk
. Right click on the file and choose Edit Script
to open in a text editor rather than running it.
I found a few helpful snippets from this conversation thread which I modified and refined a bit: https://autohotkey.com/board/topic/60675-osx-style-command-keys-in-windows.
Enter the following to give us the remaining key mapping, then save and close it:
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
; Command-backspace deletes whole line
#BS::Send {LShift down}{Home}{LShift Up}{Del}
; alt-delete deletes previous word
!BS::Send {LShift down}{LCtrl down}{Left}{LShift Up}{Lctrl up}{Del}
; Navigation of smaller chunks (skip word)
<!Left::Send {ctrl down}{Left}{ctrl up}
<!Right::Send {ctrl down}{Right}{ctrl up}
; Navigation using of bigger chunks (Skip to start/end of line/paragraph/document)
^Left::Send {Home}
^Right::Send {End}
!Up::Send {ctrl down}{Up}{ctrl up}
!Down::Send {ctrl down}{Down}{ctrl up}
^Up::Send {Lctrl down}{Home}{Lctrl up}
^Down::Send {Lctrl down}{End}{Lctrl up}
; Selection (uses a combination of the above with shift)
<!+Left::Send {ctrl down}{shift down}{Left}{shift up}{ctrl up}
<!+Right::Send {ctrl down}{shift down}{Right}{shift up}{ctrl up}
^+Left::Send {shift down}{Home}}{shift up}
^+Right::Send {shift down}{End}}{shift up}
!+Up::Send {ctrl down}{shift down}{Up}}{shift up}{ctrl up}
!+Down::Send {ctrl down}{shift down}{Down}}{shift up}{ctrl up}
^+Up::Send {Lctrl down}{shift down}{Home}}{shift up}{Lctrl up}
^+Down::Send {Lctrl down}{shift down}{End}}{shift up}{Lctrl up}
; Close current application
^q::Send {alt down}{F4 down}{alt up}{F4 up}
Double click the script so it is loaded in AutoHotKey
- you will see the AutoHotKey
icon in your status area and if you hover your cursor on it a tooltip will display showing what script is currently running. Take it for a test drive - you should now be able to navigate around in a very similar way to on your Mac.
Not everything is a 100% match but having these core basics working the same goes most of the way to keeping your sanity between operating systems.
If you want to add new source files from the file system into your Visual Studio project you will need to tell CMake to refresh itself so it can sync the file system. To force CMake to refresh itself, select:
Project -> Generate Cache for a-simple-triangle
You will see output a bit like this in the output window after:
1> CMake generation started.
1> CMake generation finished.
If you were to commit A Simple Triangle into version control, you would want to create a new .gitignore
file for the windows
folder, so it won’t include all the files that get auto generated by running the build. Here is a sample .gitignore
that would achieve this for our project:
.vs
out
The code for this article can be found here.
In the next article we will refactor our C++ code base to clean it up a bit.
Continue to Part 8: Refactor foundation.
End of part 7