reapack

Package manager for REAPER
Log | Files | Refs | Submodules | README | LICENSE

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 &current = 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