2017-05-13 23:28:45 +02:00
|
|
|
/*
|
|
|
|
* A test for gravity handling.
|
|
|
|
*
|
|
|
|
* Copyright © 2017 Uli Schlachter <psychon@znc.in>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Loosely based on test-gravity.c from metacity, which does not have a
|
|
|
|
* copyright or license header, but Metacity itself is licensed under GPLv2.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <xcb/xcb.h>
|
|
|
|
#include <xcb/xcb_aux.h>
|
|
|
|
#include <xcb/xcb_icccm.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This program tests various gravity-related things. For each possible gravity,
|
|
|
|
* this does:
|
|
|
|
* - Create a window with this gravity.
|
|
|
|
* [Wait for expose event]
|
|
|
|
* - Check that the window is mapped in the correct place.
|
|
|
|
* - Resize the window.
|
|
|
|
* [Wait for real configure notify]
|
|
|
|
* - Check that the window moves as expected by the gravity.
|
|
|
|
* - Move and resize the window (go back to the original size).
|
|
|
|
* [Wait for real configure notify]
|
|
|
|
* - Check that the window moves as expected by the gravity.
|
|
|
|
* - Move the window.
|
|
|
|
* [Wait for synthetic configure notify]
|
|
|
|
* - Check that the window moves to the expected position.
|
|
|
|
*
|
|
|
|
* This needs a WM that supports _NET_FRAME_EXTENTS.
|
|
|
|
* Also, this assumes that the WM allows and applies window moves and resizes by
|
|
|
|
* the application.
|
|
|
|
*/
|
|
|
|
enum test_state {
|
|
|
|
TEST_STATE_CREATED,
|
|
|
|
TEST_STATE_RESIZED1,
|
|
|
|
TEST_STATE_RESIZED2,
|
|
|
|
TEST_STATE_MOVED,
|
|
|
|
TEST_STATE_DONE,
|
|
|
|
TEST_STATE_DONE_GOT_CONFIGURE,
|
|
|
|
};
|
|
|
|
|
|
|
|
#define WIN_POS_X1 50
|
|
|
|
#define WIN_POS_Y1 70
|
|
|
|
#define WIN_WIDTH1 30
|
|
|
|
#define WIN_HEIGHT1 40
|
|
|
|
|
|
|
|
#define WIN_POS_X2 42
|
|
|
|
#define WIN_POS_Y2 43
|
|
|
|
#define WIN_WIDTH2 12
|
|
|
|
#define WIN_HEIGHT2 16
|
|
|
|
|
|
|
|
static int32_t state_difference[][2] = {
|
|
|
|
[TEST_STATE_CREATED] = { 0, 0 },
|
|
|
|
[TEST_STATE_RESIZED1] = { WIN_WIDTH1 - WIN_WIDTH2, WIN_HEIGHT1 - WIN_HEIGHT2 },
|
|
|
|
[TEST_STATE_RESIZED2] = { 0, 0 },
|
|
|
|
[TEST_STATE_MOVED] = { 0, 0 },
|
|
|
|
[TEST_STATE_DONE] = { 0, 0 },
|
|
|
|
[TEST_STATE_DONE_GOT_CONFIGURE] = { 0, 0 },
|
|
|
|
};
|
|
|
|
|
|
|
|
struct window_state {
|
|
|
|
xcb_window_t window;
|
|
|
|
enum test_state state;
|
|
|
|
xcb_gravity_t gravity;
|
|
|
|
bool sent_delayed_proceed;
|
|
|
|
};
|
|
|
|
|
|
|
|
static xcb_connection_t *c = NULL;
|
|
|
|
static xcb_screen_t *screen;
|
|
|
|
static struct window_state window_state;
|
|
|
|
static xcb_atom_t net_frame_extents;
|
|
|
|
static bool done;
|
|
|
|
static bool had_error = false;
|
|
|
|
|
|
|
|
static void check(bool cond, const char *format, ...)
|
|
|
|
__attribute__ ((format(printf, 2, 3)));
|
|
|
|
static void do_log(const char *format, ...)
|
|
|
|
__attribute__ ((format(printf, 1, 2)));
|
|
|
|
|
|
|
|
static void check(bool cond, const char *format, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
if (cond)
|
|
|
|
return;
|
|
|
|
|
|
|
|
va_start(ap, format);
|
|
|
|
fprintf(stderr, "ERROR: ");
|
|
|
|
vfprintf(stderr, format, ap);
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
if (c) {
|
|
|
|
xcb_no_operation(c);
|
|
|
|
free(xcb_get_input_focus_reply(c, xcb_get_input_focus(c), NULL));
|
|
|
|
}
|
|
|
|
had_error = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void do_log(const char *format, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
va_start(ap, format);
|
|
|
|
fprintf(stdout, "LOG: ");
|
|
|
|
vfprintf(stdout, format, ap);
|
|
|
|
fprintf(stdout, "\n");
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void query_frame_extents(xcb_window_t win, uint32_t *left,
|
|
|
|
uint32_t *right, uint32_t *top, uint32_t *bottom)
|
|
|
|
{
|
|
|
|
xcb_get_property_reply_t *reply = xcb_get_property_reply(c,
|
|
|
|
xcb_get_property(c, 0, win, net_frame_extents, XCB_ATOM_CARDINAL,
|
|
|
|
0, 4), NULL);
|
|
|
|
|
|
|
|
*left = 0;
|
|
|
|
*right = 0;
|
|
|
|
*top = 0;
|
|
|
|
*bottom = 0;
|
|
|
|
|
|
|
|
if (reply && reply->length == 4)
|
|
|
|
{
|
|
|
|
uint32_t *data = (uint32_t *) xcb_get_property_value(reply);
|
|
|
|
*left = data[0];
|
|
|
|
*right = data[1];
|
|
|
|
*top = data[2];
|
|
|
|
*bottom = data[3];
|
|
|
|
}
|
|
|
|
|
|
|
|
free(reply);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void delayed_proceed_with_window(void)
|
|
|
|
{
|
|
|
|
xcb_client_message_event_t ev;
|
|
|
|
|
|
|
|
if (window_state.sent_delayed_proceed)
|
|
|
|
return;
|
|
|
|
|
|
|
|
memset(&ev, 0, sizeof(ev));
|
|
|
|
ev.response_type = XCB_CLIENT_MESSAGE;
|
|
|
|
ev.window = window_state.window;
|
|
|
|
ev.format = 32;
|
|
|
|
ev.type = XCB_ATOM_NOTICE;
|
|
|
|
ev.data.data32[0] = (int) window_state.state;
|
|
|
|
|
|
|
|
xcb_send_event(c, 0, window_state.window, XCB_EVENT_MASK_NO_EVENT, (char *) &ev);
|
|
|
|
window_state.sent_delayed_proceed = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void get_geometry(xcb_window_t win, int32_t *x, int32_t *y,
|
|
|
|
uint32_t *width, uint32_t *height, uint32_t *left, uint32_t *right,
|
|
|
|
uint32_t *top, uint32_t *bottom)
|
|
|
|
{
|
|
|
|
xcb_get_geometry_cookie_t geo_c = xcb_get_geometry_unchecked(c, win);
|
|
|
|
xcb_translate_coordinates_cookie_t coord_c =
|
|
|
|
xcb_translate_coordinates(c, win, screen->root, 0, 0);
|
|
|
|
|
|
|
|
query_frame_extents(win, left, right, top, bottom);
|
|
|
|
|
|
|
|
xcb_get_geometry_reply_t *geo = xcb_get_geometry_reply(c, geo_c, NULL);
|
|
|
|
xcb_translate_coordinates_reply_t *coord =
|
|
|
|
xcb_translate_coordinates_reply(c, coord_c, NULL);
|
|
|
|
|
|
|
|
*x = coord->dst_x;
|
|
|
|
*y = coord->dst_y;
|
|
|
|
*width = geo->width;
|
|
|
|
*height = geo->height;
|
|
|
|
|
|
|
|
free(coord);
|
|
|
|
free(geo);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *gravity_to_string(xcb_gravity_t gravity) {
|
|
|
|
switch (gravity)
|
|
|
|
{
|
|
|
|
case XCB_GRAVITY_NORTH_WEST:
|
|
|
|
return "NorthWest";
|
|
|
|
case XCB_GRAVITY_NORTH:
|
|
|
|
return "North";
|
|
|
|
case XCB_GRAVITY_NORTH_EAST:
|
|
|
|
return "NorthEast";
|
|
|
|
case XCB_GRAVITY_WEST:
|
|
|
|
return "West";
|
|
|
|
case XCB_GRAVITY_CENTER:
|
|
|
|
return "Center";
|
|
|
|
case XCB_GRAVITY_EAST:
|
|
|
|
return "East";
|
|
|
|
case XCB_GRAVITY_SOUTH_WEST:
|
|
|
|
return "SouthWest";
|
|
|
|
case XCB_GRAVITY_SOUTH:
|
|
|
|
return "South";
|
|
|
|
case XCB_GRAVITY_SOUTH_EAST:
|
|
|
|
return "SouthEast";
|
|
|
|
case XCB_GRAVITY_STATIC:
|
|
|
|
return "Static";
|
|
|
|
default:
|
|
|
|
fprintf(stderr, "Unknown gravity value %d", (int) gravity);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *state_to_string(enum test_state state)
|
|
|
|
{
|
|
|
|
switch (state)
|
|
|
|
{
|
|
|
|
case TEST_STATE_CREATED:
|
|
|
|
return "CREATED";
|
|
|
|
case TEST_STATE_RESIZED1:
|
|
|
|
return "RESIZED1";
|
|
|
|
case TEST_STATE_RESIZED2:
|
|
|
|
return "RESIZED2";
|
|
|
|
case TEST_STATE_MOVED:
|
|
|
|
return "MOVED";
|
|
|
|
case TEST_STATE_DONE:
|
|
|
|
return "DONE";
|
|
|
|
case TEST_STATE_DONE_GOT_CONFIGURE:
|
|
|
|
return "DONE+c";
|
|
|
|
default:
|
|
|
|
return "UNKNOWN";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-17 11:48:49 +02:00
|
|
|
static int32_t div2(int32_t value, int32_t *rounding)
|
|
|
|
{
|
|
|
|
/* If value is odd, we could round up or down */
|
|
|
|
if (value & 1)
|
|
|
|
*rounding = 1;
|
|
|
|
return value / 2;
|
|
|
|
}
|
|
|
|
|
2017-05-13 23:28:45 +02:00
|
|
|
static void check_geometry(int32_t expected_x, int32_t expected_y, uint32_t expected_width, uint32_t expected_height)
|
|
|
|
{
|
|
|
|
int32_t actual_x, actual_y;
|
|
|
|
uint32_t actual_width, actual_height, left, right, top, bottom;
|
|
|
|
get_geometry(window_state.window, &actual_x, &actual_y, &actual_width, &actual_height,
|
|
|
|
&left, &right, &top, &bottom);
|
|
|
|
|
|
|
|
int32_t offset_x, offset_y;
|
2018-08-17 11:48:49 +02:00
|
|
|
int32_t extra_x = 0, extra_y = 0;
|
2017-05-13 23:28:45 +02:00
|
|
|
int32_t diff_x = state_difference[window_state.state][0], diff_y = state_difference[window_state.state][1];
|
|
|
|
switch (window_state.gravity)
|
|
|
|
{
|
|
|
|
case XCB_GRAVITY_NORTH_WEST:
|
|
|
|
offset_x = left;
|
|
|
|
offset_y = top;
|
|
|
|
break;
|
|
|
|
case XCB_GRAVITY_NORTH:
|
2018-08-17 11:48:49 +02:00
|
|
|
offset_x = div2(left - right + diff_x, &extra_x);;
|
2017-05-13 23:28:45 +02:00
|
|
|
offset_y = top;
|
|
|
|
break;
|
|
|
|
case XCB_GRAVITY_NORTH_EAST:
|
|
|
|
offset_x = -right + diff_x;
|
|
|
|
offset_y = top;
|
|
|
|
break;
|
|
|
|
case XCB_GRAVITY_WEST:
|
|
|
|
offset_x = left;
|
2018-08-17 11:48:49 +02:00
|
|
|
offset_y = div2(top - bottom + diff_y, &extra_y);
|
2017-05-13 23:28:45 +02:00
|
|
|
break;
|
|
|
|
case XCB_GRAVITY_CENTER:
|
2018-08-17 11:48:49 +02:00
|
|
|
offset_x = div2(left - right + diff_x, &extra_x);
|
|
|
|
offset_y = div2(top - bottom + diff_y, &extra_y);
|
2017-05-13 23:28:45 +02:00
|
|
|
break;
|
|
|
|
case XCB_GRAVITY_EAST:
|
|
|
|
offset_x = -right + diff_x;
|
2018-08-17 11:48:49 +02:00
|
|
|
offset_y = div2(top - bottom + diff_y, &extra_y);
|
2017-05-13 23:28:45 +02:00
|
|
|
break;
|
|
|
|
case XCB_GRAVITY_SOUTH_WEST:
|
|
|
|
offset_x = left;
|
|
|
|
offset_y = -bottom + diff_y;
|
|
|
|
break;
|
|
|
|
case XCB_GRAVITY_SOUTH:
|
2018-08-17 11:48:49 +02:00
|
|
|
offset_x = div2(left - right + diff_x, &extra_x);
|
2017-05-13 23:28:45 +02:00
|
|
|
offset_y = -bottom + diff_y;
|
|
|
|
break;
|
|
|
|
case XCB_GRAVITY_SOUTH_EAST:
|
|
|
|
offset_x = -right + diff_x;
|
|
|
|
offset_y = -bottom + diff_y;
|
|
|
|
break;
|
|
|
|
case XCB_GRAVITY_STATIC:
|
|
|
|
offset_x = 0;
|
|
|
|
offset_y = 0;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fprintf(stderr, "Unknown gravity!?\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
do_log("Checking if position of window with gravity %s is %dx%d and size "
|
|
|
|
"is %dx%d when frame has size left=%d, right=%d, top=%d, bottom=%d",
|
|
|
|
gravity_to_string(window_state.gravity),
|
|
|
|
expected_x, expected_y, expected_width, expected_height,
|
|
|
|
left, right, top, bottom);
|
|
|
|
|
|
|
|
check(expected_width == actual_width,
|
|
|
|
"For window with gravity %s in state %s, expected width = %d, but got %d",
|
|
|
|
gravity_to_string(window_state.gravity), state_to_string(window_state.state), (int) expected_width, (int) actual_width);
|
|
|
|
check(expected_height == actual_height,
|
|
|
|
"For window with gravity %s in state %s, expected height = %d, but got %d",
|
|
|
|
gravity_to_string(window_state.gravity), state_to_string(window_state.state), (int) expected_height, (int) actual_height);
|
2018-08-17 11:48:49 +02:00
|
|
|
check(expected_x + offset_x == actual_x || expected_x + offset_x + extra_x == actual_x,
|
|
|
|
"For window with gravity %s in state %s, expected x = %d+%d (+%d), but got %d",
|
|
|
|
gravity_to_string(window_state.gravity), state_to_string(window_state.state), (int) expected_x, (int) offset_x, (int) extra_x, (int) actual_x);
|
|
|
|
check(expected_y + offset_y == actual_y || expected_y + offset_y + extra_y == actual_y,
|
|
|
|
"For window with gravity %s in state %s, expected y = %d+%d (+%d), but got %d",
|
|
|
|
gravity_to_string(window_state.gravity), state_to_string(window_state.state), (int) expected_y, (int) offset_y, (int) extra_y, (int) actual_y);
|
2017-05-13 23:28:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void init(xcb_gravity_t gravity)
|
|
|
|
{
|
|
|
|
const char *name = gravity_to_string(gravity);
|
|
|
|
xcb_size_hints_t size_hints;
|
|
|
|
|
|
|
|
memset(&size_hints, 0, sizeof(size_hints));
|
|
|
|
size_hints.win_gravity = gravity;
|
|
|
|
size_hints.flags = XCB_ICCCM_SIZE_HINT_US_POSITION
|
|
|
|
| XCB_ICCCM_SIZE_HINT_P_WIN_GRAVITY;
|
|
|
|
|
|
|
|
window_state.window = xcb_generate_id(c);
|
|
|
|
window_state.state = TEST_STATE_CREATED;
|
|
|
|
window_state.gravity = gravity;
|
|
|
|
|
|
|
|
do_log("creating window at %dx%d with size %dx%d", WIN_POS_X1, WIN_POS_Y1, WIN_WIDTH1, WIN_HEIGHT1);
|
|
|
|
xcb_create_window(c, screen->root_depth, window_state.window, screen->root,
|
|
|
|
WIN_POS_X1, WIN_POS_Y1, WIN_WIDTH1, WIN_HEIGHT1, 0,
|
|
|
|
XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual,
|
|
|
|
XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK, (uint32_t[]) {
|
|
|
|
screen->white_pixel,
|
|
|
|
XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_EXPOSURE
|
|
|
|
});
|
|
|
|
xcb_change_property(c, XCB_PROP_MODE_REPLACE, window_state.window,
|
|
|
|
XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, strlen(name), name);
|
|
|
|
xcb_icccm_set_wm_size_hints(c, window_state.window, XCB_ATOM_WM_NORMAL_HINTS, &size_hints);
|
|
|
|
xcb_map_window(c, window_state.window);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void handle_expose(xcb_expose_event_t *ev)
|
|
|
|
{
|
|
|
|
if (window_state.window != ev->window || window_state.state != TEST_STATE_CREATED)
|
|
|
|
return;
|
|
|
|
|
|
|
|
do_log("Window in state CREATED was exposed, going to next state");
|
|
|
|
delayed_proceed_with_window();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void handle_configure_notify(xcb_configure_notify_event_t *ev)
|
|
|
|
{
|
|
|
|
bool synthetic = ev->response_type & 0x80;
|
|
|
|
if (window_state.window != ev->window)
|
|
|
|
return;
|
|
|
|
|
|
|
|
switch (window_state.state)
|
|
|
|
{
|
|
|
|
case TEST_STATE_CREATED:
|
|
|
|
return;
|
|
|
|
case TEST_STATE_RESIZED1:
|
|
|
|
if (synthetic)
|
|
|
|
return;
|
|
|
|
do_log("Window in state RESIZED1 got ConfigureNotify, going to next state");
|
|
|
|
delayed_proceed_with_window();
|
|
|
|
return;
|
|
|
|
case TEST_STATE_RESIZED2:
|
|
|
|
if (synthetic)
|
|
|
|
return;
|
|
|
|
do_log("Window in state RESIZED2 got ConfigureNotify, going to next state");
|
|
|
|
delayed_proceed_with_window();
|
|
|
|
return;
|
|
|
|
case TEST_STATE_MOVED:
|
|
|
|
if (!synthetic)
|
|
|
|
return;
|
|
|
|
do_log("Window in state MOVED got ConfigureNotify, going to next state");
|
|
|
|
delayed_proceed_with_window();
|
|
|
|
return;
|
|
|
|
case TEST_STATE_DONE:
|
|
|
|
if (!synthetic)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
check(false, "Unexpected configure notify for window 0x%x with gravity %d, %dx%d+%d+%d%s",
|
|
|
|
(int) window_state.window, (int) window_state.gravity, ev->width, ev->height,
|
|
|
|
ev->x, ev->y, synthetic ? " (generated)" : "");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void wait_for_wm(void)
|
|
|
|
{
|
|
|
|
/* Window managers can be awfully slow and ICCCM does not provide much
|
|
|
|
* possibilities to synchronise with them. We try to do this by creating a
|
|
|
|
* window and resizing it, but without mapping it.
|
|
|
|
*/
|
|
|
|
xcb_generic_event_t *event;
|
|
|
|
xcb_window_t window = xcb_generate_id(c);
|
|
|
|
|
|
|
|
do_log("waiting for WM");
|
|
|
|
xcb_create_window(c, screen->root_depth, window, screen->root,
|
|
|
|
0, 0, 1, 1, 0,
|
|
|
|
XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual,
|
|
|
|
XCB_CW_EVENT_MASK, (uint32_t[]) { XCB_EVENT_MASK_STRUCTURE_NOTIFY });
|
|
|
|
xcb_configure_window(c, window,
|
|
|
|
XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
|
|
|
|
(uint32_t[]) { 1, 2, 3, 4 });
|
|
|
|
xcb_flush(c);
|
|
|
|
|
|
|
|
while (event = xcb_wait_for_event(c)) {
|
|
|
|
if ((event->response_type & 0x7f) == XCB_CONFIGURE_NOTIFY) {
|
|
|
|
free(event);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
free(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
xcb_destroy_window(c, window);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void handle_client_message(xcb_client_message_event_t *event)
|
|
|
|
{
|
|
|
|
/* We want a full roundtrip between "we decide to proceed" and "we actually
|
|
|
|
* proceed" so that any in-flight events are handled. To do this, we send
|
|
|
|
* ourselves two client messages. (So that any events that are still in
|
|
|
|
* flight when the first message is sent are handled)
|
|
|
|
*/
|
|
|
|
if (event->type == XCB_ATOM_NOTICE)
|
|
|
|
{
|
|
|
|
wait_for_wm();
|
|
|
|
|
|
|
|
event->response_type = XCB_CLIENT_MESSAGE;
|
|
|
|
event->type = XCB_ATOM_WM_COMMAND;
|
|
|
|
xcb_send_event(c, 0, event->window, XCB_EVENT_MASK_NO_EVENT, (char *) event);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (event->type != XCB_ATOM_WM_COMMAND)
|
|
|
|
return;
|
|
|
|
|
|
|
|
check(window_state.window == event->window, "Got weird client message?!?");
|
|
|
|
|
|
|
|
if (event->data.data32[0] != (int) window_state.state)
|
|
|
|
return;
|
|
|
|
|
|
|
|
window_state.sent_delayed_proceed = false;
|
|
|
|
switch (window_state.state)
|
|
|
|
{
|
|
|
|
case TEST_STATE_CREATED:
|
|
|
|
/* Verify the position and size set in init() */
|
|
|
|
check_geometry(WIN_POS_X1, WIN_POS_Y1, WIN_WIDTH1, WIN_HEIGHT1);
|
|
|
|
|
|
|
|
do_log("Resizing window to size %dx%d", WIN_WIDTH2, WIN_HEIGHT2);
|
|
|
|
xcb_configure_window(c, window_state.window,
|
|
|
|
XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
|
|
|
|
(uint32_t[]) { WIN_WIDTH2, WIN_HEIGHT2 });
|
|
|
|
window_state.state = TEST_STATE_RESIZED1;
|
|
|
|
return;
|
|
|
|
case TEST_STATE_RESIZED1:
|
|
|
|
check_geometry(WIN_POS_X1, WIN_POS_Y1, WIN_WIDTH2, WIN_HEIGHT2);
|
|
|
|
|
|
|
|
do_log("Moving+resizing window to position %dx%d and size %dx%d",
|
|
|
|
WIN_POS_X1, WIN_POS_Y1, WIN_WIDTH1, WIN_HEIGHT1);
|
|
|
|
xcb_configure_window(c, window_state.window,
|
|
|
|
XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
|
|
|
|
(uint32_t[]) { WIN_POS_X1, WIN_POS_Y1, WIN_WIDTH1, WIN_HEIGHT1 });
|
|
|
|
window_state.state = TEST_STATE_RESIZED2;
|
|
|
|
return;
|
|
|
|
case TEST_STATE_RESIZED2:
|
|
|
|
check_geometry(WIN_POS_X1, WIN_POS_Y1, WIN_WIDTH1, WIN_HEIGHT1);
|
|
|
|
|
|
|
|
do_log("Moving window to position %dx%d", WIN_POS_X2, WIN_POS_Y2);
|
|
|
|
xcb_configure_window(c, window_state.window,
|
|
|
|
XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y,
|
|
|
|
(uint32_t[]) { WIN_POS_X2, WIN_POS_Y2 });
|
|
|
|
window_state.state = TEST_STATE_MOVED;
|
|
|
|
return;
|
|
|
|
case TEST_STATE_MOVED:
|
|
|
|
check_geometry(WIN_POS_X2, WIN_POS_Y2, WIN_WIDTH1, WIN_HEIGHT1);
|
|
|
|
|
|
|
|
xcb_destroy_window(c, window_state.window);
|
|
|
|
window_state.window = XCB_NONE;
|
|
|
|
window_state.state = TEST_STATE_DONE;
|
|
|
|
done = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void event_loop(void)
|
|
|
|
{
|
|
|
|
xcb_generic_event_t *ev;
|
|
|
|
|
|
|
|
done = false;
|
|
|
|
xcb_flush(c);
|
|
|
|
while (!done && (ev = xcb_wait_for_event(c)))
|
|
|
|
{
|
|
|
|
uint8_t type = ev->response_type & 0x7f;
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case XCB_EXPOSE:
|
|
|
|
handle_expose((xcb_expose_event_t *) ev);
|
|
|
|
break;
|
|
|
|
case XCB_CONFIGURE_NOTIFY:
|
|
|
|
handle_configure_notify((xcb_configure_notify_event_t *) ev);
|
|
|
|
break;
|
|
|
|
case XCB_CLIENT_MESSAGE:
|
|
|
|
handle_client_message((xcb_client_message_event_t *) ev);
|
|
|
|
case XCB_REPARENT_NOTIFY:
|
|
|
|
case XCB_MAP_NOTIFY:
|
|
|
|
case XCB_UNMAP_NOTIFY:
|
|
|
|
case XCB_DESTROY_NOTIFY:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
printf("Got unexpected event of type 0x%x\n", (int) ev->response_type);
|
|
|
|
}
|
|
|
|
free(ev);
|
|
|
|
xcb_flush(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static xcb_atom_t intern_atom(const char *str)
|
|
|
|
{
|
|
|
|
xcb_atom_t result;
|
|
|
|
xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(c,
|
|
|
|
xcb_intern_atom(c, 0, strlen(str), str), NULL);
|
|
|
|
if (!reply)
|
|
|
|
return XCB_NONE;
|
|
|
|
result = reply->atom;
|
|
|
|
free(reply);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void run_test(xcb_gravity_t gravity)
|
|
|
|
{
|
|
|
|
do_log("Doing run for gravity %s", gravity_to_string(gravity));
|
|
|
|
init(gravity);
|
|
|
|
event_loop();
|
|
|
|
do_log("Finished run for gravity %s", gravity_to_string(gravity));
|
|
|
|
}
|
|
|
|
|
|
|
|
int main()
|
|
|
|
{
|
|
|
|
int default_screen;
|
|
|
|
|
|
|
|
c = xcb_connect(NULL, &default_screen);
|
|
|
|
if (xcb_connection_has_error(c))
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Could not connect to X11 server: %d",
|
|
|
|
xcb_connection_has_error(c));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
screen = xcb_aux_get_screen(c, default_screen);
|
|
|
|
net_frame_extents = intern_atom("_NET_FRAME_EXTENTS");
|
|
|
|
|
|
|
|
run_test(XCB_GRAVITY_NORTH_WEST);
|
|
|
|
run_test(XCB_GRAVITY_NORTH);
|
|
|
|
run_test(XCB_GRAVITY_NORTH_EAST);
|
|
|
|
run_test(XCB_GRAVITY_WEST);
|
|
|
|
run_test(XCB_GRAVITY_CENTER);
|
|
|
|
run_test(XCB_GRAVITY_EAST);
|
|
|
|
run_test(XCB_GRAVITY_SOUTH_WEST);
|
|
|
|
run_test(XCB_GRAVITY_SOUTH);
|
|
|
|
run_test(XCB_GRAVITY_SOUTH_EAST);
|
|
|
|
run_test(XCB_GRAVITY_STATIC);
|
|
|
|
|
|
|
|
check(!xcb_connection_has_error(c),
|
|
|
|
"X11 connection has error: %d", xcb_connection_has_error(c));
|
|
|
|
xcb_disconnect(c);
|
|
|
|
if (had_error)
|
|
|
|
return 1;
|
|
|
|
puts("SUCCESS");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|