00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include <config.h>
00025 #include "edge-resistance.h"
00026 #include "boxes.h"
00027 #include "display-private.h"
00028 #include "workspace.h"
00029
00030
00031
00032
00033 #define WINDOW_EDGES_RELEVANT(window, display) \
00034 meta_window_should_be_showing (window) && \
00035 window->screen == display->grab_screen && \
00036 window != display->grab_window && \
00037 window->type != META_WINDOW_DESKTOP && \
00038 window->type != META_WINDOW_MENU && \
00039 window->type != META_WINDOW_SPLASHSCREEN
00040
00041 struct ResistanceDataForAnEdge
00042 {
00043 gboolean timeout_setup;
00044 guint timeout_id;
00045 int timeout_edge_pos;
00046 gboolean timeout_over;
00047 GSourceFunc timeout_func;
00048 MetaWindow *window;
00049 int keyboard_buildup;
00050 };
00051 typedef struct ResistanceDataForAnEdge ResistanceDataForAnEdge;
00052
00053 struct MetaEdgeResistanceData
00054 {
00055 GArray *left_edges;
00056 GArray *right_edges;
00057 GArray *top_edges;
00058 GArray *bottom_edges;
00059
00060 ResistanceDataForAnEdge left_data;
00061 ResistanceDataForAnEdge right_data;
00062 ResistanceDataForAnEdge top_data;
00063 ResistanceDataForAnEdge bottom_data;
00064 };
00065
00066
00067
00068
00069 static int
00070 find_index_of_edge_near_position (const GArray *edges,
00071 int position,
00072 gboolean want_interval_min,
00073 gboolean horizontal)
00074 {
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090 int low, high, mid;
00091 int compare;
00092 MetaEdge *edge;
00093
00094
00095
00096
00097 mid = 0;
00098 edge = g_array_index (edges, MetaEdge*, mid);
00099 compare = horizontal ? edge->rect.x : edge->rect.y;
00100
00101
00102 low = 0;
00103 high = edges->len - 1;
00104 while (low < high)
00105 {
00106 mid = low + (high - low)/2;
00107 edge = g_array_index (edges, MetaEdge*, mid);
00108 compare = horizontal ? edge->rect.x : edge->rect.y;
00109
00110 if (compare == position)
00111 break;
00112
00113 if (compare > position)
00114 high = mid - 1;
00115 else
00116 low = mid + 1;
00117 }
00118
00119
00120
00121
00122
00123
00124
00125
00126 if (want_interval_min)
00127 {
00128 while (compare >= position && mid > 0)
00129 {
00130 mid--;
00131 edge = g_array_index (edges, MetaEdge*, mid);
00132 compare = horizontal ? edge->rect.x : edge->rect.y;
00133 }
00134 while (compare < position && mid < (int)edges->len - 1)
00135 {
00136 mid++;
00137 edge = g_array_index (edges, MetaEdge*, mid);
00138 compare = horizontal ? edge->rect.x : edge->rect.y;
00139 }
00140
00141
00142 if (compare < position)
00143 return edges->len;
00144
00145
00146 return mid;
00147 }
00148 else
00149 {
00150 while (compare <= position && mid < (int)edges->len - 1)
00151 {
00152 mid++;
00153 edge = g_array_index (edges, MetaEdge*, mid);
00154 compare = horizontal ? edge->rect.x : edge->rect.y;
00155 }
00156 while (compare > position && mid > 0)
00157 {
00158 mid--;
00159 edge = g_array_index (edges, MetaEdge*, mid);
00160 compare = horizontal ? edge->rect.x : edge->rect.y;
00161 }
00162
00163
00164 if (compare > position)
00165 return -1;
00166
00167
00168 return mid;
00169 }
00170 }
00171
00172 static gboolean
00173 points_on_same_side (int ref, int pt1, int pt2)
00174 {
00175 return (pt1 - ref) * (pt2 - ref) > 0;
00176 }
00177
00178 static int
00179 find_nearest_position (const GArray *edges,
00180 int position,
00181 int old_position,
00182 const MetaRectangle *new_rect,
00183 gboolean horizontal,
00184 gboolean only_forward)
00185 {
00186
00187
00188
00189
00190
00191 int low, high, mid;
00192 int compare;
00193 MetaEdge *edge;
00194 int best, best_dist, i;
00195 gboolean edges_align;
00196
00197
00198
00199
00200 mid = 0;
00201 edge = g_array_index (edges, MetaEdge*, mid);
00202 compare = horizontal ? edge->rect.x : edge->rect.y;
00203
00204
00205 low = 0;
00206 high = edges->len - 1;
00207 while (low < high)
00208 {
00209 mid = low + (high - low)/2;
00210 edge = g_array_index (edges, MetaEdge*, mid);
00211 compare = horizontal ? edge->rect.x : edge->rect.y;
00212
00213 if (compare == position)
00214 break;
00215
00216 if (compare > position)
00217 high = mid - 1;
00218 else
00219 low = mid + 1;
00220 }
00221
00222
00223
00224
00225
00226 best = old_position;
00227 best_dist = INT_MAX;
00228
00229
00230 edge = g_array_index (edges, MetaEdge*, mid);
00231 compare = horizontal ? edge->rect.x : edge->rect.y;
00232 edges_align = meta_rectangle_edge_aligns (new_rect, edge);
00233 if (edges_align &&
00234 (!only_forward || !points_on_same_side (position, compare, old_position)))
00235 {
00236 int dist = ABS (compare - position);
00237 if (dist < best_dist)
00238 {
00239 best = compare;
00240 best_dist = dist;
00241 }
00242 }
00243
00244
00245 for (i = mid + 1; i < (int)edges->len; i++)
00246 {
00247 edge = g_array_index (edges, MetaEdge*, i);
00248 compare = horizontal ? edge->rect.x : edge->rect.y;
00249
00250 edges_align = horizontal ?
00251 meta_rectangle_vert_overlap (&edge->rect, new_rect) :
00252 meta_rectangle_horiz_overlap (&edge->rect, new_rect);
00253
00254 if (edges_align &&
00255 (!only_forward ||
00256 !points_on_same_side (position, compare, old_position)))
00257 {
00258 int dist = ABS (compare - position);
00259 if (dist < best_dist)
00260 {
00261 best = compare;
00262 best_dist = dist;
00263 }
00264 break;
00265 }
00266 }
00267
00268
00269 for (i = mid-1; i >= 0; i--)
00270 {
00271 edge = g_array_index (edges, MetaEdge*, i);
00272 compare = horizontal ? edge->rect.x : edge->rect.y;
00273
00274 edges_align = horizontal ?
00275 meta_rectangle_vert_overlap (&edge->rect, new_rect) :
00276 meta_rectangle_horiz_overlap (&edge->rect, new_rect);
00277
00278 if (edges_align &&
00279 (!only_forward ||
00280 !points_on_same_side (position, compare, old_position)))
00281 {
00282 int dist = ABS (compare - position);
00283 if (dist < best_dist)
00284 {
00285 best = compare;
00286 best_dist = dist;
00287 }
00288 break;
00289 }
00290 }
00291
00292
00293 return best;
00294 }
00295
00296 static gboolean
00297 movement_towards_edge (MetaDirection side, int increment)
00298 {
00299 switch (side)
00300 {
00301 case META_DIRECTION_LEFT:
00302 case META_DIRECTION_TOP:
00303 return increment < 0;
00304 case META_DIRECTION_RIGHT:
00305 case META_DIRECTION_BOTTOM:
00306 return increment > 0;
00307 default:
00308 g_assert_not_reached ();
00309 }
00310 }
00311
00312 static gboolean
00313 edge_resistance_timeout (gpointer data)
00314 {
00315 ResistanceDataForAnEdge *resistance_data = data;
00316
00317 resistance_data->timeout_over = TRUE;
00318 resistance_data->timeout_id = 0;
00319 (*resistance_data->timeout_func)(resistance_data->window);
00320
00321 return FALSE;
00322 }
00323
00324 static int
00325 apply_edge_resistance (MetaWindow *window,
00326 int old_pos,
00327 int new_pos,
00328 const MetaRectangle *old_rect,
00329 const MetaRectangle *new_rect,
00330 GArray *edges,
00331 ResistanceDataForAnEdge *resistance_data,
00332 GSourceFunc timeout_func,
00333 gboolean xdir,
00334 gboolean keyboard_op)
00335 {
00336 int i, begin, end;
00337 int last_edge;
00338 gboolean increasing = new_pos > old_pos;
00339 int increment = increasing ? 1 : -1;
00340
00341 const int PIXEL_DISTANCE_THRESHOLD_TOWARDS_WINDOW = 16;
00342 const int PIXEL_DISTANCE_THRESHOLD_AWAYFROM_WINDOW = 0;
00343 const int PIXEL_DISTANCE_THRESHOLD_TOWARDS_XINERAMA = 32;
00344 const int PIXEL_DISTANCE_THRESHOLD_AWAYFROM_XINERAMA = 0;
00345 const int PIXEL_DISTANCE_THRESHOLD_TOWARDS_SCREEN = 32;
00346 const int PIXEL_DISTANCE_THRESHOLD_AWAYFROM_SCREEN = 0;
00347 const int TIMEOUT_RESISTANCE_LENGTH_MS_WINDOW = 0;
00348 const int TIMEOUT_RESISTANCE_LENGTH_MS_XINERAMA = 0;
00349 const int TIMEOUT_RESISTANCE_LENGTH_MS_SCREEN = 0;
00350
00351
00352 if (old_pos == new_pos)
00353 return new_pos;
00354
00355
00356 if (resistance_data->timeout_setup &&
00357 ((resistance_data->timeout_edge_pos > old_pos &&
00358 resistance_data->timeout_edge_pos > new_pos) ||
00359 (resistance_data->timeout_edge_pos < old_pos &&
00360 resistance_data->timeout_edge_pos < new_pos)))
00361 {
00362 resistance_data->timeout_setup = FALSE;
00363 if (resistance_data->timeout_id != 0)
00364 {
00365 g_source_remove (resistance_data->timeout_id);
00366 resistance_data->timeout_id = 0;
00367 }
00368 }
00369
00370
00371 begin = find_index_of_edge_near_position (edges, old_pos, increasing, xdir);
00372 end = find_index_of_edge_near_position (edges, new_pos, !increasing, xdir);
00373
00374
00375
00376
00377 last_edge = edges->len - 1;
00378 begin = CLAMP (begin, 0, last_edge);
00379 end = CLAMP (end, 0, last_edge);
00380
00381
00382 i = begin;
00383 while ((increasing && i <= end) ||
00384 (!increasing && i >= end))
00385 {
00386 gboolean edges_align;
00387 MetaEdge *edge = g_array_index (edges, MetaEdge*, i);
00388 int compare = xdir ? edge->rect.x : edge->rect.y;
00389
00390
00391 edges_align = meta_rectangle_edge_aligns (new_rect, edge) ||
00392 meta_rectangle_edge_aligns (old_rect, edge);
00393
00394
00395 if (!edges_align)
00396 {
00397
00398 i += increment;
00399 continue;
00400 }
00401
00402
00403 if (keyboard_op)
00404 {
00405 if ((old_pos < compare && compare < new_pos) ||
00406 (old_pos > compare && compare > new_pos))
00407 return compare;
00408 }
00409 else
00410 {
00411 int threshold;
00412
00413
00414
00415
00416
00417 if (movement_towards_edge (edge->side_type, increment))
00418 {
00419
00420 int timeout_length_ms = 0;
00421 switch (edge->edge_type)
00422 {
00423 case META_EDGE_WINDOW:
00424 timeout_length_ms = TIMEOUT_RESISTANCE_LENGTH_MS_WINDOW;
00425 break;
00426 case META_EDGE_XINERAMA:
00427 timeout_length_ms = TIMEOUT_RESISTANCE_LENGTH_MS_XINERAMA;
00428 break;
00429 case META_EDGE_SCREEN:
00430 timeout_length_ms = TIMEOUT_RESISTANCE_LENGTH_MS_SCREEN;
00431 break;
00432 }
00433
00434 if (!resistance_data->timeout_setup &&
00435 timeout_length_ms != 0)
00436 {
00437 resistance_data->timeout_id =
00438 g_timeout_add (timeout_length_ms,
00439 edge_resistance_timeout,
00440 resistance_data);
00441 resistance_data->timeout_setup = TRUE;
00442 resistance_data->timeout_edge_pos = compare;
00443 resistance_data->timeout_over = FALSE;
00444 resistance_data->timeout_func = timeout_func;
00445 resistance_data->window = window;
00446 }
00447 if (!resistance_data->timeout_over &&
00448 timeout_length_ms != 0)
00449 return compare;
00450 }
00451
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461
00462 threshold = 0;
00463 switch (edge->edge_type)
00464 {
00465 case META_EDGE_WINDOW:
00466 if (movement_towards_edge (edge->side_type, increment))
00467 threshold = PIXEL_DISTANCE_THRESHOLD_TOWARDS_WINDOW;
00468 else
00469 threshold = PIXEL_DISTANCE_THRESHOLD_AWAYFROM_WINDOW;
00470 break;
00471 case META_EDGE_XINERAMA:
00472 if (movement_towards_edge (edge->side_type, increment))
00473 threshold = PIXEL_DISTANCE_THRESHOLD_TOWARDS_XINERAMA;
00474 else
00475 threshold = PIXEL_DISTANCE_THRESHOLD_AWAYFROM_XINERAMA;
00476 break;
00477 case META_EDGE_SCREEN:
00478 if (movement_towards_edge (edge->side_type, increment))
00479 threshold = PIXEL_DISTANCE_THRESHOLD_TOWARDS_SCREEN;
00480 else
00481 threshold = PIXEL_DISTANCE_THRESHOLD_AWAYFROM_SCREEN;
00482 break;
00483 }
00484
00485 if (ABS (compare - new_pos) < threshold)
00486 return compare;
00487 }
00488
00489
00490 i += increment;
00491 }
00492
00493 return new_pos;
00494 }
00495
00496 static int
00497 apply_edge_snapping (int old_pos,
00498 int new_pos,
00499 const MetaRectangle *new_rect,
00500 GArray *edges,
00501 gboolean xdir,
00502 gboolean keyboard_op)
00503 {
00504 int snap_to;
00505
00506 if (old_pos == new_pos)
00507 return new_pos;
00508
00509 snap_to = find_nearest_position (edges,
00510 new_pos,
00511 old_pos,
00512 new_rect,
00513 xdir,
00514 keyboard_op);
00515
00516
00517
00518
00519
00520
00521 if (!keyboard_op &&
00522 ABS (snap_to - old_pos) >= 8 &&
00523 ABS (new_pos - old_pos) < 8)
00524 return old_pos;
00525 else
00526
00527 return snap_to;
00528 }
00529
00530
00531
00532
00533
00534
00535
00536
00537
00538 static gboolean
00539 apply_edge_resistance_to_each_side (MetaDisplay *display,
00540 MetaWindow *window,
00541 const MetaRectangle *old_outer,
00542 MetaRectangle *new_outer,
00543 GSourceFunc timeout_func,
00544 gboolean auto_snap,
00545 gboolean keyboard_op,
00546 gboolean is_resize)
00547 {
00548 MetaEdgeResistanceData *edge_data;
00549 MetaRectangle modified_rect;
00550 gboolean modified;
00551 int new_left, new_right, new_top, new_bottom;
00552
00553 g_assert (display->grab_edge_resistance_data != NULL);
00554 edge_data = display->grab_edge_resistance_data;
00555
00556 if (auto_snap)
00557 {
00558
00559
00560
00561
00562
00563 new_left = apply_edge_snapping (BOX_LEFT (*old_outer),
00564 BOX_LEFT (*new_outer),
00565 new_outer,
00566 edge_data->left_edges,
00567 TRUE,
00568 keyboard_op);
00569
00570 new_right = apply_edge_snapping (BOX_RIGHT (*old_outer),
00571 BOX_RIGHT (*new_outer),
00572 new_outer,
00573 edge_data->right_edges,
00574 TRUE,
00575 keyboard_op);
00576
00577 new_top = apply_edge_snapping (BOX_TOP (*old_outer),
00578 BOX_TOP (*new_outer),
00579 new_outer,
00580 edge_data->top_edges,
00581 FALSE,
00582 keyboard_op);
00583
00584 new_bottom = apply_edge_snapping (BOX_BOTTOM (*old_outer),
00585 BOX_BOTTOM (*new_outer),
00586 new_outer,
00587 edge_data->bottom_edges,
00588 FALSE,
00589 keyboard_op);
00590 }
00591 else
00592 {
00593
00594
00595
00596
00597 if (!is_resize || window->size_hints.width_inc == 1)
00598 {
00599
00600 new_left = apply_edge_resistance (window,
00601 BOX_LEFT (*old_outer),
00602 BOX_LEFT (*new_outer),
00603 old_outer,
00604 new_outer,
00605 edge_data->left_edges,
00606 &edge_data->left_data,
00607 timeout_func,
00608 TRUE,
00609 keyboard_op);
00610 new_right = apply_edge_resistance (window,
00611 BOX_RIGHT (*old_outer),
00612 BOX_RIGHT (*new_outer),
00613 old_outer,
00614 new_outer,
00615 edge_data->right_edges,
00616 &edge_data->right_data,
00617 timeout_func,
00618 TRUE,
00619 keyboard_op);
00620 }
00621 else
00622 {
00623 new_left = new_outer->x;
00624 new_right = new_outer->x + new_outer->width;
00625 }
00626
00627 if (!is_resize || window->size_hints.height_inc == 1)
00628 {
00629 new_top = apply_edge_resistance (window,
00630 BOX_TOP (*old_outer),
00631 BOX_TOP (*new_outer),
00632 old_outer,
00633 new_outer,
00634 edge_data->top_edges,
00635 &edge_data->top_data,
00636 timeout_func,
00637 FALSE,
00638 keyboard_op);
00639 new_bottom = apply_edge_resistance (window,
00640 BOX_BOTTOM (*old_outer),
00641 BOX_BOTTOM (*new_outer),
00642 old_outer,
00643 new_outer,
00644 edge_data->bottom_edges,
00645 &edge_data->bottom_data,
00646 timeout_func,
00647 FALSE,
00648 keyboard_op);
00649 }
00650 else
00651 {
00652 new_top = new_outer->y;
00653 new_bottom = new_outer->y + new_outer->height;
00654 }
00655 }
00656
00657
00658 modified_rect = meta_rect (new_left,
00659 new_top,
00660 new_right - new_left,
00661 new_bottom - new_top);
00662 modified = !meta_rectangle_equal (new_outer, &modified_rect);
00663 *new_outer = modified_rect;
00664 return modified;
00665 }
00666
00667 void
00668 meta_display_cleanup_edges (MetaDisplay *display)
00669 {
00670 guint i,j;
00671 MetaEdgeResistanceData *edge_data = display->grab_edge_resistance_data;
00672 GHashTable *edges_to_be_freed;
00673
00674 g_assert (edge_data != NULL);
00675
00676
00677 edges_to_be_freed = g_hash_table_new_full (g_direct_hash, g_direct_equal,
00678 g_free, NULL);
00679 for (i = 0; i < 4; i++)
00680 {
00681 GArray *tmp = NULL;
00682 MetaDirection dir;
00683 switch (i)
00684 {
00685 case 0:
00686 tmp = edge_data->left_edges;
00687 dir = META_DIRECTION_LEFT;
00688 break;
00689 case 1:
00690 tmp = edge_data->right_edges;
00691 dir = META_DIRECTION_RIGHT;
00692 break;
00693 case 2:
00694 tmp = edge_data->top_edges;
00695 dir = META_DIRECTION_TOP;
00696 break;
00697 case 3:
00698 tmp = edge_data->bottom_edges;
00699 dir = META_DIRECTION_BOTTOM;
00700 break;
00701 default:
00702 g_assert_not_reached ();
00703 }
00704
00705 for (j = 0; j < tmp->len; j++)
00706 {
00707 MetaEdge *edge = g_array_index (tmp, MetaEdge*, j);
00708 if (edge->edge_type == META_EDGE_WINDOW &&
00709 edge->side_type == dir)
00710 {
00711
00712
00713
00714
00715
00716 g_hash_table_insert (edges_to_be_freed, edge, edge);
00717 }
00718 }
00719 }
00720
00721
00722 g_hash_table_destroy (edges_to_be_freed);
00723
00724
00725 g_array_free (edge_data->left_edges, TRUE);
00726 g_array_free (edge_data->right_edges, TRUE);
00727 g_array_free (edge_data->top_edges, TRUE);
00728 g_array_free (edge_data->bottom_edges, TRUE);
00729 edge_data->left_edges = NULL;
00730 edge_data->right_edges = NULL;
00731 edge_data->top_edges = NULL;
00732 edge_data->bottom_edges = NULL;
00733
00734
00735 if (edge_data->left_data.timeout_setup &&
00736 edge_data->left_data.timeout_id != 0)
00737 g_source_remove (edge_data->left_data.timeout_id);
00738 if (edge_data->right_data.timeout_setup &&
00739 edge_data->right_data.timeout_id != 0)
00740 g_source_remove (edge_data->right_data.timeout_id);
00741 if (edge_data->top_data.timeout_setup &&
00742 edge_data->top_data.timeout_id != 0)
00743 g_source_remove (edge_data->top_data.timeout_id);
00744 if (edge_data->bottom_data.timeout_setup &&
00745 edge_data->bottom_data.timeout_id != 0)
00746 g_source_remove (edge_data->bottom_data.timeout_id);
00747
00748 g_free (display->grab_edge_resistance_data);
00749 display->grab_edge_resistance_data = NULL;
00750 }
00751
00752 static int
00753 stupid_sort_requiring_extra_pointer_dereference (gconstpointer a,
00754 gconstpointer b)
00755 {
00756 const MetaEdge * const *a_edge = a;
00757 const MetaEdge * const *b_edge = b;
00758 return meta_rectangle_edge_cmp_ignore_type (*a_edge, *b_edge);
00759 }
00760
00761 static void
00762 cache_edges (MetaDisplay *display,
00763 GList *window_edges,
00764 GList *xinerama_edges,
00765 GList *screen_edges)
00766 {
00767 MetaEdgeResistanceData *edge_data;
00768 GList *tmp;
00769 int num_left, num_right, num_top, num_bottom;
00770 int i;
00771
00772
00773
00774
00775 #ifdef WITH_VERBOSE_MODE
00776 if (meta_is_verbose())
00777 {
00778 int max_edges = MAX (MAX( g_list_length (window_edges),
00779 g_list_length (xinerama_edges)),
00780 g_list_length (screen_edges));
00781 char big_buffer[(EDGE_LENGTH+2)*max_edges];
00782
00783 meta_rectangle_edge_list_to_string (window_edges, ", ", big_buffer);
00784 meta_topic (META_DEBUG_EDGE_RESISTANCE,
00785 "Window edges for resistance : %s\n", big_buffer);
00786
00787 meta_rectangle_edge_list_to_string (xinerama_edges, ", ", big_buffer);
00788 meta_topic (META_DEBUG_EDGE_RESISTANCE,
00789 "Xinerama edges for resistance: %s\n", big_buffer);
00790
00791 meta_rectangle_edge_list_to_string (screen_edges, ", ", big_buffer);
00792 meta_topic (META_DEBUG_EDGE_RESISTANCE,
00793 "Screen edges for resistance : %s\n", big_buffer);
00794 }
00795 #endif
00796
00797
00798
00799
00800 num_left = num_right = num_top = num_bottom = 0;
00801 for (i = 0; i < 3; i++)
00802 {
00803 tmp = NULL;
00804 switch (i)
00805 {
00806 case 0:
00807 tmp = window_edges;
00808 break;
00809 case 1:
00810 tmp = xinerama_edges;
00811 break;
00812 case 2:
00813 tmp = screen_edges;
00814 break;
00815 default:
00816 g_assert_not_reached ();
00817 }
00818
00819 while (tmp)
00820 {
00821 MetaEdge *edge = tmp->data;
00822 switch (edge->side_type)
00823 {
00824 case META_DIRECTION_LEFT:
00825 num_left++;
00826 break;
00827 case META_DIRECTION_RIGHT:
00828 num_right++;
00829 break;
00830 case META_DIRECTION_TOP:
00831 num_top++;
00832 break;
00833 case META_DIRECTION_BOTTOM:
00834 num_bottom++;
00835 break;
00836 default:
00837 g_assert_not_reached ();
00838 }
00839 tmp = tmp->next;
00840 }
00841 }
00842
00843
00844
00845
00846 g_assert (display->grab_edge_resistance_data == NULL);
00847 display->grab_edge_resistance_data = g_new (MetaEdgeResistanceData, 1);
00848 edge_data = display->grab_edge_resistance_data;
00849 edge_data->left_edges = g_array_sized_new (FALSE,
00850 FALSE,
00851 sizeof(MetaEdge*),
00852 num_left + num_right);
00853 edge_data->right_edges = g_array_sized_new (FALSE,
00854 FALSE,
00855 sizeof(MetaEdge*),
00856 num_left + num_right);
00857 edge_data->top_edges = g_array_sized_new (FALSE,
00858 FALSE,
00859 sizeof(MetaEdge*),
00860 num_top + num_bottom);
00861 edge_data->bottom_edges = g_array_sized_new (FALSE,
00862 FALSE,
00863 sizeof(MetaEdge*),
00864 num_top + num_bottom);
00865
00866
00867
00868
00869 for (i = 0; i < 3; i++)
00870 {
00871 tmp = NULL;
00872 switch (i)
00873 {
00874 case 0:
00875 tmp = window_edges;
00876 break;
00877 case 1:
00878 tmp = xinerama_edges;
00879 break;
00880 case 2:
00881 tmp = screen_edges;
00882 break;
00883 default:
00884 g_assert_not_reached ();
00885 }
00886
00887 while (tmp)
00888 {
00889 MetaEdge *edge = tmp->data;
00890 switch (edge->side_type)
00891 {
00892 case META_DIRECTION_LEFT:
00893 case META_DIRECTION_RIGHT:
00894 g_array_append_val (edge_data->left_edges, edge);
00895 g_array_append_val (edge_data->right_edges, edge);
00896 break;
00897 case META_DIRECTION_TOP:
00898 case META_DIRECTION_BOTTOM:
00899 g_array_append_val (edge_data->top_edges, edge);
00900 g_array_append_val (edge_data->bottom_edges, edge);
00901 break;
00902 default:
00903 g_assert_not_reached ();
00904 }
00905 tmp = tmp->next;
00906 }
00907 }
00908
00909
00910
00911
00912
00913
00914
00915 g_array_sort (display->grab_edge_resistance_data->left_edges,
00916 stupid_sort_requiring_extra_pointer_dereference);
00917 g_array_sort (display->grab_edge_resistance_data->right_edges,
00918 stupid_sort_requiring_extra_pointer_dereference);
00919 g_array_sort (display->grab_edge_resistance_data->top_edges,
00920 stupid_sort_requiring_extra_pointer_dereference);
00921 g_array_sort (display->grab_edge_resistance_data->bottom_edges,
00922 stupid_sort_requiring_extra_pointer_dereference);
00923 }
00924
00925 static void
00926 initialize_grab_edge_resistance_data (MetaDisplay *display)
00927 {
00928 MetaEdgeResistanceData *edge_data = display->grab_edge_resistance_data;
00929
00930 edge_data->left_data.timeout_setup = FALSE;
00931 edge_data->right_data.timeout_setup = FALSE;
00932 edge_data->top_data.timeout_setup = FALSE;
00933 edge_data->bottom_data.timeout_setup = FALSE;
00934
00935 edge_data->left_data.keyboard_buildup = 0;
00936 edge_data->right_data.keyboard_buildup = 0;
00937 edge_data->top_data.keyboard_buildup = 0;
00938 edge_data->bottom_data.keyboard_buildup = 0;
00939 }
00940
00941 void
00942 meta_display_compute_resistance_and_snapping_edges (MetaDisplay *display)
00943 {
00944 GList *stacked_windows;
00945 GList *cur_window_iter;
00946 GList *edges;
00947
00948 int stack_position;
00949 GSList *obscuring_windows, *window_stacking;
00950
00951
00952
00953 GSList *rem_windows, *rem_win_stacking;
00954
00955
00956
00957
00958 stacked_windows =
00959 meta_stack_list_windows (display->grab_screen->stack,
00960 display->grab_screen->active_workspace);
00961
00962
00963
00964
00965
00966
00967
00968 obscuring_windows = window_stacking = NULL;
00969 cur_window_iter = stacked_windows;
00970 stack_position = 0;
00971 while (cur_window_iter != NULL)
00972 {
00973 MetaWindow *cur_window = cur_window_iter->data;
00974 if (WINDOW_EDGES_RELEVANT (cur_window, display))
00975 {
00976 MetaRectangle *new_rect;
00977 new_rect = g_new (MetaRectangle, 1);
00978 meta_window_get_outer_rect (cur_window, new_rect);
00979 obscuring_windows = g_slist_prepend (obscuring_windows, new_rect);
00980 window_stacking =
00981 g_slist_prepend (window_stacking, GINT_TO_POINTER (stack_position));
00982 }
00983
00984 stack_position++;
00985 cur_window_iter = cur_window_iter->next;
00986 }
00987
00988 rem_windows = g_slist_reverse (obscuring_windows);
00989 rem_win_stacking = g_slist_reverse (window_stacking);
00990
00991
00992
00993
00994
00995
00996 edges = NULL;
00997 stack_position = 0;
00998 cur_window_iter = stacked_windows;
00999 while (cur_window_iter != NULL)
01000 {
01001 MetaRectangle cur_rect;
01002 MetaWindow *cur_window = cur_window_iter->data;
01003 meta_window_get_outer_rect (cur_window, &cur_rect);
01004
01005
01006
01007
01008
01009 if (WINDOW_EDGES_RELEVANT (cur_window, display) &&
01010 cur_window->type != META_WINDOW_DOCK)
01011 {
01012 GList *new_edges;
01013 MetaEdge *new_edge;
01014 MetaRectangle reduced;
01015
01016
01017
01018
01019
01020 meta_rectangle_intersect (&cur_rect,
01021 &display->grab_screen->rect,
01022 &reduced);
01023
01024 new_edges = NULL;
01025
01026
01027
01028
01029 new_edge = g_new (MetaEdge, 1);
01030 new_edge->rect = reduced;
01031 new_edge->rect.width = 0;
01032 new_edge->side_type = META_DIRECTION_RIGHT;
01033 new_edge->edge_type = META_EDGE_WINDOW;
01034 new_edges = g_list_prepend (new_edges, new_edge);
01035
01036
01037
01038
01039 new_edge = g_new (MetaEdge, 1);
01040 new_edge->rect = reduced;
01041 new_edge->rect.x += new_edge->rect.width;
01042 new_edge->rect.width = 0;
01043 new_edge->side_type = META_DIRECTION_LEFT;
01044 new_edge->edge_type = META_EDGE_WINDOW;
01045 new_edges = g_list_prepend (new_edges, new_edge);
01046
01047
01048
01049
01050 new_edge = g_new (MetaEdge, 1);
01051 new_edge->rect = reduced;
01052 new_edge->rect.height = 0;
01053 new_edge->side_type = META_DIRECTION_BOTTOM;
01054 new_edge->edge_type = META_EDGE_WINDOW;
01055 new_edges = g_list_prepend (new_edges, new_edge);
01056
01057
01058
01059
01060 new_edge = g_new (MetaEdge, 1);
01061 new_edge->rect = reduced;
01062 new_edge->rect.y += new_edge->rect.height;
01063 new_edge->rect.height = 0;
01064 new_edge->side_type = META_DIRECTION_TOP;
01065 new_edge->edge_type = META_EDGE_WINDOW;
01066 new_edges = g_list_prepend (new_edges, new_edge);
01067
01068
01069
01070
01071 while (rem_win_stacking &&
01072 stack_position >= GPOINTER_TO_INT (rem_win_stacking->data))
01073 {
01074 rem_windows = rem_windows->next;
01075 rem_win_stacking = rem_win_stacking->next;
01076 }
01077
01078
01079 new_edges =
01080 meta_rectangle_remove_intersections_with_boxes_from_edges (
01081 new_edges,
01082 rem_windows);
01083
01084
01085 edges = g_list_concat (new_edges, edges);
01086 }
01087
01088 stack_position++;
01089 cur_window_iter = cur_window_iter->next;
01090 }
01091
01092
01093
01094
01095 g_list_free (stacked_windows);
01096
01097 g_slist_free (window_stacking);
01098
01099
01100
01101 g_slist_foreach (obscuring_windows,
01102 (void (*)(gpointer,gpointer))&g_free,
01103 NULL);
01104 g_slist_free (obscuring_windows);
01105
01106
01107
01108
01109 edges = g_list_sort (edges, meta_rectangle_edge_cmp);
01110
01111
01112
01113
01114
01115
01116 cache_edges (display,
01117 edges,
01118 display->grab_screen->active_workspace->xinerama_edges,
01119 display->grab_screen->active_workspace->screen_edges);
01120 g_list_free (edges);
01121
01122
01123
01124
01125 initialize_grab_edge_resistance_data (display);
01126 }
01127
01128
01129
01130
01131 void
01132 meta_window_edge_resistance_for_move (MetaWindow *window,
01133 int old_x,
01134 int old_y,
01135 int *new_x,
01136 int *new_y,
01137 GSourceFunc timeout_func,
01138 gboolean snap,
01139 gboolean is_keyboard_op)
01140 {
01141 MetaRectangle old_outer, proposed_outer, new_outer;
01142 gboolean is_resize;
01143
01144 if (window == window->display->grab_window &&
01145 window->display->grab_wireframe_active)
01146 {
01147 meta_window_get_xor_rect (window,
01148 &window->display->grab_wireframe_rect,
01149 &old_outer);
01150 }
01151 else
01152 {
01153 meta_window_get_outer_rect (window, &old_outer);
01154 }
01155 proposed_outer = old_outer;
01156 proposed_outer.x += (*new_x - old_x);
01157 proposed_outer.y += (*new_y - old_y);
01158 new_outer = proposed_outer;
01159
01160 window->display->grab_last_user_action_was_snap = snap;
01161 is_resize = FALSE;
01162 if (apply_edge_resistance_to_each_side (window->display,
01163 window,
01164 &old_outer,
01165 &new_outer,
01166 timeout_func,
01167 snap,
01168 is_keyboard_op,
01169 is_resize))
01170 {
01171
01172
01173
01174
01175
01176
01177 MetaRectangle *reference;
01178 int left_change, right_change, smaller_x_change;
01179 int top_change, bottom_change, smaller_y_change;
01180
01181 if (snap && !is_keyboard_op)
01182 reference = &proposed_outer;
01183 else
01184 reference = &old_outer;
01185
01186 left_change = BOX_LEFT (new_outer) - BOX_LEFT (*reference);
01187 right_change = BOX_RIGHT (new_outer) - BOX_RIGHT (*reference);
01188 if ( snap && is_keyboard_op && left_change == 0)
01189 smaller_x_change = right_change;
01190 else if (snap && is_keyboard_op && right_change == 0)
01191 smaller_x_change = left_change;
01192 else if (ABS (left_change) < ABS (right_change))
01193 smaller_x_change = left_change;
01194 else
01195 smaller_x_change = right_change;
01196
01197 top_change = BOX_TOP (new_outer) - BOX_TOP (*reference);
01198 bottom_change = BOX_BOTTOM (new_outer) - BOX_BOTTOM (*reference);
01199 if ( snap && is_keyboard_op && top_change == 0)
01200 smaller_y_change = bottom_change;
01201 else if (snap && is_keyboard_op && bottom_change == 0)
01202 smaller_y_change = top_change;
01203 else if (ABS (top_change) < ABS (bottom_change))
01204 smaller_y_change = top_change;
01205 else
01206 smaller_y_change = bottom_change;
01207
01208 *new_x = old_x + smaller_x_change +
01209 (BOX_LEFT (*reference) - BOX_LEFT (old_outer));
01210 *new_y = old_y + smaller_y_change +
01211 (BOX_TOP (*reference) - BOX_TOP (old_outer));
01212
01213 meta_topic (META_DEBUG_EDGE_RESISTANCE,
01214 "outer x & y move-to coordinate changed from %d,%d to %d,%d\n",
01215 proposed_outer.x, proposed_outer.y,
01216 old_outer.x + (*new_x - old_x),
01217 old_outer.y + (*new_y - old_y));
01218 }
01219 }
01220
01221
01222
01223
01224 void
01225 meta_window_edge_resistance_for_resize (MetaWindow *window,
01226 int old_width,
01227 int old_height,
01228 int *new_width,
01229 int *new_height,
01230 int gravity,
01231 GSourceFunc timeout_func,
01232 gboolean snap,
01233 gboolean is_keyboard_op)
01234 {
01235 MetaRectangle old_outer, new_outer;
01236 int proposed_outer_width, proposed_outer_height;
01237 gboolean is_resize;
01238
01239 if (window == window->display->grab_window &&
01240 window->display->grab_wireframe_active)
01241 {
01242 meta_window_get_xor_rect (window,
01243 &window->display->grab_wireframe_rect,
01244 &old_outer);
01245 }
01246 else
01247 {
01248 meta_window_get_outer_rect (window, &old_outer);
01249 }
01250 proposed_outer_width = old_outer.width + (*new_width - old_width);
01251 proposed_outer_height = old_outer.height + (*new_height - old_height);
01252 meta_rectangle_resize_with_gravity (&old_outer,
01253 &new_outer,
01254 gravity,
01255 proposed_outer_width,
01256 proposed_outer_height);
01257
01258 window->display->grab_last_user_action_was_snap = snap;
01259 is_resize = TRUE;
01260 if (apply_edge_resistance_to_each_side (window->display,
01261 window,
01262 &old_outer,
01263 &new_outer,
01264 timeout_func,
01265 snap,
01266 is_keyboard_op,
01267 is_resize))
01268 {
01269 *new_width = old_width + (new_outer.width - old_outer.width);
01270 *new_height = old_height + (new_outer.height - old_outer.height);
01271
01272 meta_topic (META_DEBUG_EDGE_RESISTANCE,
01273 "outer width & height got changed from %d,%d to %d,%d\n",
01274 proposed_outer_width, proposed_outer_height,
01275 new_outer.width, new_outer.height);
01276 }
01277 }