/* -*-c-*- */ /* This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* ---------------------------- included header files ---------------------- */ #include "config.h" #include #include #include "libs/fvwmlib.h" #include "libs/FScreen.h" #include "libs/FGettext.h" #include "libs/Grab.h" #include "libs/Parse.h" #include "fvwm.h" #include "externs.h" #include "execcontext.h" #include "cursor.h" #include "events.h" #include "eventmask.h" #include "misc.h" #include "screen.h" #include "virtual.h" #include "module_interface.h" #include "module_list.h" #include "focus.h" #include "gnome.h" #include "ewmh.h" #include "move_resize.h" #include "borders.h" #include "frame.h" #include "geometry.h" #include "icons.h" #include "stack.h" #include "functions.h" /* ---------------------------- local definitions -------------------------- */ /* ---------------------------- local macros ------------------------------- */ /* ---------------------------- imports ------------------------------------ */ /* ---------------------------- included code files ------------------------ */ /* ---------------------------- local types -------------------------------- */ /* ---------------------------- forward declarations ----------------------- */ /* ---------------------------- local variables ---------------------------- */ /* * dje 12/19/98 * * Testing with edge_thickness of 1 showed that some XServers don't * care for 1 pixel windows, causing EdgeScrolling not to work with some * servers. One bug report was for SUNOS 4.1.3_U1. We didn't find out * the exact cause of the problem. Perhaps no enter/leave events were * generated. * * Allowed: 0,1,or 2 pixel pan frames. * * 0 completely disables mouse edge scrolling, even while dragging a * window. * * 1 gives the smallest pan frames, which seem to work best except on * some servers. * * 2 is the default. */ static int edge_thickness = 2; static int last_edge_thickness = 2; static int prev_page_x = 0; static int prev_page_y = 0; static int prev_desk = 0; static int prev_desk_and_page_desk = 0; static int prev_desk_and_page_page_x = 0; static int prev_desk_and_page_page_y = 0; /* ---------------------------- exported variables (globals) --------------- */ /* ---------------------------- local functions ---------------------------- */ static void __drag_viewport(const exec_context_t *exc, int scroll_speed) { XEvent e; int x; int y; unsigned int button_mask = 0; Bool is_finished = False; if (!GrabEm(CRS_MOVE, GRAB_NORMAL)) { XBell(dpy, 0); return; } if (FQueryPointer( dpy, Scr.Root, &JunkRoot, &JunkChild, &x, &y, &JunkX, &JunkY, &button_mask) == False) { /* pointer is on a different screen */ /* Is this the best thing to do? */ UngrabEm(GRAB_NORMAL); return; } MyXGrabKeyboard(dpy); button_mask &= DEFAULT_ALL_BUTTONS_MASK; memset(&e, 0, sizeof(e)); while (!is_finished) { int old_x; int old_y; old_x = x; old_y = y; FMaskEvent( dpy, ButtonPressMask | ButtonReleaseMask | KeyPressMask | PointerMotionMask | ButtonMotionMask | ExposureMask | EnterWindowMask | LeaveWindowMask, &e); /* discard extra events before a logical release */ if (e.type == MotionNotify || e.type == EnterNotify || e.type == LeaveNotify) { while (FPending(dpy) > 0 && FCheckMaskEvent( dpy, ButtonMotionMask | PointerMotionMask | ButtonPressMask | ButtonRelease | KeyPressMask | EnterWindowMask | LeaveWindowMask, &e)) { if (e.type == ButtonPress || e.type == ButtonRelease || e.type == KeyPress) { break; } } } if (e.type == EnterNotify || e.type == LeaveNotify) { XEvent e2; int px; int py; /* Query the pointer to catch the latest information. * This *is* necessary. */ if (FQueryPointer( dpy, Scr.Root, &JunkRoot, &JunkChild, &px, &py, &JunkX, &JunkY, &JunkMask) == True) { fev_make_null_event(&e2, dpy); e2.type = MotionNotify; e2.xmotion.time = fev_get_evtime(); e2.xmotion.x_root = px; e2.xmotion.y_root = py; e2.xmotion.state = JunkMask; e2.xmotion.same_screen = True; e = e2; fev_fake_event(&e); } else { /* pointer is on a different screen, * ignore event */ } } /* Handle a limited number of key press events to allow * mouseless operation */ if (e.type == KeyPress) { Keyboard_shortcuts( &e, NULL, NULL, NULL, ButtonRelease); } switch (e.type) { case KeyPress: /* simple code to bag out of move - CKH */ if (XLookupKeysym(&(e.xkey), 0) == XK_Escape) { is_finished = True; } break; case ButtonPress: if (e.xbutton.button <= NUMBER_OF_MOUSE_BUTTONS && ((Button1Mask << (e.xbutton.button - 1)) & button_mask)) { /* No new button was pressed, just a delayed * event */ break; } /* fall through */ case ButtonRelease: x = e.xbutton.x_root; y = e.xbutton.y_root; is_finished = True; break; case MotionNotify: if (e.xmotion.same_screen == False) { continue; } x = e.xmotion.x_root; y = e.xmotion.y_root; break; case Expose: dispatch_event(&e); break; default: /* cannot happen */ break; } /* switch */ if (x != old_x || y != old_y) { MoveViewport( Scr.Vx + scroll_speed * (x - old_x), Scr.Vy + scroll_speed * (y - old_y), False); FlushAllMessageQueues(); } } /* while*/ UngrabEm(GRAB_NORMAL); MyXUngrabKeyboard(dpy); WaitForButtonsUp(True); } /* * * Parse arguments for "Desk" and "MoveToDesk" (formerly "WindowsDesk"): * * (nil) : desk number = current desk * n : desk number = current desk + n * 0 n : desk number = n * n x : desk number = current desk + n * 0 n min max : desk number = n, but limit to min/max * n min max : desk number = current desk + n, but wrap around at desk #min * or desk #max * n x min max : desk number = current desk + n, but wrap around at desk #min * or desk #max * * The current desk number is returned if not enough parameters could be * read (or if action is empty). * */ static int GetDeskNumber(char *action, int current_desk) { int n; int m; int is_relative; int desk; int val[4]; int min, max; if (MatchToken(action, "prev")) { return prev_desk; } n = GetIntegerArguments(action, NULL, &(val[0]), 4); if (n <= 0) { return Scr.CurrentDesk; } if (n == 1) { return current_desk + val[0]; } desk = current_desk; m = 0; if (val[0] == 0) { /* absolute desk number */ desk = val[1]; is_relative = 0; } else { /* relative desk number */ desk += val[0]; is_relative = 1; } if (n == 3) { m = 1; } if (n == 4) { m = 2; } if (n > 2) { /* handle limits */ if (val[m] <= val[m+1]) { min = val[m]; max = val[m+1]; } else { /* min > max is nonsense, so swap 'em. */ min = val[m+1]; max = val[m]; } if (is_relative) { /* Relative moves wrap around. */ if (desk < min) { desk += (max - min + 1); } else if (desk > max) { desk -= (max - min + 1); } } else if (desk < min) { /* Relative move outside of range, wrap around. */ if (val[0] < 0) { desk = max; } else { desk = min; } } else if (desk > max) { /* Move outside of range, truncate. */ if (val[0] > 0) { desk = min; } else { desk = max; } } } return desk; } /* * * Unmaps a window on transition to a new desktop * */ static void unmap_window(FvwmWindow *t) { XWindowAttributes winattrs; unsigned long eventMask = 0; Status ret; /* * Prevent the receipt of an UnmapNotify, since that would * cause a transition to the Withdrawn state. */ ret = XGetWindowAttributes(dpy, FW_W(t), &winattrs); if (ret) { eventMask = winattrs.your_event_mask; /* suppress UnmapRequest event */ XSelectInput(dpy, FW_W(t), eventMask & ~StructureNotifyMask); } if (IS_ICONIFIED(t)) { if (FW_W_ICON_PIXMAP(t) != None) { XUnmapWindow(dpy,FW_W_ICON_PIXMAP(t)); } if (FW_W_ICON_TITLE(t) != None) { XUnmapWindow(dpy,FW_W_ICON_TITLE(t)); } } else { XUnmapWindow(dpy,FW_W_FRAME(t)); border_undraw_decorations(t); #ifdef ICCCM2_UNMAP_WINDOW_PATCH /* this is required by the ICCCM2 */ XUnmapWindow(dpy, FW_W(t)); #endif if (!Scr.bo.do_enable_ewmh_iconic_state_workaround) { SetMapStateProp(t, IconicState); } } if (ret) { XSelectInput(dpy, FW_W(t), eventMask); XFlush(dpy); } return; } /* * * Maps a window on transition to a new desktop * */ static void map_window(FvwmWindow *t) { XWindowAttributes winattrs; unsigned long eventMask = 0; Status ret; if (IS_SCHEDULED_FOR_DESTROY(t)) { return; } /* * Prevent the receipt of an UnmapNotify, since that would * cause a transition to the Withdrawn state. */ ret = XGetWindowAttributes(dpy, FW_W(t), &winattrs); if (ret) { eventMask = winattrs.your_event_mask; /* suppress MapRequest event */ XSelectInput(dpy, FW_W(t), eventMask & ~StructureNotifyMask); } if (IS_ICONIFIED(t)) { if (FW_W_ICON_PIXMAP(t) != None) { XMapWindow(dpy,FW_W_ICON_PIXMAP(t)); } if (FW_W_ICON_TITLE(t) != None) { XMapWindow(dpy,FW_W_ICON_TITLE(t)); } } else if (IS_MAPPED(t)) { border_draw_decorations( t, PART_ALL, (t == get_focus_window()) ? True : False, False, CLEAR_ALL, NULL, NULL); XMapWindow(dpy, FW_W_FRAME(t)); XMapWindow(dpy, FW_W_PARENT(t)); XMapSubwindows(dpy, FW_W_FRAME(t)); #ifdef ICCCM2_UNMAP_WINDOW_PATCH /* this is required by the ICCCM2 */ XMapWindow(dpy, FW_W(t)); #endif if (!Scr.bo.do_enable_ewmh_iconic_state_workaround) { SetMapStateProp(t, NormalState); } } if (ret) { XSelectInput(dpy, FW_W(t), eventMask); XFlush(dpy); } return; } /* * * Unmap all windows on a desk - * - Part 1 of a desktop switch * - must eventually be followed by a call to MapDesk * - unmaps from the bottom of the stack up * */ static void UnmapDesk(int desk, Bool grab) { FvwmWindow *t; FvwmWindow *sf = get_focus_window(); if (grab) { MyXGrabServer(dpy); } for (t = get_prev_window_in_stack_ring(&Scr.FvwmRoot); t != &Scr.FvwmRoot; t = get_prev_window_in_stack_ring(t)) { /* Only change mapping for non-sticky windows */ if (!is_window_sticky_across_desks(t) && !IS_ICON_UNMAPPED(t)) { if (t->Desk == desk) { if (sf == t) { t->flags.is_focused_on_other_desk = 1; t->FocusDesk = desk; DeleteFocus(True); } else { t->flags.is_focused_on_other_desk = 0; } unmap_window(t); SET_FULLY_VISIBLE(t, 0); SET_PARTIALLY_VISIBLE(t, 0); } } else { t->flags.is_focused_on_other_desk = 0; } } if (grab) { MyXUngrabServer(dpy); } return; } /* * * Map all windows on a desk - * - Part 2 of a desktop switch * - only use if UnmapDesk has previously been called * - maps from the top of the stack down * */ static void MapDesk(int desk, Bool grab) { FvwmWindow *t; FvwmWindow *FocusWin = NULL; FvwmWindow *StickyWin = NULL; FvwmWindow *sf = get_focus_window(); Scr.flags.is_map_desk_in_progress = 1; if (grab) { MyXGrabServer(dpy); } for (t = get_next_window_in_stack_ring(&Scr.FvwmRoot); t != &Scr.FvwmRoot; t = get_next_window_in_stack_ring(t)) { /* Only change mapping for non-sticky windows */ if (!is_window_sticky_across_desks(t) && !IS_ICON_UNMAPPED(t)) { if (t->Desk == desk) { map_window(t); } } else { /* If window is sticky, just update its desk (it's * still mapped). */ t->Desk = desk; GNOME_SetDesk(t); GNOME_SetWinArea(t); if (sf == t) { StickyWin = t; } } } if (grab) { MyXUngrabServer(dpy); } for (t = Scr.FvwmRoot.next; t != NULL; t = t->next) { /* Autoplace any sticky icons, so that sticky icons from the old desk don't land on top of stationary ones on the new desk. */ if (is_window_sticky_across_desks(t) && IS_ICONIFIED(t) && !IS_ICON_MOVED(t) && !IS_ICON_UNMAPPED(t)) { AutoPlaceIcon(t, NULL, True); } /* Keep track of the last-focused window on the new desk. */ if (t->flags.is_focused_on_other_desk && t->FocusDesk == desk) { t->flags.is_focused_on_other_desk = 0; FocusWin = t; } } /* If a sticky window has focus, don't disturb it. */ if (!StickyWin && FocusWin) { /* Otherwise, handle remembering the last-focused clicky * window. */ if (!FP_DO_UNFOCUS_LEAVE(FW_FOCUS_POLICY(FocusWin))) { ReturnFocusWindow(FocusWin); } else { DeleteFocus(True); } } Scr.flags.is_map_desk_in_progress = 0; return; } /* ---------------------------- interface functions ------------------------ */ /* * * Check to see if the pointer is on the edge of the screen, and scroll/page * if needed * * Returns * 0: no paging * 1: paging occured * -1: no need to call the function again before a new event arrives */ int HandlePaging( XEvent *pev, int HorWarpSize, int VertWarpSize, int *xl, int *yt, int *delta_x, int *delta_y, Bool Grab, Bool fLoop, Bool do_continue_previous, int delay) { static int add_time = 0; int x,y; XEvent e; static Time my_timestamp = 0; static Time my_last_timestamp = 0; static Bool is_timestamp_valid = False; static int last_x = 0; static int last_y = 0; static Bool is_last_position_valid = False; *delta_x = 0; *delta_y = 0; if (!is_timestamp_valid && do_continue_previous) { /* don't call me again until something has happened */ return -1; } if (!is_timestamp_valid && pev->type == MotionNotify) { x = pev->xmotion.x_root; y = pev->xmotion.y_root; if ((Scr.VxMax == 0 || (x >= edge_thickness && x < Scr.MyDisplayWidth - edge_thickness)) && (Scr.VyMax == 0 || (y >= edge_thickness && y < Scr.MyDisplayHeight - edge_thickness))) { return -1; } } if (delay < 0 || (HorWarpSize == 0 && VertWarpSize==0)) { is_timestamp_valid = False; add_time = 0; return 0; } if (Scr.VxMax == 0 && Scr.VyMax == 0) { is_timestamp_valid = False; add_time = 0; return 0; } if (!is_timestamp_valid) { is_timestamp_valid = True; my_timestamp = fev_get_evtime(); is_last_position_valid = False; add_time = 0; last_x = -1; last_y = -1; } else if (my_last_timestamp != fev_get_evtime()) { add_time = 0; } my_last_timestamp = fev_get_evtime(); do { int rc; Window JunkW; int JunkC; unsigned int JunkM; if (FCheckPeekIfEvent(dpy, &e, test_button_event, NULL)) { is_timestamp_valid = False; add_time = 0; return 0; } /* get pointer location */ rc = FQueryPointer( dpy, Scr.Root, &JunkW, &JunkW, &x, &y, &JunkC, &JunkC, &JunkM); if (rc == False) { /* pointer is on a different screen */ x = 0; y = 0; } /* check actual pointer location since PanFrames can get buried * under window being moved or resized - mab */ if (x >= edge_thickness && x < Scr.MyDisplayWidth-edge_thickness && y >= edge_thickness && y < Scr.MyDisplayHeight-edge_thickness) { is_timestamp_valid = False; add_time = 0; return 0; } if (!fLoop && is_last_position_valid && (x - last_x > MAX_PAGING_MOVE_DISTANCE || x - last_x < -MAX_PAGING_MOVE_DISTANCE || y - last_y > MAX_PAGING_MOVE_DISTANCE || y - last_y < -MAX_PAGING_MOVE_DISTANCE)) { /* The pointer is moving too fast, prevent paging until * it slows down. Don't prevent paging when fLoop is * set since we can't be sure that HandlePaging will be * called again. */ is_timestamp_valid = True; my_timestamp = fev_get_evtime(); add_time = 0; last_x = x; last_y = y; return 0; } last_x = x; last_y = y; is_last_position_valid = True; usleep(10000); add_time += 10; } while (fLoop && fev_get_evtime() - my_timestamp + add_time < delay); if (fev_get_evtime() - my_timestamp + add_time < delay) { return 0; } /* Get the latest pointer position. This is necessary as XFree 4.1.0.1 * sometimes does not report mouse movement when it should. */ if (FQueryPointer( dpy, Scr.Root, &JunkRoot, &JunkChild, &x, &y, &JunkX, &JunkY, &JunkMask) == False) { /* pointer is on a different screen - ignore */ } /* Move the viewport */ /* and/or move the cursor back to the approximate correct location */ /* that is, the same place on the virtual desktop that it */ /* started at */ if (x < edge_thickness) { *delta_x = -HorWarpSize; } else if (x >= Scr.MyDisplayWidth-edge_thickness) { *delta_x = HorWarpSize; } else { *delta_x = 0; } if (Scr.VxMax == 0) { *delta_x = 0; } if (y < edge_thickness) { *delta_y = -VertWarpSize; } else if (y >= Scr.MyDisplayHeight-edge_thickness) { *delta_y = VertWarpSize; } else { *delta_y = 0; } if (Scr.VyMax == 0) { *delta_y = 0; } /* Ouch! lots of bounds checking */ if (Scr.Vx + *delta_x < 0) { if (!(Scr.flags.do_edge_wrap_x)) { *delta_x = -Scr.Vx; *xl = x - *delta_x; } else { *delta_x += Scr.VxMax + Scr.MyDisplayWidth; *xl = x + *delta_x % Scr.MyDisplayWidth + HorWarpSize; } } else if (Scr.Vx + *delta_x > Scr.VxMax) { if (!(Scr.flags.do_edge_wrap_x)) { *delta_x = Scr.VxMax - Scr.Vx; *xl = x - *delta_x; } else { *delta_x -= Scr.VxMax +Scr.MyDisplayWidth; *xl = x + *delta_x % Scr.MyDisplayWidth - HorWarpSize; } } else { *xl = x - *delta_x; } if (Scr.Vy + *delta_y < 0) { if (!(Scr.flags.do_edge_wrap_y)) { *delta_y = -Scr.Vy; *yt = y - *delta_y; } else { *delta_y += Scr.VyMax + Scr.MyDisplayHeight; *yt = y + *delta_y % Scr.MyDisplayHeight + VertWarpSize; } } else if (Scr.Vy + *delta_y > Scr.VyMax) { if (!(Scr.flags.do_edge_wrap_y)) { *delta_y = Scr.VyMax - Scr.Vy; *yt = y - *delta_y; } else { *delta_y -= Scr.VyMax + Scr.MyDisplayHeight; *yt = y + *delta_y % Scr.MyDisplayHeight - VertWarpSize; } } else { *yt = y - *delta_y; } /* Check for paging -- and don't warp the pointer. */ is_timestamp_valid = False; add_time = 0; if (*delta_x == 0 && *delta_y == 0) { return 0; } /* make sure the pointer isn't warped into the panframes */ if (*xl < edge_thickness) { *xl = edge_thickness; } if (*yt < edge_thickness) { *yt = edge_thickness; } if (*xl >= Scr.MyDisplayWidth - edge_thickness) { *xl = Scr.MyDisplayWidth - edge_thickness -1; } if (*yt >= Scr.MyDisplayHeight - edge_thickness) { *yt = Scr.MyDisplayHeight - edge_thickness -1; } if (Grab) { MyXGrabServer(dpy); } /* Turn off the rubberband if its on */ switch_move_resize_grid(False); FWarpPointer(dpy,None,Scr.Root,0,0,0,0,*xl,*yt); MoveViewport(Scr.Vx + *delta_x,Scr.Vy + *delta_y,False); if (FQueryPointer( dpy, Scr.Root, &JunkRoot, &JunkChild, xl, yt, &JunkX, &JunkY, &JunkMask) == False) { /* pointer is on a different screen */ *xl = 0; *yt = 0; } if (Grab) { MyXUngrabServer(dpy); } return 1; } /* the root window is surrounded by four window slices, which are InputOnly. * So you can see 'through' them, but they eat the input. An EnterEvent in * one of these windows causes a Paging. The windows have the according cursor * pointing in the pan direction or are hidden if there is no more panning * in that direction. This is mostly intended to get a panning even atop * of Motif applictions, which does not work yet. It seems Motif windows * eat all mouse events. * * Hermann Dunkel, HEDU, dunkel@cul-ipn.uni-kiel.de 1/94 */ /* * checkPanFrames hides PanFrames if they are on the very border of the * VIRTUAL screen and EdgeWrap for that direction is off. * (A special cursor for the EdgeWrap border could be nice) HEDU */ void checkPanFrames(void) { Bool do_unmap_l = False; Bool do_unmap_r = False; Bool do_unmap_t = False; Bool do_unmap_b = False; if (!Scr.flags.are_windows_captured) return; /* thickness of 0 means remove the pan frames */ if (edge_thickness == 0) { do_unmap_l = True; do_unmap_r = True; do_unmap_t = True; do_unmap_b = True; } /* Remove Pan frames if paging by edge-scroll is permanently or * temporarily disabled */ if (Scr.EdgeScrollX == 0 || Scr.VxMax == 0) { do_unmap_l = True; do_unmap_r = True; } if (Scr.EdgeScrollY == 0 || Scr.VyMax == 0) { do_unmap_t = True; do_unmap_b = True; } if (Scr.Vx == 0 && !Scr.flags.do_edge_wrap_x) { do_unmap_l = True; } if (Scr.Vx == Scr.VxMax && !Scr.flags.do_edge_wrap_x) { do_unmap_r = True; } if (Scr.Vy == 0 && !Scr.flags.do_edge_wrap_y) { do_unmap_t = True; } if (Scr.Vy == Scr.VyMax && !Scr.flags.do_edge_wrap_y) { do_unmap_b = True; } /* correct the unmap variables if pan frame commands are set */ if (edge_thickness != 0) { if (Scr.PanFrameLeft.command != NULL || Scr.PanFrameLeft.command_leave != NULL) { do_unmap_l = False; } if (Scr.PanFrameRight.command != NULL || Scr.PanFrameRight.command_leave != NULL) { do_unmap_r = False; } if (Scr.PanFrameBottom.command != NULL || Scr.PanFrameBottom.command_leave != NULL) { do_unmap_b = False; } if (Scr.PanFrameTop.command != NULL || Scr.PanFrameTop.command_leave != NULL) { do_unmap_t = False; } } /* * hide or show the windows */ /* left */ if (do_unmap_l) { if (Scr.PanFrameLeft.isMapped) { XUnmapWindow(dpy, Scr.PanFrameLeft.win); Scr.PanFrameLeft.isMapped = False; } } else { if (edge_thickness != last_edge_thickness) { XResizeWindow( dpy, Scr.PanFrameLeft.win, edge_thickness, Scr.MyDisplayHeight); } if (!Scr.PanFrameLeft.isMapped) { XMapRaised(dpy, Scr.PanFrameLeft.win); Scr.PanFrameLeft.isMapped = True; } } /* right */ if (do_unmap_r) { if (Scr.PanFrameRight.isMapped) { XUnmapWindow(dpy, Scr.PanFrameRight.win); Scr.PanFrameRight.isMapped = False; } } else { if (edge_thickness != last_edge_thickness) { XMoveResizeWindow( dpy, Scr.PanFrameRight.win, Scr.MyDisplayWidth - edge_thickness, 0, edge_thickness, Scr.MyDisplayHeight); } if (!Scr.PanFrameRight.isMapped) { XMapRaised(dpy, Scr.PanFrameRight.win); Scr.PanFrameRight.isMapped = True; } } /* top */ if (do_unmap_t) { if (Scr.PanFrameTop.isMapped) { XUnmapWindow(dpy, Scr.PanFrameTop.win); Scr.PanFrameTop.isMapped = False; } } else { if (edge_thickness != last_edge_thickness) { XResizeWindow( dpy, Scr.PanFrameTop.win, Scr.MyDisplayWidth, edge_thickness); } if (!Scr.PanFrameTop.isMapped) { XMapRaised(dpy, Scr.PanFrameTop.win); Scr.PanFrameTop.isMapped = True; } } /* bottom */ if (do_unmap_b) { if (Scr.PanFrameBottom.isMapped) { XUnmapWindow(dpy, Scr.PanFrameBottom.win); Scr.PanFrameBottom.isMapped = False; } } else { if (edge_thickness != last_edge_thickness) { XMoveResizeWindow( dpy, Scr.PanFrameBottom.win, 0, Scr.MyDisplayHeight - edge_thickness, Scr.MyDisplayWidth, edge_thickness); } if (!Scr.PanFrameBottom.isMapped) { XMapRaised(dpy, Scr.PanFrameBottom.win); Scr.PanFrameBottom.isMapped = True; } } last_edge_thickness = edge_thickness; return; } /* * * Gotta make sure these things are on top of everything else, or they * don't work! * * For some reason, this seems to be unneeded. * */ void raisePanFrames(void) { Window windows[4]; int n; /* Note: make sure the stacking order of the pan frames is not changed * every time they are raised by using XRestackWindows. */ n = 0; if (Scr.PanFrameTop.isMapped) { windows[n++] = Scr.PanFrameTop.win; } if (Scr.PanFrameLeft.isMapped) { windows[n++] = Scr.PanFrameLeft.win; } if (Scr.PanFrameRight.isMapped) { windows[n++] = Scr.PanFrameRight.win; } if (Scr.PanFrameBottom.isMapped) { windows[n++] = Scr.PanFrameBottom.win; } if (n > 0) { XRaiseWindow(dpy, windows[0]); if (n > 1) { XRestackWindows(dpy, windows, n); } } return; } /* * * Creates the windows for edge-scrolling * */ void initPanFrames(void) { XSetWindowAttributes attributes; unsigned long valuemask; int saved_thickness; /* Not creating the frames disables all subsequent behavior */ /* TKP. This is bad, it will cause an XMap request on a null window * later*/ /* if (edge_thickness == 0) return; */ saved_thickness = edge_thickness; if (edge_thickness == 0) { edge_thickness = 2; } attributes.event_mask = XEVMASK_PANFW; valuemask= (CWEventMask | CWCursor); attributes.cursor = Scr.FvwmCursors[CRS_TOP_EDGE]; /* I know these overlap, it's useful when at (0,0) and the top one is * unmapped */ Scr.PanFrameTop.win = XCreateWindow( dpy, Scr.Root, 0, 0, Scr.MyDisplayWidth, edge_thickness, 0, CopyFromParent, InputOnly, CopyFromParent, valuemask, &attributes); attributes.cursor = Scr.FvwmCursors[CRS_LEFT_EDGE]; Scr.PanFrameLeft.win = XCreateWindow( dpy, Scr.Root, 0, 0, edge_thickness, Scr.MyDisplayHeight, 0, CopyFromParent, InputOnly, CopyFromParent, valuemask, &attributes); attributes.cursor = Scr.FvwmCursors[CRS_RIGHT_EDGE]; Scr.PanFrameRight.win = XCreateWindow( dpy, Scr.Root, Scr.MyDisplayWidth - edge_thickness, 0, edge_thickness, Scr.MyDisplayHeight, 0, CopyFromParent, InputOnly, CopyFromParent, valuemask, &attributes); attributes.cursor = Scr.FvwmCursors[CRS_BOTTOM_EDGE]; Scr.PanFrameBottom.win = XCreateWindow( dpy, Scr.Root, 0, Scr.MyDisplayHeight - edge_thickness, Scr.MyDisplayWidth, edge_thickness, 0, CopyFromParent, InputOnly, CopyFromParent, valuemask, &attributes); Scr.PanFrameTop.isMapped=Scr.PanFrameLeft.isMapped= Scr.PanFrameRight.isMapped= Scr.PanFrameBottom.isMapped=False; edge_thickness = saved_thickness; return; } Bool is_pan_frame(Window w) { if (w == Scr.PanFrameTop.win || w == Scr.PanFrameBottom.win || w == Scr.PanFrameLeft.win || w == Scr.PanFrameRight.win) { return True; } else { return False; } } /* * * Moves the viewport within the virtual desktop * */ void MoveViewport(int newx, int newy, Bool grab) { FvwmWindow *t, *t1; int deltax,deltay; int PageTop, PageLeft; int PageBottom, PageRight; int txl, txr, tyt, tyb; if (grab) { MyXGrabServer(dpy); } if (newx > Scr.VxMax) { newx = Scr.VxMax; } if (newy > Scr.VyMax) { newy = Scr.VyMax; } if (newx < 0) { newx = 0; } if (newy < 0) { newy = 0; } deltay = Scr.Vy - newy; deltax = Scr.Vx - newx; /* Identify the bounding rectangle that will be moved into the viewport. */ PageBottom = Scr.MyDisplayHeight - deltay - 1; PageRight = Scr.MyDisplayWidth - deltax - 1; PageTop = 0 - deltay; PageLeft = 0 - deltax; if (deltax || deltay) { prev_page_x = Scr.Vx; prev_page_y = Scr.Vy; prev_desk_and_page_page_x = Scr.Vx; prev_desk_and_page_page_y = Scr.Vy; prev_desk_and_page_desk = Scr.CurrentDesk; } Scr.Vx = newx; Scr.Vy = newy; if (deltax || deltay) { BroadcastPacket( M_NEW_PAGE, 7, (long)Scr.Vx, (long)Scr.Vy, (long)Scr.CurrentDesk, (long)Scr.MyDisplayWidth, (long)Scr.MyDisplayHeight, (long)((Scr.VxMax / Scr.MyDisplayWidth) + 1), (long)((Scr.VyMax / Scr.MyDisplayHeight) + 1)); /* * RBW - 11/13/1998 - new: chase the chain * bidirectionally, all at once! The idea is to move the * windows that are moving out of the viewport from the bottom * of the stacking order up, to minimize the expose-redraw * overhead. Windows that will be moving into view will be * moved top down, for the same reason. Use the new * stacking-order chain, rather than the old last-focused * chain. * * domivogt (29-Nov-1999): It's faster to first map windows * top to bottom and then unmap windows bottom up. */ t = get_next_window_in_stack_ring(&Scr.FvwmRoot); while (t != &Scr.FvwmRoot) { /* * If the window is moving into the viewport... */ txl = t->g.frame.x; tyt = t->g.frame.y; txr = t->g.frame.x + t->g.frame.width - 1; tyb = t->g.frame.y + t->g.frame.height - 1; if (is_window_sticky_across_pages(t) && !IS_VIEWPORT_MOVED(t)) { /* the absolute position has changed */ t->g.normal.x -= deltax; t->g.normal.y -= deltay; t->g.max.x -= deltax; t->g.max.y -= deltay; /* Block double move. */ SET_VIEWPORT_MOVED(t, 1); } if ((txr >= PageLeft && txl <= PageRight && tyb >= PageTop && tyt <= PageBottom) && !IS_VIEWPORT_MOVED(t) && !IS_WINDOW_BEING_MOVED_OPAQUE(t)) { /* Block double move. */ SET_VIEWPORT_MOVED(t, 1); /* If the window is iconified, and sticky * Icons is set, then the window should * essentially be sticky */ if (!is_window_sticky_across_pages(t)) { if (IS_ICONIFIED(t)) { modify_icon_position( t, deltax, deltay); move_icon_to_position(t); broadcast_icon_geometry( t, False); } frame_setup_window( t, t->g.frame.x + deltax, t->g.frame.y + deltay, t->g.frame.width, t->g.frame.height, False); } } /* Bump to next win... */ t = get_next_window_in_stack_ring(t); } t1 = get_prev_window_in_stack_ring(&Scr.FvwmRoot); while (t1 != &Scr.FvwmRoot) { /* *If the window is not moving into the viewport... */ SET_VIEWPORT_MOVED(t, 1); txl = t1->g.frame.x; tyt = t1->g.frame.y; txr = t1->g.frame.x + t1->g.frame.width - 1; tyb = t1->g.frame.y + t1->g.frame.height - 1; if (! (txr >= PageLeft && txl <= PageRight && tyb >= PageTop && tyt <= PageBottom) && !IS_VIEWPORT_MOVED(t1) && !IS_WINDOW_BEING_MOVED_OPAQUE(t1)) { /* If the window is iconified, and sticky * Icons is set, then the window should * essentially be sticky */ if (!is_window_sticky_across_pages(t1)) { if (IS_ICONIFIED(t1)) { modify_icon_position( t1, deltax, deltay); move_icon_to_position(t1); broadcast_icon_geometry( t1, False); } frame_setup_window( t1, t1->g.frame.x + deltax, t1->g.frame.y + deltay, t1->g.frame.width, t1->g.frame.height, False); } } /* Bump to next win... */ t1 = get_prev_window_in_stack_ring(t1); } for (t = Scr.FvwmRoot.next; t != NULL; t = t->next) { if (IS_VIEWPORT_MOVED(t)) { /* Clear double move blocker. */ SET_VIEWPORT_MOVED(t, 0); GNOME_SetWinArea(t); } /* If its an icon, and its sticking, autoplace it so * that it doesn't wind up on top a a stationary * icon */ if (is_window_sticky_across_pages(t) && IS_ICONIFIED(t) && !IS_ICON_MOVED(t) && !IS_ICON_UNMAPPED(t)) { AutoPlaceIcon(t, NULL, True); } } } checkPanFrames(); /* regrab buttons in case something got obscured or unobscured */ focus_grab_buttons_all(); #if 0 /* dv (2004-07-01): I don't think that's a good idea. We could eat too * many events. */ /* do this with PanFrames too ??? HEDU */ { XEvent e; while (FCheckTypedEvent(dpy, MotionNotify, &e)) { /* nothing */ } } #endif if (grab) { MyXUngrabServer(dpy); } /* update GNOME pager */ GNOME_SetCurrentArea(); EWMH_SetDesktopViewPort(); return; } void goto_desk(int desk) { /* RBW - the unmapping operations are now removed to their own * functions so they can also be used by the new GoToDeskAndPage * command. */ if (Scr.CurrentDesk != desk) { prev_desk = Scr.CurrentDesk; prev_desk_and_page_desk = Scr.CurrentDesk; prev_desk_and_page_page_x = Scr.Vx; prev_desk_and_page_page_y = Scr.Vy; UnmapDesk(Scr.CurrentDesk, True); Scr.CurrentDesk = desk; MapDesk(desk, True); focus_grab_buttons_all(); BroadcastPacket(M_NEW_DESK, 1, (long)Scr.CurrentDesk); /* FIXME: domivogt (22-Apr-2000): Fake a 'restack' for sticky * window upon desk change. This is a workaround for a * problem in FvwmPager: The pager has a separate 'root' * window for each desk. If the active desk changes, the * pager destroys sticky mini windows and creates new ones in * the other desktop 'root'. But the pager can't know where to * stack them. So we have to tell it explicitly where they * go :-( This should be fixed in the pager, but right now the * pager doesn't maintain the stacking order. */ BroadcastRestackAllWindows(); EWMH_SetCurrentDesktop(); GNOME_SetCurrentDesk(); GNOME_SetDeskCount(); } return; } /* * * Move a window to a new desktop * */ void do_move_window_to_desk(FvwmWindow *fw, int desk) { if (fw == NULL) { return; } /* * Set the window's desktop, and map or unmap it as needed. */ /* Only change mapping for non-sticky windows */ if (!is_window_sticky_across_desks(fw) /*&& !IS_ICON_UNMAPPED(fw)*/) { if (fw->Desk == Scr.CurrentDesk) { fw->Desk = desk; if (fw == get_focus_window()) { DeleteFocus(True); } unmap_window(fw); SET_FULLY_VISIBLE(fw, 0); SET_PARTIALLY_VISIBLE(fw, 0); } else if (desk == Scr.CurrentDesk) { fw->Desk = desk; /* If its an icon, auto-place it */ if (IS_ICONIFIED(fw)) { AutoPlaceIcon(fw, NULL, True); } map_window(fw); } else { fw->Desk = desk; } BroadcastConfig(M_CONFIGURE_WINDOW,fw); } focus_grab_buttons_on_layer(fw->layer); EWMH_SetWMDesktop(fw); GNOME_SetDeskCount(); GNOME_SetDesk(fw); GNOME_SetWinArea(fw); return; } Bool get_page_arguments(char *action, int *page_x, int *page_y) { int val[2]; int suffix[2]; char *token; char *taction; int wrapx; int wrapy; int limitdeskx; int limitdesky; int startx; int starty; wrapx = 0; wrapy = 0; limitdeskx = 1; limitdesky = 1; startx = *page_x; starty = *page_y; for (; ; action = taction) { int do_reverse; token = PeekToken(action, &taction); if (token == NULL) { *page_x = Scr.Vx; *page_y = Scr.Vy; return True; } if (StrEquals(token, "prev")) { /* last page selected */ *page_x = prev_page_x; *page_y = prev_page_y; return True; } do_reverse = 0; for ( ; *token == '!'; token++) { do_reverse = !do_reverse; } if (StrEquals(token, "wrapx")) { wrapx = (1 ^ do_reverse); } else if (StrEquals(token, "wrapy")) { wrapy = (1 ^ do_reverse); } else if (StrEquals(token, "nodesklimitx")) { limitdeskx = (0 ^ do_reverse); } else if (StrEquals(token, "nodesklimitx")) { limitdesky = (0 ^ do_reverse); } else { /* no more options */ break; } } if (GetSuffixedIntegerArguments(action, NULL, val, 2, "pw", suffix) != 2) { return 0; } if (suffix[0] == 1) { *page_x = val[0] * Scr.MyDisplayWidth + Scr.Vx; } else if (suffix[0] == 2) { *page_x += val[0] * Scr.MyDisplayWidth; } else if (val[0] >= 0) { *page_x = val[0] * Scr.MyDisplayWidth; } else { *page_x = (val[0] + 1) * Scr.MyDisplayWidth + Scr.VxMax; } if (suffix[1] == 1) { *page_y = val[1] * Scr.MyDisplayHeight + Scr.Vy; } else if (suffix[1] == 2) { *page_y += val[1] * Scr.MyDisplayHeight; } else if (val[1] >= 0) { *page_y = val[1] * Scr.MyDisplayHeight; } else { *page_y = (val[1] + 1) * Scr.MyDisplayHeight + Scr.VyMax; } /* limit to desktop size */ if (limitdeskx && !wrapx) { if (*page_x < 0) { *page_x = 0; } else if (*page_x > Scr.VxMax) { *page_x = Scr.VxMax; } } else if (limitdeskx && wrapx) { while (*page_x < 0) { *page_x += Scr.VxMax + Scr.MyDisplayWidth; } while (*page_x > Scr.VxMax) { *page_x -= Scr.VxMax + Scr.MyDisplayWidth; } } if (limitdesky && !wrapy) { if (*page_y < 0) { *page_y = 0; } else if (*page_y > Scr.VyMax) { *page_y = Scr.VyMax; } } else if (limitdesky && wrapy) { while (*page_y < 0) { *page_y += Scr.VyMax + Scr.MyDisplayHeight; } while (*page_y > Scr.VyMax) { *page_y -= Scr.VyMax + Scr.MyDisplayHeight; } } return True; } char *GetDesktopName(int desk) { DesktopsInfo *d; d = Scr.Desktops->next; while (d != NULL && d->desk != desk) { d = d->next; } if (d != NULL) { return d->name; } return NULL; } /* ---------------------------- builtin commands --------------------------- */ /* EdgeCommand - binds a function to a pan frame enter event */ void CMD_EdgeCommand(F_CMD_ARGS) { direction_t direction; char * command; /* get the direction */ direction = gravity_parse_dir_argument(action, &action, DIR_NONE); if (direction >= 0 && direction <= DIR_MAJOR_MASK) { /* check if the command does contain at least one token */ command = safestrdup(action); if (PeekToken(action , &action) == NULL) { /* the command does not contain a token so the command of this edge is removed */ free(command); command = NULL; } /* assign command to the edge(s) */ if (direction == DIR_N) { if (Scr.PanFrameTop.command != NULL) { free(Scr.PanFrameTop.command); } Scr.PanFrameTop.command = command; } else if (direction == DIR_S) { if (Scr.PanFrameBottom.command != NULL) { free(Scr.PanFrameBottom.command); } Scr.PanFrameBottom.command = command; } else if (direction == DIR_W) { if (Scr.PanFrameLeft.command != NULL) { free(Scr.PanFrameLeft.command); } Scr.PanFrameLeft.command = command; } else if (direction == DIR_E) { if (Scr.PanFrameRight.command != NULL) { free(Scr.PanFrameRight.command); } Scr.PanFrameRight.command = command; } else { /* this should never happen */ fvwm_msg(ERR, "EdgeCommand", "Internal error in CMD_EdgeCommand"); } } else { /* check if the argument does contain at least one token */ if (PeekToken(action , &action) == NULL) { /* Just plain EdgeCommand, so all edge commands are * removed */ if (Scr.PanFrameTop.command != NULL) { free(Scr.PanFrameTop.command); Scr.PanFrameTop.command = NULL; } if (Scr.PanFrameBottom.command != NULL) { free(Scr.PanFrameBottom.command); Scr.PanFrameBottom.command = NULL; } if (Scr.PanFrameLeft.command != NULL) { free(Scr.PanFrameLeft.command); Scr.PanFrameLeft.command = NULL; } if (Scr.PanFrameRight.command != NULL) { free(Scr.PanFrameRight.command); Scr.PanFrameRight.command = NULL; } } else { /* not a proper direction */ fvwm_msg(ERR, "EdgeCommand", "EdgeCommand [direction [function]]"); } } /* maybe something has changed so we adapt the pan frames */ checkPanFrames(); return; } /* EdgeLeaveCommand - binds a function to a pan frame Leave event */ void CMD_EdgeLeaveCommand(F_CMD_ARGS) { direction_t direction; char * command; /* get the direction */ direction = gravity_parse_dir_argument(action, &action, DIR_NONE); if (direction >= 0 && direction <= DIR_MAJOR_MASK) { /* check if the command does contain at least one token */ command = safestrdup(action); if (PeekToken(action , &action) == NULL) { /* the command does not contain a token so the command of this edge is removed */ free(command); command = NULL; } /* assign command to the edge(s) */ if (direction == DIR_N) { if (Scr.PanFrameTop.command_leave != NULL) { free(Scr.PanFrameTop.command_leave); } Scr.PanFrameTop.command_leave = command; } else if (direction == DIR_S) { if (Scr.PanFrameBottom.command_leave != NULL) { free(Scr.PanFrameBottom.command_leave); } Scr.PanFrameBottom.command_leave = command; } else if (direction == DIR_W) { if (Scr.PanFrameLeft.command_leave != NULL) { free(Scr.PanFrameLeft.command_leave); } Scr.PanFrameLeft.command_leave = command; } else if (direction == DIR_E) { if (Scr.PanFrameRight.command_leave != NULL) { free(Scr.PanFrameRight.command_leave); } Scr.PanFrameRight.command_leave = command; } else { /* this should never happen */ fvwm_msg(ERR, "EdgeLeaveCommand", "Internal error in CMD_EdgeLeaveCommand"); } } else { /* check if the argument does contain at least one token */ if (PeekToken(action , &action) == NULL) { /* Just plain EdgeLeaveCommand, so all edge commands are * removed */ if (Scr.PanFrameTop.command_leave != NULL) { free(Scr.PanFrameTop.command_leave); Scr.PanFrameTop.command_leave = NULL; } if (Scr.PanFrameBottom.command_leave != NULL) { free(Scr.PanFrameBottom.command_leave); Scr.PanFrameBottom.command_leave = NULL; } if (Scr.PanFrameLeft.command_leave != NULL) { free(Scr.PanFrameLeft.command_leave); Scr.PanFrameLeft.command_leave = NULL; } if (Scr.PanFrameRight.command_leave != NULL) { free(Scr.PanFrameRight.command_leave); Scr.PanFrameRight.command_leave = NULL; } } else { /* not a proper direction */ fvwm_msg(ERR, "EdgeLeaveCommand", "EdgeLeaveCommand [direction [function]]"); } } /* maybe something has changed so we adapt the pan frames */ checkPanFrames(); return; } void CMD_EdgeThickness(F_CMD_ARGS) { int val, n; n = GetIntegerArguments(action, NULL, &val, 1); if (n != 1) { fvwm_msg(ERR,"setEdgeThickness", "EdgeThickness requires 1 numeric argument," " found %d args",n); return; } /* check range */ if (val < 0 || val > 2) { fvwm_msg(ERR,"setEdgeThickness", "EdgeThickness arg must be between 0 and 2," " found %d",val); return; } edge_thickness = val; checkPanFrames(); return; } void CMD_EdgeScroll(F_CMD_ARGS) { int val1, val2, val1_unit, val2_unit, n; char *token; n = GetTwoArguments(action, &val1, &val2, &val1_unit, &val2_unit); if (n != 2) { fvwm_msg( ERR, "SetEdgeScroll", "EdgeScroll requires two arguments"); return; } /* * if edgescroll >1000 and <100000 * wrap at edges of desktop (a "spherical" desktop) */ if (val1 >= 1000 && val1_unit != 100) { val1 /= 1000; Scr.flags.do_edge_wrap_x = 1; } else { Scr.flags.do_edge_wrap_x = 0; } if (val2 >= 1000 && val2_unit != 100) { val2 /= 1000; Scr.flags.do_edge_wrap_y = 1; } else { Scr.flags.do_edge_wrap_y = 0; } action=SkipNTokens(action,2); token = PeekToken(action, NULL); if (token) { if (StrEquals(token, "wrap")) { Scr.flags.do_edge_wrap_x = 1; Scr.flags.do_edge_wrap_y = 1; } else if (StrEquals(token, "wrapx")) { Scr.flags.do_edge_wrap_x = 1; } else if (StrEquals(token, "wrapy")) { Scr.flags.do_edge_wrap_y = 1; } } Scr.EdgeScrollX = val1 * val1_unit / 100; Scr.EdgeScrollY = val2 * val2_unit / 100; checkPanFrames(); return; } void CMD_EdgeResistance(F_CMD_ARGS) { int val[3]; int n; val[0] = 0; n = GetIntegerArguments(action, NULL, val, 3); if (n > 1 && val[0] >= 10000) { /* map val[0] >= 10000 in old syntax to -1 in new syntax */ val[0] = -1; } if (n == 1) { Scr.ScrollDelay = val[0]; } else if (n >= 2 && n <= 3) { char cmd[99]; char stylecmd[99]; char stylecmd2[99]; Scr.ScrollDelay = val[0]; sprintf(cmd, "EdgeResistance %d", val[0]); sprintf(stylecmd, "Style * EdgeMoveDelay %d", val[0]); if (n == 2) { sprintf( stylecmd2, "Style * EdgeMoveResistance %d", val[1]); } else { sprintf( stylecmd2, "Style * EdgeMoveResistance %d %d", val[1], val[2]); } fvwm_msg( OLD, "CMD_EdgeResistance", "The command EdgeResistance with three arguments is" " obsolete. Please use the following commands" " instead:"); fvwm_msg(OLD, "", cmd); fvwm_msg(OLD, "", stylecmd); fvwm_msg(OLD, "", stylecmd2); execute_function( cond_rc, exc, cmd, FUNC_DONT_REPEAT | FUNC_DONT_EXPAND_COMMAND); execute_function( cond_rc, exc, stylecmd, FUNC_DONT_REPEAT | FUNC_DONT_EXPAND_COMMAND); execute_function( cond_rc, exc, stylecmd2, FUNC_DONT_REPEAT | FUNC_DONT_EXPAND_COMMAND); } else { fvwm_msg( ERR, "CMD_EdgeResistance", "EdgeResistance requires two or three arguments"); return; } return; } void CMD_Xinerama(F_CMD_ARGS) { int toggle; toggle = ParseToggleArgument(action, NULL, -1, 0); if (toggle == -1) { toggle = !FScreenIsEnabled(); } if (!toggle != !FScreenIsEnabled()) { Scr.flags.do_need_window_update = True; Scr.flags.has_xinerama_state_changed = True; FScreenOnOff(toggle); broadcast_xinerama_state(); } return; } void CMD_XineramaPrimaryScreen(F_CMD_ARGS) { int val; val = FScreenGetScreenArgument(action, 0); FScreenSetPrimaryScreen(val); if (FScreenIsEnabled()) { Scr.flags.do_need_window_update = True; Scr.flags.has_xinerama_state_changed = True; } broadcast_xinerama_state(); return; } void CMD_XineramaSls(F_CMD_ARGS) { int toggle; toggle = ParseToggleArgument(action, NULL, -1, 0); if (toggle == -1) { toggle = !FScreenIsSLSEnabled(); } if (!toggle != !FScreenIsSLSEnabled()) { if (FScreenIsEnabled()) { Scr.flags.do_need_window_update = True; Scr.flags.has_xinerama_state_changed = True; } FScreenSLSOnOff(toggle); broadcast_xinerama_state(); } return; } void CMD_XineramaSlsSize(F_CMD_ARGS) { int val[2]; if (GetIntegerArguments(action, NULL, val, 2) != 2 && GetRectangleArguments(action, &val[0], &val[1]) != 2) { val[0] = 1; val[1] = 1; } if (FScreenConfigureSLSSize(val[0], val[1])) { broadcast_xinerama_state(); } return; } void CMD_XineramaSlsScreens(F_CMD_ARGS) { int nscreens; char *args; if (GetIntegerArguments(action, &args, &nscreens, 1) != 1) { nscreens = 0; args = NULL; } else if (args == NULL) { nscreens = 0; } if (FScreenConfigureSLSScreens(nscreens, args)) { broadcast_xinerama_state(); } return; } void CMD_DesktopSize(F_CMD_ARGS) { int val[2]; if (GetIntegerArguments(action, NULL, val, 2) != 2 && GetRectangleArguments(action, &val[0], &val[1]) != 2) { fvwm_msg(ERR, "CMD_DesktopSize", "DesktopSize requires two arguments"); return; } Scr.VxMax = (val[0] <= 0) ? 0: val[0]*Scr.MyDisplayWidth-Scr.MyDisplayWidth; Scr.VyMax = (val[1] <= 0) ? 0: val[1]*Scr.MyDisplayHeight-Scr.MyDisplayHeight; BroadcastPacket( M_NEW_PAGE, 7, (long)Scr.Vx, (long)Scr.Vy, (long)Scr.CurrentDesk, (long)Scr.MyDisplayWidth, (long)Scr.MyDisplayHeight, (long)((Scr.VxMax / Scr.MyDisplayWidth) + 1), (long)((Scr.VyMax / Scr.MyDisplayHeight) + 1)); checkPanFrames(); /* update GNOME pager */ GNOME_SetAreaCount(); EWMH_SetDesktopGeometry(); return; } /* * * Move to a new desktop * */ void CMD_GotoDesk(F_CMD_ARGS) { goto_desk(GetDeskNumber(action, Scr.CurrentDesk)); return; } void CMD_Desk(F_CMD_ARGS) { CMD_GotoDesk(F_PASS_ARGS); return; } /* * * Move to a new desktop and page at the same time. * This function is designed for use by the Pager, and replaces the old * GoToDesk 0 10000 hack. * - unmap all windows on the current desk so they don't flash when the * viewport is moved, then switch the viewport, then the desk. * */ void CMD_GotoDeskAndPage(F_CMD_ARGS) { int val[3]; Bool is_new_desk; if (MatchToken(action, "prev")) { val[0] = prev_desk_and_page_desk; val[1] = prev_desk_and_page_page_x; val[2] = prev_desk_and_page_page_y; } else if (GetIntegerArguments(action, NULL, val, 3) == 3) { val[1] *= Scr.MyDisplayWidth; val[2] *= Scr.MyDisplayHeight; } else { return; } is_new_desk = (Scr.CurrentDesk != val[0]); if (is_new_desk) { UnmapDesk(Scr.CurrentDesk, True); } prev_desk_and_page_page_x = Scr.Vx; prev_desk_and_page_page_y = Scr.Vy; MoveViewport(val[1], val[2], True); if (is_new_desk) { prev_desk = Scr.CurrentDesk; prev_desk_and_page_desk = Scr.CurrentDesk; Scr.CurrentDesk = val[0]; MapDesk(val[0], True); focus_grab_buttons_all(); BroadcastPacket(M_NEW_DESK, 1, (long)Scr.CurrentDesk); /* FIXME: domivogt (22-Apr-2000): Fake a 'restack' for sticky * window upon desk change. This is a workaround for a * problem in FvwmPager: The pager has a separate 'root' * window for each desk. If the active desk changes, the * pager destroys sticky mini windows and creates new ones in * the other desktop 'root'. But the pager can't know where to * stack them. So we have to tell it ecplicitly where they * go :-( This should be fixed in the pager, but right now the * pager doesn't the stacking order. */ BroadcastRestackAllWindows(); } else { BroadcastPacket(M_NEW_DESK, 1, (long)Scr.CurrentDesk); } EWMH_SetCurrentDesktop(); GNOME_SetCurrentDesk(); GNOME_SetDeskCount(); return; } void CMD_GotoPage(F_CMD_ARGS) { int x; int y; x = Scr.Vx; y = Scr.Vy; if (!get_page_arguments(action, &x, &y)) { fvwm_msg( ERR, "goto_page_func", "GotoPage: invalid arguments: %s", action); return; } if (x < 0) { x = 0; } if (x > Scr.VxMax) { x = Scr.VxMax; } if (y < 0) { y = 0; } if (y > Scr.VyMax) { y = Scr.VyMax; } MoveViewport(x,y,True); return; } /* function with parsing of command line */ void CMD_MoveToDesk(F_CMD_ARGS) { int desk; FvwmWindow * const fw = exc->w.fw; desk = GetDeskNumber(action, fw->Desk); if (desk == fw->Desk) { return; } do_move_window_to_desk(fw, desk); return; } void CMD_Scroll(F_CMD_ARGS) { int x,y; int val1, val2, val1_unit, val2_unit; if (GetTwoArguments(action, &val1, &val2, &val1_unit, &val2_unit) != 2) { /* less then two integer parameters implies interactive * scroll check if we are scrolling in reverse direction */ char *option; int scroll_speed = 1; option = PeekToken(action, NULL); if (option != NULL) { if (StrEquals(option, "Reverse")) { scroll_speed *= -1; } } __drag_viewport(exc, scroll_speed); return; } if ((val1 > -100000)&&(val1 < 100000)) { x = Scr.Vx + val1*val1_unit/100; } else { x = Scr.Vx + (val1/1000)*val1_unit/100; } if ((val2 > -100000)&&(val2 < 100000)) { y=Scr.Vy + val2*val2_unit/100; } else { y = Scr.Vy + (val2/1000)*val2_unit/100; } if (((val1 <= -100000)||(val1 >= 100000))&&(x>Scr.VxMax)) { int xpixels; xpixels = (Scr.VxMax / Scr.MyDisplayWidth + 1) * Scr.MyDisplayWidth; x %= xpixels; y += Scr.MyDisplayHeight * (1+((x-Scr.VxMax-1)/xpixels)); if (y > Scr.VyMax) { y %= (Scr.VyMax / Scr.MyDisplayHeight + 1) * Scr.MyDisplayHeight; } } if (((val1 <= -100000)||(val1 >= 100000))&&(x<0)) { x = Scr.VxMax; y -= Scr.MyDisplayHeight; if (y < 0) { y=Scr.VyMax; } } if (((val2 <= -100000)||(val2>= 100000))&&(y>Scr.VyMax)) { int ypixels = (Scr.VyMax / Scr.MyDisplayHeight + 1) * Scr.MyDisplayHeight; y %= ypixels; x += Scr.MyDisplayWidth * (1+((y-Scr.VyMax-1)/ypixels)); if (x > Scr.VxMax) { x %= (Scr.VxMax / Scr.MyDisplayWidth + 1) * Scr.MyDisplayWidth; } } if (((val2 <= -100000)||(val2>= 100000))&&(y<0)) { y = Scr.VyMax; x -= Scr.MyDisplayWidth; if (x < 0) { x=Scr.VxMax; } } MoveViewport(x,y,True); return; } /* * * Defines the name of a desktop * */ void CMD_DesktopName(F_CMD_ARGS) { int desk; DesktopsInfo *t, *d, *new, **prev; if (GetIntegerArguments(action, &action, &desk, 1) != 1) { fvwm_msg( ERR,"CMD_DesktopName", "First argument to DesktopName must be an integer: %s", action); return; } d = Scr.Desktops->next; while (d != NULL && d->desk != desk) { d = d->next; } if (d != NULL) { if (d->name != NULL) { free(d->name); d->name = NULL; } if (action != NULL && *action && *action != '\n') { CopyString(&d->name, action); } } else { /* new deskops entries: add it in order */ d = Scr.Desktops->next; t = Scr.Desktops; prev = &(Scr.Desktops->next); while (d != NULL && d->desk < desk) { t = t->next; prev = &(d->next); d = d->next; } if (d == NULL) { /* add it at the end */ *prev = (DesktopsInfo *)safemalloc( sizeof(DesktopsInfo)); memset(*prev, 0, sizeof(DesktopsInfo)); (*prev)->desk = desk; if (action != NULL && *action && *action != '\n') { CopyString(&((*prev)->name), action); } } else { /* instert it */ new = (DesktopsInfo *)safemalloc(sizeof(DesktopsInfo)); memset(new, 0, sizeof(DesktopsInfo)); new->desk = desk; if (action != NULL && *action && *action != '\n') { CopyString(&(new->name), action); } t->next = new; new->next = d; } /* should check/set the working areas */ } if (!fFvwmInStartup) { char *msg; const char *default_desk_name = _("Desk"); /* should send the info to the FvwmPager and set the EWMH * desktop names */ if (action != NULL && *action && *action != '\n') { msg = (char *)safemalloc(strlen(action) + 44); sprintf(msg, "DesktopName %d %s", desk, action); } else { msg = (char *)safemalloc(strlen(default_desk_name)+44); sprintf( msg, "DesktopName %d %s %d", desk, default_desk_name, desk); } BroadcastConfigInfoString(msg); free(msg); EWMH_SetDesktopNames(); } return; }