From 5fdb8463def40bdfa208ecfdd4655209c35e0b2b Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Sat, 19 Jan 2002 23:59:11 +0000 Subject: [PATCH] cheesy client with static bit gravity, used to test the below change. 2002-01-19 Havoc Pennington * src/wm-tester/test-resizing.c: cheesy client with static bit gravity, used to test the below change. * src/window.c (meta_window_move_resize_internal): implement Owen's proposal for window resizing. http://mail.gnome.org/archives/wm-spec-list/1999-November/msg00088.html Currently you have to do METACITY_USE_STATIC_GRAVITY=1 in order to use it, because some GDK bug is screwing up exposes on my frames when it's enabled. * src/display.c (meta_display_create_x_cursor): fix glyph for NE/NW cursors * src/frames.c (get_control): add ability to resize from top * src/frame.c (meta_frame_get_flags): can't resize shaded windows (meta_frame_sync_to_window): add gravity arg * src/common.h (MetaWindowType): move here from window.h so it can be used in themes stuff. (MetaFrameFlags): remove META_FRAME_TRANSIENT since it overlaps with window type and was unused. --- ChangeLog | 26 ++++ src/common.h | 26 +++- src/display.c | 7 +- src/display.h | 1 + src/frame.c | 9 +- src/frame.h | 1 + src/frames.c | 29 +++- src/theme.c | 105 ++++++++++++++ src/theme.h | 64 ++++++++- src/window.c | 167 +++++++++++++++++----- src/window.h | 11 -- src/wm-tester/Makefile.am | 6 +- src/wm-tester/test-resizing.c | 255 ++++++++++++++++++++++++++++++++++ 13 files changed, 645 insertions(+), 62 deletions(-) create mode 100644 src/wm-tester/test-resizing.c diff --git a/ChangeLog b/ChangeLog index 9c2da774a..b91b25df9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,29 @@ +2002-01-19 Havoc Pennington + + * src/wm-tester/test-resizing.c: cheesy client with static + bit gravity, used to test the below change. + + * src/window.c (meta_window_move_resize_internal): implement + Owen's proposal for window resizing. + http://mail.gnome.org/archives/wm-spec-list/1999-November/msg00088.html + + Currently you have to do METACITY_USE_STATIC_GRAVITY=1 in order to + use it, because some GDK bug is screwing up exposes on my frames + when it's enabled. + + * src/display.c (meta_display_create_x_cursor): fix glyph for + NE/NW cursors + + * src/frames.c (get_control): add ability to resize from top + + * src/frame.c (meta_frame_get_flags): can't resize shaded windows + (meta_frame_sync_to_window): add gravity arg + + * src/common.h (MetaWindowType): move here from window.h so + it can be used in themes stuff. + (MetaFrameFlags): remove META_FRAME_TRANSIENT since it + overlaps with window type and was unused. + 2002-01-18 Havoc Pennington * src/window.c (constrain_position): give priority to keeping NW diff --git a/src/common.h b/src/common.h index 9ee8042f3..b3a34807b 100644 --- a/src/common.h +++ b/src/common.h @@ -34,13 +34,12 @@ typedef enum META_FRAME_ALLOWS_MAXIMIZE = 1 << 3, META_FRAME_ALLOWS_VERTICAL_RESIZE = 1 << 4, META_FRAME_ALLOWS_HORIZONTAL_RESIZE = 1 << 5, - META_FRAME_TRANSIENT = 1 << 6, - META_FRAME_HAS_FOCUS = 1 << 7, - META_FRAME_SHADED = 1 << 8, - META_FRAME_STUCK = 1 << 9, - META_FRAME_MAXIMIZED = 1 << 10, - META_FRAME_ALLOWS_SHADE = 1 << 11, - META_FRAME_ALLOWS_MOVE = 1 << 12 + META_FRAME_HAS_FOCUS = 1 << 6, + META_FRAME_SHADED = 1 << 7, + META_FRAME_STUCK = 1 << 8, + META_FRAME_MAXIMIZED = 1 << 9, + META_FRAME_ALLOWS_SHADE = 1 << 10, + META_FRAME_ALLOWS_MOVE = 1 << 11 } MetaFrameFlags; typedef enum @@ -130,6 +129,19 @@ typedef enum META_FOCUS_MODE_MOUSE } MetaFocusMode; +typedef enum +{ + META_WINDOW_NORMAL, + META_WINDOW_DESKTOP, + META_WINDOW_DOCK, + META_WINDOW_DIALOG, + META_WINDOW_MODAL_DIALOG, + META_WINDOW_TOOLBAR, + META_WINDOW_MENU, + META_WINDOW_TYPE_LAST +} MetaWindowType; + + /* should investigate changing these to whatever most apps use */ #define META_ICON_WIDTH 32 #define META_ICON_HEIGHT 32 diff --git a/src/display.c b/src/display.c index d51cc338e..c84c4511a 100644 --- a/src/display.c +++ b/src/display.c @@ -179,6 +179,9 @@ meta_display_open (const char *name) display->prev_focus_window = NULL; display->showing_desktop = FALSE; + + /* FIXME copy the checks from GDK probably */ + display->static_gravity_works = g_getenv ("METACITY_USE_STATIC_GRAVITY") != NULL; /* we have to go ahead and do this so error handlers work */ all_displays = g_slist_prepend (all_displays, display); @@ -1638,10 +1641,10 @@ meta_display_create_x_cursor (MetaDisplay *display, glyph = XC_bottom_left_corner; break; case META_CURSOR_NE_RESIZE: - glyph = XC_top_left_corner; + glyph = XC_top_right_corner; break; case META_CURSOR_NW_RESIZE: - glyph = XC_top_right_corner; + glyph = XC_top_left_corner; break; default: diff --git a/src/display.h b/src/display.h index ca7351bac..bf5186c6e 100644 --- a/src/display.h +++ b/src/display.h @@ -118,6 +118,7 @@ struct _MetaDisplay GList *workspaces; guint showing_desktop : 1; + guint static_gravity_works : 1; /*< private-ish >*/ MetaEventQueue *events; diff --git a/src/frame.c b/src/frame.c index 64b376a26..088edda7f 100644 --- a/src/frame.c +++ b/src/frame.c @@ -210,7 +210,8 @@ meta_frame_get_flags (MetaFrame *frame) flags |= META_FRAME_ALLOWS_MOVE; if (frame->window->has_resize_func && - !frame->window->maximized) + !frame->window->maximized && + !frame->window->shaded) { if (frame->window->size_hints.min_width < frame->window->size_hints.max_width) @@ -256,7 +257,8 @@ meta_frame_calc_geometry (MetaFrame *frame, } static void -set_background_none (MetaFrame *frame) +set_background_none (MetaFrame *frame, + int resize_gravity) { XSetWindowAttributes attrs; @@ -269,6 +271,7 @@ set_background_none (MetaFrame *frame) void meta_frame_sync_to_window (MetaFrame *frame, + int resize_gravity, gboolean need_move, gboolean need_resize) { @@ -283,7 +286,7 @@ meta_frame_sync_to_window (MetaFrame *frame, /* set bg to none to avoid flicker */ if (need_resize) - set_background_none (frame); + set_background_none (frame, resize_gravity); if (need_move && need_resize) XMoveResizeWindow (frame->window->display->xdisplay, diff --git a/src/frame.h b/src/frame.h index f5585ba98..6447763f2 100644 --- a/src/frame.h +++ b/src/frame.h @@ -67,6 +67,7 @@ MetaFrameFlags meta_frame_get_flags (MetaFrame *frame); void meta_frame_calc_geometry (MetaFrame *frame, MetaFrameGeometry *geomp); void meta_frame_sync_to_window (MetaFrame *frame, + int gravity, gboolean need_move, gboolean need_resize); diff --git a/src/frames.c b/src/frames.c index ed18b2e07..9dff13640 100644 --- a/src/frames.c +++ b/src/frames.c @@ -2042,8 +2042,28 @@ get_control (MetaFrames *frames, if (has_vert || has_horiz) { - if (y >= (fgeom.height - fgeom.bottom_height - RESIZE_EXTENDS) && - x >= (fgeom.width - fgeom.right_width - RESIZE_EXTENDS)) + if (y < fgeom.top_height && x < RESIZE_EXTENDS) + { + if (has_vert && has_horiz) + return META_FRAME_CONTROL_RESIZE_NW; + else if (has_vert) + return META_FRAME_CONTROL_RESIZE_N; + else + return META_FRAME_CONTROL_RESIZE_W; + + } + else if (y < fgeom.top_height && x >= (fgeom.width - RESIZE_EXTENDS)) + { + if (has_vert && has_horiz) + return META_FRAME_CONTROL_RESIZE_NE; + else if (has_vert) + return META_FRAME_CONTROL_RESIZE_N; + else + return META_FRAME_CONTROL_RESIZE_E; + + } + else if (y >= (fgeom.height - fgeom.bottom_height - RESIZE_EXTENDS) && + x >= (fgeom.width - fgeom.right_width - RESIZE_EXTENDS)) { if (has_vert && has_horiz) return META_FRAME_CONTROL_RESIZE_SE; @@ -2062,6 +2082,11 @@ get_control (MetaFrames *frames, else return META_FRAME_CONTROL_RESIZE_W; } + else if (y < fgeom.top_height) + { + if (has_vert) + return META_FRAME_CONTROL_RESIZE_N; + } else if (y >= (fgeom.height - fgeom.bottom_height - RESIZE_EXTENDS)) { if (has_vert) diff --git a/src/theme.c b/src/theme.c index 70a353815..7571f86e8 100644 --- a/src/theme.c +++ b/src/theme.c @@ -666,3 +666,108 @@ meta_texture_spec_draw (const MetaTextureSpec *spec, break; } } + +MetaFrameStyle* +meta_frame_style_new (void) +{ + MetaFrameStyle *style; + + style = g_new0 (MetaFrameStyle, 1); + + style->refcount = 1; + + return style; +} + +void +meta_frame_style_ref (MetaFrameStyle *style) +{ + g_return_if_fail (style != NULL); + + style->refcount += 1; +} + +static void +free_button_textures (MetaTextureSpec *textures[META_BUTTON_TYPE_LAST][META_BUTTON_STATE_LAST]) +{ + int i, j; + + i = 0; + while (i < META_BUTTON_TYPE_LAST) + { + j = 0; + while (j < META_BUTTON_STATE_LAST) + { + if (textures[i][j]) + meta_texture_spec_free (textures[i][j]); + + ++j; + } + + ++i; + } +} + +void +meta_frame_style_unref (MetaFrameStyle *style) +{ + g_return_if_fail (style != NULL); + g_return_if_fail (style->refcount > 0); + + style->refcount -= 1; + + if (style->refcount == 0) + { + int i; + + free_button_textures (style->button_icons); + free_button_textures (style->button_backgrounds); + + i = 0; + while (i < META_FRAME_PIECE_LAST) + { + if (style->pieces[i]) + meta_texture_spec_free (style->pieces[i]); + + ++i; + } + + if (style->layout) + meta_frame_layout_free (style->layout); + + g_free (style); + } +} + +MetaFrameStyleSet* +meta_frame_style_set_new (void) +{ + MetaFrameStyleSet *style_set; + + style_set = g_new0 (MetaFrameStyleSet, 1); + + return style_set; +} + +void +meta_frame_style_set_free (MetaFrameStyleSet *style_set) +{ + int i, j; + + i = 0; + while (i < META_WINDOW_TYPE_LAST) + { + j = 0; + while (j < META_WINDOW_STATE_LAST) + { + if (style_set->styles[i][j]) + meta_frame_style_unref (style_set->styles[i][j]); + + ++j; + } + + ++i; + } + + g_free (style_set); +} diff --git a/src/theme.h b/src/theme.h index 1cb73f06f..099b28ab8 100644 --- a/src/theme.h +++ b/src/theme.h @@ -133,6 +133,14 @@ typedef enum META_TEXTURE_IMAGE } MetaTextureType; +typedef enum +{ + META_TEXTURE_DRAW_UNSCALED, + META_TEXTURE_DRAW_SCALED_VERTICALLY, + META_TEXTURE_DRAW_SCALED_HORIZONTALLY, + META_TEXTURE_DRAW_SCALED_BOTH +} MetaTextureDrawMode; + struct _MetaTextureSpec { MetaTextureType type; @@ -243,22 +251,60 @@ typedef enum struct _MetaFrameStyle { + int refcount; MetaTextureSpec *button_icons[META_BUTTON_TYPE_LAST][META_BUTTON_STATE_LAST]; MetaTextureSpec *button_backgrounds[META_BUTTON_TYPE_LAST][META_BUTTON_STATE_LAST]; MetaTextureSpec *pieces[META_FRAME_PIECE_LAST]; MetaFrameLayout *layout; }; + typedef enum { - META_TEXTURE_DRAW_UNSCALED, - META_TEXTURE_DRAW_SCALED_VERTICALLY, - META_TEXTURE_DRAW_SCALED_HORIZONTALLY, - META_TEXTURE_DRAW_SCALED_BOTH -} MetaTextureDrawMode; + /* FIXME dammit, these are not mutually exclusive; how to handle + * the mess... + * + * normal -> noresize / vert only / horz only / both + focused / unfocused + * max -> focused / unfocused + * shaded -> focused / unfocused + * max/shaded -> focused / unfocused + * + * so 4 states with 8 sub-states in one, 2 sub-states in the other 3, + * meaning 14 total + * + * 14 window states times 7 or 8 window types. + * + * + * MetaFrameStyleSet needs rearranging to think of it this way. + * + */ + META_WINDOW_STATE_MAXIMIZED, + META_WINDOW_STATE_SHADED, + META_WINDOW_STATE_MAXIMIZED_AND_SHADED, + META_WINDOW_STATE_RESIZE_VERTICAL, + META_WINDOW_STATE_RESIZE_HORIZONTAL, + META_WINDOW_STATE_RESIZE_BOTH, + META_WINDOW_STATE_UNFOCUSED, + META_WINDOW_STATE_FOCUSED, + META_WINDOW_STATE_LAST +} MetaWindowState; + +struct _MetaFrameStyleSet +{ + MetaFrameStyle *styles[META_WINDOW_TYPE_LAST][META_WINDOW_STATE_LAST]; +}; MetaFrameLayout* meta_frame_layout_new (void); void meta_frame_layout_free (MetaFrameLayout *layout); +void meta_frame_layout_get_borders (const MetaFrameLayout *layout, + GtkWidget *widget, + int text_height, + MetaFrameFlags flags, + int *top_height, + int *bottom_height, + int *left_width, + int *right_width); void meta_frame_layout_calc_geometry (const MetaFrameLayout *layout, GtkWidget *widget, int text_height, @@ -267,6 +313,7 @@ void meta_frame_layout_calc_geometry (const MetaFrameLayout *layout, int client_height, MetaFrameGeometry *fgeom); + MetaColorSpec* meta_color_spec_new (MetaColorSpecType type); void meta_color_spec_free (MetaColorSpec *spec); void meta_color_spec_render (MetaColorSpec *spec, @@ -296,4 +343,11 @@ void meta_texture_spec_draw (const MetaTextureSpec *desc, int width, int height); +MetaFrameStyle* meta_frame_style_new (void); +void meta_frame_style_ref (MetaFrameStyle *style); +void meta_frame_style_unref (MetaFrameStyle *style); + +MetaFrameStyleSet* meta_frame_style_set_new (void); +void meta_frame_style_set_free (MetaFrameStyleSet *style_set); + #endif diff --git a/src/window.c b/src/window.c index 252e5337d..e728d826f 100644 --- a/src/window.c +++ b/src/window.c @@ -1518,6 +1518,12 @@ adjust_for_gravity (MetaWindow *window, } } +static gboolean +static_gravity_works (MetaDisplay *display) +{ + return display->static_gravity_works; +} + static void meta_window_move_resize_internal (MetaWindow *window, MetaMoveResizeFlags flags, @@ -1537,14 +1543,19 @@ meta_window_move_resize_internal (MetaWindow *window, gboolean need_resize_frame = FALSE; int size_dx; int size_dy; - int pos_dx; - int pos_dy; int frame_size_dx; int frame_size_dy; gboolean is_configure_request; gboolean do_gravity_adjust; gboolean is_user_action; - + gboolean configure_frame_first; + gboolean use_static_gravity; + /* used for the configure request, but may not be final + * destination due to StaticGravity etc. + */ + int client_move_x; + int client_move_y; + /* We don't need it in the idle queue anymore. */ meta_window_unqueue_move_resize (window); @@ -1590,12 +1601,10 @@ meta_window_move_resize_internal (MetaWindow *window, else new_h = window->rect.height + fgeom.top_height + fgeom.bottom_height; - if (new_w != window->frame->rect.width || - new_h != window->frame->rect.height) - need_resize_frame = TRUE; - frame_size_dx = new_w - window->frame->rect.width; frame_size_dy = new_h - window->frame->rect.height; + + need_resize_frame = (frame_size_dx != 0 || frame_size_dy != 0); window->frame->rect.width = new_w; window->frame->rect.height = new_h; @@ -1675,6 +1684,21 @@ meta_window_move_resize_internal (MetaWindow *window, default: break; } + + /* For nice effect, when growing the window we want to move/resize + * the frame first, when shrinking the window we want to move/resize + * the client first. If we grow one way and shrink the other, + * see which way we're moving "more" + * + * Mail from Owen subject "Suggestion: Gravity and resizing from the left" + * http://mail.gnome.org/archives/wm-spec-list/1999-November/msg00088.html + * + * An annoying fact you need to know in this code is that StaticGravity + * does nothing if you _only_ resize or _only_ move the frame; + * it must move _and_ resize, otherwise you get NorthWestGravity + * behavior. The move and resize must actually occur, it is not + * enough to set CWX | CWWidth but pass in the current size/pos. + */ constrain_position (window, window->frame ? &fgeom : NULL, @@ -1687,41 +1711,100 @@ meta_window_move_resize_internal (MetaWindow *window, if (window->frame) { int new_x, new_y; + int frame_pos_dx, frame_pos_dy; + /* Compute new frame coords */ new_x = root_x_nw - fgeom.left_width; new_y = root_y_nw - fgeom.top_height; - if (new_x != window->frame->rect.x || - new_y != window->frame->rect.y) - need_move_frame = TRUE; + frame_pos_dx = new_x - window->frame->rect.x; + frame_pos_dy = new_y - window->frame->rect.y; - if (window->rect.x != fgeom.left_width || - window->rect.y != fgeom.top_height) - need_move_client = TRUE; + need_move_frame = (frame_pos_dx != 0 || frame_pos_dy != 0); window->frame->rect.x = new_x; - window->frame->rect.y = new_y; - + window->frame->rect.y = new_y; + + /* If frame will both move and resize, then StaticGravity + * on the child window will kick in and implicitly move + * the child with respect to the frame. The implicit + * move will keep the child in the same place with + * respect to the root window. If frame only moves + * or only resizes, then the child will just move along + * with the frame. + */ + /* window->rect.x, window->rect.y are relative to frame, * remember they are the server coords */ - pos_dx = fgeom.left_width - window->rect.x; - pos_dy = fgeom.top_height - window->rect.y; - - window->rect.x = fgeom.left_width; - window->rect.y = fgeom.top_height; + + new_x = fgeom.left_width; + new_y = fgeom.top_height; + + if (need_resize_frame && need_move_frame && + static_gravity_works (window->display)) + { + /* static gravity kicks in because frame + * is both moved and resized + */ + /* when we move the frame by frame_pos_dx, frame_pos_dy the + * client will implicitly move relative to frame by the + * inverse delta. + * + * When moving client then frame, we move the client by the + * frame delta, to be canceled out by the implicit move by + * the inverse frame delta, resulting in a client at new_x, + * new_y. + * + * When moving frame then client, we move the client + * by the same delta as the frame, because the client + * was "left behind" by the frame - resulting in a client + * at new_x, new_y. + * + * In both cases we need to move the client window + * in all cases where we had to move the frame window. + */ + + client_move_x = new_x + frame_pos_dx; + client_move_y = new_y + frame_pos_dy; + + if (need_move_frame) + need_move_client = TRUE; + + use_static_gravity = TRUE; + } + else + { + client_move_x = new_x; + client_move_y = new_y; + + if (client_move_x != window->rect.x || + client_move_y != window->rect.y) + need_move_client = TRUE; + + use_static_gravity = FALSE; + } + + /* This is the final target position, but not necessarily what + * we pass to XConfigureWindow, due to StaticGravity implicit + * movement. + */ + window->rect.x = new_x; + window->rect.y = new_y; } else { if (root_x_nw != window->rect.x || root_y_nw != window->rect.y) need_move_client = TRUE; - - pos_dx = root_x_nw - window->rect.x; - pos_dy = root_y_nw - window->rect.y; window->rect.x = root_x_nw; window->rect.y = root_y_nw; + + client_move_x = window->rect.x; + client_move_y = window->rect.y; + + use_static_gravity = FALSE; } /* Fill in other frame member variables */ @@ -1736,7 +1819,7 @@ meta_window_move_resize_internal (MetaWindow *window, /* See ICCCM 4.1.5 for when to send ConfigureNotify */ need_configure_notify = FALSE; - + /* If this is a configure request and we change nothing, then we * must send configure notify. */ @@ -1756,10 +1839,30 @@ meta_window_move_resize_internal (MetaWindow *window, /* The rest of this function syncs our new size/pos with X as * efficiently as possible */ + if (use_static_gravity) + { + if ((size_dx + size_dy) >= 0) + configure_frame_first = FALSE; + else + configure_frame_first = TRUE; + } + else + { + configure_frame_first = FALSE; + } + + + if (use_static_gravity) + meta_window_set_gravity (window, StaticGravity); + if (configure_frame_first && window->frame) + meta_frame_sync_to_window (window->frame, + resize_gravity, + need_move_frame, need_resize_frame); + values.border_width = 0; - values.x = window->rect.x; - values.y = window->rect.y; + values.x = client_move_x; + values.y = client_move_y; values.width = window->rect.width; values.height = window->rect.height; @@ -1792,12 +1895,14 @@ meta_window_move_resize_internal (MetaWindow *window, meta_error_trap_pop (window->display); } - /* Now do the frame */ - if (window->frame) - { - meta_frame_sync_to_window (window->frame, need_move_frame, need_resize_frame); - } + if (!configure_frame_first && window->frame) + meta_frame_sync_to_window (window->frame, + resize_gravity, + need_move_frame, need_resize_frame); + /* Put gravity back to be nice to lesser window managers */ + if (use_static_gravity) + meta_window_set_gravity (window, NorthWestGravity); if (need_configure_notify) send_configure_notify (window); diff --git a/src/window.h b/src/window.h index bd24faf7e..e8dc976cf 100644 --- a/src/window.h +++ b/src/window.h @@ -28,17 +28,6 @@ #include #include -typedef enum -{ - META_WINDOW_NORMAL, - META_WINDOW_DESKTOP, - META_WINDOW_DOCK, - META_WINDOW_DIALOG, - META_WINDOW_MODAL_DIALOG, - META_WINDOW_TOOLBAR, - META_WINDOW_MENU -} MetaWindowType; - struct _MetaWindow { MetaDisplay *display; diff --git a/src/wm-tester/Makefile.am b/src/wm-tester/Makefile.am index 8673127a1..ffecdcc3a 100644 --- a/src/wm-tester/Makefile.am +++ b/src/wm-tester/Makefile.am @@ -10,8 +10,12 @@ test_gravity_SOURCES= \ focus_window_SOURCES= \ focus-window.c -noinst_PROGRAMS=wm-tester test-gravity focus-window +test_resizing_SOURCES= \ + test-resizing.c + +noinst_PROGRAMS=wm-tester test-gravity test-resizing focus-window wm_tester_LDADD= @METACITY_LIBS@ test_gravity_LDADD= @METACITY_LIBS@ +test_resizing_LDADD= @METACITY_LIBS@ focus_window_LDADD= @METACITY_LIBS@ \ No newline at end of file diff --git a/src/wm-tester/test-resizing.c b/src/wm-tester/test-resizing.c new file mode 100644 index 000000000..479fc69e3 --- /dev/null +++ b/src/wm-tester/test-resizing.c @@ -0,0 +1,255 @@ +#include +#include +#include +#include + +static void +calc_rects (XRectangle *rects, int width, int height) +{ + int w = (width - 21) / 3; + int h = (height - 21) / 3; + int i; + + i = 0; + while (i < 9) + { + rects[i].width = w; + rects[i].height = h; + ++i; + } + + /* NW */ + rects[0].x = 0; + rects[0].y = 0; + + /* N */ + rects[1].x = width / 2 - w / 2; + rects[1].y = 0; + + /* NE */ + rects[2].x = width - w; + rects[2].y = 0; + + /* E */ + rects[3].x = width - w; + rects[3].y = height / 2 - h / 2; + + /* SE */ + rects[4].x = width - w; + rects[4].y = height - h; + + /* S */ + rects[5].x = width / 2 - w / 2; + rects[5].y = height - h; + + /* SW */ + rects[6].x = 0; + rects[6].y = height - h; + + /* W */ + rects[7].x = 0; + rects[7].y = height / 2 - h / 2; + + /* Center */ + rects[8].x = width / 2 - w / 2; + rects[8].y = height / 2 - h / 2; +} + +static Bool +all_events (Display *display, + XEvent *event, + XPointer arg) +{ + return True; +} + +static void +get_size (Display *d, Drawable draw, + int *xp, int *yp, int *widthp, int *heightp) +{ + int x, y; + unsigned int width, height, border, depth; + Window root; + + XGetGeometry (d, draw, &root, &x, &y, &width, &height, &border, &depth); + + if (xp) + *xp = x; + if (yp) + *yp = y; + if (widthp) + *widthp = width; + if (*heightp) + *heightp = height; +} + +int +main (int argc, char **argv) +{ + Display *d; + Window w, cw; + XSizeHints hints; + int screen; + XEvent ev; + int x, y, width, height; + Pixmap pix; + GC gc; + XGCValues gc_vals; + XSetWindowAttributes set_attrs; + XWindowChanges changes; + XRectangle rects[9]; + gboolean redraw_pending; + unsigned int mask; + + d = XOpenDisplay (NULL); + + screen = DefaultScreen (d); + + /* Print some debug spew to show how StaticGravity works */ + w = XCreateSimpleWindow (d, RootWindow (d, screen), + 0, 0, 100, 100, 0, + WhitePixel (d, screen), + WhitePixel (d, screen)); + cw = XCreateSimpleWindow (d, w, + 0, 0, 100, 100, 0, + WhitePixel (d, screen), + WhitePixel (d, screen)); + set_attrs.win_gravity = StaticGravity; + + XChangeWindowAttributes (d, cw, + CWWinGravity, + &set_attrs); + + get_size (d, w, &x, &y, &width, &height); + + g_print ("Parent is %d,%d %d x %d before configuring parent\n", + x, y, width, height); + + get_size (d, cw, &x, &y, &width, &height); + + g_print ("Child is %d,%d %d x %d before configuring parent\n", + x, y, width, height); + + changes.x = 10; + changes.y = 10; + changes.width = 110; + changes.height = 110; + /* last mask wins */ + mask = CWX | CWY; + mask = CWWidth | CWHeight; + mask = CWX | CWY | CWWidth | CWHeight; + + XConfigureWindow (d, w, mask, &changes); + XSync (d, False); + + get_size (d, w, &x, &y, &width, &height); + + g_print ("Parent is %d,%d %d x %d after configuring parent\n", + x, y, width, height); + + get_size (d, cw, &x, &y, &width, &height); + + g_print ("Child is %d,%d %d x %d after configuring parent\n", + x, y, width, height); + + XDestroyWindow (d, w); + + /* The window that gets displayed */ + + x = 20; + y = 20; + width = 100; + height = 100; + + calc_rects (rects, width, height); + + w = XCreateSimpleWindow (d, RootWindow (d, screen), + x, y, width, height, 0, + WhitePixel (d, screen), + WhitePixel (d, screen)); + + set_attrs.bit_gravity = StaticGravity; + + XChangeWindowAttributes (d, w, + CWBitGravity, + &set_attrs); + + XSelectInput (d, w, + ButtonPressMask | ExposureMask | StructureNotifyMask); + + hints.flags = PMinSize; + + hints.min_width = 100; + hints.min_height = 100; + + XSetWMNormalHints (d, w, &hints); + XMapWindow (d, w); + + redraw_pending = FALSE; + while (1) + { + XNextEvent (d, &ev); + + switch (ev.xany.type) + { + case ButtonPress: + if (ev.xbutton.button == 3) + { + g_print ("Exiting on button 3 press\n"); + exit (0); + } + break; + + case ConfigureNotify: + x = ev.xconfigure.x; + y = ev.xconfigure.y; + width = ev.xconfigure.width; + height = ev.xconfigure.height; + + redraw_pending = TRUE; + break; + + case Expose: + redraw_pending = TRUE; + break; + + default: + break; + } + + /* Primitive event compression */ + if (XCheckIfEvent (d, &ev, all_events, NULL)) + { + XPutBackEvent (d, &ev); + } + else if (redraw_pending) + { + calc_rects (rects, width, height); + + pix = XCreatePixmap (d, w, width, height, + DefaultDepth (d, screen)); + + gc_vals.foreground = WhitePixel (d, screen); + + gc = XCreateGC (d, pix, GCForeground, &gc_vals); + + XFillRectangle (d, pix, gc, 0, 0, width, height); + + /* Draw rectangles at each gravity point */ + gc_vals.foreground = BlackPixel (d, screen); + XChangeGC (d, gc, GCForeground, &gc_vals); + + XFillRectangles (d, pix, gc, rects, G_N_ELEMENTS (rects)); + + XCopyArea (d, pix, w, gc, 0, 0, width, height, 0, 0); + + XFreePixmap (d, pix); + XFreeGC (d, gc); + + redraw_pending = FALSE; + } + } + + return 0; +} +