Ola

I wrote about this I while ago, but didn't get any response.  Decided to
give it another shot (at the start of the month, for a change)...

I move windows around with key bindings; to tell the truth most of my
fvwm configuration is keyboard-based.  The problem is that, sure, it's
easy to move windows to specific positions, but what I really need is
something that will snap the window to the closest intervening window
border (almost comparable to the "grow" option of the resize functions).

I hacked up a simple solution -- please note, it's a HACK, so it's not
guaranteed to take into account anybody else's preferences.  For
example, I've hardcoded it to do animated moves, which really should be
a distinct parameter.  The fvwm2rc syntax is also a bit dodgy, but for a
proof-of-concept patch it works admirably (after all, I use it every
day).

To use: apply the patch (against fvwm-2.5.12), add the following lines
to your .fvwm2rc ...

Key Up          WSF     C       SnapMove North
Key Left        WSF     C       SnapMove West
Key Down        WSF     C       SnapMove South
Key Right       WSF     C       SnapMove East

... and happily move windows around with Ctrl-Cursor.  Good idea/bad
idea -- let me know please.

Cheers
-- 
Kobus Retief
diff -Nurp fvwm-2.5.12/fvwm/commands.h fvwm-2.5.12-new/fvwm/commands.h
--- fvwm-2.5.12/fvwm/commands.h 2004-09-30 19:47:16.000000000 +0200
+++ fvwm-2.5.12-new/fvwm/commands.h     2005-01-31 23:31:15.100199288 +0200
@@ -178,6 +178,7 @@ enum
        F_RESIZEMOVE_MAXIMIZE,
        F_RESTACKTRANSIENTS,
        F_SEND_STRING,
+       F_SNAPMOVE,
        F_STATE,
        F_STICK,
        F_STICKACROSSDESKS,
@@ -355,6 +356,7 @@ P(SetAnimation);
 P(SetEnv);
 P(SnapAttraction);
 P(SnapGrid);
+P(SnapMove);
 P(State);
 P(Stick);
 P(StickAcrossDesks);
diff -Nurp fvwm-2.5.12/fvwm/functable.c fvwm-2.5.12-new/fvwm/functable.c
--- fvwm-2.5.12/fvwm/functable.c        2004-09-30 19:47:16.000000000 +0200
+++ fvwm-2.5.12-new/fvwm/functable.c    2005-01-31 23:31:15.100199288 +0200
@@ -553,6 +553,10 @@ const func_t func_table[] =
        CMD_ENT("snapgrid", CMD_SnapGrid, F_SNAP_GRID, 0, 0),
        /* - Control grid used with SnapAttraction */
 
+       CMD_ENT("snapmove", CMD_SnapMove, F_SNAPMOVE,
+               FUNC_NEEDS_WINDOW, CRS_MOVE),
+       /* - Move a window to snap to nearest neighbour */
+       
        CMD_ENT("state", CMD_State, F_STATE,
                FUNC_NEEDS_WINDOW, CRS_SELECT),
        /* - Control user defined window states */
diff -Nurp fvwm-2.5.12/fvwm/move_resize.c fvwm-2.5.12-new/fvwm/move_resize.c
--- fvwm-2.5.12/fvwm/move_resize.c      2004-09-27 11:33:04.000000000 +0200
+++ fvwm-2.5.12-new/fvwm/move_resize.c  2005-01-31 23:44:03.382402640 +0200
@@ -60,6 +60,7 @@
 #include "colormaps.h"
 #include "update.h"
 #include "stack.h"
+#include "placement.h"
 
 /* ----- move globals ----- */
 
@@ -2670,6 +2671,256 @@ void CMD_SnapGrid(F_CMD_ARGS)
        return;
 }
 
+
+
+/*
+ * Move a window to the closest position that'll snap to the border of
+ * an intervening or adjacent window.  Picture setting SnapAttraction,
+ * then moving the window by hand in a direction (north, south, east or
+ * west).  The first place it snaps is where this function places the
+ * window.
+ *
+ * Syntax: SnapMove North | South | East | West
+ *
+ * This is a proof-of-concept implementation, so don't expect it to be
+ * pretty...
+ *
+ * TODO:
+ * - Handle Warp/Animate parameters instead of just setting 'em
+ * - See rant about __move_window()
+ * - Should I handle NorthEast, SouthWest etc?
+ * - Should I snap to SnapGrid as well?
+ * - If I do snap to SnapGrid, shouldn't I be checking snap behaviour?
+ *   I.e. snap only to SnapGrid if SnapAttraction is set to 0...
+ *        snap only to window borders if SnapGrid is set to 0...
+ * - If I do check snap behaviour, shouldn't there be a clearer
+ *   mechanism to state user preference (e.g. SnapBehaviour)
+ * - I think I might be doing something horribly wrong in the depths of
+ *   this function; it works well with my .fvwm2rc, but I don't use
+ *   icons.  It'll probably break with a different config.
+ * - Xinerama spanning, screen border handling (what about
+ *   EdgeResistance?)
+ * - ...
+ */
+void CMD_SnapMove(F_CMD_ARGS)
+{
+       /* Read parameters */
+       direction_t direction = gravity_parse_dir_argument(action, &action, 
DIR_NONE);
+       if (direction != DIR_N && direction != DIR_S && direction != DIR_E && 
direction != DIR_W)
+               return;
+
+       /* May we move it? */
+       FvwmWindow *fw = exc->w.fw;
+       if (!is_function_allowed(F_MOVE, NULL, fw, True, False))
+               return;
+
+       /* Extract current window geometry
+        * Is there a better, fail-safe method to do this?  A more
+        * arcane, voodoo-magic bit of code that I didn't notice?  What
+        * I really want to know is, is get_..._geometry() sufficient
+        * for snap checking?
+        */
+       rectangle self;
+       if (!get_visible_window_or_icon_geometry(fw, &self))
+               return;
+
+       /* Compare with other window borders */
+       FvwmWindow *tmp;
+       rectangle other;
+
+       int new_x = self.x;
+       int new_y = self.y;
+
+       int min_dx = 99999;
+       int max_dx = -99999;
+       int min_dy = 99999;
+       int max_dy = -99999;
+
+       for (tmp = Scr.FvwmRoot.next; tmp; tmp = tmp->next) {
+               if (fw->Desk != tmp->Desk || fw == tmp)
+                       continue;
+               if (!get_visible_window_or_icon_geometry(tmp, &other))
+                       continue;
+
+               /* Placement penalty -- unabashedly ripped from placement.c */
+               int placement_penalty = 0;
+               if (IS_ICONIFIED(tmp))
+                       placement_penalty = ICON_PLACEMENT_PENALTY(tmp);
+               else if (compare_window_layers(tmp, fw) > 0)
+                       placement_penalty = ONTOP_PLACEMENT_PENALTY(tmp);
+               else if (compare_window_layers(tmp, fw) < 0)
+                       placement_penalty = BELOW_PLACEMENT_PENALTY(tmp);
+               else if (IS_STICKY_ACROSS_PAGES(tmp) || 
IS_STICKY_ACROSS_DESKS(tmp))
+                       placement_penalty = STICKY_PLACEMENT_PENALTY(tmp);
+               else
+                       placement_penalty = NORMAL_PLACEMENT_PENALTY(tmp);
+               if (placement_penalty == 0) continue;
+
+               int tmp_x, tmp_dx;
+               int tmp_y, tmp_dy;
+
+               if (self.y >= other.y - self.height && self.y <= other.y + 
other.height) {
+                       tmp_x = other.x + other.width;
+                       tmp_dx = tmp_x - self.x;
+                       if (tmp_x < 0)
+                               tmp_x = 0;
+                       if (tmp_x > Scr.MyDisplayWidth - self.width)
+                               tmp_x = Scr.MyDisplayWidth - self.width;
+                       if (direction == DIR_W && tmp_dx < 0 && tmp_dx > 
max_dx) {
+                               new_x = tmp_x;
+                               max_dx = tmp_dx;
+                       }
+                       if (direction == DIR_E && tmp_dx > 0 && tmp_dx < 
min_dx) {
+                               new_x = tmp_x;
+                               min_dx = tmp_dx;
+                       }
+
+                       tmp_x = other.x - self.width;
+                       tmp_dx = tmp_x - self.x;
+                       if (tmp_x < 0)
+                               tmp_x = 0;
+                       if (tmp_x > Scr.MyDisplayWidth - self.width)
+                               tmp_x = Scr.MyDisplayWidth - self.width;
+                       if (direction == DIR_W && tmp_dx < 0 && tmp_dx > 
max_dx) {
+                               new_x = tmp_x;
+                               max_dx = tmp_dx;
+                       }
+                       if (direction == DIR_E && tmp_dx > 0 && tmp_dx < 
min_dx) {
+                               new_x = tmp_x;
+                               min_dx = tmp_dx;
+                       }
+               }
+
+               if (self.x >= other.x - self.width && self.x <= other.x + 
other.width) {
+                       tmp_y = other.y + other.height;
+                       tmp_dy = tmp_y - self.y;
+                       if (tmp_y < 0)
+                               tmp_y = 0;
+                       if (tmp_y > Scr.MyDisplayHeight - self.height)
+                               tmp_y = Scr.MyDisplayHeight - self.height;
+                       if (direction == DIR_N && tmp_dy < 0 && tmp_dy > 
max_dy) {
+                               new_y = tmp_y;
+                               max_dy = tmp_dy;
+                       }
+                       if (direction == DIR_S && tmp_dy > 0 && tmp_dy < 
min_dy) {
+                               new_y = tmp_y;
+                               min_dy = tmp_dy;
+                       }
+
+                       tmp_y = other.y - self.height;
+                       tmp_dy = tmp_y - self.y;
+                       if (tmp_y < 0)
+                               tmp_y = 0;
+                       if (tmp_y > Scr.MyDisplayHeight - self.height)
+                               tmp_y = Scr.MyDisplayHeight - self.height;
+                       if (direction == DIR_N && tmp_dy < 0 && tmp_dy > 
max_dy) {
+                               new_y = tmp_y;
+                               max_dy = tmp_dy;
+                       }
+                       if (direction == DIR_S && tmp_dy > 0 && tmp_dy < 
min_dy) {
+                               new_y = tmp_y;
+                               min_dy = tmp_dy;
+                       }
+               }
+       }
+
+       /* Compare with display borders */
+       if (self.x < 0)
+               new_x = 0;
+       if (self.x > Scr.MyDisplayWidth - self.width)
+               new_x = Scr.MyDisplayWidth - self.width;
+       if (self.y < 0)
+               new_y = 0;
+       if (self.y > Scr.MyDisplayHeight - self.height)
+               new_y = Scr.MyDisplayHeight - self.height;
+       if (new_x == self.x) {
+               if (direction == DIR_W)
+                       new_x = 0;
+               if (direction == DIR_E)
+                       new_x = Scr.MyDisplayWidth - self.width;
+       }
+
+       if (new_y == self.y) {
+               if (direction == DIR_N)
+                       new_y = 0;
+               if (direction == DIR_S)
+                       new_y = Scr.MyDisplayHeight - self.height;
+       }
+
+       /* Move the window -- this code unabashedly copied, pasted and
+        * modified from __move_window().  It's ugly and hackish, but I
+        * decided to use tried and tested code blindly instead of
+        * painstakingly attempting to figure out exactly what it does,
+        * and then duplicating it by hand.  Clever, no?
+        *
+        * <RANT>
+        * Shouldn't this code block be abstracted to its own function?
+        * It does almost exactly the same thing as __move_icon(), only
+        * for client windows.  In my opinion __move_window() shouldn't
+        * be parsing parameters, it should simply move windows.  I'm
+        * biased, of course, since a pure movement function will make
+        * my life a bit easier...
+        * </RANT>
+        */
+       Window w;
+
+       w = FW_W_FRAME(fw);
+       if (IS_ICONIFIED(fw))
+       {
+               if (FW_W_ICON_PIXMAP(fw) != None)
+               {
+                       w = FW_W_ICON_PIXMAP(fw);
+                       XUnmapWindow(dpy,FW_W_ICON_TITLE(fw));
+               }
+               else
+               {
+                       w = FW_W_ICON_TITLE(fw);
+               }
+       }
+
+       Window JunkRoot;
+       int JunkWidth, JunkHeight, JunkBW, JunkDepth;
+       int old_x, old_y;
+       XGetGeometry(dpy, w, &JunkRoot, &old_x, &old_y, &JunkWidth, 
&JunkHeight, &JunkBW, &JunkDepth);
+
+       /* These two should probably be set through parameters */
+       Bool do_animate = TRUE;         /* Li'l hack */
+       Bool fWarp      = TRUE;         /* Li'l hackity hack hack hack */
+
+       if (w == FW_W_FRAME(fw))
+       {
+               int dx = new_x - fw->frame_g.x;
+               int dy = new_y - fw->frame_g.y;
+               if (do_animate)
+                       AnimatedMoveFvwmWindow(fw, w, -1, -1, new_x, new_y, 
fWarp, -1, NULL);
+               frame_setup_window( fw, new_x, new_y, fw->frame_g.width, 
fw->frame_g.height, True);
+               if (fWarp & !do_animate)
+                       FWarpPointer(dpy, None, None, 0, 0, 0, 0, new_x - 
old_x, new_y - old_y);
+               if (IS_MAXIMIZED(fw))
+               {
+                       fw->max_g.x += dx;
+                       fw->max_g.y += dy;
+               }
+               else
+               {
+                       fw->normal_g.x += dx;
+                       fw->normal_g.y += dy;
+               }
+               update_absolute_geometry(fw);
+               maximize_adjust_offset(fw);
+               XFlush(dpy);
+               GNOME_SetWinArea(fw);
+       }
+       else /* icon window */
+       {
+               __move_icon(fw, new_x, new_y, old_x, old_y, do_animate, fWarp);
+               XFlush(dpy);
+       }
+
+       return;
+}
+
+
 static Pixmap XorPixmap = None;
 
 void CMD_XorValue(F_CMD_ARGS)

Reply via email to