forked from 12to11/12to11

* 12to11-test.xml (test_manager) <error>: New error `resize_rejected'. <resize_edge>: New enum. (test_surface) <move_resize, resize_finished>: New events and requests. * compositor.h: Update prototypes. * seat.c (CancelResizeOperation): Fix dangling pointer. (FakePointerEdge): (HandlePointerEdge): Always use builtin resize on test seats. (XLMoveToplevel): Return status code. * test.c (struct _TestSurface): New field `resize_callbacks'. (HandleResizeDone): New function. (DestroyBacking): Free resize callbacks. (GetResizeDimensions, MoveResize): New functions. (test_surface_impl, GetTestSurface): Attach new role hooks etc. * tests/seat_test.c (enum test_expect_event_kind): New event kind. (struct test_recorded_button_event): Add serial field. Set it. (struct test_recorded_resize_finished_event): New struct. (enum test_kind): New test. (test_names): Name that test. (LAST_TEST): Set it to the new test. (run_resize_test, test_single_step): Implement the new test. (expect_button_event): Return the serial of the button event. (expect_surface_enter, handle_test_surface_resize_finished) (test_surface_listener, handle_pointer_button) (handle_keyboard_enter): Adjust for changes to event handling.
803 lines
20 KiB
C
803 lines
20 KiB
C
/* Wayland compositor running on top of an X server.
|
||
|
||
Copyright (C) 2022 to various contributors.
|
||
|
||
This file is part of 12to11.
|
||
|
||
12to11 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 3 of the License, or (at your
|
||
option) any later version.
|
||
|
||
12to11 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 12to11. If not, see <https://www.gnu.org/licenses/>. */
|
||
|
||
#include <string.h>
|
||
|
||
#include "compositor.h"
|
||
#include "12to11-test.h"
|
||
|
||
#define TestSurfaceFromRole(role) ((TestSurface *) (role))
|
||
|
||
#define DefaultEventMask \
|
||
(ExposureMask | StructureNotifyMask | PropertyChangeMask)
|
||
|
||
enum
|
||
{
|
||
IsSurfaceMapped = 1,
|
||
PendingBufferRelease = 1 << 1,
|
||
PendingFrameCallback = 1 << 2,
|
||
};
|
||
|
||
typedef struct _TestSurface TestSurface;
|
||
|
||
struct _TestSurface
|
||
{
|
||
/* The associated role. */
|
||
Role role;
|
||
|
||
/* The associated subcompositor. */
|
||
Subcompositor *subcompositor;
|
||
|
||
/* The associated buffer release helper. */
|
||
BufferReleaseHelper *release_helper;
|
||
|
||
/* List of live resize callbacks. */
|
||
XLList *resize_callbacks;
|
||
|
||
/* The associated window. */
|
||
Window window;
|
||
|
||
/* The associated rendering target. */
|
||
RenderTarget target;
|
||
|
||
/* The number of references to this test surface, and flags. */
|
||
int refcount, flags;
|
||
|
||
/* The last known width and height. */
|
||
int bounds_width, bounds_height;
|
||
};
|
||
|
||
/* The locked output scale. N.B. that a test_scale_lock is not an
|
||
actual resource, and just represents the state of this
|
||
variable. */
|
||
int locked_output_scale;
|
||
|
||
/* The test surface manager global. */
|
||
static struct wl_global *test_manager_global;
|
||
|
||
/* Hash table of all surfaces. */
|
||
static XLAssocTable *surfaces;
|
||
|
||
static void
|
||
HandleResizeDone (void *key, void *data)
|
||
{
|
||
TestSurface *test;
|
||
|
||
test = data;
|
||
test->resize_callbacks
|
||
= XLListRemove (test->resize_callbacks, key);
|
||
|
||
if (test->role.resource)
|
||
test_surface_send_resize_finished (test->role.resource);
|
||
}
|
||
|
||
static void
|
||
DestroyBacking (TestSurface *test)
|
||
{
|
||
if (--test->refcount)
|
||
return;
|
||
|
||
/* Release all allocated resources. */
|
||
RenderDestroyRenderTarget (test->target);
|
||
XDestroyWindow (compositor.display, test->window);
|
||
|
||
/* And the buffer release helper. */
|
||
FreeBufferReleaseHelper (test->release_helper);
|
||
|
||
/* Delete the association. */
|
||
XLDeleteAssoc (surfaces, test->window);
|
||
|
||
/* Free the subcompositor. */
|
||
SubcompositorFree (test->subcompositor);
|
||
|
||
/* Free all resize callbacks. */
|
||
XLListFree (test->resize_callbacks,
|
||
XLSeatCancelResizeCallback);
|
||
|
||
/* And since there are no C level references to the role anymore, it
|
||
can be freed. */
|
||
XLFree (test);
|
||
}
|
||
|
||
static void
|
||
RunFrameCallbacks (TestSurface *test)
|
||
{
|
||
struct timespec time;
|
||
|
||
clock_gettime (CLOCK_MONOTONIC, &time);
|
||
XLSurfaceRunFrameCallbacks (test->role.surface, time);
|
||
|
||
test->flags &= ~PendingFrameCallback;
|
||
}
|
||
|
||
static void
|
||
RunFrameCallbacksConditionally (TestSurface *test)
|
||
{
|
||
if (!test->role.surface)
|
||
return;
|
||
|
||
if (test->flags & PendingBufferRelease)
|
||
/* Wait for all buffers to be released first. */
|
||
test->flags |= PendingFrameCallback;
|
||
else
|
||
RunFrameCallbacks (test);
|
||
}
|
||
|
||
static void
|
||
AllBuffersReleased (void *data)
|
||
{
|
||
TestSurface *test;
|
||
|
||
test = data;
|
||
|
||
if (!test->role.surface)
|
||
return;
|
||
|
||
test->flags &= ~PendingBufferRelease;
|
||
|
||
/* Run pending frame callbacks. */
|
||
if (test->flags & PendingFrameCallback)
|
||
RunFrameCallbacks (test);
|
||
}
|
||
|
||
static void
|
||
NoteBounds (void *data, int min_x, int min_y, int max_x, int max_y)
|
||
{
|
||
TestSurface *test;
|
||
int bounds_width, bounds_height;
|
||
|
||
test = data;
|
||
|
||
/* Avoid resizing the window should its actual size not have
|
||
changed. */
|
||
|
||
bounds_width = max_x - min_x + 1;
|
||
bounds_height = max_y - min_y + 1;
|
||
|
||
if (test->bounds_width != bounds_width
|
||
|| test->bounds_height != bounds_height)
|
||
{
|
||
/* Resize the window to fit. */
|
||
XResizeWindow (compositor.display, test->window,
|
||
bounds_width, bounds_height);
|
||
/* Sync with the X server. */
|
||
XSync (compositor.display, False);
|
||
|
||
test->bounds_width = bounds_width;
|
||
test->bounds_height = bounds_height;
|
||
}
|
||
}
|
||
|
||
static void
|
||
NoteFrame (FrameMode mode, uint64_t id, void *data,
|
||
uint64_t msc, uint64_t ust)
|
||
{
|
||
if (mode != ModeComplete && mode != ModePresented)
|
||
return;
|
||
|
||
/* Run the frame callbacks. With the test surface, this also serves
|
||
to mean that painting has completed. */
|
||
RunFrameCallbacksConditionally (data);
|
||
}
|
||
|
||
static void
|
||
MapTestSurface (TestSurface *test)
|
||
{
|
||
/* Set the bounds width and height. */
|
||
test->bounds_width = SubcompositorWidth (test->subcompositor);
|
||
test->bounds_height = SubcompositorHeight (test->subcompositor);
|
||
|
||
/* First, resize the window to the current bounds. */
|
||
XResizeWindow (compositor.display, test->window,
|
||
test->bounds_width, test->bounds_height);
|
||
|
||
/* Next, map the window and raise it. Wait for a subsequent
|
||
MapNotify before sending the map event. */
|
||
XMapRaised (compositor.display, test->window);
|
||
|
||
/* And say that the window is now mapped. */
|
||
test->flags |= IsSurfaceMapped;
|
||
}
|
||
|
||
static void
|
||
UnmapTestSurface (TestSurface *test)
|
||
{
|
||
if (test->flags & IsSurfaceMapped)
|
||
/* Unmap the surface. */
|
||
XUnmapWindow (compositor.display, test->window);
|
||
}
|
||
|
||
static void
|
||
Commit (Surface *surface, Role *role)
|
||
{
|
||
TestSurface *test;
|
||
|
||
test = TestSurfaceFromRole (role);
|
||
|
||
if (surface->current_state.buffer
|
||
&& !(test->flags & IsSurfaceMapped))
|
||
/* Map the surface now. */
|
||
MapTestSurface (test);
|
||
else if (!surface->current_state.buffer)
|
||
{
|
||
/* Unmap the surface now. */
|
||
UnmapTestSurface (test);
|
||
|
||
/* Run frame callbacks if necessary. */
|
||
RunFrameCallbacksConditionally (test);
|
||
}
|
||
|
||
/* Finally, do a subcompositor update if the surface is now
|
||
mapped. */
|
||
if (test->flags & IsSurfaceMapped)
|
||
SubcompositorUpdate (test->subcompositor);
|
||
|
||
/* And send the presentation hint. */
|
||
if (test->role.resource)
|
||
test_surface_send_committed (test->role.resource,
|
||
surface->current_state.presentation_hint);
|
||
}
|
||
|
||
static Bool
|
||
Setup (Surface *surface, Role *role)
|
||
{
|
||
TestSurface *test;
|
||
|
||
test = TestSurfaceFromRole (role);
|
||
|
||
/* Set role->surface here, since this is where the refcounting is
|
||
done as well. */
|
||
role->surface = surface;
|
||
|
||
/* Prevent the surface from ever holding another kind of role. */
|
||
surface->role_type = TestSurfaceType;
|
||
|
||
/* Attach the views to the subcompositor. */
|
||
ViewSetSubcompositor (surface->view, test->subcompositor);
|
||
ViewSetSubcompositor (surface->under, test->subcompositor);
|
||
|
||
/* Make sure the under view ends up beneath surface->view. */
|
||
SubcompositorInsert (test->subcompositor, surface->under);
|
||
SubcompositorInsert (test->subcompositor, surface->view);
|
||
|
||
/* Retain the backing data. */
|
||
test->refcount++;
|
||
return True;
|
||
}
|
||
|
||
static void
|
||
Teardown (Surface *surface, Role *role)
|
||
{
|
||
TestSurface *test;
|
||
|
||
/* Clear role->surface here, since this is where the refcounting is
|
||
done as well. */
|
||
role->surface = NULL;
|
||
|
||
test = TestSurfaceFromRole (role);
|
||
|
||
/* Unparent the surface's views as well. */
|
||
ViewUnparent (surface->view);
|
||
ViewUnparent (surface->under);
|
||
|
||
/* Detach the surface's views from the subcompositor. */
|
||
ViewSetSubcompositor (surface->view, NULL);
|
||
ViewSetSubcompositor (surface->under, NULL);
|
||
|
||
/* Release the backing data. */
|
||
DestroyBacking (test);
|
||
}
|
||
|
||
static void
|
||
Destroy (struct wl_client *client, struct wl_resource *resource)
|
||
{
|
||
TestSurface *test;
|
||
|
||
test = wl_resource_get_user_data (resource);
|
||
|
||
/* Now detach the role from its surface, which can be reused in the
|
||
future. */
|
||
if (test->role.surface)
|
||
XLSurfaceReleaseRole (test->role.surface, &test->role);
|
||
|
||
/* And destroy the resource. */
|
||
wl_resource_destroy (resource);
|
||
}
|
||
|
||
static void
|
||
ReleaseBuffer (Surface *surface, Role *role, ExtBuffer *buffer)
|
||
{
|
||
TestSurface *test;
|
||
RenderBuffer render_buffer;
|
||
|
||
test = TestSurfaceFromRole (role);
|
||
render_buffer = XLRenderBufferFromBuffer (buffer);
|
||
|
||
if (RenderIsBufferIdle (render_buffer, test->target))
|
||
/* Release the buffer now -- it is already idle. */
|
||
XLReleaseBuffer (buffer);
|
||
else
|
||
{
|
||
/* Release the buffer once it becomes idle, or is destroyed. */
|
||
ReleaseBufferWithHelper (test->release_helper, buffer,
|
||
test->target);
|
||
|
||
/* Mark the surface as pending buffer release, so frame
|
||
callbacks can be deferred until all buffers are released. */
|
||
test->flags |= PendingBufferRelease;
|
||
}
|
||
}
|
||
|
||
static void
|
||
SubsurfaceUpdate (Surface *surface, Role *role)
|
||
{
|
||
TestSurface *test;
|
||
|
||
test = TestSurfaceFromRole (role);
|
||
SubcompositorUpdate (test->subcompositor);
|
||
}
|
||
|
||
static Window
|
||
GetWindow (Surface *surface, Role *role)
|
||
{
|
||
TestSurface *test;
|
||
|
||
test = TestSurfaceFromRole (role);
|
||
return test->window;
|
||
}
|
||
|
||
static void
|
||
Activate (Surface *surface, Role *role, int deviceid,
|
||
Timestamp timestamp, Surface *activator_surface)
|
||
{
|
||
struct wl_resource *resource;
|
||
TestSurface *test;
|
||
|
||
test = TestSurfaceFromRole (role);
|
||
|
||
if (test->role.resource)
|
||
{
|
||
/* If the activator surface belongs to the same client as the
|
||
client who created the test surface, set the resource to the
|
||
activator surface. */
|
||
if (wl_resource_get_client (activator_surface->resource)
|
||
== wl_resource_get_client (test->role.resource))
|
||
resource = activator_surface->resource;
|
||
else
|
||
resource = NULL;
|
||
|
||
test_surface_send_activated (test->role.resource,
|
||
timestamp.months,
|
||
timestamp.milliseconds,
|
||
resource);
|
||
}
|
||
}
|
||
|
||
static void
|
||
GetResizeDimensions (Surface *surface, Role *role, int *width,
|
||
int *height)
|
||
{
|
||
TestSurface *test;
|
||
|
||
test = TestSurfaceFromRole (role);
|
||
|
||
*width = SubcompositorWidth (test->subcompositor);
|
||
*height = SubcompositorHeight (test->subcompositor);
|
||
}
|
||
|
||
static void
|
||
SetAlwaysGarbage (struct wl_client *client, struct wl_resource *resource)
|
||
{
|
||
TestSurface *test;
|
||
|
||
test = wl_resource_get_user_data (resource);
|
||
|
||
/* Make the subcompositor always garbaged. */
|
||
SubcompositorSetAlwaysGarbaged (test->subcompositor);
|
||
}
|
||
|
||
static void
|
||
MoveResize (struct wl_client *client, struct wl_resource *resource,
|
||
uint32_t edge, uint32_t serial,
|
||
struct wl_resource *seat_resource)
|
||
{
|
||
TestSurface *test;
|
||
Seat *seat;
|
||
void *key;
|
||
|
||
test = wl_resource_get_user_data (resource);
|
||
seat = wl_resource_get_user_data (seat_resource);
|
||
|
||
if (!test->role.surface)
|
||
{
|
||
wl_resource_post_error (resource, TEST_MANAGER_ERROR_RESIZE_REJECTED,
|
||
"trying to resize test surface without surface");
|
||
return;
|
||
}
|
||
|
||
if (edge == TEST_MANAGER_RESIZE_EDGE_MOVE)
|
||
{
|
||
if (!XLMoveToplevel (seat, test->role.surface, serial))
|
||
wl_resource_post_error (resource, TEST_MANAGER_ERROR_RESIZE_REJECTED,
|
||
"move rejected for unspecified reason");
|
||
}
|
||
else if (!XLResizeToplevel (seat, test->role.surface, serial, edge))
|
||
wl_resource_post_error (resource, TEST_MANAGER_ERROR_RESIZE_REJECTED,
|
||
"resize rejected for unspecified reason");
|
||
|
||
/* Now attach a resize complete callback. */
|
||
key = XLSeatRunAfterResize (seat, HandleResizeDone, test);
|
||
test->resize_callbacks = XLListPrepend (test->resize_callbacks,
|
||
key);
|
||
}
|
||
|
||
static const struct test_surface_interface test_surface_impl =
|
||
{
|
||
.destroy = Destroy,
|
||
.set_always_garbage = SetAlwaysGarbage,
|
||
.move_resize = MoveResize,
|
||
};
|
||
|
||
static void
|
||
HandleResourceDestroy (struct wl_resource *resource)
|
||
{
|
||
TestSurface *test;
|
||
|
||
test = wl_resource_get_user_data (resource);
|
||
test->role.resource = NULL;
|
||
|
||
/* Dereference the backing data. */
|
||
DestroyBacking (test);
|
||
}
|
||
|
||
|
||
|
||
static void
|
||
DestroyScaleLock (struct wl_client *client, struct wl_resource *resource)
|
||
{
|
||
wl_resource_destroy (resource);
|
||
}
|
||
|
||
static void
|
||
SetScale (struct wl_client *client, struct wl_resource *resource,
|
||
uint32_t scale)
|
||
{
|
||
/* If the scale is invalid, reject it. */
|
||
if (!scale)
|
||
{
|
||
wl_resource_post_error (resource, TEST_MANAGER_ERROR_INVALID_SCALE,
|
||
"scale of 0 specified");
|
||
return;
|
||
}
|
||
|
||
/* Set the scale. As there can only be one lock at any given
|
||
time, there is no need to check the resource data. */
|
||
locked_output_scale = scale;
|
||
XLOutputHandleScaleChange (scale);
|
||
}
|
||
|
||
static const struct test_scale_lock_interface scale_lock_impl =
|
||
{
|
||
.destroy = DestroyScaleLock,
|
||
.set_scale = SetScale,
|
||
};
|
||
|
||
static void
|
||
HandleScaleLockResourceDestroy (struct wl_resource *resource)
|
||
{
|
||
/* There is no resource data associated with scale locks. Just
|
||
unlock the scale. */
|
||
locked_output_scale = 0;
|
||
XLOutputHandleScaleChange (-1);
|
||
}
|
||
|
||
static void
|
||
GetTestSurface (struct wl_client *client, struct wl_resource *resource,
|
||
uint32_t id, struct wl_resource *surface_resource)
|
||
{
|
||
Surface *surface;
|
||
TestSurface *test;
|
||
XSetWindowAttributes attrs;
|
||
unsigned long flags;
|
||
|
||
surface = wl_resource_get_user_data (surface_resource);
|
||
|
||
if (surface->role_type != AnythingType
|
||
&& surface->role_type != TestSurfaceType)
|
||
{
|
||
/* The client is trying to create a test surface for a surface
|
||
that has or had some other role. */
|
||
wl_resource_post_error (resource, TEST_MANAGER_ERROR_ROLE_PRESENT,
|
||
"a role is/was already present on the given surface");
|
||
return;
|
||
}
|
||
|
||
test = XLSafeMalloc (sizeof *test);
|
||
|
||
if (!test)
|
||
{
|
||
wl_resource_post_no_memory (resource);
|
||
return;
|
||
}
|
||
|
||
memset (test, 0, sizeof *test);
|
||
|
||
/* Now create the associated resource. */
|
||
test->role.resource
|
||
= wl_resource_create (client, &test_surface_interface,
|
||
wl_resource_get_version (resource),
|
||
id);
|
||
if (!test->role.resource)
|
||
{
|
||
wl_resource_post_no_memory (resource);
|
||
XLFree (test);
|
||
|
||
return;
|
||
}
|
||
|
||
/* Create the window. */
|
||
attrs.colormap = compositor.colormap;
|
||
attrs.border_pixel = border_pixel;
|
||
attrs.event_mask = DefaultEventMask;
|
||
attrs.cursor = InitDefaultCursor ();
|
||
attrs.override_redirect = True;
|
||
flags = (CWColormap | CWBorderPixel | CWEventMask
|
||
| CWCursor | CWOverrideRedirect);
|
||
|
||
test->window = XCreateWindow (compositor.display,
|
||
DefaultRootWindow (compositor.display),
|
||
0, 0, 20, 20, 0, compositor.n_planes,
|
||
InputOutput, compositor.visual, flags,
|
||
&attrs);
|
||
|
||
/* And the subcompositor and rendering target. */
|
||
test->subcompositor = MakeSubcompositor ();
|
||
test->target = RenderTargetFromWindow (test->window, DefaultEventMask);
|
||
|
||
/* Set the client. */
|
||
RenderSetClient (test->target, client);
|
||
|
||
/* And a buffer release helper. */
|
||
test->release_helper = MakeBufferReleaseHelper (AllBuffersReleased,
|
||
test);
|
||
|
||
/* Set the subcompositor target. */
|
||
SubcompositorSetTarget (test->subcompositor, &test->target);
|
||
|
||
/* Set some callbacks. The note frame callback is not useful as
|
||
test surfaces have no frame clock. */
|
||
SubcompositorSetBoundsCallback (test->subcompositor, NoteBounds, test);
|
||
SubcompositorSetNoteFrameCallback (test->subcompositor, NoteFrame,
|
||
test);
|
||
|
||
/* Create the hash table used to look up test surfaces if
|
||
necessary. */
|
||
|
||
if (!surfaces)
|
||
surfaces = XLCreateAssocTable (16);
|
||
|
||
/* Associate the window with the role. */
|
||
XLMakeAssoc (surfaces, test->window, test);
|
||
|
||
/* Set the role implementation. */
|
||
test->role.funcs.commit = Commit;
|
||
test->role.funcs.teardown = Teardown;
|
||
test->role.funcs.setup = Setup;
|
||
test->role.funcs.release_buffer = ReleaseBuffer;
|
||
test->role.funcs.subsurface_update = SubsurfaceUpdate;
|
||
test->role.funcs.get_window = GetWindow;
|
||
test->role.funcs.activate = Activate;
|
||
test->role.funcs.get_resize_dimensions = GetResizeDimensions;
|
||
|
||
/* Add the resource implementation. */
|
||
wl_resource_set_implementation (test->role.resource, &test_surface_impl,
|
||
test, HandleResourceDestroy);
|
||
test->refcount++;
|
||
|
||
/* Attach the role. */
|
||
if (!XLSurfaceAttachRole (surface, &test->role))
|
||
abort ();
|
||
}
|
||
|
||
static void
|
||
GetScaleLock (struct wl_client *client, struct wl_resource *resource,
|
||
uint32_t id, uint32_t scale)
|
||
{
|
||
struct wl_resource *lock_resource;
|
||
|
||
if (!scale)
|
||
{
|
||
wl_resource_post_error (resource, TEST_MANAGER_ERROR_INVALID_SCALE,
|
||
"scale of 0 specified");
|
||
return;
|
||
}
|
||
|
||
if (locked_output_scale)
|
||
{
|
||
/* The scale is already locked, so don't create another
|
||
lock. */
|
||
wl_resource_post_error (resource, TEST_MANAGER_ERROR_SCALE_LOCK_EXISTS,
|
||
"a scale lock already exists (another test is"
|
||
" already running?)");
|
||
return;
|
||
}
|
||
|
||
lock_resource = wl_resource_create (client, &test_scale_lock_interface,
|
||
wl_resource_get_version (resource),
|
||
id);
|
||
|
||
if (!lock_resource)
|
||
{
|
||
wl_resource_post_no_memory (resource);
|
||
return;
|
||
}
|
||
|
||
/* Now, set the locked scale. */
|
||
locked_output_scale = scale;
|
||
|
||
/* And update the global scale factor if need be. */
|
||
if (scale != global_scale_factor)
|
||
XLOutputHandleScaleChange (scale);
|
||
|
||
/* And resource implementation. */
|
||
wl_resource_set_implementation (lock_resource, &scale_lock_impl,
|
||
NULL, HandleScaleLockResourceDestroy);
|
||
}
|
||
|
||
static void
|
||
GetTestSeat (struct wl_client *client, struct wl_resource *resource,
|
||
uint32_t id)
|
||
{
|
||
XLGetTestSeat (client, resource, id);
|
||
}
|
||
|
||
static void
|
||
GetSerial (struct wl_client *client, struct wl_resource *resource)
|
||
{
|
||
uint32_t serial;
|
||
|
||
/* Send the display's next serial to the client. */
|
||
serial = wl_display_next_serial (compositor.wl_display);
|
||
test_manager_send_serial (resource, serial);
|
||
}
|
||
|
||
static void
|
||
SetBufferLabel (struct wl_client *client, struct wl_resource *resource,
|
||
struct wl_resource *buffer_resource, const char *label)
|
||
{
|
||
ExtBuffer *buffer;
|
||
|
||
buffer = wl_resource_get_user_data (buffer_resource);
|
||
|
||
XLFree (buffer->label);
|
||
buffer->label = XLStrdup (label);
|
||
}
|
||
|
||
static const struct test_manager_interface test_manager_impl =
|
||
{
|
||
.get_test_surface = GetTestSurface,
|
||
.get_scale_lock = GetScaleLock,
|
||
.get_test_seat = GetTestSeat,
|
||
.get_serial = GetSerial,
|
||
.set_buffer_label = SetBufferLabel,
|
||
};
|
||
|
||
|
||
|
||
static void
|
||
HandleBind (struct wl_client *client, void *data, uint32_t version,
|
||
uint32_t id)
|
||
{
|
||
struct wl_resource *resource;
|
||
char *name;
|
||
|
||
resource = wl_resource_create (client, &test_manager_interface,
|
||
version, id);
|
||
|
||
if (!resource)
|
||
{
|
||
wl_client_post_no_memory (client);
|
||
return;
|
||
}
|
||
|
||
wl_resource_set_implementation (resource, &test_manager_impl,
|
||
NULL, NULL);
|
||
|
||
/* Send the display name to the client. */
|
||
name = DisplayString (compositor.display);
|
||
test_manager_send_display_string (resource, name);
|
||
}
|
||
|
||
void
|
||
XLInitTest (void)
|
||
{
|
||
test_manager_global
|
||
= wl_global_create (compositor.wl_display, &test_manager_interface,
|
||
1, NULL, HandleBind);
|
||
}
|
||
|
||
static Bool
|
||
DispatchMapNotify (XEvent *event)
|
||
{
|
||
TestSurface *test;
|
||
|
||
/* Try to look up the surface. */
|
||
test = XLLookUpAssoc (surfaces, event->xmap.window);
|
||
|
||
if (!test)
|
||
return False;
|
||
|
||
/* The surface is now mapped. Dispatch the mapped event. */
|
||
if (test->flags & IsSurfaceMapped && test->role.resource)
|
||
test_surface_send_mapped (test->role.resource, test->window,
|
||
DisplayString (compositor.display));
|
||
return True;
|
||
}
|
||
|
||
static Bool
|
||
DispatchExpose (XEvent *event)
|
||
{
|
||
TestSurface *test;
|
||
|
||
/* Try to look up the surface. */
|
||
test = XLLookUpAssoc (surfaces, event->xexpose.window);
|
||
|
||
if (!test)
|
||
return False;
|
||
|
||
/* Expose the subcompositor. */
|
||
SubcompositorExpose (test->subcompositor, event);
|
||
|
||
return True;
|
||
}
|
||
|
||
Bool
|
||
XLHandleOneXEventForTest (XEvent *event)
|
||
{
|
||
if (!surfaces)
|
||
return False;
|
||
|
||
switch (event->type)
|
||
{
|
||
case MapNotify:
|
||
return DispatchMapNotify (event);
|
||
|
||
case Expose:
|
||
return DispatchExpose (event);
|
||
}
|
||
|
||
return False;
|
||
}
|
||
|
||
Surface *
|
||
XLLookUpTestSurface (Window window, Subcompositor **subcompositor)
|
||
{
|
||
TestSurface *test;
|
||
|
||
if (!surfaces)
|
||
return NULL;
|
||
|
||
test = XLLookUpAssoc (surfaces, window);
|
||
|
||
if (!test)
|
||
return NULL;
|
||
|
||
*subcompositor = test->subcompositor;
|
||
return test->role.surface;
|
||
}
|