Dear fvwm developers,
I am using fvwm2 since a couple of months and I am very pleased with it.
I liked the vector buttons very much and I was a little sad
because a vector button can only have 31 lines and because
these lines must always be connected to one another.
Therefore I would like to propose a new fwwm-feature.
This is the first open source project that I want to contribute,
so please be patient with me.
It would be a great honor if this feature could be introduced
in one of the next versions.
I am using it in version 2.4.6 and it works fine.
Yours,
Johannes Gajdosik
------------------------------------------------------
Feature description:
the man-page says:
...,num is a number of
point specifications of the form [EMAIL PROTECTED]
... C specifies a line color
(0 - the shadow color, 1 - the highlight color, 2 -
the background color, 3 - the foreground color).
... You can use up to 32 points in a line pattern.
The new feature is:
1) You can use up to 1000 (or more?) points in a line pattern.
2) special color C=-1: no line is drawn.
This is different from color C=2, because a line of color C=2
makes that all pixels of this line get the background color
and so are erased.
The implementation affects 5 files.
-----------------------------------------------------
1) the man page:
The new text should be:
... C specifies a line color
(0 - the shadow color, 1 - the highlight color,
2 - the background color, 3 - the foreground color,
-1 - special meaning: this line is not drawn).
... You can use up to 1000 points in a line pattern.
------------------------------------------------------
2) screen.h
Instead of
struct vector_coords
{
int num;
int *x;
int *y;
unsigned long line_style;
unsigned long use_fgbg;
} vector;
I propose
struct vector_coords
{
int num;
struct vector_vertex {
unsigned char x;
unsigned char y;
unsigned short color;
} *vertices;
unsigned long use_fgbg;
} vector;
Please observe that in the old code every point needs 8 bytes,
in the new code only 4 bytes. Furthermore the color is not any more
stored
in the bits of two unsigned long variables(line_style,use_fgbg),
but instead in just on variable(color), so the code becomes more
readable.
The variable use_fgbg remains just because of an optimization when
drawing the button.
----------------------------------------------------------
3) fvwm.c
The functions LoadDefaultLeftButton and LoadDefaultRightButton
need to be adapted:
for example, instead of
v->line_style = 0;
v->x = (int *) safemalloc (sizeof(int) * v->num);
v->y = (int *) safemalloc (sizeof(int) * v->num);
v->x[0] = 22;
v->y[0] = 39;
v->line_style |= (1 << 0);
v->x[1] = 78;
v->y[1] = 39;
v->line_style |= (1 << 1);
v->x[2] = 78;
v->y[2] = 61;
v->x[3] = 22;
v->y[3] = 61;
v->x[4] = 22;
v->y[4] = 39;
v->line_style |= (1 << 4);
the code must be
v->vertices = (struct vector_vertex *)
safemalloc (sizeof(struct vector_vertex) * v->num);
v->vertices[0].x = 22;
v->vertices[0].y = 39;
v->vertices[0].color = 1;
v->vertices[1].x = 78;
v->vertices[1].y = 39;
v->vertices[1].color = 1;
v->vertices[2].x = 78;
v->vertices[2].y = 61;
v->vertices[2].color = 0;
v->vertices[3].x = 22;
v->vertices[3].y = 61;
v->vertices[3].color = 0;
v->vertices[4].x = 22;
v->vertices[4].y = 39;
v->vertices[4].color = 1;
v->use_fgbg = 0;
------------------------------------------------
4) builtins.c
Instead of line 1423:
if (df->u.vector.x)
free (df->u.vector.x);
if (df->u.vector.y)
free (df->u.vector.y);
it must be
if (df->u.vector.vertices)
free (df->u.vector.vertices);
Instead of line 1515:
else if (strncasecmp(style,"Vector",6)==0 ||
(strlen(style)<=2 && isdigit(*style)))
{
/* normal coordinate list button style */
int i, num_coords, num, line_style;
struct vector_coords *vc = &df->u.vector;
/* get number of points */
if (strncasecmp(style,"Vector",6)==0)
{
num = sscanf(s,"%d%n",&num_coords,&offset);
s += offset;
} else
num = sscanf(style,"%d",&num_coords);
if((num != 1)||(num_coords>32)||(num_coords<2))
{
if (verbose)
{
fvwm_msg(
ERR,"ReadDecorFace", "Bad button style (2) in line:
%s",action);
}
return False;
}
vc->num = num_coords;
vc->line_style = 0;
vc->use_fgbg = 0;
vc->x = (int *) safemalloc (sizeof (int) * num_coords);
vc->y = (int *) safemalloc (sizeof (int) * num_coords);
/* get the points */
for(i = 0; i < vc->num; ++i)
{
/* X x Y @ line_style */
num = sscanf(s,"[EMAIL PROTECTED]",&vc->x[i],&vc->y[i],
&line_style, &offset);
if(num != 3)
{
if (verbose)
{
fvwm_msg(
ERR, "ReadDecorFace", "Bad button style (3) in line %s",
action);
}
free(vc->x);
free(vc->y);
vc->x = NULL;
vc->y = NULL;
return False;
}
if (line_style % 2)
{
vc->line_style |= (1 << i);
}
if (line_style >= 2)
{
vc->use_fgbg |= (1 << i);
}
s += offset;
}
memset(&df->style, 0, sizeof(df->style));
DFS_FACE_TYPE(df->style) = VectorButton;
}
I propose
else if (strncasecmp(style,"Vector",6)==0 ||
(strlen(style)<=2 && isdigit(*style)))
{
/* normal coordinate list button style */
int i, num_coords, num, x, y, line_style;
struct vector_coords *vc = &df->u.vector;
/* get number of points */
if (strncasecmp(style,"Vector",6)==0)
{
num = sscanf(s,"%d%n",&num_coords,&offset);
s += offset;
} else
num = sscanf(style,"%d",&num_coords);
if((num != 1)||(num_coords>1000)||(num_coords<2))
{
if (verbose)
{
fvwm_msg(
ERR,"ReadDecorFace", "Bad button style (2) in line:
%s",action);
}
return False;
}
vc->num = num_coords;
vc->use_fgbg = 0;
vc->vertices = (struct vector_vertex *)
safemalloc (sizeof(struct vector_vertex) *
num_coords);
/* get the points */
for(i = 0; i < vc->num; ++i)
{
/* X x Y @ line_style */
num = sscanf(s,"[EMAIL PROTECTED]", &x, &y, &line_style, &offset);
vc->vertices[i].x = x;
vc->vertices[i].y = y;
vc->vertices[i].color = line_style;
if(num != 3)
{
if (verbose)
{
fvwm_msg(
ERR, "ReadDecorFace", "Bad button style (3) in line %s",
action);
}
free(vc->vertices);
vc->vertices = NULL;
vc->num = 0;
return False;
}
if (line_style >= 2 && line_style < 4)
{
vc->use_fgbg = 1;
}
s += offset;
}
memset(&df->style, 0, sizeof(df->style));
DFS_FACE_TYPE(df->style) = VectorButton;
}
----------------------------------------------------
5) borders.c
Instead of
static void DrawLinePattern(
Window win, GC ReliefGC, GC ShadowGC, Pixel fore_color, Pixel
back_color,
struct vector_coords *coords, int w, int h)
{
int i;
/* It is rare, so evaluate this only when needed */
GC fore_GC = NULL, back_GC = NULL;
if (coords->use_fgbg)
{
Globalgcv.foreground = fore_color;
Globalgcm = GCForeground;
XChangeGC(dpy, Scr.ScratchGC3, Globalgcm, &Globalgcv);
fore_GC = Scr.ScratchGC3;
Globalgcv.foreground = back_color;
XChangeGC(dpy, Scr.ScratchGC4, Globalgcm, &Globalgcv);
back_GC = Scr.ScratchGC4;
}
for (i = 1; i < coords->num; ++i)
{
XDrawLine(dpy,win,
(coords->use_fgbg & (1 << i))
? (coords->line_style & (1 << i)) ? fore_GC : back_GC
: (coords->line_style & (1 << i)) ? ReliefGC :
ShadowGC,
w * coords->x[i-1]/100,
h * coords->y[i-1]/100,
w * coords->x[i]/100,
h * coords->y[i]/100);
}
}
I propose
static void DrawLinePattern(
Window win, GC ReliefGC, GC ShadowGC, Pixel fore_color, Pixel
back_color,
struct vector_coords *coords, int w, int h)
{
int i, prev_x, prev_y;
GC gcs[4];
gcs[0] = ShadowGC;
gcs[1] = ReliefGC;
gcs[2] = NULL;
gcs[3] = NULL;
/* It is rare, so evaluate this only when needed */
if (coords->use_fgbg)
{
Globalgcv.foreground = fore_color;
Globalgcm = GCForeground;
XChangeGC(dpy, Scr.ScratchGC3, Globalgcm, &Globalgcv);
gcs[3] = Scr.ScratchGC3;
Globalgcv.foreground = back_color;
XChangeGC(dpy, Scr.ScratchGC4, Globalgcm, &Globalgcv);
gcs[2] = Scr.ScratchGC4;
}
/* safety check */
if (coords->num <= 0) return;
prev_x = w * coords->vertices->x/100;
prev_y = h * coords->vertices->y/100;
for (i = 1; i < coords->num; ++i)
{
struct vector_vertex *v = coords->vertices + i;
int x = w * v->x/100;
int y = h * v->y/100;
if (v->color < 4)
XDrawLine(dpy,win,gcs[v->color],prev_x,prev_y,x,y);
prev_x = x;
prev_y = y;
}
}
Please notice that the newer version should be slightly faster
than the old one.
/* 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 module is based on Twm, but has been siginificantly modified
* by Rob Nation
****************************************************************************/
/*
* Copyright 1989 Massachusetts Institute of Technology
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose and without fee is hereby granted, 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 M.I.T. not be used in advertising
* or publicity pertaining to distribution of the software without specific,
* written prior permission. M.I.T. makes no representations about the
* suitability of this software for any purpose. It is provided "as is"
* without express or implied warranty.
*
* M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL M.I.T.
* BE LIABLE FOR ANY SPECIAL, 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.
*/
/***********************************************************************
*
* fvwm per-screen data include file
*
***********************************************************************/
#ifndef _SCREEN_
#define _SCREEN_
#define SIZE_HINDENT 5
#define SIZE_VINDENT 3
/* colormap focus styes */
#define COLORMAP_FOLLOWS_MOUSE 1 /* default */
#define COLORMAP_FOLLOWS_FOCUS 2
typedef struct
{
Window win;
int isMapped;
} PanFrame;
typedef enum
{
/* button types */
DefaultVectorButton ,
VectorButton ,
SimpleButton ,
GradientButton ,
PixmapButton ,
TiledPixmapButton ,
#ifdef MINI_ICONS
MiniIconButton ,
#endif
SolidButton
} DecorFaceType;
typedef enum
{
JUST_CENTER = 0,
JUST_LEFT = 1,
JUST_TOP = 1,
JUST_RIGHT = 2,
JUST_BOTTOM = 2,
JUST_MASK = 3
} JustificationType;
typedef struct
{
unsigned face_type : 3; /* was DecorFaceType : 3 */
struct
{
unsigned h_justification : 2; /* was JustificationType : 2 */
unsigned v_justification : 2; /* was JustificationType : 2 */
#define DFS_BUTTON_IS_UP 0
#define DFS_BUTTON_IS_FLAT 1
#define DFS_BUTTON_IS_SUNK 2
#define DFS_BUTTON_MASK 3
unsigned int button_relief : 2;
/* not used in border styles */
unsigned int use_title_style : 1;
unsigned int use_border_style : 1;
/* only used in border styles */
unsigned int has_hidden_handles : 1;
unsigned int has_no_inset : 1;
} flags;
} DecorFaceStyle;
#define DFS_FACE_TYPE(dfs) ((dfs).face_type)
#define DFS_FLAGS(dfs) ((dfs).flags)
#define DFS_H_JUSTIFICATION(dfs) ((dfs).flags.h_justification)
#define DFS_V_JUSTIFICATION(dfs) ((dfs).flags.v_justification)
#define DFS_BUTTON_RELIEF(dfs) ((dfs).flags.button_relief)
#define DFS_USE_TITLE_STYLE(dfs) ((dfs).flags.use_title_style)
#define DFS_USE_BORDER_STYLE(dfs) ((dfs).flags.use_border_style)
#define DFS_HAS_HIDDEN_HANDLES(dfs) ((dfs).flags.has_hidden_handles)
#define DFS_HAS_NO_INSET(dfs) ((dfs).flags.has_no_inset)
typedef struct DecorFace
{
DecorFaceStyle style;
struct
{
Picture *p;
Pixel back;
struct
{
int npixels;
Pixel *pixels;
char gradient_type;
} grad;
struct vector_coords
{
int num;
struct vector_vertex {
unsigned char x;
unsigned char y;
unsigned short color;
} *vertices;
unsigned long use_fgbg;
} vector;
} u;
#ifdef MULTISTYLE
struct DecorFace *next;
#endif
struct
{
unsigned has_changed : 1;
} flags;
} DecorFace;
enum ButtonState
{
BS_All = -1,
BS_ActiveUp,
BS_ActiveDown,
BS_Inactive,
BS_ToggledActiveUp,
BS_ToggledActiveDown,
BS_ToggledInactive,
BS_MaxButtonState
};
typedef enum
{
/* The first five are used in title buttons. These can't be renumbered
* without extending the mwm_decor_flags member below and adapting the style
* structure. */
MWM_DECOR_MENU = 0x1,
MWM_DECOR_MINIMIZE = 0x2,
MWM_DECOR_MAXIMIZE = 0x4,
MWM_DECOR_SHADE = 0x8,
MWM_DECOR_STICK = 0x10,
/* --- */
MWM_DECOR_BORDER = 0x20,
MWM_DECOR_RESIZEH = 0x40,
MWM_DECOR_TITLE = 0x80,
MWM_DECOR_ALL = 0x100,
MWM_DECOR_EVERYTHING = 0xff
} mwm_flags;
typedef struct
{
unsigned just : 2; /* was JustificationType : 2 */
struct
{
unsigned has_changed : 1;
mwm_flags mwm_decor_flags : 9;
} flags;
DecorFace state[BS_MaxButtonState];
} TitleButton;
#define TB_FLAGS(tb) ((tb).flags)
#define TB_STATE(tb) ((tb).state)
#define TB_JUSTIFICATION(tb) ((tb).just)
#define TB_MWM_DECOR_FLAGS(tb) ((tb).flags.mwm_decor_flags)
#define TB_HAS_CHANGED(tb) \
(!!((tb).flags.has_changed))
#define TB_HAS_MWM_DECOR_MENU(tb) \
(!!((tb).flags.mwm_decor_flags & MWM_DECOR_MENU))
#define TB_HAS_MWM_DECOR_MINIMIZE(tb) \
(!!((tb).flags.mwm_decor_flags & MWM_DECOR_MINIMIZE))
#define TB_HAS_MWM_DECOR_MAXIMIZE(tb) \
(!!((tb).flags.mwm_decor_flags & MWM_DECOR_MAXIMIZE))
#define TB_HAS_MWM_DECOR_SHADE(tb) \
(!!((tb).flags.mwm_decor_flags & MWM_DECOR_SHADE))
#define TB_HAS_MWM_DECOR_STICK(tb) \
(!!((tb).flags.mwm_decor_flags & MWM_DECOR_STICK))
typedef struct FvwmDecor
{
#ifdef USEDECOR
char *tag; /* general style tag */
#endif
int title_height; /* explicitly specified title bar height */
/* titlebar buttons */
TitleButton buttons[NUMBER_OF_BUTTONS];
TitleButton titlebar;
struct BorderStyle
{
DecorFace active, inactive;
} BorderStyle;
#ifdef USEDECOR
struct FvwmDecor *next; /* additional user-defined styles */
#endif
struct
{
unsigned has_changed : 1;
unsigned has_title_height_changed : 1;
} flags;
} FvwmDecor;
typedef struct ScreenInfo
{
unsigned long screen;
Screen *pscreen;
int NumberOfScreens; /* number of screens on display */
int MyDisplayWidth; /* my copy of DisplayWidth(dpy, screen) */
int MyDisplayHeight; /* my copy of DisplayHeight(dpy, screen) */
FvwmWindow FvwmRoot; /* the head of the fvwm window list */
Window Root; /* the root window */
Window SizeWindow; /* the resize dimensions window */
Window NoFocusWin; /* Window which will own focus when no other
* windows have it */
PanFrame PanFrameTop;
PanFrame PanFrameLeft;
PanFrame PanFrameRight;
PanFrame PanFrameBottom;
Pixmap gray_bitmap; /*dark gray pattern for shaded out menu items*/
Pixmap gray_pixmap; /* dark gray pattern for inactive borders */
Pixmap light_gray_pixmap; /* light gray pattern for inactive borders */
Pixmap sticky_gray_pixmap; /* light gray pattern for sticky borders */
Binding *AllBindings;
int root_pushes; /* current push level to install root
colormap windows */
int fvwm_pushes; /* current push level to install fvwm
colormap windows */
FvwmWindow *pushed_window; /* saved window to install when pushes drops
to zero */
Cursor *FvwmCursors;
int BusyCursor; /* context where we display the busy cursor */
char *DefaultIcon; /* Icon to use when no other icons are found */
int TopLayer;
int DefaultLayer;
int BottomLayer;
FvwmFunction *functions;
FvwmFont DefaultFont; /* font structure */
GC TransMaskGC; /* GC for transparency masks */
Pixel StdFore, StdBack, StdHilite, StdShadow; /* don't change the order */
GC StdGC;
GC StdReliefGC;
GC StdShadowGC;
Pixmap ScratchMonoPixmap; /* A scratch 1x1x1 pixmap */
GC MonoGC; /* GC for drawing into depth 1 drawables */
GC XorGC; /* GC to draw lines for move and resize */
GC ScratchGC1;
GC ScratchGC2;
GC ScratchGC3;
GC ScratchGC4;
GC TitleGC;
int SizeStringWidth; /* minimum width of size window */
FvwmDecor DefaultDecor; /* decoration style(s) */
FvwmDecor *cur_decor;
int nr_left_buttons; /* number of left-side title-bar buttons */
int nr_right_buttons; /* number of right-side title-bar buttons */
FvwmWindow *Hilite; /* the fvwm window that is highlighted
* except for networking delays, this is the
* window which REALLY has the focus */
Window UnknownWinFocused; /* None, if the focus is nowhere or on an fvwm
* managed window. Set to id of otherwindow
* with focus otherwise */
FvwmWindow *Ungrabbed;
int EdgeScrollX; /* #pixels to scroll on screen edge */
int EdgeScrollY; /* #pixels to scroll on screen edge */
unsigned char buttons2grab; /* buttons to grab in click to focus mode */
int NumBoxes;
int randomx; /* values used for randomPlacement */
int randomy;
int VxMax; /* Max location for top left of virt desk*/
int VyMax;
int Vx; /* Current loc for top left of virt desk */
int Vy;
int ClickTime; /*Max button-click delay for Function built-in*/
int ScrollResistance; /* resistance to scrolling in desktop */
int MoveResistance; /* res to moving windows over viewport edge */
int XiMoveResistance; /* the same for edges of xinerama screens */
int MoveThreshold; /* number of pixels of mouse motion to decide
* it's a move operation */
int SnapAttraction; /* attractiveness of window edges */
int SnapMode; /* mode of snap attraction */
int SnapGridX; /* snap grid X size */
int SnapGridY; /* snap grid Y size */
int OpaqueSize;
int CurrentDesk; /* The current desktop number */
int ColormapFocus; /* colormap focus style */
int ColorLimit; /* Limit on colors used in pixmaps */
int DefaultColorset; /* Default Colorset used by feedback window */
int use_backing_store;
/*
** some additional global options which will probably become window
** specific options later on:
*/
#if 0
/* Scr.go.ModifyUSP was always 1. How is it supposed to be set? */
struct
{
unsigned ModifyUSP : 1; /* - RBW - 11/02/1998 */
} go; /* global options */
#endif
struct
{
unsigned ModalityIsEvil : 1;
unsigned RaiseHackNeeded : 1;
unsigned DisableConfigureNotify : 1;
unsigned InstallRootCmap : 1;
unsigned RaiseOverUnmanaged : 1;
unsigned FlickeringQtDialogsWorkaround : 1;
} bo; /* bug workaround control options */
struct
{
unsigned EmulateMWM : 1;
unsigned EmulateWIN : 1;
unsigned use_active_down_buttons : 1;
unsigned use_inactive_buttons : 1;
unsigned do_hide_position_window : 1;
unsigned do_hide_resize_window : 1;
} gs; /* global style structure */
struct
{
Bool do_save_under : 1;
unsigned do_need_window_update : 1;
unsigned do_need_style_list_update : 1;
unsigned has_default_font_changed : 1;
unsigned has_default_color_changed : 1;
unsigned has_mouse_binding_changed : 1;
unsigned has_nr_buttons_changed : 1;
unsigned has_xinerama_state_changed : 1;
unsigned is_executing_complex_function : 1;
unsigned is_map_desk_in_progress : 1;
unsigned is_pointer_on_this_screen : 1;
unsigned is_single_screen : 1;
unsigned is_window_scheduled_for_destroy : 1;
unsigned is_wire_frame_displayed : 1;
unsigned silent_functions : 1;
unsigned windows_captured : 1;
unsigned edge_wrap_x : 1;
unsigned edge_wrap_y : 1;
} flags;
struct
{
last_added_item_type type;
void *item;
} last_added_item;
} ScreenInfo;
/*
Macro which gets specific decor or default decor.
This saves an indirection in case you don't want
the UseDecor mechanism.
*/
#ifdef USEDECOR
#define GetDecor(window,part) ((window)->decor->part)
#else
#define GetDecor(window,part) (Scr.DefaultDecor.part)
#endif
/* some protos for the decoration structures */
void LoadDefaultButton(DecorFace *bf, int i);
void ResetAllButtons(FvwmDecor *decor);
void DestroyAllButtons(FvwmDecor *decor);
void simplify_style_list(void);
/*
* Diverts a style definition to an FvwmDecor structure ([EMAIL PROTECTED])
*/
void AddToDecor(FvwmDecor *decor, char *s);
extern ScreenInfo Scr;
#endif /* _SCREEN_ */
/* 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 module is all original code
* by Rob Nation
* Copyright 1993, Robert Nation
* You may use this code for any purpose, as long as the original
* copyright remains in the source code and all documentation
****************************************************************************/
/***********************************************************************
* fvwm - "F? Virtual Window Manager"
***********************************************************************/
#include "config.h"
#include <stdio.h>
#include <signal.h>
#include <sys/wait.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_GETPWUID
# include <pwd.h>
#endif
#include "libs/fvwmlib.h"
#include "libs/FScreen.h"
#include "libs/FShape.h"
#include "fvwm.h"
#include "externs.h"
#include "cursor.h"
#include "functions.h"
#include "bindings.h"
#include "misc.h"
#include "screen.h"
#include "defaults.h"
#include "builtins.h"
#include "menus.h"
#include "module_interface.h"
#include "libs/Colorset.h"
#include "icccm2.h"
#include "gnome.h"
#include "icons.h"
#include "add_window.h"
#include "fvwmsignal.h"
#include "colormaps.h"
#include "stack.h"
#include "virtual.h"
#include "session.h"
#include "events.h"
#include "read.h"
#include "colors.h"
#include "focus.h"
#include "update.h"
#include "window_flags.h"
#include "move_resize.h"
#include <X11/Xproto.h>
#include <X11/Xatom.h>
/* need to get prototype for XrmUniqueQuark for XUniqueContext call */
#include <X11/Xresource.h>
#if defined(I18N_MB) || defined(COMPOUND_TEXT)
#include <X11/Xlocale.h>
#endif
#if HAVE_SYS_SYSTEMINFO_H
/* Solaris has sysinfo instead of gethostname. */
#include <sys/systeminfo.h>
#endif
#define MAXHOSTNAME 255
#ifndef lint
static char sccsid[] __attribute__((__unused__))
= "@(#)fvwm.c " VERSION " " __DATE__ " fvwm";
#endif
int master_pid; /* process number of 1st fvwm process */
ScreenInfo Scr; /* structures for the screen */
MenuInfo Menus; /* structures for menus */
Display *dpy = NULL; /* which display are we talking to */
Bool fFvwmInStartup = True; /* Set to False when startup has finished */
Bool DoingCommandLine = False; /* Set True before each cmd line arg */
/* Grab pointer state. Set by GrabEm, UngrabEm, menus.c and StartupStuff */
#define MAX_CFG_CMDS 10
static char *config_commands[MAX_CFG_CMDS];
static int num_config_commands=0;
/* Is this definition of mkdir portable? */
int mkdir(const char *, mode_t);
int FvwmErrorHandler(Display *, XErrorEvent *);
int CatchFatal(Display *);
int CatchRedirectError(Display *, XErrorEvent *);
void InstallSignals(void);
void SaveDesktopState(void);
void SetRCDefaults(void);
void StartupStuff(void);
static void catch_exit(void);
static RETSIGTYPE SigDone(int);
static RETSIGTYPE Restart(int);
static RETSIGTYPE ReapChildren(int);
static int parseCommandArgs(
const char *command, char **argv, int maxArgc, const char **errorMsg);
static void InternUsefulAtoms(void);
static void InitVariables(void);
static void usage(void);
static void setVersionInfo(void);
XContext FvwmContext; /* context for fvwm windows */
XContext MenuContext; /* context for fvwm menus */
int JunkX = 0, JunkY = 0;
Window JunkRoot, JunkChild; /* junk window */
unsigned int JunkWidth, JunkHeight, JunkBW, JunkDepth, JunkMask;
Bool debugging = False;
Bool debugging_stack_ring = False;
Window bad_window = None;
char **g_argv;
int g_argc;
char *state_filename = NULL;
char *restart_state_filename = NULL; /* $HOME/.fs-restart */
/* assorted gray bitmaps for decorative borders */
#define g_width 2
#define g_height 2
static char g_bits[] = {0x02, 0x01};
#define l_g_width 4
#define l_g_height 2
static char l_g_bits[] = {0x08, 0x02};
#define s_g_width 4
#define s_g_height 4
static char s_g_bits[] = {0x01, 0x02, 0x04, 0x08};
extern XEvent Event;
Bool Restarting = False;
int fd_width, x_fd;
char *display_name = NULL;
char *fvwm_userdir;
static char *home_dir;
char const *Fvwm_VersionInfo;
char const *Fvwm_SupportInfo;
typedef enum { FVWM_RUNNING=0, FVWM_DONE, FVWM_RESTART } FVWM_STATE;
static volatile sig_atomic_t fvwmRunState = FVWM_RUNNING;
/***********************************************************************
*
* Procedure:
* main - start of fvwm
*
***********************************************************************
*/
int main(int argc, char **argv)
{
unsigned long valuemask; /* mask for create windows */
XSetWindowAttributes attributes; /* attributes for create windows */
int i;
extern int x_fd;
int len;
char *display_string;
char message[255];
Bool do_force_single_screen = False;
Bool replace_wm = False;
Bool option_error = False;
int visualClass = -1;
int visualId = -1;
/* for use on restart */
g_argv = (char **)safemalloc((argc + 4) * sizeof(char *));
g_argc = argc;
for (i = 0; i < argc; i++)
g_argv[i] = argv[i];
g_argv[g_argc] = NULL;
DBUG("main","Entered, about to parse args");
setVersionInfo();
#if defined(I18N_MB) || defined(COMPOUND_TEXT)
{
char *s;
if ((s = setlocale(LC_CTYPE, "")) == NULL)
fvwm_msg(ERR, "main","Can't set locale. Check your $LC_CTYPE or $LANG.\n");
else if (!XSupportsLocale())
{
fvwm_msg(WARN, "main", "X does not support current locale: %s\n", s);
}
}
#endif
/* Put the default module directory into the environment so it can be used
later by the config file, etc. */
putenv("FVWM_MODULEDIR=" FVWM_MODULEDIR);
/* Figure out user's home directory */
home_dir = getenv("HOME");
#ifdef HAVE_GETPWUID
if (home_dir == NULL) {
struct passwd* pw = getpwuid(getuid());
if (pw != NULL)
home_dir = safestrdup(pw->pw_dir);
}
#endif
if (home_dir == NULL)
home_dir = "/"; /* give up and use root dir */
/* Figure out where to read and write user's data files. */
fvwm_userdir = getenv("FVWM_USERDIR");
if (fvwm_userdir == NULL)
{
fvwm_userdir = safestrdup(CatString2(home_dir, "/.fvwm"));
/* Put the user directory into the environment so it can be used
later everywhere. */
putenv(safestrdup(CatString2("FVWM_USERDIR=", fvwm_userdir)));
}
/* Create FVWM_USERDIR directory if needed */
if (access(fvwm_userdir, F_OK) != 0)
mkdir(fvwm_userdir, 0777);
if (access(fvwm_userdir, W_OK) != 0)
fvwm_msg(ERR, "main", "No write permissions in `%s/'.\n", fvwm_userdir);
for (i = 1; i < argc; i++)
{
if (strncasecmp(argv[i],"-debug_stack_ring", 17)==0)
{
debugging_stack_ring = True;
}
else if (strncasecmp(argv[i],"-debug",6)==0)
{
debugging = True;
}
else if (strncasecmp(argv[i], "-clientId", 9) == 0)
{
if (++i >= argc)
usage();
SetClientID(argv[i]);
}
else if (strncasecmp(argv[i], "-restore", 8) == 0)
{
if (++i >= argc)
usage();
state_filename = argv[i];
}
else if (strncasecmp(argv[i],"-s",2)==0)
{
do_force_single_screen = True;
}
else if (strncasecmp(argv[i],"-d",2)==0)
{
if (++i >= argc)
usage();
display_name = argv[i];
}
else if (strncasecmp(argv[i],"-f",2)==0)
{
if (++i >= argc)
usage();
if (num_config_commands < MAX_CFG_CMDS)
{
config_commands[num_config_commands] =
(char *)malloc(6+strlen(argv[i]));
strcpy(config_commands[num_config_commands],"Read ");
strcat(config_commands[num_config_commands],argv[i]);
num_config_commands++;
}
else
{
fvwm_msg(ERR,"main","only %d -f and -cmd parms allowed!",MAX_CFG_CMDS);
}
}
else if (strncasecmp(argv[i],"-cmd",4)==0)
{
if (++i >= argc)
usage();
if (num_config_commands < MAX_CFG_CMDS)
{
config_commands[num_config_commands] = safestrdup(argv[i]);
num_config_commands++;
}
else
{
fvwm_msg(ERR,"main","only %d -f and -cmd parms allowed!",MAX_CFG_CMDS);
}
}
else if (strncasecmp(argv[i],"-h",2)==0)
{
usage();
exit(0);
}
else if (strncasecmp(argv[i],"-blackout",9)==0)
{
/* obsolete option */
fvwm_msg(WARN, "main", "The -blackout option is obsolete, it may be "
"removed in the future.",
VERSION,__DATE__,__TIME__);
}
else if (strncasecmp(argv[i], "-replace", 8) == 0)
{
replace_wm = True;
}
/* check for visualId before visual to remove ambiguity */
else if (strncasecmp(argv[i],"-visualId",9)==0) {
visualClass = -1;
if (++i >= argc)
usage();
if (sscanf(argv[i], "0x%x", &visualId) == 0)
if (sscanf(argv[i], "%d", &visualId) == 0)
usage();
}
else if (strncasecmp(argv[i],"-visual",7)==0) {
visualId = None;
if (++i >= argc)
usage();
if (strncasecmp(argv[i], "staticg", 7) == 0)
visualClass = StaticGray;
else if (strncasecmp(argv[i], "g", 1) == 0)
visualClass = GrayScale;
else if (strncasecmp(argv[i], "staticc", 7) == 0)
visualClass = StaticColor;
else if (strncasecmp(argv[i], "p", 1) == 0)
visualClass = PseudoColor;
else if (strncasecmp(argv[i], "t", 1) == 0)
visualClass = TrueColor;
else if (strncasecmp(argv[i], "d", 1) == 0)
visualClass = DirectColor;
else
usage();
}
else if (strncasecmp(argv[i], "-version", 8) == 0)
{
printf("%s\n%s\n", Fvwm_VersionInfo, Fvwm_SupportInfo);
exit(0);
}
else
{
fvwm_msg(ERR,"main","Unknown option: `%s'", argv[i]);
option_error = TRUE;
}
}
DBUG("main","Done parsing args");
if (option_error)
{
usage();
}
DBUG("main","Installing signal handlers");
InstallSignals();
if (!(Pdpy = dpy = XOpenDisplay(display_name)))
{
fvwm_msg(ERR,"main","can't open display %s", XDisplayName(display_name));
exit (1);
}
atexit(catch_exit);
Scr.screen= DefaultScreen(dpy);
Scr.NumberOfScreens = ScreenCount(dpy);
master_pid = getpid();
if(!do_force_single_screen)
{
int myscreen = 0;
char *cp;
strcpy(message, XDisplayString(dpy));
for(i=0;i<Scr.NumberOfScreens;i++)
{
if (i != Scr.screen && fork() == 0)
{
myscreen = i;
/*
* Truncate the string 'whatever:n.n' to 'whatever:n',
* and then append the screen number.
*/
cp = strchr(message, ':');
if (cp != NULL)
{
cp = strchr(cp, '.');
if (cp != NULL)
*cp = '\0'; /* truncate at display part */
}
sprintf(message + strlen(message), ".%d", myscreen);
Pdpy = dpy = XOpenDisplay(message);
Scr.screen = myscreen;
Scr.NumberOfScreens = ScreenCount(dpy);
break;
}
}
g_argv[argc++] = "-s";
/*
g_argv[argc++] = "-d";
g_argv[argc++] = safestrdup(XDisplayString(dpy));
*/
g_argv[argc] = NULL;
}
FScreenInit(dpy);
x_fd = XConnectionNumber(dpy);
fd_width = GetFdWidth();
#ifdef HAVE_X11_FD
if (fcntl(x_fd, F_SETFD, 1) == -1)
{
fvwm_msg(ERR,"main","close-on-exec failed");
exit (1);
}
#endif
/* Add a DISPLAY entry to the environment, incase we were started
* with fvwm -display term:0.0
*/
len = strlen(XDisplayString(dpy));
display_string = safemalloc(len+10);
sprintf(display_string,"DISPLAY=%s",XDisplayString(dpy));
putenv(display_string);
/* Add a HOSTDISPLAY environment variable, which is the same as
* DISPLAY, unless display = :0.0 or unix:0.0, in which case the full
* host name will be used for ease in networking . */
/* Note: Can't free the rdisplay_string after putenv, because it
* becomes part of the environment! */
if(strncmp(display_string,"DISPLAY=:",9)==0)
{
char client[MAXHOSTNAME], *rdisplay_string;
gethostname(client,MAXHOSTNAME);
rdisplay_string = safemalloc(len+14 + strlen(client));
sprintf(rdisplay_string,"HOSTDISPLAY=%s:%s",client,&display_string[9]);
putenv(rdisplay_string);
}
else if(strncmp(display_string,"DISPLAY=unix:",13)==0)
{
char client[MAXHOSTNAME], *rdisplay_string;
gethostname(client,MAXHOSTNAME);
rdisplay_string = safemalloc(len+14 + strlen(client));
sprintf(rdisplay_string,"HOSTDISPLAY=%s:%s",client,
&display_string[13]);
putenv(rdisplay_string);
}
else
{
char *rdisplay_string;
rdisplay_string = safemalloc(len+14);
sprintf(rdisplay_string,"HOSTDISPLAY=%s",XDisplayString(dpy));
putenv(rdisplay_string);
}
Scr.Root = RootWindow(dpy, Scr.screen);
if(Scr.Root == None)
{
fvwm_msg(ERR,"main","Screen %d is not a valid screen",(char *)Scr.screen);
exit(1);
}
if (visualClass != -1) {
/* grab the best visual of the required class */
XVisualInfo template, *vizinfo;
int total, i;
Pdepth = 0;
template.screen = Scr.screen;
template.class = visualClass;
vizinfo = XGetVisualInfo(dpy, VisualScreenMask | VisualClassMask, &template,
&total);
if (total) {
for (i = 0; i < total; i++) {
if (vizinfo[i].depth > Pdepth) {
Pvisual = vizinfo[i].visual;
Pdepth = vizinfo[i].depth;
}
}
XFree(vizinfo);
/* have to have a colormap for non-default visual windows */
Pcmap = XCreateColormap(dpy, Scr.Root, Pvisual, AllocNone);
Pdefault = False;
} else {
fvwm_msg(ERR, "main","Cannot find visual class %d", visualClass);
visualClass = -1;
}
} else if (visualId != -1) {
/* grab visual id asked for */
XVisualInfo template, *vizinfo;
int total;
Pdepth = 0;
template.screen = Scr.screen;
template.visualid = visualId;
vizinfo = XGetVisualInfo(dpy, VisualScreenMask | VisualIDMask, &template,
&total);
if (total) {
/* visualID's are unique so there will only be one */
Pvisual = vizinfo[0].visual;
Pdepth = vizinfo[0].depth;
XFree(vizinfo);
/* have to have a colormap for non-default visual windows */
Pcmap = XCreateColormap(dpy, Scr.Root, Pvisual, AllocNone);
Pdefault = False;
} else {
fvwm_msg(ERR, "main", "VisualId 0x%x is not valid ", visualId);
visualId = -1;
}
}
/* use default visuals if none found so far */
if (visualClass == -1 && visualId == -1) {
Pvisual = DefaultVisual(dpy, Scr.screen);
Pdepth = DefaultDepth(dpy, Scr.screen);
Pcmap = DefaultColormap(dpy, Scr.screen);
Pdefault = True;
}
/* make a copy of the visual stuff so that XorPixmap can swap with root */
SaveFvwmVisual();
FShapeInit(dpy);
Scr.pscreen = XScreenOfDisplay(dpy, Scr.screen);
Scr.use_backing_store = DoesBackingStore(Scr.pscreen);
Scr.flags.do_save_under = DoesSaveUnders(Scr.pscreen);
InternUsefulAtoms ();
/* Make sure property priority colors is empty */
XChangeProperty(dpy, Scr.Root, _XA_MIT_PRIORITY_COLORS,
XA_CARDINAL, 32, PropModeReplace, NULL, 0);
/* create a window which will accept the keyboard focus when no other
windows have it */
/* do this before any RC parsing as some GC's are created from this window
* rather than the root window */
attributes.event_mask = XEVMASK_NOFOCUSW;
attributes.override_redirect = True;
attributes.colormap = Pcmap;
attributes.background_pixmap = None;
attributes.border_pixel = 0;
Scr.NoFocusWin=XCreateWindow(dpy, Scr.Root, -10, -10, 10, 10, 0, Pdepth,
InputOutput, Pvisual,
CWEventMask | CWOverrideRedirect | CWColormap
| CWBackPixmap | CWBorderPixel, &attributes);
XMapWindow(dpy, Scr.NoFocusWin);
SetMWM_INFO(Scr.NoFocusWin);
FOCUS_SET(Scr.NoFocusWin);
XSync(dpy, 0);
if(debugging)
XSynchronize(dpy,1);
SetupICCCM2 (replace_wm);
XSetErrorHandler(CatchRedirectError);
XSetIOErrorHandler(CatchFatal);
XSelectInput(dpy, Scr.Root, XEVMASK_ROOTW);
XSync(dpy, 0);
XSetErrorHandler(FvwmErrorHandler);
{
/* do not grab the pointer earlier because if fvwm exits with the pointer
* grabbed while a different display is visible, XFree 4.0 freezes. */
Cursor cursor = XCreateFontCursor(dpy, XC_watch);
XGrabPointer(dpy, Scr.Root, 0, 0, GrabModeAsync, GrabModeAsync,
None, cursor, CurrentTime);
}
{
Atom atype;
int aformat;
unsigned long nitems, bytes_remain;
unsigned char *prop;
if (
XGetWindowProperty(
dpy, Scr.Root, _XA_WM_DESKTOP, 0L, 1L, True, _XA_WM_DESKTOP,
&atype, &aformat, &nitems, &bytes_remain, &prop
) == Success
) {
if (prop != NULL) {
Restarting = True;
/* do_force_single_screen = True; */
}
}
}
restart_state_filename =
safestrdup(
CatString3(fvwm_userdir, "/.fs-restart-", getenv("HOSTDISPLAY")));
if (!state_filename && Restarting)
state_filename = restart_state_filename;
/*
This should be done early enough to have the window states loaded
before the first call to AddWindow.
*/
LoadWindowStates(state_filename);
Scr.FvwmCursors = CreateCursors(dpy);
InitVariables();
if (visualClass != -1 || visualId != -1) {
/* this is so that menus use the (non-default) fvwm colormap */
Scr.FvwmRoot.w = Scr.NoFocusWin;
Scr.FvwmRoot.number_cmap_windows = 1;
Scr.FvwmRoot.cmap_windows = &Scr.NoFocusWin;
}
InitEventHandlerJumpTable();
initModules();
Scr.gray_bitmap =
XCreateBitmapFromData(dpy,Scr.Root,g_bits, g_width,g_height);
DBUG("main","Setting up rc file defaults...");
SetRCDefaults();
flush_window_updates();
simplify_style_list();
DBUG("main","Running config_commands...");
if (num_config_commands > 0)
{
int i;
for(i=0;i<num_config_commands;i++)
{
DoingCommandLine = True;
old_execute_function(
config_commands[i], NULL, &Event, C_ROOT, 1, 0, NULL);
free(config_commands[i]);
}
DoingCommandLine = False;
} else {
/* Run startup command file in 5 places: FVWM_USERDIR, FVWM_DATADIR,
and for compatibility: ~/.fvwm2rc, $sysconfdir/system.fvwm2rc */
if (
!run_command_file( CatString3(fvwm_userdir, "/", FVWMRC),
&Event, NULL, C_ROOT, 1 ) &&
!run_command_file( CatString3(home_dir, "/", FVWMRC),
&Event, NULL, C_ROOT, 1 ) &&
!run_command_file( CatString3(FVWM_DATADIR, "/", FVWMRC),
&Event, NULL, C_ROOT, 1 ) &&
!run_command_file( CatString3(FVWM_DATADIR, "/system", FVWMRC),
&Event, NULL, C_ROOT, 1 ) &&
!run_command_file( CatString3(FVWM_CONFDIR, "/system", FVWMRC),
&Event, NULL, C_ROOT, 1 ) )
{
fvwm_msg( ERR, "main", "Cannot read startup file, tried: "
"\n\t%s/%s\n\t%s/%s\n\t%s/%s\n\t%s/system%s\n\t%s/system%s",
fvwm_userdir, FVWMRC,
home_dir, FVWMRC,
FVWM_DATADIR, FVWMRC,
FVWM_DATADIR, FVWMRC,
FVWM_CONFDIR, FVWMRC);
}
}
DBUG("main","Done running config_commands");
if(Pdepth<2)
{
Scr.gray_pixmap =
XCreatePixmapFromBitmapData(dpy,Scr.NoFocusWin, g_bits, g_width,
g_height, BlackPixel(dpy, Scr.screen),
WhitePixel(dpy, Scr.screen), Pdepth);
Scr.light_gray_pixmap =
XCreatePixmapFromBitmapData(dpy, Scr.NoFocusWin, l_g_bits, l_g_width,
l_g_height, BlackPixel(dpy, Scr.screen),
WhitePixel(dpy, Scr.screen), Pdepth);
Scr.sticky_gray_pixmap =
XCreatePixmapFromBitmapData(dpy, Scr.NoFocusWin, s_g_bits, s_g_width,
s_g_height, BlackPixel(dpy, Scr.screen),
WhitePixel(dpy, Scr.screen), Pdepth);
}
attributes.background_pixel = Scr.StdBack;
attributes.colormap = Pcmap;
attributes.border_pixel = 0;
valuemask = CWBackPixel | CWColormap | CWBorderPixel;
Scr.SizeWindow = XCreateWindow(
dpy, Scr.Root, 0, 0, 1, 1, (unsigned int)0, Pdepth, InputOutput, Pvisual,
valuemask, &attributes);
resize_geometry_window();
initPanFrames();
MyXGrabServer(dpy);
checkPanFrames();
MyXUngrabServer(dpy);
CoerceEnterNotifyOnCurrentWindow();
SessionInit();
GNOME_Init();
DBUG("main","Entering HandleEvents loop...");
HandleEvents();
switch( fvwmRunState )
{
case FVWM_DONE:
Done(0, NULL); /* does not return */
case FVWM_RESTART:
Done(1, ""); /* does not return */
default:
DBUG("main","Unknown FVWM run-state");
}
exit(0);
}
/* exit handler that will try to release any grabs */
static void catch_exit(void)
{
if (dpy != NULL)
{
/* Don't care if this is called from an X error handler. We *have* to try
* this, whatever happens. XFree 4.0 may freeze if we don't do this. */
XUngrabServer(dpy);
XUngrabPointer(dpy, CurrentTime);
XUngrabKeyboard(dpy, CurrentTime);
}
return;
}
/*
** StartupStuff
**
** Does initial window captures and runs init/restart function
*/
void StartupStuff(void)
{
#define startFuncName "StartFunction"
const char *initFuncName;
CaptureAllWindows(False);
/* Turn off the SM stuff after the initial capture so that new windows will
* not be matched by accident. */
if (Restarting)
DisableRestoringState();
/* Have to do this here too because preprocessor modules have not run to the
* end when HandleEvents is entered from the main loop. */
checkPanFrames();
fFvwmInStartup = False;
/* Make sure we have the correct click time now. */
if (Scr.ClickTime < 0)
Scr.ClickTime = -Scr.ClickTime;
/* It is safe to Ungrab here: if not and one of the init functions not
* finish we've got a complete freeze ! */
UngrabEm(GRAB_STARTUP);
XUngrabPointer(dpy, CurrentTime);
/* migo (04-Sep-1999): execute StartFunction */
if (FindFunction(startFuncName)) {
char *action = "Function " startFuncName;
old_execute_function(action, NULL, &Event, C_ROOT, 1, 0, NULL);
}
/* migo (03-Jul-1999): execute [Session]{Init|Restart}Function */
initFuncName = getInitFunctionName(Restarting == True);
if (FindFunction(initFuncName)) {
char *action = safestrdup(CatString2("Function ", initFuncName));
old_execute_function(action, NULL, &Event, C_ROOT, 1, 0, NULL);
free(action);
}
/*
This should be done after the initialization is finished, since
it directly changes the global state.
*/
LoadGlobalState(state_filename);
/*
** migo (20-Jun-1999): Remove state file after usage.
** migo (09-Jul-1999): but only on restart, otherwise it can be reused.
*/
if (Restarting)
unlink(state_filename);
} /* StartupStuff */
/*
** SetRCDefaults
**
** Sets some initial style values & such
*/
void SetRCDefaults(void)
{
/* set up default colors, fonts, etc */
char *defaults[] = {
"XORValue 0",
"DefaultFont fixed",
"DefaultColors black grey",
DEFAULT_MENU_STYLE,
"TitleStyle Centered -- Raised",
"Style * Color lightgrey/dimgrey",
"Style * HilightFore black, HilightBack grey",
"AddToMenu MenuFvwmRoot \"Builtin Menu\" Title",
"+ \"&1. XTerm\" Exec xterm",
"+ \"&2. Setup Form\" Module FvwmForm FvwmForm-Setup",
"+ \"&3. Setup 95 Script\" Module FvwmScript FvwmScript-Setup95",
"+ \"&4. Issue FVWM commands\" Module FvwmConsole",
"+ \"&R. Restart FVWM\" Restart",
"+ \"&X. Exit FVWM\" Quit",
"Mouse 0 R N Menu MenuFvwmRoot",
"Read "FVWM_DATADIR"/ConfigFvwmDefaults",
NULL
};
int i=0;
while (defaults[i])
{
old_execute_function(defaults[i], NULL, &Event, C_ROOT, 1, 0, NULL);
i++;
}
} /* SetRCDefaults */
/***********************************************************************
*
* Procedure:
* InternUsefulAtoms:
* Dont really know what it does
*
***********************************************************************
*/
Atom _XA_MIT_PRIORITY_COLORS;
Atom _XA_WM_CHANGE_STATE;
Atom _XA_WM_STATE;
Atom _XA_WM_COLORMAP_WINDOWS;
extern Atom _XA_WM_PROTOCOLS;
Atom _XA_WM_TAKE_FOCUS;
Atom _XA_WM_DELETE_WINDOW;
Atom _XA_WM_DESKTOP;
Atom _XA_MwmAtom;
Atom _XA_MOTIF_WM;
Atom _XA_OL_WIN_ATTR;
Atom _XA_OL_WT_BASE;
Atom _XA_OL_WT_CMD;
Atom _XA_OL_WT_HELP;
Atom _XA_OL_WT_NOTICE;
Atom _XA_OL_WT_OTHER;
Atom _XA_OL_DECOR_ADD;
Atom _XA_OL_DECOR_DEL;
Atom _XA_OL_DECOR_CLOSE;
Atom _XA_OL_DECOR_RESIZE;
Atom _XA_OL_DECOR_HEADER;
Atom _XA_OL_DECOR_ICON_NAME;
Atom _XA_WM_WINDOW_ROLE;
Atom _XA_WM_CLIENT_LEADER;
Atom _XA_SM_CLIENT_ID;
static void InternUsefulAtoms (void)
{
/*
* Create priority colors if necessary.
*/
_XA_MIT_PRIORITY_COLORS = XInternAtom(dpy, "_MIT_PRIORITY_COLORS", False);
_XA_WM_CHANGE_STATE = XInternAtom (dpy, "WM_CHANGE_STATE", False);
_XA_WM_STATE = XInternAtom (dpy, "WM_STATE", False);
_XA_WM_COLORMAP_WINDOWS = XInternAtom (dpy, "WM_COLORMAP_WINDOWS", False);
_XA_WM_PROTOCOLS = XInternAtom (dpy, "WM_PROTOCOLS", False);
_XA_WM_TAKE_FOCUS = XInternAtom (dpy, "WM_TAKE_FOCUS", False);
_XA_WM_DELETE_WINDOW = XInternAtom (dpy, "WM_DELETE_WINDOW", False);
_XA_WM_DESKTOP = XInternAtom (dpy, "WM_DESKTOP", False);
_XA_MwmAtom=XInternAtom(dpy,"_MOTIF_WM_HINTS",False);
_XA_MOTIF_WM=XInternAtom(dpy,"_MOTIF_WM_INFO",False);
_XA_OL_WIN_ATTR=XInternAtom(dpy,"_OL_WIN_ATTR",False);
_XA_OL_WT_BASE=XInternAtom(dpy,"_OL_WT_BASE",False);
_XA_OL_WT_CMD=XInternAtom(dpy,"_OL_WT_CMD",False);
_XA_OL_WT_HELP=XInternAtom(dpy,"_OL_WT_HELP",False);
_XA_OL_WT_NOTICE=XInternAtom(dpy,"_OL_WT_NOTICE",False);
_XA_OL_WT_OTHER=XInternAtom(dpy,"_OL_WT_OTHER",False);
_XA_OL_DECOR_ADD=XInternAtom(dpy,"_OL_DECOR_ADD",False);
_XA_OL_DECOR_DEL=XInternAtom(dpy,"_OL_DECOR_DEL",False);
_XA_OL_DECOR_CLOSE=XInternAtom(dpy,"_OL_DECOR_CLOSE",False);
_XA_OL_DECOR_RESIZE=XInternAtom(dpy,"_OL_DECOR_RESIZE",False);
_XA_OL_DECOR_HEADER=XInternAtom(dpy,"_OL_DECOR_HEADER",False);
_XA_OL_DECOR_ICON_NAME=XInternAtom(dpy,"_OL_DECOR_ICON_NAME",False);
_XA_WM_WINDOW_ROLE=XInternAtom(dpy, "WM_WINDOW_ROLE",False);
_XA_WM_CLIENT_LEADER=XInternAtom(dpy, "WM_CLIENT_LEADER",False);
_XA_SM_CLIENT_ID=XInternAtom(dpy, "SM_CLIENT_ID",False);
return;
}
/***********************************************************************
*
* Procedure:
* InstallSignals: install the signal handlers, using whatever
* means we have at our disposal. The more POSIXy, the better
*
************************************************************************/
void
InstallSignals(void)
{
#ifdef HAVE_SIGACTION
struct sigaction sigact;
/*
* All signals whose handlers call fvwmSetTerminate()
* must be mutually exclusive - we mustn't receive one
* while processing any of the others ...
*/
sigemptyset(&sigact.sa_mask);
sigaddset(&sigact.sa_mask, SIGINT);
sigaddset(&sigact.sa_mask, SIGHUP);
sigaddset(&sigact.sa_mask, SIGQUIT);
sigaddset(&sigact.sa_mask, SIGTERM);
sigaddset(&sigact.sa_mask, SIGUSR1);
#ifdef SA_RESTART
sigact.sa_flags = SA_RESTART;
#else
sigact.sa_flags = 0;
#endif
sigact.sa_handler = DeadPipe;
sigaction(SIGPIPE, &sigact, NULL);
sigact.sa_handler = Restart;
sigaction(SIGUSR1, &sigact, NULL);
sigact.sa_handler = SigDone;
sigaction(SIGINT, &sigact, NULL);
sigaction(SIGHUP, &sigact, NULL);
sigaction(SIGQUIT, &sigact, NULL);
sigaction(SIGTERM, &sigact, NULL);
/*
* Reap all zombies automatically! This signal handler will
* only be called if a child process dies, not if someone
* sends a child a STOP signal. Note that none of our "terminate"
* signals can be delivered until the SIGCHLD handler completes,
* and this is a Good Thing because the terminate handlers
* might exit abruptly via "siglongjmp". This could potentially
* leave SIGCHLD handler with unfinished business ...
*
* NOTE: We could still receive SIGPIPE signals within the
* SIGCHLD handler, but the SIGPIPE handler has the
* SA_RESTART flag set and so should not affect our
* "wait" system call.
*/
sigact.sa_flags |= SA_NOCLDSTOP;
sigact.sa_handler = ReapChildren;
sigaction(SIGCHLD, &sigact, NULL);
#else
#ifdef USE_BSD_SIGNALS
fvwmSetSignalMask( sigmask(SIGUSR1) |
sigmask(SIGINT) |
sigmask(SIGHUP) |
sigmask(SIGQUIT) |
sigmask(SIGTERM) );
#endif
/*
* We don't have sigaction(), so fall back on
* less reliable methods ...
*/
signal(SIGPIPE, DeadPipe);
signal(SIGUSR1, Restart);
#ifdef HAVE_SIGINTERRUPT
siginterrupt(SIGUSR1, 0);
#endif
signal(SIGINT, SigDone);
#ifdef HAVE_SIGINTERRUPT
siginterrupt(SIGINT, 0);
#endif
signal(SIGHUP, SigDone);
#ifdef HAVE_SIGINTERRUPT
siginterrupt(SIGHUP, 0);
#endif
signal(SIGQUIT, SigDone);
#ifdef HAVE_SIGINTERRUPT
siginterrupt(SIGQUIT, 0);
#endif
signal(SIGTERM, SigDone);
#ifdef HAVE_SIGINTERRUPT
siginterrupt(SIGTERM, 0);
#endif
signal(SIGCHLD, ReapChildren);
#ifdef HAVE_SIGINTERRUPT
siginterrupt(SIGCHLD, 0);
#endif
#endif
/* When FVWM restarts, the SIGCHLD handler is automatically reset
* to the default handler. This means that Zombies left over from
* the previous instance of FVWM could still be roaming the process
* table if they exited while the default handler was in place.
* We fix this by invoking the SIGCHLD handler NOW, so that they
* may finally rest in peace. */
ReapChildren(0);
}
/*************************************************************************
* Reap child processes, preventing them from becoming zombies.
* We do this asynchronously within the SIGCHLD handler so that
* "it just happens".
************************************************************************/
static RETSIGTYPE
ReapChildren(int sig)
{
(void)sig;
BSD_BLOCK_SIGNALS;
/*
* This is a signal handler, AND SO MUST BE REENTRANT!
* Now the wait() functions are safe here, but please don't
* add anything unless you're SURE that the new functions
* (plus EVERYTHING they call) are also reentrant. There
* are very few functions which are truly safe.
*/
#if HAVE_WAITPID
while (waitpid(-1, NULL, WNOHANG) > 0)
;
#elif HAVE_WAIT3
while (wait3(NULL, WNOHANG, NULL) > 0)
;
#else
# error One of waitpid or wait3 is needed.
#endif
BSD_UNBLOCK_SIGNALS;
}
/*************************************************************************
* Restart on a signal
************************************************************************/
static RETSIGTYPE
Restart(int sig)
{
fvwmRunState = FVWM_RESTART;
/*
* This function might not return - it could "long-jump"
* right out, so we need to do everything we need to do
* BEFORE we call it ...
*/
fvwmSetTerminate(sig);
}
/***********************************************************************
*
* LoadDefaultLeftButton -- loads default left button # into
* assumes associated button memory is already free
*
************************************************************************/
static void LoadDefaultLeftButton(DecorFace *df, int i)
{
struct vector_coords *v = &df->u.vector;
memset(&df->style, 0, sizeof(df->style));
DFS_FACE_TYPE(df->style) = DefaultVectorButton;
switch (i % 5)
{
case 0:
case 4:
v->num = 5;
v->vertices = (struct vector_vertex *)
safemalloc (sizeof(struct vector_vertex) * v->num);
v->vertices[0].x = 22;
v->vertices[0].y = 39;
v->vertices[0].color = 1;
v->vertices[1].x = 78;
v->vertices[1].y = 39;
v->vertices[1].color = 1;
v->vertices[2].x = 78;
v->vertices[2].y = 61;
v->vertices[2].color = 0;
v->vertices[3].x = 22;
v->vertices[3].y = 61;
v->vertices[3].color = 0;
v->vertices[4].x = 22;
v->vertices[4].y = 39;
v->vertices[4].color = 1;
v->use_fgbg = 0;
break;
case 1:
v->num = 5;
v->vertices = (struct vector_vertex *)
safemalloc (sizeof(struct vector_vertex) * v->num);
v->vertices[0].x = 32;
v->vertices[0].y = 45;
v->vertices[0].color = 0;
v->vertices[1].x = 68;
v->vertices[1].y = 45;
v->vertices[1].color = 0;
v->vertices[2].x = 68;
v->vertices[2].y = 55;
v->vertices[2].color = 1;
v->vertices[3].x = 32;
v->vertices[3].y = 55;
v->vertices[3].color = 1;
v->vertices[4].x = 32;
v->vertices[4].y = 45;
v->vertices[4].color = 0;
v->use_fgbg = 0;
break;
case 2:
v->num = 5;
v->vertices = (struct vector_vertex *)
safemalloc (sizeof(struct vector_vertex) * v->num);
v->vertices[0].x = 49;
v->vertices[0].y = 49;
v->vertices[0].color = 1;
v->vertices[1].x = 51;
v->vertices[1].y = 49;
v->vertices[1].color = 1;
v->vertices[2].x = 51;
v->vertices[2].y = 51;
v->vertices[2].color = 0;
v->vertices[3].x = 49;
v->vertices[3].y = 51;
v->vertices[3].color = 0;
v->vertices[4].x = 49;
v->vertices[4].y = 49;
v->vertices[4].color = 1;
v->use_fgbg = 0;
break;
case 3:
v->num = 5;
v->vertices = (struct vector_vertex *)
safemalloc (sizeof(struct vector_vertex) * v->num);
v->vertices[0].x = 32;
v->vertices[0].y = 45;
v->vertices[0].color = 1;
v->vertices[1].x = 68;
v->vertices[1].y = 45;
v->vertices[1].color = 1;
v->vertices[2].x = 68;
v->vertices[2].y = 55;
v->vertices[2].color = 0;
v->vertices[3].x = 32;
v->vertices[3].y = 55;
v->vertices[3].color = 0;
v->vertices[4].x = 32;
v->vertices[4].y = 45;
v->vertices[4].color = 1;
v->use_fgbg = 0;
break;
}
}
/***********************************************************************
*
* LoadDefaultRightButton -- loads default left button # into
* assumes associated button memory is already free
*
************************************************************************/
static void LoadDefaultRightButton(DecorFace *df, int i)
{
struct vector_coords *v = &df->u.vector;
memset(&df->style, 0, sizeof(df->style));
DFS_FACE_TYPE(df->style) = DefaultVectorButton;
switch (i % 5)
{
case 0:
case 3:
v->num = 5;
v->vertices = (struct vector_vertex *)
safemalloc (sizeof(struct vector_vertex) * v->num);
v->vertices[0].x = 25;
v->vertices[0].y = 25;
v->vertices[0].color = 1;
v->vertices[1].x = 75;
v->vertices[1].y = 25;
v->vertices[1].color = 1;
v->vertices[2].x = 75;
v->vertices[2].y = 75;
v->vertices[2].color = 0;
v->vertices[3].x = 25;
v->vertices[3].y = 75;
v->vertices[3].color = 0;
v->vertices[4].x = 25;
v->vertices[4].y = 25;
v->vertices[4].color = 1;
v->use_fgbg = 0;
break;
case 1:
v->num = 5;
v->vertices = (struct vector_vertex *)
safemalloc (sizeof(struct vector_vertex) * v->num);
v->vertices[0].x = 39;
v->vertices[0].y = 39;
v->vertices[0].color = 1;
v->vertices[1].x = 61;
v->vertices[1].y = 39;
v->vertices[1].color = 1;
v->vertices[2].x = 61;
v->vertices[2].y = 61;
v->vertices[2].color = 0;
v->vertices[3].x = 39;
v->vertices[3].y = 61;
v->vertices[3].color = 0;
v->vertices[4].x = 39;
v->vertices[4].y = 39;
v->vertices[4].color = 1;
v->use_fgbg = 0;
break;
case 2:
v->num = 5;
v->vertices = (struct vector_vertex *)
safemalloc (sizeof(struct vector_vertex) * v->num);
v->vertices[0].x = 49;
v->vertices[0].y = 49;
v->vertices[0].color = 1;
v->vertices[1].x = 51;
v->vertices[1].y = 49;
v->vertices[1].color = 1;
v->vertices[2].x = 51;
v->vertices[2].y = 51;
v->vertices[2].color = 0;
v->vertices[3].x = 49;
v->vertices[3].y = 51;
v->vertices[3].color = 0;
v->vertices[4].x = 49;
v->vertices[4].y = 49;
v->vertices[4].color = 1;
v->use_fgbg = 0;
break;
case 4:
v->num = 5;
v->vertices = (struct vector_vertex *)
safemalloc (sizeof(struct vector_vertex) * v->num);
v->vertices[0].x = 36;
v->vertices[0].y = 36;
v->vertices[0].color = 1;
v->vertices[1].x = 64;
v->vertices[1].y = 36;
v->vertices[1].color = 1;
v->vertices[2].x = 64;
v->vertices[2].y = 64;
v->vertices[2].color = 0;
v->vertices[3].x = 36;
v->vertices[3].y = 64;
v->vertices[3].color = 0;
v->vertices[4].x = 36;
v->vertices[4].y = 36;
v->vertices[4].color = 1;
v->use_fgbg = 0;
break;
}
}
/***********************************************************************
*
* LoadDefaultButton -- loads default button # into button structure
* assumes associated button memory is already free
*
************************************************************************/
void LoadDefaultButton(DecorFace *df, int i)
{
if (i & 1)
LoadDefaultRightButton(df, i / 2);
else
LoadDefaultLeftButton(df, i / 2);
}
extern void FreeDecorFace(Display *dpy, DecorFace *df);
/***********************************************************************
*
* ResetOrDestroyAllButtons -- resets all buttons to defaults
* destroys existing buttons
*
************************************************************************/
void DestroyAllButtons(FvwmDecor *decor)
{
TitleButton *tbp;
DecorFace *face;
int i;
int j;
for (tbp = decor->buttons, i = 0; i < NUMBER_OF_BUTTONS; i++, tbp++)
{
for (j = 0, face = TB_STATE(*tbp); j < BS_MaxButtonState; j++, face++)
{
FreeDecorFace(dpy, face);
}
}
return;
}
void ResetAllButtons(FvwmDecor *decor)
{
TitleButton *tbp;
DecorFace *face;
int i;
int j;
for (tbp = decor->buttons, i = 0; i < NUMBER_OF_BUTTONS; i++, tbp++)
{
memset(&TB_FLAGS(*tbp), 0, sizeof(TB_FLAGS(*tbp)));
TB_JUSTIFICATION(*tbp) = JUST_CENTER;
for (face = TB_STATE(*tbp), j = 0; j < BS_MaxButtonState; j++, face++)
{
LoadDefaultButton(face, i);
}
}
/* standard MWM decoration hint assignments ([EMAIL PROTECTED])
[Menu] - Title Bar - [Minimize] [Maximize] */
TB_MWM_DECOR_FLAGS(decor->buttons[0]) |= MWM_DECOR_MENU;
TB_MWM_DECOR_FLAGS(decor->buttons[1]) |= MWM_DECOR_MAXIMIZE;
TB_MWM_DECOR_FLAGS(decor->buttons[3]) |= MWM_DECOR_MINIMIZE;
}
/***********************************************************************
*
* Procedure:
* CreateGCs - open fonts and create all the needed GC's. I only
* want to do this once, hence the first_time flag.
*
***********************************************************************/
static void CreateGCs(void)
{
XGCValues gcv;
unsigned long gcm;
XColor c;
/* create scratch GC's */
gcm = GCFunction|GCLineWidth;
gcv.function = GXcopy;
gcv.line_width = 0;
Scr.ScratchGC1 = fvwmlib_XCreateGC(dpy, Scr.NoFocusWin, gcm, &gcv);
Scr.ScratchGC2 = fvwmlib_XCreateGC(dpy, Scr.NoFocusWin, gcm, &gcv);
Scr.ScratchGC3 = fvwmlib_XCreateGC(dpy, Scr.NoFocusWin, gcm, &gcv);
Scr.ScratchGC4 = fvwmlib_XCreateGC(dpy, Scr.NoFocusWin, gcm, &gcv);
Scr.TitleGC = fvwmlib_XCreateGC(dpy, Scr.NoFocusWin, gcm, &gcv);
Scr.TransMaskGC = fvwmlib_XCreateGC(dpy, Scr.NoFocusWin, gcm, &gcv);
c.pixel = GetColor(DEFAULT_FORE_COLOR);
Scr.ScratchMonoPixmap = XCreatePixmap(dpy, Scr.Root, 1, 1, 1);
Scr.MonoGC = fvwmlib_XCreateGC(dpy, Scr.ScratchMonoPixmap, gcm, &gcv);
}
/***********************************************************************
*
* Procedure:
* InitVariables - initialize fvwm variables
*
************************************************************************/
static void InitVariables(void)
{
FvwmContext = XUniqueContext();
MenuContext = XUniqueContext();
/* initialize some lists */
Scr.AllBindings = NULL;
Scr.functions = NULL;
Menus.all = NULL;
Menus.DefaultStyle = NULL;
Menus.LastStyle = NULL;
Scr.last_added_item.type = ADDED_NONE;
Scr.DefaultIcon = NULL;
Scr.DefaultColorset = -1;
AllocColorset(0);
/* set up colorset 0 so that if FvwmTheme fails to start any modules
* using colorsets don't appear black on black */
Colorset[0].fg = Scr.StdFore = GetColor(DEFAULT_FORE_COLOR);
Colorset[0].bg = Scr.StdBack = GetColor(DEFAULT_BACK_COLOR);
Colorset[0].hilite = Scr.StdHilite = GetHilite(Scr.StdBack);
Colorset[0].shadow = Scr.StdShadow = GetShadow(Scr.StdBack);
Scr.StdGC = 0;
Scr.StdReliefGC = 0;
Scr.StdShadowGC = 0;
Scr.XorGC = 0;
/* zero all flags */
memset(&Scr.flags, 0, sizeof(Scr.flags));
/* create graphics contexts */
CreateGCs();
if (Pdepth <= 8) { /* if the color is limited */
/* a number > than the builtin table! */
Scr.ColorLimit = 255;
}
Scr.FvwmRoot.w = Scr.Root;
Scr.FvwmRoot.next = 0;
init_stack_and_layers();
if (!XGetWindowAttributes(dpy,Scr.Root,&(Scr.FvwmRoot.attr)))
{
fvwm_msg(ERR, "InitVariables", "Could not get root window attributes");
exit(0);
}
Scr.root_pushes = 0;
Scr.fvwm_pushes = 0;
Scr.pushed_window = &Scr.FvwmRoot;
Scr.FvwmRoot.number_cmap_windows = 0;
Scr.MyDisplayWidth = DisplayWidth(dpy, Scr.screen);
Scr.MyDisplayHeight = DisplayHeight(dpy, Scr.screen);
Scr.BusyCursor = BUSY_NONE;
Scr.Hilite = NULL;
/* this indicates that the root window was never entered since the startup of
* fvwm. */
Scr.Ungrabbed = NULL;
Scr.DefaultFont.font = NULL;
Scr.VxMax = 2*Scr.MyDisplayWidth;
Scr.VyMax = 2*Scr.MyDisplayHeight;
Scr.Vx = Scr.Vy = 0;
Scr.SizeWindow = None;
/* Sets the current desktop number to zero */
/* Multiple desks are available even in non-virtual
* compilations */
Scr.CurrentDesk = 0;
Scr.EdgeScrollX = Scr.EdgeScrollY = DEFAULT_EDGE_SCROLL;
Scr.ScrollResistance = DEFAULT_SCROLL_RESISTANCE;
Scr.MoveResistance = DEFAULT_MOVE_RESISTANCE;
Scr.XiMoveResistance = DEFAULT_XIMOVE_RESISTANCE;
Scr.SnapAttraction = DEFAULT_SNAP_ATTRACTION;
Scr.SnapMode = DEFAULT_SNAP_ATTRACTION_MODE;
Scr.SnapGridX = DEFAULT_SNAP_GRID_X;
Scr.SnapGridY = DEFAULT_SNAP_GRID_Y;
Scr.OpaqueSize = DEFAULT_OPAQUE_MOVE_SIZE;
Scr.MoveThreshold = DEFAULT_MOVE_THRESHOLD;
/* ClickTime is set to the positive value upon entering the event loop. */
Scr.ClickTime = -DEFAULT_CLICKTIME;
Scr.ColormapFocus = COLORMAP_FOLLOWS_MOUSE;
/* set major operating modes */
Scr.NumBoxes = 0;
Scr.randomx = Scr.randomy = 0;
Scr.buttons2grab = 0;
InitFvwmDecor(&Scr.DefaultDecor);
#ifdef USEDECOR
Scr.DefaultDecor.tag = "Default";
#endif
/* Initialize RaiseHackNeeded by identifying X servers
possibly running under NT. This is probably not an
ideal solution, since eg NCD also produces X servers
which do not run under NT.
"Hummingbird Communications Ltd."
is the ServerVendor string of the Exceed X server under NT,
"Network Computing Devices Inc."
is the ServerVendor string of the PCXware X server under Windows.
"WRQ, Inc."
is the ServerVendor string of the Reflection X server under Windows.
*/
Scr.bo.RaiseHackNeeded =
(strcmp (ServerVendor (dpy), "Hummingbird Communications Ltd.") == 0) ||
(strcmp (ServerVendor (dpy), "Network Computing Devices Inc.") == 0) ||
(strcmp (ServerVendor (dpy), "WRQ, Inc.") == 0);
Scr.bo.ModalityIsEvil = 0;
Scr.bo.DisableConfigureNotify = 0;
Scr.bo.InstallRootCmap = 0;
Scr.bo.FlickeringQtDialogsWorkaround = 1;
Scr.gs.EmulateMWM = DEFAULT_EMULATE_MWM;
Scr.gs.EmulateWIN = DEFAULT_EMULATE_WIN;
Scr.gs.use_active_down_buttons = DEFAULT_USE_ACTIVE_DOWN_BUTTONS;
Scr.gs.use_inactive_buttons = DEFAULT_USE_INACTIVE_BUTTONS;
/* Not the right place for this, should only be called once somewhere .. */
return;
}
/***********************************************************************
*
* Procedure:
* Done - tells FVWM to clean up and exit
*
***********************************************************************
*/
static RETSIGTYPE
SigDone(int sig)
{
fvwmRunState = FVWM_DONE;
/*
* This function might not return - it could "long-jump"
* right out, so we need to do everything we need to do
* BEFORE we call it ...
*/
fvwmSetTerminate(sig);
}
/* if restart is true, command must not be NULL... */
void Done(int restart, char *command)
{
const char *exitFuncName;
/* XFree freeze hack */
XUngrabPointer(dpy, CurrentTime);
XUngrabKeyboard(dpy, CurrentTime);
XUngrabServer(dpy);
if (!restart)
{
MoveViewport(0,0,False);
}
/* migo (03/Jul/1999): execute [Session]ExitFunction */
exitFuncName = getInitFunctionName(2);
if (FindFunction(exitFuncName))
{
char *action = safestrdup(CatString2("Function ", exitFuncName));
old_execute_function(action, NULL, &Event, C_ROOT, 1, 0, NULL);
free(action);
}
if (!restart)
{
Reborder();
}
if (restart)
{
Bool doPreserveState = True;
SaveDesktopState();
if (command)
{
while (isspace(command[0])) command++;
if (strncmp(command, "--dont-preserve-state", 21) == 0)
{
doPreserveState = False;
command += 21;
while (isspace(command[0])) command++;
}
}
if (command[0] == '\0')
command = NULL; /* native restart */
/* won't return under SM on Restart without parameters */
RestartInSession(restart_state_filename, command == NULL, doPreserveState);
/*
RBW - 06/08/1999 - without this, windows will wander to other pages on
a Restart/Recapture because Restart gets the window position information
out of sync. There may be a better way to do this (i.e., adjust the
Restart code), but this works for now.
*/
MoveViewport(0,0,False);
Reborder();
/* Really make sure that the connection is closed and cleared! */
CloseICCCM2();
catch_exit();
XCloseDisplay(dpy);
dpy = NULL;
/* really need to destroy all windows, explicitly,
* not sleep, but this is adequate for now */
sleep(1);
if (command)
{
#define MAX_ARG_SIZE 25
char *my_argv[MAX_ARG_SIZE];
const char *errorMsg;
int n = parseCommandArgs(command, my_argv, MAX_ARG_SIZE, &errorMsg);
if (n <= 0)
{
fvwm_msg(ERR, "Done", "Restart command parsing error in (%s): [%s]",
command, errorMsg);
}
else if (StrEquals(my_argv[0], "--pass-args"))
{
if (n != 2)
{
fvwm_msg(ERR, "Done",
"Restart --pass-args: single name expected. "
"(restarting '%s' instead)",
g_argv[0]);
}
else
{
int i;
my_argv[0] = my_argv[1];
for (i = 1; i < g_argc && i < MAX_ARG_SIZE - 1; i++)
my_argv[i] = g_argv[i];
my_argv[i] = NULL;
execvp(my_argv[0], my_argv);
fvwm_msg(ERR, "Done",
"Call of '%s' failed! (restarting '%s' instead)",
my_argv[0], g_argv[0]);
perror(" system error description");
}
}
else
{
/* Warn against an old 'Restart fvwm2' usage */
if (n == 1 && strcmp(my_argv[0], "fvwm2") == 0)
{
fvwm_msg(WARN, "Done",
"`Restart fvwm2' might not do what you want, see the man page.\n\t"
"Use Restart without parameters if you mean to restart the same WM."
);
}
execvp(my_argv[0], my_argv);
fvwm_msg(ERR, "Done", "Call of '%s' failed! (restarting '%s' instead)",
my_argv[0], g_argv[0]);
perror(" system error description");
}
}
execvp(g_argv[0], g_argv); /* that _should_ work */
fvwm_msg(ERR, "Done", "Call of '%s' failed!", g_argv[0]);
perror(" system error description");
}
else
{
CloseICCCM2();
catch_exit();
XCloseDisplay(dpy);
dpy = NULL;
}
/* Close all my pipes */
/* domivogt (15-Jan-2000): This must be done after calling CloseICCCM2()!
* Otherwise fvwm ignores map requests while it still has
* SubstructureRedirect selected on the root window ==> windows end up in
* nirvana. This explicitly happened with windows unswallowed by FvwmButtons.
*/
ClosePipes();
exit(0);
}
int CatchRedirectError(Display *dpy, XErrorEvent *event)
{
fvwm_msg(ERR,"CatchRedirectError","another WM is running");
exit(1);
/* to make insure happy */
return 0;
}
/***********************************************************************
*
* Procedure:
* CatchFatal - Shuts down if the server connection is lost
*
************************************************************************/
int CatchFatal(Display *dpy)
{
/* No action is taken because usually this action is caused by someone
using "xlogout" to be able to switch between multiple window managers
*/
ClosePipes();
exit(1);
/* to make insure happy */
return 0;
}
/***********************************************************************
*
* Procedure:
* FvwmErrorHandler - displays info on internal errors
*
************************************************************************/
int FvwmErrorHandler(Display *dpy, XErrorEvent *event)
{
extern int last_event_type;
if (event->error_code == BadWindow)
{
bad_window = event->resourceid;
return 0;
}
/* some errors are acceptable, mostly they're caused by
* trying to update a lost window or free'ing another modules colors */
if(event->error_code == BadWindow ||
event->request_code == X_GetGeometry ||
event->error_code == BadDrawable ||
event->request_code == X_ConfigureWindow ||
event->request_code == X_SetInputFocus||
event->request_code == X_GrabButton ||
event->request_code == X_ChangeWindowAttributes ||
event->request_code == X_InstallColormap ||
event->request_code == X_FreePixmap ||
event->request_code == X_FreeColors)
return 0;
fvwm_msg(ERR,"FvwmErrorHandler","*** internal error ***");
fvwm_msg(ERR,"FvwmErrorHandler","Request %d, Error %d, EventType: %d",
event->request_code,
event->error_code,
last_event_type);
return 0;
}
static void usage(void)
{
fprintf(stderr,"\nFVWM version %s Usage:\n\n",VERSION);
fprintf(stderr," %s [-d dpy] [-debug] [-f config_file] [-cmd config_cmd] [-s] [-version] [-h] [-replace] [-clientId id] [-restore file] [-visualId id] [-visual class]\n\n",g_argv[0]);
exit(1);
}
/****************************************************************************
*
* Save Desktop State
*
****************************************************************************/
void SaveDesktopState()
{
FvwmWindow *t;
unsigned long data[1];
for (t = Scr.FvwmRoot.next; t != NULL; t = t->next)
{
data[0] = (unsigned long) t->Desk;
XChangeProperty (dpy, t->w, _XA_WM_DESKTOP, _XA_WM_DESKTOP, 32,
PropModeReplace, (unsigned char *) data, 1);
}
data[0] = (unsigned long) Scr.CurrentDesk;
XChangeProperty (dpy, Scr.Root, _XA_WM_DESKTOP, _XA_WM_DESKTOP, 32,
PropModeReplace, (unsigned char *) data, 1);
XSync(dpy, 0);
}
void SetMWM_INFO(Window window)
{
struct mwminfo
{
long flags;
Window win;
} motif_wm_info;
static char set_yorn='n';
if(set_yorn=='y') {
return;
}
if (Scr.bo.ModalityIsEvil)
{
/* Set Motif WM_INFO atom to make motif relinquish
* broken handling of modal dialogs */
motif_wm_info.flags = 2;
motif_wm_info.win = window;
XChangeProperty(dpy,Scr.Root,_XA_MOTIF_WM,_XA_MOTIF_WM,32,
PropModeReplace,(unsigned char *)&motif_wm_info,2);
set_yorn='y';
}
}
/*
* parseCommandArgs - parses a given command string into a given limited
* argument array suitable for execv*. The parsing is similar to shell's.
* Returns:
* positive number of parsed arguments - on success,
* 0 - on empty command (only spaces),
* negative - on no command or parsing error.
*
* Any character can be quoted with a backslash (even inside single quotes).
* Every command argument is separated by a space/tab/new-line from both sizes
* or is at the start/end of the command. Sequential spaces are ignored.
* An argument can be enclosed into single quotes (no further expanding)
* or double quotes (expending environmental variables $VAR or ${VAR}).
* The character '~' is expanded into user home directory (if not in quotes).
*
* In the current implementation, parsed arguments are stored in one
* large static string pointed by returned argv[0], so they will be lost
* on the next function call. This can be changed using dynamic allocation,
* in this case the caller must free the string pointed by argv[0].
*/
static int parseCommandArgs(
const char *command, char **argv, int maxArgc, const char **errorMsg)
{
/* It is impossible to guess the exact length because of expanding */
#define MAX_TOTAL_ARG_LEN 256
/* char *argString = safemalloc(MAX_TOTAL_ARG_LEN); */
static char argString[MAX_TOTAL_ARG_LEN];
int totalArgLen = 0;
int errorCode = 0;
int argc;
char *aptr = argString;
const char *cptr = command;
#define theChar (*cptr)
#define advChar (cptr++)
#define topChar (*cptr == '\\'? *(cptr+1): *cptr)
#define popChar (*(cptr++) == '\\'? *(cptr++): *(cptr-1))
#define canAddArgChar (totalArgLen < MAX_TOTAL_ARG_LEN-1)
#define addArgChar(ch) (++totalArgLen, *(aptr++) = ch)
#define canAddArgStr(str) (totalArgLen < MAX_TOTAL_ARG_LEN-strlen(str))
#define addArgStr(str) {const char *tmp = str; while (*tmp) { addArgChar(*(tmp++)); }}
*errorMsg = "";
if (!command) { *errorMsg = "No command"; return -1; }
for (argc = 0; argc < maxArgc - 1; argc++) {
int sQuote = 0;
argv[argc] = aptr;
while (isspace(theChar)) advChar;
if (theChar == '\0') break;
while ((sQuote || !isspace(theChar)) && theChar != '\0' && canAddArgChar) {
if (theChar == '"') {
if (sQuote) { sQuote = 0; }
else { sQuote = 1; }
advChar;
} else if (!sQuote && theChar == '\'') {
advChar;
while (theChar != '\'' && theChar != '\0' && canAddArgChar) {
addArgChar(popChar);
}
if (theChar == '\'') advChar;
else if (!canAddArgChar) break;
else { *errorMsg = "No closing single quote"; errorCode = -3; break; }
} else if (!sQuote && theChar == '~') {
if (!canAddArgStr(home_dir)) break;
addArgStr(home_dir);
advChar;
} else if (theChar == '$') {
int beg, len;
const char *str = getFirstEnv(cptr, &beg, &len);
if (!str || beg) { addArgChar(theChar); advChar; continue; }
if (!canAddArgStr(str)) { break; }
addArgStr(str);
cptr += len;
} else {
if (addArgChar(popChar) == '\0') break;
}
}
if (*(aptr-1) == '\0')
{
*errorMsg = "Unexpected last backslash";
errorCode = -2;
break;
}
if (errorCode)
break;
if (theChar == '~' || theChar == '$' || !canAddArgChar)
{
*errorMsg = "The command is too long";
errorCode = -argc - 100;
break;
}
if (sQuote)
{
*errorMsg = "No closing double quote";
errorCode = -4;
break;
}
addArgChar('\0');
}
#undef theChar
#undef advChar
#undef topChar
#undef popChar
#undef canAddArgChar
#undef addArgChar
#undef canAddArgStr
#undef addArgStr
argv[argc] = NULL;
if (argc == 0 && !errorCode) *errorMsg = "Void command";
return errorCode? errorCode: argc;
}
/*
* setInitFunctionName - sets one of the init, restart or exit function names
* getInitFunctionName - gets one of the init, restart or exit function names
*
* First parameter defines a function type: 0 - init, 1 - restart, 2 - exit.
*/
static const char *initFunctionNames[4] = {
"InitFunction", "RestartFunction", "ExitFunction", "Nop"
};
void setInitFunctionName(int n, const char *name)
{
initFunctionNames[n >= 0 && n < 3? n: 3] = name;
}
const char *getInitFunctionName(int n)
{
return initFunctionNames[n >= 0 && n < 3? n: 3];
}
static void setVersionInfo(void)
{
char version_str[256];
char support_str[512] = "";
int support_len;
/* Set version information string */
sprintf(version_str, "FVWM version %s compiled on %s at %s",
VERSION, __DATE__, __TIME__);
Fvwm_VersionInfo = safestrdup(version_str);
#ifdef HAVE_READLINE
strcat(support_str, " ReadLine,");
#endif
#ifdef HAVE_RPLAY
strcat(support_str, " RPlay,");
#endif
#ifdef HAVE_STROKE
strcat(support_str, " Stroke,");
#endif
#ifdef XPM
strcat(support_str, " XPM,");
#endif
#ifdef GNOME
strcat(support_str, " GNOME WM hints,");
#endif
if (FHaveShapeExtension)
strcat(support_str, " Shape,");
#ifdef SESSION
strcat(support_str, " SM,");
#endif
#ifdef I18N_MB
strcat(support_str, " Multibyte,");
#endif
#ifdef HAVE_XINERAMA
strcat(support_str, " Xinerama,");
#endif
support_len = strlen(support_str);
if (support_len > 0)
{
/* strip last comma */
support_str[support_len - 1] = '\0';
Fvwm_SupportInfo = safestrdup(CatString2("with support for:", support_str));
}
else
Fvwm_SupportInfo = "with no optional feature support";
}
/* 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 module is all original code
* by Rob Nation
* Copyright 1993, Robert Nation
* You may use this code for any purpose, as long as the original
* copyright remains in the source code and all documentation
****************************************************************************/
#include "config.h"
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <X11/keysym.h>
#include "libs/fvwmlib.h"
#include "libs/fvwmsignal.h"
#include "libs/FShape.h"
#include "fvwm.h"
#include "externs.h"
#include "libs/Colorset.h"
#include "bindings.h"
#include "misc.h"
#include "cursor.h"
#include "functions.h"
#include "commands.h"
#include "screen.h"
#include "defaults.h"
#include "builtins.h"
#include "module_interface.h"
#include "borders.h"
#include "geometry.h"
#include "events.h"
#include "gnome.h"
#include "icons.h"
#include "menus.h"
#include "virtual.h"
#include "decorations.h"
#include "add_window.h"
#include "update.h"
#include "style.h"
#include "focus.h"
#include "stack.h"
#include "move_resize.h"
#ifdef HAVE_STROKE
#include "stroke.h"
#endif /* HAVE_STROKE */
static char *ReadTitleButton(
char *s, TitleButton *tb, Boolean append, int button);
static void DestroyFvwmDecor(FvwmDecor *decor);
extern float rgpctMovementDefault[32];
extern int cpctMovementDefault;
extern int cmsDelayDefault;
char *ModulePath = FVWM_MODULEDIR;
int moduleTimeout = DEFAULT_MODULE_TIMEOUT;
static char *exec_shell_name="/bin/sh";
/* button state strings must match the enumerated states */
static char *button_states[BS_MaxButtonState + 1] =
{
"ActiveUp",
"ActiveDown",
"Inactive",
"ToggledActiveUp",
"ToggledActiveDown",
"ToggledInactive",
NULL
};
/***********************************************************************
*
* WindowShade -- shades or unshades a window ([EMAIL PROTECTED])
*
* Args: 1 -- shade, 2 -- unshade No Arg: toggle
*
***********************************************************************
*
* Animation added.
* Based on code from AfterStep-1.0 (CT: [EMAIL PROTECTED])
* Additional fixes by Tomas Ogren <[EMAIL PROTECTED]>
*
* Builtin function: WindowShade
***********************************************************************/
void CMD_WindowShade(F_CMD_ARGS)
{
int bwl;
int bwr;
int bw;
int bht;
int bhb;
int bh;
int cw;
int ch;
int step = 1;
int toggle;
int grav = NorthWestGravity;
int client_grav = NorthWestGravity;
int move_parent_too = False;
rectangle frame_g;
rectangle parent_g;
rectangle shape_g;
rectangle diff;
rectangle pdiff;
rectangle sdiff;
rectangle big_g;
rectangle small_g;
Bool do_scroll;
FvwmWindow *sf;
static Window shape_w = None;
if (DeferExecution(eventp,&w,&tmp_win,&context, CRS_SELECT,ButtonRelease))
return;
if (tmp_win == NULL || IS_ICONIFIED(tmp_win))
return;
if (FShapesSupported && shape_w == None && tmp_win->wShaped)
{
XSetWindowAttributes attributes;
unsigned long valuemask;
valuemask = CWOverrideRedirect;
attributes.override_redirect = True;
shape_w = XCreateWindow(
dpy, Scr.Root, -32768, -32768, 1, 1, 0, CopyFromParent,
(unsigned int) CopyFromParent, CopyFromParent, valuemask, &attributes);
}
sf = get_focus_window();
/* parse arguments */
toggle = ParseToggleArgument(action, NULL, -1, 0);
if (toggle == -1)
{
if (GetIntegerArguments(action, NULL, &toggle, 1) > 0)
{
if (toggle == 1)
toggle = 1;
else if (toggle == 2)
toggle = 0;
else
toggle = -1;
}
}
if (toggle == -1)
toggle = (IS_SHADED(tmp_win)) ? 0 : 1;
/* prepare some convenience variables */
frame_g = tmp_win->frame_g;
big_g = (IS_MAXIMIZED(tmp_win)) ? tmp_win->max_g : tmp_win->normal_g;
get_relative_geometry(&big_g, &big_g);
bwl = tmp_win->boundary_width;
bwr = tmp_win->boundary_width;
bw = bwl + bwr;
bht = tmp_win->boundary_width + tmp_win->title_top_height;
bhb = tmp_win->boundary_width +
tmp_win->title_g.height - tmp_win->title_top_height;;
bh = bht + bhb;
cw = big_g.width - bw;
ch = big_g.height - bh;
do_scroll = !DO_SHRINK_WINDOWSHADE(tmp_win);
/* calcuate the step size */
if (tmp_win->shade_anim_steps > 0)
step = big_g.height / tmp_win->shade_anim_steps;
else if (tmp_win->shade_anim_steps < 0) /* if it's -ve it means pixels */
step = -tmp_win->shade_anim_steps;
if (step <= 0) /* We don't want an endless loop, do we? */
step = 1;
if (tmp_win->shade_anim_steps)
{
grav = (HAS_BOTTOM_TITLE(tmp_win)) ? SouthEastGravity : NorthWestGravity;
if (!do_scroll)
client_grav = grav;
else
client_grav =
(HAS_BOTTOM_TITLE(tmp_win)) ? NorthWestGravity : SouthEastGravity;
set_decor_gravity(tmp_win, grav, grav, client_grav);
move_parent_too = HAS_BOTTOM_TITLE(tmp_win);
}
if (IS_SHADED(tmp_win) && toggle == 0)
{
/* unshade window */
SET_SHADED(tmp_win, 0);
if (tmp_win->shade_anim_steps != 0)
{
XMoveResizeWindow(dpy, tmp_win->Parent, bwl, bht, cw, 1);
XMoveWindow(dpy, tmp_win->w, 0,
(client_grav == SouthEastGravity) ? -ch + 1 : 0);
XLowerWindow(dpy, tmp_win->decor_w);
parent_g.x = bwl;
parent_g.y = bht + (HAS_BOTTOM_TITLE(tmp_win) ? -step : 0);
parent_g.width = cw;
parent_g.height = 0;
pdiff.x = 0;
pdiff.y = 0;
pdiff.width = 0;
pdiff.height = step;
if (client_grav == SouthEastGravity)
{
shape_g.x = bwl;
shape_g.y = bht - ch;
sdiff.x = 0;
sdiff.y = step;
}
else
{
shape_g.x = bwl;
shape_g.y = bht;
sdiff.x = 0;
sdiff.y = 0;
}
diff.x = 0;
diff.y = HAS_BOTTOM_TITLE(tmp_win) ? -step : 0;
diff.width = 0;
diff.height = step;
/* This causes a ConfigureNotify if the client size has changed while
* the window was shaded. */
XResizeWindow(dpy, tmp_win->w, cw, ch);
/* make the decor window the full sze before the animation unveils it */
XMoveResizeWindow(dpy, tmp_win->decor_w, 0, HAS_BOTTOM_TITLE(tmp_win)
? frame_g.height - big_g.height : 0, big_g.width, big_g.height);
/* draw the border decoration iff backing store is on */
if (Scr.use_backing_store != NotUseful)
{ /* eek! fvwm doesn't know the backing_store setting for windows */
XWindowAttributes xwa;
if (XGetWindowAttributes(dpy, tmp_win->decor_w, &xwa)
&& xwa.backing_store != NotUseful)
{
tmp_win->frame_g.width = big_g.width;
tmp_win->frame_g.height = big_g.height;
DrawDecorations(
tmp_win, DRAW_FRAME, sf == tmp_win, True, None);
}
}
while (frame_g.height + diff.height < big_g.height)
{
parent_g.x += pdiff.x;
parent_g.y += pdiff.y;
parent_g.width += pdiff.width;
parent_g.height += pdiff.height;
shape_g.x += sdiff.x;
shape_g.y += sdiff.y;
frame_g.x += diff.x;
frame_g.y += diff.y;
frame_g.width += diff.width;
frame_g.height += diff.height;
if (FShapesSupported && tmp_win->wShaped && shape_w)
{
FShapeCombineShape(
dpy, shape_w, FShapeBounding, shape_g.x, shape_g.y, tmp_win->w,
FShapeBounding, FShapeSet);
if (tmp_win->title_w)
{
XRectangle rect;
/* windows w/ titles */
rect.x = bwl;
rect.y = (HAS_BOTTOM_TITLE(tmp_win)) ? frame_g.height - bhb : 0;
rect.width = frame_g.width - bw;
rect.height = tmp_win->title_g.height;
FShapeCombineRectangles(
dpy, shape_w, FShapeBounding, 0, 0, &rect, 1, FShapeUnion,
Unsorted);
}
}
if (move_parent_too)
{
if (FShapesSupported && tmp_win->wShaped && shape_w)
{
FShapeCombineShape(
dpy, tmp_win->frame, FShapeBounding, 0, 0, shape_w,
FShapeBounding, FShapeUnion);
}
XMoveResizeWindow(
dpy, tmp_win->Parent, parent_g.x, parent_g.y, parent_g.width,
parent_g.height);
}
else
{
XResizeWindow(dpy, tmp_win->Parent, parent_g.width, parent_g.height);
}
XMoveResizeWindow(
dpy, tmp_win->frame, frame_g.x, frame_g.y, frame_g.width,
frame_g.height);
if (FShapesSupported && tmp_win->wShaped && shape_w)
{
FShapeCombineShape(
dpy, tmp_win->frame, FShapeBounding, 0, 0, shape_w, FShapeBounding,
FShapeSet);
}
FlushAllMessageQueues();
XSync(dpy, 0);
}
if (client_grav == SouthEastGravity && move_parent_too)
{
/* We must finish above loop to the very end for this special case.
* Otherwise there is a visible jump of the client window. */
XMoveResizeWindow(
dpy, tmp_win->Parent, parent_g.x,
bht - (big_g.height - frame_g.height), parent_g.width, ch);
XMoveResizeWindow(
dpy, tmp_win->frame, big_g.x, big_g.y, big_g.width, big_g.height);
}
else
{
XMoveWindow(dpy, tmp_win->w, 0, 0);
}
if (sf)
{
focus_grab_buttons(sf, True);
}
}
else
{
XMoveResizeWindow(dpy, tmp_win->w, 0, 0, cw, ch);
if (HAS_BOTTOM_TITLE(tmp_win))
{
XMoveResizeWindow(dpy, tmp_win->Parent, bwl, bht - ch + 1, cw, ch);
set_decor_gravity(
tmp_win, SouthEastGravity, SouthEastGravity, NorthWestGravity);
}
else
{
XMoveResizeWindow(dpy, tmp_win->Parent, bwl, bht, cw, ch);
set_decor_gravity(
tmp_win, NorthWestGravity, NorthWestGravity, NorthWestGravity);
}
XLowerWindow(dpy, tmp_win->decor_w);
XMoveResizeWindow(
dpy, tmp_win->frame, big_g.x, big_g.y, big_g.width, big_g.height);
}
set_decor_gravity(
tmp_win, NorthWestGravity, NorthWestGravity, NorthWestGravity);
tmp_win->frame_g.height = big_g.height + 1;
SetupFrame(
tmp_win, big_g.x, big_g.y, big_g.width, big_g.height, True);
tmp_win->frame_g = big_g;
update_absolute_geometry(tmp_win);
BroadcastConfig(M_CONFIGURE_WINDOW, tmp_win);
BroadcastPacket(M_DEWINDOWSHADE, 3, tmp_win->w, tmp_win->frame,
(unsigned long)tmp_win);
}
else if (!IS_SHADED(tmp_win) && toggle == 1)
{
/* shade window */
SET_SHADED(tmp_win, 1);
get_shaded_geometry(tmp_win, &small_g, &big_g);
if (small_g.width < 1)
{
small_g.width = 1;
}
if (small_g.height < 1)
{
small_g.height = 1;
}
if (tmp_win->shade_anim_steps != 0)
{
parent_g.x = bwl;
parent_g.y = bht;
parent_g.width = cw;
parent_g.height = frame_g.height - bh;
pdiff.x = 0;
pdiff.y = 0;
pdiff.width = 0;
pdiff.height = -step;
shape_g = parent_g;
sdiff.x = 0;
sdiff.y = (client_grav == SouthEastGravity) ? -step : 0;
diff.x = 0;
diff.y = HAS_BOTTOM_TITLE(tmp_win) ? step : 0;
diff.width = 0;
diff.height = -step;
while (frame_g.height + diff.height > small_g.height)
{
parent_g.x += pdiff.x;
parent_g.y += pdiff.y;
parent_g.width += pdiff.width;
parent_g.height += pdiff.height;
shape_g.x += sdiff.x;
shape_g.y += sdiff.y;
frame_g.x += diff.x;
frame_g.y += diff.y;
frame_g.width += diff.width;
frame_g.height += diff.height;
if (FShapesSupported && tmp_win->wShaped && shape_w)
{
FShapeCombineShape(
dpy, shape_w, FShapeBounding, shape_g.x, shape_g.y, tmp_win->w,
FShapeBounding, FShapeSet);
if (tmp_win->title_w)
{
XRectangle rect;
/* windows w/ titles */
rect.x = bwl;
rect.y = (HAS_BOTTOM_TITLE(tmp_win)) ? frame_g.height - bhb : 0;
rect.width = big_g.width - bw;
rect.height = tmp_win->title_g.height;
FShapeCombineRectangles(
dpy, shape_w, FShapeBounding, 0, 0, &rect, 1, FShapeUnion,
Unsorted);
}
}
if (move_parent_too)
{
if (FShapesSupported && tmp_win->wShaped && shape_w)
{
FShapeCombineShape(
dpy, tmp_win->frame, FShapeBounding, 0, 0, shape_w,
FShapeBounding, FShapeUnion);
}
XMoveResizeWindow(
dpy, tmp_win->frame, frame_g.x, frame_g.y, frame_g.width,
frame_g.height);
XMoveResizeWindow(
dpy, tmp_win->Parent, parent_g.x, parent_g.y, parent_g.width,
parent_g.height);
}
else
{
XResizeWindow(dpy, tmp_win->Parent, parent_g.width, parent_g.height);
XMoveResizeWindow(
dpy, tmp_win->frame, frame_g.x, frame_g.y, frame_g.width,
frame_g.height);
}
if (FShapesSupported && tmp_win->wShaped && shape_w)
{
FShapeCombineShape(
dpy, tmp_win->frame, FShapeBounding, 0, 0, shape_w, FShapeBounding,
FShapeSet);
}
FlushAllMessageQueues();
XSync(dpy, 0);
}
}
/* All this stuff is necessary to prevent flickering */
set_decor_gravity(tmp_win, grav, UnmapGravity, client_grav);
XMoveResizeWindow(
dpy, tmp_win->frame, small_g.x, small_g.y, small_g.width,
small_g.height);
XResizeWindow(dpy, tmp_win->Parent, cw, 1);
XMoveWindow(dpy, tmp_win->decor_w, 0, 0);
XRaiseWindow(dpy, tmp_win->decor_w);
XMapWindow(dpy, tmp_win->Parent);
if (HAS_BOTTOM_TITLE(tmp_win))
{
XMoveWindow(dpy, tmp_win->w, 0, 1 - ch);
}
else
{
XMoveWindow(dpy, tmp_win->w, 0, 0);
}
/* domivogt (28-Dec-1999): For some reason the XMoveResize() on the frame
* window removes the input focus from the client window. I have no idea
* why, but if we explicitly restore the focus here everything works fine.
*/
if (sf == tmp_win)
FOCUS_SET(tmp_win->w);
set_decor_gravity(
tmp_win, NorthWestGravity, NorthWestGravity, NorthWestGravity);
/* Finally let SetupFrame take care of the window */
SetupFrame(
tmp_win, small_g.x, small_g.y, small_g.width, small_g.height, False);
tmp_win->frame_g = small_g;
update_absolute_geometry(tmp_win);
BroadcastConfig(M_CONFIGURE_WINDOW, tmp_win);
BroadcastPacket(
M_WINDOWSHADE, 3, tmp_win->w, tmp_win->frame, (unsigned long)tmp_win);
}
DrawDecorations(
tmp_win, DRAW_FRAME | DRAW_BUTTONS, (Scr.Hilite == tmp_win), True, None);
FlushAllMessageQueues();
XSync(dpy, 0);
GNOME_SetHints(tmp_win);
GNOME_SetWinArea(tmp_win);
}
void CMD_Beep(F_CMD_ARGS)
{
XBell(dpy, 0);
}
void CMD_Plus(F_CMD_ARGS)
{
if (Scr.last_added_item.type == ADDED_MENU)
add_another_menu_item(action);
else if (Scr.last_added_item.type == ADDED_FUNCTION)
AddToFunction(Scr.last_added_item.item, action);
#ifdef USEDECOR
else if (Scr.last_added_item.type == ADDED_DECOR) {
FvwmDecor *tmp = &Scr.DefaultDecor;
for (; tmp; tmp = tmp->next)
if (tmp == Scr.last_added_item.item)
break;
if (!tmp)
return;
AddToDecor(tmp, action);
}
#endif /* USEDECOR */
}
void CMD_DestroyFunc(F_CMD_ARGS)
{
FvwmFunction *func;
char *token;
GetNextToken(action,&token);
if (!token)
return;
func = FindFunction(token);
free(token);
if (!func)
return;
if (Scr.last_added_item.type == ADDED_FUNCTION)
set_last_added_item(ADDED_NONE, NULL);
DestroyFunction(func);
return;
}
void CMD_AddToFunc(F_CMD_ARGS)
{
FvwmFunction *func;
char *token;
action = GetNextToken(action,&token);
if (!token)
return;
func = FindFunction(token);
if(func == NULL)
func = NewFvwmFunction(token);
/* Set + state to last function */
set_last_added_item(ADDED_FUNCTION, func);
free(token);
AddToFunction(func, action);
return;
}
void CMD_Nop(F_CMD_ARGS)
{
}
void CMD_EscapeFunc(F_CMD_ARGS)
{
}
void CMD_CursorMove(F_CMD_ARGS)
{
int x = 0, y = 0;
int val1, val2, val1_unit, val2_unit;
int virtual_x, virtual_y;
int pan_x, pan_y;
int x_pages, y_pages;
if (GetTwoArguments(action, &val1, &val2, &val1_unit, &val2_unit) != 2)
{
fvwm_msg(ERR, "movecursor", "CursorMove needs 2 arguments");
return;
}
XQueryPointer( dpy, Scr.Root, &JunkRoot, &JunkChild,
&x, &y, &JunkX, &JunkY, &JunkMask);
x = x + val1 * val1_unit / 100;
y = y + val2 * val2_unit / 100;
virtual_x = Scr.Vx;
virtual_y = Scr.Vy;
if (x >= 0)
x_pages = x / Scr.MyDisplayWidth;
else
x_pages = ((x + 1) / Scr.MyDisplayWidth) - 1;
virtual_x += x_pages * Scr.MyDisplayWidth;
x -= x_pages * Scr.MyDisplayWidth;
if (virtual_x < 0)
{
x += virtual_x;
virtual_x = 0;
}
else if (virtual_x > Scr.VxMax)
{
x += virtual_x - Scr.VxMax;
virtual_x = Scr.VxMax;
}
if (y >= 0)
y_pages = y / Scr.MyDisplayHeight;
else
y_pages = ((y + 1) / Scr.MyDisplayHeight) - 1;
virtual_y += y_pages * Scr.MyDisplayHeight;
y -= y_pages * Scr.MyDisplayHeight;
if (virtual_y < 0)
{
y += virtual_y;
virtual_y = 0;
}
else if (virtual_y > Scr.VyMax)
{
y += virtual_y - Scr.VyMax;
virtual_y = Scr.VyMax;
}
if (virtual_x != Scr.Vx || virtual_y != Scr.Vy)
MoveViewport(virtual_x, virtual_y, True);
pan_x = (Scr.EdgeScrollX != 0) ? 2 : 0;
pan_y = (Scr.EdgeScrollY != 0) ? 2 : 0;
/* prevent paging if EdgeScroll is active */
if (x >= Scr.MyDisplayWidth - pan_x)
x = Scr.MyDisplayWidth - pan_x -1;
else if (x < pan_x)
x = pan_x;
if (y >= Scr.MyDisplayHeight - pan_y)
y = Scr.MyDisplayHeight - pan_y - 1;
else if (y < pan_y)
y = pan_y;
XWarpPointer(dpy, None, Scr.Root, 0, 0, Scr.MyDisplayWidth,
Scr.MyDisplayHeight, x, y);
return;
}
void CMD_Destroy(F_CMD_ARGS)
{
if (DeferExecution(eventp,&w,&tmp_win,&context, CRS_DESTROY, ButtonRelease))
return;
if(check_if_function_allowed(F_DESTROY,tmp_win,True,NULL) == 0)
{
XBell(dpy, 0);
return;
}
if (XGetGeometry(dpy, tmp_win->w, &JunkRoot, &JunkX, &JunkY,
&JunkWidth, &JunkHeight, &JunkBW, &JunkDepth) == 0)
{
destroy_window(tmp_win);
}
else
XKillClient(dpy, tmp_win->w);
XSync(dpy,0);
}
void CMD_Delete(F_CMD_ARGS)
{
if (DeferExecution(eventp,&w,&tmp_win,&context, CRS_DESTROY,ButtonRelease))
return;
if(check_if_function_allowed(F_DELETE,tmp_win,True,NULL) == 0)
{
XBell(dpy, 0);
return;
}
if (WM_DELETES_WINDOW(tmp_win))
{
send_clientmessage (dpy, tmp_win->w, _XA_WM_DELETE_WINDOW, CurrentTime);
return;
}
else
{
XBell (dpy, 0);
}
XSync(dpy,0);
}
void CMD_Close(F_CMD_ARGS)
{
if (DeferExecution(eventp,&w,&tmp_win,&context, CRS_DESTROY,ButtonRelease))
return;
if(check_if_function_allowed(F_CLOSE,tmp_win,True,NULL) == 0)
{
XBell(dpy, 0);
return;
}
if (WM_DELETES_WINDOW(tmp_win))
{
send_clientmessage (dpy, tmp_win->w, _XA_WM_DELETE_WINDOW, CurrentTime);
return;
}
else if (XGetGeometry(dpy, tmp_win->w, &JunkRoot, &JunkX, &JunkY,
&JunkWidth, &JunkHeight, &JunkBW, &JunkDepth) == 0)
{
destroy_window(tmp_win);
}
else
{
XKillClient(dpy, tmp_win->w);
}
XSync(dpy,0);
}
void CMD_Restart(F_CMD_ARGS)
{
Done(1, action);
}
void CMD_ExecUseShell(F_CMD_ARGS)
{
char *arg=NULL;
static char shell_set = 0;
if (shell_set)
free(exec_shell_name);
shell_set = 1;
action = GetNextToken(action,&arg);
if (arg) /* specific shell was specified */
{
exec_shell_name = arg;
}
else /* no arg, so use $SHELL -- not working??? */
{
if (getenv("SHELL"))
exec_shell_name = safestrdup(getenv("SHELL"));
else
/* if $SHELL not set, use default */
exec_shell_name = safestrdup("/bin/sh");
}
}
void CMD_Exec(F_CMD_ARGS)
{
char *cmd=NULL;
/* if it doesn't already have an 'exec' as the first word, add that
* to keep down number of procs started */
/* need to parse string better to do this right though, so not doing this
for now... */
#if 0
if (strncasecmp(action,"exec",4)!=0)
{
cmd = (char *)safemalloc(strlen(action)+6);
strcpy(cmd,"exec ");
strcat(cmd,action);
}
else
#endif
{
cmd = safestrdup(action);
}
if (!cmd)
return;
/* Use to grab the pointer here, but the fork guarantees that
* we wont be held up waiting for the function to finish,
* so the pointer-gram just caused needless delay and flashing
* on the screen */
/* Thought I'd try vfork and _exit() instead of regular fork().
* The man page says that its better. */
/* Not everyone has vfork! */
if (!(fork())) /* child process */
{
if (execl(exec_shell_name, exec_shell_name, "-c", cmd, NULL)==-1)
{
fvwm_msg(ERR,"exec_function","execl failed (%s)",strerror(errno));
exit(100);
}
}
free(cmd);
return;
}
static void refresh_window(Window w)
{
XSetWindowAttributes attributes;
unsigned long valuemask;
valuemask = CWOverrideRedirect | CWBackingStore | CWSaveUnder | CWBackPixmap;
attributes.override_redirect = True;
attributes.save_under = False;
attributes.background_pixmap = None;
attributes.backing_store = NotUseful;
w = XCreateWindow(dpy,
w,
0, 0,
(unsigned int) Scr.MyDisplayWidth,
(unsigned int) Scr.MyDisplayHeight,
(unsigned int) 0,
CopyFromParent, (unsigned int) CopyFromParent,
CopyFromParent, valuemask,
&attributes);
XMapWindow(dpy, w);
if (Scr.flags.do_need_window_update)
{
flush_window_updates();
}
XDestroyWindow(dpy, w);
XSync(dpy, 0);
usleep(1);
XSync(dpy, 0);
handle_all_expose();
return;
}
void CMD_Refresh(F_CMD_ARGS)
{
refresh_window(Scr.Root);
}
void CMD_RefreshWindow(F_CMD_ARGS)
{
if (DeferExecution(eventp,&w,&tmp_win,&context,CRS_SELECT,ButtonRelease))
return;
refresh_window((context == C_ICON)? tmp_win->icon_w : tmp_win->frame);
}
void CMD_Wait(F_CMD_ARGS)
{
Bool done = False;
Bool redefine_cursor = False;
Bool is_ungrabbed;
char *escape;
Window nonewin = None;
extern FvwmWindow *Tmp_win;
char *wait_string, *rest;
FvwmWindow *s_Tmp_win = Tmp_win;
/* try to get a single token */
rest = GetNextToken(action, &wait_string);
if (wait_string)
{
while (*rest && isspace((unsigned char)*rest))
rest++;
if (*rest)
{
int i;
char *temp;
/* nope, multiple tokens - try old syntax */
/* strip leading and trailing whitespace */
temp = action;
while (*temp && isspace((unsigned char)*temp))
temp++;
wait_string = safestrdup(temp);
for (i = strlen(wait_string) - 1; i >= 0 && isspace(wait_string[i]); i--)
{
wait_string[i] = 0;
}
}
}
else
{
wait_string = safestrdup("");
}
is_ungrabbed = UngrabEm(GRAB_NORMAL);
while (!done && !isTerminated)
{
if (BUSY_WAIT & Scr.BusyCursor)
{
XDefineCursor(dpy, Scr.Root, Scr.FvwmCursors[CRS_WAIT]);
redefine_cursor = True;
}
if (My_XNextEvent(dpy, &Event))
{
DispatchEvent(False);
if(Event.type == MapNotify)
{
if (!*wait_string)
done = True;
if((Tmp_win)&&(matchWildcards(wait_string, Tmp_win->name)==True))
done = True;
else if((Tmp_win)&&(Tmp_win->class.res_class)&&
(matchWildcards(wait_string, Tmp_win->class.res_class)==True))
done = True;
else if((Tmp_win)&&(Tmp_win->class.res_name)&&
(matchWildcards(wait_string, Tmp_win->class.res_name)==True))
done = True;
}
else if (Event.type == KeyPress)
{
escape = CheckBinding(Scr.AllBindings,
STROKE_ARG(0)
Event.xkey.keycode,
Event.xkey.state, GetUnusedModifiers(),
GetContext(Tmp_win,&Event,&nonewin),
KEY_BINDING);
if (escape != NULL)
{
if (!strcasecmp(escape,"escapefunc"))
done = True;
}
}
}
}
if (redefine_cursor)
XDefineCursor(dpy, Scr.Root, Scr.FvwmCursors[CRS_ROOT]);
if (is_ungrabbed)
{
GrabEm(CRS_NONE, GRAB_NORMAL);
}
Tmp_win = (check_if_fvwm_window_exists(s_Tmp_win)) ? s_Tmp_win : NULL;
free(wait_string);
return;
}
void CMD_Quit(F_CMD_ARGS)
{
if (master_pid != getpid())
kill(master_pid, SIGTERM);
Done(0,NULL);
}
void CMD_QuitScreen(F_CMD_ARGS)
{
Done(0,NULL);
}
void CMD_Echo(F_CMD_ARGS)
{
unsigned int len;
if (!action)
action = "";
len = strlen(action);
if (len != 0)
{
if (action[len-1]=='\n')
action[len-1]='\0';
}
fvwm_msg(ECHO,"Echo",action);
}
void CMD_ColormapFocus(F_CMD_ARGS)
{
if (MatchToken(action,"FollowsFocus"))
{
Scr.ColormapFocus = COLORMAP_FOLLOWS_FOCUS;
}
else if (MatchToken(action,"FollowsMouse"))
{
Scr.ColormapFocus = COLORMAP_FOLLOWS_MOUSE;
}
else
{
fvwm_msg(ERR,"SetColormapFocus",
"ColormapFocus requires 1 arg: FollowsFocus or FollowsMouse");
return;
}
}
void CMD_ClickTime(F_CMD_ARGS)
{
int val;
if(GetIntegerArguments(action, NULL, &val, 1) != 1)
{
Scr.ClickTime = DEFAULT_CLICKTIME;
}
else
{
Scr.ClickTime = (val < 0)? 0 : val;
}
/* Use a negative value during startup and change sign afterwards. This
* speeds things up quite a bit. */
if (fFvwmInStartup)
Scr.ClickTime = -Scr.ClickTime;
}
void CMD_ImagePath(F_CMD_ARGS)
{
SetImagePath( action );
}
/** Prepend rather than replace the image path.
Used for obsolete PixmapPath and IconPath **/
static void obsolete_imagepaths( const char* pre_path )
{
char* tmp = stripcpy( pre_path );
char* path = alloca( strlen( tmp ) + strlen( GetImagePath() ) + 2 );
strcpy( path, tmp );
free( tmp );
strcat( path, ":" );
strcat( path, GetImagePath() );
SetImagePath( path );
}
void CMD_IconPath(F_CMD_ARGS)
{
fvwm_msg(ERR, "iconPath_function",
"IconPath is deprecated since 2.3.0; use ImagePath instead." );
obsolete_imagepaths( action );
}
void CMD_PixmapPath(F_CMD_ARGS)
{
fvwm_msg(ERR, "pixmapPath_function",
"PixmapPath is deprecated since 2.3.0; use ImagePath instead." );
obsolete_imagepaths( action );
}
void CMD_ModulePath(F_CMD_ARGS)
{
static int need_to_free = 0;
setPath( &ModulePath, action, need_to_free );
need_to_free = 1;
}
void CMD_ModuleTimeout(F_CMD_ARGS)
{
int timeout;
moduleTimeout = DEFAULT_MODULE_TIMEOUT;
if (
(GetIntegerArguments(action, NULL, &timeout, 1) == 1) &&
(timeout > 0)
)
{
moduleTimeout = timeout;
}
}
void CMD_HilightColor(F_CMD_ARGS)
{
char *fore;
char *back;
#ifdef USEDECOR
if (Scr.cur_decor && Scr.cur_decor != &Scr.DefaultDecor)
{
fvwm_msg(
ERR, "SetHiColor",
"Decors do not support the HilightColor command anymore."
" Please use 'Style <stylename> HilightFore <forecolor>' and"
" 'Style <stylename> HilightBack <backcolor>' instead."
" Sorry for the inconvenience.");
return;
}
#endif
action = GetNextToken(action, &fore);
GetNextToken(action, &back);
if (fore && back)
{
action = safemalloc(strlen(fore) + strlen(back) + 29);
sprintf(action, "* HilightFore %s, HilightBack %s", fore, back);
CMD_Style(F_PASS_ARGS);
}
if (fore)
free(fore);
if (back)
free(back);
}
void CMD_HilightColorset(F_CMD_ARGS)
{
char *newaction;
#ifdef USEDECOR
if (Scr.cur_decor && Scr.cur_decor != &Scr.DefaultDecor)
{
fvwm_msg(
ERR, "SetHiColorset",
"Decors do not support the HilightColorset command anymore."
" Please use 'Style <stylename> HilightColorset <colorset>' instead."
" Sorry for the inconvenience.");
return;
}
#endif
if (action)
{
newaction = safemalloc(strlen(action) + 32);
sprintf(newaction, "* HilightColorset %s", action);
action = newaction;
CMD_Style(F_PASS_ARGS);
free(newaction);
}
}
void do_title_style(F_CMD_ARGS, Bool do_add)
{
char *parm;
char *prev;
#ifdef USEDECOR
FvwmDecor *decor = Scr.cur_decor ? Scr.cur_decor : &Scr.DefaultDecor;
#else
FvwmDecor *decor = &Scr.DefaultDecor;
#endif
Scr.flags.do_need_window_update = 1;
decor->flags.has_changed = 1;
decor->titlebar.flags.has_changed = 1;
for (prev = action ; (parm = PeekToken(action, &action)); prev = action)
{
if (!do_add && StrEquals(parm,"centered"))
{
TB_JUSTIFICATION(decor->titlebar) = JUST_CENTER;
}
else if (!do_add && StrEquals(parm,"leftjustified"))
{
TB_JUSTIFICATION(decor->titlebar) = JUST_LEFT;
}
else if (!do_add && StrEquals(parm,"rightjustified"))
{
TB_JUSTIFICATION(decor->titlebar) = JUST_RIGHT;
}
else if (!do_add && StrEquals(parm,"height"))
{
int height = 0;
int next = 0;
if (!action || sscanf(action, "%d%n", &height, &next) <= 0 ||
height < MIN_FONT_HEIGHT || height > MAX_FONT_HEIGHT)
{
if (height != 0)
{
fvwm_msg(ERR, "do_title_style",
"bad height argument (height must be from 5 to 256)");
height = 0;
}
}
if (decor->title_height != height)
{
decor->title_height = height;
decor->flags.has_title_height_changed = 1;
}
if (action)
action += next;
}
else
{
action = ReadTitleButton(prev, &decor->titlebar, do_add, -1);
}
}
}
void CMD_TitleStyle(F_CMD_ARGS)
{
do_title_style(F_PASS_ARGS, False);
} /* SetTitleStyle */
#if defined(MULTISTYLE)
/*****************************************************************************
*
* Appends a titlestyle ([EMAIL PROTECTED])
*
****************************************************************************/
void CMD_AddTitleStyle(F_CMD_ARGS)
{
do_title_style(F_PASS_ARGS, True);
}
#endif /* MULTISTYLE */
void ApplyDefaultFontAndColors(void)
{
XGCValues gcv;
unsigned long gcm;
int cset = Scr.DefaultColorset;
/* make GC's */
gcm = GCFunction|GCFont|GCLineWidth|GCForeground|GCBackground;
gcv.function = GXcopy;
gcv.font = Scr.DefaultFont.font->fid;
gcv.line_width = 0;
if (cset >= 0) {
gcv.foreground = Colorset[cset].fg;
gcv.background = Colorset[cset].bg;
} else {
gcv.foreground = Scr.StdFore;
gcv.background = Scr.StdBack;
}
if(Scr.StdGC)
XChangeGC(dpy, Scr.StdGC, gcm, &gcv);
else
Scr.StdGC = fvwmlib_XCreateGC(dpy, Scr.NoFocusWin, gcm, &gcv);
gcm = GCFunction|GCLineWidth|GCForeground;
if (cset >= 0)
gcv.foreground = Colorset[cset].hilite;
else
gcv.foreground = Scr.StdHilite;
if(Scr.StdReliefGC)
XChangeGC(dpy, Scr.StdReliefGC, gcm, &gcv);
else
Scr.StdReliefGC = fvwmlib_XCreateGC(dpy, Scr.NoFocusWin, gcm, &gcv);
if (cset >= 0)
gcv.foreground = Colorset[cset].shadow;
else
gcv.foreground = Scr.StdShadow;
if(Scr.StdShadowGC)
XChangeGC(dpy, Scr.StdShadowGC, gcm, &gcv);
else
Scr.StdShadowGC = fvwmlib_XCreateGC(dpy, Scr.NoFocusWin, gcm, &gcv);
/* update the geometry window for move/resize */
if(Scr.SizeWindow != None)
{
resize_geometry_window();
}
UpdateAllMenuStyles();
}
void CMD_Colorset(F_CMD_ARGS)
{
int n = -1, ret;
char *token;
token = PeekToken(action, NULL);
if (token == NULL)
return;
ret = sscanf(token, "%x", &n);
if ((ret == 0) || (n < 0))
return;
LoadColorset(action);
BroadcastColorset(n);
if (n == Scr.DefaultColorset)
{
Scr.flags.do_need_window_update = 1;
Scr.flags.has_default_color_changed = 1;
}
UpdateMenuColorset(n);
update_style_colorset(n);
}
void CMD_DefaultIcon(F_CMD_ARGS)
{
if (Scr.DefaultIcon)
free(Scr.DefaultIcon);
GetNextToken(action, &Scr.DefaultIcon);
}
void CMD_DefaultColorset(F_CMD_ARGS)
{
int cset;
if (GetIntegerArguments(action, NULL, &cset, 1) != 1)
return;
Scr.DefaultColorset = cset;
if (Scr.DefaultColorset < 0)
Scr.DefaultColorset = -1;
AllocColorset(Scr.DefaultColorset);
Scr.flags.do_need_window_update = 1;
Scr.flags.has_default_color_changed = 1;
}
void CMD_DefaultColors(F_CMD_ARGS)
{
char *fore = NULL;
char *back = NULL;
action = GetNextToken(action, &fore);
if (action)
action = GetNextToken(action, &back);
if (!back)
back = safestrdup(DEFAULT_BACK_COLOR);
if (!fore)
fore = safestrdup(DEFAULT_FORE_COLOR);
if (!StrEquals(fore, "-"))
{
XFreeColors(dpy, Pcmap, &Scr.StdFore, 1, 0);
Scr.StdFore = GetColor(fore);
}
if (!StrEquals(back, "-"))
{
XFreeColors(dpy, Pcmap, &Scr.StdBack, 3, 0);
Scr.StdBack = GetColor(back);
Scr.StdHilite = GetHilite(Scr.StdBack);
Scr.StdShadow = GetShadow(Scr.StdBack);
}
free(fore);
free(back);
Scr.DefaultColorset = -1;
Scr.flags.do_need_window_update = 1;
Scr.flags.has_default_color_changed = 1;
}
void CMD_DefaultFont(F_CMD_ARGS)
{
char *font;
FvwmFont new_font;
font = PeekToken(action, &action);
if (!font)
{
/* Try 'fixed', pass NULL font name */
}
if (!LoadFvwmFont(dpy, font, &new_font))
{
if (Scr.DefaultFont.font == NULL)
exit(1);
else
return;
}
FreeFvwmFont(dpy, &Scr.DefaultFont);
Scr.DefaultFont = new_font;
/* set flags to indicate that the font has changed */
Scr.flags.do_need_window_update = 1;
Scr.flags.has_default_font_changed = 1;
return;
}
void CMD_IconFont(F_CMD_ARGS)
{
char *newaction;
#ifdef USEDECOR
if (Scr.cur_decor && Scr.cur_decor != &Scr.DefaultDecor)
{
fvwm_msg(
ERR, "LoadIconFont",
"Decors do not support the IconFont command anymore."
" Please use 'Style <stylename> IconFont <fontname>' instead."
" Sorry for the inconvenience.");
return;
}
#endif
if (action)
{
newaction = safemalloc(strlen(action) + 16);
sprintf(newaction, "* IconFont %s", action);
action = newaction;
CMD_Style(F_PASS_ARGS);
free(newaction);
}
}
void CMD_WindowFont(F_CMD_ARGS)
{
char *newaction;
#ifdef USEDECOR
if (Scr.cur_decor && Scr.cur_decor != &Scr.DefaultDecor)
{
fvwm_msg(
ERR, "LoadWindowFont",
"Decors do not support the WindowFont command anymore."
" Please use 'Style <stylename> Font <fontname>' instead."
" Sorry for the inconvenience.");
return;
}
#endif
if (action)
{
newaction = safemalloc(strlen(action) + 16);
sprintf(newaction, "* Font %s", action);
action = newaction;
CMD_Style(F_PASS_ARGS);
free(newaction);
}
}
void FreeDecorFace(Display *dpy, DecorFace *df)
{
switch (DFS_FACE_TYPE(df->style))
{
case GradientButton:
/* - should we check visual is not TrueColor before doing this?
XFreeColors(dpy, PictureCMap,
df->u.grad.pixels, df->u.grad.npixels,
AllPlanes); */
free(df->u.grad.pixels);
break;
case PixmapButton:
case TiledPixmapButton:
if (df->u.p)
DestroyPicture(dpy, df->u.p);
break;
case VectorButton:
case DefaultVectorButton:
if (df->u.vector.vertices)
free (df->u.vector.vertices);
default:
/* see below */
break;
}
#ifdef MULTISTYLE
/* delete any compound styles */
if (df->next)
{
FreeDecorFace(dpy, df->next);
free(df->next);
}
df->next = NULL;
#endif
memset(&df->style, 0, sizeof(df->style));
memset(&df->u, 0, sizeof(df->u));
DFS_FACE_TYPE(df->style) = SimpleButton;
}
/*****************************************************************************
*
* Reads a button face line into a structure ([EMAIL PROTECTED])
*
****************************************************************************/
Bool ReadDecorFace(char *s, DecorFace *df, int button, int verbose)
{
int offset;
char style[256], *file;
char *action = s;
/* some variants of scanf do not increase the assign count when %n is used,
* so a return value of 1 is no error. */
if (sscanf(s, "%256s%n", style, &offset) < 1)
{
if (verbose)
{
fvwm_msg(ERR, "ReadDecorFace", "error in face `%s'", s);
}
return False;
}
style[255] = 0;
if (strncasecmp(style, "--", 2) != 0)
{
s += offset;
FreeDecorFace(dpy, df);
/* determine button style */
if (strncasecmp(style,"Simple",6)==0)
{
memset(&df->style, 0, sizeof(df->style));
DFS_FACE_TYPE(df->style) = SimpleButton;
}
else if (strncasecmp(style,"Default",7)==0)
{
int b = -1, n = sscanf(s, "%d%n", &b, &offset);
if (n < 1)
{
if (button == -1)
{
if (verbose)
{
fvwm_msg(ERR,"ReadDecorFace", "need default button number to load");
}
return False;
}
b = button;
}
else
{
b = BUTTON_INDEX(b);
s += offset;
}
if (b >= 0 && b < NUMBER_OF_BUTTONS)
LoadDefaultButton(df, b);
else
{
if (verbose)
{
fvwm_msg(ERR,"ReadDecorFace", "button number out of range: %d", b);
}
return False;
}
}
else if (strncasecmp(style,"Vector",6)==0 ||
(strlen(style)<=2 && isdigit(*style)))
{
/* normal coordinate list button style */
int i, num_coords, num, x, y, line_style;
struct vector_coords *vc = &df->u.vector;
/* get number of points */
if (strncasecmp(style,"Vector",6)==0)
{
num = sscanf(s,"%d%n",&num_coords,&offset);
s += offset;
} else
num = sscanf(style,"%d",&num_coords);
if((num != 1)||(num_coords>1000)||(num_coords<2))
{
if (verbose)
{
fvwm_msg(
ERR,"ReadDecorFace", "Bad button style (2) in line: %s",action);
}
return False;
}
vc->num = num_coords;
vc->use_fgbg = 0;
vc->vertices = (struct vector_vertex *)
safemalloc (sizeof(struct vector_vertex) * num_coords);
/* get the points */
for(i = 0; i < vc->num; ++i)
{
/* X x Y @ line_style */
num = sscanf(s,"[EMAIL PROTECTED]", &x, &y, &line_style, &offset);
vc->vertices[i].x = x;
vc->vertices[i].y = y;
vc->vertices[i].color = line_style;
if(num != 3)
{
if (verbose)
{
fvwm_msg(
ERR, "ReadDecorFace", "Bad button style (3) in line %s", action);
}
free(vc->vertices);
vc->vertices = NULL;
vc->num = 0;
return False;
}
if (line_style >= 2 && line_style < 4)
{
vc->use_fgbg = 1;
}
s += offset;
}
memset(&df->style, 0, sizeof(df->style));
DFS_FACE_TYPE(df->style) = VectorButton;
}
else if (strncasecmp(style,"Solid",5)==0)
{
s = GetNextToken(s, &file);
if (file)
{
memset(&df->style, 0, sizeof(df->style));
DFS_FACE_TYPE(df->style) = SolidButton;
df->u.back = GetColor(file);
free(file);
}
else
{
if (verbose)
{
fvwm_msg(
ERR, "ReadDecorFace", "no color given for Solid face type: %s",
action);
}
return False;
}
}
else if (strncasecmp(style+1, "Gradient", 8)==0)
{
char **s_colors;
int npixels, nsegs, *perc;
Pixel *pixels;
if (!IsGradientTypeSupported(style[0]))
return False;
/* translate the gradient string into an array of colors etc */
npixels = ParseGradient(s, &s, &s_colors, &perc, &nsegs);
while (*s && isspace(*s))
s++;
if (npixels <= 0)
return False;
/* grab the colors */
pixels = AllocAllGradientColors(s_colors, perc, nsegs, npixels);
if (pixels == None)
return False;
df->u.grad.pixels = pixels;
df->u.grad.npixels = npixels;
memset(&df->style, 0, sizeof(df->style));
DFS_FACE_TYPE(df->style) = GradientButton;
df->u.grad.gradient_type = toupper(style[0]);
}
else if (strncasecmp(style,"Pixmap",6)==0
|| strncasecmp(style,"TiledPixmap",11)==0)
{
s = GetNextToken(s, &file);
df->u.p = CachePicture(dpy, Scr.NoFocusWin, NULL,
file,Scr.ColorLimit);
if (df->u.p == NULL)
{
if (file)
{
if (verbose)
{
fvwm_msg(ERR,"ReadDecorFace", "couldn't load pixmap %s", file);
}
free(file);
}
return False;
}
if (file)
{
free(file);
file = NULL;
}
memset(&df->style, 0, sizeof(df->style));
if (strncasecmp(style,"Tiled",5)==0)
DFS_FACE_TYPE(df->style) = TiledPixmapButton;
else
DFS_FACE_TYPE(df->style) = PixmapButton;
}
#ifdef MINI_ICONS
else if (strncasecmp (style, "MiniIcon", 8) == 0)
{
memset(&df->style, 0, sizeof(df->style));
DFS_FACE_TYPE(df->style) = MiniIconButton;
#if 0
/* Have to remove this again. This is all so badly written there is no chance
* to prevent a coredump and a memory leak the same time without a rewrite of
* large parts of the code. */
if (df->u.p)
DestroyPicture(dpy, df->u.p);
#endif
df->u.p = NULL; /* pixmap read in when the window is created */
}
#endif
else
{
if (verbose)
{
fvwm_msg(ERR,"ReadDecorFace", "unknown style %s: %s", style, action);
}
return False;
}
}
/* Process button flags ("--" signals start of flags,
it is also checked for above) */
s = GetNextToken(s, &file);
if (file && (strcmp(file,"--")==0))
{
char *tok;
s = GetNextToken(s, &tok);
while (tok && *tok)
{
int set = 1;
char *old_tok = NULL;
if (*tok == '!')
{ /* flag negate */
set = 0;
old_tok = tok;
tok++;
}
if (StrEquals(tok,"Clear"))
{
memset(&DFS_FLAGS(df->style), (set) ? 0 : 0xff,
sizeof(DFS_FLAGS(df->style)));
/* ? what is set == 0 good for ? */
}
else if (StrEquals(tok,"Left"))
{
if (set)
DFS_H_JUSTIFICATION(df->style) = JUST_LEFT;
else
DFS_H_JUSTIFICATION(df->style) = JUST_RIGHT;
}
else if (StrEquals(tok,"Right"))
{
if (set)
DFS_H_JUSTIFICATION(df->style) = JUST_RIGHT;
else
DFS_H_JUSTIFICATION(df->style) = JUST_LEFT;
}
else if (StrEquals(tok,"Centered"))
{
DFS_H_JUSTIFICATION(df->style) = JUST_CENTER;
DFS_V_JUSTIFICATION(df->style) = JUST_CENTER;
}
else if (StrEquals(tok,"Top"))
{
if (set)
DFS_V_JUSTIFICATION(df->style) = JUST_TOP;
else
DFS_V_JUSTIFICATION(df->style) = JUST_BOTTOM;
}
else if (StrEquals(tok,"Bottom"))
{
if (set)
DFS_V_JUSTIFICATION(df->style) = JUST_BOTTOM;
else
DFS_V_JUSTIFICATION(df->style) = JUST_TOP;
}
else if (StrEquals(tok,"Flat"))
{
if (set)
DFS_BUTTON_RELIEF(df->style) = DFS_BUTTON_IS_FLAT;
else if (DFS_BUTTON_RELIEF(df->style) == DFS_BUTTON_IS_FLAT)
DFS_BUTTON_RELIEF(df->style) = DFS_BUTTON_IS_UP;
}
else if (StrEquals(tok,"Sunk"))
{
if (set)
DFS_BUTTON_RELIEF(df->style) = DFS_BUTTON_IS_SUNK;
else if (DFS_BUTTON_RELIEF(df->style) == DFS_BUTTON_IS_SUNK)
DFS_BUTTON_RELIEF(df->style) = DFS_BUTTON_IS_UP;
}
else if (StrEquals(tok,"Raised"))
{
if (set)
DFS_BUTTON_RELIEF(df->style) = DFS_BUTTON_IS_UP;
else
DFS_BUTTON_RELIEF(df->style) = DFS_BUTTON_IS_SUNK;
}
else if (StrEquals(tok,"UseTitleStyle"))
{
if (set)
{
DFS_USE_TITLE_STYLE(df->style) = 1;
DFS_USE_BORDER_STYLE(df->style) = 0;
}
else
DFS_USE_TITLE_STYLE(df->style) = 0;
}
else if (StrEquals(tok,"HiddenHandles"))
{
DFS_HAS_HIDDEN_HANDLES(df->style) = !!set;
}
else if (StrEquals(tok,"NoInset"))
{
DFS_HAS_NO_INSET(df->style) = !!set;
}
else if (StrEquals(tok,"UseBorderStyle"))
{
if (set)
{
DFS_USE_BORDER_STYLE(df->style) = 1;
DFS_USE_TITLE_STYLE(df->style) = 0;
}
else
DFS_USE_BORDER_STYLE(df->style) = 0;
}
else
if (verbose)
{
fvwm_msg(
ERR, "ReadDecorFace", "unknown button face flag '%s' -- line: %s",
tok, action);
}
if (set)
free(tok);
else
free(old_tok);
s = GetNextToken(s, &tok);
}
}
if (file)
free(file);
return True;
}
/*****************************************************************************
*
* Reads a title button description ([EMAIL PROTECTED])
*
****************************************************************************/
static char *ReadTitleButton(
char *s, TitleButton *tb, Boolean append, int button)
{
char *end = NULL;
char *spec;
char *t;
int i;
int bs;
int pstyle = 0;
DecorFace tmpdf;
Bool do_set_all = False;
s = SkipSpaces(s, NULL, 0);
t = GetNextTokenIndex(s, button_states, 0, &bs);
if (bs != BS_All)
{
s = SkipSpaces(t, NULL, 0);
}
else
{
do_set_all = True;
}
if (*s == '(')
{
int len;
pstyle = 1;
if (!(end = strchr(++s, ')')))
{
fvwm_msg(ERR, "ReadTitleButton", "missing parenthesis: %s", s);
return NULL;
}
s = SkipSpaces(s, NULL, 0);
len = end - s + 1;
spec = safemalloc(len);
strncpy(spec, s, len - 1);
spec[len - 1] = 0;
}
else
{
spec = s;
}
spec = SkipSpaces(spec, NULL, 0);
/* setup temporary in case button read fails */
memset(&tmpdf, 0, sizeof(DecorFace));
DFS_FACE_TYPE(tmpdf.style) = SimpleButton;
if (strncmp(spec, "--",2)==0)
{
/* only change flags */
if (do_set_all)
{
for (i = 0; i < BS_MaxButtonState; ++i)
{
ReadDecorFace(spec, &TB_STATE(*tb)[i], BS_All, !i);
}
}
else
{
ReadDecorFace(spec, &TB_STATE(*tb)[bs], button, True);
}
}
else if (ReadDecorFace(spec, &tmpdf, button, True))
{
int b = (do_set_all) ? 0 : bs;
#ifdef MULTISTYLE
if (append)
{
DecorFace *head = &TB_STATE(*tb)[b];
DecorFace *tail = head;
DecorFace *next;
while (tail->next)
{
tail = tail->next;
}
tail->next = (DecorFace *)safemalloc(sizeof(DecorFace));
memcpy(tail->next, &tmpdf, sizeof(DecorFace));
if (DFS_FACE_TYPE(tail->next->style) == VectorButton &&
DFS_FACE_TYPE((&TB_STATE(*tb)[b])->style) == DefaultVectorButton)
{
/* override the default vector style */
memcpy(&tail->next->style, &head->style, sizeof(DecorFaceStyle));
DFS_FACE_TYPE(tail->next->style) = VectorButton;
next = head->next;
head->next = NULL;
FreeDecorFace(dpy, head);
memcpy(head, next, sizeof(DecorFace));
free(next);
}
if (do_set_all)
{
for (i = 1; i < BS_MaxButtonState; ++i)
{
if (i == b)
{
/* already done above */
continue;
}
head = &TB_STATE(*tb)[i];
tail = head;
while (tail->next)
{
tail = tail->next;
}
tail->next = (DecorFace *)safemalloc(sizeof(DecorFace));
memset(&DFS_FLAGS(tail->next->style), 0,
sizeof(DFS_FLAGS(tail->next->style)));
DFS_FACE_TYPE(tail->next->style) = SimpleButton;
tail->next->next = NULL;
ReadDecorFace(spec, tail->next, button, False);
if (DFS_FACE_TYPE(tail->next->style) == VectorButton &&
DFS_FACE_TYPE((&TB_STATE(*tb)[i])->style) == DefaultVectorButton)
{
/* override the default vector style */
memcpy(&tail->next->style, &head->style, sizeof(DecorFaceStyle));
DFS_FACE_TYPE(tail->next->style) = VectorButton;
next = head->next;
head->next = NULL;
FreeDecorFace(dpy, head);
memcpy(head, next, sizeof(DecorFace));
free(next);
}
}
}
}
else
#endif
{
FreeDecorFace(dpy, &TB_STATE(*tb)[b]);
memcpy(&(TB_STATE(*tb)[b]), &tmpdf, sizeof(DecorFace));
if (do_set_all)
{
for (i = 1; i < BS_MaxButtonState; ++i)
{
ReadDecorFace(spec, &TB_STATE(*tb)[i], button, False);
}
}
}
}
if (pstyle)
{
free(spec);
end++;
end = SkipSpaces(end, NULL, 0);
}
return end;
}
#ifdef USEDECOR
/*****************************************************************************
*
* Diverts a style definition to an FvwmDecor structure ([EMAIL PROTECTED])
*
****************************************************************************/
void AddToDecor(FvwmDecor *decor, char *s)
{
if (!s)
return;
while (*s && isspace((unsigned char)*s))
++s;
if (!*s)
return;
Scr.cur_decor = decor;
old_execute_function(s, NULL, &Event, C_ROOT, -1, 0, NULL);
Scr.cur_decor = NULL;
}
/*****************************************************************************
*
* Changes the window's FvwmDecor pointer ([EMAIL PROTECTED])
*
****************************************************************************/
void CMD_ChangeDecor(F_CMD_ARGS)
{
char *item;
int old_height;
int extra_height;
FvwmDecor *decor = &Scr.DefaultDecor;
FvwmDecor *found = NULL;
if (DeferExecution(eventp,&w,&tmp_win,&context, CRS_SELECT,ButtonRelease))
return;
item = PeekToken(action, &action);
if (!action || !item)
return;
/* search for tag */
for (; decor; decor = decor->next)
{
if (decor->tag)
{
if (StrEquals(item, decor->tag))
{
found = decor;
break;
}
}
}
if (!found)
{
XBell(dpy, 0);
return;
}
SET_DECOR_CHANGED(tmp_win, 1);
old_height = (tmp_win->decor) ? tmp_win->decor->title_height : 0;
tmp_win->decor = found;
apply_decor_change(tmp_win);
extra_height = (HAS_TITLE(tmp_win) && old_height) ?
(old_height - tmp_win->decor->title_height) : 0;
ForceSetupFrame(
tmp_win, tmp_win->frame_g.x, tmp_win->frame_g.y, tmp_win->frame_g.width,
tmp_win->frame_g.height - extra_height, True);
DrawDecorations(tmp_win, DRAW_ALL, (Scr.Hilite == tmp_win), 2, None);
}
/*****************************************************************************
*
* Destroys an FvwmDecor ([EMAIL PROTECTED])
*
****************************************************************************/
void CMD_DestroyDecor(F_CMD_ARGS)
{
char *item;
FvwmDecor *decor = Scr.DefaultDecor.next;
FvwmDecor *prev = &Scr.DefaultDecor, *found = NULL;
Bool do_recreate = False;
item = PeekToken(action, &action);
if (!item)
{
return;
}
if (StrEquals(item, "recreate"))
{
do_recreate = True;
item = PeekToken(action, NULL);
}
if (!item)
{
return;
}
/* search for tag */
for (; decor; decor = decor->next)
{
if (decor->tag)
{
if (StrEquals(item, decor->tag))
{
found = decor;
break;
}
}
prev = decor;
}
if (found && (found != &Scr.DefaultDecor || do_recreate))
{
FvwmWindow *fw;
if (!do_recreate)
{
for (fw = Scr.FvwmRoot.next; fw; fw = fw->next)
{
if (fw->decor == found)
{
/* remove the extra title height now because we delete the current
* decor before calling ChangeDecor(). */
fw->frame_g.height -= fw->decor->title_height;
fw->decor = NULL;
old_execute_function(
"ChangeDecor Default", fw, eventp, C_WINDOW, *Module, 0, NULL);
}
}
}
DestroyFvwmDecor(found);
if (do_recreate)
{
int i;
InitFvwmDecor(found);
found->tag = safestrdup(item);
Scr.flags.do_need_window_update = 1;
found->flags.has_changed = 1;
found->flags.has_title_height_changed = 0;
found->titlebar.flags.has_changed = 1;
for (i = 0; i < NUMBER_OF_BUTTONS; ++i)
{
TB_FLAGS(found->buttons[i]).has_changed = 1;
}
}
else
{
prev->next = found->next;
free(found);
}
}
}
/***********************************************************************
*
* InitFvwmDecor -- initializes an FvwmDecor structure to defaults
*
************************************************************************/
void InitFvwmDecor(FvwmDecor *decor)
{
int i;
DecorFace tmpdf;
/* zero out the structures */
memset(decor, 0, sizeof (FvwmDecor));
memset(&tmpdf, 0, sizeof(DecorFace));
/* initialize title-bar button styles */
DFS_FACE_TYPE(tmpdf.style) = SimpleButton;
for (i = 0; i < NUMBER_OF_BUTTONS; ++i)
{
int j = 0;
for (; j < BS_MaxButtonState; ++j)
{
TB_STATE(decor->buttons[i])[j] = tmpdf;
}
}
/* reset to default button set */
ResetAllButtons(decor);
/* initialize title-bar styles */
for (i = 0; i < BS_MaxButtonState; ++i)
{
DFS_FACE_TYPE(TB_STATE(decor->titlebar)[i].style) = SimpleButton;
}
/* initialize border texture styles */
DFS_FACE_TYPE(decor->BorderStyle.active.style) = SimpleButton;
DFS_FACE_TYPE(decor->BorderStyle.inactive.style) = SimpleButton;
return;
}
/***********************************************************************
*
* DestroyFvwmDecor -- frees all memory assocated with an FvwmDecor
* structure, but does not free the FvwmDecor itself
*
************************************************************************/
static void DestroyFvwmDecor(FvwmDecor *decor)
{
int i;
/* reset to default button set (frees allocated mem) */
DestroyAllButtons(decor);
for (i = 0; i < BS_MaxButtonState; ++i)
{
FreeDecorFace(dpy, &TB_STATE(decor->titlebar)[i]);
}
FreeDecorFace(dpy, &decor->BorderStyle.active);
FreeDecorFace(dpy, &decor->BorderStyle.inactive);
#ifdef USEDECOR
if (decor->tag)
{
free(decor->tag);
decor->tag = NULL;
}
#endif
}
/*****************************************************************************
*
* Initiates an AddToDecor ([EMAIL PROTECTED])
*
****************************************************************************/
void CMD_AddToDecor(F_CMD_ARGS)
{
FvwmDecor *decor, *found = NULL;
char *item = NULL, *s = action;
s = GetNextToken(s, &item);
if (!item)
return;
if (!s)
{
free(item);
return;
}
/* search for tag */
for (decor = &Scr.DefaultDecor; decor; decor = decor->next)
{
if (decor->tag)
{
if (StrEquals(item, decor->tag))
{
found = decor;
break;
}
}
}
if (!found)
{ /* then make a new one */
found = (FvwmDecor *)safemalloc(sizeof( FvwmDecor ));
InitFvwmDecor(found);
found->tag = item; /* tag it */
/* add it to list */
for (decor = &Scr.DefaultDecor; decor->next; decor = decor->next)
{
/* nop */
}
decor->next = found;
}
else
{
free(item);
}
if (found)
{
AddToDecor(found, s);
/* Set + state to last decor */
set_last_added_item(ADDED_DECOR, found);
}
}
#endif /* USEDECOR */
/*****************************************************************************
*
* Updates window decoration styles ([EMAIL PROTECTED])
*
****************************************************************************/
void CMD_UpdateDecor(F_CMD_ARGS)
{
FvwmWindow *fw;
#ifdef USEDECOR
FvwmDecor *decor, *found = NULL;
FvwmWindow *hilight = Scr.Hilite;
char *item = NULL;
action = GetNextToken(action, &item);
if (item)
{
/* search for tag */
for (decor = &Scr.DefaultDecor; decor; decor = decor->next)
{
if (decor->tag)
{
if (strcasecmp(item, decor->tag)==0)
{
found = decor;
break;
}
}
}
free(item);
}
#endif
for (fw = Scr.FvwmRoot.next; fw; fw = fw->next)
{
#ifdef USEDECOR
/* update specific decor, or all */
if (found)
{
if (fw->decor == found)
{
DrawDecorations(fw, DRAW_ALL, True, True, None);
DrawDecorations(fw, DRAW_ALL, False, True, None);
}
}
else
#endif
{
DrawDecorations(fw, DRAW_ALL, True, True, None);
DrawDecorations(fw, DRAW_ALL, False, True, None);
}
}
DrawDecorations(hilight, DRAW_ALL, True, True, None);
}
void reset_decor_changes(void)
{
#ifndef USEDECOR
Scr.DefaultDecor.flags.has_changed = 0;
Scr.DefaultDecor.flags.has_title_height_changed = 0;
#else
FvwmDecor *decor;
for (decor = &Scr.DefaultDecor; decor; decor = decor->next)
{
decor->flags.has_changed = 0;
decor->flags.has_title_height_changed = 0;
}
/* todo: must reset individual change flags too */
#endif
}
/*****************************************************************************
*
* Changes a button decoration style (changes by [EMAIL PROTECTED])
*
****************************************************************************/
static void SetMWMButtonFlag(
mwm_flags flag, int multi, int set, FvwmDecor *decor, TitleButton *tb)
{
int i;
int start = 0;
int add = 2;
if (multi)
{
if (multi == 2)
start = 1;
else if (multi == 3)
add = 1;
for (i = start; i < NUMBER_OF_BUTTONS; i += add)
{
if (set)
TB_MWM_DECOR_FLAGS(decor->buttons[i]) |= flag;
else
TB_MWM_DECOR_FLAGS(decor->buttons[i]) &= ~flag;
}
}
else
{
if (set)
TB_MWM_DECOR_FLAGS(*tb) |= flag;
else
TB_MWM_DECOR_FLAGS(*tb) &= ~flag;
}
return;
}
static void do_button_style(F_CMD_ARGS, Bool do_add)
{
int i;
int multi = 0;
int button = 0;
char *text = NULL;
char *prev = NULL;
char *parm = NULL;
TitleButton *tb = NULL;
#ifdef USEDECOR
FvwmDecor *decor = Scr.cur_decor ? Scr.cur_decor : &Scr.DefaultDecor;
#else
FvwmDecor *decor = &Scr.DefaultDecor;
#endif
parm = PeekToken(action, &text);
if (parm && isdigit(*parm))
{
button = atoi(parm);
button = BUTTON_INDEX(button);
}
if (parm == NULL || button >= NUMBER_OF_BUTTONS || button < 0)
{
fvwm_msg(ERR,"ButtonStyle","Bad button style (1) in line %s",action);
return;
}
Scr.flags.do_need_window_update = 1;
if (!isdigit(*parm))
{
if (StrEquals(parm,"left"))
multi = 1; /* affect all left buttons */
else if (StrEquals(parm,"right"))
multi = 2; /* affect all right buttons */
else if (StrEquals(parm,"all"))
multi = 3; /* affect all buttons */
else
{
/* we're either resetting buttons or an invalid button set was specified
*/
if (StrEquals(parm,"reset"))
ResetAllButtons(decor);
else
fvwm_msg(ERR,"ButtonStyle","Bad button style (2) in line %s", action);
return;
}
}
/* mark button style and decor as changed */
decor->flags.has_changed = 1;
if (multi == 0)
{
/* a single button was specified */
tb = &decor->buttons[button];
TB_FLAGS(*tb).has_changed = 1;
}
else
{
for (i = 0; i < NUMBER_OF_BUTTONS; ++i)
{
if (((multi & 1) && !(i & 1)) || ((multi & 2) && (i & 1)))
{
TB_FLAGS(decor->buttons[i]).has_changed = 1;
}
}
}
for (prev = text; (parm = PeekToken(text, &text)); prev = text)
{
if (!do_add && strcmp(parm,"-") == 0)
{
char *tok;
text = GetNextToken(text, &tok);
while (tok)
{
int set = 1;
char *old_tok = NULL;
if (*tok == '!')
{
/* flag negate */
set = 0;
old_tok = tok;
tok++;
}
if (StrEquals(tok,"Clear"))
{
if (multi)
{
for (i = 0; i < NUMBER_OF_BUTTONS; ++i)
{
if (((multi & 1) && !(i & 1)) ||
((multi & 2) && (i & 1)))
{
TB_JUSTIFICATION(decor->buttons[i]) =
(set) ? JUST_CENTER : JUST_RIGHT;
memset(&TB_FLAGS(decor->buttons[i]), (set) ? 0 : 0xff,
sizeof(TB_FLAGS(decor->buttons[i])));
/* ? not very useful if set == 0 ? */
}
}
}
else
{
TB_JUSTIFICATION(*tb) = (set) ? JUST_CENTER : JUST_RIGHT;
memset(&TB_FLAGS(*tb), (set) ? 0 : 0xff, sizeof(TB_FLAGS(*tb)));
/* ? not very useful if set == 0 ? */
}
}
else if (StrEquals(tok, "MWMDecorMenu"))
{
SetMWMButtonFlag(MWM_DECOR_MENU, multi, set, decor, tb);
}
else if (StrEquals(tok, "MWMDecorMin"))
{
SetMWMButtonFlag(MWM_DECOR_MINIMIZE, multi, set, decor, tb);
}
else if (StrEquals(tok, "MWMDecorMax"))
{
SetMWMButtonFlag(MWM_DECOR_MAXIMIZE, multi, set, decor, tb);
}
else if (StrEquals(tok, "MWMDecorShade"))
{
SetMWMButtonFlag(MWM_DECOR_SHADE, multi, set, decor, tb);
}
else if (StrEquals(tok, "MWMDecorStick"))
{
SetMWMButtonFlag(MWM_DECOR_STICK, multi, set, decor, tb);
}
else
{
fvwm_msg(ERR, "ButtonStyle",
"unknown title button flag %s -- line: %s",
tok, text);
}
if (set)
free(tok);
else
free(old_tok);
text = GetNextToken(text, &tok);
}
break;
}
else
{
if (multi)
{
for (i = 0; i < NUMBER_OF_BUTTONS; ++i)
{
if (((multi & 1) && !(i & 1)) ||
((multi & 2) && (i & 1)))
{
text = ReadTitleButton(prev, &decor->buttons[i], do_add, i);
}
}
}
else if (!(text = ReadTitleButton(prev, tb, do_add, button)))
{
break;
}
}
}
}
void CMD_ButtonStyle(F_CMD_ARGS)
{
do_button_style(F_PASS_ARGS, False);
}
#ifdef MULTISTYLE
/*****************************************************************************
*
* Appends a button decoration style ([EMAIL PROTECTED])
*
****************************************************************************/
void CMD_AddButtonStyle(F_CMD_ARGS)
{
do_button_style(F_PASS_ARGS, True);
}
#endif /* MULTISTYLE */
/* add_to_env_list
* This function keeps a list of all strings that were set in the environment
* via SetEnv or UnsetEnv. If a variable is written again, the old memory is
* freed. */
typedef struct
{
char *var;
char *env;
} env_list_item;
#define ENV_LIST_INC 10
static void add_to_env_list(char *var, char *env)
{
static env_list_item *env_list = NULL;
static unsigned int env_len = 0;
unsigned int i;
/* find string in list */
if (env_list && env_len)
{
for (i = 0; i < env_len; i++)
{
if (strcmp(var, env_list[i].var) == 0)
{
/* found it - replace old string */
free(env_list[i].var);
free(env_list[i].env);
env_list[i].var = var;
env_list[i].env = env;
return;
}
}
/* not found, add to list */
if (env_len % ENV_LIST_INC == 0)
{
/* need more memory */
env_list = (env_list_item *)saferealloc(
(void *)env_list, (env_len + ENV_LIST_INC) * sizeof(env_list_item));
}
}
else if (env_list == NULL)
{
/* list is still empty */
env_list = (env_list_item *)safecalloc(sizeof(env_list_item), ENV_LIST_INC);
}
env_list[env_len].var = var;
env_list[env_len].env = env;
env_len++;
return;
}
void CMD_SetEnv(F_CMD_ARGS)
{
char *szVar = NULL;
char *szValue = NULL;
char *szPutenv = NULL;
action = GetNextToken(action,&szVar);
if (!szVar)
return;
action = GetNextToken(action,&szValue);
if (!szValue)
{
/* no value, treat as unset */
CMD_UnsetEnv(F_PASS_ARGS);
free(szVar);
return;
}
szPutenv = safemalloc(strlen(szVar)+strlen(szValue)+2);
sprintf(szPutenv,"%s=%s",szVar,szValue);
putenv(szPutenv);
add_to_env_list(szVar, szPutenv);
/* szVar is stored in the env list. do not free it */
free(szValue);
}
void CMD_UnsetEnv(F_CMD_ARGS)
{
char *szVar = NULL;
char *szPutenv = NULL;
action = GetNextToken(action,&szVar);
if (!szVar)
return;
szPutenv = (char *)safemalloc(strlen(szVar) + 2);
sprintf(szPutenv, "%s=", szVar);
putenv(szPutenv);
add_to_env_list(szVar, szPutenv);
/* szVar is stored in the env list. do not free it */
/*free(szVar);*/
}
void CMD_GlobalOpts(F_CMD_ARGS)
{
char *opt;
char *replace;
char buf[64];
int i;
Bool is_bugopt;
char *optlist[] = {
"WindowShadeShrinks",
"WindowShadeScrolls",
"SmartPlacementIsReallySmart",
"SmartPlacementIsNormal",
"ClickToFocusDoesntPassClick",
"ClickToFocusPassesClick",
"ClickToFocusDoesntRaise",
"ClickToFocusRaises",
"MouseFocusClickDoesntRaise",
"MouseFocusClickRaises",
"NoStipledTitles",
"StipledTitles",
"CaptureHonorsStartsOnPage",
"CaptureIgnoresStartsOnPage",
"RecaptureHonorsStartsOnPage",
"RecaptureIgnoresStartsOnPage",
"ActivePlacementHonorsStartsOnPage",
"ActivePlacementIgnoresStartsOnPage",
"RaiseOverNativeWindows",
"IgnoreNativeWindows",
NULL
};
char *replacelist[] = {
/* These options are mapped to the Style * command */
NULL, /* NULL means to use "Style * <optionname>" */
NULL,
"* MinOverlapPlacement",
"* TileCascadePlacement",
"* ClickToFocusPassesClickOff",
"* ClickToFocusPassesClick",
"* ClickToFocusRaisesOff",
"* ClickToFocusRaises",
"* MouseFocusClickRaisesOff",
"* MouseFocusClickRaises",
"* StippledTitleOff",
"* StippledTitle",
NULL,
NULL,
NULL,
NULL,
"* ManualPlacementHonorsStartsOnPage",
"* ManualPlacementIgnoresStartsOnPage",
/* These options are mapped to the BugOpts command */
"RaiseOverNativeWindows on",
"RaiseOverNativeWindows off"
};
fvwm_msg(ERR, "SetGlobalOptions", "The GlobalOpts command is obsolete.");
for (action = GetNextSimpleOption(action, &opt); opt;
action = GetNextSimpleOption(action, &opt))
{
replace = NULL;
is_bugopt = False;
i = GetTokenIndex(opt, optlist, 0, NULL);
if (i > -1)
{
char *cmd;
char *tmp;
replace = replacelist[i];
if (replace == NULL)
{
replace = &(buf[0]);
sprintf(buf, "* %s", opt);
}
else if (*replace != '*')
{
is_bugopt = True;
}
tmp = action;
action = replace;
if (!is_bugopt)
{
CMD_Style(F_PASS_ARGS);
cmd = "Style";
}
else
{
CMD_BugOpts(F_PASS_ARGS);
cmd = "BugOpts";
}
action = tmp;
fvwm_msg(
ERR, "SetGlobalOptions",
"Please replace 'GlobalOpts %s' with '%s %s'.", opt, cmd, replace);
}
else
{
fvwm_msg(ERR, "SetGlobalOptions", "Unknown Global Option '%s'", opt);
}
/* should never be null, but checking anyways... */
if (opt)
free(opt);
}
if (opt)
free(opt);
}
void CMD_BugOpts(F_CMD_ARGS)
{
char *opt;
char delim;
int toggle;
/* fvwm_msg(DBG,"SetGlobalOptions","init action == '%s'\n",action); */
while (action)
{
action = DoGetNextToken(action, &opt, NULL, ",", &delim);
if (!opt)
{
/* no more options */
return;
}
if (delim == '\n' || delim == ',')
{
/* missing toggle argument */
toggle = 2;
}
else
{
toggle = ParseToggleArgument(action, &action, 1, False);
}
if (StrEquals(opt, "FlickeringMoveWorkaround"))
{
switch (toggle)
{
case -1:
Scr.bo.DisableConfigureNotify ^= 1;
break;
case 0:
case 1:
Scr.bo.DisableConfigureNotify = toggle;
break;
default:
Scr.bo.DisableConfigureNotify = 0;
break;
}
}
else if (StrEquals(opt, "MixedVisualWorkaround"))
{
switch (toggle)
{
case -1:
Scr.bo.InstallRootCmap ^= 1;
break;
case 0:
case 1:
Scr.bo.InstallRootCmap = toggle;
break;
default:
Scr.bo.InstallRootCmap = 0;
break;
}
}
else if (StrEquals(opt, "ModalityIsEvil"))
{
switch (toggle)
{
case -1:
Scr.bo.ModalityIsEvil ^= 1;
break;
case 0:
case 1:
Scr.bo.ModalityIsEvil = toggle;
break;
default:
Scr.bo.ModalityIsEvil = 0;
break;
}
if (Scr.bo.ModalityIsEvil)
{
SetMWM_INFO(Scr.NoFocusWin);
}
}
else if (StrEquals(opt, "RaiseOverNativeWindows"))
{
switch (toggle)
{
case -1:
Scr.bo.RaiseHackNeeded ^= 1;
break;
case 0:
case 1:
Scr.bo.RaiseHackNeeded = toggle;
break;
default:
Scr.bo.RaiseHackNeeded = 0;
break;
}
}
else if (StrEquals(opt, "RaiseOverUnmanaged"))
{
switch (toggle)
{
case -1:
Scr.bo.RaiseOverUnmanaged ^= 1;
break;
case 0:
case 1:
Scr.bo.RaiseOverUnmanaged = toggle;
break;
default:
Scr.bo.RaiseOverUnmanaged = 0;
break;
}
}
else if (StrEquals(opt, "FlickeringQtDialogsWorkaround"))
{
switch (toggle)
{
case -1:
Scr.bo.FlickeringQtDialogsWorkaround ^= 1;
break;
case 0:
case 1:
Scr.bo.FlickeringQtDialogsWorkaround = toggle;
break;
default:
Scr.bo.FlickeringQtDialogsWorkaround = 0;
break;
}
}
else
{
fvwm_msg(ERR, "SetBugOptions", "Unknown Bug Option '%s'", opt);
}
free(opt);
}
}
void CMD_Emulate(F_CMD_ARGS)
{
char *style;
style = PeekToken(action, NULL);
if (!style || StrEquals(style, "fvwm"))
{
Scr.gs.EmulateMWM = False;
Scr.gs.EmulateWIN = False;
}
else if (StrEquals(style, "mwm"))
{
Scr.gs.EmulateMWM = True;
Scr.gs.EmulateWIN = False;
}
else if (StrEquals(style, "win"))
{
Scr.gs.EmulateMWM = False;
Scr.gs.EmulateWIN = True;
}
else
{
fvwm_msg(ERR, "Emulate", "Unknown style '%s'", style);
return;
}
Scr.flags.do_need_window_update = 1;
Scr.flags.has_default_font_changed = 1;
Scr.flags.has_default_color_changed = 1;
return;
}
/*
* The ColorLimit command is ignored if the user has no reason to limit
* color. This is so the same configuration will work on colorlimited
* and non-colorlimited displays without resorting to using a
* preprocessor.
*
* Lets assume the display is no more than 2000x1000 pixels. Ie. the
* display can display no more than 2,000,000 million pixels at once. A
* display depth of 21 will display 2 million colors at once. Hence the
* logic below.
*
* dje 03/22/99
*/
/* It is also ignored if the colormap is static i.e you can't run out */
void CMD_ColorLimit(F_CMD_ARGS)
{
int val;
/* from X.h:
* Note that the statically allocated ones are even numbered and the
* dynamically changeable ones are odd numbered */
if (!(Pvisual->class & 1))
return;
if (Pdepth > 20) { /* if more than 20 bit color */
return; /* ignore the limit */
}
if (GetIntegerArguments(action, NULL, &val, 1) != 1)
{
fvwm_msg(ERR,"SetColorLimit","ColorLimit requires one argument");
return;
}
Scr.ColorLimit = (long)val;
}
/* set animation parameters */
void CMD_SetAnimation(F_CMD_ARGS)
{
char *opt;
int delay;
float pct;
int i = 0;
action = GetNextToken(action, &opt);
if (!opt || sscanf(opt,"%d",&delay) != 1)
{
fvwm_msg(ERR,"SetAnimation",
"Improper milli-second delay as first argument");
if (opt)
free(opt);
return;
}
free(opt);
if (delay > 500)
{
fvwm_msg(WARN,"SetAnimation",
"Using longer than .5 seconds as between frame animation delay");
}
cmsDelayDefault = delay;
for (action = GetNextToken(action, &opt); opt;
free(opt), action = GetNextToken(action, &opt))
{
if (sscanf(opt,"%f",&pct) != 1)
{
fvwm_msg(ERR,"SetAnimation",
"Use fractional values ending in 1.0 as args 2 and on");
free(opt);
return;
}
rgpctMovementDefault[i++] = pct;
}
/* No pct entries means don't change them at all */
if (i>0 && rgpctMovementDefault[i-1] != 1.0)
{
rgpctMovementDefault[i++] = 1.0;
}
}
/* set the number or size of shade animation steps, N => steps, Np => pixels */
void CMD_WindowShadeAnimate(F_CMD_ARGS)
{
char *buf;
if (!action)
action = "";
fvwm_msg(
ERR, "setShadeAnim", "The WindowshadeAnimate command is obsolete. "
"Please use 'Style * WindowShadeSteps %s' instead.", action);
buf = safemalloc(strlen(action) + 32);
sprintf(buf, "* WindowShadeSteps %s", action);
action = buf;
CMD_Style(F_PASS_ARGS);
free(buf);
return;
}
void CMD_FakeClick(F_CMD_ARGS)
{
char *token;
char *optlist[] =
{
"press", "p",
"release", "r",
"wait", "w",
"modifiers", "m",
"depth", "d",
NULL
};
unsigned int mask = 0;
Window root = Scr.Root;
int maxdepth = 0;
/* get the mask of pressed/released buttons */
XQueryPointer(
dpy, Scr.Root, &root, &JunkRoot, &JunkX, &JunkY, &JunkX, &JunkY, &mask);
token = PeekToken(action, &action);
while (token && action)
{
int index = GetTokenIndex(token, optlist, 0, NULL);
int val;
XEvent e;
Window w;
Window child_w;
int x = 0;
int y = 0;
int rx = 0;
int ry = 0;
Bool do_unset;
long add_mask = 0;
XSync(dpy, 0);
if (GetIntegerArguments(action, &action, &val, 1) != 1)
{
/* error */
return;
}
do_unset = True;
switch (index)
{
case 0:
case 1:
do_unset = False;
/* fall through */
case 2:
case 3:
/* button press or release */
if (val >= 1 && val <= NUMBER_OF_MOUSE_BUTTONS)
{
int depth = 1;
w = None;
child_w = root;
for (depth = 1; depth != maxdepth && w != child_w && child_w != None;
depth++)
{
w = child_w;
XQueryPointer(dpy, w, &root, &child_w, &rx, &ry, &x, &y, &JunkMask);
}
if (do_unset)
{
e.type = ButtonRelease;
add_mask = ButtonPressMask;
}
else
{
e.type = ButtonPress;
add_mask = ButtonPressMask | ButtonReleaseMask;
}
e.xbutton.display = dpy;
e.xbutton.window = w;
e.xbutton.subwindow = None;
e.xbutton.root = root;
e.xbutton.time = lastTimestamp;
e.xbutton.x = x;
e.xbutton.y = y;
e.xbutton.x_root = rx;
e.xbutton.y_root = ry;
e.xbutton.button = val;
e.xbutton.state = mask;
e.xbutton.same_screen = (Scr.Root == root);
XSendEvent(
dpy, PointerWindow, True, SubstructureNotifyMask | add_mask, &e);
XSync(dpy, 0);
if (do_unset)
mask &= ~(Button1Mask << (val - 1));
else
mask |= (Button1Mask << (val - 1));
}
else
{
/* error */
return;
}
break;
case 4:
case 5:
/* wait */
if (val > 0 && val <= 1000000)
{
usleep(1000 * val);
XQueryPointer(
dpy, Scr.Root, &root, &JunkRoot, &JunkX, &JunkY, &JunkX, &JunkY,
&mask);
}
else
{
/* error */
return;
}
break;
case 6:
case 7:
/* set modifier */
do_unset = False;
if (val < 0)
{
do_unset = True;
val = -val;
}
if (val == 6)
val = ShiftMask;
else if (val == 7)
val = LockMask;
else if (val == 8)
val = ControlMask;
else if (val >=1 && val <= 5)
val = (Mod1Mask << (val - 1));
else
/* error */
return;
if (do_unset)
mask &= ~val;
else
mask |= val;
break;
case 8:
case 9:
/* new max depth */
maxdepth = val;
break;
default:
/* error */
return;
}
if (action)
token = PeekToken(action, &action);
}
}
/* A function to handle stroke (olicha Nov 11, 1999) */
#ifdef HAVE_STROKE
void CMD_StrokeFunc(F_CMD_ARGS)
{
int finished = 0;
int abort = 0;
int modifiers = eventp->xbutton.state;
int start_event_type = eventp->type;
char sequence[STROKE_MAX_SEQUENCE + 1];
char *stroke_action;
char *opt = NULL;
Bool finish_on_release = True;
KeySym keysym;
Bool restor_repeat = False;
Bool echo_sequence = False;
Bool draw_motion = False;
int i = 0;
int* x = (int*)0;
int* y = (int*)0;
const int STROKE_CHUNK_SIZE = 0xff;
int coords_size = STROKE_CHUNK_SIZE;
Window JunkRoot, JunkChild;
int JunkX, JunkY;
int tmpx, tmpy;
unsigned int JunkMask;
Bool feed_back = False;
int stroke_width = 1;
x = (int*)safemalloc(coords_size * sizeof(int));
y = (int*)safemalloc(coords_size * sizeof(int));
if(!GrabEm(CRS_STROKE, GRAB_NORMAL))
{
XBell(dpy, 0);
return;
}
/* set the default option */
if (eventp->type == KeyPress || eventp->type == ButtonPress)
finish_on_release = True;
else
finish_on_release = False;
/* parse the option */
for (action = GetNextSimpleOption(action, &opt); opt;
action = GetNextSimpleOption(action, &opt))
{
if (StrEquals("NotStayPressed",opt))
finish_on_release = False;
else if (StrEquals("EchoSequence",opt))
echo_sequence = True;
else if (StrEquals("DrawMotion",opt))
draw_motion = True;
else if (StrEquals("FeedBack",opt))
feed_back = True;
else if (StrEquals("StrokeWidth",opt))
{
/* stroke width takes a positive integer argument */
if (opt)
free(opt);
action = GetNextToken(action, &opt);
if (!opt)
fvwm_msg(WARN,"StrokeWidth","needs an integer argument");
/* we allow stroke_width == 0 which means drawing a `fast' line
* of width 1; the upper level of 100 is arbitrary */
else if (!sscanf(opt, "%d", &stroke_width) || stroke_width < 0 ||
stroke_width > 100)
{
fvwm_msg(WARN,"StrokeWidth","Bad integer argument %d", stroke_width);
stroke_width = 1;
}
}
else
fvwm_msg(WARN,"StrokeFunc","Unknown option %s", opt);
if (opt)
free(opt);
}
if (opt)
free(opt);
/* Force auto repeat off and grab the Keyboard to get proper
* KeyRelease events if we need it.
* Some computers do not support keyRelease events, can we
* check this here ? No ? */
if (start_event_type == KeyPress && finish_on_release)
{
XKeyboardState kstate;
XGetKeyboardControl(dpy, &kstate);
if (kstate.global_auto_repeat == AutoRepeatModeOn)
{
XAutoRepeatOff(dpy);
restor_repeat = True;
}
MyXGrabKeyboard(dpy);
}
/* be ready to get a stroke sequence */
stroke_init();
if (draw_motion)
{
MyXGrabServer(dpy);
XQueryPointer(dpy, Scr.Root, &JunkRoot, &JunkChild, &x[0], &y[0],
&JunkX, &JunkY, &JunkMask);
XSetLineAttributes(dpy,Scr.XorGC,stroke_width,LineSolid,CapButt,JoinMiter);
}
while (!finished && !abort)
{
/* block until there is an event */
XMaskEvent(dpy, ButtonPressMask | ButtonReleaseMask | KeyPressMask |
KeyReleaseMask | ButtonMotionMask | PointerMotionMask, eventp);
/* Records the time */
StashEventTime(eventp);
switch (eventp->type)
{
case MotionNotify:
if (eventp->xany.window != Scr.Root)
{
XQueryPointer(dpy, Scr.Root, &JunkRoot, &JunkChild, &tmpx, &tmpy,
&JunkX, &JunkY, &JunkMask);
}
else
{
tmpx = eventp->xmotion.x;
tmpy = eventp->xmotion.y;
}
stroke_record(tmpx,tmpy);
if (draw_motion)
{
if ((x[i] != tmpx || y[i] != tmpy))
{
i++;
if (i >= coords_size) {
coords_size += STROKE_CHUNK_SIZE;
x = (int*)saferealloc((void *)x, coords_size * sizeof(int));
y = (int*)saferealloc((void *)y, coords_size * sizeof(int));
if (!x || !y)
{
/* unlikely */
fvwm_msg(WARN,
"strokeFunc","unable to allocate %d bytes ... aborted.",
coords_size * sizeof(int));
abort = 1;
i = -1; /* no undrawing possible since either x or y == 0 */
break;
}
}
x[i] = tmpx;
y[i] = tmpy;
XDrawLine(dpy, Scr.Root, Scr.XorGC, x[i-1], y[i-1], x[i], y[i]);
}
}
break;
case ButtonRelease:
if (finish_on_release && start_event_type == ButtonPress)
finished = 1;
break;
case KeyRelease:
if (finish_on_release && start_event_type == KeyPress)
finished = 1;
break;
case KeyPress:
keysym = XLookupKeysym(&eventp->xkey,0);
/* abort if Escape or Delete is pressed (as in menus.c) */
if (keysym == XK_Escape || keysym == XK_Delete ||
keysym == XK_KP_Separator)
abort = 1;
/* finish on enter or space (as in menus.c) */
if (keysym == XK_Return || keysym == XK_KP_Enter ||
keysym == XK_space)
finished = 1;
break;
case ButtonPress:
if (!finish_on_release)
finished = 1;
break;
default:
break;
}
}
if (draw_motion)
{
while (i > 0)
{
XDrawLine(dpy, Scr.Root, Scr.XorGC, x[i-1], y[i-1], x[i], y[i]);
i--;
}
XSetLineAttributes(dpy,Scr.XorGC,0,LineSolid,CapButt,JoinMiter);
MyXUngrabServer(dpy);
free(x);
free(y);
}
if (start_event_type == KeyPress && finish_on_release)
MyXUngrabKeyboard(dpy);
UngrabEm(GRAB_NORMAL);
if (restor_repeat)
XAutoRepeatOn(dpy);
/* get the stroke sequence */
stroke_trans(sequence);
if (echo_sequence)
{
char num_seq[STROKE_MAX_SEQUENCE + 1];
for(i=0;sequence[i] != '\0';i++)
{
/* Telephone to numeric pad */
if ('7' <= sequence[i] && sequence[i] <= '9')
num_seq[i] = sequence[i]-6;
else if ('1' <= sequence[i] && sequence[i] <= '3')
num_seq[i] = sequence[i]+6;
else
num_seq[i] = sequence[i];
}
num_seq[i++] = '\0';
fvwm_msg(INFO, "StrokeFunc", "stroke sequence: %s (N%s)",
sequence, num_seq);
}
if (abort) return;
/* check for a binding */
stroke_action = CheckBinding(Scr.AllBindings, sequence, 0, modifiers,
GetUnusedModifiers(), context, STROKE_BINDING);
/* execute the action */
if (stroke_action != NULL)
{
if (feed_back && atoi(sequence) != 0)
{
GrabEm(CRS_WAIT, GRAB_BUSY);
usleep(200000);
UngrabEm(GRAB_BUSY);
}
old_execute_function(stroke_action, tmp_win, eventp, context, -1, 0, NULL);
}
}
#endif /* HAVE_STROKE */
/* 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 module is all original code
* by Rob Nation
* Copyright 1993, Robert Nation
* You may use this code for any purpose, as long as the original
* copyright remains in the source code and all documentation
****************************************************************************/
/***********************************************************************
*
* fvwm window border drawing code
*
***********************************************************************/
#include "config.h"
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include "libs/fvwmlib.h"
#include "libs/FShape.h"
#include "fvwm.h"
#include "externs.h"
#include "events.h"
#include "cursor.h"
#include "functions.h"
#include "bindings.h"
#include "misc.h"
#include "screen.h"
#include "defaults.h"
#include "geometry.h"
#include "borders.h"
#include "builtins.h"
#include "icons.h"
#include "module_interface.h"
#include "libs/Colorset.h"
typedef struct
{
int relief_width;
GC relief_gc;
GC shadow_gc;
Pixel fore_color;
Pixel back_color;
Pixmap back_pixmap;
XSetWindowAttributes attributes;
unsigned long valuemask;
Pixmap texture_pixmap;
XSetWindowAttributes notex_attributes;
unsigned long notex_valuemask;
struct
{
unsigned has_color_changed : 1;
} flags;
} common_decorations_type;
XGCValues Globalgcv;
unsigned long Globalgcm;
extern Window PressedW;
/* used by several drawing functions */
static void ClipClear(Window win, XRectangle *rclip, Bool do_flush_expose)
{
if (rclip)
{
XClearArea(
dpy, win, rclip->x, rclip->y, rclip->width, rclip->height, False);
}
else
{
if (do_flush_expose)
{
flush_expose(win);
}
XClearWindow(dpy, win);
}
return;
}
/***********************************************************************
* change by [EMAIL PROTECTED] to correct popups off title buttons
*
* Procedure:
*ButtonPosition - find the actual position of the button
* since some buttons may be disabled
*
* Returned Value:
*The button count from left or right taking in to account
*that some buttons may not be enabled for this window
*
* Inputs:
* context - context as per the global Context
* t - the window (FvwmWindow) to test against
*
***********************************************************************/
int ButtonPosition(int context, FvwmWindow *t)
{
int i = 0;
int buttons = -1;
int end = Scr.nr_left_buttons;
if (context & C_RALL)
{
i = 1;
end = Scr.nr_right_buttons;
}
{
for (; i / 2 < end; i += 2)
{
if (t->button_w[i])
{
buttons++;
}
/* is this the button ? */
if (((1 << i) * C_L1) & context)
return buttons;
}
}
/* you never know... */
return 0;
}
static void change_window_background(
Window w, unsigned long valuemask, XSetWindowAttributes *attributes)
{
XChangeWindowAttributes(dpy, w, valuemask, attributes);
XClearWindow(dpy, w);
}
/****************************************************************************
*
* Draws a little pattern within a window (more complex)
*
****************************************************************************/
static void DrawLinePattern(
Window win, GC ReliefGC, GC ShadowGC, Pixel fore_color, Pixel back_color,
struct vector_coords *coords, int w, int h)
{
int i, prev_x, prev_y;
GC gcs[4];
gcs[0] = ShadowGC;
gcs[1] = ReliefGC;
gcs[2] = NULL;
gcs[3] = NULL;
/* It is rare, so evaluate this only when needed */
if (coords->use_fgbg)
{
Globalgcv.foreground = fore_color;
Globalgcm = GCForeground;
XChangeGC(dpy, Scr.ScratchGC3, Globalgcm, &Globalgcv);
gcs[3] = Scr.ScratchGC3;
Globalgcv.foreground = back_color;
XChangeGC(dpy, Scr.ScratchGC4, Globalgcm, &Globalgcv);
gcs[2] = Scr.ScratchGC4;
}
/* safety check */
if (coords->num <= 0) return;
prev_x = w * coords->vertices->x/100;
prev_y = h * coords->vertices->y/100;
for (i = 1; i < coords->num; ++i)
{
struct vector_vertex *v = coords->vertices + i;
int x = w * v->x/100;
int y = h * v->y/100;
if (v->color < 4) XDrawLine(dpy,win,gcs[v->color],prev_x,prev_y,x,y);
prev_x = x;
prev_y = y;
}
}
/* used by DrawButton() */
static void clip_button_pixmap(
int *ox, int *oy, int *cw, int *ch, int*cx, int *cy, XRectangle *rclip)
{
if (rclip)
{
if (rclip->x > *cx)
{
*ox = (rclip->x - *cx);
*cx += *ox;
*cw -= *ox;
}
if (*cx + *cw > rclip->x + rclip->width)
{
*cw = rclip->x - *cx + rclip->width;
}
if (rclip->y > *cy)
{
*oy = (rclip->y - *cy);
*cy += *oy;
*ch -= *oy;
}
if (*cy + *ch > rclip->y + rclip->height)
{
*ch = rclip->y - *cy + rclip->height;
}
}
return;
}
/****************************************************************************
*
* Redraws buttons ([EMAIL PROTECTED])
*
****************************************************************************/
static void DrawButton(
FvwmWindow *t, Window win, int w, int h, DecorFace *df, GC ReliefGC,
GC ShadowGC, Pixel fore_color, Pixel back_color, Bool is_lowest,
mwm_flags stateflags, int left1right0, XRectangle *rclip,
Pixmap *pbutton_background_pixmap)
{
register DecorFaceType type = DFS_FACE_TYPE(df->style);
Picture *p;
int border = 0;
int x;
int y;
int width;
int height;
/* Note: it is assumed that ReliefGC and ShadowGC are already clipped with
* the rclip rectangle when this function is called. */
switch (type)
{
case SimpleButton:
break;
case SolidButton:
if (pbutton_background_pixmap)
{
XSetWindowBackground(dpy, win, df->u.back);
*pbutton_background_pixmap = None;
}
ClipClear(win, rclip, True);
break;
case VectorButton:
case DefaultVectorButton:
if(HAS_MWM_BUTTONS(t) &&
((stateflags & MWM_DECOR_MAXIMIZE && IS_MAXIMIZED(t)) ||
(stateflags & MWM_DECOR_SHADE && IS_SHADED(t)) ||
(stateflags & MWM_DECOR_STICK && IS_STICKY(t))))
{
DrawLinePattern(win, ShadowGC, ReliefGC, fore_color, back_color,
&df->u.vector, w, h);
}
else
{
DrawLinePattern(win, ReliefGC, ShadowGC, fore_color, back_color,
&df->u.vector, w, h);
}
break;
#ifdef MINI_ICONS
case MiniIconButton:
case PixmapButton:
case TiledPixmapButton:
if (type == PixmapButton || type == TiledPixmapButton)
{
p = df->u.p;
}
else
{
if (!t->mini_icon)
break;
p = t->mini_icon;
}
#else
case PixmapButton:
case TiledPixmapButton:
p = df->u.p;
#endif /* MINI_ICONS */
if (DFS_BUTTON_RELIEF(df->style) == DFS_BUTTON_IS_FLAT)
border = 0;
else
border = HAS_MWM_BORDER(t) ? 1 : 2;
x = border;
y = border;
width = w - border * 2;
height = h - border * 2;
if (is_lowest && !p->mask && pbutton_background_pixmap)
{
if (type == TiledPixmapButton ||
(p && p->width >= width && p->height >= height))
{
/* better performance: set a background pixmap if possible, i.e. if the
* decoration is the lowest one in the button and it has no transparent
* parts and it is tiled or bigger than the button */
if (*pbutton_background_pixmap != p->picture)
{
*pbutton_background_pixmap = p->picture;
XSetWindowBackgroundPixmap(dpy, win, p->picture);
ClipClear(win, rclip, True);
}
else
{
ClipClear(win, rclip, True);
}
break;
}
}
else if (pbutton_background_pixmap && *pbutton_background_pixmap != None)
{
XSetWindowBackgroundPixmap(dpy, win, None);
if (pbutton_background_pixmap)
{
*pbutton_background_pixmap = None;
flush_expose(win);
XClearWindow(dpy, win);
}
}
if (type != TiledPixmapButton)
{
if (DFS_H_JUSTIFICATION(df->style) == JUST_RIGHT)
x += (int)(width - p->width);
else if (DFS_H_JUSTIFICATION(df->style) == JUST_CENTER)
/* round up for left buttons, down for right buttons */
x += (int)(width - p->width + left1right0) / 2;
if (DFS_V_JUSTIFICATION(df->style) == JUST_BOTTOM)
y += (int)(height - p->height);
else if (DFS_V_JUSTIFICATION(df->style) == JUST_CENTER)
/* round up */
y += (int)(height - p->height + 1) / 2;
if (x < border)
x = border;
if (y < border)
y = border;
if (width > p->width)
width = p->width;
if (height > p->height)
height = p->height;
if (width > w - x - border)
width = w - x - border;
if (height > h - y - border)
height = h - y - border;
}
XSetClipMask(dpy, Scr.TransMaskGC, p->mask);
XSetClipOrigin(dpy, Scr.TransMaskGC, x, y);
if (type != TiledPixmapButton)
{
int cx = x;
int cy = y;
int cw = width;
int ch = height;
int ox = 0;
int oy = 0;
clip_button_pixmap(&ox, &oy, &cw, &ch, &cx, &cy, rclip);
if (cw > 0 && ch > 0)
{
XCopyArea(
dpy, p->picture, win, Scr.TransMaskGC, ox, oy, cw, ch, cx, cy);
}
}
else
{
int xi;
int yi;
for (yi = border; yi < height; yi += p->height)
{
for (xi = border; xi < width; xi += p->width)
{
int lw = width - xi;
int lh = height - yi;
int cx;
int cy;
int cw;
int ch;
int ox;
int oy;
if (lw > p->width)
lw = p->width;
if (lh > p->height)
lh = p->height;
if (rclip)
{
if (xi + lw <= rclip->x || xi >= rclip->x + rclip->width ||
yi + lh <= rclip->y || yi >= rclip->y + rclip->height)
{
continue;
}
}
cx = xi;
cy = yi;
cw = lw;
ch = lh;
ox = 0;
oy = 0;
clip_button_pixmap(&ox, &oy, &cw, &ch, &cx, &cy, rclip);
if (cw > 0 && ch > 0)
{
XSetClipOrigin(dpy, Scr.TransMaskGC, xi, yi);
XCopyArea(dpy, p->picture, win, Scr.TransMaskGC,
ox, oy, cw, ch, cx, cy);
}
}
}
}
XSetClipMask(dpy, Scr.TransMaskGC, None);
break;
case GradientButton:
{
unsigned int g_width;
unsigned int g_height;
if (!rclip)
{
flush_expose(win);
}
XSetClipMask(dpy, Scr.TransMaskGC, None);
/* find out the size the pixmap should be */
CalculateGradientDimensions(
dpy, win, df->u.grad.npixels, df->u.grad.gradient_type,
&g_width, &g_height);
/* draw the gradient directly into the window */
CreateGradientPixmap(
dpy, win, Scr.TransMaskGC, df->u.grad.gradient_type, g_width, g_height,
df->u.grad.npixels, df->u.grad.pixels, win, 0, 0, w, h, rclip);
}
break;
default:
fvwm_msg(ERR, "DrawButton", "unknown button type");
break;
}
return;
}
/* rules to get button state */
static enum ButtonState get_button_state(
Bool has_focus, Bool toggled, Window w)
{
if (Scr.gs.use_active_down_buttons)
{
if (Scr.gs.use_inactive_buttons && !has_focus)
{
return (toggled) ? BS_ToggledInactive : BS_Inactive;
}
else
{
if (PressedW == w)
{
return (toggled) ? BS_ToggledActiveDown : BS_ActiveDown;
}
else
{
return (toggled) ? BS_ToggledActiveUp : BS_ActiveUp;
}
}
}
else
{
if (Scr.gs.use_inactive_buttons && !has_focus)
{
return (toggled) ? BS_ToggledInactive : BS_Inactive;
}
else
{
return (toggled) ? BS_ToggledActiveUp : BS_ActiveUp;
}
}
}
static const char ulgc[] = { 1, 0, 0, 0x7f, 2, 1, 1 };
static const char brgc[] = { 1, 1, 2, 0x7f, 0, 0, 3 };
/* called twice by RedrawBorder */
static void draw_frame_relief(
FvwmWindow *t, GC rgc, GC sgc, GC tgc, GC dgc, int w_dout, int w_hiout,
int w_trout, int w_c, int w_trin, int w_shin, int w_din)
{
int i;
int offset = 0;
int width = t->frame_g.width - 1;
int height = t->frame_g.height - 1;
int w[7];
GC gc[4];
w[0] = w_dout;
w[1] = w_hiout;
w[2] = w_trout;
w[3] = w_c;
w[4] = w_trin;
w[5] = w_shin;
w[6] = w_din;
gc[0] = rgc;
gc[1] = sgc;
gc[2] = tgc;
gc[3] = dgc;
for (i = 0; i < 7; i++)
{
if (ulgc[i] != 0x7f && w[i] > 0)
{
RelieveRectangle(
dpy, t->decor_w, offset, offset, width, height, gc[(int)ulgc[i]],
gc[(int)brgc[i]], w[i]);
}
offset += w[i];
width -= 2 * w[i];
height -= 2 * w[i];
}
}
/****************************************************************************
*
* Redraws the windows borders
*
****************************************************************************/
static void RedrawBorder(
common_decorations_type *cd, FvwmWindow *t, Bool has_focus, int force,
Window expose_win, XRectangle *rclip)
{
static GC tgc = None;
int i;
GC rgc;
GC sgc;
DecorFaceStyle *borderstyle;
Bool is_reversed = False;
Bool is_clipped = False;
int w_dout;
int w_hiout;
int w_trout;
int w_c;
int w_trin;
int w_shin;
int w_din;
int sum;
int trim;
if (tgc == None && !HAS_MWM_BORDER(t))
{
XGCValues xgcv;
xgcv.function = GXnoop;
xgcv.plane_mask = 0;
tgc = fvwmlib_XCreateGC(dpy, t->frame, GCFunction | GCPlaneMask, &xgcv);
}
/*
* draw the border; resize handles are InputOnly so draw in the decor_w and
* it will show through. The entire border is redrawn so flush all exposes
* up to this point.
*/
#if 0
flush_expose(t->decor_w);
#endif
if (t->boundary_width < 2)
{
/*
* for mono - put a black border on
*/
if (Pdepth <2)
{
XSetWindowBackgroundPixmap(dpy, t->decor_w, cd->back_pixmap);
XClearWindow(dpy, t->decor_w);
}
else
{
if (cd->texture_pixmap != None)
XSetWindowBackgroundPixmap(dpy, t->decor_w, cd->texture_pixmap);
XClearWindow(dpy, t->decor_w);
}
return;
}
/*
* for color, make it the color of the decoration background
*/
/* get the border style bits */
borderstyle = (has_focus) ?
&GetDecor(t, BorderStyle.active.style) :
&GetDecor(t, BorderStyle.inactive.style);
is_reversed ^= (borderstyle->flags.button_relief == DFS_BUTTON_IS_SUNK);
if (is_reversed)
{
sgc = cd->relief_gc;
rgc = cd->shadow_gc;
}
else
{
rgc = cd->relief_gc;
sgc = cd->shadow_gc;
}
/*
* remove any lines drawn in the border if hidden handles or noinset and if
* a handle was pressed
*/
if (PressedW == None &&
(DFS_HAS_NO_INSET(*borderstyle) ||
DFS_HAS_HIDDEN_HANDLES(*borderstyle)))
{
ClipClear(t->decor_w, rclip, False);
}
/*
* Nothing to do if flat
*/
if (borderstyle->flags.button_relief == DFS_BUTTON_IS_FLAT)
return;
/*
* draw the inside relief
*/
if (HAS_MWM_BORDER(t))
{
/* MWM borders look like this:
*
* HHCCCCS from outside to inside on the left and top border
* SSCCCCH from outside to inside on the bottom and right border
* |||||||
* |||||||__ w_shin (inner shadow area)
* ||||||___ w_c (transparent area)
* |||||____ w_c (transparent area)
* ||||_____ w_c (transparent area)
* |||______ w_c (transparent area)
* ||_______ w_hiout (outer hilight area)
* |________ w_hiout (outer hilight area)
*
*
* C = original colour
* H = hilight
* S = shadow
*/
w_dout = 0;
w_hiout = 2;
w_trout = 0;
w_trin = 0;
w_shin = 1;
w_din = 0;
sum = 3;
trim = sum - t->boundary_width + 1;
}
else
{
/* Fvwm borders look like this:
*
* SHHCCSS from outside to inside on the left and top border
* SSCCHHS from outside to inside on the bottom and right border
* |||||||
* |||||||__ w_din (inner dark area)
* ||||||___ w_shin (inner shadow area)
* |||||____ w_trin (inner transparent/shadow area)
* ||||_____ w_c (transparent area)
* |||______ w_trout (outer transparent/hilight area)
* ||_______ w_hiout (outer hilight area)
* |________ w_dout (outer dark area)
*
* C = original colour
* H = hilight
* S = shadow
*
* reduced to 5 pixels it looks like this:
*
* SHHCS
* SSCHS
* |||||
* |||||__ w_din (inner dark area)
* ||||___ w_trin (inner transparent/shadow area)
* |||____ w_trout (outer transparent/hilight area)
* ||_____ w_hiout (outer hilight area)
* |______ w_dout (outer dark area)
*/
w_dout = 1;
w_hiout = 1;
w_trout = 1;
w_trin = 1;
w_shin = 1;
w_din = 1;
/* w_trout + w_trin counts only as one pixel of border because they let one
* pixel of the original colour shine through. */
sum = 6;
trim = sum - t->boundary_width;
}
if (DFS_HAS_NO_INSET(*borderstyle))
{
w_shin = 0;
sum--;
trim--;
if (w_trin)
{
w_trout = 0;
w_trin = 0;
w_din = 0;
w_hiout = 1;
sum -= 2;
trim -= 2;
}
}
/* If the border is too thin to accomodate the standard look, we remove parts
* of the border so that at least one pixel of the original colour is
* visible. We make an exception for windows with a border width of 2,
* though. */
if ((!IS_SHADED(t) || HAS_TITLE(t)) && t->boundary_width == 2)
trim--;
if (trim < 0)
trim = 0;
for ( ; trim > 0; trim--)
{
if (w_hiout > 1)
w_hiout--;
else if (w_shin > 0)
w_shin--;
else if (w_hiout > 0)
w_hiout--;
else if (w_trout > 0)
{
w_trout = 0;
w_trin = 0;
w_din = 0;
w_hiout = 1;
}
sum--;
}
w_c = t->boundary_width - sum;
if (rclip)
{
XSetClipRectangles(dpy, rgc, 0, 0, rclip, 1, Unsorted);
XSetClipRectangles(dpy, sgc, 0, 0, rclip, 1, Unsorted);
is_clipped = True;
}
draw_frame_relief(
t, rgc, sgc, tgc, sgc,
w_dout, w_hiout, w_trout, w_c, w_trin, w_shin, w_din);
/*
* draw the handle marks
*/
/* draw the handles as eight marks around the border */
if (HAS_BORDER(t) && (t->boundary_width > 1) &&
!DFS_HAS_HIDDEN_HANDLES(*borderstyle))
{
/* MWM border windows have thin 3d effects
* FvwmBorders have 3 pixels top/left, 2 bot/right so this makes
* calculating the length of the marks difficult, top and bottom
* marks for FvwmBorders are different if NoInset is specified */
int inset = (w_shin || w_din);
XSegment marks[8];
int k;
/*
* draw the relief
*/
for (k = 0; k < cd->relief_width; k++)
{
int loff = k;
int boff = k + w_dout + 1;
int length = t->boundary_width - boff - inset;
if (length < 0)
break;
/* hilite marks */
i = 0;
/* top left */
marks[i].x1 = t->visual_corner_width + loff;
marks[i].x2 = t->visual_corner_width + loff;
marks[i].y1 = w_dout;
marks[i].y2 = marks[i].y1 + length;
i++;
/* top right */
marks[i].x1 = t->frame_g.width - t->visual_corner_width + loff;
marks[i].x2 = t->frame_g.width - t->visual_corner_width + loff;
marks[i].y1 = w_dout;
marks[i].y2 = marks[i].y1 + length;
i++;
/* bot left */
marks[i].x1 = t->visual_corner_width + loff;
marks[i].x2 = t->visual_corner_width + loff;
marks[i].y1 = t->frame_g.height - boff;
marks[i].y2 = marks[i].y1 - length;
i++;
/* bot right */
marks[i].x1 = t->frame_g.width - t->visual_corner_width + loff;
marks[i].x2 = t->frame_g.width - t->visual_corner_width + loff;
marks[i].y1 = t->frame_g.height - boff;
marks[i].y2 = marks[i].y1 - length;
i++;
if (!IS_SHADED(t))
{
/* left top */
marks[i].x1 = w_dout;
marks[i].x2 = marks[i].x1 + length;
marks[i].y1 = t->visual_corner_width + loff;
marks[i].y2 = t->visual_corner_width + loff;
i++;
/* left bot */
marks[i].x1 = w_dout;
marks[i].x2 = marks[i].x1 + length;
marks[i].y1 = t->frame_g.height-t->visual_corner_width + loff;
marks[i].y2 = t->frame_g.height-t->visual_corner_width + loff;
i++;
/* right top */
marks[i].x1 = t->frame_g.width - boff;
marks[i].x2 = marks[i].x1 - length;
marks[i].y1 = t->visual_corner_width + loff;
marks[i].y2 = t->visual_corner_width + loff;
i++;
/* right bot */
marks[i].x1 = t->frame_g.width - boff;
marks[i].x2 = marks[i].x1 - length;
marks[i].y1 = t->frame_g.height-t->visual_corner_width + loff;
marks[i].y2 = t->frame_g.height-t->visual_corner_width + loff;
i++;
}
XDrawSegments(dpy, t->decor_w, rgc, marks, i);
/* shadow marks, reuse the array (XDrawSegments doesn't trash it) */
i = 0;
loff = 1 + k + k;
/* top left */
marks[i].x1 -= loff;
marks[i].x2 -= loff;
marks[i].y1 += k;
marks[i].y2 += k;
i++;
/* top right */
marks[i].x1 -= loff;
marks[i].x2 -= loff;
marks[i].y1 += k;
marks[i].y2 += k;
i++;
/* bot left */
marks[i].x1 -= loff;
marks[i].x2 -= loff;
marks[i].y1 += k;
marks[i].y2 += k;
i++;
/* bot right */
marks[i].x1 -= loff;
marks[i].x2 -= loff;
marks[i].y1 += k;
marks[i].y2 += k;
i++;
if (!IS_SHADED(t))
{
/* left top */
marks[i].x1 += k;
marks[i].x2 += k;
marks[i].y1 -= loff;
marks[i].y2 -= loff;
i++;
/* left bot */
marks[i].x1 += k;
marks[i].x2 += k;
marks[i].y1 -= loff;
marks[i].y2 -= loff;
i++;
/* right top */
marks[i].x1 += k;
marks[i].x2 += k;
marks[i].y1 -= loff;
marks[i].y2 -= loff;
i++;
/* right bot */
marks[i].x1 += k;
marks[i].x2 += k;
marks[i].y1 -= loff;
marks[i].y2 -= loff;
i++;
}
XDrawSegments(dpy, t->decor_w, sgc, marks, i);
}
}
/*
* now draw the pressed in part on top if we have depressable borders
*/
/* a bit hacky to draw twice but you should see the code it replaces, never
* mind the esoterics, feel the thin-ness */
if ((HAS_BORDER(t) || PressedW == t->decor_w || PressedW == t->frame) &&
HAS_DEPRESSABLE_BORDER(t))
{
XRectangle r;
Bool is_pressed = False;
if (PressedW == t->sides[0])
{
/* top */
r.x = t->visual_corner_width;
r.y = 1;
r.width = t->frame_g.width - 2 * t->visual_corner_width;
r.height = t->boundary_width - 1;
is_pressed = True;
}
else if (PressedW == t->sides[1])
{
/* right */
if (!IS_SHADED(t))
{
r.x = t->frame_g.width - t->boundary_width;
r.y = t->visual_corner_width;
r.width = t->boundary_width - 1;
r.height = t->frame_g.height - 2 * t->visual_corner_width;
is_pressed = True;
}
}
else if (PressedW == t->sides[2])
{
/* bottom */
r.x = t->visual_corner_width;
r.y = t->frame_g.height - t->boundary_width;
r.width = t->frame_g.width - 2 * t->visual_corner_width;
r.height = t->boundary_width - 1;
is_pressed = True;
}
else if (PressedW == t->sides[3])
{
/* left */
if (!IS_SHADED(t))
{
r.x = 1;
r.y = t->visual_corner_width;
r.width = t->boundary_width - 1;
r.height = t->frame_g.height - 2 * t->visual_corner_width;
is_pressed = True;
}
}
else if (PressedW == t->corners[0])
{
/* top left */
r.x = 1;
r.y = 1;
r.width = t->visual_corner_width - 1;
r.height = t->visual_corner_width - 1;
is_pressed = True;
}
else if (PressedW == t->corners[1])
{
/* top right */
r.x = t->frame_g.width - t->visual_corner_width;
r.y = 1;
r.width = t->visual_corner_width - 1;
r.height = t->visual_corner_width - 1;
is_pressed = True;
}
else if (PressedW == t->corners[2])
{
/* bottom left */
r.x = 1;
r.y = t->frame_g.height - t->visual_corner_width;
r.width = t->visual_corner_width - 1;
r.height = t->visual_corner_width - 1;
is_pressed = True;
}
else if (PressedW == t->corners[3])
{
/* bottom right */
r.x = t->frame_g.width - t->visual_corner_width;
r.y = t->frame_g.height - t->visual_corner_width;
r.width = t->visual_corner_width - 1;
r.height = t->visual_corner_width - 1;
is_pressed = True;
}
else if (PressedW == t->decor_w || PressedW == t->frame)
{
/* whole border */
r.x = 1;
r.y = 1;
r.width = t->frame_g.width - 2;
r.height = t->frame_g.height - 2;
is_pressed = True;
}
if (is_pressed == True)
{
Bool is_not_empty = True;
if (rclip)
{
is_not_empty = intersect_xrectangles(&r, rclip);
}
if (is_not_empty)
{
XSetClipRectangles(dpy, rgc, 0, 0, &r, 1, Unsorted);
XSetClipRectangles(dpy, sgc, 0, 0, &r, 1, Unsorted);
draw_frame_relief(
t, sgc, rgc, tgc, sgc,
w_dout, w_hiout, w_trout, w_c, w_trin, w_shin, w_din);
is_clipped = True;
}
}
}
if (is_clipped)
{
XSetClipMask(dpy, rgc, None);
XSetClipMask(dpy, sgc, None);
}
return;
}
/****************************************************************************
*
* Redraws the window buttons
*
****************************************************************************/
static void RedrawButtons(
common_decorations_type *cd, FvwmWindow *t, Bool has_focus, int force,
Window expose_win, XRectangle *rclip)
{
int i;
Bool is_lowest = True;
Pixmap *pass_bg_pixmap;
/* Note: the rclip rectangle is not used in this function. Buttons are usually
* so small that it makes not much sense to limit drawing to a clip rectangle.
*/
/*
* draw buttons
*/
for (i = 0; i < NUMBER_OF_BUTTONS; i++)
{
if (t->button_w[i] != None && (((i & 1) && i / 2 < Scr.nr_right_buttons) ||
(!(i & 1) && i / 2 < Scr.nr_left_buttons)))
{
mwm_flags stateflags =
TB_MWM_DECOR_FLAGS(GetDecor(t, buttons[i]));
Bool toggled =
(HAS_MWM_BUTTONS(t) &&
((stateflags & MWM_DECOR_MAXIMIZE && IS_MAXIMIZED(t)) ||
(stateflags & MWM_DECOR_SHADE && IS_SHADED(t)) ||
(stateflags & MWM_DECOR_STICK && IS_STICKY(t))));
enum ButtonState bs =
get_button_state(has_focus, toggled, t->button_w[i]);
DecorFace *df = &TB_STATE(GetDecor(t, buttons[i]))[bs];
if(flush_expose(t->button_w[i]) || expose_win == t->button_w[i] ||
expose_win == None || cd->flags.has_color_changed)
{
int is_inverted = (PressedW == t->button_w[i]);
if (DFS_USE_BORDER_STYLE(df->style))
{
XChangeWindowAttributes(
dpy, t->button_w[i], cd->valuemask, &cd->attributes);
if (df->u.p)
{
t->button_background_pixmap[i] = df->u.p->picture;
}
if (df->u.p && df->u.p->picture)
{
pass_bg_pixmap = NULL;
}
else
{
pass_bg_pixmap = &t->button_background_pixmap[i];
}
}
else
{
XChangeWindowAttributes(
dpy, t->button_w[i], cd->notex_valuemask, &cd->notex_attributes);
t->button_background_pixmap[i] = None;
pass_bg_pixmap = &t->button_background_pixmap[i];
}
XClearWindow(dpy, t->button_w[i]);
if (DFS_USE_TITLE_STYLE(df->style))
{
DecorFace *tsdf = &TB_STATE(GetDecor(t, titlebar))[bs];
is_lowest = True;
#ifdef MULTISTYLE
for (; tsdf; tsdf = tsdf->next)
#endif
{
DrawButton(t, t->button_w[i], t->title_g.height, t->title_g.height,
tsdf, cd->relief_gc, cd->shadow_gc,
cd->fore_color, cd->back_color, is_lowest,
TB_MWM_DECOR_FLAGS(GetDecor(t, buttons[i])), 1, NULL,
pass_bg_pixmap);
is_lowest = False;
}
}
is_lowest = True;
#ifdef MULTISTYLE
for (; df; df = df->next)
#endif
{
DrawButton(t, t->button_w[i], t->title_g.height, t->title_g.height,
df, cd->relief_gc, cd->shadow_gc,
cd->fore_color, cd->back_color, is_lowest,
TB_MWM_DECOR_FLAGS(GetDecor(t, buttons[i])), 1, NULL,
pass_bg_pixmap);
is_lowest = False;
}
{
Bool reverse = is_inverted;
switch (DFS_BUTTON_RELIEF(
TB_STATE(GetDecor(t, buttons[i]))[bs].style))
{
case DFS_BUTTON_IS_SUNK:
reverse = !is_inverted;
case DFS_BUTTON_IS_UP:
RelieveRectangle2(
dpy, t->button_w[i], 0, 0, t->title_g.height - 1,
t->title_g.height - 1, (reverse ? cd->shadow_gc : cd->relief_gc),
(reverse ? cd->relief_gc : cd->shadow_gc), cd->relief_width);
break;
default:
/* flat */
break;
}
}
}
} /* if (HAS_BUTTON(i))*/
} /* for */
}
/****************************************************************************
*
* Redraws just the title bar
*
****************************************************************************/
static void RedrawTitle(
common_decorations_type *cd, FvwmWindow *t, Bool has_focus, XRectangle *rclip)
{
int hor_off;
int w;
int i;
enum ButtonState title_state;
DecorFaceStyle *tb_style;
Pixmap *pass_bg_pixmap;
GC rgc = cd->relief_gc;
GC sgc = cd->shadow_gc;
Bool reverse = False;
Bool is_clipped = False;
Bool toggled =
(HAS_MWM_BUTTONS(t) &&
((TB_HAS_MWM_DECOR_MAXIMIZE(GetDecor(t, titlebar)) && IS_MAXIMIZED(t))||
(TB_HAS_MWM_DECOR_SHADE(GetDecor(t, titlebar)) && IS_SHADED(t)) ||
(TB_HAS_MWM_DECOR_STICK(GetDecor(t, titlebar)) && IS_STICKY(t))));
if (PressedW == t->title_w)
{
GC tgc;
tgc = sgc;
sgc = rgc;
rgc = tgc;
}
#if 0
flush_expose(t->title_w);
#endif
if (t->name != (char *)NULL)
{
#ifdef I18N_MB /* cannot use macro here, rewriting... */
w = XmbTextEscapement(t->title_font.fontset,t->name, strlen(t->name));
#else
w = XTextWidth(t->title_font.font, t->name, strlen(t->name));
#endif
if (w > t->title_g.width - 12)
w = t->title_g.width - 4;
if (w < 0)
w = 0;
}
else
{
w = 0;
}
title_state = get_button_state(has_focus, toggled, t->title_w);
tb_style = &TB_STATE(GetDecor(t, titlebar))[title_state].style;
switch (TB_JUSTIFICATION(GetDecor(t, titlebar)))
{
case JUST_LEFT:
hor_off = WINDOW_TITLE_TEXT_OFFSET;
break;
case JUST_RIGHT:
hor_off = t->title_g.width - w - WINDOW_TITLE_TEXT_OFFSET;
break;
case JUST_CENTER:
default:
hor_off = (t->title_g.width - w) / 2;
break;
}
NewFontAndColor(
t->title_font.font->fid, cd->fore_color, cd->back_color);
/* the next bit tries to minimize redraw ([EMAIL PROTECTED]) */
/* we need to check for UseBorderStyle for the titlebar */
{
DecorFace *df = (has_focus) ?
&GetDecor(t,BorderStyle.active) : &GetDecor(t,BorderStyle.inactive);
if (DFS_USE_BORDER_STYLE(*tb_style) &&
DFS_FACE_TYPE(df->style) == TiledPixmapButton &&
df->u.p)
{
XSetWindowBackgroundPixmap(dpy, t->title_w, df->u.p->picture);
t->title_background_pixmap = df->u.p->picture;
pass_bg_pixmap = NULL;
}
else
{
pass_bg_pixmap = &t->title_background_pixmap;
}
}
ClipClear(t->title_w, rclip, False);
if (rclip)
{
XSetClipRectangles(dpy, rgc, 0, 0, rclip, 1, Unsorted);
XSetClipRectangles(dpy, sgc, 0, 0, rclip, 1, Unsorted);
XSetClipRectangles(dpy, Scr.TitleGC, 0, 0, rclip, 1, Unsorted);
is_clipped = True;
}
/*
* draw title
*/
if (Pdepth < 2)
{
/* for mono, we clear an area in the title bar where the window
* title goes, so that its more legible. For color, no need */
RelieveRectangle(
dpy, t->title_w, 0, 0, hor_off - 3, t->title_g.height - 1, rgc, sgc,
cd->relief_width);
RelieveRectangle(
dpy, t->title_w, hor_off + w + 2, 0, t->title_g.width - w - hor_off - 3,
t->title_g.height - 1, rgc, sgc, cd->relief_width);
XFillRectangle(
dpy, t->title_w, ((PressedW == t->title_w) ? sgc : rgc), hor_off - 2, 0,
w+4,t->title_g.height);
XDrawLine(
dpy, t->title_w, sgc, hor_off + w + 1, 0, hor_off + w + 1,
t->title_g.height);
if(t->name != (char *)NULL)
#ifdef I18N_MB
XmbDrawString(dpy, t->title_w, t->title_font.fontset,
Scr.TitleGC, hor_off,
t->title_font.y + 1, t->name, strlen(t->name));
#else
XDrawString(dpy, t->title_w, Scr.TitleGC, hor_off,
t->title_font.y + 1, t->name, strlen(t->name));
#endif
}
else
{
DecorFace *df = &TB_STATE(GetDecor(t, titlebar))[title_state];
/* draw compound titlebar ([EMAIL PROTECTED]) */
Bool is_lowest = True;
if (PressedW == t->title_w)
{
#ifdef MULTISTYLE
for (; df; df = df->next)
#endif
{
DrawButton(
t, t->title_w, t->title_g.width, t->title_g.height, df, sgc,
rgc, cd->fore_color, cd->back_color, is_lowest, 0, 1, rclip,
pass_bg_pixmap);
is_lowest = False;
}
}
else
{
#ifdef MULTISTYLE
for (; df; df = df->next)
#endif
{
DrawButton(
t, t->title_w, t->title_g.width, t->title_g.height, df, rgc,
sgc, cd->fore_color, cd->back_color, is_lowest, 0, 1, rclip,
pass_bg_pixmap);
is_lowest = False;
}
}
/*
* draw title relief
*/
switch (DFS_BUTTON_RELIEF(*tb_style))
{
case DFS_BUTTON_IS_SUNK:
reverse = 1;
case DFS_BUTTON_IS_UP:
RelieveRectangle2(
dpy, t->title_w, 0, 0, t->title_g.width - 1, t->title_g.height - 1,
(reverse) ? sgc : rgc, (reverse) ? rgc : sgc, cd->relief_width);
break;
default:
/* flat */
break;
}
#ifdef I18N_MB
if(t->name != (char *)NULL)
XmbDrawString(dpy, t->title_w, t->title_font.fontset,
Scr.TitleGC, hor_off,
t->title_font.y + 1, t->name, strlen(t->name));
#else
if(t->name != (char *)NULL)
XDrawString(dpy, t->title_w, Scr.TitleGC, hor_off,
t->title_font.y + 1, t->name, strlen(t->name));
#endif
}
/*
* draw the 'sticky' lines
*/
if (IS_STICKY(t) || HAS_STIPPLED_TITLE(t))
{
/* an odd number of lines every WINDOW_TITLE_STICK_VERT_DIST pixels */
int num =
(int)(t->title_g.height / WINDOW_TITLE_STICK_VERT_DIST / 2) * 2 - 1;
int min = t->title_g.height / 2 - num * 2 + 1;
int max =
t->title_g.height / 2 + num * 2 - WINDOW_TITLE_STICK_VERT_DIST + 1;
int left_x = WINDOW_TITLE_STICK_OFFSET;
int left_w = hor_off - left_x - WINDOW_TITLE_TO_STICK_GAP;
int right_x = hor_off + w + WINDOW_TITLE_TO_STICK_GAP - 1;
int right_w = t->title_g.width - right_x - WINDOW_TITLE_STICK_OFFSET;
if (left_w < WINDOW_TITLE_STICK_MIN_WIDTH)
{
left_x = 0;
left_w = WINDOW_TITLE_STICK_MIN_WIDTH;
}
if (right_w < WINDOW_TITLE_STICK_MIN_WIDTH)
{
right_w = WINDOW_TITLE_STICK_MIN_WIDTH;
right_x = t->title_g.width - WINDOW_TITLE_STICK_MIN_WIDTH - 1;
}
for (i = min; i <= max; i += WINDOW_TITLE_STICK_VERT_DIST)
{
if (left_w > 0)
{
RelieveRectangle(
dpy, t->title_w, left_x, i, left_w, 1, sgc, rgc, 1);
}
if (right_w > 0)
{
RelieveRectangle(
dpy, t->title_w, right_x, i, right_w, 1, sgc, rgc, 1);
}
}
}
if (is_clipped)
{
XSetClipMask(dpy, rgc, None);
XSetClipMask(dpy, sgc, None);
XSetClipMask(dpy, Scr.TitleGC, None);
}
return;
}
void SetupTitleBar(FvwmWindow *tmp_win, int w, int h)
{
XWindowChanges xwc;
unsigned long xwcm;
int i;
int buttons = 0;
int ww = tmp_win->frame_g.width - 2 * tmp_win->boundary_width;
int rest = 0;
int bw;
if (HAS_BOTTOM_TITLE(tmp_win))
{
tmp_win->title_g.y =
h - tmp_win->boundary_width - tmp_win->title_g.height;
tmp_win->title_top_height = 0;
}
else
{
tmp_win->title_g.y = tmp_win->boundary_width;
tmp_win->title_top_height = tmp_win->title_g.height;
}
tmp_win->title_g.x = tmp_win->boundary_width;
xwcm = CWX | CWY | CWHeight | CWWidth;
xwc.y = tmp_win->title_g.y;
xwc.width = tmp_win->title_g.height;
xwc.height = tmp_win->title_g.height;
for (i = 0; i < NUMBER_OF_BUTTONS; i++)
{
if (tmp_win->button_w[i])
buttons++;
}
ww = tmp_win->frame_g.width - 2 * tmp_win->boundary_width -
tmp_win->title_g.width;
if (ww < buttons * xwc.width)
{
xwc.width = ww / buttons;
if (xwc.width < 1)
xwc.width = 1;
if (xwc.width > tmp_win->title_g.height)
xwc.width = tmp_win->title_g.height;
rest = ww - buttons * xwc.width;
if (rest > 0)
xwc.width++;
}
/* left */
xwc.x = tmp_win->boundary_width;
for (i = 0; i < NUMBER_OF_BUTTONS; i += 2)
{
if (tmp_win->button_w[i] != None)
{
if (xwc.x + xwc.width < w - tmp_win->boundary_width)
{
XConfigureWindow(dpy, tmp_win->button_w[i], xwcm, &xwc);
xwc.x += xwc.width;
tmp_win->title_g.x += xwc.width;
}
else
{
xwc.x = -tmp_win->title_g.height;
XConfigureWindow(dpy, tmp_win->button_w[i], xwcm, &xwc);
}
rest--;
if (rest == 0)
{
xwc.width--;
}
}
}
bw = xwc.width;
/* title */
if (tmp_win->title_g.width <= 0 || ww < 0)
tmp_win->title_g.x = -10;
xwc.x = tmp_win->title_g.x;
xwc.width = tmp_win->title_g.width;
XConfigureWindow(dpy, tmp_win->title_w, xwcm, &xwc);
/* right */
xwc.width = bw;
xwc.x = w - tmp_win->boundary_width - xwc.width;
for (i = 1 ; i < NUMBER_OF_BUTTONS; i += 2)
{
if (tmp_win->button_w[i] != None)
{
if (xwc.x > tmp_win->boundary_width)
{
XConfigureWindow(dpy, tmp_win->button_w[i], xwcm, &xwc);
xwc.x -= xwc.width;
}
else
{
xwc.x = -tmp_win->title_g.height;
XConfigureWindow(dpy, tmp_win->button_w[i], xwcm, &xwc);
}
rest--;
if (rest == 0)
{
xwc.width--;
xwc.x++;
}
}
}
return;
}
static void get_common_decorations(
common_decorations_type *cd, FvwmWindow *t, draw_window_parts draw_parts,
Bool has_focus, int force, Window expose_win, Bool is_border,
Bool do_change_gcs)
{
color_quad *draw_colors;
if (has_focus)
{
/* are we using textured borders? */
if (DFS_FACE_TYPE(
GetDecor(t, BorderStyle.active.style)) == TiledPixmapButton)
{
cd->texture_pixmap = GetDecor(t, BorderStyle.active.u.p->picture);
}
cd->back_pixmap= Scr.gray_pixmap;
if (is_border)
draw_colors = &(t->border_hicolors);
else
draw_colors = &(t->hicolors);
}
else
{
if (DFS_FACE_TYPE(GetDecor(t, BorderStyle.inactive.style)) ==
TiledPixmapButton)
{
cd->texture_pixmap = GetDecor(t, BorderStyle.inactive.u.p->picture);
}
if(IS_STICKY(t))
cd->back_pixmap = Scr.sticky_gray_pixmap;
else
cd->back_pixmap = Scr.light_gray_pixmap;
if (is_border)
draw_colors = &(t->border_colors);
else
draw_colors = &(t->colors);
}
cd->fore_color = draw_colors->fore;
cd->back_color = draw_colors->back;
if (do_change_gcs)
{
Globalgcv.foreground = draw_colors->hilight;
Globalgcm = GCForeground;
XChangeGC(dpy, Scr.ScratchGC1, Globalgcm, &Globalgcv);
cd->relief_gc = Scr.ScratchGC1;
Globalgcv.foreground = draw_colors->shadow;
XChangeGC(dpy, Scr.ScratchGC2, Globalgcm, &Globalgcv);
cd->shadow_gc = Scr.ScratchGC2;
}
/* MWMBorder style means thin 3d effects */
cd->relief_width = (HAS_MWM_BORDER(t) ? 1 : 2);
if (cd->texture_pixmap)
{
cd->attributes.background_pixmap = cd->texture_pixmap;
cd->valuemask = CWBackPixmap;
if (Pdepth < 2)
{
cd->notex_attributes.background_pixmap = cd->back_pixmap;
cd->notex_valuemask = CWBackPixmap;
}
else
{
cd->notex_attributes.background_pixel = cd->back_color;
cd->notex_valuemask = CWBackPixel;
}
}
else
{
if (Pdepth < 2)
{
cd->attributes.background_pixmap = cd->back_pixmap;
cd->valuemask = CWBackPixmap;
cd->notex_attributes.background_pixmap = cd->back_pixmap;
cd->notex_valuemask = CWBackPixmap;
}
else
{
cd->attributes.background_pixel = cd->back_color;
cd->valuemask = CWBackPixel;
cd->notex_attributes.background_pixel = cd->back_color;
cd->notex_valuemask = CWBackPixel;
}
}
}
void draw_clipped_decorations(
FvwmWindow *t, draw_window_parts draw_parts, Bool has_focus, int force,
Window expose_win, XRectangle *rclip)
{
common_decorations_type cd;
Bool is_frame_redraw_allowed = False;
Bool is_button_redraw_allowed = False;
Bool is_title_redraw_allowed = False;
Bool do_redraw_title = False;
Bool do_redraw_buttons = False;
Bool do_change_gcs = False;
if (!t)
return;
memset(&cd, 0, sizeof(cd));
if (force || expose_win == None)
{
is_frame_redraw_allowed = True;
is_button_redraw_allowed = True;
is_title_redraw_allowed = True;
}
else if (expose_win == t->title_w)
is_title_redraw_allowed = True;
else if (expose_win == t->frame || expose_win == t->decor_w)
/* sides and corners are InputOnly and incapable of triggering Expose events
*/
{
is_frame_redraw_allowed = True;
}
else if (expose_win != None)
is_button_redraw_allowed = True;
if (has_focus)
{
/* don't re-draw just for kicks */
if(!force && Scr.Hilite == t)
{
is_frame_redraw_allowed = False;
is_button_redraw_allowed = False;
is_title_redraw_allowed = False;
}
else
{
if (Scr.Hilite != t || force > 1)
cd.flags.has_color_changed = True;
if (Scr.Hilite != t && Scr.Hilite != NULL)
{
/* make sure that the previously highlighted window got
* unhighlighted */
DrawDecorations(Scr.Hilite, DRAW_ALL, False, True, None);
}
Scr.Hilite = t;
}
}
else
{
/* don't re-draw just for kicks */
if (!force && Scr.Hilite != t)
{
is_frame_redraw_allowed = False;
is_button_redraw_allowed = False;
is_title_redraw_allowed = False;
}
else if (Scr.Hilite == t || force > 1)
{
cd.flags.has_color_changed = True;
Scr.Hilite = NULL;
}
}
if(IS_ICONIFIED(t))
{
DrawIconWindow(t);
return;
}
/* calculate some values and flags */
if ((draw_parts & DRAW_TITLE) && HAS_TITLE(t) && is_title_redraw_allowed)
{
do_redraw_title = True;
do_change_gcs = True;
}
if ((draw_parts & DRAW_BUTTONS) && HAS_TITLE(t) && is_button_redraw_allowed)
{
do_redraw_buttons = True;
do_change_gcs = True;
}
get_common_decorations(
&cd, t, draw_parts, has_focus, force, expose_win, False, do_change_gcs);
/* redraw */
if (cd.flags.has_color_changed && HAS_TITLE(t))
{
if (!do_redraw_title || !rclip)
{
if (t->title_background_pixmap == None || force)
{
change_window_background(
t->title_w, cd.notex_valuemask, &cd.notex_attributes);
t->title_background_pixmap = None;
}
}
}
if (do_redraw_buttons)
{
RedrawButtons(&cd, t, has_focus, force, expose_win, rclip);
}
if (do_redraw_title)
{
RedrawTitle(&cd, t, has_focus, rclip);
}
if (cd.flags.has_color_changed ||
((draw_parts & DRAW_FRAME) && (is_frame_redraw_allowed)))
{
get_common_decorations(
&cd, t, draw_parts, has_focus, force, expose_win, True, True);
if (!rclip || !IS_WINDOW_BORDER_DRAWN(t))
{
change_window_background(t->decor_w, cd.valuemask, &cd.attributes);
SET_WINDOW_BORDER_DRAWN(t, 1);
rclip = NULL;
}
RedrawBorder(&cd, t, has_focus, force, expose_win, rclip);
}
/* Sync to make the change look fast! */
XSync(dpy,0);
}
void DrawDecorations(
FvwmWindow *t, draw_window_parts draw_parts, Bool has_focus, int force,
Window expose_win)
{
draw_clipped_decorations(t, draw_parts, has_focus, force, expose_win, NULL);
return;
}
/***********************************************************************
*
* Procedure:
* SetupFrame - set window sizes
*
* Inputs:
* tmp_win - the FvwmWindow pointer
* x - the x coordinate of the upper-left outer corner of the frame
* y - the y coordinate of the upper-left outer corner of the frame
* w - the width of the frame window w/o border
* h - the height of the frame window w/o border
*
* Special Considerations:
* This routine will check to make sure the window is not completely
* off the display, if it is, it'll bring some of it back on.
*
* The tmp_win->frame_XXX variables should NOT be updated with the
* values of x,y,w,h prior to calling this routine, since the new
* values are compared against the old to see whether a synthetic
* ConfigureNotify event should be sent. (It should be sent if the
* window was moved but not resized.)
*
************************************************************************/
void SetupFrame(
FvwmWindow *tmp_win, int x, int y, int w, int h, Bool sendEvent)
{
XWindowChanges xwc;
unsigned long xwcm;
int i;
Bool is_resized = False;
Bool is_moved = False;
int xwidth;
int ywidth;
int left;
int right;
int shaded = IS_SHADED(tmp_win);
int xmove_sign;
int ymove_sign;
int xsize_sign;
int ysize_sign;
int i0 = 0;
int i1 = 0;
int id = 0;
int dx;
int dy;
int px;
int py;
int decor_gravity;
Bool is_order_reversed = False;
#ifdef FVWM_DEBUG_MSGS
fvwm_msg(DBG,"SetupFrame",
"Routine Entered (x == %d, y == %d, w == %d, h == %d)",
x, y, w, h);
#endif
if (w < 1)
{
w = 1;
}
if (h < 1)
{
h = 1;
}
if (w != tmp_win->frame_g.width || h != tmp_win->frame_g.height)
is_resized = True;
if (x != tmp_win->frame_g.x || y != tmp_win->frame_g.y)
is_moved = True;
/*
* According to the July 27, 1988 ICCCM draft, we should send a
* "synthetic" ConfigureNotify event to the client if the window
* was moved but not resized.
*/
if (is_moved && !is_resized)
sendEvent = True;
/* optimization code for opaque resize */
#define SIGN(x) (((x) < 0) ? -1 : !!(x))
xmove_sign = SIGN(x - tmp_win->frame_g.x);
ymove_sign = SIGN(y - tmp_win->frame_g.y);
xsize_sign = SIGN(w - tmp_win->frame_g.width);
ysize_sign = SIGN(h - tmp_win->frame_g.height);
#undef SIGN
if (HAS_BOTTOM_TITLE(tmp_win))
{
decor_gravity = SouthEastGravity;
}
else
{
decor_gravity = NorthWestGravity;
}
if (xsize_sign != 0 || ysize_sign != 0)
{
if (ysize_sign == 0 || xsize_sign > 0)
is_order_reversed = (xsize_sign >= 0);
else
is_order_reversed = (ysize_sign >= 0);
}
else
{
is_order_reversed = False;
}
/*
* Set up the title geometry first
*/
if (is_resized)
{
left = tmp_win->nr_left_buttons;
right = tmp_win->nr_right_buttons;
tmp_win->title_g.width = w - (left + right) * tmp_win->title_g.height
- 2 * tmp_win->boundary_width;
if(tmp_win->title_g.width < 1)
tmp_win->title_g.width = 1;
}
/*
* Now set up the client, the parent and the frame window
*/
set_decor_gravity(
tmp_win, decor_gravity, NorthWestGravity, NorthWestGravity);
tmp_win->attr.width = w - 2 * tmp_win->boundary_width;
tmp_win->attr.height =
h - tmp_win->title_g.height - 2*tmp_win->boundary_width;
if (tmp_win->attr.height <= 1 || tmp_win->attr.width <= 1)
is_order_reversed = 0;
px = tmp_win->boundary_width;
py = tmp_win->boundary_width + tmp_win->title_top_height;
dx = 0;
dy = 0;
if (is_order_reversed && decor_gravity == SouthEastGravity)
{
dx = tmp_win->frame_g.width - w;
dy = tmp_win->frame_g.height - h;
}
if (is_order_reversed)
{
i0 = 3;
i1 = -1;
id = -1;
}
else
{
i0 = 0;
i1 = 4;
id = 1;
}
for (i = i0; i != i1; i += id)
{
switch(i)
{
case 0:
XMoveResizeWindow(dpy, tmp_win->frame, x, y, w, h);
break;
case 1:
if (!shaded)
{
XMoveResizeWindow(
dpy, tmp_win->Parent, px, py, max(tmp_win->attr.width, 1),
max(tmp_win->attr.height, 1));
}
else
{
XMoveWindow(dpy, tmp_win->Parent, px, py);
}
break;
case 2:
if (!shaded)
{
XResizeWindow(
dpy, tmp_win->w, max(tmp_win->attr.width, 1),
max(tmp_win->attr.height, 1));
/* This reduces flickering */
XSync(dpy, 0);
}
else if (HAS_BOTTOM_TITLE(tmp_win))
{
rectangle big_g;
big_g = (IS_MAXIMIZED(tmp_win)) ? tmp_win->max_g : tmp_win->normal_g;
get_relative_geometry(&big_g, &big_g);
XMoveWindow(dpy, tmp_win->w, 0, 1 - big_g.height +
2 * tmp_win->boundary_width + tmp_win->title_g.height);
}
else
{
XMoveWindow(dpy, tmp_win->w, 0, 0);
}
break;
case 3:
XMoveResizeWindow(dpy, tmp_win->decor_w, dx, dy, w, h);
break;
}
}
set_decor_gravity(
tmp_win, NorthWestGravity, NorthWestGravity, NorthWestGravity);
XMoveWindow(dpy, tmp_win->decor_w, 0, 0);
tmp_win->frame_g.x = x;
tmp_win->frame_g.y = y;
tmp_win->frame_g.width = w;
tmp_win->frame_g.height = h;
/*
* Set up the decoration windows
*/
if (is_resized)
{
if (HAS_TITLE(tmp_win))
{
SetupTitleBar(tmp_win, w, h);
}
if(HAS_BORDER(tmp_win))
{
int add;
tmp_win->visual_corner_width = tmp_win->corner_width;
if(w < 2*tmp_win->corner_width)
tmp_win->visual_corner_width = w / 3;
if (h < 2*tmp_win->corner_width && !shaded)
tmp_win->visual_corner_width = h / 3;
xwidth = w - 2 * tmp_win->visual_corner_width;
ywidth = h - 2 * tmp_win->visual_corner_width;
xwcm = CWWidth | CWHeight | CWX | CWY;
if(xwidth<2)
xwidth = 2;
if(ywidth<2)
ywidth = 2;
if (IS_SHADED(tmp_win))
add = tmp_win->visual_corner_width / 3;
else
add = 0;
for(i = 0; i < 4; i++)
{
if(i==0)
{
xwc.x = tmp_win->visual_corner_width;
xwc.y = 0;
xwc.height = tmp_win->boundary_width;
xwc.width = xwidth;
}
else if (i==1)
{
xwc.x = w - tmp_win->boundary_width;
xwc.y = tmp_win->visual_corner_width;
if (IS_SHADED(tmp_win))
{
xwc.y /= 3;
xwc.height = add + 2;
}
else
{
xwc.height = ywidth;
}
xwc.width = tmp_win->boundary_width;
}
else if(i==2)
{
xwc.x = tmp_win->visual_corner_width;
xwc.y = h - tmp_win->boundary_width;
xwc.height = tmp_win->boundary_width;
xwc.width = xwidth;
}
else
{
xwc.x = 0;
xwc.y = tmp_win->visual_corner_width;
if (IS_SHADED(tmp_win))
{
xwc.y /= 3;
xwc.height = add + 2;
}
else
{
xwc.height = ywidth;
}
xwc.width = tmp_win->boundary_width;
}
XConfigureWindow(dpy, tmp_win->sides[i], xwcm, &xwc);
}
xwcm = CWX|CWY|CWWidth|CWHeight;
xwc.width = tmp_win->visual_corner_width;
xwc.height = tmp_win->visual_corner_width;
for(i = 0; i < 4; i++)
{
if (i & 0x1)
xwc.x = w - tmp_win->visual_corner_width;
else
xwc.x = 0;
if (i & 0x2)
xwc.y = h - tmp_win->visual_corner_width + add;
else
xwc.y = -add;
XConfigureWindow(dpy, tmp_win->corners[i], xwcm, &xwc);
}
}
}
/*
* Set up window shape
*/
if (FShapesSupported && tmp_win->wShaped && is_resized)
{
SetShape(tmp_win, w);
}
/*
* Send ConfigureNotify
*/
XSync(dpy,0);
/* must not send events to shaded windows because this might cause them to
* look at their current geometry */
if (sendEvent && !shaded)
{
SendConfigureNotify(tmp_win, x, y, w, h, 0, True);
#ifdef FVWM_DEBUG_MSGS
fvwm_msg(DBG,"SetupFrame","Sent ConfigureNotify (w == %d, h == %d)",
w,h);
#endif
}
XSync(dpy,0);
BroadcastConfig(M_CONFIGURE_WINDOW,tmp_win);
#if 0
fprintf(stderr,"sf: window '%s'\n", tmp_win->name);
fprintf(stderr," frame: %5d %5d, %4d x %4d\n", tmp_win->frame_g.x, tmp_win->frame_g.y, tmp_win->frame_g.width, tmp_win->frame_g.height);
fprintf(stderr," normal: %5d %5d, %4d x %4d\n", tmp_win->normal_g.x, tmp_win->normal_g.y, tmp_win->normal_g.width, tmp_win->normal_g.height);
if (IS_MAXIMIZED(tmp_win))
fprintf(stderr," max: %5d %5d, %4d x %4d, %5d %5d\n", tmp_win->max_g.x, tmp_win->max_g.y, tmp_win->max_g.width, tmp_win->max_g.height, tmp_win->max_offset.x, tmp_win->max_offset.y);
#endif
}
void ForceSetupFrame(
FvwmWindow *tmp_win, int x, int y, int w, int h, Bool sendEvent)
{
tmp_win->frame_g.x = x + 1;
tmp_win->frame_g.y = y + 1;
tmp_win->frame_g.width = 0;
tmp_win->frame_g.height = 0;
SetupFrame(tmp_win, x, y, w, h, sendEvent);
}
void set_decor_gravity(
FvwmWindow *tmp_win, int gravity, int parent_gravity, int client_gravity)
{
int valuemask = CWWinGravity;
XSetWindowAttributes xcwa;
int i;
xcwa.win_gravity = client_gravity;
XChangeWindowAttributes(dpy, tmp_win->w, valuemask, &xcwa);
xcwa.win_gravity = parent_gravity;
XChangeWindowAttributes(dpy, tmp_win->Parent, valuemask, &xcwa);
xcwa.win_gravity = gravity;
XChangeWindowAttributes(dpy, tmp_win->decor_w, valuemask, &xcwa);
if (HAS_TITLE(tmp_win))
{
int left_gravity;
int right_gravity;
if (gravity == StaticGravity || gravity == ForgetGravity)
{
left_gravity = gravity;
right_gravity = gravity;
}
else
{
/* West...Gravity */
left_gravity = gravity - ((gravity - 1) % 3);
/* East...Gravity */
right_gravity = left_gravity + 2;
}
xcwa.win_gravity = left_gravity;
XChangeWindowAttributes(dpy, tmp_win->title_w, valuemask, &xcwa);
for (i = 0; i < NUMBER_OF_BUTTONS; i++)
{
if (i == NR_LEFT_BUTTONS)
xcwa.win_gravity = right_gravity;
if (tmp_win->button_w[i])
XChangeWindowAttributes(dpy, tmp_win->button_w[i], valuemask, &xcwa);
}
}
}
/****************************************************************************
*
* Sets up the shaped window borders
*
****************************************************************************/
void SetShape(FvwmWindow *tmp_win, int w)
{
if (FShapesSupported)
{
if (tmp_win->wShaped)
{
XRectangle rect;
FShapeCombineShape(
dpy, tmp_win->frame, FShapeBounding, tmp_win->boundary_width,
tmp_win->title_top_height + tmp_win->boundary_width, tmp_win->w,
FShapeBounding, FShapeSet);
if (tmp_win->title_w)
{
/* windows w/ titles */
rect.x = tmp_win->boundary_width;
rect.y = tmp_win->title_g.y;
rect.width = w - 2*tmp_win->boundary_width;
rect.height = tmp_win->title_g.height;
FShapeCombineRectangles(
dpy, tmp_win->frame, FShapeBounding, 0, 0, &rect, 1, FShapeUnion,
Unsorted);
}
}
else
{
/* unset shape */
FShapeCombineMask(
dpy, tmp_win->frame, FShapeBounding, 0, 0, None, FShapeSet);
}
}
}
/****************************************************************************
*
* Sets the allowed button states
*
****************************************************************************/
void CMD_ButtonState(F_CMD_ARGS)
{
char *token;
while ((token = PeekToken(action, &action)))
{
static char first = True;
if (!token && first)
{
Scr.gs.use_active_down_buttons = DEFAULT_USE_ACTIVE_DOWN_BUTTONS;
Scr.gs.use_inactive_buttons = DEFAULT_USE_INACTIVE_BUTTONS;
return;
}
first = False;
if (StrEquals("activedown", token))
{
Scr.gs.use_active_down_buttons =
ParseToggleArgument(action, &action, DEFAULT_USE_ACTIVE_DOWN_BUTTONS,
True);
}
else if (StrEquals("inactive", token))
{
Scr.gs.use_inactive_buttons =
ParseToggleArgument(action, &action, DEFAULT_USE_ACTIVE_DOWN_BUTTONS,
True);
}
else
{
Scr.gs.use_active_down_buttons = DEFAULT_USE_ACTIVE_DOWN_BUTTONS;
Scr.gs.use_inactive_buttons = DEFAULT_USE_INACTIVE_BUTTONS;
fvwm_msg(ERR, "cmd_button_state", "unknown button state %s\n", token);
return;
}
}
return;
}
/****************************************************************************
*
* Sets the border style ([EMAIL PROTECTED])
*
****************************************************************************/
void CMD_BorderStyle(F_CMD_ARGS)
{
char *parm;
char *prev;
#ifdef USEDECOR
FvwmDecor *decor = Scr.cur_decor ? Scr.cur_decor : &Scr.DefaultDecor;
#else
FvwmDecor *decor = &Scr.DefaultDecor;
#endif
Scr.flags.do_need_window_update = 1;
decor->flags.has_changed = 1;
for (prev = action; (parm = PeekToken(action, &action)); prev = action)
{
if (StrEquals(parm, "active") || StrEquals(parm, "inactive"))
{
int len;
char *end, *tmp;
DecorFace tmpdf, *df;
memset(&tmpdf.style, 0, sizeof(tmpdf.style));
DFS_FACE_TYPE(tmpdf.style) = SimpleButton;
#ifdef MULTISTYLE
tmpdf.next = NULL;
#endif
#ifdef MINI_ICONS
tmpdf.u.p = NULL;
#endif
if (StrEquals(parm,"active"))
df = &decor->BorderStyle.active;
else
df = &decor->BorderStyle.inactive;
df->flags.has_changed = 1;
while (isspace(*action))
++action;
if (*action != '(')
{
if (!*action)
{
fvwm_msg(ERR,"SetBorderStyle",
"error in %s border specification", parm);
return;
}
while (isspace(*action))
++action;
if (ReadDecorFace(action, &tmpdf,-1,True))
{
FreeDecorFace(dpy, df);
*df = tmpdf;
}
break;
}
end = strchr(++action, ')');
if (!end)
{
fvwm_msg(ERR,"SetBorderStyle",
"error in %s border specification", parm);
return;
}
len = end - action + 1;
tmp = safemalloc(len);
strncpy(tmp, action, len - 1);
tmp[len - 1] = 0;
ReadDecorFace(tmp, df,-1,True);
free(tmp);
action = end + 1;
}
else if (strcmp(parm,"--")==0)
{
if (ReadDecorFace(prev, &decor->BorderStyle.active,-1,True))
ReadDecorFace(prev, &decor->BorderStyle.inactive,-1,False);
decor->BorderStyle.active.flags.has_changed = 1;
decor->BorderStyle.inactive.flags.has_changed = 1;
break;
}
else
{
DecorFace tmpdf;
memset(&tmpdf.style, 0, sizeof(tmpdf.style));
DFS_FACE_TYPE(tmpdf.style) = SimpleButton;
#ifdef MULTISTYLE
tmpdf.next = NULL;
#endif
#ifdef MINI_ICONS
tmpdf.u.p = NULL;
#endif
if (ReadDecorFace(prev, &tmpdf,-1,True))
{
FreeDecorFace(dpy,&decor->BorderStyle.active);
decor->BorderStyle.active = tmpdf;
ReadDecorFace(prev, &decor->BorderStyle.inactive,-1,False);
decor->BorderStyle.active.flags.has_changed = 1;
decor->BorderStyle.inactive.flags.has_changed = 1;
}
break;
}
}
}