/* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include <stdio.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#include "libs/fvwmlib.h"
#include "fvwm.h"
#include "externs.h"
#include "cursor.h"
#include "functions.h"
#include "bindings.h"
#include "misc.h"
#include "screen.h"
#include "events.h"
Time managing_since;
Atom _XA_WM_SX;
Atom _XA_MANAGER;
Atom _XA_ATOM_PAIR;
Atom _XA_WM_COLORMAP_NOTIFY;
#define _XA_TARGETS conversion_targets[0]
#define _XA_MULTIPLE conversion_targets[1]
#define _XA_TIMESTAMP conversion_targets[2]
#define _XA_VERSION conversion_targets[3]
#define MAX_TARGETS 4
Atom conversion_targets[MAX_TARGETS];
long icccm_version[] = { 2, 0 };
void
SetupICCCM2 (Bool replace_wm)
{
Window running_wm_win;
XSetWindowAttributes attr;
XEvent xev;
XClientMessageEvent ev;
char wm_sx[20];
sprintf (wm_sx, "WM_S%lu", Scr.screen);
_XA_WM_SX = XInternAtom (dpy, wm_sx, False);
_XA_MANAGER = XInternAtom (dpy, "MANAGER", False);
_XA_ATOM_PAIR = XInternAtom (dpy, "ATOM_PAIR", False);
_XA_TARGETS = XInternAtom (dpy, "TARGETS", False);
_XA_MULTIPLE = XInternAtom (dpy, "MULTIPLE", False);
_XA_TIMESTAMP = XInternAtom (dpy, "TIMESTAMP", False);
_XA_VERSION = XInternAtom (dpy, "VERSION", False);
_XA_WM_COLORMAP_NOTIFY = XInternAtom (dpy, "WM_COLORMAP_NOTIFY", False);
/* Check for a running ICCCM 2.0 compliant WM */
running_wm_win = XGetSelectionOwner (dpy, _XA_WM_SX);
if (running_wm_win != None) {
DBUG("SetupICCCM2", "another ICCCM 2.0 compliant WM is running");
if (!replace_wm)
{
fvwm_msg(ERR, "SetupICCCM2",
"another ICCCM 2.0 compliant WM is running, try -replace");
exit (1);
}
/* We need to know when the old manager is gone.
Thus we wait until it destroys running_wm_win. */
attr.event_mask = StructureNotifyMask;
XChangeWindowAttributes (dpy, running_wm_win, CWEventMask, &attr);
}
/* add PropChange to NoFocusWin events */
attr.event_mask = PropertyChangeMask;
XChangeWindowAttributes (dpy, Scr.NoFocusWin, CWEventMask, &attr);
/* We are not yet in the event loop, thus lastTimestamp will not
be ready. Have to get a timestamp manually by provoking a
PropertyNotify. */
XChangeProperty (dpy, Scr.NoFocusWin, XA_WM_CLASS, XA_STRING, 8,
PropModeAppend, NULL, 0);
XWindowEvent (dpy, Scr.NoFocusWin, PropertyChangeMask, &xev);
attr.event_mask = NoEventMask;
XChangeWindowAttributes (dpy, Scr.NoFocusWin, CWEventMask, &attr);
managing_since = xev.xproperty.time;
XSetSelectionOwner (dpy, _XA_WM_SX, Scr.NoFocusWin, managing_since);
if (XGetSelectionOwner (dpy, _XA_WM_SX) != Scr.NoFocusWin) {
fvwm_msg (ERR, "SetupICCCM2", "failed to acquire selection ownership");
exit (1);
}
/* Announce ourself as the new wm */
ev.type = ClientMessage;
ev.window = Scr.Root;
ev.message_type = _XA_MANAGER;
ev.format = 32;
ev.data.l[0] = managing_since;
ev.data.l[1] = _XA_WM_SX;
XSendEvent (dpy, Scr.Root, False, StructureNotifyMask, (XEvent*)&ev);
if (running_wm_win != None) {
/* Wait for the old wm to finish. */
/* FIXME: need a timeout here. */
DBUG("SetupICCCM2", "waiting for WM to give up");
do {
XWindowEvent (dpy, running_wm_win, StructureNotifyMask, &xev);
} while (xev.type != DestroyNotify);
}
/* restore NoFocusWin event mask */
attr.event_mask = XEVMASK_NOFOCUSW;
XChangeWindowAttributes (dpy, Scr.NoFocusWin, CWEventMask, &attr);
}
/* We must make sure that we have released SubstructureRedirect
before we destroy manager_win, so that another wm can start
successfully. */
void
CloseICCCM2 (void)
{
DBUG("CloseICCCM2", "good luck, new wm");
XSelectInput(dpy, Scr.Root, NoEventMask);
XSync(dpy, 0);
}
/* FIXME: property change actually succeeded */
static Bool
convertProperty (Window w, Atom target, Atom property)
{
if (target == _XA_TARGETS)
XChangeProperty (dpy, w, property,
XA_ATOM, 32, PropModeReplace,
(unsigned char *)conversion_targets, MAX_TARGETS);
else if (target == _XA_TIMESTAMP)
XChangeProperty (dpy, w, property,
XA_INTEGER, 32, PropModeReplace,
(unsigned char *)&managing_since, 1);
else if (target == _XA_VERSION)
XChangeProperty (dpy, w, property,
XA_INTEGER, 32, PropModeReplace,
(unsigned char *)icccm_version, 2);
else return False;
/* FIXME: This is ugly. We should rather select for
PropertyNotify on the window, return to the main loop,
and send the SelectionNotify once we are sure the property
has arrived. Problem: this needs a list of pending
SelectionNotifys. */
XSync (dpy, 0);
return True;
}
void
HandleSelectionRequest (void)
{
Atom type, *adata;
int i, format;
unsigned long num, rest;
unsigned char *data;
XSelectionRequestEvent ev = Event.xselectionrequest;
XSelectionEvent reply;
reply.type = SelectionNotify;
reply.display = dpy;
reply.requestor = ev.requestor;
reply.selection = ev.selection;
reply.target = ev.target;
reply.property = None;
reply.time = ev.time;
if (ev.target == _XA_MULTIPLE) {
if (ev.property != None) {
XGetWindowProperty (dpy, ev.requestor, ev.property, 0, 256, False,
_XA_ATOM_PAIR, &type, &format, &num, &rest, &data);
/* 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;
for (i = 0; i < num; i += 2) {
if (!convertProperty(ev.requestor, adata[i], adata[i+1]))
adata[i+1] = None;
}
XChangeProperty (dpy, ev.requestor, ev.property, _XA_ATOM_PAIR,
32, PropModeReplace, data, num);
XFree (data);
}
} else {
if (ev.property == None) ev.property = ev.target;
if (convertProperty (ev.requestor, ev.target, ev.property))
reply.property = ev.property;
}
XSync (dpy, 0);
XSendEvent (dpy, ev.requestor, False, 0L, (XEvent*)&reply);
}
/* If another wm is requesting ownership of the selection,
we receive a SelectionClear event. In that case, we have to
release all resources and destroy manager_win. Done() calls
CloseICCCM2() after undecorating all windows. */
void
HandleSelectionClear (void)
{
DBUG("HandleSelectionClear", "I lost my selection!");
Done(0, NULL);
}
syntax highlighted by Code2HTML, v. 0.9.1