Mac keystrokes using the minDevkit
This post has a lot of information to give the necessary context, but essentially I can't get the project to build when I add the code to simulate a keystroke and I am not sure why. I was able to figure this out for Windows, but I am running into a problem on Mac that I can't get past. I followed the instructions of this video and github page which seem to be the most up to date.
https://www.youtube.com/watch?v=il5WblTBUgs
https://github.com/Cycling74/max-sdk/blob/main/README-8.2-update.md
I started by manipulating the min.hello-world project. I removed the greeting when the object loads, edited the bang function and created a function that responds to integers. Everything compiles and works as expected. However, I want to simulate a keystroke so I brought in the following libraries...
#include <CoreGraphics/CoreGraphics.h>
#include <ApplicationServices/ApplicationServices.h>
#include <Carbon/Carbon.h>
As far as I understand, I only need Application Services, but the other ones were added as I tried to troubleshoot issues. Anyway, to use those libraries I updated my "respond to integer" function to the following code...
message<> number {this, "int",
MIN_FUNCTION {
int key = 56;
//EDIT: this code is close to correct, but definitely don't trust it.
//there are better examples on Stack Overflow, if you are really stuck
//comment and could share something better. Also, it appears
//olsen put 11strokes code up on Github. It says it's cross platform, so it
//may be a better and more comprehensive source than what I could offer.
// Create an HID hardware event source
CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
// Create a new keyboard key press event
CGEventRef evt = CGEventCreateKeyboardEvent(src, (CGKeyCode) key, true);
// Post keyboard event and release
CGEventPost (kCGHIDEventTap, evt);
CFRelease (evt);
CFRelease (src);
//this line printed before I added the above
cout<< "Sometimes I work, sometimes I don't" << endl;
return {};
}
};
Instead of Xcode (I am not very familiar with it) I am using the terminal to build using the command "cmake --build ." inside the build folder which works great. However, the build fails once I add the keystroke specific code. This is the error given with crucial information in bold...
Undefined symbols for architecture x86_64:
"_CFRelease", referenced from:
hello_world::number::'lambda'(std::__1::vector<c74::min::atom, hello_world::number::allocator<c74::min>> const&, int)::operator()(std::__1::vector<c74::min::atom, hello_world::number::allocator<c74::min>> const, int) const in min.hello-world.o
"_CGEventCreateKeyboardEvent", referenced from:
hello_world::number::'lambda'(std::__1::vector<c74::min::atom, hello_world::number::allocator<c74::min>> const&, int)::operator()(std::__1::vector<c74::min::atom, hello_world::number::allocator<c74::min>> const, int) const in min.hello-world.o
"_CGEventPost", referenced from:
hello_world::number::'lambda'(std::__1::vector<c74::min::atom, hello_world::number::allocator<c74::min>> const&, int)::operator()(std::__1::vector<c74::min::atom, hello_world::number::allocator<c74::min>> const, int) const in min.hello-world.o
"_CGEventSourceCreate", referenced from:
hello_world::number::'lambda'(std::__1::vector<c74::min::atom, hello_world::number::allocator<c74::min>> const&, int)::operator()(std::__1::vector<c74::min::atom, hello_world::number::allocator<c74::min>> const, int) const in min.hello-world.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Even though it says the issue is for x86_64 I have seen it say arm64 instead as I've tried to get things working. For that reason, I don't think the issue is specific to the architecture, but I could be wrong. Also, Xcode intellisense autocompletes these symbols as I type them and I am able to right click and "Jump to Definition" so there is a clearly a path to these symbols, but apparently the linker or compiler is unaware of that path. To combat this (and an earlier issue), I tried updating the CMakeLists.txt for min.helloworld to the following...
# Copyright 2018 The Min-DevKit Authors. All rights reserved.
# Use of this source code is governed by the MIT License found in the License.md file.
cmake_minimum_required(VERSION 3.0)
set(C74_MIN_API_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../min-api)
include(${C74_MIN_API_DIR}/script/min-pretarget.cmake)
#############################################################
# MAX EXTERNAL
#############################################################
include_directories(
"${C74_INCLUDES}"
"/System/Library/Frameworks/CoreFoundation.framework/Headers"
"/System/Library/Frameworks/ApplicationServices.framework/Headers"
"/System/Library/Frameworks/Carbon.framework/Headers"
"/System/Library/Frameworks/CoreGraphics.framework/Headers"
)
set( SOURCE_FILES
${PROJECT_NAME}.cpp
)
add_library(
${PROJECT_NAME}
MODULE
${SOURCE_FILES}
)
target_link_libraries(
${PROJECT_NAME}
"${C74_LIBS}"
"-framework CoreFoundation"
"-framework ApplicationServices"
"-framework Carbon"
"-framework CoreGraphics"
)
include(${C74_MIN_API_DIR}/script/min-posttarget.cmake)
#############################################################
# UNIT TEST
#############################################################
include(${C74_MIN_API_DIR}/test/min-object-unittest.cmake)
Lastly, in trying to figure this out I was made aware of the 11olsen 11strokes project. That could potentially work for me, but I've already built something specific on Windows and replicating it on Mac would be much less future headache if possible. Also, learning is fun most of the time haha.
Anyway I'm at the point where I don't know how to proceed. It's probably something simple, but this is a bit out of my element so I'm struggling to see it. Any advice or ideas are welcomed and greatly appreciated!
2021 Macbook Pro, M1 Pro, Ventura 13.3.1
cmake version 3.26.0
xcode version 14.3
max version 8.5.3
Let me know if you need anymore info!
Following up on my own post, here's what I did to accomplish the task for those in the future.
First, I decided to eliminate CMake and the min-devkit as variables completely and just write a basic "Command Line Tool" in Xcode to simulate a keystroke. This was a struggle and I went at the problem from many angles over a couple of days. As far I can tell, this isn't possible via C++ or Apple doesn't let any old app simulate keystrokes anymore . In hindsight, I'm thinking it's the latter. I would run my code, it would trigger Security and Privacy settings and I would allow it, but then it still not give the expected output. As far as I can tell you need to submit some kind of enhancement request now. (I tested this on Apple Silicon and Intel Based Macs by the way)
https://developer.apple.com/forums/thread/28605
Second, from my post above I was now aware of 11strokes by olsen. I downloaded it and tested it in Max, works great! That told me it was definitely possible to simulate keystrokes. In addition, I'm not sure now, but at the time I was suspicious that C++ may be part of the issue. Also I noticed 11olsen used the max-sdk instead of the min-devkit so I decided to switch to that. I am not as familiar with C so it took a little while to understand and get a customized basic object up and running.
Once that was ready, I brought in the code to simulate a single keystroke. Immediately, I got undefined symbols error I described in my first post. At this point, I knew the problem was with CMake. I needed to manipulate the CMakeLists.txt file located under max-sdk/source/basics/myCustomExternal to make it aware of the Carbon framework I was using. This is what ended up working for me...
include(${CMAKE_CURRENT_SOURCE_DIR}/../../max-sdk-base/script/max-pretarget.cmake)
#############################################################
# MAX EXTERNAL
#############################################################
include_directories(
"${MAX_SDK_INCLUDES}"
"${MAX_SDK_MSP_INCLUDES}"
"${MAX_SDK_JIT_INCLUDES}"
/Developer/Headers/FlatCarbon
)
FIND_LIBRARY(CARBON_LIBRARY Carbon /System/Library/Frameworks)
MARK_AS_ADVANCED (CARBON_LIBRARY)
SET(EXTRA_LIBS ${CARBON_LIBRARY})
file(GLOB PROJECT_SRC
"*.h"
"*.c"
"*.cpp"
)
add_library(
${PROJECT_NAME}
MODULE
${PROJECT_SRC}
)
target_link_libraries(myCustomExternal(<-your project name) PRIVATE ${EXTRA_LIBS})
include(${CMAKE_CURRENT_SOURCE_DIR}/../../max-sdk-base/script/max-posttarget.cmake)
The way that CMake is implemented for the sdk would take me, a beginner, at least a few days to wrap my head around. I mostly got there by trail and error and using these sources...
https://gitlab.kitware.com/cmake/community/-/wikis/doc/cmake/platform_dependent_issues/HowToUseExistingOSXFrameworks
https://stackoverflow.com/questions/2908640/how-to-add-a-framework-to-cmake
Mainly the second video in this youtube playlist
https://www.youtube.com/playlist?list=PLalVdRk2RC6o5GHu618ARWh0VO0bFlif4
Also, the official CMake documentation and ChatGPT gave me a clue at some point.
Lastly, something that helped me troubleshoot my CMakeLists.txt file was deleting the max-sdk/build folder's contents as I made educated guesses. Then running "cmake -G Xcode .." from the build folder in the terminal and then follow that with "cmake --build ." The first command would usually throw errors if there was something wrong with my CMakeLists.txt.
Overall, I banged my head on this problem for about a week and some of that was definitely my lack of knowledge. However, I think some extra guidance like some common basic CMake manipulations (like adding a framework in my case) would go a long way. Anyway, if this post helps you out with something like this, comment! It would be encouraging to know this helped.