Add a test for gravity handling (#1760)
This adds a C program which tests if the window manager handles gravities correctly. This program is loosely based on metacity's test-gravity.c, but completely rewritten and this version does automatic tests instead of allowing the user to perform testing by hand. By having this as a self-contained C program, it is possible to compare awesome's behaviour with the behaviour of other WMs. In my testing, only metacity and awesome pass this test. This is not that much of a big surprise since awesome was fixed in https://github.com/awesomeWM/awesome/pull/505 to work correctly with metacity's test-gravity.c. However, I am surprised that e.g. Fluxbox gets this wrong. Signed-off-by: Uli Schlachter <psychon@znc.in>
This commit is contained in:
parent
9d1d5d5461
commit
3ed0be6d85
|
@ -375,12 +375,16 @@ endif()
|
|||
# }}}
|
||||
|
||||
# {{{ Tests
|
||||
add_executable(test-gravity tests/test-gravity.c)
|
||||
target_link_libraries(test-gravity
|
||||
${AWESOME_COMMON_REQUIRED_LDFLAGS} ${AWESOME_REQUIRED_LDFLAGS})
|
||||
add_custom_target(check-integration
|
||||
sh -c "CMAKE_BINARY_DIR='${CMAKE_BINARY_DIR}' ${CMAKE_SOURCE_DIR}/tests/run.sh"
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
COMMENT "Running integration tests"
|
||||
USES_TERMINAL
|
||||
VERBATIM)
|
||||
add_dependencies(check-integration test-gravity)
|
||||
add_custom_target(check-requires
|
||||
lua "${CMAKE_SOURCE_DIR}/build-utils/check_for_invalid_requires.lua"
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
|
|
|
@ -34,6 +34,7 @@ if [ -z "$build_dir" ]; then
|
|||
build_dir="$source_dir"
|
||||
fi
|
||||
fi
|
||||
export build_dir
|
||||
|
||||
# Get test files: test*, or the ones provided as args (relative to tests/).
|
||||
if [ $# != 0 ]; then
|
||||
|
|
|
@ -0,0 +1,581 @@
|
|||
/*
|
||||
* 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";
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
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:
|
||||
offset_x = (left - right + diff_x + 1) / 2;
|
||||
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;
|
||||
offset_y = (top - bottom + diff_y + 1) / 2;
|
||||
break;
|
||||
case XCB_GRAVITY_CENTER:
|
||||
offset_x = (left - right + diff_x + 1) / 2;
|
||||
offset_y = (top - bottom + diff_y + 1) / 2;
|
||||
break;
|
||||
case XCB_GRAVITY_EAST:
|
||||
offset_x = -right + diff_x;
|
||||
offset_y = (top - bottom + diff_y + 1) / 2;
|
||||
break;
|
||||
case XCB_GRAVITY_SOUTH_WEST:
|
||||
offset_x = left;
|
||||
offset_y = -bottom + diff_y;
|
||||
break;
|
||||
case XCB_GRAVITY_SOUTH:
|
||||
offset_x = (left - right + diff_x + 1) / 2;
|
||||
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);
|
||||
check(expected_x + offset_x == actual_x,
|
||||
"For window with gravity %s in state %s, expected x = %d+%d, but got %d",
|
||||
gravity_to_string(window_state.gravity), state_to_string(window_state.state), (int) expected_x, (int) offset_x, (int) actual_x);
|
||||
check(expected_y + offset_y == actual_y,
|
||||
"For window with gravity %s in state %s, expected y = %d+%d, but got %d",
|
||||
gravity_to_string(window_state.gravity), state_to_string(window_state.state), (int) expected_y, (int) offset_y, (int) actual_y);
|
||||
}
|
||||
|
||||
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
|
|
@ -0,0 +1,64 @@
|
|||
-- Test if client's c:swap() corrupts the Lua stack
|
||||
|
||||
local runner = require("_runner")
|
||||
local spawn = require("awful.spawn")
|
||||
|
||||
local todo = 2
|
||||
local had_error = false
|
||||
|
||||
local function wait_a_bit(count)
|
||||
if todo == 0 or count == 5 then
|
||||
return true
|
||||
end
|
||||
end
|
||||
runner.run_steps({
|
||||
function()
|
||||
local err = spawn.with_line_callback(
|
||||
{ os.getenv("build_dir") .. "/test-gravity" },
|
||||
{
|
||||
exit = function(what, code)
|
||||
assert(what == "exit", what)
|
||||
assert(code == 0, "Exit code was " .. code)
|
||||
todo = todo - 1
|
||||
end,
|
||||
stderr = function(line)
|
||||
had_error = true
|
||||
print("Read on stderr: " .. line)
|
||||
end,
|
||||
stdout = function(line)
|
||||
if line == "SUCCESS" then
|
||||
todo = todo - 1
|
||||
elseif line:sub(1, 5) ~= "LOG: " then
|
||||
had_error = true
|
||||
print("Read on stdout: " .. line)
|
||||
end
|
||||
end
|
||||
})
|
||||
assert(type(err) ~= "string", err)
|
||||
return true
|
||||
end,
|
||||
-- Buy the external program some time to finish
|
||||
wait_a_bit,
|
||||
wait_a_bit,
|
||||
wait_a_bit,
|
||||
wait_a_bit,
|
||||
wait_a_bit,
|
||||
wait_a_bit,
|
||||
wait_a_bit,
|
||||
wait_a_bit,
|
||||
wait_a_bit,
|
||||
wait_a_bit,
|
||||
wait_a_bit,
|
||||
wait_a_bit,
|
||||
wait_a_bit,
|
||||
wait_a_bit,
|
||||
wait_a_bit,
|
||||
function()
|
||||
if todo == 0 then
|
||||
assert(not had_error, "Some error occurred, see above")
|
||||
return true
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
Loading…
Reference in New Issue