Fix segfault when creating a tearoff menu using a Pixmap background.
[fvwm.git] / fvwm / virtual.c
blob9539fcfc412d36c012d39414026d0a2e430064c3
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 is_timestamp_valid = False;
802 add_time = 0;
803 if (*delta_x == 0 && *delta_y == 0)
805 return 0;
808 /* Ouch! lots of bounds checking */
809 if (Scr.Vx + *delta_x < 0)
811 if (!(Scr.flags.do_edge_wrap_x))
813 *delta_x = -Scr.Vx;
814 *xl = x - *delta_x;
816 else
818 *delta_x += Scr.VxMax + Scr.MyDisplayWidth;
819 *xl = x + *delta_x % Scr.MyDisplayWidth + HorWarpSize;
822 else if (Scr.Vx + *delta_x > Scr.VxMax)
824 if (!(Scr.flags.do_edge_wrap_x))
826 *delta_x = Scr.VxMax - Scr.Vx;
827 *xl = x - *delta_x;
829 else
831 *delta_x -= Scr.VxMax +Scr.MyDisplayWidth;
832 *xl = x + *delta_x % Scr.MyDisplayWidth - HorWarpSize;
835 else
837 *xl = x - *delta_x;
840 if (Scr.Vy + *delta_y < 0)
842 if (!(Scr.flags.do_edge_wrap_y))
844 *delta_y = -Scr.Vy;
845 *yt = y - *delta_y;
847 else
849 *delta_y += Scr.VyMax + Scr.MyDisplayHeight;
850 *yt = y + *delta_y % Scr.MyDisplayHeight + VertWarpSize;
853 else if (Scr.Vy + *delta_y > Scr.VyMax)
855 if (!(Scr.flags.do_edge_wrap_y))
857 *delta_y = Scr.VyMax - Scr.Vy;
858 *yt = y - *delta_y;
860 else
862 *delta_y -= Scr.VyMax + Scr.MyDisplayHeight;
863 *yt = y + *delta_y % Scr.MyDisplayHeight - VertWarpSize;
866 else
868 *yt = y - *delta_y;
871 /* make sure the pointer isn't warped into the panframes */
872 if (*xl < edge_thickness)
874 *xl = edge_thickness;
876 if (*yt < edge_thickness)
878 *yt = edge_thickness;
880 if (*xl >= Scr.MyDisplayWidth - edge_thickness)
882 *xl = Scr.MyDisplayWidth - edge_thickness -1;
884 if (*yt >= Scr.MyDisplayHeight - edge_thickness)
886 *yt = Scr.MyDisplayHeight - edge_thickness -1;
889 if (Grab)
891 MyXGrabServer(dpy);
893 /* Turn off the rubberband if its on */
894 switch_move_resize_grid(False);
895 FWarpPointer(dpy,None,Scr.Root,0,0,0,0,*xl,*yt);
896 MoveViewport(Scr.Vx + *delta_x,Scr.Vy + *delta_y,False);
897 if (FQueryPointer(
898 dpy, Scr.Root, &JunkRoot, &JunkChild, xl, yt, &JunkX,
899 &JunkY, &JunkMask) == False)
901 /* pointer is on a different screen */
902 *xl = 0;
903 *yt = 0;
905 if (Grab)
907 MyXUngrabServer(dpy);
910 return 1;
913 /* the root window is surrounded by four window slices, which are InputOnly.
914 * So you can see 'through' them, but they eat the input. An EnterEvent in
915 * one of these windows causes a Paging. The windows have the according cursor
916 * pointing in the pan direction or are hidden if there is no more panning
917 * in that direction. This is mostly intended to get a panning even atop
918 * of Motif applictions, which does not work yet. It seems Motif windows
919 * eat all mouse events.
921 * Hermann Dunkel, HEDU, dunkel@cul-ipn.uni-kiel.de 1/94
925 * checkPanFrames hides PanFrames if they are on the very border of the
926 * VIRTUAL screen and EdgeWrap for that direction is off.
927 * (A special cursor for the EdgeWrap border could be nice) HEDU
929 void checkPanFrames(void)
931 Bool do_unmap_l = False;
932 Bool do_unmap_r = False;
933 Bool do_unmap_t = False;
934 Bool do_unmap_b = False;
936 if (!Scr.flags.are_windows_captured)
937 return;
939 /* thickness of 0 means remove the pan frames */
940 if (edge_thickness == 0)
942 do_unmap_l = True;
943 do_unmap_r = True;
944 do_unmap_t = True;
945 do_unmap_b = True;
947 /* Remove Pan frames if paging by edge-scroll is permanently or
948 * temporarily disabled */
949 if (Scr.EdgeScrollX == 0 || Scr.VxMax == 0)
951 do_unmap_l = True;
952 do_unmap_r = True;
954 if (Scr.EdgeScrollY == 0 || Scr.VyMax == 0)
956 do_unmap_t = True;
957 do_unmap_b = True;
959 if (Scr.Vx == 0 && !Scr.flags.do_edge_wrap_x)
961 do_unmap_l = True;
963 if (Scr.Vx == Scr.VxMax && !Scr.flags.do_edge_wrap_x)
965 do_unmap_r = True;
967 if (Scr.Vy == 0 && !Scr.flags.do_edge_wrap_y)
969 do_unmap_t = True;
971 if (Scr.Vy == Scr.VyMax && !Scr.flags.do_edge_wrap_y)
973 do_unmap_b = True;
976 /* correct the unmap variables if pan frame commands are set */
977 if (edge_thickness != 0)
979 if (Scr.PanFrameLeft.command != NULL || Scr.PanFrameLeft.command_leave != NULL)
981 do_unmap_l = False;
983 if (Scr.PanFrameRight.command != NULL || Scr.PanFrameRight.command_leave != NULL)
985 do_unmap_r = False;
987 if (Scr.PanFrameBottom.command != NULL || Scr.PanFrameBottom.command_leave != NULL)
989 do_unmap_b = False;
991 if (Scr.PanFrameTop.command != NULL || Scr.PanFrameTop.command_leave != NULL)
993 do_unmap_t = False;
998 * hide or show the windows
1001 /* left */
1002 if (do_unmap_l)
1004 if (Scr.PanFrameLeft.isMapped)
1006 XUnmapWindow(dpy, Scr.PanFrameLeft.win);
1007 Scr.PanFrameLeft.isMapped = False;
1010 else
1012 if (edge_thickness != last_edge_thickness)
1014 XResizeWindow(
1015 dpy, Scr.PanFrameLeft.win, edge_thickness,
1016 Scr.MyDisplayHeight);
1018 if (!Scr.PanFrameLeft.isMapped)
1020 XMapRaised(dpy, Scr.PanFrameLeft.win);
1021 Scr.PanFrameLeft.isMapped = True;
1024 /* right */
1025 if (do_unmap_r)
1027 if (Scr.PanFrameRight.isMapped)
1029 XUnmapWindow(dpy, Scr.PanFrameRight.win);
1030 Scr.PanFrameRight.isMapped = False;
1033 else
1035 if (edge_thickness != last_edge_thickness)
1037 XMoveResizeWindow(
1038 dpy, Scr.PanFrameRight.win,
1039 Scr.MyDisplayWidth - edge_thickness, 0,
1040 edge_thickness, Scr.MyDisplayHeight);
1042 if (!Scr.PanFrameRight.isMapped)
1044 XMapRaised(dpy, Scr.PanFrameRight.win);
1045 Scr.PanFrameRight.isMapped = True;
1048 /* top */
1049 if (do_unmap_t)
1051 if (Scr.PanFrameTop.isMapped)
1053 XUnmapWindow(dpy, Scr.PanFrameTop.win);
1054 Scr.PanFrameTop.isMapped = False;
1057 else
1059 if (edge_thickness != last_edge_thickness)
1061 XResizeWindow(
1062 dpy, Scr.PanFrameTop.win, Scr.MyDisplayWidth,
1063 edge_thickness);
1065 if (!Scr.PanFrameTop.isMapped)
1067 XMapRaised(dpy, Scr.PanFrameTop.win);
1068 Scr.PanFrameTop.isMapped = True;
1071 /* bottom */
1072 if (do_unmap_b)
1074 if (Scr.PanFrameBottom.isMapped)
1076 XUnmapWindow(dpy, Scr.PanFrameBottom.win);
1077 Scr.PanFrameBottom.isMapped = False;
1080 else
1082 if (edge_thickness != last_edge_thickness)
1084 XMoveResizeWindow(
1085 dpy, Scr.PanFrameBottom.win, 0,
1086 Scr.MyDisplayHeight - edge_thickness,
1087 Scr.MyDisplayWidth, edge_thickness);
1089 if (!Scr.PanFrameBottom.isMapped)
1091 XMapRaised(dpy, Scr.PanFrameBottom.win);
1092 Scr.PanFrameBottom.isMapped = True;
1095 last_edge_thickness = edge_thickness;
1097 return;
1102 * Gotta make sure these things are on top of everything else, or they
1103 * don't work!
1105 * For some reason, this seems to be unneeded.
1108 void raisePanFrames(void)
1110 Window windows[4];
1111 int n;
1113 /* Note: make sure the stacking order of the pan frames is not changed
1114 * every time they are raised by using XRestackWindows. */
1115 n = 0;
1116 if (Scr.PanFrameTop.isMapped)
1118 windows[n++] = Scr.PanFrameTop.win;
1120 if (Scr.PanFrameLeft.isMapped)
1122 windows[n++] = Scr.PanFrameLeft.win;
1124 if (Scr.PanFrameRight.isMapped)
1126 windows[n++] = Scr.PanFrameRight.win;
1128 if (Scr.PanFrameBottom.isMapped)
1130 windows[n++] = Scr.PanFrameBottom.win;
1132 if (n > 0)
1134 XRaiseWindow(dpy, windows[0]);
1135 if (n > 1)
1137 XRestackWindows(dpy, windows, n);
1141 return;
1146 * Creates the windows for edge-scrolling
1149 void initPanFrames(void)
1151 XSetWindowAttributes attributes;
1152 unsigned long valuemask;
1153 int saved_thickness;
1155 /* Not creating the frames disables all subsequent behavior */
1156 /* TKP. This is bad, it will cause an XMap request on a null window
1157 * later*/
1158 /* if (edge_thickness == 0) return; */
1159 saved_thickness = edge_thickness;
1160 if (edge_thickness == 0)
1162 edge_thickness = 2;
1165 attributes.event_mask = XEVMASK_PANFW;
1166 valuemask= (CWEventMask | CWCursor);
1168 attributes.cursor = Scr.FvwmCursors[CRS_TOP_EDGE];
1169 /* I know these overlap, it's useful when at (0,0) and the top one is
1170 * unmapped */
1171 Scr.PanFrameTop.win = XCreateWindow(
1172 dpy, Scr.Root, 0, 0, Scr.MyDisplayWidth, edge_thickness,
1173 0, CopyFromParent, InputOnly, CopyFromParent, valuemask,
1174 &attributes);
1175 attributes.cursor = Scr.FvwmCursors[CRS_LEFT_EDGE];
1176 Scr.PanFrameLeft.win = XCreateWindow(
1177 dpy, Scr.Root, 0, 0, edge_thickness, Scr.MyDisplayHeight,
1178 0, CopyFromParent, InputOnly, CopyFromParent, valuemask,
1179 &attributes);
1180 attributes.cursor = Scr.FvwmCursors[CRS_RIGHT_EDGE];
1181 Scr.PanFrameRight.win = XCreateWindow(
1182 dpy, Scr.Root, Scr.MyDisplayWidth - edge_thickness, 0,
1183 edge_thickness, Scr.MyDisplayHeight, 0, CopyFromParent,
1184 InputOnly, CopyFromParent, valuemask, &attributes);
1185 attributes.cursor = Scr.FvwmCursors[CRS_BOTTOM_EDGE];
1186 Scr.PanFrameBottom.win = XCreateWindow(
1187 dpy, Scr.Root, 0, Scr.MyDisplayHeight - edge_thickness,
1188 Scr.MyDisplayWidth, edge_thickness, 0, CopyFromParent,
1189 InputOnly, CopyFromParent, valuemask, &attributes);
1190 Scr.PanFrameTop.isMapped=Scr.PanFrameLeft.isMapped=
1191 Scr.PanFrameRight.isMapped= Scr.PanFrameBottom.isMapped=False;
1192 edge_thickness = saved_thickness;
1194 return;
1197 Bool is_pan_frame(Window w)
1199 if (w == Scr.PanFrameTop.win || w == Scr.PanFrameBottom.win ||
1200 w == Scr.PanFrameLeft.win || w == Scr.PanFrameRight.win)
1202 return True;
1204 else
1206 return False;
1212 * Moves the viewport within the virtual desktop
1215 void MoveViewport(int newx, int newy, Bool grab)
1217 FvwmWindow *t, *t1;
1218 int deltax,deltay;
1219 int PageTop, PageLeft;
1220 int PageBottom, PageRight;
1221 int txl, txr, tyt, tyb;
1223 if (grab)
1225 MyXGrabServer(dpy);
1227 if (newx > Scr.VxMax)
1229 newx = Scr.VxMax;
1231 if (newy > Scr.VyMax)
1233 newy = Scr.VyMax;
1235 if (newx < 0)
1237 newx = 0;
1239 if (newy < 0)
1241 newy = 0;
1243 deltay = Scr.Vy - newy;
1244 deltax = Scr.Vx - newx;
1246 Identify the bounding rectangle that will be moved into
1247 the viewport.
1249 PageBottom = Scr.MyDisplayHeight - deltay - 1;
1250 PageRight = Scr.MyDisplayWidth - deltax - 1;
1251 PageTop = 0 - deltay;
1252 PageLeft = 0 - deltax;
1253 if (deltax || deltay)
1255 prev_page_x = Scr.Vx;
1256 prev_page_y = Scr.Vy;
1257 prev_desk_and_page_page_x = Scr.Vx;
1258 prev_desk_and_page_page_y = Scr.Vy;
1259 prev_desk_and_page_desk = Scr.CurrentDesk;
1261 Scr.Vx = newx;
1262 Scr.Vy = newy;
1264 if (deltax || deltay)
1266 BroadcastPacket(
1267 M_NEW_PAGE, 7, (long)Scr.Vx, (long)Scr.Vy,
1268 (long)Scr.CurrentDesk, (long)Scr.MyDisplayWidth,
1269 (long)Scr.MyDisplayHeight,
1270 (long)((Scr.VxMax / Scr.MyDisplayWidth) + 1),
1271 (long)((Scr.VyMax / Scr.MyDisplayHeight) + 1));
1274 * RBW - 11/13/1998 - new: chase the chain
1275 * bidirectionally, all at once! The idea is to move the
1276 * windows that are moving out of the viewport from the bottom
1277 * of the stacking order up, to minimize the expose-redraw
1278 * overhead. Windows that will be moving into view will be
1279 * moved top down, for the same reason. Use the new
1280 * stacking-order chain, rather than the old last-focused
1281 * chain.
1283 * domivogt (29-Nov-1999): It's faster to first map windows
1284 * top to bottom and then unmap windows bottom up.
1286 t = get_next_window_in_stack_ring(&Scr.FvwmRoot);
1287 while (t != &Scr.FvwmRoot)
1290 * If the window is moving into the viewport...
1292 txl = t->g.frame.x;
1293 tyt = t->g.frame.y;
1294 txr = t->g.frame.x + t->g.frame.width - 1;
1295 tyb = t->g.frame.y + t->g.frame.height - 1;
1296 if (is_window_sticky_across_pages(t) &&
1297 !IS_VIEWPORT_MOVED(t))
1299 /* the absolute position has changed */
1300 t->g.normal.x -= deltax;
1301 t->g.normal.y -= deltay;
1302 t->g.max.x -= deltax;
1303 t->g.max.y -= deltay;
1304 /* Block double move. */
1305 SET_VIEWPORT_MOVED(t, 1);
1307 if ((txr >= PageLeft && txl <= PageRight
1308 && tyb >= PageTop && tyt <= PageBottom)
1309 && !IS_VIEWPORT_MOVED(t)
1310 && !IS_WINDOW_BEING_MOVED_OPAQUE(t))
1312 /* Block double move. */
1313 SET_VIEWPORT_MOVED(t, 1);
1314 /* If the window is iconified, and sticky
1315 * Icons is set, then the window should
1316 * essentially be sticky */
1317 if (!is_window_sticky_across_pages(t))
1319 if (IS_ICONIFIED(t))
1321 modify_icon_position(
1322 t, deltax, deltay);
1323 move_icon_to_position(t);
1324 broadcast_icon_geometry(
1325 t, False);
1327 frame_setup_window(
1328 t, t->g.frame.x + deltax,
1329 t->g.frame.y + deltay,
1330 t->g.frame.width,
1331 t->g.frame.height, False);
1334 /* Bump to next win... */
1335 t = get_next_window_in_stack_ring(t);
1337 t1 = get_prev_window_in_stack_ring(&Scr.FvwmRoot);
1338 while (t1 != &Scr.FvwmRoot)
1341 *If the window is not moving into the viewport...
1343 SET_VIEWPORT_MOVED(t, 1);
1344 txl = t1->g.frame.x;
1345 tyt = t1->g.frame.y;
1346 txr = t1->g.frame.x + t1->g.frame.width - 1;
1347 tyb = t1->g.frame.y + t1->g.frame.height - 1;
1348 if (! (txr >= PageLeft && txl <= PageRight
1349 && tyb >= PageTop && tyt <= PageBottom)
1350 && !IS_VIEWPORT_MOVED(t1)
1351 && !IS_WINDOW_BEING_MOVED_OPAQUE(t1))
1353 /* If the window is iconified, and sticky
1354 * Icons is set, then the window should
1355 * essentially be sticky */
1356 if (!is_window_sticky_across_pages(t1))
1358 if (IS_ICONIFIED(t1))
1360 modify_icon_position(
1361 t1, deltax, deltay);
1362 move_icon_to_position(t1);
1363 broadcast_icon_geometry(
1364 t1, False);
1366 frame_setup_window(
1367 t1, t1->g.frame.x + deltax,
1368 t1->g.frame.y + deltay,
1369 t1->g.frame.width,
1370 t1->g.frame.height, False);
1373 /* Bump to next win... */
1374 t1 = get_prev_window_in_stack_ring(t1);
1376 for (t = Scr.FvwmRoot.next; t != NULL; t = t->next)
1378 if (IS_VIEWPORT_MOVED(t))
1380 /* Clear double move blocker. */
1381 SET_VIEWPORT_MOVED(t, 0);
1382 GNOME_SetWinArea(t);
1384 /* If its an icon, and its sticking, autoplace it so
1385 * that it doesn't wind up on top a a stationary
1386 * icon */
1387 if (is_window_sticky_across_pages(t) &&
1388 IS_ICONIFIED(t) && !IS_ICON_MOVED(t) &&
1389 !IS_ICON_UNMAPPED(t))
1391 AutoPlaceIcon(t, NULL, True);
1395 checkPanFrames();
1396 /* regrab buttons in case something got obscured or unobscured */
1397 focus_grab_buttons_all();
1399 #if 0
1400 /* dv (2004-07-01): I don't think that's a good idea. We could eat too
1401 * many events. */
1402 /* do this with PanFrames too ??? HEDU */
1404 XEvent e;
1406 while (FCheckTypedEvent(dpy, MotionNotify, &e))
1408 /* nothing */
1411 #endif
1412 if (grab)
1414 MyXUngrabServer(dpy);
1416 /* update GNOME pager */
1417 GNOME_SetCurrentArea();
1418 EWMH_SetDesktopViewPort();
1420 return;
1423 void goto_desk(int desk)
1425 /* RBW - the unmapping operations are now removed to their own
1426 * functions so they can also be used by the new GoToDeskAndPage
1427 * command. */
1428 if (Scr.CurrentDesk != desk)
1430 prev_desk = Scr.CurrentDesk;
1431 prev_desk_and_page_desk = Scr.CurrentDesk;
1432 prev_desk_and_page_page_x = Scr.Vx;
1433 prev_desk_and_page_page_y = Scr.Vy;
1434 UnmapDesk(Scr.CurrentDesk, True);
1435 Scr.CurrentDesk = desk;
1436 MapDesk(desk, True);
1437 focus_grab_buttons_all();
1438 BroadcastPacket(M_NEW_DESK, 1, (long)Scr.CurrentDesk);
1439 /* FIXME: domivogt (22-Apr-2000): Fake a 'restack' for sticky
1440 * window upon desk change. This is a workaround for a
1441 * problem in FvwmPager: The pager has a separate 'root'
1442 * window for each desk. If the active desk changes, the
1443 * pager destroys sticky mini windows and creates new ones in
1444 * the other desktop 'root'. But the pager can't know where to
1445 * stack them. So we have to tell it explicitly where they
1446 * go :-( This should be fixed in the pager, but right now the
1447 * pager doesn't maintain the stacking order. */
1448 BroadcastRestackAllWindows();
1449 EWMH_SetCurrentDesktop();
1450 GNOME_SetCurrentDesk();
1451 GNOME_SetDeskCount();
1454 return;
1459 * Move a window to a new desktop
1462 void do_move_window_to_desk(FvwmWindow *fw, int desk)
1464 if (fw == NULL)
1466 return;
1470 * Set the window's desktop, and map or unmap it as needed.
1472 /* Only change mapping for non-sticky windows */
1473 if (!is_window_sticky_across_desks(fw) /*&& !IS_ICON_UNMAPPED(fw)*/)
1475 if (fw->Desk == Scr.CurrentDesk)
1477 fw->Desk = desk;
1478 if (fw == get_focus_window())
1480 DeleteFocus(True);
1482 unmap_window(fw);
1483 SET_FULLY_VISIBLE(fw, 0);
1484 SET_PARTIALLY_VISIBLE(fw, 0);
1486 else if (desk == Scr.CurrentDesk)
1488 fw->Desk = desk;
1489 /* If its an icon, auto-place it */
1490 if (IS_ICONIFIED(fw))
1492 AutoPlaceIcon(fw, NULL, True);
1494 map_window(fw);
1496 else
1498 fw->Desk = desk;
1500 BroadcastConfig(M_CONFIGURE_WINDOW,fw);
1502 focus_grab_buttons_on_layer(fw->layer);
1503 EWMH_SetWMDesktop(fw);
1504 GNOME_SetDeskCount();
1505 GNOME_SetDesk(fw);
1506 GNOME_SetWinArea(fw);
1508 return;
1511 Bool get_page_arguments(char *action, int *page_x, int *page_y)
1513 int val[2];
1514 int suffix[2];
1515 char *token;
1516 char *taction;
1517 int wrapx;
1518 int wrapy;
1519 int limitdeskx;
1520 int limitdesky;
1521 int startx;
1522 int starty;
1524 wrapx = 0;
1525 wrapy = 0;
1526 limitdeskx = 1;
1527 limitdesky = 1;
1528 startx = *page_x;
1529 starty = *page_y;
1530 for (; ; action = taction)
1532 int do_reverse;
1534 token = PeekToken(action, &taction);
1535 if (token == NULL)
1537 *page_x = Scr.Vx;
1538 *page_y = Scr.Vy;
1539 return True;
1541 if (StrEquals(token, "prev"))
1543 /* last page selected */
1544 *page_x = prev_page_x;
1545 *page_y = prev_page_y;
1546 return True;
1548 do_reverse = 0;
1549 for ( ; *token == '!'; token++)
1551 do_reverse = !do_reverse;
1553 if (StrEquals(token, "wrapx"))
1555 wrapx = (1 ^ do_reverse);
1557 else if (StrEquals(token, "wrapy"))
1559 wrapy = (1 ^ do_reverse);
1561 else if (StrEquals(token, "nodesklimitx"))
1563 limitdeskx = (0 ^ do_reverse);
1565 else if (StrEquals(token, "nodesklimitx"))
1567 limitdesky = (0 ^ do_reverse);
1569 else
1571 /* no more options */
1572 break;
1576 if (GetSuffixedIntegerArguments(action, NULL, val, 2, "pw", suffix) !=
1579 return 0;
1582 if (suffix[0] == 1)
1584 *page_x = val[0] * Scr.MyDisplayWidth + Scr.Vx;
1586 else if (suffix[0] == 2)
1588 *page_x += val[0] * Scr.MyDisplayWidth;
1590 else if (val[0] >= 0)
1592 *page_x = val[0] * Scr.MyDisplayWidth;
1594 else
1596 *page_x = (val[0] + 1) * Scr.MyDisplayWidth + Scr.VxMax;
1598 if (suffix[1] == 1)
1600 *page_y = val[1] * Scr.MyDisplayHeight + Scr.Vy;
1602 else if (suffix[1] == 2)
1604 *page_y += val[1] * Scr.MyDisplayHeight;
1606 else if (val[1] >= 0)
1608 *page_y = val[1] * Scr.MyDisplayHeight;
1610 else
1612 *page_y = (val[1] + 1) * Scr.MyDisplayHeight + Scr.VyMax;
1615 /* limit to desktop size */
1616 if (limitdeskx && !wrapx)
1618 if (*page_x < 0)
1620 *page_x = 0;
1622 else if (*page_x > Scr.VxMax)
1624 *page_x = Scr.VxMax;
1627 else if (limitdeskx && wrapx)
1629 while (*page_x < 0)
1631 *page_x += Scr.VxMax + Scr.MyDisplayWidth;
1633 while (*page_x > Scr.VxMax)
1635 *page_x -= Scr.VxMax + Scr.MyDisplayWidth;
1638 if (limitdesky && !wrapy)
1640 if (*page_y < 0)
1642 *page_y = 0;
1644 else if (*page_y > Scr.VyMax)
1646 *page_y = Scr.VyMax;
1649 else if (limitdesky && wrapy)
1651 while (*page_y < 0)
1653 *page_y += Scr.VyMax + Scr.MyDisplayHeight;
1655 while (*page_y > Scr.VyMax)
1657 *page_y -= Scr.VyMax + Scr.MyDisplayHeight;
1661 return True;
1664 char *GetDesktopName(int desk)
1666 DesktopsInfo *d;
1668 d = Scr.Desktops->next;
1669 while (d != NULL && d->desk != desk)
1671 d = d->next;
1673 if (d != NULL)
1675 return d->name;
1678 return NULL;
1681 /* ---------------------------- builtin commands --------------------------- */
1683 /* EdgeCommand - binds a function to a pan frame enter event */
1684 void CMD_EdgeCommand(F_CMD_ARGS)
1686 direction_t direction;
1687 char * command;
1689 /* get the direction */
1690 direction = gravity_parse_dir_argument(action, &action, DIR_NONE);
1692 if (direction >= 0 && direction <= DIR_MAJOR_MASK)
1695 /* check if the command does contain at least one token */
1696 command = safestrdup(action);
1697 if (PeekToken(action , &action) == NULL)
1699 /* the command does not contain a token so
1700 the command of this edge is removed */
1701 free(command);
1702 command = NULL;
1704 /* assign command to the edge(s) */
1705 if (direction == DIR_N)
1707 if (Scr.PanFrameTop.command != NULL)
1709 free(Scr.PanFrameTop.command);
1711 Scr.PanFrameTop.command = command;
1713 else if (direction == DIR_S)
1715 if (Scr.PanFrameBottom.command != NULL)
1717 free(Scr.PanFrameBottom.command);
1719 Scr.PanFrameBottom.command = command;
1721 else if (direction == DIR_W)
1723 if (Scr.PanFrameLeft.command != NULL)
1725 free(Scr.PanFrameLeft.command);
1727 Scr.PanFrameLeft.command = command;
1729 else if (direction == DIR_E)
1731 if (Scr.PanFrameRight.command != NULL)
1733 free(Scr.PanFrameRight.command);
1735 Scr.PanFrameRight.command = command;
1737 else
1739 /* this should never happen */
1740 fvwm_msg(ERR, "EdgeCommand",
1741 "Internal error in CMD_EdgeCommand");
1745 else
1748 /* check if the argument does contain at least one token */
1749 if (PeekToken(action , &action) == NULL)
1751 /* Just plain EdgeCommand, so all edge commands are
1752 * removed */
1753 if (Scr.PanFrameTop.command != NULL)
1755 free(Scr.PanFrameTop.command);
1756 Scr.PanFrameTop.command = NULL;
1758 if (Scr.PanFrameBottom.command != NULL)
1760 free(Scr.PanFrameBottom.command);
1761 Scr.PanFrameBottom.command = NULL;
1763 if (Scr.PanFrameLeft.command != NULL)
1765 free(Scr.PanFrameLeft.command);
1766 Scr.PanFrameLeft.command = NULL;
1768 if (Scr.PanFrameRight.command != NULL)
1770 free(Scr.PanFrameRight.command);
1771 Scr.PanFrameRight.command = NULL;
1774 else
1776 /* not a proper direction */
1777 fvwm_msg(ERR, "EdgeCommand",
1778 "EdgeCommand [direction [function]]");
1782 /* maybe something has changed so we adapt the pan frames */
1783 checkPanFrames();
1785 return;
1788 /* EdgeLeaveCommand - binds a function to a pan frame Leave event */
1789 void CMD_EdgeLeaveCommand(F_CMD_ARGS)
1791 direction_t direction;
1792 char * command;
1794 /* get the direction */
1795 direction = gravity_parse_dir_argument(action, &action, DIR_NONE);
1797 if (direction >= 0 && direction <= DIR_MAJOR_MASK)
1800 /* check if the command does contain at least one token */
1801 command = safestrdup(action);
1802 if (PeekToken(action , &action) == NULL)
1804 /* the command does not contain a token so
1805 the command of this edge is removed */
1806 free(command);
1807 command = NULL;
1809 /* assign command to the edge(s) */
1810 if (direction == DIR_N)
1812 if (Scr.PanFrameTop.command_leave != NULL)
1814 free(Scr.PanFrameTop.command_leave);
1816 Scr.PanFrameTop.command_leave = command;
1818 else if (direction == DIR_S)
1820 if (Scr.PanFrameBottom.command_leave != NULL)
1822 free(Scr.PanFrameBottom.command_leave);
1824 Scr.PanFrameBottom.command_leave = command;
1826 else if (direction == DIR_W)
1828 if (Scr.PanFrameLeft.command_leave != NULL)
1830 free(Scr.PanFrameLeft.command_leave);
1832 Scr.PanFrameLeft.command_leave = command;
1834 else if (direction == DIR_E)
1836 if (Scr.PanFrameRight.command_leave != NULL)
1838 free(Scr.PanFrameRight.command_leave);
1840 Scr.PanFrameRight.command_leave = command;
1842 else
1844 /* this should never happen */
1845 fvwm_msg(ERR, "EdgeLeaveCommand",
1846 "Internal error in CMD_EdgeLeaveCommand");
1850 else
1853 /* check if the argument does contain at least one token */
1854 if (PeekToken(action , &action) == NULL)
1856 /* Just plain EdgeLeaveCommand, so all edge commands are
1857 * removed */
1858 if (Scr.PanFrameTop.command_leave != NULL)
1860 free(Scr.PanFrameTop.command_leave);
1861 Scr.PanFrameTop.command_leave = NULL;
1863 if (Scr.PanFrameBottom.command_leave != NULL)
1865 free(Scr.PanFrameBottom.command_leave);
1866 Scr.PanFrameBottom.command_leave = NULL;
1868 if (Scr.PanFrameLeft.command_leave != NULL)
1870 free(Scr.PanFrameLeft.command_leave);
1871 Scr.PanFrameLeft.command_leave = NULL;
1873 if (Scr.PanFrameRight.command_leave != NULL)
1875 free(Scr.PanFrameRight.command_leave);
1876 Scr.PanFrameRight.command_leave = NULL;
1879 else
1881 /* not a proper direction */
1882 fvwm_msg(ERR, "EdgeLeaveCommand",
1883 "EdgeLeaveCommand [direction [function]]");
1887 /* maybe something has changed so we adapt the pan frames */
1888 checkPanFrames();
1890 return;
1893 void CMD_EdgeThickness(F_CMD_ARGS)
1895 int val, n;
1897 n = GetIntegerArguments(action, NULL, &val, 1);
1898 if (n != 1)
1900 fvwm_msg(ERR,"setEdgeThickness",
1901 "EdgeThickness requires 1 numeric argument,"
1902 " found %d args",n);
1903 return;
1905 /* check range */
1906 if (val < 0 || val > 2)
1908 fvwm_msg(ERR,"setEdgeThickness",
1909 "EdgeThickness arg must be between 0 and 2,"
1910 " found %d",val);
1911 return;
1913 edge_thickness = val;
1914 checkPanFrames();
1916 return;
1919 void CMD_EdgeScroll(F_CMD_ARGS)
1921 int val1, val2, val1_unit, val2_unit, n;
1922 char *token;
1924 n = GetTwoArguments(action, &val1, &val2, &val1_unit, &val2_unit);
1925 if (n != 2)
1927 fvwm_msg(
1928 ERR, "SetEdgeScroll",
1929 "EdgeScroll requires two arguments");
1930 return;
1934 * if edgescroll >1000 and <100000
1935 * wrap at edges of desktop (a "spherical" desktop)
1937 if (val1 >= 1000 && val1_unit != 100)
1939 val1 /= 1000;
1940 Scr.flags.do_edge_wrap_x = 1;
1942 else
1944 Scr.flags.do_edge_wrap_x = 0;
1946 if (val2 >= 1000 && val2_unit != 100)
1948 val2 /= 1000;
1949 Scr.flags.do_edge_wrap_y = 1;
1951 else
1953 Scr.flags.do_edge_wrap_y = 0;
1956 action=SkipNTokens(action,2);
1957 token = PeekToken(action, NULL);
1959 if (token)
1961 if (StrEquals(token, "wrap"))
1963 Scr.flags.do_edge_wrap_x = 1;
1964 Scr.flags.do_edge_wrap_y = 1;
1966 else if (StrEquals(token, "wrapx"))
1968 Scr.flags.do_edge_wrap_x = 1;
1970 else if (StrEquals(token, "wrapy"))
1972 Scr.flags.do_edge_wrap_y = 1;
1976 Scr.EdgeScrollX = val1 * val1_unit / 100;
1977 Scr.EdgeScrollY = val2 * val2_unit / 100;
1979 checkPanFrames();
1981 return;
1984 void CMD_EdgeResistance(F_CMD_ARGS)
1986 int val[3];
1987 int n;
1989 val[0] = 0;
1990 n = GetIntegerArguments(action, NULL, val, 3);
1991 if (n > 1 && val[0] >= 10000)
1993 /* map val[0] >= 10000 in old syntax to -1 in new syntax */
1994 val[0] = -1;
1996 if (n == 1)
1998 Scr.ScrollDelay = val[0];
2000 else if (n >= 2 && n <= 3)
2002 char cmd[99];
2003 char stylecmd[99];
2004 char stylecmd2[99];
2006 Scr.ScrollDelay = val[0];
2007 sprintf(cmd, "EdgeResistance %d", val[0]);
2008 sprintf(stylecmd, "Style * EdgeMoveDelay %d", val[0]);
2009 if (n == 2)
2011 sprintf(
2012 stylecmd2, "Style * EdgeMoveResistance %d",
2013 val[1]);
2015 else
2017 sprintf(
2018 stylecmd2, "Style * EdgeMoveResistance %d %d",
2019 val[1], val[2]);
2021 fvwm_msg(
2022 OLD, "CMD_EdgeResistance",
2023 "The command EdgeResistance with three arguments is"
2024 " obsolete. Please use the following commands"
2025 " instead:");
2026 fvwm_msg(OLD, "", cmd);
2027 fvwm_msg(OLD, "", stylecmd);
2028 fvwm_msg(OLD, "", stylecmd2);
2029 execute_function(
2030 cond_rc, exc, cmd,
2031 FUNC_DONT_REPEAT | FUNC_DONT_EXPAND_COMMAND);
2032 execute_function(
2033 cond_rc, exc, stylecmd,
2034 FUNC_DONT_REPEAT | FUNC_DONT_EXPAND_COMMAND);
2035 execute_function(
2036 cond_rc, exc, stylecmd2,
2037 FUNC_DONT_REPEAT | FUNC_DONT_EXPAND_COMMAND);
2039 else
2041 fvwm_msg(
2042 ERR, "CMD_EdgeResistance",
2043 "EdgeResistance requires two or three arguments");
2044 return;
2047 return;
2050 void CMD_Xinerama(F_CMD_ARGS)
2052 int toggle;
2054 toggle = ParseToggleArgument(action, NULL, -1, 0);
2055 if (toggle == -1)
2057 toggle = !FScreenIsEnabled();
2059 if (!toggle != !FScreenIsEnabled())
2061 Scr.flags.do_need_window_update = True;
2062 Scr.flags.has_xinerama_state_changed = True;
2063 FScreenOnOff(toggle);
2064 broadcast_xinerama_state();
2067 return;
2070 void CMD_XineramaPrimaryScreen(F_CMD_ARGS)
2072 int val;
2074 val = FScreenGetScreenArgument(action, 0);
2075 FScreenSetPrimaryScreen(val);
2076 if (FScreenIsEnabled())
2078 Scr.flags.do_need_window_update = True;
2079 Scr.flags.has_xinerama_state_changed = True;
2081 broadcast_xinerama_state();
2083 return;
2086 void CMD_XineramaSls(F_CMD_ARGS)
2088 int toggle;
2090 toggle = ParseToggleArgument(action, NULL, -1, 0);
2091 if (toggle == -1)
2093 toggle = !FScreenIsSLSEnabled();
2095 if (!toggle != !FScreenIsSLSEnabled())
2097 if (FScreenIsEnabled())
2099 Scr.flags.do_need_window_update = True;
2100 Scr.flags.has_xinerama_state_changed = True;
2102 FScreenSLSOnOff(toggle);
2103 broadcast_xinerama_state();
2106 return;
2109 void CMD_XineramaSlsSize(F_CMD_ARGS)
2111 int val[2];
2113 if (GetIntegerArguments(action, NULL, val, 2) != 2 &&
2114 GetRectangleArguments(action, &val[0], &val[1]) != 2)
2116 val[0] = 1;
2117 val[1] = 1;
2119 if (FScreenConfigureSLSSize(val[0], val[1]))
2121 broadcast_xinerama_state();
2124 return;
2127 void CMD_XineramaSlsScreens(F_CMD_ARGS)
2129 int nscreens;
2130 char *args;
2132 if (GetIntegerArguments(action, &args, &nscreens, 1) != 1)
2134 nscreens = 0;
2135 args = NULL;
2137 else if (args == NULL)
2139 nscreens = 0;
2141 if (FScreenConfigureSLSScreens(nscreens, args))
2143 broadcast_xinerama_state();
2146 return;
2149 void CMD_DesktopSize(F_CMD_ARGS)
2151 int val[2];
2153 if (GetIntegerArguments(action, NULL, val, 2) != 2 &&
2154 GetRectangleArguments(action, &val[0], &val[1]) != 2)
2156 fvwm_msg(ERR, "CMD_DesktopSize",
2157 "DesktopSize requires two arguments");
2158 return;
2161 Scr.VxMax = (val[0] <= 0) ?
2162 0: val[0]*Scr.MyDisplayWidth-Scr.MyDisplayWidth;
2163 Scr.VyMax = (val[1] <= 0) ?
2164 0: val[1]*Scr.MyDisplayHeight-Scr.MyDisplayHeight;
2165 BroadcastPacket(
2166 M_NEW_PAGE, 7, (long)Scr.Vx, (long)Scr.Vy,
2167 (long)Scr.CurrentDesk, (long)Scr.MyDisplayWidth,
2168 (long)Scr.MyDisplayHeight,
2169 (long)((Scr.VxMax / Scr.MyDisplayWidth) + 1),
2170 (long)((Scr.VyMax / Scr.MyDisplayHeight) + 1));
2172 checkPanFrames();
2173 /* update GNOME pager */
2174 GNOME_SetAreaCount();
2175 EWMH_SetDesktopGeometry();
2177 return;
2182 * Move to a new desktop
2185 void CMD_GotoDesk(F_CMD_ARGS)
2187 goto_desk(GetDeskNumber(action, Scr.CurrentDesk));
2189 return;
2192 void CMD_Desk(F_CMD_ARGS)
2194 CMD_GotoDesk(F_PASS_ARGS);
2196 return;
2201 * Move to a new desktop and page at the same time.
2202 * This function is designed for use by the Pager, and replaces the old
2203 * GoToDesk 0 10000 hack.
2204 * - unmap all windows on the current desk so they don't flash when the
2205 * viewport is moved, then switch the viewport, then the desk.
2208 void CMD_GotoDeskAndPage(F_CMD_ARGS)
2210 int val[3];
2211 Bool is_new_desk;
2213 if (MatchToken(action, "prev"))
2215 val[0] = prev_desk_and_page_desk;
2216 val[1] = prev_desk_and_page_page_x;
2217 val[2] = prev_desk_and_page_page_y;
2219 else if (GetIntegerArguments(action, NULL, val, 3) == 3)
2221 val[1] *= Scr.MyDisplayWidth;
2222 val[2] *= Scr.MyDisplayHeight;
2224 else
2226 return;
2229 is_new_desk = (Scr.CurrentDesk != val[0]);
2230 if (is_new_desk)
2232 UnmapDesk(Scr.CurrentDesk, True);
2234 prev_desk_and_page_page_x = Scr.Vx;
2235 prev_desk_and_page_page_y = Scr.Vy;
2236 MoveViewport(val[1], val[2], True);
2237 if (is_new_desk)
2239 prev_desk = Scr.CurrentDesk;
2240 prev_desk_and_page_desk = Scr.CurrentDesk;
2241 Scr.CurrentDesk = val[0];
2242 MapDesk(val[0], True);
2243 focus_grab_buttons_all();
2244 BroadcastPacket(M_NEW_DESK, 1, (long)Scr.CurrentDesk);
2245 /* FIXME: domivogt (22-Apr-2000): Fake a 'restack' for sticky
2246 * window upon desk change. This is a workaround for a
2247 * problem in FvwmPager: The pager has a separate 'root'
2248 * window for each desk. If the active desk changes, the
2249 * pager destroys sticky mini windows and creates new ones in
2250 * the other desktop 'root'. But the pager can't know where to
2251 * stack them. So we have to tell it ecplicitly where they
2252 * go :-( This should be fixed in the pager, but right now the
2253 * pager doesn't the stacking order. */
2254 BroadcastRestackAllWindows();
2256 else
2258 BroadcastPacket(M_NEW_DESK, 1, (long)Scr.CurrentDesk);
2260 EWMH_SetCurrentDesktop();
2261 GNOME_SetCurrentDesk();
2262 GNOME_SetDeskCount();
2264 return;
2267 void CMD_GotoPage(F_CMD_ARGS)
2269 int x;
2270 int y;
2272 x = Scr.Vx;
2273 y = Scr.Vy;
2274 if (!get_page_arguments(action, &x, &y))
2276 fvwm_msg(
2277 ERR, "goto_page_func",
2278 "GotoPage: invalid arguments: %s", action);
2279 return;
2281 if (x < 0)
2283 x = 0;
2285 if (x > Scr.VxMax)
2287 x = Scr.VxMax;
2289 if (y < 0)
2291 y = 0;
2293 if (y > Scr.VyMax)
2295 y = Scr.VyMax;
2297 MoveViewport(x,y,True);
2299 return;
2302 /* function with parsing of command line */
2303 void CMD_MoveToDesk(F_CMD_ARGS)
2305 int desk;
2306 FvwmWindow * const fw = exc->w.fw;
2308 desk = GetDeskNumber(action, fw->Desk);
2309 if (desk == fw->Desk)
2311 return;
2313 do_move_window_to_desk(fw, desk);
2315 return;
2318 void CMD_Scroll(F_CMD_ARGS)
2320 int x,y;
2321 int val1, val2, val1_unit, val2_unit;
2323 if (GetTwoArguments(action, &val1, &val2, &val1_unit, &val2_unit) != 2)
2325 /* less then two integer parameters implies interactive
2326 * scroll check if we are scrolling in reverse direction */
2327 char *option;
2328 int scroll_speed = 1;
2330 option = PeekToken(action, NULL);
2331 if (option != NULL)
2333 if (StrEquals(option, "Reverse"))
2335 scroll_speed *= -1;
2338 __drag_viewport(exc, scroll_speed);
2340 return;
2342 if ((val1 > -100000)&&(val1 < 100000))
2344 x = Scr.Vx + val1*val1_unit/100;
2346 else
2348 x = Scr.Vx + (val1/1000)*val1_unit/100;
2350 if ((val2 > -100000)&&(val2 < 100000))
2352 y=Scr.Vy + val2*val2_unit/100;
2354 else
2356 y = Scr.Vy + (val2/1000)*val2_unit/100;
2358 if (((val1 <= -100000)||(val1 >= 100000))&&(x>Scr.VxMax))
2360 int xpixels;
2362 xpixels = (Scr.VxMax / Scr.MyDisplayWidth + 1) *
2363 Scr.MyDisplayWidth;
2364 x %= xpixels;
2365 y += Scr.MyDisplayHeight * (1+((x-Scr.VxMax-1)/xpixels));
2366 if (y > Scr.VyMax)
2368 y %= (Scr.VyMax / Scr.MyDisplayHeight + 1) *
2369 Scr.MyDisplayHeight;
2372 if (((val1 <= -100000)||(val1 >= 100000))&&(x<0))
2374 x = Scr.VxMax;
2375 y -= Scr.MyDisplayHeight;
2376 if (y < 0)
2378 y=Scr.VyMax;
2381 if (((val2 <= -100000)||(val2>= 100000))&&(y>Scr.VyMax))
2383 int ypixels = (Scr.VyMax / Scr.MyDisplayHeight + 1) *
2384 Scr.MyDisplayHeight;
2385 y %= ypixels;
2386 x += Scr.MyDisplayWidth * (1+((y-Scr.VyMax-1)/ypixels));
2387 if (x > Scr.VxMax)
2389 x %= (Scr.VxMax / Scr.MyDisplayWidth + 1) *
2390 Scr.MyDisplayWidth;
2393 if (((val2 <= -100000)||(val2>= 100000))&&(y<0))
2395 y = Scr.VyMax;
2396 x -= Scr.MyDisplayWidth;
2397 if (x < 0)
2399 x=Scr.VxMax;
2402 MoveViewport(x,y,True);
2404 return;
2409 * Defines the name of a desktop
2412 void CMD_DesktopName(F_CMD_ARGS)
2414 int desk;
2415 DesktopsInfo *t, *d, *new, **prev;
2417 if (GetIntegerArguments(action, &action, &desk, 1) != 1)
2419 fvwm_msg(
2420 ERR,"CMD_DesktopName",
2421 "First argument to DesktopName must be an integer: %s",
2422 action);
2423 return;
2426 d = Scr.Desktops->next;
2427 while (d != NULL && d->desk != desk)
2429 d = d->next;
2432 if (d != NULL)
2434 if (d->name != NULL)
2436 free(d->name);
2437 d->name = NULL;
2439 if (action != NULL && *action && *action != '\n')
2441 CopyString(&d->name, action);
2444 else
2446 /* new deskops entries: add it in order */
2447 d = Scr.Desktops->next;
2448 t = Scr.Desktops;
2449 prev = &(Scr.Desktops->next);
2450 while (d != NULL && d->desk < desk)
2452 t = t->next;
2453 prev = &(d->next);
2454 d = d->next;
2456 if (d == NULL)
2458 /* add it at the end */
2459 *prev = (DesktopsInfo *)safemalloc(
2460 sizeof(DesktopsInfo));
2461 memset(*prev, 0, sizeof(DesktopsInfo));
2462 (*prev)->desk = desk;
2463 if (action != NULL && *action && *action != '\n')
2465 CopyString(&((*prev)->name), action);
2468 else
2470 /* instert it */
2471 new = (DesktopsInfo *)safemalloc(sizeof(DesktopsInfo));
2472 memset(new, 0, sizeof(DesktopsInfo));
2473 new->desk = desk;
2474 if (action != NULL && *action && *action != '\n')
2476 CopyString(&(new->name), action);
2478 t->next = new;
2479 new->next = d;
2481 /* should check/set the working areas */
2484 if (!fFvwmInStartup)
2486 char *msg;
2487 const char *default_desk_name = _("Desk");
2489 /* should send the info to the FvwmPager and set the EWMH
2490 * desktop names */
2491 if (action != NULL && *action && *action != '\n')
2493 msg = (char *)safemalloc(strlen(action) + 44);
2494 sprintf(msg, "DesktopName %d %s", desk, action);
2496 else
2498 msg = (char *)safemalloc(strlen(default_desk_name)+44);
2499 sprintf(
2500 msg, "DesktopName %d %s %d", desk,
2501 default_desk_name, desk);
2503 BroadcastConfigInfoString(msg);
2504 free(msg);
2505 EWMH_SetDesktopNames();
2508 return;