Skip to content

Commit

Permalink
Adds events for wlmtk_window_t, and a signal for window state changes. (
Browse files Browse the repository at this point in the history
#148)

* Adds wlmtk_window_events_t.*

* Wires up state-changed signal when (un)maximizing window.

* Adds missing doxygen comment.

* Clarify window state scope.

* Wires up state_changed signal in fullscreen call.

* Adds tests for window shading, and document behaviour on client-side-decoration.

* Wires up state_change signal from window-shading.
  • Loading branch information
phkaeser committed Dec 9, 2024
1 parent e01d14c commit 6dfb0ca
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 1 deletion.
120 changes: 119 additions & 1 deletion src/toolkit/window.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ struct _wlmtk_window_t {
/** Virtual method table. */
wlmtk_window_vmt_t vmt;

/** Events for this window. */
wlmtk_window_events_t events;

/** Box: In `super_bordered`, holds surface, title bar and resizebar. */
wlmtk_box_t box;

Expand Down Expand Up @@ -263,6 +266,13 @@ void wlmtk_window_destroy(wlmtk_window_t *window_ptr)
free(window_ptr);
}

/* ------------------------------------------------------------------------- */
wlmtk_window_events_t *wlmtk_window_events(
wlmtk_window_t *window_ptr)
{
return &window_ptr->events;
}

/* ------------------------------------------------------------------------- */
wlmtk_element_t *wlmtk_window_element(wlmtk_window_t *window_ptr)
{
Expand Down Expand Up @@ -315,8 +325,14 @@ void wlmtk_window_set_server_side_decorated(
bool decorated)
{
if (window_ptr->server_side_decorated == decorated) return;

if (!decorated && wlmtk_window_is_shaded(window_ptr)) {
wlmtk_window_request_shaded(window_ptr, false);
}

window_ptr->server_side_decorated = decorated;
_wlmtk_window_apply_decoration(window_ptr);

}

/* ------------------------------------------------------------------------- */
Expand Down Expand Up @@ -430,6 +446,7 @@ void wlmtk_window_commit_maximized(
if (window_ptr->maximized == maximized) return;

window_ptr->maximized = maximized;
wl_signal_emit(&window_ptr->events.state_changed, window_ptr);
}

/* ------------------------------------------------------------------------- */
Expand Down Expand Up @@ -494,6 +511,7 @@ void wlmtk_window_commit_fullscreen(

wlmtk_workspace_window_to_fullscreen(
wlmtk_window_get_workspace(window_ptr), window_ptr, fullscreen);
wl_signal_emit(&window_ptr->events.state_changed, window_ptr);
}

/* ------------------------------------------------------------------------- */
Expand All @@ -517,6 +535,7 @@ void wlmtk_window_request_shaded(wlmtk_window_t *window_ptr, bool shaded)
}

window_ptr->shaded = shaded;
wl_signal_emit(&window_ptr->events.state_changed, window_ptr);
}

/* ------------------------------------------------------------------------- */
Expand Down Expand Up @@ -725,6 +744,8 @@ bool _wlmtk_window_init(

wlmtk_box_add_element_front(&window_ptr->box, element_ptr);
wlmtk_element_set_visible(element_ptr, true);

wl_signal_init(&window_ptr->events.state_changed);
return true;
}

Expand Down Expand Up @@ -1263,6 +1284,7 @@ static void test_server_side_decorated_properties(bs_test_t *test_ptr);
static void test_maximize(bs_test_t *test_ptr);
static void test_fullscreen(bs_test_t *test_ptr);
static void test_fullscreen_unmap(bs_test_t *test_ptr);
static void test_shade(bs_test_t *test_ptr);
static void test_fake(bs_test_t *test_ptr);

const bs_test_case_t wlmtk_window_test_cases[] = {
Expand All @@ -1276,10 +1298,18 @@ const bs_test_case_t wlmtk_window_test_cases[] = {
{ 1, "maximize", test_maximize },
{ 1, "fullscreen", test_fullscreen },
{ 1, "fullscreen_unmap", test_fullscreen_unmap },
{ 1, "shade", test_shade },
{ 1, "fake", test_fake },
{ 0, NULL, NULL }
};

/** For testing: Tracks whether handle_state_change was called. */
static bool _wlmtk_window_test_handle_state_changed_called;

static void _wlmtk_window_test_handle_state_changed(
struct wl_listener *listener_ptr,
void *data_ptr);

/* ------------------------------------------------------------------------- */
/** Tests setup and teardown. */
void test_create_destroy(bs_test_t *test_ptr)
Expand Down Expand Up @@ -1449,12 +1479,18 @@ void test_server_side_decorated_properties(bs_test_t *test_ptr)
void test_maximize(bs_test_t *test_ptr)
{
struct wlr_box box;
struct wl_listener listener;

wlmtk_workspace_t *ws_ptr = wlmtk_workspace_create_for_test(1024, 768, 0);
BS_TEST_VERIFY_NEQ_OR_RETURN(test_ptr, NULL, ws_ptr);
wlmtk_fake_window_t *fw_ptr = wlmtk_fake_window_create();
BS_TEST_VERIFY_NEQ_OR_RETURN(test_ptr, NULL, fw_ptr);

wlmtk_util_connect_listener_signal(
&wlmtk_window_events(fw_ptr->window_ptr)->state_changed,
&listener,
_wlmtk_window_test_handle_state_changed);

// Window must be mapped to get maximized: Need workspace dimensions.
wlmtk_workspace_map_window(ws_ptr, fw_ptr->window_ptr);

Expand Down Expand Up @@ -1485,16 +1521,20 @@ void test_maximize(bs_test_t *test_ptr)
BS_TEST_VERIFY_EQ(test_ptr, 100, box.height);

// Maximize.
_wlmtk_window_test_handle_state_changed_called = false;
wlmtk_window_request_maximized(fw_ptr->window_ptr, true);
BS_TEST_VERIFY_FALSE(test_ptr, wlmtk_window_is_maximized(fw_ptr->window_ptr));
wlmtk_fake_window_commit_size(fw_ptr);
BS_TEST_VERIFY_FALSE(test_ptr, _wlmtk_window_test_handle_state_changed_called);
wlmtk_window_commit_maximized(fw_ptr->window_ptr, true);
box = wlmtk_window_get_position_and_size(fw_ptr->window_ptr);
BS_TEST_VERIFY_EQ(test_ptr, 0, box.x);
BS_TEST_VERIFY_EQ(test_ptr, 0, box.y);
BS_TEST_VERIFY_EQ(test_ptr, 960, box.width);
BS_TEST_VERIFY_EQ(test_ptr, 704, box.height);
BS_TEST_VERIFY_TRUE(test_ptr, wlmtk_window_is_maximized(fw_ptr->window_ptr));
BS_TEST_VERIFY_TRUE(test_ptr, _wlmtk_window_test_handle_state_changed_called);
_wlmtk_window_test_handle_state_changed_called = false;

// A second commit: should not overwrite the organic dimension.
wlmtk_fake_window_commit_size(fw_ptr);
Expand All @@ -1503,20 +1543,23 @@ void test_maximize(bs_test_t *test_ptr)
wlmtk_window_request_maximized(fw_ptr->window_ptr, false);
BS_TEST_VERIFY_TRUE(test_ptr, wlmtk_window_is_maximized(fw_ptr->window_ptr));
wlmtk_fake_window_commit_size(fw_ptr);
BS_TEST_VERIFY_FALSE(test_ptr, _wlmtk_window_test_handle_state_changed_called);
wlmtk_window_commit_maximized(fw_ptr->window_ptr, false);
box = wlmtk_window_get_position_and_size(fw_ptr->window_ptr);
BS_TEST_VERIFY_EQ(test_ptr, 50, box.x);
BS_TEST_VERIFY_EQ(test_ptr, 30, box.y);
BS_TEST_VERIFY_EQ(test_ptr, 200, box.width);
BS_TEST_VERIFY_EQ(test_ptr, 100, box.height);
BS_TEST_VERIFY_FALSE(test_ptr, wlmtk_window_is_maximized(fw_ptr->window_ptr));
BS_TEST_VERIFY_TRUE(test_ptr, _wlmtk_window_test_handle_state_changed_called);

// TODO([email protected]): Define what should happen when a maximized
// window is moved. Should it lose maximization? Should it not move?
// Or just move on?
// Window Maker keeps maximization, but it's ... odd.

wlmtk_workspace_unmap_window(ws_ptr, fw_ptr->window_ptr);
wlmtk_util_disconnect_listener(&listener);
wlmtk_fake_window_destroy(fw_ptr);
wlmtk_workspace_destroy(ws_ptr);
}
Expand All @@ -1526,12 +1569,18 @@ void test_maximize(bs_test_t *test_ptr)
void test_fullscreen(bs_test_t *test_ptr)
{
struct wlr_box box;
struct wl_listener listener;

wlmtk_workspace_t *ws_ptr = wlmtk_workspace_create_for_test(1024, 768, 0);
BS_TEST_VERIFY_NEQ_OR_RETURN(test_ptr, NULL, ws_ptr);
wlmtk_fake_window_t *fw_ptr = wlmtk_fake_window_create();
BS_TEST_VERIFY_NEQ_OR_RETURN(test_ptr, NULL, fw_ptr);

wlmtk_util_connect_listener_signal(
&wlmtk_window_events(fw_ptr->window_ptr)->state_changed,
&listener,
_wlmtk_window_test_handle_state_changed);

wlmtk_window_set_server_side_decorated(fw_ptr->window_ptr, true);
wlmtk_workspace_map_window(ws_ptr, fw_ptr->window_ptr);

Expand All @@ -1552,11 +1601,13 @@ void test_fullscreen(bs_test_t *test_ptr)
BS_TEST_VERIFY_FALSE(test_ptr, fw_ptr->window_ptr->inorganic_sizing);

// Request fullscreen. Does not take immediate effect.
_wlmtk_window_test_handle_state_changed_called = false;
wlmtk_window_request_fullscreen(fw_ptr->window_ptr, true);
BS_TEST_VERIFY_FALSE(test_ptr, wlmtk_window_is_fullscreen(fw_ptr->window_ptr));
BS_TEST_VERIFY_TRUE(
test_ptr,
wlmtk_titlebar_is_activated(fw_ptr->window_ptr->titlebar_ptr));
BS_TEST_VERIFY_FALSE(test_ptr, _wlmtk_window_test_handle_state_changed_called);

// Only after "commit", it will take effect.
wlmtk_fake_window_commit_size(fw_ptr);
Expand All @@ -1567,6 +1618,8 @@ void test_fullscreen(bs_test_t *test_ptr)
BS_TEST_VERIFY_EQ(test_ptr, 0, box.y);
BS_TEST_VERIFY_EQ(test_ptr, 1024, box.width);
BS_TEST_VERIFY_EQ(test_ptr, 768, box.height);
BS_TEST_VERIFY_TRUE(test_ptr, _wlmtk_window_test_handle_state_changed_called);
_wlmtk_window_test_handle_state_changed_called = false;

BS_TEST_VERIFY_TRUE(test_ptr, fw_ptr->fake_content_ptr->activated);
BS_TEST_VERIFY_EQ(
Expand All @@ -1581,6 +1634,7 @@ void test_fullscreen(bs_test_t *test_ptr)
// Request to end fullscreen. Not taking immediate effect.
wlmtk_window_request_fullscreen(fw_ptr->window_ptr, false);
BS_TEST_VERIFY_TRUE(test_ptr, wlmtk_window_is_fullscreen(fw_ptr->window_ptr));
BS_TEST_VERIFY_FALSE(test_ptr, _wlmtk_window_test_handle_state_changed_called);

// Takes effect after commit. We'll want the same position as before.
wlmtk_fake_window_commit_size(fw_ptr);
Expand All @@ -1600,14 +1654,16 @@ void test_fullscreen(bs_test_t *test_ptr)
test_ptr,
fw_ptr->window_ptr,
wlmtk_workspace_get_activated_window(ws_ptr));
BS_TEST_VERIFY_TRUE(test_ptr, _wlmtk_window_test_handle_state_changed_called);
_wlmtk_window_test_handle_state_changed_called = false;

BS_TEST_VERIFY_TRUE(test_ptr, fw_ptr->window_ptr->server_side_decorated);
BS_TEST_VERIFY_NEQ(test_ptr, NULL, fw_ptr->window_ptr->titlebar_ptr);
BS_TEST_VERIFY_NEQ(test_ptr, NULL, fw_ptr->window_ptr->resizebar_ptr);

wlmtk_workspace_unmap_window(ws_ptr, fw_ptr->window_ptr);
wlmtk_util_disconnect_listener(&listener);
wlmtk_fake_window_destroy(fw_ptr);

wlmtk_workspace_destroy(ws_ptr);
}

Expand Down Expand Up @@ -1656,6 +1712,59 @@ void test_fullscreen_unmap(bs_test_t *test_ptr)
wlmtk_workspace_destroy(ws_ptr);
}

/* ------------------------------------------------------------------------- */
/** Verifies that window shading hides the element and raises signal. */
void test_shade(bs_test_t *test_ptr)
{
struct wl_listener listener;

wlmtk_fake_window_t *fw_ptr = wlmtk_fake_window_create();
BS_TEST_VERIFY_NEQ_OR_RETURN(test_ptr, NULL, fw_ptr);
wlmtk_util_connect_listener_signal(
&wlmtk_window_events(fw_ptr->window_ptr)->state_changed,
&listener,
_wlmtk_window_test_handle_state_changed);

BS_TEST_VERIFY_FALSE(test_ptr, wlmtk_window_is_shaded(fw_ptr->window_ptr));
_wlmtk_window_test_handle_state_changed_called = false;

// Shading only works on server-side-decorated windows.
wlmtk_window_set_server_side_decorated(fw_ptr->window_ptr, true);

wlmtk_window_request_shaded(fw_ptr->window_ptr, true);
BS_TEST_VERIFY_TRUE(test_ptr, wlmtk_window_is_shaded(fw_ptr->window_ptr));
BS_TEST_VERIFY_FALSE(
test_ptr,
wlmtk_content_element(&fw_ptr->fake_content_ptr->content)->visible);
BS_TEST_VERIFY_TRUE(test_ptr, _wlmtk_window_test_handle_state_changed_called);
_wlmtk_window_test_handle_state_changed_called = false;

wlmtk_window_request_shaded(fw_ptr->window_ptr, false);
BS_TEST_VERIFY_FALSE(test_ptr, wlmtk_window_is_shaded(fw_ptr->window_ptr));
BS_TEST_VERIFY_TRUE(test_ptr, _wlmtk_window_test_handle_state_changed_called);
_wlmtk_window_test_handle_state_changed_called = false;

// Shading not supported on client-side decoration. Must be disabled.
wlmtk_window_request_shaded(fw_ptr->window_ptr, true);
BS_TEST_VERIFY_TRUE(test_ptr, wlmtk_window_is_shaded(fw_ptr->window_ptr));
BS_TEST_VERIFY_TRUE(test_ptr, _wlmtk_window_test_handle_state_changed_called);
_wlmtk_window_test_handle_state_changed_called = false;

wlmtk_window_set_server_side_decorated(fw_ptr->window_ptr, false);
BS_TEST_VERIFY_FALSE(test_ptr, wlmtk_window_is_shaded(fw_ptr->window_ptr));
BS_TEST_VERIFY_TRUE(test_ptr, _wlmtk_window_test_handle_state_changed_called);
_wlmtk_window_test_handle_state_changed_called = false;

// Verify that 'shading' on client decorations does not do anything.
wlmtk_window_set_server_side_decorated(fw_ptr->window_ptr, false);
wlmtk_window_request_shaded(fw_ptr->window_ptr, true);
BS_TEST_VERIFY_FALSE(test_ptr, wlmtk_window_is_shaded(fw_ptr->window_ptr));
BS_TEST_VERIFY_FALSE(test_ptr, _wlmtk_window_test_handle_state_changed_called);

wlmtk_util_disconnect_listener(&listener);
wlmtk_fake_window_destroy(fw_ptr);
}

/* ------------------------------------------------------------------------- */
/** Tests fake window ctor and dtor. */
void test_fake(bs_test_t *test_ptr)
Expand All @@ -1665,4 +1774,13 @@ void test_fake(bs_test_t *test_ptr)
wlmtk_fake_window_destroy(fake_window_ptr);
}

/* ------------------------------------------------------------------------- */
/** Reports a state change. */
void _wlmtk_window_test_handle_state_changed(
__UNUSED__ struct wl_listener *listener_ptr,
__UNUSED__ void *data_ptr)
{
_wlmtk_window_test_handle_state_changed_called = true;
}

/* == End of window.c ====================================================== */
24 changes: 24 additions & 0 deletions src/toolkit/window.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,19 @@ typedef struct _wlmtk_window_t wlmtk_window_t;
extern "C" {
#endif // __cplusplus

/** Signals available for the @ref wlmtk_window_t class. */
typedef struct {
/**
* Signals that the window state (maximize, iconify, ...) changed.
*
* Window state can be retrieved from:
* - @ref wlmtk_window_is_maximized
* - @ref wlmtk_window_is_fullscreen
* - @ref wlmtk_window_is_shaded
*/
struct wl_signal state_changed;
} wlmtk_window_events_t;

/** Style options for the window. */
typedef struct {
/** The titlebar's style. */
Expand Down Expand Up @@ -85,6 +98,16 @@ wlmtk_window_t *wlmtk_window_create(
const wlmtk_menu_style_t *menu_style_ptr,
wlmtk_env_t *env_ptr);

/**
* Gets the set of events available to a window, for binding listeners.
*
* @param window_ptr
*
* @return Pointer to this window's @ref wlmtk_window_t::events.
*/
wlmtk_window_events_t *wlmtk_window_events(
wlmtk_window_t *window_ptr);

/**
* Destroys the window.
*
Expand Down Expand Up @@ -327,6 +350,7 @@ bool wlmtk_window_is_fullscreen(wlmtk_window_t *window_ptr);
* Requests the window to be "shaded", ie. rolled-up to just the title bar.
*
* This is supported only for server-side decorated windows.
*
* @param window_ptr
* @param shaded
*/
Expand Down

0 comments on commit 6dfb0ca

Please sign in to comment.