Skip to content
plugin.cpp 14.2 KiB
Newer Older
David Reveman's avatar
David Reveman committed
/*
 * Copyright © 2005 Novell, Inc.
 * Copyright 2014 Canonical Ltd.
David Reveman's avatar
David Reveman committed
 *
 * Permission to use, copy, modify, distribute, and sell this software
 * and its documentation for any purpose is hereby granted without
 * fee, provided that the above copyright notice appear in all copies
 * and that both that copyright notice and this permission notice
 * appear in supporting documentation, and that the name of
 * Novell, Inc. not be used in advertising or publicity pertaining to
 * distribution of the software without specific, written prior permission.
 * Novell, Inc. makes no representations about the suitability of this
 * software for any purpose. It is provided "as is" without express or
 * implied warranty.
 *
 * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
David Reveman's avatar
David Reveman committed
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
 * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
David Reveman's avatar
David Reveman committed
 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * Author: David Reveman <davidr@novell.com>
 */


/* XXX: This dependency needs to go away */
#include <boost/scoped_array.hpp>
#include <boost/foreach.hpp>

#include <boost/algorithm/string.hpp>

David Reveman's avatar
David Reveman committed
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#include <errno.h>
#include <sys/types.h>
#include <algorithm>
#include <set>
#define foreach BOOST_FOREACH
static const char here[] = "core";
CompPlugin::Map pluginsMap;
Erkin Bahceci's avatar
Erkin Bahceci committed
CompPlugin::List plugins;
David Reveman's avatar
David Reveman committed

class CorePluginVTable : public CompPlugin::VTable
	bool init ();
	void markReadyToInstantiate ();
	void markNoFurtherInstantiation ();

	CompOption::Vector & getOptions ();
	bool setOption (const CompString  &name,
			CompOption::Value &value);
COMPIZ_PLUGIN_20090315 (core, CorePluginVTable)
CompPlugin::VTable * getCoreVTable ()
    if (!coreVTable)
    {
	return getCompPluginVTable20090315_core ();
    }

    return coreVTable;
bool
CorePluginVTable::init ()
    return true;
void
CorePluginVTable::markReadyToInstantiate ()
{
}

void
CorePluginVTable::markNoFurtherInstantiation ()
{
}

CompOption::Vector &
CorePluginVTable::getOptions ()
    return screen->getOptions ();
CorePluginVTable::setOption (const CompString  &name,
			     CompOption::Value &value)
    return screen->setOption (name, value);
cloaderLoadPlugin (CompPlugin *p,
		   const char *path,
		   const char *name)
{
    if (strcmp (name, getCoreVTable ()->name ().c_str ()))
    p->vTable	      = getCoreVTable ();
    p->devPrivate.ptr = NULL;
    p->devType	      = "cloader";

}

static void
cloaderUnloadPlugin (CompPlugin *p)
{
Erkin Bahceci's avatar
Erkin Bahceci committed
    delete p->vTable;
static bool
setOpenGLPluginEnvironment ()
{
    /*
     * Check if the hardware is adequate for Unity and if not, use LLVMpipe.
     * Unfortunately the design of Mesa requires that this be done before
     * libGL is loaded, which means before the opengl plugin is loaded.
     */
    bool toggledLLVM = false;

    if (!getenv ("LIBGL_ALWAYS_SOFTWARE"))
    {
	const char *profile = getenv ("COMPIZ_CONFIG_PROFILE");
	if (profile && strcmp (profile, "ubuntu") == 0)
	{
	    int result = system ("/usr/lib/nux/unity_support_test");
	    int status = WEXITSTATUS (result);
	    compLogMessage ("core", CompLogLevelInfo,
		"Unity is %s",
		status == 0 ?   "fully supported by your hardware." :
		status == 127 ? "undetectable" :
		                "not supported by your hardware. "
		                "Enabling software rendering instead (slow).");
	    if (status > 0 && status < 127)
	    {
		setenv ("LIBGL_ALWAYS_SOFTWARE", "1", 1);
		toggledLLVM = true;
	    }
	}
    }

    return toggledLLVM;
}

static void
unsetUnityshellPluginEnvironment ()
{
    unsetenv ("LIBGL_ALWAYS_SOFTWARE");
}

static void
setPluginEnvironment (const char *name)
{
    if (strcmp (name, "opengl") == 0)
	setOpenGLPluginEnvironment ();
}

static void
unsetPluginEnvironment (const char *name)
{
    if (strcmp (name, "unityshell") == 0)
	unsetUnityshellPluginEnvironment ();
}

dlloaderLoadPlugin (CompPlugin *p,
		    const char *path,
		    const char *name)
    CompString  file;
    void        *dlhand;
    bool        loaded = false;
    if (cloaderLoadPlugin (p, path, name))
	return true;
    setPluginEnvironment (name);

    {
	file  = path;
	file += "/";
    }

    file += "lib";
    file += name;
    file += ".so";
    compLogMessage (here, CompLogLevelDebug,
                    "Trying to load %s from: %s", name, file.c_str ());
    
#ifdef DEBUG
    // Do not unload the library during dlclose.
    open_flags |= RTLD_NODELETE;
    // Make the symbols available globally
    open_flags |= RTLD_GLOBAL;
#endif
    dlhand = dlopen (file.c_str (), open_flags);
    if (dlhand)
    {
	PluginGetInfoProc getInfo;
	char		  *error;
	char              sym[1024];
	compLogMessage (here, CompLogLevelDebug,
	                "Opened library: %s", file.c_str ());
	snprintf (sym, 1024, "getCompPluginVTable20090315_%s", name);
	getInfo = (PluginGetInfoProc) dlsym (dlhand, sym);
	    compLogMessage (here, CompLogLevelError, "dlsym: %s", error);
	    getInfo = 0;
	}

	if (getInfo)
	{
	    p->vTable = (*getInfo) ();
	    if (!p->vTable)
	    {
		compLogMessage (here, CompLogLevelError,
				"Couldn't get vtable from '%s' plugin",
				file.c_str ());
	    }
	    else
	    {
		p->devPrivate.ptr = dlhand;
		p->devType	  = "dlloader";
		loaded            = true;
		compLogMessage (here, CompLogLevelDebug,
		                "Loaded plugin %s from: %s",
		                name, file.c_str ());
	compLogMessage (here, CompLogLevelDebug,
			"dlopen failed: %s", dlerror ());
    if (!loaded && dlhand)
	dlclose (dlhand);
David Reveman's avatar
David Reveman committed

    unsetPluginEnvironment (name);

}

static void
dlloaderUnloadPlugin (CompPlugin *p)
{
	const char *name = p->vTable->name ().c_str ();
	compLogMessage (here, CompLogLevelDebug, "Closing library: %s", name);
	delete p->vTable;
David Reveman's avatar
David Reveman committed
	dlclose (p->devPrivate.ptr);
David Reveman's avatar
David Reveman committed
    else
	cloaderUnloadPlugin (p);
}

LoadPluginProc   loaderLoadPlugin   = dlloaderLoadPlugin;
UnloadPluginProc loaderUnloadPlugin = dlloaderUnloadPlugin;
Sam Spilsbury's avatar
Sam Spilsbury committed
bool
CompManager::initPlugin (CompPlugin *p)
    const char *name = p->vTable->name ().c_str ();
    if (!p->vTable->init ())
	compLogMessage (here, CompLogLevelError,
	                "Plugin init failed: %s", name);
    if (screen && screen->displayInitialised())
	if (!p->vTable->initScreen (screen))
	{
	    compLogMessage (here, CompLogLevelError,
	                    "Plugin initScreen failed: %s", name);
	    p->vTable->fini ();
	    return false;
	}
	if (!screen->initPluginForScreen (p))
	{
	    compLogMessage (here, CompLogLevelError,
	                    "initPluginForScreen failed: %s", name);
	    p->vTable->fini ();
	    return false;
	}
Sam Spilsbury's avatar
Sam Spilsbury committed
void
CompManager::finiPlugin (CompPlugin *p)
    if (screen)
    {
	screen->finiPluginForScreen (p);
	p->vTable->finiScreen (screen);
    }
    p->vTable->fini ();
    p->vTable->markNoFurtherInstantiation ();
CompScreen::initPluginForScreen (CompPlugin *p)
David Reveman's avatar
David Reveman committed
{
    WRAPABLE_HND_FUNCTN_RETURN (bool, initPluginForScreen, p)
David Reveman's avatar
David Reveman committed

CompScreenImpl::_initPluginForScreen (CompPlugin *p)
    using compiz::private_screen::WindowManager;

    bool status               = true;
    WindowManager::iterator it, fail;
    it = fail = windowManager.begin ();
    for (;it != windowManager.end (); ++it)
David Reveman's avatar
David Reveman committed
    {
	w = *it;
	if (!p->vTable->initWindow (w))
	{
	    const char *name = p->vTable->name ().c_str ();
	    compLogMessage (here, CompLogLevelError,
	                    "initWindow failed for %s", name);
            fail   = it;
            status = false;
Alan Griffiths's avatar
Alan Griffiths committed
            break;
David Reveman's avatar
David Reveman committed
    }

    it = windowManager.begin ();
	w = *it;
	p->vTable->finiWindow (w);
David Reveman's avatar
David Reveman committed
    }

    return status;
David Reveman's avatar
David Reveman committed
}

CompScreen::finiPluginForScreen (CompPlugin *p)
David Reveman's avatar
David Reveman committed
{
    WRAPABLE_HND_FUNCTN (finiPluginForScreen, p)
David Reveman's avatar
David Reveman committed

CompScreenImpl::_finiPluginForScreen (CompPlugin *p)
    windowManager.forEachWindow(boost::bind(&CompPlugin::VTable::finiWindow, p->vTable, _1));
David Reveman's avatar
David Reveman committed
}

Alan Griffiths's avatar
Alan Griffiths committed
CompPlugin::screenInitPlugins (CompScreen *s)
David Reveman's avatar
David Reveman committed
{
    CompPlugin::List::reverse_iterator it = plugins.rbegin ();
David Reveman's avatar
David Reveman committed

    CompPlugin *p = NULL;
David Reveman's avatar
David Reveman committed

    /* Plugins is a btf list, so iterate it in reverse */
    while (it != plugins.rend ())
David Reveman's avatar
David Reveman committed
    {
Sam Spilsbury's avatar
Sam Spilsbury committed
	p = (*it);
David Reveman's avatar
David Reveman committed

	if (p->vTable->initScreen (s))
	    s->initPluginForScreen (p);
Danny Baumann's avatar
Danny Baumann committed

MC Return's avatar
MC Return committed
	++it;
David Reveman's avatar
David Reveman committed
    }
David Reveman's avatar
David Reveman committed
}

void
Alan Griffiths's avatar
Alan Griffiths committed
CompPlugin::screenFiniPlugins (CompScreen *s)
David Reveman's avatar
David Reveman committed
{
    foreach (CompPlugin *p, plugins)
    {
	s->finiPluginForScreen (p);
	p->vTable->finiScreen (s);
    }

}
bool
CompPlugin::windowInitPlugins (CompWindow *w)
{
    bool status = true;
David Reveman's avatar
David Reveman committed

    for (List::reverse_iterator rit = plugins.rbegin ();
         rit != plugins.rend (); ++rit)
David Reveman's avatar
David Reveman committed
    {
	status &= (*rit)->vTable->initWindow (w);
void
CompPlugin::windowFiniPlugins (CompWindow *w)
{
    foreach (CompPlugin *p, plugins)
    {
	p->vTable->finiWindow (w);
David Reveman's avatar
David Reveman committed
CompPlugin *
CompPlugin::find (const char *name)
David Reveman's avatar
David Reveman committed
{
    CompPlugin::Map::iterator it = pluginsMap.find (name);

    if (it != pluginsMap.end ())
        return it->second;
David Reveman's avatar
David Reveman committed

David Reveman's avatar
David Reveman committed
}

CompPlugin::unload (CompPlugin *p)
    if (p->vTable)
    {
	const char *name = p->vTable->name ().c_str ();
	compLogMessage (here, CompLogLevelInfo, "Unloading plugin: %s", name);
    }
    loaderUnloadPlugin (p);
#ifndef COMPIZ_LIBDIR
# define COMPIZ_LIBDIR "/usr/lib/compiz"
#endif
David Reveman's avatar
David Reveman committed

typedef std::vector<std::string> PluginSearchPath;
David Reveman's avatar
David Reveman committed

static PluginSearchPath
create_plugin_search_path ()
{
    PluginSearchPath plugin_search_path;
    if (char* plugin_dir_override = getenv ("COMPIZ_PLUGIN_DIR"))
Sam Spilsbury's avatar
Sam Spilsbury committed
    {
	std::vector <std::string> paths;
	boost::split (paths, plugin_dir_override, boost::is_any_of (":"));
	foreach (const std::string &path, paths)
	{
	    if (!path.empty ())
		plugin_search_path.push_back (path);
Sam Spilsbury's avatar
Sam Spilsbury committed
    }
    if (char* home = getenv ("HOME"))
David Reveman's avatar
David Reveman committed
    {
	plugin_search_path.push_back (std::string (home) + "/" HOME_PLUGINDIR);
David Reveman's avatar
David Reveman committed
    }
    plugin_search_path.push_back (PLUGINDIR);
    plugin_search_path.push_back (COMPIZ_LIBDIR);
    return plugin_search_path;
}
David Reveman's avatar
David Reveman committed

CompPlugin *
CompPlugin::load (const char *name)
{
    std::auto_ptr <CompPlugin> p (new CompPlugin ());
    p->devPrivate.uval = 0;
    p->devType	       = "";
    p->vTable	       = 0;
David Reveman's avatar
David Reveman committed

    compLogMessage (here, CompLogLevelInfo, "Loading plugin: %s", name);

    PluginSearchPath plugin_search_path = create_plugin_search_path ();
    foreach (const std::string &path, plugin_search_path)
    {
	if (loaderLoadPlugin (p.get (), path.c_str (), name))
	  return p.release();
    }

    if (loaderLoadPlugin (p.get (), NULL, name))
      return p.release();
David Reveman's avatar
David Reveman committed

    compLogMessage (here, CompLogLevelError, "Failed to load plugin: %s", name);
David Reveman's avatar
David Reveman committed

David Reveman's avatar
David Reveman committed
}

bool
CompPlugin::push (CompPlugin *p)
David Reveman's avatar
David Reveman committed
{
    const CompString &name = p->vTable->name ();
    std::pair<CompPlugin::Map::iterator, bool> insertRet =
        pluginsMap.insert (std::make_pair (name, p));
David Reveman's avatar
David Reveman committed
    {
	compLogMessage (here, CompLogLevelWarn,
			"Plugin '%s' already active",
David Reveman's avatar
David Reveman committed

David Reveman's avatar
David Reveman committed
    }

    plugins.push_front (p);
David Reveman's avatar
David Reveman committed

    compLogMessage (here, CompLogLevelInfo, "Starting plugin: %s", name.c_str ());
	compLogMessage (here, CompLogLevelDebug, "Started plugin: %s", name.c_str ());
David Reveman's avatar
David Reveman committed
    {
	compLogMessage (here, CompLogLevelError,
	    "Failed to start plugin: %s", name.c_str ());
David Reveman's avatar
David Reveman committed

        pluginsMap.erase (name);
	plugins.pop_front ();

	return false;
David Reveman's avatar
David Reveman committed
    }

David Reveman's avatar
David Reveman committed
}

CompPlugin *
David Reveman's avatar
David Reveman committed
{
Dennis Kasprzyk's avatar
Dennis Kasprzyk committed
    if (plugins.empty ())
	return NULL;

    CompPlugin *p = plugins.front ();
David Reveman's avatar
David Reveman committed

    if (!p)
	return 0;

    const CompString &name = p->vTable->name ();
    compLogMessage (here, CompLogLevelInfo, "Stopping plugin: %s", name.c_str ());
Sam Spilsbury's avatar
Sam Spilsbury committed
    CompManager::finiPlugin (p);
    compLogMessage (here, CompLogLevelDebug, "Stopped plugin: %s", name.c_str ());
David Reveman's avatar
David Reveman committed

David Reveman's avatar
David Reveman committed

    return p;
}
CompPlugin::List &
CompPlugin::getPlugins (void)
David Reveman's avatar
David Reveman committed
{
    return plugins;
}
CompPlugin::getPluginABI (const char *name)
    CompPlugin *p = find (name);
Dennis Kasprzyk's avatar
Dennis Kasprzyk committed
    CompString s = name;
Dennis Kasprzyk's avatar
Dennis Kasprzyk committed
    s += "_ABI";

    if (!screen->hasValue (s))
Dennis Kasprzyk's avatar
Dennis Kasprzyk committed
	return 0;
    return screen->getValue (s).uval;
bool
CompPlugin::checkPluginABI (const char *name,
			    int        abi)
    int pluginABI;

    pluginABI = getPluginABI (name);
    if (!pluginABI)
    {
	compLogMessage (here, CompLogLevelError,
Danny Baumann's avatar
Danny Baumann committed
			"Plugin '%s' not loaded.\n", name);
    }
    else if (pluginABI != abi)
	compLogMessage (here, CompLogLevelError,
			"Plugin '%s' has ABI version '%d', expected "
			"ABI version '%d'.\n",
			name, pluginABI, abi);
CompPlugin::VTable::VTable () :
    mName (""),
    mSelf (NULL)
{
}

CompPlugin::VTable::~VTable ()
    if (mSelf)
	*mSelf = NULL;
}

void
CompPlugin::VTable::initVTable (CompString         name,
				CompPlugin::VTable **self)
{
    mName = name;
    if (self)
    {
	mSelf = self;
	*mSelf = this;
    }
}

CompPlugin::VTable::name () const
{
    return mName;
void
CompPlugin::VTable::fini ()
{
}
CompPlugin::VTable::initScreen (CompScreen *)
{
    return true;
}

void
CompPlugin::VTable::finiScreen (CompScreen *)
{
}

bool
CompPlugin::VTable::initWindow (CompWindow *)
CompPlugin::VTable::finiWindow (CompWindow *)
Danny Baumann's avatar
Danny Baumann committed

CompOption::Vector &
CompPlugin::VTable::getOptions ()
CompPlugin::VTable::setOption (const CompString  &name,
			       CompOption::Value &value)
William Hua's avatar
William Hua committed

CompAction::Vector &
CompPlugin::VTable::getActions ()
{
    return noActions ();
}