/****************************************************************************
*  karamba_python.cpp  -  Functions for calling python scripts
*
*  Copyright (C) 2003 Hans Karlsson <karlsson.h@home.se>
*  Copyright (C) 2003-2004 Adam Geitgey <adam@rootnode.org>
*  Copyright (c) 2004 Petri Damstén <damu@iki.fi>
*
*  This file is part of Superkaramba.
*
*  Superkaramba is free software; you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation; either version 2 of the License, or
*  (at your option) any later version.
*
*  Superkaramba is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with Superkaramba; if not, write to the Free Software
*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
****************************************************************************/

#include <Python.h>
#include "karambaapp.h"
#include "karamba_python.h"

#include "meter_python.h"
#include "bar_python.h"
#include "graph_python.h"
#include "textlabel_python.h"
#include "richtextlabel_python.h"
#include "imagelabel_python.h"
#include "widget_python.h"
#include "menu_python.h"
#include "config_python.h"
#include "task_python.h"
#include "systray_python.h"
#include "misc_python.h"

/*******************************************
 * Python methods are defined here.
 *   Each method accessible from python should have:
 *     - A wrapper function that returns a PyObject or appropriate python type
 *     - A C++ implementation of the python method, named the same as the python call
 *     - An entry in the python methods array so the call is accessible from python
 *
 *   Example:
 *     py_move_systay - wrapper function
 *     moveSystray - actual implementation of method
 *     {"moveSystray", py_move_systray, METH_VARARGS, "Move the Systray"} - array entry
 */

static PyMethodDef karamba_methods[] = {
    // Bar - bar_python.cpp
    {"createBar", py_createBar, METH_VARARGS, "Create new Bar."},
    {"deleteBar", py_deleteBar, METH_VARARGS, "Delete Bar."},
    {"getThemeBar", py_getThemeBar, METH_VARARGS, "Get Bar from .theme using it's name."},
    {"getBarSize", py_getBarSize, METH_VARARGS, "Get Bar size."},
    {"resizeBar", py_resizeBar, METH_VARARGS, "Resize Bar."},
    {"getBarPos", py_getBarPos, METH_VARARGS, "Get Bar position."},
    {"moveBar", py_moveBar, METH_VARARGS, "Move Bar."},
    {"hideBar", py_hideBar, METH_VARARGS, "Hide Bar."},
    {"showBar", py_showBar, METH_VARARGS, "Show Bar."},
    {"getBarSensor", py_getBarSensor, METH_VARARGS, "Get Bar sensor."},
    {"setBarSensor", py_setBarSensor, METH_VARARGS, "Set Bar sensor."},
    {"setBarImage", py_setBarImage, METH_VARARGS, "Set bar image"},
    {"getBarImage", py_getBarImage, METH_VARARGS, "Get bar image"},
    {"setBarVertical", py_setBarVertical, METH_VARARGS, "Set bar orientation"},
    {"getBarVertical", py_getBarVertical, METH_VARARGS, "Get bar orientation"},
    {"setBarValue", py_setBarValue, METH_VARARGS, "Set bar value"},
    {"getBarValue", py_getBarValue, METH_VARARGS, "Get bar value"},
    {"setBarMinMax", py_setBarMinMax, METH_VARARGS, "Set bar min & max"},
    {"getBarMinMax", py_getBarMinMax, METH_VARARGS, "Get bar min & max"},

    // Graph - graph_python.cpp
    {"createGraph", py_createGraph, METH_VARARGS, "Create new Graph."},
    {"deleteGraph", py_deleteGraph, METH_VARARGS, "Delete Graph."},
    {"getThemeGraph", py_getThemeGraph, METH_VARARGS, "Get Graph from .theme using it's name."},
    {"getGraphSize", py_getGraphSize, METH_VARARGS, "Get Graph size."},
    {"resizeGraph", py_resizeGraph, METH_VARARGS, "Resize Graph."},
    {"getGraphPos", py_getGraphPos, METH_VARARGS, "Get Graph position."},
    {"moveGraph", py_moveGraph, METH_VARARGS, "Move Graph."},
    {"hideGraph", py_hideGraph, METH_VARARGS, "Hide Graph."},
    {"showGraph", py_showGraph, METH_VARARGS, "Show Graph."},
    {"getGraphSensor", py_getGraphSensor, METH_VARARGS, "Get Graph sensor."},
    {"setGraphSensor", py_setGraphSensor, METH_VARARGS, "Set Graph sensor."},
    {"setGraphValue", py_setGraphValue, METH_VARARGS, "Set graph value"},
    {"getGraphValue", py_getGraphValue, METH_VARARGS, "Get graph value"},
    {"setGraphMinMax", py_setGraphMinMax, METH_VARARGS, "Set graph min & max"},
    {"getGraphMinMax", py_getGraphMinMax, METH_VARARGS, "Get graph min & max"},
    {"setGraphColor", py_setGraphColor, METH_VARARGS, "Change a Graph Sensor's Color"},
    {"getGraphColor", py_getGraphColor, METH_VARARGS, "Get a Graph Sensor's Color"},

    // TextLabel - textlabel_python.cpp
    {"createText", py_createText, METH_VARARGS, "Create new Text."},
    {"deleteText", py_deleteText, METH_VARARGS, "Delete Text."},
    {"getThemeText", py_getThemeText, METH_VARARGS, "Get Text from .theme using it's name."},
    {"getTextSize", py_getTextSize, METH_VARARGS, "Get Text size."},
    {"resizeText", py_resizeText, METH_VARARGS, "Resize Text."},
    {"getTextPos", py_getTextPos, METH_VARARGS, "Get Text position."},
    {"moveText", py_moveText, METH_VARARGS, "Move Text."},
    {"hideText", py_hideText, METH_VARARGS, "Hide Text."},
    {"showText", py_showText, METH_VARARGS, "Show Text."},
    {"getTextSensor", py_getTextSensor, METH_VARARGS, "Get Text sensor."},
    {"setTextSensor", py_setTextSensor, METH_VARARGS, "Set Text sensor."},
    {"changeText", py_setTextValue, METH_VARARGS, "Change a Text Sensor's Text"},
    {"getTextValue", py_getTextValue, METH_VARARGS, "Get Text value"},
    {"changeTextShadow", py_setTextShadow, METH_VARARGS, "Change a Text Shadow size"},
    {"getTextShadow", py_getTextShadow, METH_VARARGS, "Get a Text Shadow size"},
    {"changeTextFont", py_setTextFont, METH_VARARGS, "Change a Text Sensor's Font"},
    {"getTextFont", py_getTextFont, METH_VARARGS, "Get a Text Sensor's Font"},
    {"changeTextColor", py_setTextColor, METH_VARARGS, "Change a Text Sensor's Color"},
    {"getTextColor", py_getTextColor, METH_VARARGS, "Get a Text Sensor's Color"},
    {"changeTextSize", py_setTextFontSize, METH_VARARGS, "Change a Text Sensor's Font Size"},
    {"getTextFontSize", py_getTextFontSize, METH_VARARGS, "Get a Text Sensor's Font Size"},
    {"getTextAlign", py_getTextAlign, METH_VARARGS, "Get Text alignment."},
    {"setTextAlign", py_setTextAlign, METH_VARARGS, "Set Text alignment."},
    {"setTextScroll", py_setTextScroll, METH_VARARGS, "Set Text scroll."},

    // RichTextLabel - richtextlabel_python.cpp
    {"createRichText", py_createRichText, METH_VARARGS, "Create a Rich Text Sensor"},
    {"deleteRichText", py_deleteRichText, METH_VARARGS, "Deletes a Rich Text Sensor"},
    {"getThemeRichText", py_getThemeRichText, METH_VARARGS, "Get Rich Text from .theme using it's name."},
    {"getRichTextSize", py_getRichTextSize, METH_VARARGS, "Get the (width, height) of a Rich Text Sensor"},
    {"resizeRichText", py_resizeRichText, METH_VARARGS, "Resize Rich Text."},
    {"setRichTextWidth", py_set_rich_text_width, METH_VARARGS, "Sets the width of a Rich Text Sensor"},
    {"getRichTextPos", py_getRichTextPos, METH_VARARGS, "Get Rich Text position."},
    {"moveRichText", py_moveRichText, METH_VARARGS, "Moves a Rich Text Sensor"},
    {"hideRichText", py_hideRichText, METH_VARARGS, "hides a Rich Text Sensor"},
    {"showRichText", py_showRichText, METH_VARARGS, "shows a Rich Text Sensor"},
    {"getRichTextSensor", py_getRichTextSensor, METH_VARARGS, "Get Rich Text sensor."},
    {"setRichTextSensor", py_setRichTextSensor, METH_VARARGS, "Set Rich Text sensor."},
    {"changeRichText", py_setRichTextValue, METH_VARARGS, "Change the content of a Rich Text Sensor"},
    {"getRichTextValue", py_getRichTextValue, METH_VARARGS, "Get Rich Text value"},
    {"changeRichTextFont", py_setRichTextFont, METH_VARARGS, "Change a Rich Text Sensor's Font"},
    {"getRichTextFont", py_getRichTextFont, METH_VARARGS, "Get a Rich Text Sensor's Font"},
    {"changeRichTextSize", py_setRichTextFontSize, METH_VARARGS, "Change a Rich Text Sensor's Font Size"},
    {"getRichTextFontSize", py_getRichTextFontSize, METH_VARARGS, "Get a Rich Text Sensor's Font Size"},

    // ImageLabel - imagelabel_python.cpp
    {"createImage", py_createImage, METH_VARARGS, "Create an Image"},
    {"createTaskIcon", py_createTaskIcon, METH_VARARGS, "Create an Image of the Icon for a Task"},
    {"createBackgroundImage", py_createBackgroundImage, METH_VARARGS, "Create an Image (only redraw it when background changes)"},
    {"deleteImage", py_deleteImage, METH_VARARGS, "Delete an Image"},
    {"getThemeImage", py_getThemeImage, METH_VARARGS, "Get image meter from .theme using it's name"},
    {"getImageSize", py_getImageSize, METH_VARARGS, "Get Image size."},
    {"getImageWidth", py_getImageWidth, METH_VARARGS, "Get the width of an Image"},
    {"getImageHeight", py_getImageHeight, METH_VARARGS, "Get the height of an Image"},
    {"getImagePos", py_getImagePos, METH_VARARGS, "Get Image position."},
    {"moveImage", py_moveImage, METH_VARARGS, "Move an Image"},
    {"hideImage", py_hideImage, METH_VARARGS, "Hide an Image"},
    {"showImage", py_showImage, METH_VARARGS, "Show an Image"},
    {"getImagePath", py_getImageValue, METH_VARARGS, "Get Image path."},
    {"setImagePath", py_setImageValue, METH_VARARGS, "Set Image path."},
    {"getImageSensor", py_getImageSensor, METH_VARARGS, "Get Image sensor."},
    {"setImageSensor", py_setImageSensor, METH_VARARGS, "Set Image sensor."},
    {"addImageTooltip", py_addImageTooltip, METH_VARARGS, "Create a Tooltip for an Image"},
    {"resizeImage", py_resizeImage, METH_VARARGS, "Scale an Image"},
    {"resizeImageSmooth", py_resizeImageSmooth, METH_VARARGS, "Scale an Image (slower, better looking)"},
    {"rotateImage", py_rotateImage, METH_VARARGS, "Rotate an Image"},
    {"removeImageTransformations", py_removeImageTransformations, METH_VARARGS, "Restore original size and orientation of an Image"},
    {"removeImageEffects", py_removeImageEffects, METH_VARARGS, "Remove Effects of an Image"},
    {"changeImageIntensity", py_changeImageIntensity, METH_VARARGS, "Change Intensity of an Image"},
    {"changeImageChannelIntensity", py_changeImageChannelIntensity, METH_VARARGS, "Change Intensity of an Image Channel"},
    {"changeImageToGray", py_changeImageToGray, METH_VARARGS, "Converts an Image to Grayscale"},

    // Menu - menu_python.cpp
    {"createMenu", py_create_menu, METH_VARARGS, "Create a popup menu"},
    {"deleteMenu", py_delete_menu, METH_VARARGS, "Delete a popup menu"},
    {"addMenuItem", py_add_menu_item, METH_VARARGS, "Add a popup menu entry"},
    {"addMenuSeparator", py_add_menu_separator, METH_VARARGS, "Add a popup menu seperator item"},
    {"removeMenuItem", py_remove_menu_item, METH_VARARGS, "Remove a popup menu entry"},
    {"popupMenu", py_popup_menu, METH_VARARGS, "Popup a menu at a specified location"},

    // Config - config_python.cpp
    {"addMenuConfigOption", py_add_menu_config_option, METH_VARARGS, "Add a configuration entry to the menu"},
    {"setMenuConfigOption", py_set_menu_config_option, METH_VARARGS, "Set a configuration entry in the menu"},
    {"readMenuConfigOption", py_read_menu_config_option, METH_VARARGS, "Read a configuration entry in the menu"},
    {"readConfigEntry", py_read_config_entry, METH_VARARGS, "Read a configuration entry"},
    {"writeConfigEntry", py_write_config_entry, METH_VARARGS, "Writes a configuration entry"},

    // Widget - widget_python.cpp
    {"moveWidget", py_move_widget, METH_VARARGS, "Move Widget to x,y"},
    {"resizeWidget", py_resize_widget, METH_VARARGS, "Resize Widget to width,height"},
    {"createWidgetMask", py_create_widget_mask, METH_VARARGS, "Create a clipping mask for this widget"},
    {"redrawWidget", py_redraw_widget, METH_VARARGS, "Update Widget to reflect your changes"},
    {"redrawWidgetBackground", py_redraw_widget_background, METH_VARARGS, "Update Widget to reflect background image changes"},
    {"getWidgetPosition", py_get_widget_position, METH_VARARGS, "Get Widget Position"},
    {"toggleWidgetRedraw", py_toggle_widget_redraw, METH_VARARGS, "Toggle Widget redrawing"},

    // Task - task_python.cpp
    {"getStartupList", py_get_startup_list, METH_VARARGS, "Get the system startup list"},
    {"getStartupInfo", py_get_startup_info, METH_VARARGS, "Get all the info for a startup"},
    {"getTaskList", py_get_task_list, METH_VARARGS, "Get the system task list"},
    {"getTaskNames", py_get_task_names, METH_VARARGS, "Get the system task list in name form"},
    {"getTaskInfo", py_get_task_info, METH_VARARGS, "Get all the info for a task"},
    {"performTaskAction", py_perform_task_action, METH_VARARGS, "Do something with a task, such as minimize it"},

    // System Tray - systray_python.cpp
    {"createSystray", py_create_systray, METH_VARARGS, "Create a Systray"},
    {"hideSystray", py_hide_systray, METH_VARARGS, "Hide the Systray"},
    {"showSystray", py_show_systray, METH_VARARGS, "Show the Systray"},
    {"moveSystray", py_move_systray, METH_VARARGS, "Move the Systray"},
    {"getCurrentWindowCount", py_get_current_window_count, METH_VARARGS, "Get current Window count"},
    {"updateSystrayLayout", py_update_systray_layout, METH_VARARGS, "Update Systray layout"},

    // Misc - misc_python.cpp
    {"getThemePath", py_get_theme_path, METH_VARARGS,  "Get the file path of the theme"},
    {"openTheme", py_open_theme, METH_VARARGS, "Open a new theme"},
    {"acceptDrops", py_accept_drops, METH_VARARGS, "Allows widget to receive Drop (I.E. Drag and Drop) events"},
    {"toggleShowDesktop", py_toggle_show_desktop, METH_VARARGS, "Show/Hide the desktop"},
    {"execute", py_execute_command, METH_VARARGS, "Execute a command"},
    {"executeInteractive", py_execute_command_interactive, METH_VARARGS, "Execute a command and get it's output (stdout)"},
    {"attachClickArea", (PyCFunction)py_attach_clickArea, METH_VARARGS|METH_KEYWORDS, "Add a clickArea to the given text or image"},
    {"createClickArea", py_create_click_area, METH_VARARGS, "Create a Click Area Sensor"},
    {"getNumberOfDesktops", py_get_number_of_desktops, METH_VARARGS, "Get current number of virtual desktops"},
    {"getIp", py_get_ip, METH_VARARGS, "Get current host's IP address"},
    {"translateAll", py_translate_all, METH_VARARGS, "Translate all widgets in a theme"},
    {"show", py_show, METH_VARARGS, "Show theme"},
    {"hide", py_show, METH_VARARGS, "Hide theme"},
    {NULL, NULL, 0 ,NULL}
};

PyThreadState* KarambaPython::mainThreadState = 0;

KarambaPython::KarambaPython(QString themePath, QString themeName, bool reloading):
  pythonThemeExtensionLoaded(false), pName(0), pModule(0), pDict(0)
{
  PyThreadState* myThreadState;
  char pypath[1024];

  getLock(&myThreadState);

  // load the .py file for this .theme
  PyRun_SimpleString("import sys");
  //Add theme path to python path so that we can find the python file
  snprintf(pypath, 1023, "sys.path.insert(0, '%s')", themePath.ascii());
  PyRun_SimpleString(pypath);
  PyRun_SimpleString("sys.path.insert(0, '')");

  PyImport_AddModule("karamba");
  Py_InitModule("karamba", karamba_methods);

  pName = PyString_FromString(themeName.ascii());
  pModule = PyImport_Import(pName);

  //Make sure the module is up to date.
  if (reloading)
    PyImport_ReloadModule(pModule);

  if (pModule != NULL)
  {
    pDict = PyModule_GetDict(pModule);
    if (pDict != NULL)
    {
      pythonThemeExtensionLoaded = true;
    }
  }
  else
  {
    PyErr_Print();
    fprintf(stderr,
            "------------------------------------------------------\n");
    fprintf(stderr, "What does ImportError mean?\n");
    fprintf(stderr, "\n");
    fprintf(stderr,
            "It means that I couldn't load a python add-on for %s.theme\n",
            themeName.ascii());
    fprintf(stderr, "If this is a regular theme and doesn't use python\n");
    fprintf(stderr, "extensions, then nothing is wrong.\n");
    fprintf(stderr,
            "------------------------------------------------------\n");
  }
  releaseLock(myThreadState);
}

KarambaPython::~KarambaPython()
{
  //Clean up Python references
  if (pythonThemeExtensionLoaded) {
    PyThreadState* myThreadState;
    getLock(&myThreadState);

    //Displose of current python module so we can reload in constructor.
    Py_DECREF(pModule);
    Py_DECREF(pName);

    releaseLock(myThreadState);
  }
}

void KarambaPython::initPython()
{
  // initialize Python
  Py_Initialize();

  // initialize thread support
  PyEval_InitThreads();

  // save a pointer to the main PyThreadState object
  mainThreadState = PyThreadState_Get();

  // release the lock
  PyEval_ReleaseLock();
}

void KarambaPython::shutdownPython()
{
  // shut down the interpreter
  PyInterpreterState * mainInterpreterState = mainThreadState->interp;
  // create a thread state object for this thread
  PyThreadState * myThreadState = PyThreadState_New(mainInterpreterState);
  PyThreadState_Swap(myThreadState);
  PyEval_AcquireLock();
  Py_Finalize();
}

void KarambaPython::getLock(PyThreadState** myThreadState)
{
  // get the global lock
  PyEval_AcquireLock();

  // create a thread state object for this thread
  *myThreadState = PyThreadState_New(mainThreadState->interp);
  PyThreadState_Swap(*myThreadState);
}

void KarambaPython::releaseLock(PyThreadState* myThreadState)
{
  // swap my thread state out of the interpreter
  PyThreadState_Swap(NULL);
  // clear out any cruft from thread state object
  PyThreadState_Clear(myThreadState);
  // delete my thread state object
  PyThreadState_Delete(myThreadState);
  // release the lock
  PyEval_ReleaseLock();
}

PyObject* KarambaPython::getFunc(char* function)
{
  PyObject* pFunc = PyDict_GetItemString(pDict, function);
  if (pFunc && PyCallable_Check(pFunc))
    return pFunc;
  return NULL;
}

bool KarambaPython::callObject(char* func, PyObject* pArgs)
{
  bool result = false;
  PyThreadState* myThreadState;

  //qDebug("Calling %s", func.ascii());

  getLock(&myThreadState);
  PyObject* pFunc = getFunc(func);

  if (pFunc != NULL)
  {
    PyObject* pValue = PyObject_CallObject(pFunc, pArgs);

    if (pValue != NULL)
    {
      Py_DECREF(pValue);
      result = true;
    }
    else
    {
      qWarning("Call to %s failed", func);
      PyErr_Print();
    }
  }
  Py_DECREF(pArgs);
  releaseLock(myThreadState);
  return result;
}

bool KarambaPython::initWidget(karamba* k)
{
  PyObject* pArgs = Py_BuildValue("(l)", k);
  return callObject("initWidget", pArgs);
}

bool KarambaPython::widgetUpdated(karamba* k)
{
  PyObject* pArgs = Py_BuildValue("(l)", k);
  return callObject("widgetUpdated", pArgs);
}

bool KarambaPython::widgetClosed(karamba* k)
{
  PyObject* pArgs = Py_BuildValue("(l)", k);
  return callObject("widgetClosed", pArgs);
}

bool KarambaPython::menuOptionChanged(karamba* k, QString key, bool value)
{
  PyObject* pArgs = Py_BuildValue("(lsi)", k, key.ascii(), (int)value);
  return callObject("menuOptionChanged", pArgs);
}

bool KarambaPython::menuItemClicked(karamba* k, KPopupMenu* menu, long id)
{
  PyObject* pArgs = Py_BuildValue("(lll)", k, menu, id);
  return callObject("menuItemClicked", pArgs);
}

bool KarambaPython::meterClicked(karamba* k, Meter* meter, int button)
{
  PyObject* pArgs = Py_BuildValue("(lli)", k, meter, button);
  return callObject("meterClicked", pArgs);
}

bool KarambaPython::meterClicked(karamba* k, QString anchor, int button)
{
  PyObject* pArgs = Py_BuildValue("(lsi)", k, anchor.ascii(), button);
  return callObject("meterClicked", pArgs);
}

bool KarambaPython::widgetClicked(karamba* k, int x, int y, int button)
{
  PyObject* pArgs = Py_BuildValue("(liii)", k, x, y, button);
  return callObject("widgetClicked", pArgs);
}

bool KarambaPython::keyPressed(karamba* k, QString text)
{
  PyObject* pArgs = Py_BuildValue("(ls)", text.ascii());
  return callObject("keyPressed", pArgs);
}

bool KarambaPython::widgetMouseMoved(karamba* k, int x, int y, int button)
{
  PyObject* pArgs = Py_BuildValue("(liii)", k, x, y, button);
  return callObject("widgetMouseMoved", pArgs);
}

bool KarambaPython::activeTaskChanged(karamba* k, Task* t)
{
  PyObject* pArgs = Py_BuildValue("(ll)", k, t);
  return callObject("activeTaskChanged", pArgs);
}

bool KarambaPython::taskAdded(karamba* k, Task* t)
{
  PyObject* pArgs = Py_BuildValue("(ll)", k, t);
  return callObject("taskAdded", pArgs);
}

bool KarambaPython::taskRemoved(karamba* k, Task* t)
{
  PyObject* pArgs = Py_BuildValue("(ll)", k, t);
  return callObject("taskRemoved", pArgs);
}

bool KarambaPython::startupAdded(karamba* k, Startup* t)
{
  PyObject* pArgs = Py_BuildValue("(ll)", k, t);
  return callObject("startupAdded", pArgs);
}

bool KarambaPython::startupRemoved(karamba* k, Startup* t)
{
  PyObject* pArgs = Py_BuildValue("(ll)", k, t);
  return callObject("startupRemoved", pArgs);
}

bool KarambaPython::commandOutput(karamba* k, int pid, char *buffer)
{
  PyObject* pArgs = Py_BuildValue("(lis)", k, pid, buffer);
  return callObject("commandOutput", pArgs);
}

bool KarambaPython::itemDropped(karamba* k, QString text)
{
  PyObject* pArgs = Py_BuildValue("(ls)", k, text.ascii());
  return callObject("itemDropped", pArgs);
}

bool KarambaPython::systrayUpdated(karamba* k)
{
  PyObject* pArgs = Py_BuildValue("(l)", k);
  return callObject("systrayUpdated", pArgs);
}

bool KarambaPython::desktopChanged(karamba* k, int desktop)
{
  PyObject* pArgs = Py_BuildValue("(li)", k, desktop);
  return callObject("desktopChanged", pArgs);
}

bool KarambaPython::wallpaperChanged(karamba* k, int desktop)
{
  PyObject* pArgs = Py_BuildValue("(li)", k, desktop);
  return callObject("wallpaperChanged", pArgs);
}
