Old 10-17-2018, 01:41 AM   #1
juliansader
Human being with feelings
 
Join Date: Jul 2009
Posts: 3,714
Default How to: SWELL_PROVIDED_BY_APP and Linux?

I am struggling to get SWELL_PROVIDED_BY_APP to work in Linux.

Here is a very minimal .cpp for an extension that provides the function "JS_Test":
Code:
// WARNING: Apparently, the file names of user extensions MUST start with "reaper_".

#define REAPERAPI_IMPLEMENT
#define REAPERAPI_MINIMAL // Only load the API functions #define'd by REAPERAPI_WANT_...
#define REAPERAPI_WANT_plugin_register
#define REAPERAPI_WANT_GetMainHwnd


//#define SWELL_PROVIDED_BY_APP


// reaper_plugin_functions.h #include's reaper_plugin.h, which in turn #include's either windows.h or swell.h, depending on platform.
// So probably only necessary to #include reaper_plugins_functions.h
#include "reaper_plugin_functions.h"

#include <cstdio>
#include <cstring>


// Example of a function that will be exported to the ReaScript API

int JS_Test()
{
	RECT r{0, 0, 0, 0};
	HWND w = GetMainHwnd();
	GetWindowRect(w, &r); // Uncomment to test SWELL_PROVIDED_BY_APP
	return r.left;
}



// Apparently, under the hood, REAPER converts all ReaScript API functions to this standard format:
// void* func(void** arglist, int numparams)
// The names and types of the parameters and return values that the user see in the IDE are registered by plugin_register(APIdef_...)
// So all functions must either be in this format, or get a wrapper function such as this:
static void* __vararg_JS_Test(void** arglist, int numparms)
{
	return (void*)(intptr_t)JS_Test();
}


// (The following struct and macro are derived from SWS.)
// Struct to store info such as function name and help text for each function that the extensions intends to expose as API.
// This info will be used by REAPER's plugin_register functions to register the functions.
struct structAPIdef
{
	void* func; // pointer to the function that other extensions use
	const char* func_name;
	void* func_vararg; // pointer to the wrapper function that ReaScript API calls
	const char* regkey_vararg; // "APIvararg_funcName" - for
	const char* regkey_func; // "API_funcName"
	const char* regkey_def; // "APIdef_funcName"
	const char* defstring; // \0-separated string for APIdef... Will be concatenated and assigned while registering function
};


// Macro to construct a comma-separated list of all the variants of a function name that are required for plugin_register(), in the order required by structAPIdef in which these variants are stored.
// APIFUNC(funcName) becomes (void*)funcName, "funcName", (void*)__vararg_funcName, "APIvararg_funcName", "API_funcName", "APIdef_funcName"
#define APIFUNC(x)  (void*)x,#x,(void*)__vararg_ ## x,"APIvararg_" #x "","API_" #x "","APIdef_" #x ""


// Array of function info structs
// Each function that will be registered must be listed here.
structAPIdef arrayAPIdefs[] =
{
	{ APIFUNC(JS_Test), "int\0\0\0Test\0", }
};


// REAPER calls the "REAPER_PLUGIN_ENTRYPOINT" function to load or unload plugins, so all plugins must include this function.
extern "C" REAPER_PLUGIN_DLL_EXPORT int REAPER_PLUGIN_ENTRYPOINT(REAPER_PLUGIN_HINSTANCE hInstance, reaper_plugin_info_t *rec)
{
	// If rec !- nil, the extension is being loaded.  If rec == nil, the extension is being UNloaded.
	if (rec)
	{

		if (REAPERAPI_LoadAPI(rec->GetFunc) != 0)
		{
			fprintf(stderr, "Unable to import API functions.\n");
			return 0;
		}
		else
		{
			// functions imported, continue initing plugin...

			for (structAPIdef& f : arrayAPIdefs)
			{
				// Each function must be registered in three ways:
				// APIdef_... provides for converting parameters to vararg format, and for documentation in the auto-generated REAPER API header and ReaScript documentation.
				plugin_register(f.regkey_def, (void*)f.defstring);
				// API_... for exposing to other extensions, and for IDE to recognize and color functions while typing .
				plugin_register(f.regkey_func, f.func);
				// APIvarag_... for exporting to ReaScript API.
				plugin_register(f.regkey_vararg, f.func_vararg);
			}
			return 1; // I'm not sure the relevance is of REAPER_PLUGIN_ENTRYPOINT's return values.
		}
	}

	// Not sure what clean-up needs to be done when REAPER quits.
	// Perhaps freeing allocated memory blocks, or mouse cursors created by CreateCursor?
	else
	{
		return 0;
	}
}
If I comment out line 9: "#define SWELL_PROVIDED_BY_APP" and link the file to swell myself, the extension loads and the test function works:
Code:
g++ -fPIC -shared -I"./WDL/swell/" Minimal.cpp \
./WDL/swell/swell-appstub-generic.cpp   ./WDL/swell/swell-kb-generic.cpp \
./WDL/swell/swell-dlg-generic.cpp       ./WDL/swell/swell-menu-generic.cpp \
./WDL/swell/swell-gdi-generic.cpp       ./WDL/swell/swell-miscdlg-generic.cpp \
./WDL/swell/swell-gdi-lice.cpp          ./WDL/swell/swell-misc-generic.cpp \
./WDL/swell/swell-generic-gdk.cpp \
./WDL/swell/swell-generic-headless.cpp  ./WDL/swell/swell.cpp \
./WDL/swell/swell-ini.cpp               ./WDL/swell/swell-wnd-generic.cpp \
-std=c++11 -ldl -lXi -lX11 -lfreetype `pkg-config --cflags --libs gtk+-3.0` \
-o reaper_minimal32.so
However, if I uncomment the #define, and only link with swell-modstub-generic.cpp, the extension fails to load, with an error "undefined symbol: GetWindowRect":
Code:
g++ -fPIC -shared -I"./WDL/swell/" Minimal.cpp \
./WDL/swell/swell-modstub-generic.cpp \
-std=c++11 \
-o reaper_minimal32.so
juliansader is offline   Reply With Quote
Old 10-17-2018, 02:16 AM   #2
cfillion
Human being with feelings
 
cfillion's Avatar
 
Join Date: May 2015
Location: Québec, Canada
Posts: 4,937
Default

swell-modstub-generic.cpp must be built with SWELL_PROVIDED_BY_APP set. Otherwise it does absolutely nothing (there's a big ifdef in it).

You can define it for every source files being built using the compiler command line: -DSWELL_PROVIDED_BY_APP.

Last edited by cfillion; 10-17-2018 at 02:23 AM.
cfillion is offline   Reply With Quote
Old 10-18-2018, 01:11 PM   #3
Justin
Administrator
 
Justin's Avatar
 
Join Date: Jan 2005
Location: NYC
Posts: 15,721
Default

Quote:
Originally Posted by cfillion View Post
You can define it for every source files being built using the compiler command line: -DSWELL_PROVIDED_BY_APP.
This is very much recommended. Also, for best compatibility you should not build with swell directly, and just use that with swell-modstub-generic.cpp in order to use REAPER's (or the end-user's customized) libSwell.so. If you do this, you can also drop the pkg-config and -lX11 stuff, as that'll all be handled for you.
Justin is offline   Reply With Quote
Old 10-18-2018, 01:16 PM   #4
juliansader
Human being with feelings
 
Join Date: Jul 2009
Posts: 3,714
Default

Quote:
Originally Posted by cfillion View Post
swell-modstub-generic.cpp must be built with SWELL_PROVIDED_BY_APP set. Otherwise it does absolutely nothing (there's a big ifdef in it).
Ah, got it. The new version of js_ReaScriptAPI now loads properly in Linux.
juliansader is offline   Reply With Quote
Old 04-21-2019, 08:32 AM   #5
nofish
Human being with feelings
 
nofish's Avatar
 
Join Date: Oct 2007
Location: home is where the heart is
Posts: 12,096
Default

Sorry for reviving this, as I'm looking into crossplatform development myself currently...

What's the difference between swell-modstub-generic.cpp and swell-modstub.mm, i.e. when to use which? (seeing that OSX uses swell-modstub.mm on OSX).
nofish is offline   Reply With Quote
Old 04-21-2019, 08:51 AM   #6
cfillion
Human being with feelings
 
cfillion's Avatar
 
Join Date: May 2015
Location: Québec, Canada
Posts: 4,937
Default

Files with the -generic suffix are the Linux port of SWELL. Objective-C (.mm) files are for macOS.
cfillion is offline   Reply With Quote
Old 04-21-2019, 09:07 AM   #7
nofish
Human being with feelings
 
nofish's Avatar
 
Join Date: Oct 2007
Location: home is where the heart is
Posts: 12,096
Default

Thanks.
nofish is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -7. The time now is 08:58 AM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.