/*
* Copyright © 2005 Novell, Inc.
*
* 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,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
* NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECI<<<<<fAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* 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>
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <math.h>
#include <dlfcn.h>
#include <string.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <assert.h>
#include <limits.h>
#include <poll.h>
#include <algorithm>
#include <boost/bind.hpp>
#include <boost/foreach.hpp>
#define foreach BOOST_FOREACH
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xproto.h>
#include <X11/extensions/Xrandr.h>
#include <X11/extensions/shape.h>
#include <X11/cursorfont.h>
#include <core/core.h>
#include <core/screen.h>
#include <core/icon.h>
#include <core/atoms.h>
#include "privatescreen.h"
#include "privatewindow.h"
static unsigned int virtualModMask[] = {
CompAltMask, CompMetaMask, CompSuperMask, CompHyperMask,
CompModeSwitchMask, CompNumLockMask, CompScrollLockMask
};
bool inHandleEvent = false;
CompScreen *targetScreen = NULL;
CompOutput *targetOutput;
int lastPointerX = 0;
int lastPointerY = 0;
int pointerX = 0;
int pointerY = 0;
#define MwmHintsFunctions (1L << 0)
#define MwmHintsDecorations (1L << 1)
#define PropMotifWmHintElements 3
typedef struct {
unsigned long flags;
unsigned long functions;
unsigned long decorations;
} MwmHints;
CompScreen *screen;
#define NUM_OPTIONS(s) (sizeof ((s)->priv->opt) / sizeof (CompOption))
CompPrivateStorage::Indices screenPrivateIndices (0);
int
CompScreen::allocPrivateIndex ()
{
int i = CompPrivateStorage::allocatePrivateIndex (&screenPrivateIndices);
if (screenPrivateIndices.size () != screen->privates.size ())
screen->privates.resize (screenPrivateIndices.size ());
return i;
}
void
CompScreen::freePrivateIndex (int index)
{
CompPrivateStorage::freePrivateIndex (&screenPrivateIndices, index);
if (screenPrivateIndices.size () != screen->privates.size ())
screen->privates.resize (screenPrivateIndices.size ());
}
#define TIMEVALDIFF(tv1, tv2) \
((tv1)->tv_sec == (tv2)->tv_sec || (tv1)->tv_usec >= (tv2)->tv_usec) ? \
((((tv1)->tv_sec - (tv2)->tv_sec) * 1000000) + \
((tv1)->tv_usec - (tv2)->tv_usec)) / 1000 : \
((((tv1)->tv_sec - 1 - (tv2)->tv_sec) * 1000000) + \
(1000000 + (tv1)->tv_usec - (tv2)->tv_usec)) / 1000
void
CompScreen::eventLoop ()
{
struct timeval tv;
CompTimer *t;
int time;
CompWatchFdHandle watchFdHandle;
watchFdHandle = addWatchFd (ConnectionNumber (priv->dpy), POLLIN, NULL);
for (;;)
{
if (restartSignal || shutDown)
break;
priv->processEvents ();
if (!priv->timers.empty())
{
gettimeofday (&tv, 0);
priv->handleTimers (&tv);
if (priv->timers.front()->mMinLeft > 0)
{
std::list<CompTimer *>::iterator it = priv->timers.begin();
t = (*it);
time = t->mMaxLeft;
while (it != priv->timers.end())
{
t = (*it);
if (t->mMinLeft <= time)
break;
if (t->mMaxLeft < time)
time = t->mMaxLeft;
it++;
}
if (time < 5)
usleep (time * 1000);
else
priv->doPoll (time);
gettimeofday (&tv, 0);
priv->handleTimers (&tv);
}
}
else
{
priv->doPoll (-1);
}
}
removeWatchFd (watchFdHandle);
}
CompFileWatchHandle
CompScreen::addFileWatch (const char *path,
int mask,
FileWatchCallBack callBack)
{
CompFileWatch *fileWatch = new CompFileWatch();
if (!fileWatch)
return 0;
fileWatch->path = path;
fileWatch->mask = mask;
fileWatch->callBack = callBack;
fileWatch->handle = priv->lastFileWatchHandle++;
if (priv->lastFileWatchHandle == MAXSHORT)
priv->lastFileWatchHandle = 1;
priv->fileWatch.push_front(fileWatch);
fileWatchAdded (fileWatch);
return fileWatch->handle;
}
void
CompScreen::removeFileWatch (CompFileWatchHandle handle)
{
std::list<CompFileWatch *>::iterator it;
CompFileWatch *w;
for (it = priv->fileWatch.begin(); it != priv->fileWatch.end(); it++)
if ((*it)->handle == handle)
break;
if (it == priv->fileWatch.end())
return;
w = (*it);
priv->fileWatch.erase (it);
fileWatchRemoved (w);
delete w;
}
const CompFileWatchList &
CompScreen::getFileWatches () const
{
return priv->fileWatch;
}
void
PrivateScreen::addTimer (CompTimer *timer)
{
std::list<CompTimer *>::iterator it;
it = std::find (timers.begin (), timers.end (), timer);
if (it != timers.end ())
return;
for (it = timers.begin(); it != timers.end(); it++)
{
if ((int) timer->mMinTime < (*it)->mMinLeft)
break;
}
timer->mMinLeft = timer->mMinTime;
timer->mMaxLeft = timer->mMaxTime;
timers.insert (it, timer);
}
void
PrivateScreen::removeTimer (CompTimer *timer)
{
std::list<CompTimer *>::iterator it;
it = std::find (timers.begin (), timers.end (), timer);
if (it == timers.end ())
return;
timers.erase (it);
}
CompWatchFdHandle
CompScreen::addWatchFd (int fd,
short int events,
FdWatchCallBack callBack)
{
CompWatchFd *watchFd = new CompWatchFd();
if (!watchFd)
return 0;
watchFd->fd = fd;
watchFd->callBack = callBack;
watchFd->handle = priv->lastWatchFdHandle++;
if (priv->lastWatchFdHandle == MAXSHORT)
priv->lastWatchFdHandle = 1;
priv->watchFds.push_front (watchFd);
priv->nWatchFds++;
priv->watchPollFds = (struct pollfd *) realloc (priv->watchPollFds,
priv->nWatchFds * sizeof (struct pollfd));
priv->watchPollFds[priv->nWatchFds - 1].fd = fd;
priv->watchPollFds[priv->nWatchFds - 1].events = events;
return watchFd->handle;
}
void
CompScreen::removeWatchFd (CompWatchFdHandle handle)
{
std::list<CompWatchFd *>::iterator it;
CompWatchFd *w;
int i;
for (it = priv->watchFds.begin(), i = priv->nWatchFds - 1;
it != priv->watchFds.end(); it++, i--)
if ((*it)->handle == handle)
break;
if (it == priv->watchFds.end())
return;
w = (*it);
priv->watchFds.erase (it);
priv->nWatchFds--;
if (i < priv->nWatchFds)
memmove (&priv->watchPollFds[i], &priv->watchPollFds[i + 1],
(priv->nWatchFds - i) * sizeof (struct pollfd));
delete w;
}
void
CompScreen::storeValue (CompString key, CompPrivate value)
{
std::map<CompString,CompPrivate>::iterator it;
it = priv->valueMap.find (key);
if (it != priv->valueMap.end ())
{
it->second = value;
}
else
{
priv->valueMap.insert (std::pair<CompString,CompPrivate> (key, value));
}
}
bool
CompScreen::hasValue (CompString key)
{
return (priv->valueMap.find (key) != priv->valueMap.end ());
}
CompPrivate
CompScreen::getValue (CompString key)
{
CompPrivate p;
std::map<CompString,CompPrivate>::iterator it;
it = priv->valueMap.find (key);
if (it != priv->valueMap.end ())
{
return it->second;
}
else
{
p.uval = 0;
return p;
}
}
void
CompScreen::eraseValue (CompString key)
{
std::map<CompString,CompPrivate>::iterator it;
it = priv->valueMap.find (key);
if (it != priv->valueMap.end ())
{
priv->valueMap.erase (key);
}
}
short int
PrivateScreen::watchFdEvents (CompWatchFdHandle handle)
{
std::list<CompWatchFd *>::iterator it;
int i;
for (it = watchFds.begin(), i = nWatchFds - 1; it != watchFds.end();
it++, i--)
if ((*it)->handle == handle)
return watchPollFds[i].revents;
return 0;
}
int
PrivateScreen::doPoll (int timeout)
{
int rv;
rv = poll (watchPollFds, nWatchFds, timeout);
if (rv)
{
std::list<CompWatchFd *>::iterator it;
int i;
for (it = watchFds.begin(), i = nWatchFds - 1; it != watchFds.end();
it++, i--)
if (watchPollFds[i].revents != 0 && (*it)->callBack)
(*it)->callBack ();
}
return rv;
}
void
PrivateScreen::handleTimers (struct timeval *tv)
{
CompTimer *t;
int timeDiff;
std::list<CompTimer *>::iterator it;
timeDiff = TIMEVALDIFF (tv, &lastTimeout);
/* handle clock rollback */
if (timeDiff < 0)
timeDiff = 0;
for (it = timers.begin(); it != timers.end(); it++)
{
t = (*it);
t->mMinLeft -= timeDiff;
t->mMaxLeft -= timeDiff;
}
while (timers.begin() != timers.end() &&
(*timers.begin())->mMinLeft <= 0)
{
t = (*timers.begin());
timers.pop_front();
t->mActive = false;
if (t->mCallBack ())
{
addTimer (t);
t->mActive = true;
}
}
lastTimeout = *tv;
}
void
CompScreen::fileWatchAdded (CompFileWatch *watch)
WRAPABLE_HND_FUNC(0, fileWatchAdded, watch)
void
CompScreen::fileWatchRemoved (CompFileWatch *watch)
WRAPABLE_HND_FUNC(1, fileWatchRemoved, watch)
bool
CompScreen::setOptionForPlugin (const char *plugin,
const char *name,
CompOption::Value &value)
{
WRAPABLE_HND_FUNC_RETURN(4, bool, setOptionForPlugin,
plugin, name, value)
CompPlugin *p = CompPlugin::find (plugin);
if (p)
return p->vTable->setOption (name, value);
return false;
}
void
CompScreen::sessionEvent (CompSession::Event event,
CompOption::Vector &arguments)
WRAPABLE_HND_FUNC(5, sessionEvent, event, arguments)
void
ScreenInterface::fileWatchAdded (CompFileWatch *watch)
WRAPABLE_DEF (fileWatchAdded, watch)
void
ScreenInterface::fileWatchRemoved (CompFileWatch *watch)
WRAPABLE_DEF (fileWatchRemoved, watch)
bool
ScreenInterface::initPluginForScreen (CompPlugin *plugin)
WRAPABLE_DEF (initPluginForScreen, plugin)
void
ScreenInterface::finiPluginForScreen (CompPlugin *plugin)
WRAPABLE_DEF (finiPluginForScreen, plugin)
bool
ScreenInterface::setOptionForPlugin (const char *plugin,
const char *name,
CompOption::Value &value)
WRAPABLE_DEF (setOptionForPlugin, plugin, name, value)
void
ScreenInterface::sessionEvent (CompSession::Event event,
CompOption::Vector &arguments)
WRAPABLE_DEF (sessionEvent, event, arguments)
const CompMetadata::OptionInfo coreOptionInfo[COMP_OPTION_NUM] = {
{ "active_plugins", "list", "<type>string</type>", 0, 0 },
{ "click_to_focus", "bool", 0, 0, 0 },
{ "autoraise", "bool", 0, 0, 0 },
{ "autoraise_delay", "int", 0, 0, 0 },
{ "close_window_key", "key", 0, CompScreen::closeWin, 0 },
{ "close_window_button", "button", 0, CompScreen::closeWin, 0 },
{ "main_menu_key", "key", 0, CompScreen::mainMenu, 0 },
{ "run_key", "key", 0, CompScreen::runDialog, 0 },
{ "command0", "string", 0, 0, 0 },
{ "command1", "string", 0, 0, 0 },
{ "command2", "string", 0, 0, 0 },
{ "command3", "string", 0, 0, 0 },
{ "command4", "string", 0, 0, 0 },
{ "command5", "string", 0, 0, 0 },
{ "command6", "string", 0, 0, 0 },
{ "command7", "string", 0, 0, 0 },
{ "command8", "string", 0, 0, 0 },
{ "command9", "string", 0, 0, 0 },
{ "command10", "string", 0, 0, 0 },
{ "command11", "string", 0, 0, 0 },
{ "run_command0_key", "key", 0, CompScreen::runCommandDispatch, 0 },
{ "run_command1_key", "key", 0, CompScreen::runCommandDispatch, 0 },
{ "run_command2_key", "key", 0, CompScreen::runCommandDispatch, 0 },
{ "run_command3_key", "key", 0, CompScreen::runCommandDispatch, 0 },
{ "run_command4_key", "key", 0, CompScreen::runCommandDispatch, 0 },
{ "run_command5_key", "key", 0, CompScreen::runCommandDispatch, 0 },
{ "run_command6_key", "key", 0, CompScreen::runCommandDispatch, 0 },
{ "run_command7_key", "key", 0, CompScreen::runCommandDispatch, 0 },
{ "run_command8_key", "key", 0, CompScreen::runCommandDispatch, 0 },
{ "run_command9_key", "key", 0, CompScreen::runCommandDispatch, 0 },
{ "run_command10_key", "key", 0, CompScreen::runCommandDispatch, 0 },
{ "run_command11_key", "key", 0, CompScreen::runCommandDispatch, 0 },
{ "raise_window_key", "key", 0, CompScreen::raiseWin, 0 },
{ "raise_window_button", "button", 0, CompScreen::raiseWin, 0 },
{ "lower_window_key", "key", 0, CompScreen::lowerWin, 0 },
{ "lower_window_button", "button", 0, CompScreen::lowerWin, 0 },
{ "unmaximize_window_key", "key", 0, CompScreen::unmaximizeWin, 0 },
{ "minimize_window_key", "key", 0, CompScreen::minimizeWin, 0 },
{ "minimize_window_button", "button", 0, CompScreen::minimizeWin, 0 },
{ "maximize_window_key", "key", 0, CompScreen::maximizeWin, 0 },
{ "maximize_window_horizontally_key", "key", 0,
CompScreen::maximizeWinHorizontally, 0 },
{ "maximize_window_vertically_key", "key", 0,
CompScreen::maximizeWinVertically, 0 },
{ "command_screenshot", "string", 0, 0, 0 },
{ "run_command_screenshot_key", "key", 0,
CompScreen::runCommandScreenshot, 0 },
{ "command_window_screenshot", "string", 0, 0, 0 },
{ "run_command_window_screenshot_key", "key", 0,
CompScreen::runCommandWindowScreenshot, 0 },
{ "window_menu_button", "button", 0, CompScreen::windowMenu, 0 },
{ "window_menu_key", "key", 0, CompScreen::windowMenu, 0 },
{ "show_desktop_key", "key", 0, CompScreen::showDesktop, 0 },
{ "show_desktop_edge", "edge", 0, CompScreen::showDesktop, 0 },
{ "raise_on_click", "bool", 0, 0, 0 },
{ "audible_bell", "bool", 0, 0, 0 },
{ "toggle_window_maximized_key", "key", 0,
CompScreen::toggleWinMaximized, 0 },
{ "toggle_window_maximized_button", "button", 0,
CompScreen::toggleWinMaximized, 0 },
{ "toggle_window_maximized_horizontally_key", "key", 0,
CompScreen::toggleWinMaximizedHorizontally, 0 },
{ "toggle_window_maximized_vertically_key", "key", 0,
CompScreen::toggleWinMaximizedVertically, 0 },
{ "hide_skip_taskbar_windows", "bool", 0, 0, 0 },
{ "toggle_window_shaded_key", "key", 0, CompScreen::shadeWin, 0 },
{ "ignore_hints_when_maximized", "bool", 0, 0, 0 },
{ "command_terminal", "string", 0, 0, 0 },
{ "run_command_terminal_key", "key", 0,
CompScreen::runCommandTerminal, 0 },
{ "ping_delay", "int", "<min>1000</min>", 0, 0 },
{ "edge_delay", "int", "<min>0</min>", 0, 0 },
{ "hsize", "int", "<min>1</min><max>32</max>", 0, 0 },
{ "vsize", "int", "<min>1</min><max>32</max>", 0, 0 },
{ "default_icon", "string", 0, 0, 0 },
{ "number_of_desktops", "int", "<min>1</min>", 0, 0 },
{ "detect_outputs", "bool", 0, 0, 0 },
{ "outputs", "list", "<type>string</type>", 0, 0 },
{ "overlapping_outputs", "int",
RESTOSTRING (0, OUTPUT_OVERLAP_MODE_LAST), 0, 0 },
{ "focus_prevention_level", "int",
RESTOSTRING (0, FOCUS_PREVENTION_LEVEL_LAST), 0, 0 },
{ "focus_prevention_match", "match", 0, 0, 0 }
};
static const int maskTable[] = {
ShiftMask, LockMask, ControlMask, Mod1Mask,
Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
};
static const int maskTableSize = sizeof (maskTable) / sizeof (int);
static int errors = 0;
static int
errorHandler (Display *dpy,
XErrorEvent *e)
{
#ifdef DEBUG
char str[128];
#endif
errors++;
#ifdef DEBUG
XGetErrorDatabaseText (dpy, "XlibMessage", "XError", "", str, 128);
fprintf (stderr, "%s", str);
XGetErrorText (dpy, e->error_code, str, 128);
fprintf (stderr, ": %s\n ", str);
XGetErrorDatabaseText (dpy, "XlibMessage", "MajorCode", "%d", str, 128);
fprintf (stderr, str, e->request_code);
sprintf (str, "%d", e->request_code);
XGetErrorDatabaseText (dpy, "XRequest", str, "", str, 128);
if (strcmp (str, ""))
fprintf (stderr, " (%s)", str);
fprintf (stderr, "\n ");
XGetErrorDatabaseText (dpy, "XlibMessage", "MinorCode", "%d", str, 128);
fprintf (stderr, str, e->minor_code);
fprintf (stderr, "\n ");
XGetErrorDatabaseText (dpy, "XlibMessage", "ResourceID", "%d", str, 128);
fprintf (stderr, str, e->resourceid);
fprintf (stderr, "\n");
/* abort (); */
#endif
return 0;
}
int
CompScreen::checkForError (Display *dpy)
{
int e;
XSync (dpy, FALSE);
e = errors;
errors = 0;
return e;
}
Display *
CompScreen::dpy ()
{
return priv->dpy;
}
CompOption *
CompScreen::getOption (const char *name)
{
CompOption *o = CompOption::findOption (priv->opt, name);
return o;
}
bool
CompScreen::XRandr ()
{
return priv->randrExtension;
}
int
CompScreen::randrEvent ()
{
return priv->randrEvent;
}
bool
CompScreen::XShape ()
{
return priv->shapeExtension;
}
int
CompScreen::shapeEvent ()
{
return priv->shapeEvent;
}
int
CompScreen::syncEvent ()
{
return priv->syncEvent;
}
SnDisplay *
CompScreen::snDisplay ()
{
return priv->snDisplay;
}
Window
CompScreen::activeWindow ()
{
return priv->activeWindow;
}
Window
CompScreen::autoRaiseWindow ()
{
return priv->autoRaiseWindow;
}
const char *
CompScreen::displayString ()
{
return priv->displayString;
}
void
PrivateScreen::updateScreenInfo ()
{
if (xineramaExtension)
{
int nInfo;
XineramaScreenInfo *info = XineramaQueryScreens (dpy, &nInfo);
screenInfo = std::vector<XineramaScreenInfo> (info, info + nInfo);
if (info)
XFree (info);
}
}
void
PrivateScreen::setAudibleBell (bool audible)
{
if (xkbExtension)
XkbChangeEnabledControls (dpy,
XkbUseCoreKbd,
XkbAudibleBellMask,
audible ? XkbAudibleBellMask : 0);
}
bool
PrivateScreen::handlePingTimeout ()
{
XEvent ev;
int ping = lastPing + 1;
ev.type = ClientMessage;
ev.xclient.window = 0;
ev.xclient.message_type = Atoms::wmProtocols;
ev.xclient.format = 32;
ev.xclient.data.l[0] = Atoms::wmPing;
ev.xclient.data.l[1] = ping;
ev.xclient.data.l[2] = 0;
ev.xclient.data.l[3] = 0;
ev.xclient.data.l[4] = 0;
foreach (CompWindow *w, windows)
{
if (w->priv->handlePingTimeout (lastPing))
{
ev.xclient.window = w->id ();
ev.xclient.data.l[2] = w->id ();
XSendEvent (dpy, w->id (), false, NoEventMask, &ev);
}
}
lastPing = ping;
return true;
}
CompOption::Vector &
CompScreen::getOptions ()
{
return priv->opt;
}
bool
CompScreen::setOption (const char *name,
CompOption::Value &value)
{
CompOption *o;
unsigned int index;
o = CompOption::findOption (priv->opt, name, &index);
if (!o)
return false;
switch (index) {
case COMP_OPTION_ACTIVE_PLUGINS:
if (o->set (value))
{
priv->dirtyPluginList = true;
return true;
}
break;
case COMP_OPTION_PING_DELAY:
if (o->set (value))
{
priv->pingTimer.setTimes (o->value ().i (),
o->value ().i () + 500);
return true;
}
break;
case COMP_OPTION_AUDIBLE_BELL:
if (o->set (value))
{
priv->setAudibleBell (o->value ().b ());
return true;
}
break;
case COMP_OPTION_DETECT_OUTPUTS:
if (o->set (value))
{
if (value.b ())
priv->detectOutputDevices ();
return true;
}
break;
case COMP_OPTION_HSIZE:
if (o->set (value))
{
CompOption *vsize;
vsize = CompOption::findOption (priv->opt, "vsize");
if (!vsize)
return false;
if (o->value ().i () * width () > MAXSHORT)
return false;
priv->setVirtualScreenSize (o->value ().i (), vsize->value ().i ());
return true;
}
break;
case COMP_OPTION_VSIZE:
if (o->set (value))
{
CompOption *hsize;
hsize = CompOption::findOption (priv->opt, "hsize");
if (!hsize)
return false;
if (o->value ().i () * height () > MAXSHORT)
return false;
priv->setVirtualScreenSize (hsize->value ().i (), o->value ().i ());
return true;
}
break;
case COMP_OPTION_NUMBER_OF_DESKTOPS:
if (o->set (value))
{
priv->setNumberOfDesktops (o->value ().i ());
return true;
}
break;
case COMP_OPTION_DEFAULT_ICON:
if (o->set (value))
return updateDefaultIcon ();
break;
case COMP_OPTION_OUTPUTS:
if (!noDetection &&
priv->opt[COMP_OPTION_DETECT_OUTPUTS].value ().b ())
return false;
if (o->set (value))
{
priv->updateOutputDevices ();
return true;
}
break;
default:
if (CompOption::setOption (*o, value))
return true;
break;
}
return false;
}
void
PrivateScreen::updateModifierMappings ()
{
unsigned int modMask[CompModNum];
int i, minKeycode, maxKeycode, keysymsPerKeycode = 0;
KeySym* key;
for (i = 0; i < CompModNum; i++)
modMask[i] = 0;
XDisplayKeycodes (this->dpy, &minKeycode, &maxKeycode);
key = XGetKeyboardMapping (this->dpy,
minKeycode, (maxKeycode - minKeycode + 1),
&keysymsPerKeycode);
if (this->modMap)
XFreeModifiermap (this->modMap);
this->modMap = XGetModifierMapping (this->dpy);
if (this->modMap && this->modMap->max_keypermod > 0)
{
KeySym keysym;
int index, size, mask;
size = maskTableSize * this->modMap->max_keypermod;
for (i = 0; i < size; i++)
{
if (!this->modMap->modifiermap[i])
continue;
index = 0;
do
{
keysym = XKeycodeToKeysym (this->dpy,
this->modMap->modifiermap[i],
index++);
} while (!keysym && index < keysymsPerKeycode);
if (keysym)
{
mask = maskTable[i / this->modMap->max_keypermod];
if (keysym == XK_Alt_L ||
keysym == XK_Alt_R)
{
modMask[CompModAlt] |= mask;
}
else if (keysym == XK_Meta_L ||
keysym == XK_Meta_R)
{
modMask[CompModMeta] |= mask;
}
else if (keysym == XK_Super_L ||
keysym == XK_Super_R)
{
modMask[CompModSuper] |= mask;
}
else if (keysym == XK_Hyper_L ||
keysym == XK_Hyper_R)
{
modMask[CompModHyper] |= mask;
}
else if (keysym == XK_Mode_switch)
{
modMask[CompModModeSwitch] |= mask;
}
else if (keysym == XK_Scroll_Lock)
{
modMask[CompModScrollLock] |= mask;
}
else if (keysym == XK_Num_Lock)
{
modMask[CompModNumLock] |= mask;
}
}
}
for (i = 0; i < CompModNum; i++)
{
if (!modMask[i])
modMask[i] = CompNoMask;
}
if (memcmp (modMask, this->modMask, sizeof (modMask)))
{
memcpy (this->modMask, modMask, sizeof (modMask));
this->ignoredModMask = LockMask |
(modMask[CompModNumLock] & ~CompNoMask) |
(modMask[CompModScrollLock] & ~CompNoMask);
this->updatePassiveKeyGrabs ();
}
}
if (key)
XFree (key);
}
unsigned int
PrivateScreen::virtualToRealModMask (unsigned int modMask)
{
int i;
for (i = 0; i < CompModNum; i++)
{
if (modMask & virtualModMask[i])
{
modMask &= ~virtualModMask[i];
modMask |= this->modMask[i];
}
}
return modMask;
}
unsigned int
PrivateScreen::keycodeToModifiers (int keycode)
{
unsigned int mods = 0;
int mod, k;
for (mod = 0; mod < maskTableSize; mod++)
{
for (k = 0; k < modMap->max_keypermod; k++)
{
if (modMap->modifiermap[mod *
modMap->max_keypermod + k] == keycode)
mods |= maskTable[mod];
}
}
return mods;
}
void
PrivateScreen::processEvents ()
{
XEvent event;
/* remove destroyed windows */
removeDestroyed ();
if (dirtyPluginList)
updatePlugins ();
while (XPending (dpy))
{
XNextEvent (dpy, &event);
switch (event.type) {
case ButtonPress:
case ButtonRelease:
pointerX = event.xbutton.x_root;
pointerY = event.xbutton.y_root;
break;
case KeyPress:
case KeyRelease:
pointerX = event.xkey.x_root;
pointerY = event.xkey.y_root;
break;
case MotionNotify:
pointerX = event.xmotion.x_root;
pointerY = event.xmotion.y_root;
break;
case EnterNotify:
case LeaveNotify:
pointerX = event.xcrossing.x_root;
pointerY = event.xcrossing.y_root;
break;
case ClientMessage:
if (event.xclient.message_type == Atoms::xdndPosition)
{
pointerX = event.xclient.data.l[2] >> 16;
pointerY = event.xclient.data.l[2] & 0xffff;
}
default:
break;
}
sn_display_process_event (snDisplay, &event);
inHandleEvent = true;
screen->handleEvent (&event);
inHandleEvent = false;
lastPointerX = pointerX;
lastPointerY = pointerY;
}
}
void
PrivateScreen::updatePlugins ()
{
CompOption *o;
CompOption::Value value;
CompPlugin *p;
unsigned int nPop, i, j;
CompPlugin::List pop;
bool failedPush;
dirtyPluginList = false;
o = &opt[COMP_OPTION_ACTIVE_PLUGINS];
value = o->value ();
/* The old plugin list always begins with the core plugin. To make sure
we don't unnecessarily unload plugins if the new plugin list does not
contain the core plugin, we have to use an offset */
if (value.list ().size () > 0 && value.list ()[0]. s () != "core")
i = 0;
else
i = 1;
/* j is initialized to 1 to make sure we never pop the core plugin */
for (j = 1; j < plugin.list ().size () &&
i < value.list ().size (); i++, j++)
{
if (plugin.list ()[j].s () != value.list ()[i].s ())
break;
}
nPop = plugin.list ().size () - j;
for (j = 0; j < nPop; j++)
{
pop.push_back (CompPlugin::pop ());
plugin.list ().pop_back ();
}
for (; i < value.list ().size (); i++)
{
p = NULL;
failedPush = false;
foreach (CompPlugin *pp, pop)
{
if (value.list ()[i]. s () == pp->vTable->name ())
{
if (CompPlugin::push (pp))
{
p = pp;
pop.erase (std::find (pop.begin (), pop.end (), pp));
break;
}
else
{
pop.erase (std::find (pop.begin (), pop.end (), pp));
CompPlugin::unload (pp);
p = NULL;
failedPush = true;
break;
}
}
}
if (p == 0 && !failedPush)
{
p = CompPlugin::load (value.list ()[i].s ().c_str ());
if (p)
{
if (!CompPlugin::push (p))
{
CompPlugin::unload (p);
p = 0;
}
}
}
if (p)
plugin.list ().push_back (p->vTable->name ());
}
foreach (CompPlugin *pp, pop)
CompPlugin::unload (pp);
if (!priv->dirtyPluginList)
screen->setOptionForPlugin ("core", o->name ().c_str (), plugin);
}
/* from fvwm2, Copyright Matthias Clasen, Dominik Vogt */
static bool
convertProperty (Display *dpy,
Time time,
Window w,
Atom target,
Atom property)
{
#define N_TARGETS 4
Atom conversionTargets[N_TARGETS];
long icccmVersion[] = { 2, 0 };
conversionTargets[0] = Atoms::targets;
conversionTargets[1] = Atoms::multiple;
conversionTargets[2] = Atoms::timestamp;
conversionTargets[3] = Atoms::version;
if (target == Atoms::targets)
XChangeProperty (dpy, w, property,
XA_ATOM, 32, PropModeReplace,
(unsigned char *) conversionTargets, N_TARGETS);
else if (target == Atoms::timestamp)
XChangeProperty (dpy, w, property,
XA_INTEGER, 32, PropModeReplace,
(unsigned char *) &time, 1);
else if (target == Atoms::version)
XChangeProperty (dpy, w, property,
XA_INTEGER, 32, PropModeReplace,
(unsigned char *) icccmVersion, 2);
else
return false;
/* Be sure the PropertyNotify has arrived so we
* can send SelectionNotify
*/
XSync (dpy, FALSE);
return true;
}
/* from fvwm2, Copyright Matthias Clasen, Dominik Vogt */
void
PrivateScreen::handleSelectionRequest (XEvent *event)
{
XSelectionEvent reply;
if (wmSnSelectionWindow != event->xselectionrequest.owner ||
wmSnAtom != event->xselectionrequest.selection)
return;
reply.type = SelectionNotify;
reply.display = dpy;
reply.requestor = event->xselectionrequest.requestor;
reply.selection = event->xselectionrequest.selection;
reply.target = event->xselectionrequest.target;
reply.property = None;
reply.time = event->xselectionrequest.time;
if (event->xselectionrequest.target == Atoms::multiple)
{
if (event->xselectionrequest.property != None)
{
Atom type, *adata;
int i, format;
unsigned long num, rest;
unsigned char *data;
if (XGetWindowProperty (dpy,
event->xselectionrequest.requestor,
event->xselectionrequest.property,
0, 256, FALSE,
Atoms::atomPair,
&type, &format, &num, &rest,
&data) != Success)
return;
/* FIXME: to be 100% correct, should deal with rest > 0,
* but since we have 4 possible targets, we will hardly ever
* meet multiple requests with a length > 8
*/
adata = (Atom *) data;
i = 0;
while (i < (int) num)
{
if (!convertProperty (dpy, wmSnTimestamp,
event->xselectionrequest.requestor,
adata[i], adata[i + 1]))
adata[i + 1] = None;
i += 2;
}
XChangeProperty (dpy,
event->xselectionrequest.requestor,
event->xselectionrequest.property,
Atoms::atomPair,
32, PropModeReplace, data, num);
if (data)
XFree (data);
}
}
else
{
if (event->xselectionrequest.property == None)
event->xselectionrequest.property = event->xselectionrequest.target;
if (convertProperty (dpy, wmSnTimestamp,
event->xselectionrequest.requestor,
event->xselectionrequest.target,
event->xselectionrequest.property))
reply.property = event->xselectionrequest.property;
}
XSendEvent (dpy, event->xselectionrequest.requestor,
FALSE, 0L, (XEvent *) &reply);
}
void
PrivateScreen::handleSelectionClear (XEvent *event)
{
/* We need to unmanage the screen on which we lost the selection */
if (wmSnSelectionWindow != event->xselectionclear.window ||
wmSnAtom != event->xselectionclear.selection)
return;
shutDown = TRUE;
}
#define HOME_IMAGEDIR ".compiz/images"
bool
CompScreen::readImageFromFile (CompString &name,
CompSize &size,
void *&data)
{
bool status;
int stride;
status = fileToImage (name, size, stride, data);
if (!status)
{
char *home = getenv ("HOME");
CompString path;
if (home)
{
path = home;
path += "/";
path += HOME_IMAGEDIR;
path += name;
status = fileToImage (path, size, stride, data);
if (status)
return true;
}
path = IMAGEDIR + name;
status = fileToImage (path, size, stride, data);
}
return status;
}
bool
CompScreen::writeImageToFile (CompString &path,
const char *format,
CompSize &size,
void *data)
{
CompString formatString (format);
return imageToFile (path, formatString, size, size.width () * 4, data);
}
Window
PrivateScreen::getActiveWindow (Window root)
{
Atom actual;
int result, format;
unsigned long n, left;
unsigned char *data;
Window w = None;
result = XGetWindowProperty (priv->dpy, root,
Atoms::winActive, 0L, 1L, FALSE,
XA_WINDOW, &actual, &format,
&n, &left, &data);
if (result == Success && data)
{
if (n)
memcpy (&w, data, sizeof (Window));
XFree (data);
}
return w;
}
bool
CompScreen::fileToImage (CompString &name,
CompSize &size,
int &stride,
void *&data)
{
WRAPABLE_HND_FUNC_RETURN(8, bool, fileToImage, name, size, stride, data);
return false;
}
bool
CompScreen::imageToFile (CompString &path,
CompString &format,
CompSize &size,
int stride,
void *data)
{
WRAPABLE_HND_FUNC_RETURN(9, bool, imageToFile, path, format, size,
stride, data)
return false;
}
const char *
logLevelToString (CompLogLevel level)
{
switch (level) {
case CompLogLevelFatal:
return "Fatal";
case CompLogLevelError:
return "Error";
case CompLogLevelWarn:
return "Warn";
case CompLogLevelInfo:
return "Info";
case CompLogLevelDebug:
return "Debug";
default:
break;
}
return "Unknown";
}
static void
logMessage (const char *componentName,
CompLogLevel level,
const char *message)
{
fprintf (stderr, "%s (%s) - %s: %s\n",
programName, componentName,
logLevelToString (level), message);
}
void
CompScreen::logMessage (const char *componentName,
CompLogLevel level,
const char *message)
{
WRAPABLE_HND_FUNC(13, logMessage, componentName, level, message)
::logMessage (componentName, level, message);
}
void
compLogMessage (const char *componentName,
CompLogLevel level,
const char *format,
...)
{
va_list args;
char message[2048];
va_start (args, format);
vsnprintf (message, 2048, format, args);
if (screen)
screen->logMessage (componentName, level, message);
else
logMessage (componentName, level, message);
va_end (args);
}
int
PrivateScreen::getWmState (Window id)
{
Atom actual;
int result, format;
unsigned long n, left;
unsigned char *data;
unsigned long state = NormalState;
result = XGetWindowProperty (priv->dpy, id,
Atoms::wmState, 0L, 2L, FALSE,
Atoms::wmState, &actual, &format,
&n, &left, &data);
if (result == Success && data)
{
if (n)
memcpy (&state, data, sizeof (unsigned long));
XFree ((void *) data);
}
return state;
}
void
PrivateScreen::setWmState (int state, Window id)
{
unsigned long data[2];
data[0] = state;
data[1] = None;
XChangeProperty (priv->dpy, id,
Atoms::wmState, Atoms::wmState,
32, PropModeReplace, (unsigned char *) data, 2);
}
unsigned int
PrivateScreen::windowStateMask (Atom state)
{
if (state == Atoms::winStateModal)
return CompWindowStateModalMask;
else if (state == Atoms::winStateSticky)
return CompWindowStateStickyMask;
else if (state == Atoms::winStateMaximizedVert)
return CompWindowStateMaximizedVertMask;
else if (state == Atoms::winStateMaximizedHorz)
return CompWindowStateMaximizedHorzMask;
else if (state == Atoms::winStateShaded)
return CompWindowStateShadedMask;
else if (state == Atoms::winStateSkipTaskbar)
return CompWindowStateSkipTaskbarMask;
else if (state == Atoms::winStateSkipPager)
return CompWindowStateSkipPagerMask;
else if (state == Atoms::winStateHidden)
return CompWindowStateHiddenMask;
else if (state == Atoms::winStateFullscreen)
return CompWindowStateFullscreenMask;
else if (state == Atoms::winStateAbove)
return CompWindowStateAboveMask;
else if (state == Atoms::winStateBel
|