Fix ExplainWindowPlacement when using "NoUSPosition" style.
[fvwm.git] / fvwm / virtual.c
blobd3c0b8026ec3e03a2d7c64f26da09fb78426c755
1 /* -*-c-*- */
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 ---------------------- */
19 #include "config.h"
21 #include <stdio.h>
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"
29 #include "fvwm.h"
30 #include "externs.h"
31 #include "execcontext.h"
32 #include "cursor.h"
33 #include "events.h"
34 #include "eventmask.h"
35 #include "misc.h"
36 #include "screen.h"
37 #include "virtual.h"
38 #include "module_interface.h"
39 #include "module_list.h"
40 #include "focus.h"
41 #include "gnome.h"
42 #include "ewmh.h"
43 #include "move_resize.h"
44 #include "borders.h"
45 #include "frame.h"
46 #include "geometry.h"
47 #include "icons.h"
48 #include "stack.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 ---------------------------- */
66 * dje 12/19/98
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
72 * generated.
74 * Allowed: 0,1,or 2 pixel pan frames.
76 * 0 completely disables mouse edge scrolling, even while dragging a
77 * window.
79 * 1 gives the smallest pan frames, which seem to work best except on
80 * some servers.
82 * 2 is the default.
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)
100 XEvent e;
101 int x;
102 int y;
103 unsigned int button_mask = 0;
104 Bool is_finished = False;
106 if (!GrabEm(CRS_MOVE, GRAB_NORMAL))
108 XBell(dpy, 0);
109 return;
112 if (FQueryPointer(
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);
119 return;
121 MyXGrabKeyboard(dpy);
122 button_mask &= DEFAULT_ALL_BUTTONS_MASK;
123 memset(&e, 0, sizeof(e));
124 while (!is_finished)
126 int old_x;
127 int old_y;
129 old_x = x;
130 old_y = y;
131 FMaskEvent(
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 &&
141 FCheckMaskEvent(
142 dpy, ButtonMotionMask |
143 PointerMotionMask | ButtonPressMask |
144 ButtonRelease | KeyPressMask |
145 EnterWindowMask | LeaveWindowMask, &e))
147 if (e.type == ButtonPress ||
148 e.type == ButtonRelease ||
149 e.type == KeyPress)
151 break;
155 if (e.type == EnterNotify || e.type == LeaveNotify)
157 XEvent e2;
158 int px;
159 int py;
161 /* Query the pointer to catch the latest information.
162 * This *is* necessary. */
163 if (FQueryPointer(
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;
174 e = e2;
175 fev_fake_event(&e);
177 else
179 /* pointer is on a different screen,
180 * ignore event */
183 /* Handle a limited number of key press events to allow
184 * mouseless operation */
185 if (e.type == KeyPress)
187 Keyboard_shortcuts(
188 &e, NULL, NULL, NULL, ButtonRelease);
190 switch (e.type)
192 case KeyPress:
193 /* simple code to bag out of move - CKH */
194 if (XLookupKeysym(&(e.xkey), 0) == XK_Escape)
196 is_finished = True;
198 break;
199 case ButtonPress:
200 if (e.xbutton.button <= NUMBER_OF_MOUSE_BUTTONS &&
201 ((Button1Mask << (e.xbutton.button - 1)) &
202 button_mask))
204 /* No new button was pressed, just a delayed
205 * event */
206 break;
208 /* fall through */
209 case ButtonRelease:
210 x = e.xbutton.x_root;
211 y = e.xbutton.y_root;
212 is_finished = True;
213 break;
214 case MotionNotify:
215 if (e.xmotion.same_screen == False)
217 continue;
219 x = e.xmotion.x_root;
220 y = e.xmotion.y_root;
221 break;
222 case Expose:
223 dispatch_event(&e);
224 break;
225 default:
226 /* cannot happen */
227 break;
228 } /* switch */
229 if (x != old_x || y != old_y)
231 MoveViewport(
232 Scr.Vx + scroll_speed * (x - old_x),
233 Scr.Vy + scroll_speed * (y - old_y), False);
234 FlushAllMessageQueues();
236 } /* while*/
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
252 * or desk #max
253 * n x min max : desk number = current desk + n, but wrap around at desk #min
254 * or desk #max
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)
262 int n;
263 int m;
264 int is_relative;
265 int desk;
266 int val[4];
267 int min, max;
269 if (MatchToken(action, "prev"))
271 return prev_desk;
273 n = GetIntegerArguments(action, NULL, &(val[0]), 4);
274 if (n <= 0)
276 return Scr.CurrentDesk;
278 if (n == 1)
280 return current_desk + val[0];
282 desk = current_desk;
283 m = 0;
284 if (val[0] == 0)
286 /* absolute desk number */
287 desk = val[1];
288 is_relative = 0;
290 else
292 /* relative desk number */
293 desk += val[0];
294 is_relative = 1;
296 if (n == 3)
298 m = 1;
300 if (n == 4)
302 m = 2;
304 if (n > 2)
306 /* handle limits */
307 if (val[m] <= val[m+1])
309 min = val[m];
310 max = val[m+1];
312 else
314 /* min > max is nonsense, so swap 'em. */
315 min = val[m+1];
316 max = val[m];
318 if (is_relative)
320 /* Relative moves wrap around. */
321 if (desk < min)
323 desk += (max - min + 1);
325 else if (desk > max)
327 desk -= (max - min + 1);
330 else if (desk < min)
332 /* Relative move outside of range, wrap around. */
333 if (val[0] < 0)
335 desk = max;
337 else
339 desk = min;
342 else if (desk > max)
344 /* Move outside of range, truncate. */
345 if (val[0] > 0)
347 desk = min;
349 else
351 desk = max;
356 return desk;
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;
368 Status ret;
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);
375 if (ret)
377 eventMask = winattrs.your_event_mask;
378 /* suppress UnmapRequest event */
379 XSelectInput(dpy, FW_W(t), eventMask & ~StructureNotifyMask);
381 if (IS_ICONIFIED(t))
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));
392 else
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));
399 #endif
400 if (!Scr.bo.do_enable_ewmh_iconic_state_workaround)
402 SetMapStateProp(t, IconicState);
405 if (ret)
407 XSelectInput(dpy, FW_W(t), eventMask);
408 XFlush(dpy);
411 return;
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;
423 Status ret;
425 if (IS_SCHEDULED_FOR_DESTROY(t))
427 return;
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);
434 if (ret)
436 eventMask = winattrs.your_event_mask;
437 /* suppress MapRequest event */
438 XSelectInput(dpy, FW_W(t), eventMask & ~StructureNotifyMask);
440 if (IS_ICONIFIED(t))
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));
462 #endif
463 if (!Scr.bo.do_enable_ewmh_iconic_state_workaround)
465 SetMapStateProp(t, NormalState);
468 if (ret)
470 XSelectInput(dpy, FW_W(t), eventMask);
471 XFlush(dpy);
474 return;
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)
487 FvwmWindow *t;
488 FvwmWindow *sf = get_focus_window();
490 if (grab)
492 MyXGrabServer(dpy);
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))
500 if (t->Desk == desk)
502 if (sf == t)
504 t->flags.is_focused_on_other_desk = 1;
505 t->FocusDesk = desk;
506 DeleteFocus(True);
508 else
510 t->flags.is_focused_on_other_desk = 0;
512 unmap_window(t);
513 SET_FULLY_VISIBLE(t, 0);
514 SET_PARTIALLY_VISIBLE(t, 0);
517 else
519 t->flags.is_focused_on_other_desk = 0;
522 if (grab)
524 MyXUngrabServer(dpy);
527 return;
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)
540 FvwmWindow *t;
541 FvwmWindow *FocusWin = NULL;
542 FvwmWindow *StickyWin = NULL;
543 FvwmWindow *sf = get_focus_window();
545 Scr.flags.is_map_desk_in_progress = 1;
546 if (grab)
548 MyXGrabServer(dpy);
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))
556 if (t->Desk == desk)
558 map_window(t);
561 else
563 /* If window is sticky, just update its desk (it's
564 * still mapped). */
565 t->Desk = desk;
566 GNOME_SetDesk(t);
567 GNOME_SetWinArea(t);
568 if (sf == t)
570 StickyWin = t;
574 if (grab)
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;
594 FocusWin = t;
598 /* If a sticky window has focus, don't disturb it. */
599 if (!StickyWin && FocusWin)
601 /* Otherwise, handle remembering the last-focused clicky
602 * window. */
603 if (!FP_DO_UNFOCUS_LEAVE(FW_FOCUS_POLICY(FocusWin)))
605 ReturnFocusWindow(FocusWin);
607 else
609 DeleteFocus(True);
612 Scr.flags.is_map_desk_in_progress = 0;
614 return;
617 /* ---------------------------- interface functions ------------------------ */
621 * Check to see if the pointer is on the edge of the screen, and scroll/page
622 * if needed
624 * Returns
625 * 0: no paging
626 * 1: paging occured
627 * -1: no need to call the function again before a new event arrives
629 int HandlePaging(
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;
635 int x,y;
636 XEvent e;
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;
644 *delta_x = 0;
645 *delta_y = 0;
646 if (!is_timestamp_valid && do_continue_previous)
648 /* don't call me again until something has happened */
649 return -1;
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)) &&
658 (Scr.VyMax == 0 ||
659 (y >= edge_thickness &&
660 y < Scr.MyDisplayHeight - edge_thickness)))
662 return -1;
665 if (delay < 0 || (HorWarpSize == 0 && VertWarpSize==0))
667 is_timestamp_valid = False;
668 add_time = 0;
669 return 0;
671 if (Scr.VxMax == 0 && Scr.VyMax == 0)
673 is_timestamp_valid = False;
674 add_time = 0;
675 return 0;
677 if (!is_timestamp_valid)
679 is_timestamp_valid = True;
680 my_timestamp = fev_get_evtime();
681 is_last_position_valid = False;
682 add_time = 0;
683 last_x = -1;
684 last_y = -1;
686 else if (my_last_timestamp != fev_get_evtime())
688 add_time = 0;
690 my_last_timestamp = fev_get_evtime();
694 int rc;
695 Window JunkW;
696 int JunkC;
697 unsigned int JunkM;
699 if (FCheckPeekIfEvent(dpy, &e, test_button_event, NULL))
701 is_timestamp_valid = False;
702 add_time = 0;
703 return 0;
705 /* get pointer location */
706 rc = FQueryPointer(
707 dpy, Scr.Root, &JunkW, &JunkW, &x, &y, &JunkC, &JunkC,
708 &JunkM);
709 if (rc == False)
711 /* pointer is on a different screen */
712 x = 0;
713 y = 0;
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;
724 add_time = 0;
725 return 0;
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
736 * called again. */
737 is_timestamp_valid = True;
738 my_timestamp = fev_get_evtime();
739 add_time = 0;
740 last_x = x;
741 last_y = y;
742 return 0;
744 last_x = x;
745 last_y = y;
746 is_last_position_valid = True;
747 usleep(10000);
748 add_time += 10;
749 } while (fLoop && fev_get_evtime() - my_timestamp + add_time < delay);
751 if (fev_get_evtime() - my_timestamp + add_time < delay)
753 return 0;
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. */
758 if (FQueryPointer(
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 */
767 /* started at */
768 if (x < edge_thickness)
770 *delta_x = -HorWarpSize;
772 else if (x >= Scr.MyDisplayWidth-edge_thickness)
774 *delta_x = HorWarpSize;
776 else
778 *delta_x = 0;
780 if (Scr.VxMax == 0)
782 *delta_x = 0;
784 if (y < edge_thickness)
786 *delta_y = -VertWarpSize;
788 else if (y >= Scr.MyDisplayHeight-edge_thickness)
790 *delta_y = VertWarpSize;
792 else
794 *delta_y = 0;
796 if (Scr.VyMax == 0)
798 *delta_y = 0;
801 /* Ouch! lots of bounds checking */
802 if (Scr.Vx + *delta_x < 0)
804 if (!(Scr.flags.do_edge_wrap_x))
806 *delta_x = -Scr.Vx;
807 *xl = x - *delta_x;
809 else
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;
820 *xl = x - *delta_x;
822 else
824 *delta_x -= Scr.VxMax +Scr.MyDisplayWidth;
825 *xl = x + *delta_x % Scr.MyDisplayWidth - HorWarpSize;
828 else
830 *xl = x - *delta_x;
833 if (Scr.Vy + *delta_y < 0)
835 if (!(Scr.flags.do_edge_wrap_y))
837 *delta_y = -Scr.Vy;
838 *yt = y - *delta_y;
840 else
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;
851 *yt = y - *delta_y;
853 else
855 *delta_y -= Scr.VyMax + Scr.MyDisplayHeight;
856 *yt = y + *delta_y % Scr.MyDisplayHeight - VertWarpSize;
859 else
861 *yt = y - *delta_y;
864 /* Check for paging -- and don't warp the pointer. */
865 is_timestamp_valid = False;
866 add_time = 0;
867 if (*delta_x == 0 && *delta_y == 0)
869 return 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;
890 if (Grab)
892 MyXGrabServer(dpy);
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);
898 if (FQueryPointer(
899 dpy, Scr.Root, &JunkRoot, &JunkChild, xl, yt, &JunkX,
900 &JunkY, &JunkMask) == False)
902 /* pointer is on a different screen */
903 *xl = 0;
904 *yt = 0;
906 if (Grab)
908 MyXUngrabServer(dpy);
911 return 1;
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)
938 return;
940 /* thickness of 0 means remove the pan frames */
941 if (edge_thickness == 0)
943 do_unmap_l = True;
944 do_unmap_r = True;
945 do_unmap_t = True;
946 do_unmap_b = True;
948 /* Remove Pan frames if paging by edge-scroll is permanently or
949 * temporarily disabled */
950 if (Scr.EdgeScrollX == 0 || Scr.VxMax == 0)
952 do_unmap_l = True;
953 do_unmap_r = True;
955 if (Scr.EdgeScrollY == 0 || Scr.VyMax == 0)
957 do_unmap_t = True;
958 do_unmap_b = True;
960 if (Scr.Vx == 0 && !Scr.flags.do_edge_wrap_x)
962 do_unmap_l = True;
964 if (Scr.Vx == Scr.VxMax && !Scr.flags.do_edge_wrap_x)
966 do_unmap_r = True;
968 if (Scr.Vy == 0 && !Scr.flags.do_edge_wrap_y)
970 do_unmap_t = True;
972 if (Scr.Vy == Scr.VyMax && !Scr.flags.do_edge_wrap_y)
974 do_unmap_b = True;
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)
982 do_unmap_l = False;
984 if (Scr.PanFrameRight.command != NULL || Scr.PanFrameRight.command_leave != NULL)
986 do_unmap_r = False;
988 if (Scr.PanFrameBottom.command != NULL || Scr.PanFrameBottom.command_leave != NULL)
990 do_unmap_b = False;
992 if (Scr.PanFrameTop.command != NULL || Scr.PanFrameTop.command_leave != NULL)
994 do_unmap_t = False;
999 * hide or show the windows
1002 /* left */
1003 if (do_unmap_l)
1005 if (Scr.PanFrameLeft.isMapped)
1007 XUnmapWindow(dpy, Scr.PanFrameLeft.win);
1008 Scr.PanFrameLeft.isMapped = False;
1011 else
1013 if (edge_thickness != last_edge_thickness)
1015 XResizeWindow(
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;
1025 /* right */
1026 if (do_unmap_r)
1028 if (Scr.PanFrameRight.isMapped)
1030 XUnmapWindow(dpy, Scr.PanFrameRight.win);
1031 Scr.PanFrameRight.isMapped = False;
1034 else
1036 if (edge_thickness != last_edge_thickness)
1038 XMoveResizeWindow(
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;
1049 /* top */
1050 if (do_unmap_t)
1052 if (Scr.PanFrameTop.isMapped)
1054 XUnmapWindow(dpy, Scr.PanFrameTop.win);
1055 Scr.PanFrameTop.isMapped = False;
1058 else
1060 if (edge_thickness != last_edge_thickness)
1062 XResizeWindow(
1063 dpy, Scr.PanFrameTop.win, Scr.MyDisplayWidth,
1064 edge_thickness);
1066 if (!Scr.PanFrameTop.isMapped)
1068 XMapRaised(dpy, Scr.PanFrameTop.win);
1069 Scr.PanFrameTop.isMapped = True;
1072 /* bottom */
1073 if (do_unmap_b)
1075 if (Scr.PanFrameBottom.isMapped)
1077 XUnmapWindow(dpy, Scr.PanFrameBottom.win);
1078 Scr.PanFrameBottom.isMapped = False;
1081 else
1083 if (edge_thickness != last_edge_thickness)
1085 XMoveResizeWindow(
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;
1098 return;
1103 * Gotta make sure these things are on top of everything else, or they
1104 * don't work!
1106 * For some reason, this seems to be unneeded.
1109 void raisePanFrames(void)
1111 Window windows[4];
1112 int n;
1114 /* Note: make sure the stacking order of the pan frames is not changed
1115 * every time they are raised by using XRestackWindows. */
1116 n = 0;
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;
1133 if (n > 0)
1135 XRaiseWindow(dpy, windows[0]);
1136 if (n > 1)
1138 XRestackWindows(dpy, windows, n);
1142 return;
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
1158 * later*/
1159 /* if (edge_thickness == 0) return; */
1160 saved_thickness = edge_thickness;
1161 if (edge_thickness == 0)
1163 edge_thickness = 2;
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
1171 * unmapped */
1172 Scr.PanFrameTop.win = XCreateWindow(
1173 dpy, Scr.Root, 0, 0, Scr.MyDisplayWidth, edge_thickness,
1174 0, CopyFromParent, InputOnly, CopyFromParent, valuemask,
1175 &attributes);
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,
1180 &attributes);
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;
1195 return;
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)
1203 return True;
1205 else
1207 return False;
1213 * Moves the viewport within the virtual desktop
1216 void MoveViewport(int newx, int newy, Bool grab)
1218 FvwmWindow *t, *t1;
1219 int deltax,deltay;
1220 int PageTop, PageLeft;
1221 int PageBottom, PageRight;
1222 int txl, txr, tyt, tyb;
1224 if (grab)
1226 MyXGrabServer(dpy);
1228 if (newx > Scr.VxMax)
1230 newx = Scr.VxMax;
1232 if (newy > Scr.VyMax)
1234 newy = Scr.VyMax;
1236 if (newx < 0)
1238 newx = 0;
1240 if (newy < 0)
1242 newy = 0;
1244 deltay = Scr.Vy - newy;
1245 deltax = Scr.Vx - newx;
1247 Identify the bounding rectangle that will be moved into
1248 the viewport.
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;
1262 Scr.Vx = newx;
1263 Scr.Vy = newy;
1265 if (deltax || deltay)
1267 BroadcastPacket(
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
1282 * chain.
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...
1293 txl = t->g.frame.x;
1294 tyt = t->g.frame.y;
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(
1323 t, deltax, deltay);
1324 move_icon_to_position(t);
1325 broadcast_icon_geometry(
1326 t, False);
1328 frame_setup_window(
1329 t, t->g.frame.x + deltax,
1330 t->g.frame.y + deltay,
1331 t->g.frame.width,
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(
1365 t1, False);
1367 frame_setup_window(
1368 t1, t1->g.frame.x + deltax,
1369 t1->g.frame.y + deltay,
1370 t1->g.frame.width,
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
1387 * icon */
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);
1396 checkPanFrames();
1397 /* regrab buttons in case something got obscured or unobscured */
1398 focus_grab_buttons_all();
1400 #if 0
1401 /* dv (2004-07-01): I don't think that's a good idea. We could eat too
1402 * many events. */
1403 /* do this with PanFrames too ??? HEDU */
1405 XEvent e;
1407 while (FCheckTypedEvent(dpy, MotionNotify, &e))
1409 /* nothing */
1412 #endif
1413 if (grab)
1415 MyXUngrabServer(dpy);
1417 /* update GNOME pager */
1418 GNOME_SetCurrentArea();
1419 EWMH_SetDesktopViewPort();
1421 return;
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
1428 * command. */
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();
1455 return;
1460 * Move a window to a new desktop
1463 void do_move_window_to_desk(FvwmWindow *fw, int desk)
1465 if (fw == NULL)
1467 return;
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)
1478 fw->Desk = desk;
1479 if (fw == get_focus_window())
1481 DeleteFocus(True);
1483 unmap_window(fw);
1484 SET_FULLY_VISIBLE(fw, 0);
1485 SET_PARTIALLY_VISIBLE(fw, 0);
1487 else if (desk == Scr.CurrentDesk)
1489 fw->Desk = desk;
1490 /* If its an icon, auto-place it */
1491 if (IS_ICONIFIED(fw))
1493 AutoPlaceIcon(fw, NULL, True);
1495 map_window(fw);
1497 else
1499 fw->Desk = desk;
1501 BroadcastConfig(M_CONFIGURE_WINDOW,fw);
1503 focus_grab_buttons_on_layer(fw->layer);
1504 EWMH_SetWMDesktop(fw);
1505 GNOME_SetDeskCount();
1506 GNOME_SetDesk(fw);
1507 GNOME_SetWinArea(fw);
1509 return;
1512 Bool get_page_arguments(char *action, int *page_x, int *page_y)
1514 int val[2];
1515 int suffix[2];
1516 char *token;
1517 char *taction;
1518 int wrapx;
1519 int wrapy;
1520 int limitdeskx;
1521 int limitdesky;
1522 int startx;
1523 int starty;
1525 wrapx = 0;
1526 wrapy = 0;
1527 limitdeskx = 1;
1528 limitdesky = 1;
1529 startx = *page_x;
1530 starty = *page_y;
1531 for (; ; action = taction)
1533 int do_reverse;
1535 token = PeekToken(action, &taction);
1536 if (token == NULL)
1538 *page_x = Scr.Vx;
1539 *page_y = Scr.Vy;
1540 return True;
1542 if (StrEquals(token, "prev"))
1544 /* last page selected */
1545 *page_x = prev_page_x;
1546 *page_y = prev_page_y;
1547 return True;
1549 do_reverse = 0;
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);
1570 else
1572 /* no more options */
1573 break;
1577 if (GetSuffixedIntegerArguments(action, NULL, val, 2, "pw", suffix) !=
1580 return 0;
1583 if (suffix[0] == 1)
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;
1595 else
1597 *page_x = (val[0] + 1) * Scr.MyDisplayWidth + Scr.VxMax;
1599 if (suffix[1] == 1)
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;
1611 else
1613 *page_y = (val[1] + 1) * Scr.MyDisplayHeight + Scr.VyMax;
1616 /* limit to desktop size */
1617 if (limitdeskx && !wrapx)
1619 if (*page_x < 0)
1621 *page_x = 0;
1623 else if (*page_x > Scr.VxMax)
1625 *page_x = Scr.VxMax;
1628 else if (limitdeskx && wrapx)
1630 while (*page_x < 0)
1632 *page_x += Scr.VxMax + Scr.MyDisplayWidth;
1634 while (*page_x > Scr.VxMax)
1636 *page_x -= Scr.VxMax + Scr.MyDisplayWidth;
1639 if (limitdesky && !wrapy)
1641 if (*page_y < 0)
1643 *page_y = 0;
1645 else if (*page_y > Scr.VyMax)
1647 *page_y = Scr.VyMax;
1650 else if (limitdesky && wrapy)
1652 while (*page_y < 0)
1654 *page_y += Scr.VyMax + Scr.MyDisplayHeight;
1656 while (*page_y > Scr.VyMax)
1658 *page_y -= Scr.VyMax + Scr.MyDisplayHeight;
1662 return True;
1665 char *GetDesktopName(int desk)
1667 DesktopsInfo *d;
1669 d = Scr.Desktops->next;
1670 while (d != NULL && d->desk != desk)
1672 d = d->next;
1674 if (d != NULL)
1676 return d->name;
1679 return NULL;
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;
1688 char * command;
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 */
1702 free(command);
1703 command = NULL;
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;
1738 else
1740 /* this should never happen */
1741 fvwm_msg(ERR, "EdgeCommand",
1742 "Internal error in CMD_EdgeCommand");
1746 else
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
1753 * removed */
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;
1775 else
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 */
1784 checkPanFrames();
1786 return;
1789 /* EdgeLeaveCommand - binds a function to a pan frame Leave event */
1790 void CMD_EdgeLeaveCommand(F_CMD_ARGS)
1792 direction_t direction;
1793 char * command;
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 */
1807 free(command);
1808 command = NULL;
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;
1843 else
1845 /* this should never happen */
1846 fvwm_msg(ERR, "EdgeLeaveCommand",
1847 "Internal error in CMD_EdgeLeaveCommand");
1851 else
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
1858 * removed */
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;
1880 else
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 */
1889 checkPanFrames();
1891 return;
1894 void CMD_EdgeThickness(F_CMD_ARGS)
1896 int val, n;
1898 n = GetIntegerArguments(action, NULL, &val, 1);
1899 if (n != 1)
1901 fvwm_msg(ERR,"setEdgeThickness",
1902 "EdgeThickness requires 1 numeric argument,"
1903 " found %d args",n);
1904 return;
1906 /* check range */
1907 if (val < 0 || val > 2)
1909 fvwm_msg(ERR,"setEdgeThickness",
1910 "EdgeThickness arg must be between 0 and 2,"
1911 " found %d",val);
1912 return;
1914 edge_thickness = val;
1915 checkPanFrames();
1917 return;
1920 void CMD_EdgeScroll(F_CMD_ARGS)
1922 int val1, val2, val1_unit, val2_unit, n;
1923 char *token;
1925 n = GetTwoArguments(action, &val1, &val2, &val1_unit, &val2_unit);
1926 if (n != 2)
1928 fvwm_msg(
1929 ERR, "SetEdgeScroll",
1930 "EdgeScroll requires two arguments");
1931 return;
1935 * if edgescroll >1000 and <100000
1936 * wrap at edges of desktop (a "spherical" desktop)
1938 if (val1 >= 1000 && val1_unit != 100)
1940 val1 /= 1000;
1941 Scr.flags.do_edge_wrap_x = 1;
1943 else
1945 Scr.flags.do_edge_wrap_x = 0;
1947 if (val2 >= 1000 && val2_unit != 100)
1949 val2 /= 1000;
1950 Scr.flags.do_edge_wrap_y = 1;
1952 else
1954 Scr.flags.do_edge_wrap_y = 0;
1957 action=SkipNTokens(action,2);
1958 token = PeekToken(action, NULL);
1960 if (token)
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;
1980 checkPanFrames();
1982 return;
1985 void CMD_EdgeResistance(F_CMD_ARGS)
1987 int val[3];
1988 int n;
1990 val[0] = 0;
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 */
1995 val[0] = -1;
1997 if (n == 1)
1999 Scr.ScrollDelay = val[0];
2001 else if (n >= 2 && n <= 3)
2003 char cmd[99];
2004 char stylecmd[99];
2005 char stylecmd2[99];
2007 Scr.ScrollDelay = val[0];
2008 sprintf(cmd, "EdgeResistance %d", val[0]);
2009 sprintf(stylecmd, "Style * EdgeMoveDelay %d", val[0]);
2010 if (n == 2)
2012 sprintf(
2013 stylecmd2, "Style * EdgeMoveResistance %d",
2014 val[1]);
2016 else
2018 sprintf(
2019 stylecmd2, "Style * EdgeMoveResistance %d %d",
2020 val[1], val[2]);
2022 fvwm_msg(
2023 OLD, "CMD_EdgeResistance",
2024 "The command EdgeResistance with three arguments is"
2025 " obsolete. Please use the following commands"
2026 " instead:");
2027 fvwm_msg(OLD, "", cmd);
2028 fvwm_msg(OLD, "", stylecmd);
2029 fvwm_msg(OLD, "", stylecmd2);
2030 execute_function(
2031 cond_rc, exc, cmd,
2032 FUNC_DONT_REPEAT | FUNC_DONT_EXPAND_COMMAND);
2033 execute_function(
2034 cond_rc, exc, stylecmd,
2035 FUNC_DONT_REPEAT | FUNC_DONT_EXPAND_COMMAND);
2036 execute_function(
2037 cond_rc, exc, stylecmd2,
2038 FUNC_DONT_REPEAT | FUNC_DONT_EXPAND_COMMAND);
2040 else
2042 fvwm_msg(
2043 ERR, "CMD_EdgeResistance",
2044 "EdgeResistance requires two or three arguments");
2045 return;
2048 return;
2051 void CMD_Xinerama(F_CMD_ARGS)
2053 int toggle;
2055 toggle = ParseToggleArgument(action, NULL, -1, 0);
2056 if (toggle == -1)
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();
2068 return;
2071 void CMD_XineramaPrimaryScreen(F_CMD_ARGS)
2073 int val;
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();
2084 return;
2087 void CMD_XineramaSls(F_CMD_ARGS)
2089 int toggle;
2091 toggle = ParseToggleArgument(action, NULL, -1, 0);
2092 if (toggle == -1)
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();
2107 return;
2110 void CMD_XineramaSlsSize(F_CMD_ARGS)
2112 int val[2];
2114 if (GetIntegerArguments(action, NULL, val, 2) != 2 &&
2115 GetRectangleArguments(action, &val[0], &val[1]) != 2)
2117 val[0] = 1;
2118 val[1] = 1;
2120 if (FScreenConfigureSLSSize(val[0], val[1]))
2122 broadcast_xinerama_state();
2125 return;
2128 void CMD_XineramaSlsScreens(F_CMD_ARGS)
2130 int nscreens;
2131 char *args;
2133 if (GetIntegerArguments(action, &args, &nscreens, 1) != 1)
2135 nscreens = 0;
2136 args = NULL;
2138 else if (args == NULL)
2140 nscreens = 0;
2142 if (FScreenConfigureSLSScreens(nscreens, args))
2144 broadcast_xinerama_state();
2147 return;
2150 void CMD_DesktopSize(F_CMD_ARGS)
2152 int val[2];
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");
2159 return;
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;
2166 BroadcastPacket(
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));
2173 checkPanFrames();
2174 /* update GNOME pager */
2175 GNOME_SetAreaCount();
2176 EWMH_SetDesktopGeometry();
2178 return;
2183 * Move to a new desktop
2186 void CMD_GotoDesk(F_CMD_ARGS)
2188 goto_desk(GetDeskNumber(action, Scr.CurrentDesk));
2190 return;
2193 void CMD_Desk(F_CMD_ARGS)
2195 CMD_GotoDesk(F_PASS_ARGS);
2197 return;
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)
2211 int val[3];
2212 Bool is_new_desk;
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;
2225 else
2227 return;
2230 is_new_desk = (Scr.CurrentDesk != val[0]);
2231 if (is_new_desk)
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);
2238 if (is_new_desk)
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();
2257 else
2259 BroadcastPacket(M_NEW_DESK, 1, (long)Scr.CurrentDesk);
2261 EWMH_SetCurrentDesktop();
2262 GNOME_SetCurrentDesk();
2263 GNOME_SetDeskCount();
2265 return;
2268 void CMD_GotoPage(F_CMD_ARGS)
2270 int x;
2271 int y;
2273 x = Scr.Vx;
2274 y = Scr.Vy;
2275 if (!get_page_arguments(action, &x, &y))
2277 fvwm_msg(
2278 ERR, "goto_page_func",
2279 "GotoPage: invalid arguments: %s", action);
2280 return;
2282 if (x < 0)
2284 x = 0;
2286 if (x > Scr.VxMax)
2288 x = Scr.VxMax;
2290 if (y < 0)
2292 y = 0;
2294 if (y > Scr.VyMax)
2296 y = Scr.VyMax;
2298 MoveViewport(x,y,True);
2300 return;
2303 /* function with parsing of command line */
2304 void CMD_MoveToDesk(F_CMD_ARGS)
2306 int desk;
2307 FvwmWindow * const fw = exc->w.fw;
2309 desk = GetDeskNumber(action, fw->Desk);
2310 if (desk == fw->Desk)
2312 return;
2314 do_move_window_to_desk(fw, desk);
2316 return;
2319 void CMD_Scroll(F_CMD_ARGS)
2321 int x,y;
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 */
2328 char *option;
2329 int scroll_speed = 1;
2331 option = PeekToken(action, NULL);
2332 if (option != NULL)
2334 if (StrEquals(option, "Reverse"))
2336 scroll_speed *= -1;
2339 __drag_viewport(exc, scroll_speed);
2341 return;
2343 if ((val1 > -100000)&&(val1 < 100000))
2345 x = Scr.Vx + val1*val1_unit/100;
2347 else
2349 x = Scr.Vx + (val1/1000)*val1_unit/100;
2351 if ((val2 > -100000)&&(val2 < 100000))
2353 y=Scr.Vy + val2*val2_unit/100;
2355 else
2357 y = Scr.Vy + (val2/1000)*val2_unit/100;
2359 if (((val1 <= -100000)||(val1 >= 100000))&&(x>Scr.VxMax))
2361 int xpixels;
2363 xpixels = (Scr.VxMax / Scr.MyDisplayWidth + 1) *
2364 Scr.MyDisplayWidth;
2365 x %= xpixels;
2366 y += Scr.MyDisplayHeight * (1+((x-Scr.VxMax-1)/xpixels));
2367 if (y > Scr.VyMax)
2369 y %= (Scr.VyMax / Scr.MyDisplayHeight + 1) *
2370 Scr.MyDisplayHeight;
2373 if (((val1 <= -100000)||(val1 >= 100000))&&(x<0))
2375 x = Scr.VxMax;
2376 y -= Scr.MyDisplayHeight;
2377 if (y < 0)
2379 y=Scr.VyMax;
2382 if (((val2 <= -100000)||(val2>= 100000))&&(y>Scr.VyMax))
2384 int ypixels = (Scr.VyMax / Scr.MyDisplayHeight + 1) *
2385 Scr.MyDisplayHeight;
2386 y %= ypixels;
2387 x += Scr.MyDisplayWidth * (1+((y-Scr.VyMax-1)/ypixels));
2388 if (x > Scr.VxMax)
2390 x %= (Scr.VxMax / Scr.MyDisplayWidth + 1) *
2391 Scr.MyDisplayWidth;
2394 if (((val2 <= -100000)||(val2>= 100000))&&(y<0))
2396 y = Scr.VyMax;
2397 x -= Scr.MyDisplayWidth;
2398 if (x < 0)
2400 x=Scr.VxMax;
2403 MoveViewport(x,y,True);
2405 return;
2410 * Defines the name of a desktop
2413 void CMD_DesktopName(F_CMD_ARGS)
2415 int desk;
2416 DesktopsInfo *t, *d, *new, **prev;
2418 if (GetIntegerArguments(action, &action, &desk, 1) != 1)
2420 fvwm_msg(
2421 ERR,"CMD_DesktopName",
2422 "First argument to DesktopName must be an integer: %s",
2423 action);
2424 return;
2427 d = Scr.Desktops->next;
2428 while (d != NULL && d->desk != desk)
2430 d = d->next;
2433 if (d != NULL)
2435 if (d->name != NULL)
2437 free(d->name);
2438 d->name = NULL;
2440 if (action != NULL && *action && *action != '\n')
2442 CopyString(&d->name, action);
2445 else
2447 /* new deskops entries: add it in order */
2448 d = Scr.Desktops->next;
2449 t = Scr.Desktops;
2450 prev = &(Scr.Desktops->next);
2451 while (d != NULL && d->desk < desk)
2453 t = t->next;
2454 prev = &(d->next);
2455 d = d->next;
2457 if (d == NULL)
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);
2469 else
2471 /* instert it */
2472 new = (DesktopsInfo *)safemalloc(sizeof(DesktopsInfo));
2473 memset(new, 0, sizeof(DesktopsInfo));
2474 new->desk = desk;
2475 if (action != NULL && *action && *action != '\n')
2477 CopyString(&(new->name), action);
2479 t->next = new;
2480 new->next = d;
2482 /* should check/set the working areas */
2485 if (!fFvwmInStartup)
2487 char *msg;
2488 const char *default_desk_name = _("Desk");
2490 /* should send the info to the FvwmPager and set the EWMH
2491 * desktop names */
2492 if (action != NULL && *action && *action != '\n')
2494 msg = (char *)safemalloc(strlen(action) + 44);
2495 sprintf(msg, "DesktopName %d %s", desk, action);
2497 else
2499 msg = (char *)safemalloc(strlen(default_desk_name)+44);
2500 sprintf(
2501 msg, "DesktopName %d %s %d", desk,
2502 default_desk_name, desk);
2504 BroadcastConfigInfoString(msg);
2505 free(msg);
2506 EWMH_SetDesktopNames();
2509 return;