diff --git a/src/display.c b/src/display.c index 0d4d6b165..aec65d798 100644 --- a/src/display.c +++ b/src/display.c @@ -1290,7 +1290,11 @@ meta_display_begin_grab_op (MetaDisplay *display, display->grab_button = button; display->grab_root_x = root_x; display->grab_root_y = root_y; - + display->grab_initial_window_pos = window->rect; + meta_window_get_position (display->grab_window, + &display->grab_initial_window_pos.x, + &display->grab_initial_window_pos.y); + meta_verbose ("Grab op %d on window %s successful\n", display->grab_op, display->grab_window->desc); diff --git a/src/display.h b/src/display.h index f2afd56ea..11718ab49 100644 --- a/src/display.h +++ b/src/display.h @@ -129,6 +129,7 @@ struct _MetaDisplay gulong grab_mask; guint grab_have_pointer : 1; guint grab_have_keyboard : 1; + MetaRectangle grab_initial_window_pos; }; gboolean meta_display_open (const char *name); diff --git a/src/keybindings.c b/src/keybindings.c index 759baf8d0..f91c7d0a4 100644 --- a/src/keybindings.c +++ b/src/keybindings.c @@ -24,6 +24,7 @@ #include "errors.h" #include "ui.h" #include "frame.h" +#include "place.h" #include @@ -401,47 +402,84 @@ meta_display_process_key_event (MetaDisplay *display, int x, y; int incr; gboolean smart_snap; + int edge; + + if (event->type == KeyRelease) + return; /* don't care about releases */ if (window == NULL) meta_bug ("NULL window while META_GRAB_OP_MOVING\n"); meta_window_get_position (window, &x, &y); - smart_snap = - (event->xkey.state & ControlMask) != 0 && - (event->xkey.state & ShiftMask) != 0; + smart_snap = (event->xkey.state & ShiftMask) != 0; - /* FIXME replace LARGE_INCREMENT with intelligent snapping */ #define SMALL_INCREMENT 1 #define NORMAL_INCREMENT 10 -#define LARGE_INCREMENT 100 if (smart_snap) - incr = LARGE_INCREMENT; + incr = 0; else if (event->xkey.state & ControlMask) incr = SMALL_INCREMENT; else incr = NORMAL_INCREMENT; + + /* When moving by increments, we still snap to edges if the move + * to the edge is smaller than the increment. This is because + * Shift + arrow to snap is sort of a hidden feature. This way + * people using just arrows shouldn't get too frustrated. + */ switch (keysym) { case XK_Up: + case XK_KP_Up: + edge = meta_window_find_next_horizontal_edge (window, FALSE); y -= incr; + + if (smart_snap || ((edge > y) && ABS (edge - y) < incr)) + y = edge; + handled = TRUE; break; case XK_Down: + case XK_KP_Down: + edge = meta_window_find_next_horizontal_edge (window, TRUE); y += incr; + + if (smart_snap || ((edge < y) && ABS (edge - y) < incr)) + y = edge; + handled = TRUE; break; case XK_Left: + case XK_KP_Left: + edge = meta_window_find_next_vertical_edge (window, FALSE); x -= incr; + + if (smart_snap || ((edge > x) && ABS (edge - x) < incr)) + x = edge; + handled = TRUE; break; case XK_Right: + case XK_KP_Right: + edge = meta_window_find_next_vertical_edge (window, TRUE); x += incr; + if (smart_snap || ((edge < x) && ABS (edge - x) < incr)) + x = edge; handled = TRUE; break; + case XK_Escape: + /* End move and restore to original position */ + meta_window_move_resize (display->grab_window, + display->grab_initial_window_pos.x, + display->grab_initial_window_pos.y, + display->grab_initial_window_pos.width, + display->grab_initial_window_pos.height); + break; + default: break; } diff --git a/src/place.c b/src/place.c index c303cfb44..f2c70adbe 100644 --- a/src/place.c +++ b/src/place.c @@ -23,6 +23,7 @@ #include "workspace.h" #include #include +#include static gint northwestcmp (gconstpointer a, gconstpointer b) @@ -304,3 +305,351 @@ meta_window_place (MetaWindow *window, *new_x = x; *new_y = y; } + + +/* These are used while moving or resizing to "snap" to useful + * places; the return value is the x/y position of the window to + * be snapped to the given edge. + * + * They only use edges on the current workspace, since things + * would be weird otherwise. + */ +static GSList* +get_windows_on_same_workspace (MetaWindow *window, + int *n_windows) +{ + GSList *windows; + GSList *all_windows; + GSList *tmp; + int i; + + windows = NULL; + + i = 0; + all_windows = meta_display_list_windows (window->display); + + tmp = all_windows; + while (tmp != NULL) + { + MetaWindow *w = tmp->data; + + if (!w->minimized && + w != window && + meta_workspace_contains_window (window->screen->active_workspace, + w)) + { + windows = g_slist_prepend (windows, w); + ++i; + } + + tmp = tmp->next; + } + + if (n_windows) + *n_windows = i; + + return windows; +} + +static void +window_get_edges (MetaWindow *w, + int *left, + int *right, + int *top, + int *bottom) +{ + int left_edge; + int right_edge; + int top_edge; + int bottom_edge; + MetaRectangle rect; + + meta_window_get_outer_rect (w, &rect); + + left_edge = rect.x; + right_edge = rect.x + rect.width; + top_edge = rect.y; + bottom_edge = rect.y + rect.height; + + if (left) + *left = left_edge; + if (right) + *right = right_edge; + if (top) + *top = top_edge; + if (bottom) + *bottom = bottom_edge; +} + +static int +intcmp (const void* a, const void* b) +{ + const int *ai = a; + const int *bi = b; + + if (*ai < *bi) + return -1; + else if (*ai > *bi) + return 1; + else + return 0; +} + +static gboolean +rects_overlap_vertically (const MetaRectangle *a, + const MetaRectangle *b) +{ + /* if they don't overlap, then either a is above b + * or b is above a + */ + if ((a->y + a->height) < b->y) + return FALSE; + else if ((b->y + b->height) < a->y) + return FALSE; + else + return TRUE; +} + +static gboolean +rects_overlap_horizontally (const MetaRectangle *a, + const MetaRectangle *b) +{ + if ((a->x + a->width) < b->x) + return FALSE; + else if ((b->x + b->width) < a->x) + return FALSE; + else + return TRUE; +} + +int +meta_window_find_next_vertical_edge (MetaWindow *window, + gboolean right) +{ + GSList *windows; + GSList *tmp; + int left_edge, right_edge; + int n_windows; + int *edges; + int i; + int n_edges; + int retval; + MetaRectangle rect; + + windows = get_windows_on_same_workspace (window, &n_windows); + + i = 0; + n_edges = n_windows * 2 + 4; /* 4 = workspace/screen edges */ + edges = g_new (int, n_edges); + + /* workspace/screen edges */ + edges[i] = window->screen->active_workspace->workarea.x; + ++i; + edges[i] = + window->screen->active_workspace->workarea.x + + window->screen->active_workspace->workarea.width; + ++i; + edges[i] = 0; + ++i; + edges[i] = window->screen->width; + ++i; + + g_assert (i == 4); + + meta_window_get_outer_rect (window, &rect); + + /* get window edges */ + tmp = windows; + while (tmp != NULL) + { + MetaWindow *w = tmp->data; + MetaRectangle w_rect; + + meta_window_get_outer_rect (w, &w_rect); + + if (rects_overlap_vertically (&rect, &w_rect)) + { + window_get_edges (w, &edges[i], &edges[i+1], NULL, NULL); + i += 2; + } + + tmp = tmp->next; + } + n_edges = i; + + g_slist_free (windows); + + /* Sort */ + qsort (edges, n_edges, sizeof (int), intcmp); + + /* Find next */ + meta_window_get_position (window, &retval, NULL); + + window_get_edges (window, &left_edge, &right_edge, NULL, NULL); + + if (right) + { + i = 0; + while (i < n_edges) + { + if (edges[i] > right_edge) + { + /* This is the one we want, snap right + * edge of window to edges[i] + */ + retval = edges[i]; + if (window->frame) + { + retval -= window->frame->rect.width; + retval += window->frame->child_x; + } + else + { + retval -= window->rect.width; + } + break; + } + + ++i; + } + } + else + { + i = n_edges; + do + { + --i; + + if (edges[i] < left_edge) + { + /* This is the one we want */ + retval = edges[i]; + if (window->frame) + retval += window->frame->child_x; + + break; + } + } + while (i > 0); + } + + g_free (edges); + + return retval; +} + +int +meta_window_find_next_horizontal_edge (MetaWindow *window, + gboolean down) +{ + GSList *windows; + GSList *tmp; + int top_edge, bottom_edge; + int n_windows; + int *edges; + int i; + int n_edges; + int retval; + MetaRectangle rect; + + windows = get_windows_on_same_workspace (window, &n_windows); + + i = 0; + n_edges = n_windows * 2 + 4; /* 4 = workspace/screen edges */ + edges = g_new (int, n_edges); + + /* workspace/screen edges */ + edges[i] = window->screen->active_workspace->workarea.y; + ++i; + edges[i] = + window->screen->active_workspace->workarea.y + + window->screen->active_workspace->workarea.height; + ++i; + edges[i] = 0; + ++i; + edges[i] = window->screen->height; + ++i; + + g_assert (i == 4); + + meta_window_get_outer_rect (window, &rect); + + /* get window edges */ + tmp = windows; + while (tmp != NULL) + { + MetaWindow *w = tmp->data; + MetaRectangle w_rect; + + meta_window_get_outer_rect (w, &w_rect); + + if (rects_overlap_horizontally (&rect, &w_rect)) + { + window_get_edges (w, NULL, NULL, &edges[i], &edges[i+1]); + i += 2; + } + + tmp = tmp->next; + } + n_edges = i; + + g_slist_free (windows); + + /* Sort */ + qsort (edges, n_edges, sizeof (int), intcmp); + + /* Find next */ + meta_window_get_position (window, NULL, &retval); + + window_get_edges (window, NULL, NULL, &top_edge, &bottom_edge); + + if (down) + { + i = 0; + while (i < n_edges) + { + if (edges[i] > bottom_edge) + { + /* This is the one we want, snap right + * edge of window to edges[i] + */ + retval = edges[i]; + if (window->frame) + { + retval -= window->frame->rect.height; + retval += window->frame->child_y; + } + else + { + retval -= window->rect.height; + } + break; + } + + ++i; + } + } + else + { + i = n_edges; + do + { + --i; + + if (edges[i] < top_edge) + { + /* This is the one we want */ + retval = edges[i]; + if (window->frame) + retval += window->frame->child_y; + + break; + } + } + while (i > 0); + } + + g_free (edges); + + return retval; +} diff --git a/src/place.h b/src/place.h index 6b79ecdc9..1d3274ba8 100644 --- a/src/place.h +++ b/src/place.h @@ -32,6 +32,11 @@ void meta_window_place (MetaWindow *window, int *new_x, int *new_y); +int meta_window_find_next_vertical_edge (MetaWindow *window, + gboolean right); +int meta_window_find_next_horizontal_edge (MetaWindow *window, + gboolean down); + #endif diff --git a/src/run-metacity.sh b/src/run-metacity.sh index 7aa123a71..b2266dc97 100755 --- a/src/run-metacity.sh +++ b/src/run-metacity.sh @@ -15,7 +15,7 @@ if test -z "$CLIENTS"; then fi if test -z "$ONLY_WM"; then - Xnest -ac :1 -scrns $SCREENS -geometry 640x400 -bw 15 & + Xnest -ac :1 -scrns $SCREENS -geometry 640x480 -bw 15 & usleep 500000 if test $CLIENTS != 0; then diff --git a/src/session.c b/src/session.c index 414b59881..15258516c 100644 --- a/src/session.c +++ b/src/session.c @@ -54,6 +54,10 @@ process_ice_messages (GIOChannel *channel, IceConn connection = (IceConn) client_data; IceProcessMessagesStatus status; + /* This blocks infinitely sometimes. I don't know what + * to do about it. Checking "condition" just breaks + * session management. + */ status = IceProcessMessages (connection, NULL, NULL); if (status == IceProcessMessagesIOError) diff --git a/src/window.c b/src/window.c index 0c0724635..cb079b97e 100644 --- a/src/window.c +++ b/src/window.c @@ -1408,13 +1408,17 @@ meta_window_get_position (MetaWindow *window, { if (window->frame) { - *x = window->frame->rect.x + window->frame->child_x; - *y = window->frame->rect.y + window->frame->child_y; + if (x) + *x = window->frame->rect.x + window->frame->child_x; + if (y) + *y = window->frame->rect.y + window->frame->child_y; } else { - *x = window->rect.x; - *y = window->rect.y; + if (x) + *x = window->rect.x; + if (y) + *y = window->rect.y; } } @@ -1489,6 +1493,16 @@ meta_window_get_gravity_position (MetaWindow *window, *root_y = y; } +void +meta_window_get_outer_rect (MetaWindow *window, + MetaRectangle *rect) +{ + if (window->frame) + *rect = window->frame->rect; + else + *rect = window->rect; +} + void meta_window_delete (MetaWindow *window, Time timestamp) diff --git a/src/window.h b/src/window.h index 192a0daf3..9bd3b8b9d 100644 --- a/src/window.h +++ b/src/window.h @@ -257,6 +257,8 @@ void meta_window_get_position (MetaWindow *window, void meta_window_get_gravity_position (MetaWindow *window, int *x, int *y); +void meta_window_get_outer_rect (MetaWindow *window, + MetaRectangle *rect); void meta_window_delete (MetaWindow *window, Time timestamp); void meta_window_focus (MetaWindow *window,