/* -*-c-*- */
/* 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
*/
/*
*
* This is all original code by Robert Nation
* which reads motif mwm window manager
* hints from a window, and makes necessary adjustments for fvwm.
*
*/
/* ---------------------------- included header files ---------------------- */
#include "config.h"
#include <stdio.h>
#include <X11/Xproto.h>
#include <X11/Xatom.h>
#include "libs/fvwmlib.h"
#include "libs/FShape.h"
#include "libs/Parse.h"
#include "libs/lang-strings.h"
#include "fvwm.h"
#include "externs.h"
#include "execcontext.h"
#include "commands.h"
#include "misc.h"
#include "screen.h"
#include "update.h"
#include "style.h"
#include "geometry.h"
#include "decorations.h"
/* ---------------------------- local definitions -------------------------- */
/* Motif window hints */
#define MWM_HINTS_FUNCTIONS (1L << 0)
#define MWM_HINTS_DECORATIONS (1L << 1)
/* bit definitions for MwmHints.functions */
#define MWM_FUNC_ALL (1L << 0)
#define MWM_FUNC_RESIZE (1L << 1)
#define MWM_FUNC_MOVE (1L << 2)
#define MWM_FUNC_MINIMIZE (1L << 3)
#define MWM_FUNC_MAXIMIZE (1L << 4)
#define MWM_FUNC_CLOSE (1L << 5)
/* bit definitions for MwmHints.decorations */
#if 0
#define MWM_DECOR_ALL (1L << 0)
#define MWM_DECOR_BORDER (1L << 1)
#define MWM_DECOR_RESIZEH (1L << 2)
#define MWM_DECOR_TITLE (1L << 3)
#define MWM_DECOR_MENU (1L << 4)
#define MWM_DECOR_MINIMIZE (1L << 5)
#define MWM_DECOR_MAXIMIZE (1L << 6)
#endif
#define PROP_MOTIF_WM_HINTS_ELEMENTS 4
#define PROP_MWM_HINTS_ELEMENTS PROP_MOTIF_WM_HINTS_ELEMENTS
/* bit definitions for OL hints; I just
* made these up, OL stores hints as atoms */
#define OL_DECOR_CLOSE (1L << 0)
#define OL_DECOR_RESIZEH (1L << 1)
#define OL_DECOR_HEADER (1L << 2)
#define OL_DECOR_ICON_NAME (1L << 3)
#define OL_DECOR_ALL \
(OL_DECOR_CLOSE | OL_DECOR_RESIZEH | OL_DECOR_HEADER | OL_DECOR_ICON_NAME)
/* indicates if there are any OL hints */
#define OL_ANY_HINTS (1L << 7)
/* ---------------------------- local macros ------------------------------- */
/* ---------------------------- imports ------------------------------------ */
extern Atom _XA_MwmAtom;
/* ---------------------------- included code files ------------------------ */
/* ---------------------------- local types -------------------------------- */
/* Motif window hints */
typedef struct
{
long flags;
long functions;
long decorations;
long inputMode;
} PropMotifWmHints;
typedef PropMotifWmHints PropMwmHints;
/* ---------------------------- forward declarations ----------------------- */
/* ---------------------------- local variables ---------------------------- */
/* ---------------------------- exported variables (globals) --------------- */
/* ---------------------------- local functions ---------------------------- */
/* ---------------------------- interface functions ------------------------ */
/*
*
* Reads the property MOTIF_WM_HINTS
*
*/
void GetMwmHints(FvwmWindow *t)
{
int actual_format;
Atom actual_type;
unsigned long nitems, bytesafter;
if (t->mwm_hints)
{
XFree((char *)t->mwm_hints);
t->mwm_hints = NULL;
}
if (XGetWindowProperty(
dpy, FW_W(t), _XA_MwmAtom, 0L, 32L, False,
_XA_MwmAtom, &actual_type, &actual_format, &nitems,
&bytesafter,(unsigned char **)&t->mwm_hints)==Success)
{
if (nitems >= PROP_MOTIF_WM_HINTS_ELEMENTS)
{
return;
}
}
t->mwm_hints = NULL;
return;
}
/*
*
* Reads the openlook properties _OL_WIN_ATTR, _OL_DECOR_ADD, _OL_DECOR_DEL
*
* _OL_WIN_ATTR - the win_type field is the either the first atom if the
* property has length three or the second atom if the property has
* length five. It can be any of:
* _OL_WT_BASE (no changes)
* _OL_WT_CMD (no minimize decoration)
* _OL_WT_HELP (no minimize, maximize or resize handle decorations)
* _OL_WT_NOTICE (no minimize, maximize, system menu, resize handle
* or titlebar decorations)
* _OL_WT_OTHER (no minimize, maximize, system menu, resize handle
* or titlebar decorations)
* In addition, if the _OL_WIN_ATTR property is in the three atom format
* or if the type is _OL_WT_OTHER, then the icon name is not displayed
* (same behavior as olvwm).
*
* _OL_DECOR_ADD or _OL_DECOR_DEL - indivdually add or remove minimize
* button (_OL_DECOR_CLOSE), resize handles (_OL_DECOR_RESIZE), title bar
* (_OL_DECOR_HEADER), or icon name (_OL_DECOR_ICON_NAME).
*
* The documentation for the Open Look hints was taken from "Advanced X
* Window Application Programming", Eric F. Johnson and Kevin Reichard
* (M&T Books), and the olvwm source code (available at ftp.x.org in
* /R5contrib).
*/
void GetOlHints(FvwmWindow *t)
{
int actual_format;
Atom actual_type;
unsigned long nitems, bytesafter;
Atom *hints;
int i;
Atom win_type;
t->ol_hints = OL_DECOR_ALL;
if (XGetWindowProperty(
dpy, FW_W(t), _XA_OL_WIN_ATTR, 0L, 32L, False,
_XA_OL_WIN_ATTR, &actual_type, &actual_format,
&nitems, &bytesafter, (unsigned char **)&hints) == Success)
{
if (nitems > 0)
{
t->ol_hints |= OL_ANY_HINTS;
if (nitems == 3)
{
win_type = hints[0];
}
else
{
win_type = hints[1];
}
/* got this from olvwm and sort of mapped it to
* fvwm/MWM hints */
if (win_type == _XA_OL_WT_BASE)
{
t->ol_hints = OL_DECOR_ALL;
}
else if (win_type == _XA_OL_WT_CMD)
{
t->ol_hints = OL_DECOR_ALL & ~OL_DECOR_CLOSE;
}
else if (win_type == _XA_OL_WT_HELP)
{
t->ol_hints = OL_DECOR_ALL &
~(OL_DECOR_CLOSE | OL_DECOR_RESIZEH);
}
else if (win_type == _XA_OL_WT_NOTICE)
{
t->ol_hints = OL_DECOR_ALL &
~(OL_DECOR_CLOSE | OL_DECOR_RESIZEH |
OL_DECOR_HEADER | OL_DECOR_ICON_NAME);
}
else if (win_type == _XA_OL_WT_OTHER)
{
t->ol_hints = 0;
}
else
{
t->ol_hints = OL_DECOR_ALL;
}
if (nitems == 3)
{
t->ol_hints &= ~OL_DECOR_ICON_NAME;
}
}
if (hints)
{
XFree (hints);
}
}
if (XGetWindowProperty(
dpy, FW_W(t), _XA_OL_DECOR_ADD, 0L, 32L, False,
XA_ATOM, &actual_type, &actual_format, &nitems,
&bytesafter,(unsigned char **)&hints)==Success)
{
for (i = 0; i < nitems; i++)
{
t->ol_hints |= OL_ANY_HINTS;
if (hints[i] == _XA_OL_DECOR_CLOSE)
{
t->ol_hints |= OL_DECOR_CLOSE;
}
else if (hints[i] == _XA_OL_DECOR_RESIZE)
{
t->ol_hints |= OL_DECOR_RESIZEH;
}
else if (hints[i] == _XA_OL_DECOR_HEADER)
{
t->ol_hints |= OL_DECOR_HEADER;
}
else if (hints[i] == _XA_OL_DECOR_ICON_NAME)
{
t->ol_hints |= OL_DECOR_ICON_NAME;
}
}
if (hints)
{
XFree (hints);
}
}
if (XGetWindowProperty(
dpy, FW_W(t), _XA_OL_DECOR_DEL, 0L, 32L, False,
XA_ATOM, &actual_type, &actual_format, &nitems,
&bytesafter,(unsigned char **)&hints)==Success)
{
for (i = 0; i < nitems; i++)
{
t->ol_hints |= OL_ANY_HINTS;
if (hints[i] == _XA_OL_DECOR_CLOSE)
{
t->ol_hints &= ~OL_DECOR_CLOSE;
}
else if (hints[i] == _XA_OL_DECOR_RESIZE)
{
t->ol_hints &= ~OL_DECOR_RESIZEH;
}
else if (hints[i] == _XA_OL_DECOR_HEADER)
{
t->ol_hints &= ~OL_DECOR_HEADER;
}
else if (hints[i] == _XA_OL_DECOR_ICON_NAME)
{
t->ol_hints &= ~OL_DECOR_ICON_NAME;
}
}
if (hints)
{
XFree (hints);
}
}
return;
}
/*
*
* Interprets the property MOTIF_WM_HINTS, sets decoration and functions
* accordingly
*
*/
void SelectDecor(FvwmWindow *t, window_style *pstyle, short *buttons)
{
int decor;
int i;
int border_width;
int handle_width;
int used_width;
PropMwmHints *prop;
style_flags *sflags = &(pstyle->flags);
border_width = (SHAS_BORDER_WIDTH(sflags)) ?
SGET_BORDER_WIDTH(*pstyle) : DEFAULT_BORDER_WIDTH;
if (border_width > MAX_BORDER_WIDTH)
{
border_width = MAX_BORDER_WIDTH;
}
handle_width = (SHAS_HANDLE_WIDTH(sflags)) ?
SGET_HANDLE_WIDTH(*pstyle) : DEFAULT_HANDLE_WIDTH;
if (handle_width > MAX_HANDLE_WIDTH)
{
handle_width = MAX_HANDLE_WIDTH;
}
*buttons = (1 << NUMBER_OF_TITLE_BUTTONS) - 1;
decor = MWM_DECOR_ALL;
t->functions = MWM_FUNC_ALL;
if (t->mwm_hints)
{
prop = (PropMwmHints *)t->mwm_hints;
if (SHAS_MWM_DECOR(sflags))
{
if (prop->flags & MWM_HINTS_DECORATIONS)
{
decor = 0;
if (prop->decorations & 0x1)
{
decor |= MWM_DECOR_ALL;
}
if (prop->decorations & 0x2)
{
decor |= MWM_DECOR_BORDER;
}
if (prop->decorations & 0x4)
{
decor |= MWM_DECOR_RESIZEH;
}
if (prop->decorations & 0x8)
{
decor |= MWM_DECOR_TITLE;
}
if (prop->decorations & 0x10)
{
decor |= MWM_DECOR_MENU;
}
if (prop->decorations & 0x20)
{
decor |= MWM_DECOR_MINIMIZE;
}
if (prop->decorations & 0x40)
{
decor |= MWM_DECOR_MAXIMIZE;
}
}
}
if (SHAS_MWM_FUNCTIONS(sflags))
{
if (prop->flags & MWM_HINTS_FUNCTIONS)
{
t->functions = prop->functions;
}
}
}
/* functions affect the decorations! if the user says
* no iconify function, then the iconify button doesn't show
* up. */
if (t->functions & MWM_FUNC_ALL)
{
/* If we get ALL + some other things, that means to use
* ALL except the other things... */
t->functions &= ~MWM_FUNC_ALL;
t->functions =
(MWM_FUNC_RESIZE | MWM_FUNC_MOVE | MWM_FUNC_MINIMIZE |
MWM_FUNC_MAXIMIZE | MWM_FUNC_CLOSE) &
(~(t->functions));
}
if (SHAS_MWM_FUNCTIONS(sflags) && IS_TRANSIENT(t))
{
t->functions &= ~(MWM_FUNC_MAXIMIZE|MWM_FUNC_MINIMIZE);
}
if (decor & MWM_DECOR_ALL)
{
/* If we get ALL + some other things, that means to use
* ALL except the other things... */
decor &= ~MWM_DECOR_ALL;
decor = MWM_DECOR_EVERYTHING & (~decor);
}
/* now add/remove any functions specified in the OL hints */
if (SHAS_OL_DECOR(sflags) && (t->ol_hints & OL_ANY_HINTS))
{
if (t->ol_hints & OL_DECOR_CLOSE)
{
t->functions |= MWM_FUNC_MINIMIZE;
decor |= MWM_FUNC_MINIMIZE;
}
else
{
t->functions &= ~MWM_FUNC_MINIMIZE;
decor &= ~MWM_FUNC_MINIMIZE;
}
if (t->ol_hints & OL_DECOR_RESIZEH)
{
t->functions |= (MWM_FUNC_RESIZE | MWM_FUNC_MAXIMIZE);
decor |= (MWM_FUNC_RESIZE | MWM_FUNC_MAXIMIZE);
}
else
{
t->functions &= ~(MWM_FUNC_RESIZE | MWM_FUNC_MAXIMIZE);
decor &= ~(MWM_FUNC_RESIZE | MWM_FUNC_MAXIMIZE);
}
if (t->ol_hints & OL_DECOR_HEADER)
{
t->functions |= (MWM_DECOR_MENU | MWM_FUNC_MINIMIZE |
MWM_FUNC_MAXIMIZE | MWM_DECOR_TITLE);
decor |= (MWM_DECOR_MENU | MWM_FUNC_MINIMIZE |
MWM_FUNC_MAXIMIZE | MWM_DECOR_TITLE);
}
else
{
t->functions &= ~(MWM_DECOR_MENU | MWM_FUNC_MINIMIZE |
MWM_FUNC_MAXIMIZE | MWM_DECOR_TITLE);
decor &= ~(MWM_DECOR_MENU | MWM_FUNC_MINIMIZE |
MWM_FUNC_MAXIMIZE | MWM_DECOR_TITLE);
}
if (t->ol_hints & OL_DECOR_ICON_NAME)
{
SET_HAS_NO_ICON_TITLE(t, 0);
}
else
{
SET_HAS_NO_ICON_TITLE(t, 1);
}
}
/* Now I have the un-altered decor and functions, but with the
* ALL attribute cleared and interpreted. I need to modify the
* decorations that are affected by the functions */
if (!(t->functions & MWM_FUNC_RESIZE))
{
decor &= ~MWM_DECOR_RESIZEH;
}
/* MWM_FUNC_MOVE has no impact on decorations. */
if (!(t->functions & MWM_FUNC_MINIMIZE))
{
decor &= ~MWM_DECOR_MINIMIZE;
}
if (!(t->functions & MWM_FUNC_MAXIMIZE))
{
decor &= ~MWM_DECOR_MAXIMIZE;
}
/* MWM_FUNC_CLOSE has no impact on decorations. */
/* This rule is implicit, but its easier to deal with if
* I take care of it now */
if (decor & (MWM_DECOR_MENU| MWM_DECOR_MINIMIZE | MWM_DECOR_MAXIMIZE))
{
decor |= MWM_DECOR_TITLE;
}
/* Selected the mwm-decor field, now trim down, based on
* .fvwmrc entries */
if (SHAS_NO_TITLE(sflags) ||
(!SDO_DECORATE_TRANSIENT(sflags) && IS_TRANSIENT(t)))
{
decor &= ~MWM_DECOR_TITLE;
}
if (SHAS_NO_HANDLES(sflags) ||
(!SDO_DECORATE_TRANSIENT(sflags) && IS_TRANSIENT(t)))
{
decor &= ~MWM_DECOR_RESIZEH;
}
if (SHAS_MWM_DECOR(sflags) && IS_TRANSIENT(t))
{
decor &= ~(MWM_DECOR_MAXIMIZE|MWM_DECOR_MINIMIZE);
}
if (FShapesSupported)
{
if (t->wShaped)
{
decor &= ~(MWM_DECOR_BORDER|MWM_DECOR_RESIZEH);
}
}
if (IS_EWMH_FULLSCREEN(t))
{
decor &=~(MWM_DECOR_BORDER|MWM_DECOR_RESIZEH|MWM_DECOR_TITLE);
}
/* Assume no decorations, and build up */
SET_HAS_TITLE(t, 0);
SET_HAS_HANDLES(t, 0);
used_width = 0;
if (decor & MWM_DECOR_BORDER)
{
/* A narrow border is displayed (5 pixels - 2 relief, 1 top,
* (2 shadow) */
used_width = border_width;
}
if (decor & MWM_DECOR_TITLE)
{
/* A title bar with no buttons in it
* window gets a 1 pixel wide black border. */
SET_HAS_TITLE(t, 1);
}
if (decor & MWM_DECOR_RESIZEH)
{
/* A wide border, with corner tiles is desplayed
* (10 pixels - 2 relief, 2 shadow) */
SET_HAS_HANDLES(t, 1);
used_width = handle_width;
}
SET_HAS_NO_BORDER(t, S_HAS_NO_BORDER(SFC(*sflags)) || used_width <= 0);
if (HAS_NO_BORDER(t))
{
used_width = 0;
}
SET_HAS_HANDLES(t, (!HAS_NO_BORDER(t) && HAS_HANDLES(t)));
set_window_border_size(t, used_width);
if (!(decor & MWM_DECOR_MENU))
{
/* title-bar menu button omitted
* window gets 1 pixel wide black border */
/* disable any buttons with the MWMDecorMenu flag */
int i;
for (i = 0; i < NUMBER_OF_TITLE_BUTTONS; ++i)
{
if (TB_HAS_MWM_DECOR_MENU(GetDecor(t, buttons[i])))
{
*buttons &= ~(1 << i);
}
}
}
if (!(decor & MWM_DECOR_MINIMIZE))
{
/* title-bar + iconify button, no menu button.
* window gets 1 pixel wide black border */
/* disable any buttons with the MWMDecorMinimize/MWMDecorShaded
* flag */
int i;
for (i = 0; i < NUMBER_OF_TITLE_BUTTONS; ++i)
{
if (TB_HAS_MWM_DECOR_MINIMIZE(GetDecor(t, buttons[i])))
{
*buttons &= ~(1 << i);
}
}
}
if (!(decor & MWM_DECOR_MAXIMIZE))
{
/* title-bar + maximize button, no menu button, no iconify.
* window has 1 pixel wide black border */
/* disable any buttons with the MWMDecorMaximize flag */
int i;
for (i = 0; i < NUMBER_OF_TITLE_BUTTONS; ++i)
{
if (TB_HAS_MWM_DECOR_MAXIMIZE(GetDecor(t, buttons[i])))
{
*buttons &= ~(1 << i);
}
}
}
for (i = (1 << (NUMBER_OF_TITLE_BUTTONS - 1)); i; i >>= 1)
{
if (t->buttons & i)
*buttons &= ~i;
}
t->nr_left_buttons = Scr.nr_left_buttons;
t->nr_right_buttons = Scr.nr_right_buttons;
for (i = 0; i / 2 < Scr.nr_left_buttons; i += 2)
{
if ((*buttons & (1 << i)) == 0)
t->nr_left_buttons--;
}
for (i = 1; i / 2 < Scr.nr_right_buttons; i += 2)
{
if ((*buttons & (1 << i)) == 0)
t->nr_right_buttons--;
}
return;
}
static Bool __is_resize_allowed(
const FvwmWindow *t, int functions, request_origin_t request_origin)
{
if (!HAS_OVERRIDE_SIZE_HINTS(t) &&
t->hints.min_width == t->hints.max_width &&
t->hints.min_height == t->hints.max_height)
{
return False;
}
if (request_origin && IS_SIZE_FIXED(t))
{
return False;
}
else if (!request_origin && IS_PSIZE_FIXED(t))
{
return False;
}
if (request_origin && !(functions & MWM_FUNC_RESIZE))
{
return False;
}
return True;
}
/*
** seemed kind of silly to have check_allowed_function and
** check_allowed_function2 partially overlapping in their checks, so I
** combined them here and made them wrapper functions instead.
*/
Bool is_function_allowed(
int function, char *action_string, const FvwmWindow *t,
request_origin_t request_origin, Bool do_allow_override_mwm_hints)
{
unsigned int functions;
char *functionlist[] = {
MOVE_STRING,
RESIZE_STRING1,
RESIZE_STRING2,
MINIMIZE_STRING,
MINIMIZE_STRING2,
MAXIMIZE_STRING,
CLOSE_STRING1,
CLOSE_STRING2,
CLOSE_STRING3,
CLOSE_STRING4,
NULL
};
if (t == NULL)
{
return True; /* this logic come from animated menu */
}
if (do_allow_override_mwm_hints && HAS_MWM_OVERRIDE_HINTS(t))
{
/* allow everything */
functions = ~0;
}
else
{
/* restrict by mwm hints */
functions = t->functions;
}
/* Hate to do it, but for lack of a better idea, check based on the
* menu entry name */
/* Complex functions are a little tricky, ignore them if no menu item*/
if (function == F_FUNCTION && action_string != NULL)
{
int i;
/* remap to regular actions */
i = GetTokenIndex(action_string, functionlist, -1, NULL);
switch (i)
{
case 0:
function = F_MOVE;
break;
case 1:
function = F_RESIZE;
break;
case 2:
function = F_RESIZE;
break;
case 3:
function = F_ICONIFY;
break;
case 4:
function = F_ICONIFY;
break;
case 5:
function = F_MAXIMIZE;
break;
case 6:
function = F_CLOSE;
break;
case 7:
function = F_DELETE;
break;
case 8:
function = F_DESTROY;
break;
case 9:
function = F_QUIT;
break;
default:
break;
}
}
/* now do the real checks */
switch(function)
{
case F_DELETE:
if (IS_UNCLOSABLE(t))
{
return False;
}
if (IS_TEAR_OFF_MENU(t))
{
/* always allow this on tear off menus */
break;
}
if (!WM_DELETES_WINDOW(t))
{
return False;
}
/* fall through to close clause */
case F_CLOSE:
if (IS_UNCLOSABLE(t))
{
return False;
}
if (IS_TEAR_OFF_MENU(t))
{
/* always allow this on tear off menus */
break;
}
if (!(functions & MWM_FUNC_CLOSE))
{
return False;
}
break;
case F_DESTROY: /* shouldn't destroy always be allowed??? */
if (IS_UNCLOSABLE(t))
{
return False;
}
if (IS_TEAR_OFF_MENU(t))
{
/* always allow this on tear off menus */
break;
}
if (!(functions & MWM_FUNC_CLOSE))
{
return False;
}
break;
case F_RESIZE:
if(!__is_resize_allowed(t, functions, request_origin))
{
return False;
}
break;
case F_ICONIFY:
if ((!IS_ICONIFIED(t) && !(functions & MWM_FUNC_MINIMIZE)) ||
IS_UNICONIFIABLE(t))
{
return False;
}
break;
case F_MAXIMIZE:
if (IS_MAXIMIZE_FIXED_SIZE_DISALLOWED(t) &&
!__is_resize_allowed(t, functions, request_origin))
{
return False;
}
if ((request_origin && !(functions & MWM_FUNC_MAXIMIZE)) ||
IS_UNMAXIMIZABLE(t))
{
return False;
}
break;
case F_MOVE:
/* Move is a funny hint. Keeps it out of the menu, but you're
* still allowed to move. */
if (request_origin && IS_FIXED(t))
{
return False;
}
else if (!request_origin && IS_FIXED_PPOS(t))
{
return False;
}
if (request_origin && !(functions & MWM_FUNC_MOVE))
{
return False;
}
break;
case F_FUNCTION:
default:
break;
} /* end of switch */
/* if we fell through, just return True */
return True;
}
syntax highlighted by Code2HTML, v. 0.9.1