/* 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 #include /* for STDIN_FILENO */ #include /* for open */ #include /* for open */ #include /* for open */ #include #include #include #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 * * 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 HilightFore ' and" " 'Style HilightBack ' 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 HilightColorset ' 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 IconFont ' 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 Font ' 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 * " */ 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 */