main.cpp (4954B)
1 /* ReaPack: Package manager for REAPER 2 * Copyright (C) 2015-2025 Christian Fillion 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU Lesser General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU Lesser General Public License for more details. 13 * 14 * You should have received a copy of the GNU Lesser General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 #include "api.hpp" 19 #include "buildinfo.hpp" 20 #include "errors.hpp" 21 #include "filesystem.hpp" 22 #include "menu.hpp" 23 #include "reapack.hpp" 24 #include "win32.hpp" 25 26 #define REAPERAPI_IMPLEMENT 27 #include <reaper_plugin_functions.h> 28 #include <reaper_plugin_secrets.h> 29 30 #define REQUIRED_API(name) {reinterpret_cast<void **>(&name), #name, true} 31 #define OPTIONAL_API(name) {reinterpret_cast<void **>(&name), #name, false} 32 33 static bool loadAPI(void *(*getFunc)(const char *)) 34 { 35 struct ApiFunc { void **ptr; const char *name; bool required; }; 36 37 const ApiFunc funcs[] { 38 REQUIRED_API(Splash_GetWnd), // v4.7 39 40 REQUIRED_API(AddExtensionsMainMenu), 41 REQUIRED_API(EnsureNotCompletelyOffscreen), 42 REQUIRED_API(GetAppVersion), 43 REQUIRED_API(GetResourcePath), 44 REQUIRED_API(NamedCommandLookup), // v3.1415 45 REQUIRED_API(plugin_register), 46 REQUIRED_API(ShowMessageBox), 47 48 OPTIONAL_API(AddRemoveReaScript), // v5.12 49 50 #ifdef __APPLE__ 51 REQUIRED_API(ListView_HeaderHitTest), // v5.1, undocumented 52 #endif 53 }; 54 55 for(const ApiFunc &func : funcs) { 56 *func.ptr = getFunc(func.name); 57 58 if(func.required && *func.ptr == nullptr) { 59 Win32::messageBox(Splash_GetWnd ? Splash_GetWnd() : nullptr, String::format( 60 "ReaPack v%s is incompatible with this version of REAPER.\n\n" 61 "(Unable to import the following API function: %s)", 62 REAPACK_VERSION, func.name 63 ).c_str(), "ReaPack: Missing REAPER feature", MB_OK); 64 65 return false; 66 } 67 } 68 69 return true; 70 } 71 72 static bool commandHook(const int id, const int flag) 73 { 74 return g_reapack->actions()->run(id); 75 } 76 77 static void menuHook(const char *name, HMENU handle, const int f) 78 { 79 if(strcmp(name, "Main extensions") || f != 0) 80 return; 81 82 Menu menu = Menu(handle).addMenu("Rea&Pack"); 83 menu.addAction("&Synchronize packages", "_REAPACK_SYNC"); 84 menu.addAction("&Browse packages...", "_REAPACK_BROWSE"); 85 menu.addAction("&Import repositories...", "_REAPACK_IMPORT"); 86 menu.addAction("&Manage repositories...", "_REAPACK_MANAGE"); 87 menu.addSeparator(); 88 menu.addAction(String::format("&About ReaPack v%s", REAPACK_VERSION), "_REAPACK_ABOUT"); 89 } 90 91 static bool checkLocation(REAPER_PLUGIN_HINSTANCE module) 92 { 93 // using FS::canonical is required on macOS Catalina and newer, 94 // whose dladdr automatically resolves symbolic links from the module's path 95 96 Path expected; 97 expected.append(ReaPack::resourcePath()); 98 expected.append("UserPlugins"); 99 expected.append(REAPACK_FILENAME); 100 expected = FS::canonical(expected); 101 102 #ifdef _WIN32 103 Win32::char_type self[MAX_PATH]{}; 104 GetModuleFileName(module, self, static_cast<DWORD>(std::size(self))); 105 const Path current(Win32::narrow(self)); 106 #else 107 Dl_info info{}; 108 dladdr(reinterpret_cast<const void *>(&checkLocation), &info); 109 const Path ¤t = FS::canonical({info.dli_fname}); 110 #endif 111 112 if(current == expected) 113 return true; 114 115 Win32::messageBox(Splash_GetWnd(), String::format( 116 "ReaPack was not loaded from the standard extension path" 117 " or its filename was altered.\n" 118 "Move or rename it to the expected location and retry.\n\n" 119 "Current: %s\n\nExpected: %s", 120 current.join().c_str(), expected.join().c_str() 121 ).c_str(), "ReaPack: Installation path mismatch", MB_OK); 122 123 return false; 124 } 125 126 extern "C" REAPER_PLUGIN_DLL_EXPORT int REAPER_PLUGIN_ENTRYPOINT( 127 REAPER_PLUGIN_HINSTANCE instance, reaper_plugin_info_t *rec) 128 { 129 if(!rec) { 130 plugin_register("-hookcommand", reinterpret_cast<void *>(commandHook)); 131 plugin_register("-hookcustommenu", reinterpret_cast<void *>(menuHook)); 132 133 delete g_reapack; 134 135 return 0; 136 } 137 else if(rec->caller_version != REAPER_PLUGIN_VERSION 138 || !loadAPI(rec->GetFunc) || !checkLocation(instance)) 139 return 0; 140 141 new ReaPack(instance, rec->hwnd_main); 142 143 plugin_register("hookcommand", reinterpret_cast<void *>(commandHook)); 144 plugin_register("hookcustommenu", reinterpret_cast<void *>(menuHook)); 145 146 AddExtensionsMainMenu(); 147 148 return 1; 149 } 150 151 #ifndef _WIN32 152 # include "resource.hpp" 153 # include <swell/swell-dlggen.h> 154 # include "resource.rc_mac_dlg" 155 # include <swell/swell-menugen.h> 156 # include "resource.rc_mac_menu" 157 #endif