/* 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 <unistd.h> /* for STDIN_FILENO */
#include <sys/types.h> /* for open */
#include <sys/stat.h> /* for open */
#include <fcntl.h> /* for open */
#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 (veliaa@rpi.edu)
*
* Args: 1 -- shade, 2 -- unshade No Arg: toggle
*
***********************************************************************
*
* Animation added.
* Based on code from AfterStep-1.0 (CT: ctibirna@gch.ulaval.ca)
* Additional fixes by Tomas Ogren <stric@ing.umu.se>
*
* 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 */
{
/* This is for fixing a problem with rox filer */
int fd;
fd = open("/dev/null", O_RDONLY, 0);
dup2(fd,STDIN_FILENO);
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, "CMD_HilightColor",
"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, "CMD_HilightColorset",
"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 (veliaa@rpi.edu)
*
****************************************************************************/
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;
/* migo (19/May/2002): this block also fixes a crash with new configs */
char *ptr;
for (ptr = action; *ptr; ptr++)
{
if (*ptr != ' ' && !isxdigit(*ptr))
{
/* suppose it is a new Colorset command and pass it to FvwmTheme */
char *old_action = action;
action = CatString2("FvwmTheme Colorset ", action);
CMD_SendToModule(F_PASS_ARGS);
action = old_action;
return;
}
}
token = PeekToken(action, NULL);
if (token == NULL)
return;
ret = sscanf(token, "%x", &n);
if ((ret == 0) || (n < 0))
return;
/* add a check to avoid crash in intentionally-incorect-config */
if (LoadColorset(action) < 0)
return;
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.x)
free (df->u.vector.x);
if (df->u.vector.y)
free (df->u.vector.y);
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 (veliaa@rpi.edu)
*
****************************************************************************/
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, "%255s%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, 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,"%dx%d@%d%n",&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;
}
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 (veliaa@rpi.edu)
*
****************************************************************************/
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], button, !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 (veliaa@rpi.edu)
*
****************************************************************************/
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 (veliaa@rpi.edu)
*
****************************************************************************/
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 (veliaa@rpi.edu)
*
****************************************************************************/
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 (veliaa@rpi.edu)
*
****************************************************************************/
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 (veliaa@rpi.edu)
*
****************************************************************************/
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 veliaa@rpi.edu)
*
****************************************************************************/
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 (veliaa@rpi.edu)
*
****************************************************************************/
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, ignore */
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->xmotion.same_screen == False)
{
continue;
}
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 */
syntax highlighted by Code2HTML, v. 0.9.1