S
Sam (Deleted User)
Guest
GM Version: GMStudio 1.4 and GMStudio 2.x
Target Platform: Mac OS X / macOS
GMStudio 1.4 Download: [macOSExtension.gmz]
GMStudio 2.x Download: [macOSExtension.yyz]
What will this tutorial not be covering?
To be clear, I will NOT be teaching you how to program in C++ or Objective-C, nor will I give you instructions on how to use the GNU G++ compiler. This tutorial only demonstrates how to communicate between C++ and Objective-C. You must already be pretty familiar with how to create C++ extensions for this tutorial to make any sense to you. You should be able to follow this quite well even if you are have never learned Objective-C, due to how little Objective-C code is actually used in this tutorial. If you have programmed C++ extensions before on any desktop platform, and if you know how to install and use the GNU G++ command line compiler, you should be all set! Google how to install and use the GNU G++ compiler on Mac if you must, it actually isn't too hard!
What this tutorial does cover?
One very important reason a user might need to bridge the C++ and Objective-C languages is so he or she might access and manipulate the properties of the GameMaker game's window, via passing the window_handle() as an argument to a C++ function.
This process is much more simple on Windows and Linux, because everything can be done with one programming language, most preferably C++.
But in Mac, all window manipulation is done in the CoCoa API using Objective-C, and none of it can actually be done in pure C++. However, as I mentioned already, you can bridge between them.
So, in this tutorial I will be showing you how to pass GameMaker's window_handle() to a C++ function, which simply will act as a wrapper for a CoCoa function. The CoCoa function will retrieve the game's title bar caption text, based on the game's associated window_handle(). Then, that value is returned and passed over to the C++ side, which is where we will be calling the function from inside GameMaker Studio.
Step 1) The Objective-C code
For starters, here's the Objective-C code, be sure to paste it in an empty file you will name "cocoa.m":
GameMaker's window_handle() on macOS is an NSWindow * pointer. This type can only be accessed in Objective-C, so in order to use it in C++, you need to C-style cast it to (or from) a type that both C++ and Objective-C will recognize as a valid pointer type. In this case, we are casting it from a void * pointer, this way we can ensure that no data is lost, because casting to (or from) a void * pointer does not modify the value of what is being casted at all.
Step 2) The C++ code
Now for the C++ code, which you will need to paste into an empty file named "main.cpp":
Look at the code above, and then notice the line that says this:
This is how you declare a function from your Objective-C code stored in the "cocoa.m" file you created earlier. Notice there is no body to the function, please do not declare the function with a body; instead, give the declaration a semicolon like you normally do at the end of a declaration line. Don't forget to list the function's name, return type, all of its arguments, and the correct argument types, just as shown in the two snippets above. As you see with I what I did in "main.cpp", make sure you put "EXPORTED_FUNCTION", (without the quotes), in front of each function definition you want to call from GameMaker.
Step 3) Building the DYLIB for GMStudio
Now, finally, build the DYLIB. You need your "main.cpp" file and "cocoa.m" file in the same directory.
Run these terminal commands, (make sure you install xCode, homebrew, and g++ first):
Import the DYLIB file as an Included File resource into GMStudio. Then, you may use the following GML to execute the DYLIB's function, (paste it into an empty Script resource, and name the Script resource "cocoa_window_get_caption"):
The above Script resource has no arguments. Always pass the window_handle() as a ty_string, because ty_string is a pointer, and can represent either a char * pointer or a window handle pointer.
You can now see the value the function returns with the following GML, (which can be put in an empty Room resource's Creation Code if you want):
And there you have it. PM me if you run into any problems with this tutorial.
Samuel
Target Platform: Mac OS X / macOS
GMStudio 1.4 Download: [macOSExtension.gmz]
GMStudio 2.x Download: [macOSExtension.yyz]
What will this tutorial not be covering?
To be clear, I will NOT be teaching you how to program in C++ or Objective-C, nor will I give you instructions on how to use the GNU G++ compiler. This tutorial only demonstrates how to communicate between C++ and Objective-C. You must already be pretty familiar with how to create C++ extensions for this tutorial to make any sense to you. You should be able to follow this quite well even if you are have never learned Objective-C, due to how little Objective-C code is actually used in this tutorial. If you have programmed C++ extensions before on any desktop platform, and if you know how to install and use the GNU G++ command line compiler, you should be all set! Google how to install and use the GNU G++ compiler on Mac if you must, it actually isn't too hard!
What this tutorial does cover?
One very important reason a user might need to bridge the C++ and Objective-C languages is so he or she might access and manipulate the properties of the GameMaker game's window, via passing the window_handle() as an argument to a C++ function.
This process is much more simple on Windows and Linux, because everything can be done with one programming language, most preferably C++.
But in Mac, all window manipulation is done in the CoCoa API using Objective-C, and none of it can actually be done in pure C++. However, as I mentioned already, you can bridge between them.
So, in this tutorial I will be showing you how to pass GameMaker's window_handle() to a C++ function, which simply will act as a wrapper for a CoCoa function. The CoCoa function will retrieve the game's title bar caption text, based on the game's associated window_handle(). Then, that value is returned and passed over to the C++ side, which is where we will be calling the function from inside GameMaker Studio.
Step 1) The Objective-C code
For starters, here's the Objective-C code, be sure to paste it in an empty file you will name "cocoa.m":
Code:
#import <Cocoa/Cocoa.h>
const char *cocoa_window_get_caption(void *window_handle)
{
NSWindow *window;
window = (NSWindow *)window_handle;
return [[window title] UTF8String];
}
Step 2) The C++ code
Now for the C++ code, which you will need to paste into an empty file named "main.cpp":
Code:
#include <string>
using std::string;
#define EXPORTED_FUNCTION extern "C" __attribute__((visibility("default")))
extern "C" const char *cocoa_window_get_caption(void *window_handle);
EXPORTED_FUNCTION char *window_get_caption(void *window)
{
static string window_caption;
window_caption = cocoa_window_get_caption(window);
return (char *)window_caption.c_str();
}
Code:
extern "C" const char *cocoa_window_get_caption(void *window_handle);
Step 3) Building the DYLIB for GMStudio
Now, finally, build the DYLIB. You need your "main.cpp" file and "cocoa.m" file in the same directory.
Run these terminal commands, (make sure you install xCode, homebrew, and g++ first):
Code:
cd /Path/To/Your/macOSExtension/
g++ -c -std=c++11 main.cpp -m64
g++ -c -ObjC cocoa.m -m64
g++ main.o cocoa.o -o macOSExtension.dylib -shared -framework Cocoa -m64
Code:
var CoCoa_result;
CoCoa_result = external_call(external_define("macOSExtension.dylib", "window_get_caption", dll_cdecl, ty_string, 1, ty_string), window_handle());
external_free("macOSExtension.dylib");
return CoCoa_result;
You can now see the value the function returns with the following GML, (which can be put in an empty Room resource's Creation Code if you want):
Code:
show_message_async(cocoa_window_get_caption());
Samuel
Last edited by a moderator: