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 /* Ouch! lots of bounds checking */
802 if (Scr
.Vx
+ *delta_x
< 0)
804 if (!(Scr
.flags
.do_edge_wrap_x
))
811 *delta_x
+= Scr
.VxMax
+ Scr
.MyDisplayWidth
;
812 *xl
= x
+ *delta_x
% Scr
.MyDisplayWidth
+ HorWarpSize
;
815 else if (Scr
.Vx
+ *delta_x
> Scr
.VxMax
)
817 if (!(Scr
.flags
.do_edge_wrap_x
))
819 *delta_x
= Scr
.VxMax
- Scr
.Vx
;
824 *delta_x
-= Scr
.VxMax
+Scr
.MyDisplayWidth
;
825 *xl
= x
+ *delta_x
% Scr
.MyDisplayWidth
- HorWarpSize
;
833 if (Scr
.Vy
+ *delta_y
< 0)
835 if (!(Scr
.flags
.do_edge_wrap_y
))
842 *delta_y
+= Scr
.VyMax
+ Scr
.MyDisplayHeight
;
843 *yt
= y
+ *delta_y
% Scr
.MyDisplayHeight
+ VertWarpSize
;
846 else if (Scr
.Vy
+ *delta_y
> Scr
.VyMax
)
848 if (!(Scr
.flags
.do_edge_wrap_y
))
850 *delta_y
= Scr
.VyMax
- Scr
.Vy
;
855 *delta_y
-= Scr
.VyMax
+ Scr
.MyDisplayHeight
;
856 *yt
= y
+ *delta_y
% Scr
.MyDisplayHeight
- VertWarpSize
;
864 /* Check for paging -- and don't warp the pointer. */
865 is_timestamp_valid
= False
;
867 if (*delta_x
== 0 && *delta_y
== 0)
872 /* make sure the pointer isn't warped into the panframes */
873 if (*xl
< edge_thickness
)
875 *xl
= edge_thickness
;
877 if (*yt
< edge_thickness
)
879 *yt
= edge_thickness
;
881 if (*xl
>= Scr
.MyDisplayWidth
- edge_thickness
)
883 *xl
= Scr
.MyDisplayWidth
- edge_thickness
-1;
885 if (*yt
>= Scr
.MyDisplayHeight
- edge_thickness
)
887 *yt
= Scr
.MyDisplayHeight
- edge_thickness
-1;
894 /* Turn off the rubberband if its on */
895 switch_move_resize_grid(False
);
896 FWarpPointer(dpy
,None
,Scr
.Root
,0,0,0,0,*xl
,*yt
);
897 MoveViewport(Scr
.Vx
+ *delta_x
,Scr
.Vy
+ *delta_y
,False
);
899 dpy
, Scr
.Root
, &JunkRoot
, &JunkChild
, xl
, yt
, &JunkX
,
900 &JunkY
, &JunkMask
) == False
)
902 /* pointer is on a different screen */
908 MyXUngrabServer(dpy
);
914 /* the root window is surrounded by four window slices, which are InputOnly.
915 * So you can see 'through' them, but they eat the input. An EnterEvent in
916 * one of these windows causes a Paging. The windows have the according cursor
917 * pointing in the pan direction or are hidden if there is no more panning
918 * in that direction. This is mostly intended to get a panning even atop
919 * of Motif applictions, which does not work yet. It seems Motif windows
920 * eat all mouse events.
922 * Hermann Dunkel, HEDU, dunkel@cul-ipn.uni-kiel.de 1/94
926 * checkPanFrames hides PanFrames if they are on the very border of the
927 * VIRTUAL screen and EdgeWrap for that direction is off.
928 * (A special cursor for the EdgeWrap border could be nice) HEDU
930 void checkPanFrames(void)
932 Bool do_unmap_l
= False
;
933 Bool do_unmap_r
= False
;
934 Bool do_unmap_t
= False
;
935 Bool do_unmap_b
= False
;
937 if (!Scr
.flags
.are_windows_captured
)
940 /* thickness of 0 means remove the pan frames */
941 if (edge_thickness
== 0)
948 /* Remove Pan frames if paging by edge-scroll is permanently or
949 * temporarily disabled */
950 if (Scr
.EdgeScrollX
== 0 || Scr
.VxMax
== 0)
955 if (Scr
.EdgeScrollY
== 0 || Scr
.VyMax
== 0)
960 if (Scr
.Vx
== 0 && !Scr
.flags
.do_edge_wrap_x
)
964 if (Scr
.Vx
== Scr
.VxMax
&& !Scr
.flags
.do_edge_wrap_x
)
968 if (Scr
.Vy
== 0 && !Scr
.flags
.do_edge_wrap_y
)
972 if (Scr
.Vy
== Scr
.VyMax
&& !Scr
.flags
.do_edge_wrap_y
)
977 /* correct the unmap variables if pan frame commands are set */
978 if (edge_thickness
!= 0)
980 if (Scr
.PanFrameLeft
.command
!= NULL
|| Scr
.PanFrameLeft
.command_leave
!= NULL
)
984 if (Scr
.PanFrameRight
.command
!= NULL
|| Scr
.PanFrameRight
.command_leave
!= NULL
)
988 if (Scr
.PanFrameBottom
.command
!= NULL
|| Scr
.PanFrameBottom
.command_leave
!= NULL
)
992 if (Scr
.PanFrameTop
.command
!= NULL
|| Scr
.PanFrameTop
.command_leave
!= NULL
)
999 * hide or show the windows
1005 if (Scr
.PanFrameLeft
.isMapped
)
1007 XUnmapWindow(dpy
, Scr
.PanFrameLeft
.win
);
1008 Scr
.PanFrameLeft
.isMapped
= False
;
1013 if (edge_thickness
!= last_edge_thickness
)
1016 dpy
, Scr
.PanFrameLeft
.win
, edge_thickness
,
1017 Scr
.MyDisplayHeight
);
1019 if (!Scr
.PanFrameLeft
.isMapped
)
1021 XMapRaised(dpy
, Scr
.PanFrameLeft
.win
);
1022 Scr
.PanFrameLeft
.isMapped
= True
;
1028 if (Scr
.PanFrameRight
.isMapped
)
1030 XUnmapWindow(dpy
, Scr
.PanFrameRight
.win
);
1031 Scr
.PanFrameRight
.isMapped
= False
;
1036 if (edge_thickness
!= last_edge_thickness
)
1039 dpy
, Scr
.PanFrameRight
.win
,
1040 Scr
.MyDisplayWidth
- edge_thickness
, 0,
1041 edge_thickness
, Scr
.MyDisplayHeight
);
1043 if (!Scr
.PanFrameRight
.isMapped
)
1045 XMapRaised(dpy
, Scr
.PanFrameRight
.win
);
1046 Scr
.PanFrameRight
.isMapped
= True
;
1052 if (Scr
.PanFrameTop
.isMapped
)
1054 XUnmapWindow(dpy
, Scr
.PanFrameTop
.win
);
1055 Scr
.PanFrameTop
.isMapped
= False
;
1060 if (edge_thickness
!= last_edge_thickness
)
1063 dpy
, Scr
.PanFrameTop
.win
, Scr
.MyDisplayWidth
,
1066 if (!Scr
.PanFrameTop
.isMapped
)
1068 XMapRaised(dpy
, Scr
.PanFrameTop
.win
);
1069 Scr
.PanFrameTop
.isMapped
= True
;
1075 if (Scr
.PanFrameBottom
.isMapped
)
1077 XUnmapWindow(dpy
, Scr
.PanFrameBottom
.win
);
1078 Scr
.PanFrameBottom
.isMapped
= False
;
1083 if (edge_thickness
!= last_edge_thickness
)
1086 dpy
, Scr
.PanFrameBottom
.win
, 0,
1087 Scr
.MyDisplayHeight
- edge_thickness
,
1088 Scr
.MyDisplayWidth
, edge_thickness
);
1090 if (!Scr
.PanFrameBottom
.isMapped
)
1092 XMapRaised(dpy
, Scr
.PanFrameBottom
.win
);
1093 Scr
.PanFrameBottom
.isMapped
= True
;
1096 last_edge_thickness
= edge_thickness
;
1103 * Gotta make sure these things are on top of everything else, or they
1106 * For some reason, this seems to be unneeded.
1109 void raisePanFrames(void)
1114 /* Note: make sure the stacking order of the pan frames is not changed
1115 * every time they are raised by using XRestackWindows. */
1117 if (Scr
.PanFrameTop
.isMapped
)
1119 windows
[n
++] = Scr
.PanFrameTop
.win
;
1121 if (Scr
.PanFrameLeft
.isMapped
)
1123 windows
[n
++] = Scr
.PanFrameLeft
.win
;
1125 if (Scr
.PanFrameRight
.isMapped
)
1127 windows
[n
++] = Scr
.PanFrameRight
.win
;
1129 if (Scr
.PanFrameBottom
.isMapped
)
1131 windows
[n
++] = Scr
.PanFrameBottom
.win
;
1135 XRaiseWindow(dpy
, windows
[0]);
1138 XRestackWindows(dpy
, windows
, n
);
1147 * Creates the windows for edge-scrolling
1150 void initPanFrames(void)
1152 XSetWindowAttributes attributes
;
1153 unsigned long valuemask
;
1154 int saved_thickness
;
1156 /* Not creating the frames disables all subsequent behavior */
1157 /* TKP. This is bad, it will cause an XMap request on a null window
1159 /* if (edge_thickness == 0) return; */
1160 saved_thickness
= edge_thickness
;
1161 if (edge_thickness
== 0)
1166 attributes
.event_mask
= XEVMASK_PANFW
;
1167 valuemask
= (CWEventMask
| CWCursor
);
1169 attributes
.cursor
= Scr
.FvwmCursors
[CRS_TOP_EDGE
];
1170 /* I know these overlap, it's useful when at (0,0) and the top one is
1172 Scr
.PanFrameTop
.win
= XCreateWindow(
1173 dpy
, Scr
.Root
, 0, 0, Scr
.MyDisplayWidth
, edge_thickness
,
1174 0, CopyFromParent
, InputOnly
, CopyFromParent
, valuemask
,
1176 attributes
.cursor
= Scr
.FvwmCursors
[CRS_LEFT_EDGE
];
1177 Scr
.PanFrameLeft
.win
= XCreateWindow(
1178 dpy
, Scr
.Root
, 0, 0, edge_thickness
, Scr
.MyDisplayHeight
,
1179 0, CopyFromParent
, InputOnly
, CopyFromParent
, valuemask
,
1181 attributes
.cursor
= Scr
.FvwmCursors
[CRS_RIGHT_EDGE
];
1182 Scr
.PanFrameRight
.win
= XCreateWindow(
1183 dpy
, Scr
.Root
, Scr
.MyDisplayWidth
- edge_thickness
, 0,
1184 edge_thickness
, Scr
.MyDisplayHeight
, 0, CopyFromParent
,
1185 InputOnly
, CopyFromParent
, valuemask
, &attributes
);
1186 attributes
.cursor
= Scr
.FvwmCursors
[CRS_BOTTOM_EDGE
];
1187 Scr
.PanFrameBottom
.win
= XCreateWindow(
1188 dpy
, Scr
.Root
, 0, Scr
.MyDisplayHeight
- edge_thickness
,
1189 Scr
.MyDisplayWidth
, edge_thickness
, 0, CopyFromParent
,
1190 InputOnly
, CopyFromParent
, valuemask
, &attributes
);
1191 Scr
.PanFrameTop
.isMapped
=Scr
.PanFrameLeft
.isMapped
=
1192 Scr
.PanFrameRight
.isMapped
= Scr
.PanFrameBottom
.isMapped
=False
;
1193 edge_thickness
= saved_thickness
;
1198 Bool
is_pan_frame(Window w
)
1200 if (w
== Scr
.PanFrameTop
.win
|| w
== Scr
.PanFrameBottom
.win
||
1201 w
== Scr
.PanFrameLeft
.win
|| w
== Scr
.PanFrameRight
.win
)
1213 * Moves the viewport within the virtual desktop
1216 void MoveViewport(int newx
, int newy
, Bool grab
)
1220 int PageTop
, PageLeft
;
1221 int PageBottom
, PageRight
;
1222 int txl
, txr
, tyt
, tyb
;
1228 if (newx
> Scr
.VxMax
)
1232 if (newy
> Scr
.VyMax
)
1244 deltay
= Scr
.Vy
- newy
;
1245 deltax
= Scr
.Vx
- newx
;
1247 Identify the bounding rectangle that will be moved into
1250 PageBottom
= Scr
.MyDisplayHeight
- deltay
- 1;
1251 PageRight
= Scr
.MyDisplayWidth
- deltax
- 1;
1252 PageTop
= 0 - deltay
;
1253 PageLeft
= 0 - deltax
;
1254 if (deltax
|| deltay
)
1256 prev_page_x
= Scr
.Vx
;
1257 prev_page_y
= Scr
.Vy
;
1258 prev_desk_and_page_page_x
= Scr
.Vx
;
1259 prev_desk_and_page_page_y
= Scr
.Vy
;
1260 prev_desk_and_page_desk
= Scr
.CurrentDesk
;
1265 if (deltax
|| deltay
)
1268 M_NEW_PAGE
, 7, (long)Scr
.Vx
, (long)Scr
.Vy
,
1269 (long)Scr
.CurrentDesk
, (long)Scr
.MyDisplayWidth
,
1270 (long)Scr
.MyDisplayHeight
,
1271 (long)((Scr
.VxMax
/ Scr
.MyDisplayWidth
) + 1),
1272 (long)((Scr
.VyMax
/ Scr
.MyDisplayHeight
) + 1));
1275 * RBW - 11/13/1998 - new: chase the chain
1276 * bidirectionally, all at once! The idea is to move the
1277 * windows that are moving out of the viewport from the bottom
1278 * of the stacking order up, to minimize the expose-redraw
1279 * overhead. Windows that will be moving into view will be
1280 * moved top down, for the same reason. Use the new
1281 * stacking-order chain, rather than the old last-focused
1284 * domivogt (29-Nov-1999): It's faster to first map windows
1285 * top to bottom and then unmap windows bottom up.
1287 t
= get_next_window_in_stack_ring(&Scr
.FvwmRoot
);
1288 while (t
!= &Scr
.FvwmRoot
)
1291 * If the window is moving into the viewport...
1295 txr
= t
->g
.frame
.x
+ t
->g
.frame
.width
- 1;
1296 tyb
= t
->g
.frame
.y
+ t
->g
.frame
.height
- 1;
1297 if (is_window_sticky_across_pages(t
) &&
1298 !IS_VIEWPORT_MOVED(t
))
1300 /* the absolute position has changed */
1301 t
->g
.normal
.x
-= deltax
;
1302 t
->g
.normal
.y
-= deltay
;
1303 t
->g
.max
.x
-= deltax
;
1304 t
->g
.max
.y
-= deltay
;
1305 /* Block double move. */
1306 SET_VIEWPORT_MOVED(t
, 1);
1308 if ((txr
>= PageLeft
&& txl
<= PageRight
1309 && tyb
>= PageTop
&& tyt
<= PageBottom
)
1310 && !IS_VIEWPORT_MOVED(t
)
1311 && !IS_WINDOW_BEING_MOVED_OPAQUE(t
))
1313 /* Block double move. */
1314 SET_VIEWPORT_MOVED(t
, 1);
1315 /* If the window is iconified, and sticky
1316 * Icons is set, then the window should
1317 * essentially be sticky */
1318 if (!is_window_sticky_across_pages(t
))
1320 if (IS_ICONIFIED(t
))
1322 modify_icon_position(
1324 move_icon_to_position(t
);
1325 broadcast_icon_geometry(
1329 t
, t
->g
.frame
.x
+ deltax
,
1330 t
->g
.frame
.y
+ deltay
,
1332 t
->g
.frame
.height
, False
);
1335 /* Bump to next win... */
1336 t
= get_next_window_in_stack_ring(t
);
1338 t1
= get_prev_window_in_stack_ring(&Scr
.FvwmRoot
);
1339 while (t1
!= &Scr
.FvwmRoot
)
1342 *If the window is not moving into the viewport...
1344 SET_VIEWPORT_MOVED(t
, 1);
1345 txl
= t1
->g
.frame
.x
;
1346 tyt
= t1
->g
.frame
.y
;
1347 txr
= t1
->g
.frame
.x
+ t1
->g
.frame
.width
- 1;
1348 tyb
= t1
->g
.frame
.y
+ t1
->g
.frame
.height
- 1;
1349 if (! (txr
>= PageLeft
&& txl
<= PageRight
1350 && tyb
>= PageTop
&& tyt
<= PageBottom
)
1351 && !IS_VIEWPORT_MOVED(t1
)
1352 && !IS_WINDOW_BEING_MOVED_OPAQUE(t1
))
1354 /* If the window is iconified, and sticky
1355 * Icons is set, then the window should
1356 * essentially be sticky */
1357 if (!is_window_sticky_across_pages(t1
))
1359 if (IS_ICONIFIED(t1
))
1361 modify_icon_position(
1362 t1
, deltax
, deltay
);
1363 move_icon_to_position(t1
);
1364 broadcast_icon_geometry(
1368 t1
, t1
->g
.frame
.x
+ deltax
,
1369 t1
->g
.frame
.y
+ deltay
,
1371 t1
->g
.frame
.height
, False
);
1374 /* Bump to next win... */
1375 t1
= get_prev_window_in_stack_ring(t1
);
1377 for (t
= Scr
.FvwmRoot
.next
; t
!= NULL
; t
= t
->next
)
1379 if (IS_VIEWPORT_MOVED(t
))
1381 /* Clear double move blocker. */
1382 SET_VIEWPORT_MOVED(t
, 0);
1383 GNOME_SetWinArea(t
);
1385 /* If its an icon, and its sticking, autoplace it so
1386 * that it doesn't wind up on top a a stationary
1388 if (is_window_sticky_across_pages(t
) &&
1389 IS_ICONIFIED(t
) && !IS_ICON_MOVED(t
) &&
1390 !IS_ICON_UNMAPPED(t
))
1392 AutoPlaceIcon(t
, NULL
, True
);
1397 /* regrab buttons in case something got obscured or unobscured */
1398 focus_grab_buttons_all();
1401 /* dv (2004-07-01): I don't think that's a good idea. We could eat too
1403 /* do this with PanFrames too ??? HEDU */
1407 while (FCheckTypedEvent(dpy
, MotionNotify
, &e
))
1415 MyXUngrabServer(dpy
);
1417 /* update GNOME pager */
1418 GNOME_SetCurrentArea();
1419 EWMH_SetDesktopViewPort();
1424 void goto_desk(int desk
)
1426 /* RBW - the unmapping operations are now removed to their own
1427 * functions so they can also be used by the new GoToDeskAndPage
1429 if (Scr
.CurrentDesk
!= desk
)
1431 prev_desk
= Scr
.CurrentDesk
;
1432 prev_desk_and_page_desk
= Scr
.CurrentDesk
;
1433 prev_desk_and_page_page_x
= Scr
.Vx
;
1434 prev_desk_and_page_page_y
= Scr
.Vy
;
1435 UnmapDesk(Scr
.CurrentDesk
, True
);
1436 Scr
.CurrentDesk
= desk
;
1437 MapDesk(desk
, True
);
1438 focus_grab_buttons_all();
1439 BroadcastPacket(M_NEW_DESK
, 1, (long)Scr
.CurrentDesk
);
1440 /* FIXME: domivogt (22-Apr-2000): Fake a 'restack' for sticky
1441 * window upon desk change. This is a workaround for a
1442 * problem in FvwmPager: The pager has a separate 'root'
1443 * window for each desk. If the active desk changes, the
1444 * pager destroys sticky mini windows and creates new ones in
1445 * the other desktop 'root'. But the pager can't know where to
1446 * stack them. So we have to tell it explicitly where they
1447 * go :-( This should be fixed in the pager, but right now the
1448 * pager doesn't maintain the stacking order. */
1449 BroadcastRestackAllWindows();
1450 EWMH_SetCurrentDesktop();
1451 GNOME_SetCurrentDesk();
1452 GNOME_SetDeskCount();
1460 * Move a window to a new desktop
1463 void do_move_window_to_desk(FvwmWindow
*fw
, int desk
)
1471 * Set the window's desktop, and map or unmap it as needed.
1473 /* Only change mapping for non-sticky windows */
1474 if (!is_window_sticky_across_desks(fw
) /*&& !IS_ICON_UNMAPPED(fw)*/)
1476 if (fw
->Desk
== Scr
.CurrentDesk
)
1479 if (fw
== get_focus_window())
1484 SET_FULLY_VISIBLE(fw
, 0);
1485 SET_PARTIALLY_VISIBLE(fw
, 0);
1487 else if (desk
== Scr
.CurrentDesk
)
1490 /* If its an icon, auto-place it */
1491 if (IS_ICONIFIED(fw
))
1493 AutoPlaceIcon(fw
, NULL
, True
);
1501 BroadcastConfig(M_CONFIGURE_WINDOW
,fw
);
1503 focus_grab_buttons_on_layer(fw
->layer
);
1504 EWMH_SetWMDesktop(fw
);
1505 GNOME_SetDeskCount();
1507 GNOME_SetWinArea(fw
);
1512 Bool
get_page_arguments(char *action
, int *page_x
, int *page_y
)
1531 for (; ; action
= taction
)
1535 token
= PeekToken(action
, &taction
);
1542 if (StrEquals(token
, "prev"))
1544 /* last page selected */
1545 *page_x
= prev_page_x
;
1546 *page_y
= prev_page_y
;
1550 for ( ; *token
== '!'; token
++)
1552 do_reverse
= !do_reverse
;
1554 if (StrEquals(token
, "wrapx"))
1556 wrapx
= (1 ^ do_reverse
);
1558 else if (StrEquals(token
, "wrapy"))
1560 wrapy
= (1 ^ do_reverse
);
1562 else if (StrEquals(token
, "nodesklimitx"))
1564 limitdeskx
= (0 ^ do_reverse
);
1566 else if (StrEquals(token
, "nodesklimitx"))
1568 limitdesky
= (0 ^ do_reverse
);
1572 /* no more options */
1577 if (GetSuffixedIntegerArguments(action
, NULL
, val
, 2, "pw", suffix
) !=
1585 *page_x
= val
[0] * Scr
.MyDisplayWidth
+ Scr
.Vx
;
1587 else if (suffix
[0] == 2)
1589 *page_x
+= val
[0] * Scr
.MyDisplayWidth
;
1591 else if (val
[0] >= 0)
1593 *page_x
= val
[0] * Scr
.MyDisplayWidth
;
1597 *page_x
= (val
[0] + 1) * Scr
.MyDisplayWidth
+ Scr
.VxMax
;
1601 *page_y
= val
[1] * Scr
.MyDisplayHeight
+ Scr
.Vy
;
1603 else if (suffix
[1] == 2)
1605 *page_y
+= val
[1] * Scr
.MyDisplayHeight
;
1607 else if (val
[1] >= 0)
1609 *page_y
= val
[1] * Scr
.MyDisplayHeight
;
1613 *page_y
= (val
[1] + 1) * Scr
.MyDisplayHeight
+ Scr
.VyMax
;
1616 /* limit to desktop size */
1617 if (limitdeskx
&& !wrapx
)
1623 else if (*page_x
> Scr
.VxMax
)
1625 *page_x
= Scr
.VxMax
;
1628 else if (limitdeskx
&& wrapx
)
1632 *page_x
+= Scr
.VxMax
+ Scr
.MyDisplayWidth
;
1634 while (*page_x
> Scr
.VxMax
)
1636 *page_x
-= Scr
.VxMax
+ Scr
.MyDisplayWidth
;
1639 if (limitdesky
&& !wrapy
)
1645 else if (*page_y
> Scr
.VyMax
)
1647 *page_y
= Scr
.VyMax
;
1650 else if (limitdesky
&& wrapy
)
1654 *page_y
+= Scr
.VyMax
+ Scr
.MyDisplayHeight
;
1656 while (*page_y
> Scr
.VyMax
)
1658 *page_y
-= Scr
.VyMax
+ Scr
.MyDisplayHeight
;
1665 char *GetDesktopName(int desk
)
1669 d
= Scr
.Desktops
->next
;
1670 while (d
!= NULL
&& d
->desk
!= desk
)
1682 /* ---------------------------- builtin commands --------------------------- */
1684 /* EdgeCommand - binds a function to a pan frame enter event */
1685 void CMD_EdgeCommand(F_CMD_ARGS
)
1687 direction_t direction
;
1690 /* get the direction */
1691 direction
= gravity_parse_dir_argument(action
, &action
, DIR_NONE
);
1693 if (direction
>= 0 && direction
<= DIR_MAJOR_MASK
)
1696 /* check if the command does contain at least one token */
1697 command
= safestrdup(action
);
1698 if (PeekToken(action
, &action
) == NULL
)
1700 /* the command does not contain a token so
1701 the command of this edge is removed */
1705 /* assign command to the edge(s) */
1706 if (direction
== DIR_N
)
1708 if (Scr
.PanFrameTop
.command
!= NULL
)
1710 free(Scr
.PanFrameTop
.command
);
1712 Scr
.PanFrameTop
.command
= command
;
1714 else if (direction
== DIR_S
)
1716 if (Scr
.PanFrameBottom
.command
!= NULL
)
1718 free(Scr
.PanFrameBottom
.command
);
1720 Scr
.PanFrameBottom
.command
= command
;
1722 else if (direction
== DIR_W
)
1724 if (Scr
.PanFrameLeft
.command
!= NULL
)
1726 free(Scr
.PanFrameLeft
.command
);
1728 Scr
.PanFrameLeft
.command
= command
;
1730 else if (direction
== DIR_E
)
1732 if (Scr
.PanFrameRight
.command
!= NULL
)
1734 free(Scr
.PanFrameRight
.command
);
1736 Scr
.PanFrameRight
.command
= command
;
1740 /* this should never happen */
1741 fvwm_msg(ERR
, "EdgeCommand",
1742 "Internal error in CMD_EdgeCommand");
1749 /* check if the argument does contain at least one token */
1750 if (PeekToken(action
, &action
) == NULL
)
1752 /* Just plain EdgeCommand, so all edge commands are
1754 if (Scr
.PanFrameTop
.command
!= NULL
)
1756 free(Scr
.PanFrameTop
.command
);
1757 Scr
.PanFrameTop
.command
= NULL
;
1759 if (Scr
.PanFrameBottom
.command
!= NULL
)
1761 free(Scr
.PanFrameBottom
.command
);
1762 Scr
.PanFrameBottom
.command
= NULL
;
1764 if (Scr
.PanFrameLeft
.command
!= NULL
)
1766 free(Scr
.PanFrameLeft
.command
);
1767 Scr
.PanFrameLeft
.command
= NULL
;
1769 if (Scr
.PanFrameRight
.command
!= NULL
)
1771 free(Scr
.PanFrameRight
.command
);
1772 Scr
.PanFrameRight
.command
= NULL
;
1777 /* not a proper direction */
1778 fvwm_msg(ERR
, "EdgeCommand",
1779 "EdgeCommand [direction [function]]");
1783 /* maybe something has changed so we adapt the pan frames */
1789 /* EdgeLeaveCommand - binds a function to a pan frame Leave event */
1790 void CMD_EdgeLeaveCommand(F_CMD_ARGS
)
1792 direction_t direction
;
1795 /* get the direction */
1796 direction
= gravity_parse_dir_argument(action
, &action
, DIR_NONE
);
1798 if (direction
>= 0 && direction
<= DIR_MAJOR_MASK
)
1801 /* check if the command does contain at least one token */
1802 command
= safestrdup(action
);
1803 if (PeekToken(action
, &action
) == NULL
)
1805 /* the command does not contain a token so
1806 the command of this edge is removed */
1810 /* assign command to the edge(s) */
1811 if (direction
== DIR_N
)
1813 if (Scr
.PanFrameTop
.command_leave
!= NULL
)
1815 free(Scr
.PanFrameTop
.command_leave
);
1817 Scr
.PanFrameTop
.command_leave
= command
;
1819 else if (direction
== DIR_S
)
1821 if (Scr
.PanFrameBottom
.command_leave
!= NULL
)
1823 free(Scr
.PanFrameBottom
.command_leave
);
1825 Scr
.PanFrameBottom
.command_leave
= command
;
1827 else if (direction
== DIR_W
)
1829 if (Scr
.PanFrameLeft
.command_leave
!= NULL
)
1831 free(Scr
.PanFrameLeft
.command_leave
);
1833 Scr
.PanFrameLeft
.command_leave
= command
;
1835 else if (direction
== DIR_E
)
1837 if (Scr
.PanFrameRight
.command_leave
!= NULL
)
1839 free(Scr
.PanFrameRight
.command_leave
);
1841 Scr
.PanFrameRight
.command_leave
= command
;
1845 /* this should never happen */
1846 fvwm_msg(ERR
, "EdgeLeaveCommand",
1847 "Internal error in CMD_EdgeLeaveCommand");
1854 /* check if the argument does contain at least one token */
1855 if (PeekToken(action
, &action
) == NULL
)
1857 /* Just plain EdgeLeaveCommand, so all edge commands are
1859 if (Scr
.PanFrameTop
.command_leave
!= NULL
)
1861 free(Scr
.PanFrameTop
.command_leave
);
1862 Scr
.PanFrameTop
.command_leave
= NULL
;
1864 if (Scr
.PanFrameBottom
.command_leave
!= NULL
)
1866 free(Scr
.PanFrameBottom
.command_leave
);
1867 Scr
.PanFrameBottom
.command_leave
= NULL
;
1869 if (Scr
.PanFrameLeft
.command_leave
!= NULL
)
1871 free(Scr
.PanFrameLeft
.command_leave
);
1872 Scr
.PanFrameLeft
.command_leave
= NULL
;
1874 if (Scr
.PanFrameRight
.command_leave
!= NULL
)
1876 free(Scr
.PanFrameRight
.command_leave
);
1877 Scr
.PanFrameRight
.command_leave
= NULL
;
1882 /* not a proper direction */
1883 fvwm_msg(ERR
, "EdgeLeaveCommand",
1884 "EdgeLeaveCommand [direction [function]]");
1888 /* maybe something has changed so we adapt the pan frames */
1894 void CMD_EdgeThickness(F_CMD_ARGS
)
1898 n
= GetIntegerArguments(action
, NULL
, &val
, 1);
1901 fvwm_msg(ERR
,"setEdgeThickness",
1902 "EdgeThickness requires 1 numeric argument,"
1903 " found %d args",n
);
1907 if (val
< 0 || val
> 2)
1909 fvwm_msg(ERR
,"setEdgeThickness",
1910 "EdgeThickness arg must be between 0 and 2,"
1914 edge_thickness
= val
;
1920 void CMD_EdgeScroll(F_CMD_ARGS
)
1922 int val1
, val2
, val1_unit
, val2_unit
, n
;
1925 n
= GetTwoArguments(action
, &val1
, &val2
, &val1_unit
, &val2_unit
);
1929 ERR
, "SetEdgeScroll",
1930 "EdgeScroll requires two arguments");
1935 * if edgescroll >1000 and <100000
1936 * wrap at edges of desktop (a "spherical" desktop)
1938 if (val1
>= 1000 && val1_unit
!= 100)
1941 Scr
.flags
.do_edge_wrap_x
= 1;
1945 Scr
.flags
.do_edge_wrap_x
= 0;
1947 if (val2
>= 1000 && val2_unit
!= 100)
1950 Scr
.flags
.do_edge_wrap_y
= 1;
1954 Scr
.flags
.do_edge_wrap_y
= 0;
1957 action
=SkipNTokens(action
,2);
1958 token
= PeekToken(action
, NULL
);
1962 if (StrEquals(token
, "wrap"))
1964 Scr
.flags
.do_edge_wrap_x
= 1;
1965 Scr
.flags
.do_edge_wrap_y
= 1;
1967 else if (StrEquals(token
, "wrapx"))
1969 Scr
.flags
.do_edge_wrap_x
= 1;
1971 else if (StrEquals(token
, "wrapy"))
1973 Scr
.flags
.do_edge_wrap_y
= 1;
1977 Scr
.EdgeScrollX
= val1
* val1_unit
/ 100;
1978 Scr
.EdgeScrollY
= val2
* val2_unit
/ 100;
1985 void CMD_EdgeResistance(F_CMD_ARGS
)
1991 n
= GetIntegerArguments(action
, NULL
, val
, 3);
1992 if (n
> 1 && val
[0] >= 10000)
1994 /* map val[0] >= 10000 in old syntax to -1 in new syntax */
1999 Scr
.ScrollDelay
= val
[0];
2001 else if (n
>= 2 && n
<= 3)
2007 Scr
.ScrollDelay
= val
[0];
2008 sprintf(cmd
, "EdgeResistance %d", val
[0]);
2009 sprintf(stylecmd
, "Style * EdgeMoveDelay %d", val
[0]);
2013 stylecmd2
, "Style * EdgeMoveResistance %d",
2019 stylecmd2
, "Style * EdgeMoveResistance %d %d",
2023 OLD
, "CMD_EdgeResistance",
2024 "The command EdgeResistance with three arguments is"
2025 " obsolete. Please use the following commands"
2027 fvwm_msg(OLD
, "", cmd
);
2028 fvwm_msg(OLD
, "", stylecmd
);
2029 fvwm_msg(OLD
, "", stylecmd2
);
2032 FUNC_DONT_REPEAT
| FUNC_DONT_EXPAND_COMMAND
);
2034 cond_rc
, exc
, stylecmd
,
2035 FUNC_DONT_REPEAT
| FUNC_DONT_EXPAND_COMMAND
);
2037 cond_rc
, exc
, stylecmd2
,
2038 FUNC_DONT_REPEAT
| FUNC_DONT_EXPAND_COMMAND
);
2043 ERR
, "CMD_EdgeResistance",
2044 "EdgeResistance requires two or three arguments");
2051 void CMD_Xinerama(F_CMD_ARGS
)
2055 toggle
= ParseToggleArgument(action
, NULL
, -1, 0);
2058 toggle
= !FScreenIsEnabled();
2060 if (!toggle
!= !FScreenIsEnabled())
2062 Scr
.flags
.do_need_window_update
= True
;
2063 Scr
.flags
.has_xinerama_state_changed
= True
;
2064 FScreenOnOff(toggle
);
2065 broadcast_xinerama_state();
2071 void CMD_XineramaPrimaryScreen(F_CMD_ARGS
)
2075 val
= FScreenGetScreenArgument(action
, 0);
2076 FScreenSetPrimaryScreen(val
);
2077 if (FScreenIsEnabled())
2079 Scr
.flags
.do_need_window_update
= True
;
2080 Scr
.flags
.has_xinerama_state_changed
= True
;
2082 broadcast_xinerama_state();
2087 void CMD_XineramaSls(F_CMD_ARGS
)
2091 toggle
= ParseToggleArgument(action
, NULL
, -1, 0);
2094 toggle
= !FScreenIsSLSEnabled();
2096 if (!toggle
!= !FScreenIsSLSEnabled())
2098 if (FScreenIsEnabled())
2100 Scr
.flags
.do_need_window_update
= True
;
2101 Scr
.flags
.has_xinerama_state_changed
= True
;
2103 FScreenSLSOnOff(toggle
);
2104 broadcast_xinerama_state();
2110 void CMD_XineramaSlsSize(F_CMD_ARGS
)
2114 if (GetIntegerArguments(action
, NULL
, val
, 2) != 2 &&
2115 GetRectangleArguments(action
, &val
[0], &val
[1]) != 2)
2120 if (FScreenConfigureSLSSize(val
[0], val
[1]))
2122 broadcast_xinerama_state();
2128 void CMD_XineramaSlsScreens(F_CMD_ARGS
)
2133 if (GetIntegerArguments(action
, &args
, &nscreens
, 1) != 1)
2138 else if (args
== NULL
)
2142 if (FScreenConfigureSLSScreens(nscreens
, args
))
2144 broadcast_xinerama_state();
2150 void CMD_DesktopSize(F_CMD_ARGS
)
2154 if (GetIntegerArguments(action
, NULL
, val
, 2) != 2 &&
2155 GetRectangleArguments(action
, &val
[0], &val
[1]) != 2)
2157 fvwm_msg(ERR
, "CMD_DesktopSize",
2158 "DesktopSize requires two arguments");
2162 Scr
.VxMax
= (val
[0] <= 0) ?
2163 0: val
[0]*Scr
.MyDisplayWidth
-Scr
.MyDisplayWidth
;
2164 Scr
.VyMax
= (val
[1] <= 0) ?
2165 0: val
[1]*Scr
.MyDisplayHeight
-Scr
.MyDisplayHeight
;
2167 M_NEW_PAGE
, 7, (long)Scr
.Vx
, (long)Scr
.Vy
,
2168 (long)Scr
.CurrentDesk
, (long)Scr
.MyDisplayWidth
,
2169 (long)Scr
.MyDisplayHeight
,
2170 (long)((Scr
.VxMax
/ Scr
.MyDisplayWidth
) + 1),
2171 (long)((Scr
.VyMax
/ Scr
.MyDisplayHeight
) + 1));
2174 /* update GNOME pager */
2175 GNOME_SetAreaCount();
2176 EWMH_SetDesktopGeometry();
2183 * Move to a new desktop
2186 void CMD_GotoDesk(F_CMD_ARGS
)
2188 goto_desk(GetDeskNumber(action
, Scr
.CurrentDesk
));
2193 void CMD_Desk(F_CMD_ARGS
)
2195 CMD_GotoDesk(F_PASS_ARGS
);
2202 * Move to a new desktop and page at the same time.
2203 * This function is designed for use by the Pager, and replaces the old
2204 * GoToDesk 0 10000 hack.
2205 * - unmap all windows on the current desk so they don't flash when the
2206 * viewport is moved, then switch the viewport, then the desk.
2209 void CMD_GotoDeskAndPage(F_CMD_ARGS
)
2214 if (MatchToken(action
, "prev"))
2216 val
[0] = prev_desk_and_page_desk
;
2217 val
[1] = prev_desk_and_page_page_x
;
2218 val
[2] = prev_desk_and_page_page_y
;
2220 else if (GetIntegerArguments(action
, NULL
, val
, 3) == 3)
2222 val
[1] *= Scr
.MyDisplayWidth
;
2223 val
[2] *= Scr
.MyDisplayHeight
;
2230 is_new_desk
= (Scr
.CurrentDesk
!= val
[0]);
2233 UnmapDesk(Scr
.CurrentDesk
, True
);
2235 prev_desk_and_page_page_x
= Scr
.Vx
;
2236 prev_desk_and_page_page_y
= Scr
.Vy
;
2237 MoveViewport(val
[1], val
[2], True
);
2240 prev_desk
= Scr
.CurrentDesk
;
2241 prev_desk_and_page_desk
= Scr
.CurrentDesk
;
2242 Scr
.CurrentDesk
= val
[0];
2243 MapDesk(val
[0], True
);
2244 focus_grab_buttons_all();
2245 BroadcastPacket(M_NEW_DESK
, 1, (long)Scr
.CurrentDesk
);
2246 /* FIXME: domivogt (22-Apr-2000): Fake a 'restack' for sticky
2247 * window upon desk change. This is a workaround for a
2248 * problem in FvwmPager: The pager has a separate 'root'
2249 * window for each desk. If the active desk changes, the
2250 * pager destroys sticky mini windows and creates new ones in
2251 * the other desktop 'root'. But the pager can't know where to
2252 * stack them. So we have to tell it ecplicitly where they
2253 * go :-( This should be fixed in the pager, but right now the
2254 * pager doesn't the stacking order. */
2255 BroadcastRestackAllWindows();
2259 BroadcastPacket(M_NEW_DESK
, 1, (long)Scr
.CurrentDesk
);
2261 EWMH_SetCurrentDesktop();
2262 GNOME_SetCurrentDesk();
2263 GNOME_SetDeskCount();
2268 void CMD_GotoPage(F_CMD_ARGS
)
2275 if (!get_page_arguments(action
, &x
, &y
))
2278 ERR
, "goto_page_func",
2279 "GotoPage: invalid arguments: %s", action
);
2298 MoveViewport(x
,y
,True
);
2303 /* function with parsing of command line */
2304 void CMD_MoveToDesk(F_CMD_ARGS
)
2307 FvwmWindow
* const fw
= exc
->w
.fw
;
2309 desk
= GetDeskNumber(action
, fw
->Desk
);
2310 if (desk
== fw
->Desk
)
2314 do_move_window_to_desk(fw
, desk
);
2319 void CMD_Scroll(F_CMD_ARGS
)
2322 int val1
, val2
, val1_unit
, val2_unit
;
2324 if (GetTwoArguments(action
, &val1
, &val2
, &val1_unit
, &val2_unit
) != 2)
2326 /* less then two integer parameters implies interactive
2327 * scroll check if we are scrolling in reverse direction */
2329 int scroll_speed
= 1;
2331 option
= PeekToken(action
, NULL
);
2334 if (StrEquals(option
, "Reverse"))
2339 __drag_viewport(exc
, scroll_speed
);
2343 if ((val1
> -100000)&&(val1
< 100000))
2345 x
= Scr
.Vx
+ val1
*val1_unit
/100;
2349 x
= Scr
.Vx
+ (val1
/1000)*val1_unit
/100;
2351 if ((val2
> -100000)&&(val2
< 100000))
2353 y
=Scr
.Vy
+ val2
*val2_unit
/100;
2357 y
= Scr
.Vy
+ (val2
/1000)*val2_unit
/100;
2359 if (((val1
<= -100000)||(val1
>= 100000))&&(x
>Scr
.VxMax
))
2363 xpixels
= (Scr
.VxMax
/ Scr
.MyDisplayWidth
+ 1) *
2366 y
+= Scr
.MyDisplayHeight
* (1+((x
-Scr
.VxMax
-1)/xpixels
));
2369 y
%= (Scr
.VyMax
/ Scr
.MyDisplayHeight
+ 1) *
2370 Scr
.MyDisplayHeight
;
2373 if (((val1
<= -100000)||(val1
>= 100000))&&(x
<0))
2376 y
-= Scr
.MyDisplayHeight
;
2382 if (((val2
<= -100000)||(val2
>= 100000))&&(y
>Scr
.VyMax
))
2384 int ypixels
= (Scr
.VyMax
/ Scr
.MyDisplayHeight
+ 1) *
2385 Scr
.MyDisplayHeight
;
2387 x
+= Scr
.MyDisplayWidth
* (1+((y
-Scr
.VyMax
-1)/ypixels
));
2390 x
%= (Scr
.VxMax
/ Scr
.MyDisplayWidth
+ 1) *
2394 if (((val2
<= -100000)||(val2
>= 100000))&&(y
<0))
2397 x
-= Scr
.MyDisplayWidth
;
2403 MoveViewport(x
,y
,True
);
2410 * Defines the name of a desktop
2413 void CMD_DesktopName(F_CMD_ARGS
)
2416 DesktopsInfo
*t
, *d
, *new, **prev
;
2418 if (GetIntegerArguments(action
, &action
, &desk
, 1) != 1)
2421 ERR
,"CMD_DesktopName",
2422 "First argument to DesktopName must be an integer: %s",
2427 d
= Scr
.Desktops
->next
;
2428 while (d
!= NULL
&& d
->desk
!= desk
)
2435 if (d
->name
!= NULL
)
2440 if (action
!= NULL
&& *action
&& *action
!= '\n')
2442 CopyString(&d
->name
, action
);
2447 /* new deskops entries: add it in order */
2448 d
= Scr
.Desktops
->next
;
2450 prev
= &(Scr
.Desktops
->next
);
2451 while (d
!= NULL
&& d
->desk
< desk
)
2459 /* add it at the end */
2460 *prev
= (DesktopsInfo
*)safemalloc(
2461 sizeof(DesktopsInfo
));
2462 memset(*prev
, 0, sizeof(DesktopsInfo
));
2463 (*prev
)->desk
= desk
;
2464 if (action
!= NULL
&& *action
&& *action
!= '\n')
2466 CopyString(&((*prev
)->name
), action
);
2472 new = (DesktopsInfo
*)safemalloc(sizeof(DesktopsInfo
));
2473 memset(new, 0, sizeof(DesktopsInfo
));
2475 if (action
!= NULL
&& *action
&& *action
!= '\n')
2477 CopyString(&(new->name
), action
);
2482 /* should check/set the working areas */
2485 if (!fFvwmInStartup
)
2488 const char *default_desk_name
= _("Desk");
2490 /* should send the info to the FvwmPager and set the EWMH
2492 if (action
!= NULL
&& *action
&& *action
!= '\n')
2494 msg
= (char *)safemalloc(strlen(action
) + 44);
2495 sprintf(msg
, "DesktopName %d %s", desk
, action
);
2499 msg
= (char *)safemalloc(strlen(default_desk_name
)+44);
2501 msg
, "DesktopName %d %s %d", desk
,
2502 default_desk_name
, desk
);
2504 BroadcastConfigInfoString(msg
);
2506 EWMH_SetDesktopNames();