diff options
author | Owen Taylor <otaylor@src.gnome.org> | 2001-03-29 21:17:45 +0000 |
---|---|---|
committer | Owen Taylor <otaylor@src.gnome.org> | 2001-03-29 21:17:45 +0000 |
commit | c61a8f282fd3d3fe32efbdb1b41791447a2602da (patch) | |
tree | d478e8dc1b9a2c82361e00e2b411150e115a1309 /gdk | |
parent | 1c537e58537317be98ea0b7596b93a3acf143457 (diff) | |
download | gtk+-c61a8f282fd3d3fe32efbdb1b41791447a2602da.tar.gz |
*** empty log message ***
Diffstat (limited to 'gdk')
-rw-r--r-- | gdk/gdkwindow.c | 128 | ||||
-rw-r--r-- | gdk/gdkwindow.h | 32 | ||||
-rw-r--r-- | gdk/x11/gdkevents-x11.c | 19 | ||||
-rw-r--r-- | gdk/x11/gdkprivate-x11.h | 5 | ||||
-rw-r--r-- | gdk/x11/gdkwindow-x11.c | 491 |
5 files changed, 670 insertions, 5 deletions
diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c index a9fd49c8b6..f64fab21a6 100644 --- a/gdk/gdkwindow.c +++ b/gdk/gdkwindow.c @@ -2151,3 +2151,131 @@ gdk_window_set_debug_updates (gboolean setting) debug_updates = setting; } +/** + * gdk_window_constrain_size: + * @geometry: a #GdkGeometry structure + * @flags: a mask indicating what portions of @geometry are set + * @width: desired width of window + * @height: desired height of the window + * @new_width: location to store resulting width + * @new_height: location to store resulting height + * + * Constrains a desired width and height according to a + * set of geometry hints (such as minimum and maximum size). + */ +void +gdk_window_constrain_size (GdkGeometry *geometry, + guint flags, + gint width, + gint height, + gint *new_width, + gint *new_height) +{ + /* This routine is partially borrowed from fvwm. + * + * Copyright 1993, Robert Nation + * You may use this code for any purpose, as long as the original + * copyright remains in the source code and all documentation + * + * which in turn borrows parts of the algorithm from uwm + */ + gint min_width = 0; + gint min_height = 0; + gint base_width = 0; + gint base_height = 0; + gint xinc = 1; + gint yinc = 1; + gint max_width = G_MAXINT; + gint max_height = G_MAXINT; + +#define FLOOR(value, base) ( ((gint) ((value) / (base))) * (base) ) + + if ((flags & GDK_HINT_BASE_SIZE) && (flags & GDK_HINT_MIN_SIZE)) + { + base_width = geometry->base_width; + base_height = geometry->base_height; + min_width = geometry->min_width; + min_height = geometry->min_height; + } + else if (flags & GDK_HINT_BASE_SIZE) + { + base_width = geometry->base_width; + base_height = geometry->base_height; + min_width = geometry->base_width; + min_height = geometry->base_height; + } + else if (flags & GDK_HINT_MIN_SIZE) + { + base_width = geometry->min_width; + base_height = geometry->min_height; + min_width = geometry->min_width; + min_height = geometry->min_height; + } + + if (flags & GDK_HINT_MAX_SIZE) + { + max_width = geometry->max_width ; + max_height = geometry->max_height; + } + + if (flags & GDK_HINT_RESIZE_INC) + { + xinc = MAX (xinc, geometry->width_inc); + yinc = MAX (yinc, geometry->height_inc); + } + + /* clamp width and height to min and max values + */ + width = CLAMP (width, min_width, max_width); + height = CLAMP (height, min_height, max_height); + + /* shrink to base + N * inc + */ + width = base_width + FLOOR (width - base_width, xinc); + height = base_height + FLOOR (height - base_height, yinc); + + /* constrain aspect ratio, according to: + * + * width + * min_aspect <= -------- <= max_aspect + * height + */ + + if (flags & GDK_HINT_ASPECT && + geometry->min_aspect > 0 && + geometry->max_aspect > 0) + { + gint delta; + + if (geometry->min_aspect * height > width) + { + delta = FLOOR (height - width * geometry->min_aspect, yinc); + if (height - delta >= min_height) + height -= delta; + else + { + delta = FLOOR (height * geometry->min_aspect - width, xinc); + if (width + delta <= max_width) + width += delta; + } + } + + if (geometry->max_aspect * height < width) + { + delta = FLOOR (width - height * geometry->max_aspect, xinc); + if (width - delta >= min_width) + width -= delta; + else + { + delta = FLOOR (width / geometry->max_aspect - height, yinc); + if (height + delta <= max_height) + height += delta; + } + } + } + +#undef FLOOR + + *new_width = width; + *new_height = height; +} diff --git a/gdk/gdkwindow.h b/gdk/gdkwindow.h index 4d0e22b54c..dbb91da22f 100644 --- a/gdk/gdkwindow.h +++ b/gdk/gdkwindow.h @@ -150,6 +150,19 @@ typedef enum GDK_GRAVITY_STATIC } GdkGravity; + +typedef enum +{ + GDK_WINDOW_EDGE_NORTH_WEST, + GDK_WINDOW_EDGE_NORTH, + GDK_WINDOW_EDGE_NORTH_EAST, + GDK_WINDOW_EDGE_WEST, + GDK_WINDOW_EDGE_EAST, + GDK_WINDOW_EDGE_SOUTH_WEST, + GDK_WINDOW_EDGE_SOUTH, + GDK_WINDOW_EDGE_SOUTH_EAST +} GdkWindowEdge; + struct _GdkWindowAttr { gchar *title; @@ -443,6 +456,18 @@ void gdk_window_unmaximize (GdkWindow *window); void gdk_window_register_dnd (GdkWindow *window); +void gdk_window_begin_resize_drag (GdkWindow *window, + GdkWindowEdge edge, + gint button, + gint root_x, + gint root_y, + guint32 timestamp); +void gdk_window_begin_move_drag (GdkWindow *window, + gint button, + gint root_x, + gint root_y, + guint32 timestamp); + /* Interface for dirty-region queueing */ void gdk_window_invalidate_rect (GdkWindow *window, GdkRectangle *rect, @@ -462,6 +487,13 @@ void gdk_window_process_updates (GdkWindow *window, /* Enable/disable flicker, so you can tell if your code is inefficient. */ void gdk_window_set_debug_updates (gboolean setting); +void gdk_window_constrain_size (GdkGeometry *geometry, + guint flags, + gint width, + gint height, + gint *new_width, + gint *new_height); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/gdk/x11/gdkevents-x11.c b/gdk/x11/gdkevents-x11.c index 90cbb60223..ccb50711a4 100644 --- a/gdk/x11/gdkevents-x11.c +++ b/gdk/x11/gdkevents-x11.c @@ -428,6 +428,15 @@ gdk_event_translate (GdkEvent *event, if (window != NULL) gdk_window_ref (window); + if (_gdk_moveresize_window && + (xevent->xany.type == MotionNotify || + xevent->xany.type == ButtonRelease)) + { + _gdk_moveresize_handle_event (xevent); + gdk_window_unref (window); + return FALSE; + } + if (wmspec_check_window != None && xevent->xany.window == wmspec_check_window) { @@ -1130,8 +1139,14 @@ gdk_event_translate (GdkEvent *event, window_private->y = event->configure.y; GDK_WINDOW_IMPL_X11 (window_private->impl)->width = xevent->xconfigure.width; GDK_WINDOW_IMPL_X11 (window_private->impl)->height = xevent->xconfigure.height; - if (window_private->resize_count > 1) - window_private->resize_count -= 1; + if (window_private->resize_count >= 1) + { + window_private->resize_count -= 1; + + if (window_private->resize_count == 0 && + window == _gdk_moveresize_window) + _gdk_moveresize_configure_done (); + } } break; diff --git a/gdk/x11/gdkprivate-x11.h b/gdk/x11/gdkprivate-x11.h index f864a6b0a5..7652f3d73f 100644 --- a/gdk/x11/gdkprivate-x11.h +++ b/gdk/x11/gdkprivate-x11.h @@ -92,6 +92,9 @@ void _gdk_region_get_xrectangles (GdkRegion *region, XRectangle **rects, gint *n_rects); +void _gdk_moveresize_handle_event (XEvent *event); +void _gdk_moveresize_configure_done (void); + extern GdkDrawableClass _gdk_x11_drawable_class; extern gboolean gdk_use_xshm; extern Atom gdk_wm_delete_window; @@ -120,4 +123,6 @@ extern gboolean _gdk_have_xkb_autorepeat; */ extern gboolean _gdk_have_xkb_autorepeat; +extern GdkWindow *_gdk_moveresize_window; + #endif /* __GDK_PRIVATE_X11_H__ */ diff --git a/gdk/x11/gdkwindow-x11.c b/gdk/x11/gdkwindow-x11.c index 9988235ae8..b53146f424 100644 --- a/gdk/x11/gdkwindow-x11.c +++ b/gdk/x11/gdkwindow-x11.c @@ -931,10 +931,14 @@ gdk_window_resize (GdkWindow *window, width, height); else { + GdkWindowImplX11 *impl = GDK_WINDOW_IMPL_X11 (private->impl); + + if (width != impl->width || height != impl->height) + private->resize_count += 1; + XResizeWindow (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window), width, height); - private->resize_count += 1; } } } @@ -964,10 +968,14 @@ gdk_window_move_resize (GdkWindow *window, _gdk_window_move_resize_child (window, x, y, width, height); else { + GdkWindowImplX11 *impl = GDK_WINDOW_IMPL_X11 (private->impl); + + if (width != impl->width || height != impl->height) + private->resize_count += 1; + XMoveResizeWindow (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window), x, y, width, height); - private->resize_count += 1; } } } @@ -1344,7 +1352,7 @@ gdk_window_set_geometry_hints (GdkWindow *window, if (geom_mask & GDK_HINT_WIN_GRAVITY) { size_hints.flags |= PWinGravity; - size_hints.width_inc = geometry->win_gravity; + size_hints.win_gravity = geometry->win_gravity; } /* FIXME: Would it be better to delete this property of @@ -1355,6 +1363,65 @@ gdk_window_set_geometry_hints (GdkWindow *window, &size_hints); } +static void +gdk_window_get_geometry_hints (GdkWindow *window, + GdkGeometry *geometry, + GdkWindowHints *geom_mask) +{ + XSizeHints size_hints; + glong junk_size_mask = 0; + + g_return_if_fail (GDK_IS_WINDOW (window)); + g_return_if_fail (geometry != NULL); + g_return_if_fail (geom_mask != NULL); + + *geom_mask = 0; + + if (GDK_WINDOW_DESTROYED (window)) + return; + + if (!XGetWMNormalHints (GDK_WINDOW_XDISPLAY (window), + GDK_WINDOW_XID (window), + &size_hints, + &junk_size_mask)) + return; + + if (size_hints.flags & PMinSize) + { + *geom_mask |= GDK_HINT_MIN_SIZE; + geometry->min_width = size_hints.min_width; + geometry->min_height = size_hints.min_height; + } + + if (size_hints.flags & PMaxSize) + { + *geom_mask |= GDK_HINT_MAX_SIZE; + geometry->max_width = MAX (size_hints.max_width, 1); + geometry->max_height = MAX (size_hints.max_height, 1); + } + + if (size_hints.flags & PResizeInc) + { + *geom_mask |= GDK_HINT_RESIZE_INC; + geometry->width_inc = size_hints.width_inc; + geometry->height_inc = size_hints.height_inc; + } + + if (size_hints.flags & PAspect) + { + *geom_mask |= GDK_HINT_ASPECT; + + geometry->min_aspect = (gdouble) size_hints.min_aspect.x / (gdouble) size_hints.min_aspect.y; + geometry->max_aspect = (gdouble) size_hints.max_aspect.x / (gdouble) size_hints.max_aspect.y; + } + + if (size_hints.flags & PWinGravity) + { + *geom_mask |= GDK_HINT_WIN_GRAVITY; + geometry->win_gravity = size_hints.win_gravity; + } +} + static gboolean utf8_is_latin1 (const gchar *str) { @@ -3236,3 +3303,421 @@ gdk_window_xid_at_coords (gint x, return root; } +static void +wmspec_moveresize (GdkWindow *window, + gint direction, + gint root_x, + gint root_y, + guint32 timestamp) +{ + XEvent xev; + + /* Release passive grab */ + gdk_pointer_ungrab (timestamp); + + xev.xclient.type = ClientMessage; + xev.xclient.serial = 0; + xev.xclient.send_event = True; + xev.xclient.display = gdk_display; + xev.xclient.window = GDK_WINDOW_XID (window); + xev.xclient.message_type = gdk_atom_intern ("_NET_WM_MOVERESIZE", FALSE); + xev.xclient.format = 32; + xev.xclient.data.l[0] = root_x; + xev.xclient.data.l[1] = root_y; + xev.xclient.data.l[2] = direction; + xev.xclient.data.l[3] = 0; + xev.xclient.data.l[4] = 0; + + XSendEvent (gdk_display, gdk_root_window, False, + SubstructureRedirectMask | SubstructureNotifyMask, + &xev); +} + +/* From the WM spec */ +#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0 +#define _NET_WM_MOVERESIZE_SIZE_TOP 1 +#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2 +#define _NET_WM_MOVERESIZE_SIZE_RIGHT 3 +#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4 +#define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5 +#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6 +#define _NET_WM_MOVERESIZE_SIZE_LEFT 7 +#define _NET_WM_MOVERESIZE_MOVE 8 + +static void +wmspec_resize_drag (GdkWindow *window, + GdkWindowEdge edge, + gint button, + gint root_x, + gint root_y, + guint32 timestamp) +{ + gint direction; + + /* Let the compiler turn a switch into a table, instead + * of doing the table manually, this way is easier to verify. + */ + switch (edge) + { + case GDK_WINDOW_EDGE_NORTH_WEST: + direction = _NET_WM_MOVERESIZE_SIZE_TOPLEFT; + break; + + case GDK_WINDOW_EDGE_NORTH: + direction = _NET_WM_MOVERESIZE_SIZE_TOP; + break; + + case GDK_WINDOW_EDGE_NORTH_EAST: + direction = _NET_WM_MOVERESIZE_SIZE_TOPRIGHT; + break; + + case GDK_WINDOW_EDGE_WEST: + direction = _NET_WM_MOVERESIZE_SIZE_LEFT; + break; + + case GDK_WINDOW_EDGE_EAST: + direction = _NET_WM_MOVERESIZE_SIZE_RIGHT; + break; + + case GDK_WINDOW_EDGE_SOUTH_WEST: + direction = _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT; + break; + + case GDK_WINDOW_EDGE_SOUTH: + direction = _NET_WM_MOVERESIZE_SIZE_BOTTOM; + break; + + case GDK_WINDOW_EDGE_SOUTH_EAST: + direction = _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT; + break; + + default: + g_warning ("gdk_window_begin_resize_drag: bad resize edge %d!", + edge); + return; + break; + } + + wmspec_moveresize (window, direction, root_x, root_y, timestamp); +} + +/* This is global for use in gdkevents-x11.c */ +GdkWindow *_gdk_moveresize_window; + +static GdkWindow *moveresize_emulation_window = NULL; +static gboolean is_resize = FALSE; +static GdkWindowEdge resize_edge; +static gint moveresize_button; +static gint moveresize_x; +static gint moveresize_y; +static gint moveresize_orig_x; +static gint moveresize_orig_y; +static gint moveresize_orig_width; +static gint moveresize_orig_height; +static GdkWindowHints moveresize_geom_mask = 0; +static GdkGeometry moveresize_geometry; +static Time moveresize_process_time; + +static XEvent *moveresize_pending_event; + +static void +update_pos (gint new_root_x, + gint new_root_y) +{ + gint dx, dy; + + dx = new_root_x - moveresize_x; + dy = new_root_y - moveresize_y; + + if (is_resize) + { + gint w, h; + + w = moveresize_orig_width; + h = moveresize_orig_height; + + switch (resize_edge) + { + case GDK_WINDOW_EDGE_SOUTH_EAST: + w += dx; + h += dy; + break; + + } + + w = MAX (w, 1); + h = MAX (h, 1); + + if (moveresize_geom_mask) + { + gdk_window_constrain_size (&moveresize_geometry, + moveresize_geom_mask, + w, h, + &w, &h); + } + + gdk_window_resize (_gdk_moveresize_window, w, h); + } + else + { + gint x, y; + + x = moveresize_orig_x + dx; + y = moveresize_orig_y + dy; + + gdk_window_move (_gdk_moveresize_window, x, y); + } +} + +static void +finish_drag (void) +{ + gdk_window_destroy (moveresize_emulation_window); + moveresize_emulation_window = NULL; + _gdk_moveresize_window = NULL; + + if (moveresize_pending_event) + { + g_free (moveresize_pending_event); + moveresize_pending_event = NULL; + } +} + +static int +lookahead_motion_predicate (Display *display, + XEvent *event, + XPointer arg) +{ + gboolean *seen_release = (gboolean *)arg; + + if (*seen_release) + return False; + + switch (event->xany.type) + { + case ButtonRelease: + *seen_release = TRUE; + break; + case MotionNotify: + moveresize_process_time = event->xmotion.time; + break; + default: + break; + } + + return False; +} + +static gboolean +moveresize_lookahead (XEvent *event) +{ + XEvent tmp_event; + gboolean seen_release = FALSE; + + if (moveresize_process_time) + { + if (event->xmotion.time == moveresize_process_time) + { + moveresize_process_time = 0; + return TRUE; + } + else + return FALSE; + } + + XCheckIfEvent (gdk_display, &tmp_event, + lookahead_motion_predicate, (XPointer)&seen_release); + + return moveresize_process_time == 0; +} + +void +_gdk_moveresize_handle_event (XEvent *event) +{ + guint button_mask = 0; + GdkWindowObject *window_private = (GdkWindowObject *) _gdk_moveresize_window; + + button_mask = GDK_BUTTON1_MASK << (moveresize_button - 1); + + switch (event->xany.type) + { + case MotionNotify: + if (window_private->resize_count > 0) + { + if (moveresize_pending_event) + *moveresize_pending_event = *event; + else + moveresize_pending_event = g_memdup (event, sizeof (XEvent)); + + break; + } + if (!moveresize_lookahead (event)) + break; + + update_pos (event->xmotion.x_root, + event->xmotion.y_root); + + /* This should never be triggered in normal cases, but in the + * case where the drag started without an implicit grab being + * in effect, we could miss the release if it occurs before + * we grab the pointer; this ensures that we will never + * get a permanently stuck grab. + */ + if ((event->xmotion.state & button_mask) == 0) + finish_drag (); + break; + + case ButtonRelease: + update_pos (event->xbutton.x_root, + event->xbutton.y_root); + + if (event->xbutton.button == moveresize_button) + finish_drag (); + break; + } +} + +void +_gdk_moveresize_configure_done (void) +{ + XEvent *tmp_event; + + if (moveresize_pending_event) + { + tmp_event = moveresize_pending_event; + moveresize_pending_event = NULL; + _gdk_moveresize_handle_event (tmp_event); + g_free (tmp_event); + } +} + +static void +create_moveresize_window (guint32 timestamp) +{ + GdkWindowAttr attributes; + gint attributes_mask; + GdkGrabStatus status; + + g_assert (moveresize_emulation_window == NULL); + + attributes.x = -100; + attributes.y = -100; + attributes.width = 10; + attributes.height = 10; + attributes.window_type = GDK_WINDOW_TEMP; + attributes.wclass = GDK_INPUT_ONLY; + attributes.override_redirect = TRUE; + attributes.event_mask = 0; + + attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_NOREDIR; + + moveresize_emulation_window = + gdk_window_new (NULL, &attributes, attributes_mask); + + gdk_window_show (moveresize_emulation_window); + + status = gdk_pointer_grab (moveresize_emulation_window, + FALSE, + GDK_BUTTON_RELEASE_MASK | + GDK_POINTER_MOTION_MASK, + FALSE, + NULL, + timestamp); + + if (status != GDK_GRAB_SUCCESS) + { + /* If this fails, some other client has grabbed the window + * already. + */ + gdk_window_destroy (moveresize_emulation_window); + moveresize_emulation_window = NULL; + } + + moveresize_process_time = 0; +} + +static void +emulate_resize_drag (GdkWindow *window, + GdkWindowEdge edge, + gint button, + gint root_x, + gint root_y, + guint32 timestamp) +{ + is_resize = TRUE; + moveresize_button = button; + resize_edge = edge; + moveresize_x = root_x; + moveresize_y = root_y; + _gdk_moveresize_window = GDK_WINDOW (g_object_ref (G_OBJECT (window))); + + gdk_window_get_size (window, &moveresize_orig_width, &moveresize_orig_height); + + moveresize_geom_mask = 0; + gdk_window_get_geometry_hints (window, + &moveresize_geometry, + &moveresize_geom_mask); + + create_moveresize_window (timestamp); +} + +static void +emulate_move_drag (GdkWindow *window, + gint button, + gint root_x, + gint root_y, + guint32 timestamp) +{ + is_resize = FALSE; + moveresize_button = button; + moveresize_x = root_x; + moveresize_y = root_y; + _gdk_moveresize_window = GDK_WINDOW (g_object_ref (G_OBJECT (window))); + + gdk_window_get_deskrelative_origin (_gdk_moveresize_window, + &moveresize_orig_x, + &moveresize_orig_y); + + create_moveresize_window (timestamp); +} + +void +gdk_window_begin_resize_drag (GdkWindow *window, + GdkWindowEdge edge, + gint button, + gint root_x, + gint root_y, + guint32 timestamp) +{ + g_return_if_fail (GDK_IS_WINDOW (window)); + g_return_if_fail (moveresize_emulation_window == NULL); + + if (GDK_WINDOW_DESTROYED (window)) + return; + + if (gdk_net_wm_supports (gdk_atom_intern ("_NET_WM_MOVERESIZE", FALSE))) + wmspec_resize_drag (window, edge, button, root_x, root_y, timestamp); + else + emulate_resize_drag (window, edge, button, root_x, root_y, timestamp); +} + +void +gdk_window_begin_move_drag (GdkWindow *window, + gint button, + gint root_x, + gint root_y, + guint32 timestamp) +{ + g_return_if_fail (GDK_IS_WINDOW (window)); + g_return_if_fail (moveresize_emulation_window == NULL); + + if (GDK_WINDOW_DESTROYED (window)) + return; + + if (gdk_net_wm_supports (gdk_atom_intern ("_NET_WM_MOVERESIZE", FALSE))) + wmspec_moveresize (window, _NET_WM_MOVERESIZE_MOVE, + root_x, root_y, timestamp); + else + emulate_move_drag (window, button, root_x, root_y, timestamp); +} + |