refactor. add LammpsWrapper class to hide plugin defines and interface changes.

This commit is contained in:
Axel Kohlmeyer
2023-08-02 01:10:49 -04:00
parent 310ede65d9
commit 8a53c66bef
7 changed files with 327 additions and 195 deletions

View File

@ -76,8 +76,9 @@ set(PROJECT_SOURCES
lammpsgui.cpp
lammpsgui.h
lammpsgui.ui
lammpsrunner.cpp
lammpsrunner.h
lammpswrapper.cpp
lammpswrapper.h
preferences.cpp
preferences.h
stdcapture.cpp

View File

@ -20,6 +20,7 @@
#include "stdcapture.h"
#include "ui_lammpsgui.h"
#include <QCoreApplication>
#include <QDesktopServices>
#include <QFileDialog>
#include <QFileInfo>
@ -28,21 +29,17 @@
#include <QMessageBox>
#include <QPlainTextEdit>
#include <QProgressBar>
#include <QSettings>
#include <QShortcut>
#include <QStatusBar>
#include <QTextStream>
#include <QThread>
#include <QTimer>
#include <QUrl>
#include <cstring>
#include <string>
#if defined(LAMMPS_GUI_USE_PLUGIN)
#include "liblammpsplugin.h"
#else
#include "library.h"
#endif
#if defined(_OPENMP)
#include <cstdlib>
#include <omp.h>
@ -54,6 +51,8 @@
#include <unistd.h>
#endif
static const QString blank(" ");
// duplicate string
static char *mystrdup(const std::string &text)
{
@ -65,8 +64,7 @@ static char *mystrdup(const std::string &text)
LammpsGui::LammpsGui(QWidget *parent, const char *filename) :
QMainWindow(parent), ui(new Ui::LammpsGui), highlighter(nullptr), capturer(nullptr),
status(nullptr), logwindow(nullptr), imagewindow(nullptr), logupdater(nullptr),
dirstatus(nullptr), progress(nullptr), prefdialog(nullptr), lammps_handle(nullptr),
plugin_handle(nullptr), plugin_path(nullptr), is_running(false)
dirstatus(nullptr), progress(nullptr), prefdialog(nullptr)
{
ui->setupUi(this);
this->setCentralWidget(ui->textEdit);
@ -77,9 +75,16 @@ LammpsGui::LammpsGui(QWidget *parent, const char *filename) :
current_dir = QDir(".").absolutePath();
recent_files.clear();
QCoreApplication::setOrganizationName("The LAMMPS Developers");
QCoreApplication::setOrganizationDomain("lammps.org");
QCoreApplication::setApplicationName("LAMMPS GUI");
// restorge and initialize settings
QSettings settings;
#if defined(_OPENMP)
// use maximum number of available threads unless OMP_NUM_THREADS was set
nthreads = omp_get_max_threads();
nthreads = settings.value("nthreads", omp_get_max_threads()).toInt();
#if _WIN32
if (!getenv("OMP_NUM_THREADS")) {
_putenv_s("OMP_NUM_THREADS", std::to_string(nthreads).c_str());
@ -88,7 +93,7 @@ LammpsGui::LammpsGui(QWidget *parent, const char *filename) :
setenv("OMP_NUM_THREADS", std::to_string(nthreads).c_str(), 0);
#endif
#else
nthreads = 1;
nthreads = settings.value("nthreads", 1).toInt();
#endif
const char *tmpdir = getenv("TMPDIR");
@ -100,14 +105,14 @@ LammpsGui::LammpsGui(QWidget *parent, const char *filename) :
#else
if (!tmpdir) tmpdir = "/tmp";
#endif
temp_dir = tmpdir;
settings.setValue("tempdir", QString(tmpdir));
lammps_args.clear();
lammps_args.push_back(mystrdup("LAMMPS-GUI"));
lammps_args.push_back(mystrdup("-log"));
lammps_args.push_back(mystrdup("none"));
lammps_args.push_back(mystrdup("-suffix"));
lammps_args.push_back(mystrdup("omp"));
// lammps_args.push_back(mystrdup("-suffix"));
// lammps_args.push_back(mystrdup("omp"));
setWindowIcon(QIcon(":/lammps-icon-128x128.png"));
#if (__APPLE__)
@ -158,24 +163,24 @@ LammpsGui::LammpsGui(QWidget *parent, const char *filename) :
dirstatus->show();
ui->statusbar->addWidget(progress);
#if defined(LAMMPS_GUI_USE_PLUGIN)
liblammpsplugin_t *lammps = nullptr;
for (const auto libfile : {"liblammps.so", "./liblammps.so", "liblammps.dylib",
"./liblammps.dylib", "liblammps.dll"}) {
if (!lammps) lammps = liblammpsplugin_load(libfile);
if (lammps) {
plugin_path.clear();
std::string deffile = settings.value("plugin_path", "liblammps.so").toString().toStdString();
for (const char *libfile : {deffile.c_str(), "./liblammps.so", "liblammps.dylib",
"./liblammps.dylib", "liblammps.dll"}) {
if (lammps.load_lib(libfile)) {
plugin_path = libfile;
settings.setValue("plugin_path", QString(libfile));
break;
}
}
bool do_exit = !lammps || (lammps && lammps->abiversion != LAMMPSPLUGIN_ABI_VERSION);
if (!lammps) QMessageBox::critical(this, "Error", "Cannot open LAMMPS shared library file");
if (lammps && (lammps->abiversion != LAMMPSPLUGIN_ABI_VERSION))
QMessageBox::critical(this, "Warning",
"ERROR: LAMMPS lib plugin ABI version does not match");
plugin_handle = lammps;
if (do_exit) exit(1);
#endif
if (plugin_path.empty()) {
// none of the plugin paths could load, remove key
settings.remove("plugin_path");
QMessageBox::critical(this, "Error", "Cannot open LAMMPS shared library file");
// QCoreApplication::quit();
exit(1);
}
if (filename) {
open_file(filename);
@ -200,12 +205,7 @@ void LammpsGui::new_document()
current_file.clear();
ui->textEdit->document()->setPlainText(QString());
#if defined(LAMMPS_GUI_USE_PLUGIN)
if (lammps_handle) ((liblammpsplugin_t *)plugin_handle)->close(lammps_handle);
#else
if (lammps_handle) lammps_close(lammps_handle);
#endif
lammps_handle = nullptr;
lammps.close();
setWindowTitle(QString("LAMMPS-GUI - *unknown*"));
}
@ -300,22 +300,9 @@ void LammpsGui::save_as()
void LammpsGui::quit()
{
#if defined(LAMMPS_GUI_USE_PLUGIN)
if (lammps_handle) {
liblammpsplugin_t *lammps = (liblammpsplugin_t *)plugin_handle;
lammps->close(lammps_handle);
lammps->mpi_finalize();
lammps->kokkos_finalize();
lammps->python_finalize();
}
#else
if (lammps_handle) {
lammps_close(lammps_handle);
lammps_mpi_finalize();
lammps_kokkos_finalize();
lammps_python_finalize();
}
#endif
lammps.close();
lammps.finalize();
if (ui->textEdit->document()->isModified()) {
QMessageBox msg;
msg.setWindowTitle("Unsaved Changes");
@ -374,11 +361,7 @@ void LammpsGui::redo()
void LammpsGui::stop_run()
{
#if defined(LAMMPS_GUI_USE_PLUGIN)
((liblammpsplugin_t *)plugin_handle)->force_timeout(lammps_handle);
#else
lammps_force_timeout(lammps_handle);
#endif
lammps.force_timeout();
}
void LammpsGui::logupdate()
@ -386,22 +369,12 @@ void LammpsGui::logupdate()
double t_elapsed, t_remain, t_total;
int completed = 1000;
#if defined(LAMMPS_GUI_USE_PLUGIN)
liblammpsplugin_t *lammps = (liblammpsplugin_t *)plugin_handle;
if (lammps->is_running(lammps_handle)) {
t_elapsed = lammps->get_thermo(lammps_handle, "cpu");
t_remain = lammps->get_thermo(lammps_handle, "cpuremain");
if (is_running) {
t_elapsed = lammps.get_thermo("cpu");
t_remain = lammps.get_thermo("cpuremain");
t_total = t_elapsed + t_remain + 1.0e-10;
completed = t_elapsed / t_total * 1000.0;
}
#else
if (lammps_is_running(lammps_handle)) {
t_elapsed = lammps_get_thermo(lammps_handle, "cpu");
t_remain = lammps_get_thermo(lammps_handle, "cpuremain");
t_total = t_elapsed + t_remain + 1.0e-10;
completed = t_elapsed / t_total * 1000.0;
}
#endif
progress->setValue(completed);
if (logwindow) {
@ -440,18 +413,10 @@ void LammpsGui::run_done()
constexpr int BUFLEN = 1024;
char errorbuf[BUFLEN];
#if defined(LAMMPS_GUI_USE_PLUGIN)
liblammpsplugin_t *lammps = (liblammpsplugin_t *)plugin_handle;
if (lammps->has_error(lammps_handle)) {
lammps->get_last_error_message(lammps_handle, errorbuf, BUFLEN);
if (lammps.has_error()) {
lammps.get_last_error_message(errorbuf, BUFLEN);
success = false;
}
#else
if (lammps_has_error(lammps_handle)) {
lammps_get_last_error_message(lammps_handle, errorbuf, BUFLEN);
success = false;
}
#endif
if (success) {
status->setText("Ready.");
@ -460,7 +425,6 @@ void LammpsGui::run_done()
QMessageBox::critical(this, "LAMMPS-GUI Error",
QString("Error running LAMMPS:\n\n") + errorbuf);
}
is_running = false;
progress->hide();
dirstatus->show();
}
@ -473,7 +437,7 @@ void LammpsGui::run_buffer()
status->setText(QString("Running LAMMPS with %1 thread(s)...").arg(nthreads));
status->repaint();
start_lammps();
if (!lammps_handle) return;
if (!lammps.is_open()) return;
clear();
capturer->BeginCapture();
@ -481,7 +445,7 @@ void LammpsGui::run_buffer()
is_running = true;
LammpsRunner *runner = new LammpsRunner(this);
runner->setup_run(lammps_handle, input, plugin_handle);
runner->setup_run(&lammps, input);
connect(runner, &LammpsRunner::resultReady, this, &LammpsGui::run_done);
connect(runner, &LammpsRunner::finished, runner, &QObject::deleteLater);
runner->start();
@ -516,43 +480,39 @@ void LammpsGui::run_buffer()
void LammpsGui::view_image()
{
// LAMMPS is not re-entrant, so we can only query LAMMPS when it is not running
if (!is_running) {
if (!lammps.is_running()) {
start_lammps();
int box = 0;
#if defined(LAMMPS_GUI_USE_PLUGIN)
box = ((liblammpsplugin_t *)plugin_handle)->extract_setting(lammps_handle, "box_exists");
#else
box = lammps_extract_setting(lammps_handle, "box_exist");
#endif
if (!box) {
if (!lammps.extract_setting("box_exists")) {
QMessageBox::warning(this, "ImageViewer Error",
"Cannot create snapshot image without a system box");
return;
}
std::string dumpcmd = "write_dump all image '";
QString dumpfile = temp_dir;
QSettings settings;
QString dumpcmd = "write_dump all image ";
QString dumpfile = settings.value("tempdir").toString();
#if defined(_WIN32)
dumpfile += '\\';
#else
dumpfile += '/';
#endif
dumpfile += current_file + ".ppm";
dumpcmd += dumpfile;
dumpcmd += dumpfile.toStdString() + "' type type size 800 600";
settings.beginGroup("snapshot");
dumpcmd += blank + settings.value("color", "type").toString();
dumpcmd += blank + settings.value("diameter", "type").toString();
dumpcmd += QString(" size ") + settings.value("xsize", "800").toString();
dumpcmd += blank + settings.value("ysize", "600").toString();
dumpcmd += QString(" zoom ") + settings.value("zoom", "1.0").toString();
settings.endGroup();
#if defined(LAMMPS_GUI_USE_PLUGIN)
((liblammpsplugin_t *)plugin_handle)->command(lammps_handle, dumpcmd.c_str());
#else
lammps_command(lammps_handle, dumpcmd.c_str());
#endif
lammps.command(dumpcmd.toLocal8Bit());
imagewindow = new ImageViewer(dumpfile);
#if 0
#if defined(_WIN32)
_unlink(dumpfile.toLocal8Bit());
#else
unlink(dumpfile.toLocal8Bit());
#endif
#endif
} else {
QMessageBox::warning(this, "ImageViewer Error",
@ -564,39 +524,29 @@ void LammpsGui::view_image()
void LammpsGui::clear()
{
if (lammps_handle) {
#if defined(LAMMPS_GUI_USE_PLUGIN)
((liblammpsplugin_t *)plugin_handle)->command(lammps_handle, "clear");
#else
lammps_command(lammps_handle, "clear");
#endif
}
if (!lammps.is_open()) lammps.command("clear");
ui->textEdit->moveCursor(QTextCursor::Start, QTextCursor::MoveAnchor);
}
void LammpsGui::about()
{
std::string version = "This is LAMMPS-GUI version " LAMMPS_GUI_VERSION;
#if defined(LAMMPS_GUI_USE_PLUGIN)
version += " - LAMMPS linked dynamically";
if (plugin_path) {
version += " from file ";
version += plugin_path;
if (lammps.has_plugin()) {
version += " - LAMMPS linked dynamically";
if (!plugin_path.empty()) {
version += " from file ";
version += plugin_path;
}
} else {
version += " - LAMMPS linked statically";
}
#else
version += " - LAMMPS linked statically";
#endif
std::string info = "LAMMPS is currently running. LAMMPS config info not available.";
// LAMMPS is not re-entrant, so we can only query LAMMPS when it is not running
if (!is_running) {
if (!lammps.is_running()) {
start_lammps();
capturer->BeginCapture();
#if defined(LAMMPS_GUI_USE_PLUGIN)
((liblammpsplugin_t *)plugin_handle)->commands(lammps_handle, "info config");
#else
lammps_command(lammps_handle, "info config");
#endif
lammps.command("info config");
capturer->EndCapture();
info = capturer->GetCapture();
auto start = info.find("LAMMPS version:");
@ -675,33 +625,18 @@ void LammpsGui::preferences()
void LammpsGui::start_lammps()
{
char **args = lammps_args.data();
int nargs = lammps_args.size();
int narg = lammps_args.size();
#if defined(LAMMPS_GUI_USE_PLUGIN)
liblammpsplugin_t *lammps = (liblammpsplugin_t *)plugin_handle;
lammps.open(narg, args);
// Create LAMMPS instance if not already present
if (!lammps_handle) lammps_handle = lammps->open_no_mpi(nargs, args, nullptr);
if (lammps->has_error(lammps_handle)) {
if (lammps.has_error()) {
constexpr int BUFLEN = 1024;
char errorbuf[BUFLEN];
lammps->get_last_error_message(lammps_handle, errorbuf, BUFLEN);
lammps.get_last_error_message(errorbuf, BUFLEN);
QMessageBox::critical(this, "LAMMPS-GUI Error",
QString("Error launching LAMMPS:\n\n") + errorbuf);
}
#else
// Create LAMMPS instance if not already present
if (!lammps_handle) lammps_handle = lammps_open_no_mpi(nargs, args, nullptr);
if (lammps_has_error(lammps_handle)) {
constexpr int BUFLEN = 1024;
char errorbuf[BUFLEN];
lammps_get_last_error_message(lammps_handle, errorbuf, BUFLEN);
QMessageBox::critical(this, "LAMMPS-GUI Error",
QString("Error launching LAMMPS:\n\n") + errorbuf);
}
#endif
}
// Local Variables:

View File

@ -15,9 +15,13 @@
#define LAMMPSGUI_H
#include <QMainWindow>
#include <QList>
#include <QString>
#include <vector>
#include "lammpswrapper.h"
// forward declarations
QT_BEGIN_NAMESPACE
@ -87,13 +91,11 @@ private:
QString current_file;
QString current_dir;
QString temp_dir;
void *lammps_handle;
void *plugin_handle;
const char *plugin_path;
LammpsWrapper lammps;
std::string plugin_path;
bool is_running;
int nthreads;
std::vector<char *> recent_files;
QList<QString> recent_files;
std::vector<char *> lammps_args;
};

View File

@ -1,44 +0,0 @@
/* ----------------------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://www.lammps.org/, Sandia National Laboratories
LAMMPS development team: developers@lammps.org
Copyright (2003) Sandia Corporation. Under the terms of Contract
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
certain rights in this software. This software is distributed under
the GNU General Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
#include "lammpsrunner.h"
#if defined(LAMMPS_GUI_USE_PLUGIN)
#include "liblammpsplugin.h"
#else
#include "library.h"
#endif
#include <cstdio>
LammpsRunner::LammpsRunner(QObject *parent) :
QThread(parent), handle(nullptr), plugin(nullptr), input(nullptr)
{
}
void LammpsRunner::run()
{
if (handle) {
#if defined(LAMMPS_GUI_USE_PLUGIN)
liblammpsplugin_t *lammps = (liblammpsplugin_t *)plugin;
lammps->commands_string(handle, input);
#else
lammps_commands_string(handle, input);
#endif
}
emit resultReady();
}
// Local Variables:
// c-basic-offset: 4
// End:

View File

@ -20,17 +20,21 @@ class LammpsRunner : public QThread {
Q_OBJECT
public:
LammpsRunner(QObject *parent = nullptr);
LammpsRunner(QObject *parent = nullptr) : QThread(parent), lammps(nullptr), input(nullptr) {}
~LammpsRunner() = default;
// execute LAMMPS run in runner thread
void run() override;
public:
// execute LAMMPS in runner thread
void run() override
{
lammps->commands_string(input);
emit resultReady();
}
// transfer info to worker thread
void setup_run(void *_handle, const char *_input, void *_plugin = nullptr)
void setup_run(LammpsWrapper *_lammps, const char *_input)
{
handle = _handle;
plugin = _plugin;
lammps = _lammps;
input = _input;
}
@ -38,7 +42,7 @@ signals:
void resultReady();
private:
void *handle, *plugin;
LammpsWrapper *lammps;
const char *input;
};

View File

@ -0,0 +1,185 @@
/* ----------------------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://www.lammps.org/, Sandia National Laboratories
LAMMPS development team: developers@lammps.org
Copyright (2003) Sandia Corporation. Under the terms of Contract
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
certain rights in this software. This software is distributed under
the GNU General Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
#include "lammpswrapper.h"
#if defined(LAMMPS_GUI_USE_PLUGIN)
#include "liblammpsplugin.h"
#else
#include "library.h"
#endif
LammpsWrapper::LammpsWrapper() :
lammps_handle(nullptr), plugin_handle(nullptr)
{
}
void LammpsWrapper::open(int narg, char **args)
{
if (!lammps_handle) {
#if defined(LAMMPS_GUI_USE_PLUGIN)
lammps_handle = ((liblammpsplugin_t *)plugin_handle)->open_no_mpi(narg, args, nullptr);
#else
lammps_handle = lammps_open_no_mpi(narg, args, nullptr);
#endif
}
}
int LammpsWrapper::extract_setting(const char *keyword)
{
int val = 0;
if (lammps_handle) {
#if defined(LAMMPS_GUI_USE_PLUGIN)
val = ((liblammpsplugin_t *)plugin_handle)->extract_setting(lammps_handle, keyword);
#else
val = lammps_extract_setting(lammps_handle, keyword);
#endif
}
return val;
}
double LammpsWrapper::get_thermo(const char *keyword)
{
double val = 0.0;
if (lammps_handle) {
#if defined(LAMMPS_GUI_USE_PLUGIN)
val = ((liblammpsplugin_t *)plugin_handle)->get_thermo(lammps_handle, keyword);
#else
val = lammps_get_thermo(lammps_handle, keyword);
#endif
}
return val;
}
bool LammpsWrapper::is_running()
{
int val = 0;
if (lammps_handle) {
#if defined(LAMMPS_GUI_USE_PLUGIN)
val = ((liblammpsplugin_t *)plugin_handle)->is_running(lammps_handle);
#else
val = lammps_is_running(lammps_handle);
#endif
}
return val != 0;
}
void LammpsWrapper::command(const char *input)
{
if (lammps_handle) {
#if defined(LAMMPS_GUI_USE_PLUGIN)
((liblammpsplugin_t *)plugin_handle)->command(lammps_handle, input);
#else
lammps_command(lammps_handle, input);
#endif
}
}
void LammpsWrapper::commands_string(const char *input)
{
if (lammps_handle) {
#if defined(LAMMPS_GUI_USE_PLUGIN)
((liblammpsplugin_t *)plugin_handle)->commands_string(lammps_handle, input);
#else
lammps_commands_string(lammps_handle, input);
#endif
}
}
// may be called with null handle. returns global error then.
bool LammpsWrapper::has_error() const
{
#if defined(LAMMPS_GUI_USE_PLUGIN)
return ((liblammpsplugin_t *)plugin_handle)->has_error(lammps_handle) != 0;
#else
return lammps_has_error(lammps_handle) != 0;
#endif
}
int LammpsWrapper::get_last_error_message(char *buf, int buflen)
{
#if defined(LAMMPS_GUI_USE_PLUGIN)
return ((liblammpsplugin_t *)plugin_handle)->get_last_error_message(lammps_handle, buf, buflen);
#else
return lammps_get_last_error_message(lammps_handle, buf, buflen);
#endif
}
void LammpsWrapper::force_timeout()
{
#if defined(LAMMPS_GUI_USE_PLUGIN)
((liblammpsplugin_t *)plugin_handle)->force_timeout(lammps_handle);
#else
lammps_force_timeout(lammps_handle);
#endif
}
void LammpsWrapper::close()
{
#if defined(LAMMPS_GUI_USE_PLUGIN)
if (lammps_handle) ((liblammpsplugin_t *)plugin_handle)->close(lammps_handle);
#else
if (lammps_handle) lammps_close(lammps_handle);
#endif
lammps_handle = nullptr;
}
void LammpsWrapper::finalize()
{
#if defined(LAMMPS_GUI_USE_PLUGIN)
if (lammps_handle) {
liblammpsplugin_t *lammps = (liblammpsplugin_t *)plugin_handle;
lammps->close(lammps_handle);
lammps->mpi_finalize();
lammps->kokkos_finalize();
lammps->python_finalize();
}
#else
if (lammps_handle) {
lammps_close(lammps_handle);
lammps_mpi_finalize();
lammps_kokkos_finalize();
lammps_python_finalize();
}
#endif
}
#if defined(LAMMPS_GUI_USE_PLUGIN)
bool LammpsWrapper::has_plugin() const
{
return true;
}
bool LammpsWrapper::load_lib(const char *libfile)
{
if (plugin_handle) liblammpsplugin_release((liblammpsplugin_t *)plugin_handle);
plugin_handle = liblammpsplugin_load(libfile);
if (!plugin_handle) return false;
if (((liblammpsplugin_t *)plugin_handle)->abiversion != LAMMPSPLUGIN_ABI_VERSION) return false;
return true;
}
#else
bool LammpsWrapper::has_plugin() const
{
return false;
}
bool LammpsWrapper::load_lib(const char *)
{
return true;
}
#endif
// Local Variables:
// c-basic-offset: 4
// End:

View File

@ -0,0 +1,49 @@
/* -*- c++ -*- ----------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://www.lammps.org/, Sandia National Laboratories
LAMMPS development team: developers@lammps.org
Copyright (2003) Sandia Corporation. Under the terms of Contract
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
certain rights in this software. This software is distributed under
the GNU General Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
#ifndef LAMMPSWRAPPER_H
#define LAMMPSWRAPPER_H
class LammpsWrapper {
public:
LammpsWrapper();
public:
void open(int nargs, char **args);
void close();
void finalize();
void command(const char *);
void commands_string(const char *);
void force_timeout();
int extract_setting(const char *keyword);
double get_thermo(const char *keyword);
bool is_open() const { return lammps_handle != nullptr; }
bool is_running();
bool has_error() const;
int get_last_error_message(char *errorbuf, int buflen);
bool load_lib(const char *lammpslib);
bool has_plugin() const;
private:
void *lammps_handle;
void *plugin_handle;
};
#endif
// Local Variables:
// c-basic-offset: 4
// End: