/* -*-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 #include #include #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; }