2 /* This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 /* ---------------------------- included header files ---------------------- */
23 #include <X11/keysym.h>
24 #include "libs/fvwmlib.h"
25 #include "libs/FScreen.h"
26 #include "libs/FGettext.h"
27 #include "libs/Grab.h"
28 #include "libs/Parse.h"
31 #include "execcontext.h"
34 #include "eventmask.h"
38 #include "module_interface.h"
39 #include "module_list.h"
43 #include "move_resize.h"
49 #include "functions.h"
51 /* ---------------------------- local definitions -------------------------- */
53 /* ---------------------------- local macros ------------------------------- */
55 /* ---------------------------- imports ------------------------------------ */
57 /* ---------------------------- included code files ------------------------ */
59 /* ---------------------------- local types -------------------------------- */
61 /* ---------------------------- forward declarations ----------------------- */
63 /* ---------------------------- local variables ---------------------------- */
68 * Testing with edge_thickness of 1 showed that some XServers don't
69 * care for 1 pixel windows, causing EdgeScrolling not to work with some
70 * servers. One bug report was for SUNOS 4.1.3_U1. We didn't find out
71 * the exact cause of the problem. Perhaps no enter/leave events were
74 * Allowed: 0,1,or 2 pixel pan frames.
76 * 0 completely disables mouse edge scrolling, even while dragging a
79 * 1 gives the smallest pan frames, which seem to work best except on
84 static int edge_thickness
= 2;
85 static int last_edge_thickness
= 2;
86 static int prev_page_x
= 0;
87 static int prev_page_y
= 0;
88 static int prev_desk
= 0;
89 static int prev_desk_and_page_desk
= 0;
90 static int prev_desk_and_page_page_x
= 0;
91 static int prev_desk_and_page_page_y
= 0;
93 /* ---------------------------- exported variables (globals) --------------- */
95 /* ---------------------------- local functions ---------------------------- */
98 static void __drag_viewport(const exec_context_t
*exc
, int scroll_speed
)
103 unsigned int button_mask
= 0;
104 Bool is_finished
= False
;
106 if (!GrabEm(CRS_MOVE
, GRAB_NORMAL
))
113 dpy
, Scr
.Root
, &JunkRoot
, &JunkChild
, &x
, &y
,
114 &JunkX
, &JunkY
, &button_mask
) == False
)
116 /* pointer is on a different screen */
117 /* Is this the best thing to do? */
118 UngrabEm(GRAB_NORMAL
);
121 MyXGrabKeyboard(dpy
);
122 button_mask
&= DEFAULT_ALL_BUTTONS_MASK
;
123 memset(&e
, 0, sizeof(e
));
132 dpy
, ButtonPressMask
| ButtonReleaseMask
|
133 KeyPressMask
| PointerMotionMask
|
134 ButtonMotionMask
| ExposureMask
|
135 EnterWindowMask
| LeaveWindowMask
, &e
);
136 /* discard extra events before a logical release */
137 if (e
.type
== MotionNotify
||
138 e
.type
== EnterNotify
|| e
.type
== LeaveNotify
)
140 while (FPending(dpy
) > 0 &&
142 dpy
, ButtonMotionMask
|
143 PointerMotionMask
| ButtonPressMask
|
144 ButtonRelease
| KeyPressMask
|
145 EnterWindowMask
| LeaveWindowMask
, &e
))
147 if (e
.type
== ButtonPress
||
148 e
.type
== ButtonRelease
||
155 if (e
.type
== EnterNotify
|| e
.type
== LeaveNotify
)
161 /* Query the pointer to catch the latest information.
162 * This *is* necessary. */
164 dpy
, Scr
.Root
, &JunkRoot
, &JunkChild
, &px
,
165 &py
, &JunkX
, &JunkY
, &JunkMask
) == True
)
167 fev_make_null_event(&e2
, dpy
);
168 e2
.type
= MotionNotify
;
169 e2
.xmotion
.time
= fev_get_evtime();
170 e2
.xmotion
.x_root
= px
;
171 e2
.xmotion
.y_root
= py
;
172 e2
.xmotion
.state
= JunkMask
;
173 e2
.xmotion
.same_screen
= True
;
179 /* pointer is on a different screen,
183 /* Handle a limited number of key press events to allow
184 * mouseless operation */
185 if (e
.type
== KeyPress
)
188 &e
, NULL
, NULL
, NULL
, ButtonRelease
);
193 /* simple code to bag out of move - CKH */
194 if (XLookupKeysym(&(e
.xkey
), 0) == XK_Escape
)
200 if (e
.xbutton
.button
<= NUMBER_OF_MOUSE_BUTTONS
&&
201 ((Button1Mask
<< (e
.xbutton
.button
- 1)) &
204 /* No new button was pressed, just a delayed
210 x
= e
.xbutton
.x_root
;
211 y
= e
.xbutton
.y_root
;
215 if (e
.xmotion
.same_screen
== False
)
219 x
= e
.xmotion
.x_root
;
220 y
= e
.xmotion
.y_root
;
229 if (x
!= old_x
|| y
!= old_y
)
232 Scr
.Vx
+ scroll_speed
* (x
- old_x
),
233 Scr
.Vy
+ scroll_speed
* (y
- old_y
), False
);
234 FlushAllMessageQueues();
237 UngrabEm(GRAB_NORMAL
);
238 MyXUngrabKeyboard(dpy
);
239 WaitForButtonsUp(True
);
244 * Parse arguments for "Desk" and "MoveToDesk" (formerly "WindowsDesk"):
246 * (nil) : desk number = current desk
247 * n : desk number = current desk + n
248 * 0 n : desk number = n
249 * n x : desk number = current desk + n
250 * 0 n min max : desk number = n, but limit to min/max
251 * n min max : desk number = current desk + n, but wrap around at desk #min
253 * n x min max : desk number = current desk + n, but wrap around at desk #min
256 * The current desk number is returned if not enough parameters could be
257 * read (or if action is empty).
260 static int GetDeskNumber(char *action
, int current_desk
)
269 if (MatchToken(action
, "prev"))
273 n
= GetIntegerArguments(action
, NULL
, &(val
[0]), 4);
276 return Scr
.CurrentDesk
;
280 return current_desk
+ val
[0];
286 /* absolute desk number */
292 /* relative desk number */
307 if (val
[m
] <= val
[m
+1])
314 /* min > max is nonsense, so swap 'em. */
320 /* Relative moves wrap around. */
323 desk
+= (max
- min
+ 1);
327 desk
-= (max
- min
+ 1);
332 /* Relative move outside of range, wrap around. */
344 /* Move outside of range, truncate. */
361 * Unmaps a window on transition to a new desktop
364 static void unmap_window(FvwmWindow
*t
)
366 XWindowAttributes winattrs
;
367 unsigned long eventMask
= 0;
371 * Prevent the receipt of an UnmapNotify, since that would
372 * cause a transition to the Withdrawn state.
374 ret
= XGetWindowAttributes(dpy
, FW_W(t
), &winattrs
);
377 eventMask
= winattrs
.your_event_mask
;
378 /* suppress UnmapRequest event */
379 XSelectInput(dpy
, FW_W(t
), eventMask
& ~StructureNotifyMask
);
383 if (FW_W_ICON_PIXMAP(t
) != None
)
385 XUnmapWindow(dpy
,FW_W_ICON_PIXMAP(t
));
387 if (FW_W_ICON_TITLE(t
) != None
)
389 XUnmapWindow(dpy
,FW_W_ICON_TITLE(t
));
394 XUnmapWindow(dpy
,FW_W_FRAME(t
));
395 border_undraw_decorations(t
);
396 #ifdef ICCCM2_UNMAP_WINDOW_PATCH
397 /* this is required by the ICCCM2 */
398 XUnmapWindow(dpy
, FW_W(t
));
400 if (!Scr
.bo
.do_enable_ewmh_iconic_state_workaround
)
402 SetMapStateProp(t
, IconicState
);
407 XSelectInput(dpy
, FW_W(t
), eventMask
);
416 * Maps a window on transition to a new desktop
419 static void map_window(FvwmWindow
*t
)
421 XWindowAttributes winattrs
;
422 unsigned long eventMask
= 0;
425 if (IS_SCHEDULED_FOR_DESTROY(t
))
430 * Prevent the receipt of an UnmapNotify, since that would
431 * cause a transition to the Withdrawn state.
433 ret
= XGetWindowAttributes(dpy
, FW_W(t
), &winattrs
);
436 eventMask
= winattrs
.your_event_mask
;
437 /* suppress MapRequest event */
438 XSelectInput(dpy
, FW_W(t
), eventMask
& ~StructureNotifyMask
);
442 if (FW_W_ICON_PIXMAP(t
) != None
)
444 XMapWindow(dpy
,FW_W_ICON_PIXMAP(t
));
446 if (FW_W_ICON_TITLE(t
) != None
)
448 XMapWindow(dpy
,FW_W_ICON_TITLE(t
));
451 else if (IS_MAPPED(t
))
453 border_draw_decorations(
454 t
, PART_ALL
, (t
== get_focus_window()) ? True
: False
,
455 False
, CLEAR_ALL
, NULL
, NULL
);
456 XMapWindow(dpy
, FW_W_FRAME(t
));
457 XMapWindow(dpy
, FW_W_PARENT(t
));
458 XMapSubwindows(dpy
, FW_W_FRAME(t
));
459 #ifdef ICCCM2_UNMAP_WINDOW_PATCH
460 /* this is required by the ICCCM2 */
461 XMapWindow(dpy
, FW_W(t
));
463 if (!Scr
.bo
.do_enable_ewmh_iconic_state_workaround
)
465 SetMapStateProp(t
, NormalState
);
470 XSelectInput(dpy
, FW_W(t
), eventMask
);
479 * Unmap all windows on a desk -
480 * - Part 1 of a desktop switch
481 * - must eventually be followed by a call to MapDesk
482 * - unmaps from the bottom of the stack up
485 static void UnmapDesk(int desk
, Bool grab
)
488 FvwmWindow
*sf
= get_focus_window();
494 for (t
= get_prev_window_in_stack_ring(&Scr
.FvwmRoot
);
495 t
!= &Scr
.FvwmRoot
; t
= get_prev_window_in_stack_ring(t
))
497 /* Only change mapping for non-sticky windows */
498 if (!is_window_sticky_across_desks(t
) && !IS_ICON_UNMAPPED(t
))
504 t
->flags
.is_focused_on_other_desk
= 1;
510 t
->flags
.is_focused_on_other_desk
= 0;
513 SET_FULLY_VISIBLE(t
, 0);
514 SET_PARTIALLY_VISIBLE(t
, 0);
519 t
->flags
.is_focused_on_other_desk
= 0;
524 MyXUngrabServer(dpy
);
532 * Map all windows on a desk -
533 * - Part 2 of a desktop switch
534 * - only use if UnmapDesk has previously been called
535 * - maps from the top of the stack down
538 static void MapDesk(int desk
, Bool grab
)
541 FvwmWindow
*FocusWin
= NULL
;
542 FvwmWindow
*StickyWin
= NULL
;
543 FvwmWindow
*sf
= get_focus_window();
545 Scr
.flags
.is_map_desk_in_progress
= 1;
550 for (t
= get_next_window_in_stack_ring(&Scr
.FvwmRoot
);
551 t
!= &Scr
.FvwmRoot
; t
= get_next_window_in_stack_ring(t
))
553 /* Only change mapping for non-sticky windows */
554 if (!is_window_sticky_across_desks(t
) && !IS_ICON_UNMAPPED(t
))
563 /* If window is sticky, just update its desk (it's
576 MyXUngrabServer(dpy
);
579 for (t
= Scr
.FvwmRoot
.next
; t
!= NULL
; t
= t
->next
)
582 Autoplace any sticky icons, so that sticky icons from the old
583 desk don't land on top of stationary ones on the new desk.
585 if (is_window_sticky_across_desks(t
) && IS_ICONIFIED(t
) &&
586 !IS_ICON_MOVED(t
) && !IS_ICON_UNMAPPED(t
))
588 AutoPlaceIcon(t
, NULL
, True
);
590 /* Keep track of the last-focused window on the new desk. */
591 if (t
->flags
.is_focused_on_other_desk
&& t
->FocusDesk
== desk
)
593 t
->flags
.is_focused_on_other_desk
= 0;
598 /* If a sticky window has focus, don't disturb it. */
599 if (!StickyWin
&& FocusWin
)
601 /* Otherwise, handle remembering the last-focused clicky
603 if (!FP_DO_UNFOCUS_LEAVE(FW_FOCUS_POLICY(FocusWin
)))
605 ReturnFocusWindow(FocusWin
);
612 Scr
.flags
.is_map_desk_in_progress
= 0;
617 /* ---------------------------- interface functions ------------------------ */
621 * Check to see if the pointer is on the edge of the screen, and scroll/page
627 * -1: no need to call the function again before a new event arrives
630 XEvent
*pev
, int HorWarpSize
, int VertWarpSize
, int *xl
, int *yt
,
631 int *delta_x
, int *delta_y
, Bool Grab
, Bool fLoop
,
632 Bool do_continue_previous
, int delay
)
634 static int add_time
= 0;
637 static Time my_timestamp
= 0;
638 static Time my_last_timestamp
= 0;
639 static Bool is_timestamp_valid
= False
;
640 static int last_x
= 0;
641 static int last_y
= 0;
642 static Bool is_last_position_valid
= False
;
646 if (!is_timestamp_valid
&& do_continue_previous
)
648 /* don't call me again until something has happened */
651 if (!is_timestamp_valid
&& pev
->type
== MotionNotify
)
653 x
= pev
->xmotion
.x_root
;
654 y
= pev
->xmotion
.y_root
;
655 if ((Scr
.VxMax
== 0 ||
656 (x
>= edge_thickness
&&
657 x
< Scr
.MyDisplayWidth
- edge_thickness
)) &&
659 (y
>= edge_thickness
&&
660 y
< Scr
.MyDisplayHeight
- edge_thickness
)))
665 if (delay
< 0 || (HorWarpSize
== 0 && VertWarpSize
==0))
667 is_timestamp_valid
= False
;
671 if (Scr
.VxMax
== 0 && Scr
.VyMax
== 0)
673 is_timestamp_valid
= False
;
677 if (!is_timestamp_valid
)
679 is_timestamp_valid
= True
;
680 my_timestamp
= fev_get_evtime();
681 is_last_position_valid
= False
;
686 else if (my_last_timestamp
!= fev_get_evtime())
690 my_last_timestamp
= fev_get_evtime();
699 if (FCheckPeekIfEvent(dpy
, &e
, test_button_event
, NULL
))
701 is_timestamp_valid
= False
;
705 /* get pointer location */
707 dpy
, Scr
.Root
, &JunkW
, &JunkW
, &x
, &y
, &JunkC
, &JunkC
,
711 /* pointer is on a different screen */
716 /* check actual pointer location since PanFrames can get buried
717 * under window being moved or resized - mab */
718 if (x
>= edge_thickness
&&
719 x
< Scr
.MyDisplayWidth
-edge_thickness
&&
720 y
>= edge_thickness
&&
721 y
< Scr
.MyDisplayHeight
-edge_thickness
)
723 is_timestamp_valid
= False
;
727 if (!fLoop
&& is_last_position_valid
&&
728 (x
- last_x
> MAX_PAGING_MOVE_DISTANCE
||
729 x
- last_x
< -MAX_PAGING_MOVE_DISTANCE
||
730 y
- last_y
> MAX_PAGING_MOVE_DISTANCE
||
731 y
- last_y
< -MAX_PAGING_MOVE_DISTANCE
))
733 /* The pointer is moving too fast, prevent paging until
734 * it slows down. Don't prevent paging when fLoop is
735 * set since we can't be sure that HandlePaging will be
737 is_timestamp_valid
= True
;
738 my_timestamp
= fev_get_evtime();
746 is_last_position_valid
= True
;
749 } while (fLoop
&& fev_get_evtime() - my_timestamp
+ add_time
< delay
);
751 if (fev_get_evtime() - my_timestamp
+ add_time
< delay
)
756 /* Get the latest pointer position. This is necessary as XFree 4.1.0.1
757 * sometimes does not report mouse movement when it should. */
759 dpy
, Scr
.Root
, &JunkRoot
, &JunkChild
, &x
, &y
, &JunkX
,
760 &JunkY
, &JunkMask
) == False
)
762 /* pointer is on a different screen - ignore */
764 /* Move the viewport */
765 /* and/or move the cursor back to the approximate correct location */
766 /* that is, the same place on the virtual desktop that it */
768 if (x
< edge_thickness
)
770 *delta_x
= -HorWarpSize
;
772 else if (x
>= Scr
.MyDisplayWidth
-edge_thickness
)
774 *delta_x
= HorWarpSize
;
784 if (y
< edge_thickness
)
786 *delta_y
= -VertWarpSize
;
788 else if (y
>= Scr
.MyDisplayHeight
-edge_thickness
)
790 *delta_y
= VertWarpSize
;
801 is_timestamp_valid
= False
;
803 if (*delta_x
== 0 && *delta_y
== 0)
808 /* Ouch! lots of bounds checking */
809 if (Scr
.Vx
+ *delta_x
< 0)
811 if (!(Scr
.flags
.do_edge_wrap_x
))
818 *delta_x
+= Scr
.VxMax
+ Scr
.MyDisplayWidth
;
819 *xl
= x
+ *delta_x
% Scr
.MyDisplayWidth
+ HorWarpSize
;
822 else if (Scr
.Vx
+ *delta_x
> Scr
.VxMax
)
824 if (!(Scr
.flags
.do_edge_wrap_x
))
826 *delta_x
= Scr
.VxMax
- Scr
.Vx
;
831 *delta_x
-= Scr
.VxMax
+Scr
.MyDisplayWidth
;
832 *xl
= x
+ *delta_x
% Scr
.MyDisplayWidth
- HorWarpSize
;
840 if (Scr
.Vy
+ *delta_y
< 0)
842 if (!(Scr
.flags
.do_edge_wrap_y
))
849 *delta_y
+= Scr
.VyMax
+ Scr
.MyDisplayHeight
;
850 *yt
= y
+ *delta_y
% Scr
.MyDisplayHeight
+ VertWarpSize
;
853 else if (Scr
.Vy
+ *delta_y
> Scr
.VyMax
)
855 if (!(Scr
.flags
.do_edge_wrap_y
))
857 *delta_y
= Scr
.VyMax
- Scr
.Vy
;
862 *delta_y
-= Scr
.VyMax
+ Scr
.MyDisplayHeight
;
863 *yt
= y
+ *delta_y
% Scr
.MyDisplayHeight
- VertWarpSize
;
871 /* make sure the pointer isn't warped into the panframes */
872 if (*xl
< edge_thickness
)
874 *xl
= edge_thickness
;
876 if (*yt
< edge_thickness
)
878 *yt
= edge_thickness
;
880 if (*xl
>= Scr
.MyDisplayWidth
- edge_thickness
)
882 *xl
= Scr
.MyDisplayWidth
- edge_thickness
-1;
884 if (*yt
>= Scr
.MyDisplayHeight
- edge_thickness
)
886 *yt
= Scr
.MyDisplayHeight
- edge_thickness
-1;
893 /* Turn off the rubberband if its on */
894 switch_move_resize_grid(False
);
895 FWarpPointer(dpy
,None
,Scr
.Root
,0,0,0,0,*xl
,*yt
);
896 MoveViewport(Scr
.Vx
+ *delta_x
,Scr
.Vy
+ *delta_y
,False
);
898 dpy
, Scr
.Root
, &JunkRoot
, &JunkChild
, xl
, yt
, &JunkX
,
899 &JunkY
, &JunkMask
) == False
)
901 /* pointer is on a different screen */
907 MyXUngrabServer(dpy
);
913 /* the root window is surrounded by four window slices, which are InputOnly.
914 * So you can see 'through' them, but they eat the input. An EnterEvent in
915 * one of these windows causes a Paging. The windows have the according cursor
916 * pointing in the pan direction or are hidden if there is no more panning
917 * in that direction. This is mostly intended to get a panning even atop
918 * of Motif applictions, which does not work yet. It seems Motif windows
919 * eat all mouse events.
921 * Hermann Dunkel, HEDU, dunkel@cul-ipn.uni-kiel.de 1/94
925 * checkPanFrames hides PanFrames if they are on the very border of the
926 * VIRTUAL screen and EdgeWrap for that direction is off.
927 * (A special cursor for the EdgeWrap border could be nice) HEDU
929 void checkPanFrames(void)
931 Bool do_unmap_l
= False
;
932 Bool do_unmap_r
= False
;
933 Bool do_unmap_t
= False
;
934 Bool do_unmap_b
= False
;
936 if (!Scr
.flags
.are_windows_captured
)
939 /* thickness of 0 means remove the pan frames */
940 if (edge_thickness
== 0)
947 /* Remove Pan frames if paging by edge-scroll is permanently or
948 * temporarily disabled */
949 if (Scr
.EdgeScrollX
== 0 || Scr
.VxMax
== 0)
954 if (Scr
.EdgeScrollY
== 0 || Scr
.VyMax
== 0)
959 if (Scr
.Vx
== 0 && !Scr
.flags
.do_edge_wrap_x
)
963 if (Scr
.Vx
== Scr
.VxMax
&& !Scr
.flags
.do_edge_wrap_x
)
967 if (Scr
.Vy
== 0 && !Scr
.flags
.do_edge_wrap_y
)
971 if (Scr
.Vy
== Scr
.VyMax
&& !Scr
.flags
.do_edge_wrap_y
)
976 /* correct the unmap variables if pan frame commands are set */
977 if (edge_thickness
!= 0)
979 if (Scr
.PanFrameLeft
.command
!= NULL
|| Scr
.PanFrameLeft
.command_leave
!= NULL
)
983 if (Scr
.PanFrameRight
.command
!= NULL
|| Scr
.PanFrameRight
.command_leave
!= NULL
)
987 if (Scr
.PanFrameBottom
.command
!= NULL
|| Scr
.PanFrameBottom
.command_leave
!= NULL
)
991 if (Scr
.PanFrameTop
.command
!= NULL
|| Scr
.PanFrameTop
.command_leave
!= NULL
)
998 * hide or show the windows
1004 if (Scr
.PanFrameLeft
.isMapped
)
1006 XUnmapWindow(dpy
, Scr
.PanFrameLeft
.win
);
1007 Scr
.PanFrameLeft
.isMapped
= False
;
1012 if (edge_thickness
!= last_edge_thickness
)
1015 dpy
, Scr
.PanFrameLeft
.win
, edge_thickness
,
1016 Scr
.MyDisplayHeight
);
1018 if (!Scr
.PanFrameLeft
.isMapped
)
1020 XMapRaised(dpy
, Scr
.PanFrameLeft
.win
);
1021 Scr
.PanFrameLeft
.isMapped
= True
;
1027 if (Scr
.PanFrameRight
.isMapped
)
1029 XUnmapWindow(dpy
, Scr
.PanFrameRight
.win
);
1030 Scr
.PanFrameRight
.isMapped
= False
;
1035 if (edge_thickness
!= last_edge_thickness
)
1038 dpy
, Scr
.PanFrameRight
.win
,
1039 Scr
.MyDisplayWidth
- edge_thickness
, 0,
1040 edge_thickness
, Scr
.MyDisplayHeight
);
1042 if (!Scr
.PanFrameRight
.isMapped
)
1044 XMapRaised(dpy
, Scr
.PanFrameRight
.win
);
1045 Scr
.PanFrameRight
.isMapped
= True
;
1051 if (Scr
.PanFrameTop
.isMapped
)
1053 XUnmapWindow(dpy
, Scr
.PanFrameTop
.win
);
1054 Scr
.PanFrameTop
.isMapped
= False
;
1059 if (edge_thickness
!= last_edge_thickness
)
1062 dpy
, Scr
.PanFrameTop
.win
, Scr
.MyDisplayWidth
,
1065 if (!Scr
.PanFrameTop
.isMapped
)
1067 XMapRaised(dpy
, Scr
.PanFrameTop
.win
);
1068 Scr
.PanFrameTop
.isMapped
= True
;
1074 if (Scr
.PanFrameBottom
.isMapped
)
1076 XUnmapWindow(dpy
, Scr
.PanFrameBottom
.win
);
1077 Scr
.PanFrameBottom
.isMapped
= False
;
1082 if (edge_thickness
!= last_edge_thickness
)
1085 dpy
, Scr
.PanFrameBottom
.win
, 0,
1086 Scr
.MyDisplayHeight
- edge_thickness
,
1087 Scr
.MyDisplayWidth
, edge_thickness
);
1089 if (!Scr
.PanFrameBottom
.isMapped
)
1091 XMapRaised(dpy
, Scr
.PanFrameBottom
.win
);
1092 Scr
.PanFrameBottom
.isMapped
= True
;
1095 last_edge_thickness
= edge_thickness
;
1102 * Gotta make sure these things are on top of everything else, or they
1105 * For some reason, this seems to be unneeded.
1108 void raisePanFrames(void)
1113 /* Note: make sure the stacking order of the pan frames is not changed
1114 * every time they are raised by using XRestackWindows. */
1116 if (Scr
.PanFrameTop
.isMapped
)
1118 windows
[n
++] = Scr
.PanFrameTop
.win
;
1120 if (Scr
.PanFrameLeft
.isMapped
)
1122 windows
[n
++] = Scr
.PanFrameLeft
.win
;
1124 if (Scr
.PanFrameRight
.isMapped
)
1126 windows
[n
++] = Scr
.PanFrameRight
.win
;
1128 if (Scr
.PanFrameBottom
.isMapped
)
1130 windows
[n
++] = Scr
.PanFrameBottom
.win
;
1134 XRaiseWindow(dpy
, windows
[0]);
1137 XRestackWindows(dpy
, windows
, n
);
1146 * Creates the windows for edge-scrolling
1149 void initPanFrames(void)
1151 XSetWindowAttributes attributes
;
1152 unsigned long valuemask
;
1153 int saved_thickness
;
1155 /* Not creating the frames disables all subsequent behavior */
1156 /* TKP. This is bad, it will cause an XMap request on a null window
1158 /* if (edge_thickness == 0) return; */
1159 saved_thickness
= edge_thickness
;
1160 if (edge_thickness
== 0)
1165 attributes
.event_mask
= XEVMASK_PANFW
;
1166 valuemask
= (CWEventMask
| CWCursor
);
1168 attributes
.cursor
= Scr
.FvwmCursors
[CRS_TOP_EDGE
];
1169 /* I know these overlap, it's useful when at (0,0) and the top one is
1171 Scr
.PanFrameTop
.win
= XCreateWindow(
1172 dpy
, Scr
.Root
, 0, 0, Scr
.MyDisplayWidth
, edge_thickness
,
1173 0, CopyFromParent
, InputOnly
, CopyFromParent
, valuemask
,
1175 attributes
.cursor
= Scr
.FvwmCursors
[CRS_LEFT_EDGE
];
1176 Scr
.PanFrameLeft
.win
= XCreateWindow(
1177 dpy
, Scr
.Root
, 0, 0, edge_thickness
, Scr
.MyDisplayHeight
,
1178 0, CopyFromParent
, InputOnly
, CopyFromParent
, valuemask
,
1180 attributes
.cursor
= Scr
.FvwmCursors
[CRS_RIGHT_EDGE
];
1181 Scr
.PanFrameRight
.win
= XCreateWindow(
1182 dpy
, Scr
.Root
, Scr
.MyDisplayWidth
- edge_thickness
, 0,
1183 edge_thickness
, Scr
.MyDisplayHeight
, 0, CopyFromParent
,
1184 InputOnly
, CopyFromParent
, valuemask
, &attributes
);
1185 attributes
.cursor
= Scr
.FvwmCursors
[CRS_BOTTOM_EDGE
];
1186 Scr
.PanFrameBottom
.win
= XCreateWindow(
1187 dpy
, Scr
.Root
, 0, Scr
.MyDisplayHeight
- edge_thickness
,
1188 Scr
.MyDisplayWidth
, edge_thickness
, 0, CopyFromParent
,
1189 InputOnly
, CopyFromParent
, valuemask
, &attributes
);
1190 Scr
.PanFrameTop
.isMapped
=Scr
.PanFrameLeft
.isMapped
=
1191 Scr
.PanFrameRight
.isMapped
= Scr
.PanFrameBottom
.isMapped
=False
;
1192 edge_thickness
= saved_thickness
;
1197 Bool
is_pan_frame(Window w
)
1199 if (w
== Scr
.PanFrameTop
.win
|| w
== Scr
.PanFrameBottom
.win
||
1200 w
== Scr
.PanFrameLeft
.win
|| w
== Scr
.PanFrameRight
.win
)
1212 * Moves the viewport within the virtual desktop
1215 void MoveViewport(int newx
, int newy
, Bool grab
)
1219 int PageTop
, PageLeft
;
1220 int PageBottom
, PageRight
;
1221 int txl
, txr
, tyt
, tyb
;
1227 if (newx
> Scr
.VxMax
)
1231 if (newy
> Scr
.VyMax
)
1243 deltay
= Scr
.Vy
- newy
;
1244 deltax
= Scr
.Vx
- newx
;
1246 Identify the bounding rectangle that will be moved into
1249 PageBottom
= Scr
.MyDisplayHeight
- deltay
- 1;
1250 PageRight
= Scr
.MyDisplayWidth
- deltax
- 1;
1251 PageTop
= 0 - deltay
;
1252 PageLeft
= 0 - deltax
;
1253 if (deltax
|| deltay
)
1255 prev_page_x
= Scr
.Vx
;
1256 prev_page_y
= Scr
.Vy
;
1257 prev_desk_and_page_page_x
= Scr
.Vx
;
1258 prev_desk_and_page_page_y
= Scr
.Vy
;
1259 prev_desk_and_page_desk
= Scr
.CurrentDesk
;
1264 if (deltax
|| deltay
)
1267 M_NEW_PAGE
, 7, (long)Scr
.Vx
, (long)Scr
.Vy
,
1268 (long)Scr
.CurrentDesk
, (long)Scr
.MyDisplayWidth
,
1269 (long)Scr
.MyDisplayHeight
,
1270 (long)((Scr
.VxMax
/ Scr
.MyDisplayWidth
) + 1),
1271 (long)((Scr
.VyMax
/ Scr
.MyDisplayHeight
) + 1));
1274 * RBW - 11/13/1998 - new: chase the chain
1275 * bidirectionally, all at once! The idea is to move the
1276 * windows that are moving out of the viewport from the bottom
1277 * of the stacking order up, to minimize the expose-redraw
1278 * overhead. Windows that will be moving into view will be
1279 * moved top down, for the same reason. Use the new
1280 * stacking-order chain, rather than the old last-focused
1283 * domivogt (29-Nov-1999): It's faster to first map windows
1284 * top to bottom and then unmap windows bottom up.
1286 t
= get_next_window_in_stack_ring(&Scr
.FvwmRoot
);
1287 while (t
!= &Scr
.FvwmRoot
)
1290 * If the window is moving into the viewport...
1294 txr
= t
->g
.frame
.x
+ t
->g
.frame
.width
- 1;
1295 tyb
= t
->g
.frame
.y
+ t
->g
.frame
.height
- 1;
1296 if (is_window_sticky_across_pages(t
) &&
1297 !IS_VIEWPORT_MOVED(t
))
1299 /* the absolute position has changed */
1300 t
->g
.normal
.x
-= deltax
;
1301 t
->g
.normal
.y
-= deltay
;
1302 t
->g
.max
.x
-= deltax
;
1303 t
->g
.max
.y
-= deltay
;
1304 /* Block double move. */
1305 SET_VIEWPORT_MOVED(t
, 1);
1307 if ((txr
>= PageLeft
&& txl
<= PageRight
1308 && tyb
>= PageTop
&& tyt
<= PageBottom
)
1309 && !IS_VIEWPORT_MOVED(t
)
1310 && !IS_WINDOW_BEING_MOVED_OPAQUE(t
))
1312 /* Block double move. */
1313 SET_VIEWPORT_MOVED(t
, 1);
1314 /* If the window is iconified, and sticky
1315 * Icons is set, then the window should
1316 * essentially be sticky */
1317 if (!is_window_sticky_across_pages(t
))
1319 if (IS_ICONIFIED(t
))
1321 modify_icon_position(
1323 move_icon_to_position(t
);
1324 broadcast_icon_geometry(
1328 t
, t
->g
.frame
.x
+ deltax
,
1329 t
->g
.frame
.y
+ deltay
,
1331 t
->g
.frame
.height
, False
);
1334 /* Bump to next win... */
1335 t
= get_next_window_in_stack_ring(t
);
1337 t1
= get_prev_window_in_stack_ring(&Scr
.FvwmRoot
);
1338 while (t1
!= &Scr
.FvwmRoot
)
1341 *If the window is not moving into the viewport...
1343 SET_VIEWPORT_MOVED(t
, 1);
1344 txl
= t1
->g
.frame
.x
;
1345 tyt
= t1
->g
.frame
.y
;
1346 txr
= t1
->g
.frame
.x
+ t1
->g
.frame
.width
- 1;
1347 tyb
= t1
->g
.frame
.y
+ t1
->g
.frame
.height
- 1;
1348 if (! (txr
>= PageLeft
&& txl
<= PageRight
1349 && tyb
>= PageTop
&& tyt
<= PageBottom
)
1350 && !IS_VIEWPORT_MOVED(t1
)
1351 && !IS_WINDOW_BEING_MOVED_OPAQUE(t1
))
1353 /* If the window is iconified, and sticky
1354 * Icons is set, then the window should
1355 * essentially be sticky */
1356 if (!is_window_sticky_across_pages(t1
))
1358 if (IS_ICONIFIED(t1
))
1360 modify_icon_position(
1361 t1
, deltax
, deltay
);
1362 move_icon_to_position(t1
);
1363 broadcast_icon_geometry(
1367 t1
, t1
->g
.frame
.x
+ deltax
,
1368 t1
->g
.frame
.y
+ deltay
,
1370 t1
->g
.frame
.height
, False
);
1373 /* Bump to next win... */
1374 t1
= get_prev_window_in_stack_ring(t1
);
1376 for (t
= Scr
.FvwmRoot
.next
; t
!= NULL
; t
= t
->next
)
1378 if (IS_VIEWPORT_MOVED(t
))
1380 /* Clear double move blocker. */
1381 SET_VIEWPORT_MOVED(t
, 0);
1382 GNOME_SetWinArea(t
);
1384 /* If its an icon, and its sticking, autoplace it so
1385 * that it doesn't wind up on top a a stationary
1387 if (is_window_sticky_across_pages(t
) &&
1388 IS_ICONIFIED(t
) && !IS_ICON_MOVED(t
) &&
1389 !IS_ICON_UNMAPPED(t
))
1391 AutoPlaceIcon(t
, NULL
, True
);
1396 /* regrab buttons in case something got obscured or unobscured */
1397 focus_grab_buttons_all();
1400 /* dv (2004-07-01): I don't think that's a good idea. We could eat too
1402 /* do this with PanFrames too ??? HEDU */
1406 while (FCheckTypedEvent(dpy
, MotionNotify
, &e
))
1414 MyXUngrabServer(dpy
);
1416 /* update GNOME pager */
1417 GNOME_SetCurrentArea();
1418 EWMH_SetDesktopViewPort();
1423 void goto_desk(int desk
)
1425 /* RBW - the unmapping operations are now removed to their own
1426 * functions so they can also be used by the new GoToDeskAndPage
1428 if (Scr
.CurrentDesk
!= desk
)
1430 prev_desk
= Scr
.CurrentDesk
;
1431 prev_desk_and_page_desk
= Scr
.CurrentDesk
;
1432 prev_desk_and_page_page_x
= Scr
.Vx
;
1433 prev_desk_and_page_page_y
= Scr
.Vy
;
1434 UnmapDesk(Scr
.CurrentDesk
, True
);
1435 Scr
.CurrentDesk
= desk
;
1436 MapDesk(desk
, True
);
1437 focus_grab_buttons_all();
1438 BroadcastPacket(M_NEW_DESK
, 1, (long)Scr
.CurrentDesk
);
1439 /* FIXME: domivogt (22-Apr-2000): Fake a 'restack' for sticky
1440 * window upon desk change. This is a workaround for a
1441 * problem in FvwmPager: The pager has a separate 'root'
1442 * window for each desk. If the active desk changes, the
1443 * pager destroys sticky mini windows and creates new ones in
1444 * the other desktop 'root'. But the pager can't know where to
1445 * stack them. So we have to tell it explicitly where they
1446 * go :-( This should be fixed in the pager, but right now the
1447 * pager doesn't maintain the stacking order. */
1448 BroadcastRestackAllWindows();
1449 EWMH_SetCurrentDesktop();
1450 GNOME_SetCurrentDesk();
1451 GNOME_SetDeskCount();
1459 * Move a window to a new desktop
1462 void do_move_window_to_desk(FvwmWindow
*fw
, int desk
)
1470 * Set the window's desktop, and map or unmap it as needed.
1472 /* Only change mapping for non-sticky windows */
1473 if (!is_window_sticky_across_desks(fw
) /*&& !IS_ICON_UNMAPPED(fw)*/)
1475 if (fw
->Desk
== Scr
.CurrentDesk
)
1478 if (fw
== get_focus_window())
1483 SET_FULLY_VISIBLE(fw
, 0);
1484 SET_PARTIALLY_VISIBLE(fw
, 0);
1486 else if (desk
== Scr
.CurrentDesk
)
1489 /* If its an icon, auto-place it */
1490 if (IS_ICONIFIED(fw
))
1492 AutoPlaceIcon(fw
, NULL
, True
);
1500 BroadcastConfig(M_CONFIGURE_WINDOW
,fw
);
1502 focus_grab_buttons_on_layer(fw
->layer
);
1503 EWMH_SetWMDesktop(fw
);
1504 GNOME_SetDeskCount();
1506 GNOME_SetWinArea(fw
);
1511 Bool
get_page_arguments(char *action
, int *page_x
, int *page_y
)
1530 for (; ; action
= taction
)
1534 token
= PeekToken(action
, &taction
);
1541 if (StrEquals(token
, "prev"))
1543 /* last page selected */
1544 *page_x
= prev_page_x
;
1545 *page_y
= prev_page_y
;
1549 for ( ; *token
== '!'; token
++)
1551 do_reverse
= !do_reverse
;
1553 if (StrEquals(token
, "wrapx"))
1555 wrapx
= (1 ^ do_reverse
);
1557 else if (StrEquals(token
, "wrapy"))
1559 wrapy
= (1 ^ do_reverse
);
1561 else if (StrEquals(token
, "nodesklimitx"))
1563 limitdeskx
= (0 ^ do_reverse
);
1565 else if (StrEquals(token
, "nodesklimitx"))
1567 limitdesky
= (0 ^ do_reverse
);
1571 /* no more options */
1576 if (GetSuffixedIntegerArguments(action
, NULL
, val
, 2, "pw", suffix
) !=
1584 *page_x
= val
[0] * Scr
.MyDisplayWidth
+ Scr
.Vx
;
1586 else if (suffix
[0] == 2)
1588 *page_x
+= val
[0] * Scr
.MyDisplayWidth
;
1590 else if (val
[0] >= 0)
1592 *page_x
= val
[0] * Scr
.MyDisplayWidth
;
1596 *page_x
= (val
[0] + 1) * Scr
.MyDisplayWidth
+ Scr
.VxMax
;
1600 *page_y
= val
[1] * Scr
.MyDisplayHeight
+ Scr
.Vy
;
1602 else if (suffix
[1] == 2)
1604 *page_y
+= val
[1] * Scr
.MyDisplayHeight
;
1606 else if (val
[1] >= 0)
1608 *page_y
= val
[1] * Scr
.MyDisplayHeight
;
1612 *page_y
= (val
[1] + 1) * Scr
.MyDisplayHeight
+ Scr
.VyMax
;
1615 /* limit to desktop size */
1616 if (limitdeskx
&& !wrapx
)
1622 else if (*page_x
> Scr
.VxMax
)
1624 *page_x
= Scr
.VxMax
;
1627 else if (limitdeskx
&& wrapx
)
1631 *page_x
+= Scr
.VxMax
+ Scr
.MyDisplayWidth
;
1633 while (*page_x
> Scr
.VxMax
)
1635 *page_x
-= Scr
.VxMax
+ Scr
.MyDisplayWidth
;
1638 if (limitdesky
&& !wrapy
)
1644 else if (*page_y
> Scr
.VyMax
)
1646 *page_y
= Scr
.VyMax
;
1649 else if (limitdesky
&& wrapy
)
1653 *page_y
+= Scr
.VyMax
+ Scr
.MyDisplayHeight
;
1655 while (*page_y
> Scr
.VyMax
)
1657 *page_y
-= Scr
.VyMax
+ Scr
.MyDisplayHeight
;
1664 char *GetDesktopName(int desk
)
1668 d
= Scr
.Desktops
->next
;
1669 while (d
!= NULL
&& d
->desk
!= desk
)
1681 /* ---------------------------- builtin commands --------------------------- */
1683 /* EdgeCommand - binds a function to a pan frame enter event */
1684 void CMD_EdgeCommand(F_CMD_ARGS
)
1686 direction_t direction
;
1689 /* get the direction */
1690 direction
= gravity_parse_dir_argument(action
, &action
, DIR_NONE
);
1692 if (direction
>= 0 && direction
<= DIR_MAJOR_MASK
)
1695 /* check if the command does contain at least one token */
1696 command
= safestrdup(action
);
1697 if (PeekToken(action
, &action
) == NULL
)
1699 /* the command does not contain a token so
1700 the command of this edge is removed */
1704 /* assign command to the edge(s) */
1705 if (direction
== DIR_N
)
1707 if (Scr
.PanFrameTop
.command
!= NULL
)
1709 free(Scr
.PanFrameTop
.command
);
1711 Scr
.PanFrameTop
.command
= command
;
1713 else if (direction
== DIR_S
)
1715 if (Scr
.PanFrameBottom
.command
!= NULL
)
1717 free(Scr
.PanFrameBottom
.command
);
1719 Scr
.PanFrameBottom
.command
= command
;
1721 else if (direction
== DIR_W
)
1723 if (Scr
.PanFrameLeft
.command
!= NULL
)
1725 free(Scr
.PanFrameLeft
.command
);
1727 Scr
.PanFrameLeft
.command
= command
;
1729 else if (direction
== DIR_E
)
1731 if (Scr
.PanFrameRight
.command
!= NULL
)
1733 free(Scr
.PanFrameRight
.command
);
1735 Scr
.PanFrameRight
.command
= command
;
1739 /* this should never happen */
1740 fvwm_msg(ERR
, "EdgeCommand",
1741 "Internal error in CMD_EdgeCommand");
1748 /* check if the argument does contain at least one token */
1749 if (PeekToken(action
, &action
) == NULL
)
1751 /* Just plain EdgeCommand, so all edge commands are
1753 if (Scr
.PanFrameTop
.command
!= NULL
)
1755 free(Scr
.PanFrameTop
.command
);
1756 Scr
.PanFrameTop
.command
= NULL
;
1758 if (Scr
.PanFrameBottom
.command
!= NULL
)
1760 free(Scr
.PanFrameBottom
.command
);
1761 Scr
.PanFrameBottom
.command
= NULL
;
1763 if (Scr
.PanFrameLeft
.command
!= NULL
)
1765 free(Scr
.PanFrameLeft
.command
);
1766 Scr
.PanFrameLeft
.command
= NULL
;
1768 if (Scr
.PanFrameRight
.command
!= NULL
)
1770 free(Scr
.PanFrameRight
.command
);
1771 Scr
.PanFrameRight
.command
= NULL
;
1776 /* not a proper direction */
1777 fvwm_msg(ERR
, "EdgeCommand",
1778 "EdgeCommand [direction [function]]");
1782 /* maybe something has changed so we adapt the pan frames */
1788 /* EdgeLeaveCommand - binds a function to a pan frame Leave event */
1789 void CMD_EdgeLeaveCommand(F_CMD_ARGS
)
1791 direction_t direction
;
1794 /* get the direction */
1795 direction
= gravity_parse_dir_argument(action
, &action
, DIR_NONE
);
1797 if (direction
>= 0 && direction
<= DIR_MAJOR_MASK
)
1800 /* check if the command does contain at least one token */
1801 command
= safestrdup(action
);
1802 if (PeekToken(action
, &action
) == NULL
)
1804 /* the command does not contain a token so
1805 the command of this edge is removed */
1809 /* assign command to the edge(s) */
1810 if (direction
== DIR_N
)
1812 if (Scr
.PanFrameTop
.command_leave
!= NULL
)
1814 free(Scr
.PanFrameTop
.command_leave
);
1816 Scr
.PanFrameTop
.command_leave
= command
;
1818 else if (direction
== DIR_S
)
1820 if (Scr
.PanFrameBottom
.command_leave
!= NULL
)
1822 free(Scr
.PanFrameBottom
.command_leave
);
1824 Scr
.PanFrameBottom
.command_leave
= command
;
1826 else if (direction
== DIR_W
)
1828 if (Scr
.PanFrameLeft
.command_leave
!= NULL
)
1830 free(Scr
.PanFrameLeft
.command_leave
);
1832 Scr
.PanFrameLeft
.command_leave
= command
;
1834 else if (direction
== DIR_E
)
1836 if (Scr
.PanFrameRight
.command_leave
!= NULL
)
1838 free(Scr
.PanFrameRight
.command_leave
);
1840 Scr
.PanFrameRight
.command_leave
= command
;
1844 /* this should never happen */
1845 fvwm_msg(ERR
, "EdgeLeaveCommand",
1846 "Internal error in CMD_EdgeLeaveCommand");
1853 /* check if the argument does contain at least one token */
1854 if (PeekToken(action
, &action
) == NULL
)
1856 /* Just plain EdgeLeaveCommand, so all edge commands are
1858 if (Scr
.PanFrameTop
.command_leave
!= NULL
)
1860 free(Scr
.PanFrameTop
.command_leave
);
1861 Scr
.PanFrameTop
.command_leave
= NULL
;
1863 if (Scr
.PanFrameBottom
.command_leave
!= NULL
)
1865 free(Scr
.PanFrameBottom
.command_leave
);
1866 Scr
.PanFrameBottom
.command_leave
= NULL
;
1868 if (Scr
.PanFrameLeft
.command_leave
!= NULL
)
1870 free(Scr
.PanFrameLeft
.command_leave
);
1871 Scr
.PanFrameLeft
.command_leave
= NULL
;
1873 if (Scr
.PanFrameRight
.command_leave
!= NULL
)
1875 free(Scr
.PanFrameRight
.command_leave
);
1876 Scr
.PanFrameRight
.command_leave
= NULL
;
1881 /* not a proper direction */
1882 fvwm_msg(ERR
, "EdgeLeaveCommand",
1883 "EdgeLeaveCommand [direction [function]]");
1887 /* maybe something has changed so we adapt the pan frames */
1893 void CMD_EdgeThickness(F_CMD_ARGS
)
1897 n
= GetIntegerArguments(action
, NULL
, &val
, 1);
1900 fvwm_msg(ERR
,"setEdgeThickness",
1901 "EdgeThickness requires 1 numeric argument,"
1902 " found %d args",n
);
1906 if (val
< 0 || val
> 2)
1908 fvwm_msg(ERR
,"setEdgeThickness",
1909 "EdgeThickness arg must be between 0 and 2,"
1913 edge_thickness
= val
;
1919 void CMD_EdgeScroll(F_CMD_ARGS
)
1921 int val1
, val2
, val1_unit
, val2_unit
, n
;
1924 n
= GetTwoArguments(action
, &val1
, &val2
, &val1_unit
, &val2_unit
);
1928 ERR
, "SetEdgeScroll",
1929 "EdgeScroll requires two arguments");
1934 * if edgescroll >1000 and <100000
1935 * wrap at edges of desktop (a "spherical" desktop)
1937 if (val1
>= 1000 && val1_unit
!= 100)
1940 Scr
.flags
.do_edge_wrap_x
= 1;
1944 Scr
.flags
.do_edge_wrap_x
= 0;
1946 if (val2
>= 1000 && val2_unit
!= 100)
1949 Scr
.flags
.do_edge_wrap_y
= 1;
1953 Scr
.flags
.do_edge_wrap_y
= 0;
1956 action
=SkipNTokens(action
,2);
1957 token
= PeekToken(action
, NULL
);
1961 if (StrEquals(token
, "wrap"))
1963 Scr
.flags
.do_edge_wrap_x
= 1;
1964 Scr
.flags
.do_edge_wrap_y
= 1;
1966 else if (StrEquals(token
, "wrapx"))
1968 Scr
.flags
.do_edge_wrap_x
= 1;
1970 else if (StrEquals(token
, "wrapy"))
1972 Scr
.flags
.do_edge_wrap_y
= 1;
1976 Scr
.EdgeScrollX
= val1
* val1_unit
/ 100;
1977 Scr
.EdgeScrollY
= val2
* val2_unit
/ 100;
1984 void CMD_EdgeResistance(F_CMD_ARGS
)
1990 n
= GetIntegerArguments(action
, NULL
, val
, 3);
1991 if (n
> 1 && val
[0] >= 10000)
1993 /* map val[0] >= 10000 in old syntax to -1 in new syntax */
1998 Scr
.ScrollDelay
= val
[0];
2000 else if (n
>= 2 && n
<= 3)
2006 Scr
.ScrollDelay
= val
[0];
2007 sprintf(cmd
, "EdgeResistance %d", val
[0]);
2008 sprintf(stylecmd
, "Style * EdgeMoveDelay %d", val
[0]);
2012 stylecmd2
, "Style * EdgeMoveResistance %d",
2018 stylecmd2
, "Style * EdgeMoveResistance %d %d",
2022 OLD
, "CMD_EdgeResistance",
2023 "The command EdgeResistance with three arguments is"
2024 " obsolete. Please use the following commands"
2026 fvwm_msg(OLD
, "", cmd
);
2027 fvwm_msg(OLD
, "", stylecmd
);
2028 fvwm_msg(OLD
, "", stylecmd2
);
2031 FUNC_DONT_REPEAT
| FUNC_DONT_EXPAND_COMMAND
);
2033 cond_rc
, exc
, stylecmd
,
2034 FUNC_DONT_REPEAT
| FUNC_DONT_EXPAND_COMMAND
);
2036 cond_rc
, exc
, stylecmd2
,
2037 FUNC_DONT_REPEAT
| FUNC_DONT_EXPAND_COMMAND
);
2042 ERR
, "CMD_EdgeResistance",
2043 "EdgeResistance requires two or three arguments");
2050 void CMD_Xinerama(F_CMD_ARGS
)
2054 toggle
= ParseToggleArgument(action
, NULL
, -1, 0);
2057 toggle
= !FScreenIsEnabled();
2059 if (!toggle
!= !FScreenIsEnabled())
2061 Scr
.flags
.do_need_window_update
= True
;
2062 Scr
.flags
.has_xinerama_state_changed
= True
;
2063 FScreenOnOff(toggle
);
2064 broadcast_xinerama_state();
2070 void CMD_XineramaPrimaryScreen(F_CMD_ARGS
)
2074 val
= FScreenGetScreenArgument(action
, 0);
2075 FScreenSetPrimaryScreen(val
);
2076 if (FScreenIsEnabled())
2078 Scr
.flags
.do_need_window_update
= True
;
2079 Scr
.flags
.has_xinerama_state_changed
= True
;
2081 broadcast_xinerama_state();
2086 void CMD_XineramaSls(F_CMD_ARGS
)
2090 toggle
= ParseToggleArgument(action
, NULL
, -1, 0);
2093 toggle
= !FScreenIsSLSEnabled();
2095 if (!toggle
!= !FScreenIsSLSEnabled())
2097 if (FScreenIsEnabled())
2099 Scr
.flags
.do_need_window_update
= True
;
2100 Scr
.flags
.has_xinerama_state_changed
= True
;
2102 FScreenSLSOnOff(toggle
);
2103 broadcast_xinerama_state();
2109 void CMD_XineramaSlsSize(F_CMD_ARGS
)
2113 if (GetIntegerArguments(action
, NULL
, val
, 2) != 2 &&
2114 GetRectangleArguments(action
, &val
[0], &val
[1]) != 2)
2119 if (FScreenConfigureSLSSize(val
[0], val
[1]))
2121 broadcast_xinerama_state();
2127 void CMD_XineramaSlsScreens(F_CMD_ARGS
)
2132 if (GetIntegerArguments(action
, &args
, &nscreens
, 1) != 1)
2137 else if (args
== NULL
)
2141 if (FScreenConfigureSLSScreens(nscreens
, args
))
2143 broadcast_xinerama_state();
2149 void CMD_DesktopSize(F_CMD_ARGS
)
2153 if (GetIntegerArguments(action
, NULL
, val
, 2) != 2 &&
2154 GetRectangleArguments(action
, &val
[0], &val
[1]) != 2)
2156 fvwm_msg(ERR
, "CMD_DesktopSize",
2157 "DesktopSize requires two arguments");
2161 Scr
.VxMax
= (val
[0] <= 0) ?
2162 0: val
[0]*Scr
.MyDisplayWidth
-Scr
.MyDisplayWidth
;
2163 Scr
.VyMax
= (val
[1] <= 0) ?
2164 0: val
[1]*Scr
.MyDisplayHeight
-Scr
.MyDisplayHeight
;
2166 M_NEW_PAGE
, 7, (long)Scr
.Vx
, (long)Scr
.Vy
,
2167 (long)Scr
.CurrentDesk
, (long)Scr
.MyDisplayWidth
,
2168 (long)Scr
.MyDisplayHeight
,
2169 (long)((Scr
.VxMax
/ Scr
.MyDisplayWidth
) + 1),
2170 (long)((Scr
.VyMax
/ Scr
.MyDisplayHeight
) + 1));
2173 /* update GNOME pager */
2174 GNOME_SetAreaCount();
2175 EWMH_SetDesktopGeometry();
2182 * Move to a new desktop
2185 void CMD_GotoDesk(F_CMD_ARGS
)
2187 goto_desk(GetDeskNumber(action
, Scr
.CurrentDesk
));
2192 void CMD_Desk(F_CMD_ARGS
)
2194 CMD_GotoDesk(F_PASS_ARGS
);
2201 * Move to a new desktop and page at the same time.
2202 * This function is designed for use by the Pager, and replaces the old
2203 * GoToDesk 0 10000 hack.
2204 * - unmap all windows on the current desk so they don't flash when the
2205 * viewport is moved, then switch the viewport, then the desk.
2208 void CMD_GotoDeskAndPage(F_CMD_ARGS
)
2213 if (MatchToken(action
, "prev"))
2215 val
[0] = prev_desk_and_page_desk
;
2216 val
[1] = prev_desk_and_page_page_x
;
2217 val
[2] = prev_desk_and_page_page_y
;
2219 else if (GetIntegerArguments(action
, NULL
, val
, 3) == 3)
2221 val
[1] *= Scr
.MyDisplayWidth
;
2222 val
[2] *= Scr
.MyDisplayHeight
;
2229 is_new_desk
= (Scr
.CurrentDesk
!= val
[0]);
2232 UnmapDesk(Scr
.CurrentDesk
, True
);
2234 prev_desk_and_page_page_x
= Scr
.Vx
;
2235 prev_desk_and_page_page_y
= Scr
.Vy
;
2236 MoveViewport(val
[1], val
[2], True
);
2239 prev_desk
= Scr
.CurrentDesk
;
2240 prev_desk_and_page_desk
= Scr
.CurrentDesk
;
2241 Scr
.CurrentDesk
= val
[0];
2242 MapDesk(val
[0], True
);
2243 focus_grab_buttons_all();
2244 BroadcastPacket(M_NEW_DESK
, 1, (long)Scr
.CurrentDesk
);
2245 /* FIXME: domivogt (22-Apr-2000): Fake a 'restack' for sticky
2246 * window upon desk change. This is a workaround for a
2247 * problem in FvwmPager: The pager has a separate 'root'
2248 * window for each desk. If the active desk changes, the
2249 * pager destroys sticky mini windows and creates new ones in
2250 * the other desktop 'root'. But the pager can't know where to
2251 * stack them. So we have to tell it ecplicitly where they
2252 * go :-( This should be fixed in the pager, but right now the
2253 * pager doesn't the stacking order. */
2254 BroadcastRestackAllWindows();
2258 BroadcastPacket(M_NEW_DESK
, 1, (long)Scr
.CurrentDesk
);
2260 EWMH_SetCurrentDesktop();
2261 GNOME_SetCurrentDesk();
2262 GNOME_SetDeskCount();
2267 void CMD_GotoPage(F_CMD_ARGS
)
2274 if (!get_page_arguments(action
, &x
, &y
))
2277 ERR
, "goto_page_func",
2278 "GotoPage: invalid arguments: %s", action
);
2297 MoveViewport(x
,y
,True
);
2302 /* function with parsing of command line */
2303 void CMD_MoveToDesk(F_CMD_ARGS
)
2306 FvwmWindow
* const fw
= exc
->w
.fw
;
2308 desk
= GetDeskNumber(action
, fw
->Desk
);
2309 if (desk
== fw
->Desk
)
2313 do_move_window_to_desk(fw
, desk
);
2318 void CMD_Scroll(F_CMD_ARGS
)
2321 int val1
, val2
, val1_unit
, val2_unit
;
2323 if (GetTwoArguments(action
, &val1
, &val2
, &val1_unit
, &val2_unit
) != 2)
2325 /* less then two integer parameters implies interactive
2326 * scroll check if we are scrolling in reverse direction */
2328 int scroll_speed
= 1;
2330 option
= PeekToken(action
, NULL
);
2333 if (StrEquals(option
, "Reverse"))
2338 __drag_viewport(exc
, scroll_speed
);
2342 if ((val1
> -100000)&&(val1
< 100000))
2344 x
= Scr
.Vx
+ val1
*val1_unit
/100;
2348 x
= Scr
.Vx
+ (val1
/1000)*val1_unit
/100;
2350 if ((val2
> -100000)&&(val2
< 100000))
2352 y
=Scr
.Vy
+ val2
*val2_unit
/100;
2356 y
= Scr
.Vy
+ (val2
/1000)*val2_unit
/100;
2358 if (((val1
<= -100000)||(val1
>= 100000))&&(x
>Scr
.VxMax
))
2362 xpixels
= (Scr
.VxMax
/ Scr
.MyDisplayWidth
+ 1) *
2365 y
+= Scr
.MyDisplayHeight
* (1+((x
-Scr
.VxMax
-1)/xpixels
));
2368 y
%= (Scr
.VyMax
/ Scr
.MyDisplayHeight
+ 1) *
2369 Scr
.MyDisplayHeight
;
2372 if (((val1
<= -100000)||(val1
>= 100000))&&(x
<0))
2375 y
-= Scr
.MyDisplayHeight
;
2381 if (((val2
<= -100000)||(val2
>= 100000))&&(y
>Scr
.VyMax
))
2383 int ypixels
= (Scr
.VyMax
/ Scr
.MyDisplayHeight
+ 1) *
2384 Scr
.MyDisplayHeight
;
2386 x
+= Scr
.MyDisplayWidth
* (1+((y
-Scr
.VyMax
-1)/ypixels
));
2389 x
%= (Scr
.VxMax
/ Scr
.MyDisplayWidth
+ 1) *
2393 if (((val2
<= -100000)||(val2
>= 100000))&&(y
<0))
2396 x
-= Scr
.MyDisplayWidth
;
2402 MoveViewport(x
,y
,True
);
2409 * Defines the name of a desktop
2412 void CMD_DesktopName(F_CMD_ARGS
)
2415 DesktopsInfo
*t
, *d
, *new, **prev
;
2417 if (GetIntegerArguments(action
, &action
, &desk
, 1) != 1)
2420 ERR
,"CMD_DesktopName",
2421 "First argument to DesktopName must be an integer: %s",
2426 d
= Scr
.Desktops
->next
;
2427 while (d
!= NULL
&& d
->desk
!= desk
)
2434 if (d
->name
!= NULL
)
2439 if (action
!= NULL
&& *action
&& *action
!= '\n')
2441 CopyString(&d
->name
, action
);
2446 /* new deskops entries: add it in order */
2447 d
= Scr
.Desktops
->next
;
2449 prev
= &(Scr
.Desktops
->next
);
2450 while (d
!= NULL
&& d
->desk
< desk
)
2458 /* add it at the end */
2459 *prev
= (DesktopsInfo
*)safemalloc(
2460 sizeof(DesktopsInfo
));
2461 memset(*prev
, 0, sizeof(DesktopsInfo
));
2462 (*prev
)->desk
= desk
;
2463 if (action
!= NULL
&& *action
&& *action
!= '\n')
2465 CopyString(&((*prev
)->name
), action
);
2471 new = (DesktopsInfo
*)safemalloc(sizeof(DesktopsInfo
));
2472 memset(new, 0, sizeof(DesktopsInfo
));
2474 if (action
!= NULL
&& *action
&& *action
!= '\n')
2476 CopyString(&(new->name
), action
);
2481 /* should check/set the working areas */
2484 if (!fFvwmInStartup
)
2487 const char *default_desk_name
= _("Desk");
2489 /* should send the info to the FvwmPager and set the EWMH
2491 if (action
!= NULL
&& *action
&& *action
!= '\n')
2493 msg
= (char *)safemalloc(strlen(action
) + 44);
2494 sprintf(msg
, "DesktopName %d %s", desk
, action
);
2498 msg
= (char *)safemalloc(strlen(default_desk_name
)+44);
2500 msg
, "DesktopName %d %s %d", desk
,
2501 default_desk_name
, desk
);
2503 BroadcastConfigInfoString(msg
);
2505 EWMH_SetDesktopNames();