/**
* This file is a part of the Cairo-Dock project
*
* Copyright : (C) see the 'copyright' file.
* E-mail : see the 'copyright' file.
*
* This program 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 3
* of the License, or (at your option) any later version.
*
* This program 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 this program. If not, see .
*/
#define _BSD_SOURCE
#include
#include
#include
#include
#include
#include
#include "applet-struct.h"
#include "applet-composite-manager.h"
///////////////////
/// WM BACKENDS ///
///////////////////
static CDWM *_get_wm_by_name (const gchar *cName)
{
int i;
for (i = 0; i < NB_WM; i ++)
{
if (strcmp (cName, myData.pWmList[i].cName) == 0)
return &myData.pWmList[i];
}
return NULL;
}
static CDWM *_get_wm_by_index (CDWMIndex n)
{
if (n < NB_WM)
return &myData.pWmList[n];
else
return NULL;
}
static void _set_metacity_composite (gboolean bActive)
{
int r;
if (bActive)
r = system ("gconftool-2 -s '/apps/metacity/general/compositing_manager' --type bool true");
else
r = system ("gconftool-2 -s '/apps/metacity/general/compositing_manager' --type bool false");
if (r < 0)
cd_warning ("Not able to launch this command: gconftool-2");
}
static void _set_xfwm_composite (gboolean bActive)
{
int r;
if (bActive)
r = system ("xfconf-query -c xfwm4 -p '/general/use_compositing' -t 'bool' -s 'true'");
else
r = system ("xfconf-query -c xfwm4 -p '/general/use_compositing' -t 'bool' -s 'false'");
if (r < 0)
cd_warning ("Not able to launch this command: xfconf-query");
}
static void _set_kwin_composite (gboolean bActive)
{
int r;
if (bActive)
r = system ("if test \"`qdbus org.kde.kwin /KWin compositingActive`\" = \"false\";then qdbus org.kde.kwin /KWin toggleCompositing; fi"); // not active, so activating
else
r = system ("if test \"`qdbus org.kde.kwin /KWin compositingActive`\" = \"true\"; then qdbus org.kde.kwin /KWin toggleCompositing; fi"); // active, so deactivating
if (r < 0)
cd_warning ("Not able to launch this command: qdbus");
}
static void _define_known_wms (void)
{
myData.pWmList[CD_COMPIZ].cName = "Compiz";
myData.pWmList[CD_COMPIZ].cCommand = "compiz --replace";
myData.pWmList[CD_COMPIZ].activate_composite = NULL;
myData.pWmList[CD_COMPIZ].cConfigTool = "ccsm";
myData.pWmList[CD_KWIN].cName = "KWin";
myData.pWmList[CD_KWIN].cCommand = "kwin --replace";
myData.pWmList[CD_KWIN].activate_composite = _set_kwin_composite;
myData.pWmList[CD_KWIN].cConfigTool = NULL; /// TODO: find the config tool...
myData.pWmList[CD_XFWM].cName = "Xfwm";
myData.pWmList[CD_XFWM].cCommand = "xfwm4 --replace";
myData.pWmList[CD_XFWM].activate_composite = _set_xfwm_composite;
myData.pWmList[CD_XFWM].cConfigTool = "xfwm4-settings"; // there is also xfwm4-tweaks-settings, wish they merge both ...
myData.pWmList[CD_METACITY].cName = "Metacity";
myData.pWmList[CD_METACITY].cCommand = "metacity --replace";
myData.pWmList[CD_METACITY].activate_composite = _set_metacity_composite;
myData.pWmList[CD_METACITY].cConfigTool = "gconf-editor /apps/metacity";
myData.pWmList[CD_CUSTOM_WMFB].cName = "Fallback";
myData.pWmList[CD_CUSTOM_WMFB].cCommand = NULL;
myData.pWmList[CD_CUSTOM_WMFB].activate_composite = NULL;
myData.pWmList[CD_CUSTOM_WMFB].bIsAvailable = TRUE;
myData.pWmList[CD_CUSTOM_WMC].cName = "Composite";
myData.pWmList[CD_CUSTOM_WMC].cCommand = NULL;
myData.pWmList[CD_CUSTOM_WMC].activate_composite = NULL;
myData.pWmList[CD_CUSTOM_WMC].bIsAvailable = TRUE;
}
static void _check_available_wms (gchar *cWhich)
{
if (cWhich == NULL) // no known WM is present, skip the check.
return;
CDWM *wm;
wm = _get_wm_by_index (CD_COMPIZ);
wm->bIsAvailable = (strstr (cWhich, "compiz") != NULL);
wm = _get_wm_by_index (CD_KWIN);
wm->bIsAvailable = (strstr (cWhich, "kwin") != NULL);
wm = _get_wm_by_index (CD_XFWM);
wm->bIsAvailable = (strstr (cWhich, "xfwm4") != NULL);
wm = _get_wm_by_index (CD_METACITY);
wm->bIsAvailable = (strstr (cWhich, "metacity") != NULL);
}
static CDWMIndex _check_current_wm (gchar *cPs)
{
if (cPs == NULL) // no known WM is present, skip the check.
return -1;
if (strstr (cPs, "compiz") != NULL)
return CD_COMPIZ;
if (strstr (cPs, "kwin") != NULL)
return CD_KWIN;
if (strstr (cPs, "xfwm4") != NULL)
return CD_XFWM;
if (strstr (cPs, "metacity") != NULL)
return CD_METACITY;
return -1;
}
////////////////////////
/// COMPOSITE SIGNAL ///
////////////////////////
static void _on_composited_changed (GdkScreen *pScreen, gpointer data)
{
myData.bIsComposited = gdk_screen_is_composited (pScreen);
cd_draw_current_state ();
}
static void _start_watching_composite_state (void)
{
// get the current state.
GdkScreen *pScreen = gdk_screen_get_default ();
myData.bIsComposited = gdk_screen_is_composited (pScreen);
// draw it.
cd_draw_current_state ();
// listen for future changes.
g_signal_connect (G_OBJECT (pScreen), "composited-changed", G_CALLBACK(_on_composited_changed), NULL);
}
///////////////////
/// PREFERED WM ///
///////////////////
static CDWM *_get_prefered_wmc (CDWMIndex iCurrentWm)
{
cd_debug ("%s (%s, %d)", __func__, myConfig.cWmCompositor, iCurrentWm);
CDWM *wm;
if (myConfig.cWmCompositor != NULL) // a composite WM is defined.
{
wm = _get_wm_by_name (myConfig.cWmCompositor);
if (wm == NULL) // not one of the known WM -> define and take the custom one.
{
wm = _get_wm_by_index (CD_CUSTOM_WMC);
g_free ((gchar*)wm->cCommand);
wm->cCommand = g_strdup (myConfig.cWmCompositor);
return wm;
}
else if (wm->bIsAvailable)
return wm;
}
// no WM defined, or the one defined is not available -> check if a suitable one is running.
if (iCurrentWm < NB_WM) // one of the know WM is running
{
if (myData.bIsComposited) // and it provides composite => let's take it!
{
wm = _get_wm_by_index (iCurrentWm);
if (wm->bIsAvailable) // just to be sure.
return wm;
}
}
// no succes so far, take the most suitable one.
int index[NB_COMPOSITE_WM] = {CD_COMPIZ, CD_KWIN, CD_XFWM, CD_METACITY}; // in this order by default.
switch (g_iDesktopEnv)
{
case CAIRO_DOCK_GNOME:
index[1] = CD_METACITY;
index[3] = CD_KWIN;
break;
case CAIRO_DOCK_XFCE:
index[1] = CD_XFWM;
index[2] = CD_KWIN;
break;
case CAIRO_DOCK_KDE:
default:
break;
}
int i;
for (i = 0; i < NB_COMPOSITE_WM; i ++)
{
wm = _get_wm_by_index (index[i]);
cd_debug (" %d) %s, %d", index[i], wm->cName, wm->bIsAvailable);
if (wm->bIsAvailable)
return wm;
}
return NULL;
}
static CDWM *_get_prefered_wmfb (CDWMIndex iCurrentWm)
{
cd_debug ("%s (%s, %d)", __func__, myConfig.cWmFallback, iCurrentWm);
CDWM *wm;
if (myConfig.cWmFallback != NULL) // a fallback WM is defined.
{
wm = _get_wm_by_name (myConfig.cWmFallback);
if (wm == NULL) // not one of the known WM -> define and take the custom one.
{
wm = _get_wm_by_index (CD_CUSTOM_WMFB);
g_free ((gchar*)wm->cCommand);
wm->cCommand = g_strdup (myConfig.cWmFallback);
return wm;
}
else if (wm->bIsAvailable)
return wm;
}
// no WM defined, or the one defined is not available -> check if a suitable one is running.
if (iCurrentWm < NB_WM) // one of the know WM is running
{
if (!myData.bIsComposited) // and it is a fallback => let's take it!
{
wm = _get_wm_by_index (iCurrentWm);
cd_debug ("current wm: %d, %d", iCurrentWm, wm->bIsAvailable);
if (wm->bIsAvailable) // just to be sure.
return wm;
}
}
// no succes so far, take the most suitable one.
int index[NB_FALLBACK_WM] = {CD_METACITY, CD_XFWM, CD_KWIN}; // in this order by default.
switch (g_iDesktopEnv)
{
case CAIRO_DOCK_GNOME:
index[0] = CD_METACITY;
index[1] = CD_XFWM;
break;
case CAIRO_DOCK_XFCE:
index[0] = CD_XFWM;
index[1] = CD_METACITY;
break;
case CAIRO_DOCK_KDE:
index[0] = CD_KWIN;
index[1] = CD_METACITY;
index[2] = CD_XFWM;
break;
case CAIRO_DOCK_UNKNOWN_ENV:
case CAIRO_DOCK_NB_DESKTOPS:
break;
}
int i;
for (i = 0; i < NB_FALLBACK_WM; i ++)
{
wm = _get_wm_by_index (index[i]);
cd_debug (" %s (%d)", wm->cName, wm->bIsAvailable);
if (wm->bIsAvailable)
return wm;
}
return NULL;
}
static inline gchar *_get_running_wm (void)
{
return cairo_dock_launch_command_sync ("pgrep -l \"kwin$|compiz$|xfwm4$|metacity$\""); // -l = write the name of the program (not the command next to the PID in 'ps -ef'. we add a '$' after the names to avoid listing things like compiz-decorator or xfwm4-settings
}
static void _define_prefered_wms (gchar *cPs)
{
// get the compositor and fallback WMs
CDWMIndex iCurrentWm = _check_current_wm (cPs);
myData.wmc = _get_prefered_wmc (iCurrentWm);
myData.wmfb = _get_prefered_wmfb (iCurrentWm);
cd_debug ("***** WM: %s / %s", myData.wmc?myData.wmc->cName:NULL, myData.wmfb?myData.wmfb->cName:NULL);
}
void cd_define_prefered_wms (void)
{
gchar *ps = _get_running_wm ();
_define_prefered_wms (ps);
g_free (ps);
}
/////////////////
/// INIT/STOP ///
/////////////////
static void _check_wms (CDSharedMemory *pSharedMemory)
{
pSharedMemory->which = cairo_dock_launch_command_sync ("which compiz kwin xfwm4 metacity");
pSharedMemory->ps = _get_running_wm ();
}
static void _update_from_data (CDSharedMemory *pSharedMemory)
{
_check_available_wms (pSharedMemory->which);
_define_prefered_wms (pSharedMemory->ps); // we do it once we know the current state.
cairo_dock_discard_task (myData.pTask);
myData.pTask = NULL;
}
static void _free_shared_memory (CDSharedMemory *pSharedMemory)
{
g_free (pSharedMemory->which);
g_free (pSharedMemory->ps);
g_free (pSharedMemory);
}
void cd_init_wms (void)
{
_define_known_wms ();
_start_watching_composite_state ();
CDSharedMemory *pSharedMemory = g_new0 (CDSharedMemory, 1);
myData.pTask = cairo_dock_new_task_full (0, // one-shot
(CairoDockGetDataAsyncFunc) _check_wms,
(CairoDockUpdateSyncFunc) _update_from_data,
(GFreeFunc) _free_shared_memory,
pSharedMemory);
cairo_dock_launch_task_delayed (myData.pTask, 3000); // 3s delay, since we don't need these info right away.
}
void cd_stop_wms (void)
{
// discard task.
cairo_dock_discard_task (myData.pTask);
// stop listening
GdkScreen *pScreen = gdk_screen_get_default ();
g_signal_handlers_disconnect_by_func (G_OBJECT(pScreen), _on_composited_changed, NULL);
// reset custom WMs.
CDWM *wm;
wm = _get_wm_by_index (CD_CUSTOM_WMC);
g_free ((gchar*)wm->cCommand);
wm = _get_wm_by_index (CD_CUSTOM_WMFB);
g_free ((gchar*)wm->cCommand);
}
////////////
/// DRAW ///
////////////
void cd_draw_current_state (void)
{
cd_debug ("%s (%d)", __func__, myData.bIsComposited);
if (myData.bIsComposited)
CD_APPLET_SET_USER_IMAGE_ON_MY_ICON (myConfig.cIconCompositeON, "composite-on.png");
else
CD_APPLET_SET_USER_IMAGE_ON_MY_ICON (myConfig.cIconCompositeOFF, "composite-off.png");
}
/////////////////////
/// TOGGLE ON/OFF ///
/////////////////////
static gboolean _activate_composite_delayed (gpointer data)
{
if (data)
{
if (myData.wmc->activate_composite != NULL)
myData.wmc->activate_composite (TRUE);
}
else
{
if (myData.wmfb->activate_composite != NULL)
myData.wmfb->activate_composite (FALSE);
}
return FALSE;
}
static gboolean _wm_is_running (CDWM *wm)
{
const gchar *cCommand = wm->cCommand;
gchar *cWhich = g_strdup_printf ("pgrep %s$", cCommand); // see above for the '$' character.
gchar *str = strchr (cWhich+6, ' '); // remove any parameter to the command, we just want the program name.
if (str) // a space is found.
{
*str = '$';
*(str+1) = '\0';
}
gchar *cResult = cairo_dock_launch_command_sync (cWhich);
gboolean bIsRunning = (cResult != NULL && *cResult != '\0');
g_free (cWhich);
g_free (cResult);
return bIsRunning;
}
static void cd_turn_composite_on (void)
{
if (myData.wmc == NULL) // no compositor.
{
gldi_dialog_show_temporary_with_icon (D_("No compositor is available."), myIcon, myContainer, 6000, "same icon");
return;
}
// if not already launched, launch it.
if (! _wm_is_running (myData.wmc)) // not running
{
cairo_dock_launch_command (myData.wmc->cCommand);
g_timeout_add_seconds (2, _activate_composite_delayed, GINT_TO_POINTER (1)); // let the WM start for 2s.
}
else // already running, just toggle composite ON.
{
if (myData.wmc->activate_composite != NULL)
myData.wmc->activate_composite (TRUE);
else
gldi_dialog_show_temporary_with_icon (D_("No compositor is available."), myIcon, myContainer, 6000, "same icon");
}
}
static void cd_turn_composite_off (void)
{
if (myData.wmfb == NULL) // no fallback.
{
gldi_dialog_show_temporary_with_icon (D_("No fallback is available."), myIcon, myContainer, 6000, "same icon");
return;
}
// if not already launched, launch it.
if (! _wm_is_running (myData.wmfb)) // not running
{
cairo_dock_launch_command (myData.wmfb->cCommand);
g_timeout_add_seconds (2, _activate_composite_delayed, 0); // let the WM start for 2s.
}
else // already running, just toggle composite OFF.
{
if (myData.wmfb->activate_composite != NULL)
myData.wmfb->activate_composite (FALSE);
else
gldi_dialog_show_temporary_with_icon (D_("No fallback is available."), myIcon, myContainer, 6000, "same icon");
}
}
void cd_toggle_composite (void)
{
if (myData.bIsComposited)
cd_turn_composite_off ();
else
cd_turn_composite_on ();
}
///////////////////
/// CONFIG TOOL ///
///////////////////
static const gchar *_get_config_tool (void)
{
if (myData.bIsComposited && myData.wmc)
{
return myData.wmc->cConfigTool;
}
else if (!myData.bIsComposited && myData.wmfb)
{
return myData.wmfb->cConfigTool;
}
return NULL;
}
void cd_open_wm_config (void)
{
const gchar *cConfigTool = _get_config_tool ();
if (cConfigTool != NULL)
{
gchar *cmd = g_strdup_printf ("which %s", cConfigTool);
gchar *cResult = cairo_dock_launch_command_sync (cmd);
g_free (cmd);
if (cResult == NULL || *cResult != '/')
{
gchar *msg = g_strdup_printf (D_("You need to install '%s'"), cConfigTool);
gldi_dialog_show_temporary_with_icon (msg, myIcon, myContainer, 6000, "same icon");
g_free (msg);
}
else
{
cairo_dock_launch_command (cConfigTool);
}
}
else
{
gldi_dialog_show_temporary_with_icon (D_("No configuration tool is available."), myIcon, myContainer, 6000, "same icon");
}
}
static const gchar *_get_command (void)
{
if (myData.bIsComposited && myData.wmc)
{
return myData.wmc->cCommand;
}
else if (!myData.bIsComposited && myData.wmfb)
{
return myData.wmfb->cCommand;
}
return NULL;
}
void cd_reload_wm (void)
{
const gchar *cCommand = _get_command ();
if (cCommand != NULL)
{
cairo_dock_launch_command (cCommand);
}
}