forked from 12to11/12to11
Check in C and XML files for tests
* 12to11-test.xml: * buffer_release.c: * test.c: New files.
This commit is contained in:
parent
0a68122e52
commit
4e9a442856
3 changed files with 774 additions and 0 deletions
94
12to11-test.xml
Normal file
94
12to11-test.xml
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<protocol name="test">
|
||||||
|
<copyright>
|
||||||
|
Copyright (C) 2022 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/.
|
||||||
|
</copyright>
|
||||||
|
|
||||||
|
<interface name="test_manager" version="1">
|
||||||
|
<description summary="test interface">
|
||||||
|
This protocol is used by the 12to11 protocol translator to
|
||||||
|
support various tests. The test_manager global allows creating
|
||||||
|
a surface whose bounds and contents can be inspected, and
|
||||||
|
connecting to the X server used by the compositor.
|
||||||
|
|
||||||
|
Upon binding to the test_manager, a display_string event is sent
|
||||||
|
containing the name of the X display.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<enum name="error">
|
||||||
|
<entry name="role_present" value="1"
|
||||||
|
summary="given wl_surface has/had another role"/>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<request name="get_test_surface">
|
||||||
|
<description summary="obtain test surface role">
|
||||||
|
Get a test_surface object for a particular surface. If a role
|
||||||
|
was already attached to this surface, or a role of a different
|
||||||
|
type was previously attached, a role_present error is issued.
|
||||||
|
|
||||||
|
The window is created immediately after get_test_surface is
|
||||||
|
called. It is mapped once a commit request with a non-nil
|
||||||
|
buffer is made.
|
||||||
|
|
||||||
|
Once the window associated with the test_surface object is
|
||||||
|
mapped, a mapped event is sent.
|
||||||
|
</description>
|
||||||
|
<arg name="id" type="new_id" interface="test_surface"/>
|
||||||
|
<arg name="surface" type="object" interface="wl_surface"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<event name="display_string">
|
||||||
|
<description summary="X server name">
|
||||||
|
The display_string event sends the name of the X display to
|
||||||
|
clients. It is sent immediately after binding to the
|
||||||
|
test_manager object.
|
||||||
|
</description>
|
||||||
|
<arg name="name" type="string"/>
|
||||||
|
</event>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
<interface name="test_surface" version="1">
|
||||||
|
<description summary="test surface">
|
||||||
|
This role provides a test surface. Various buffers and
|
||||||
|
subsurfaces can be attached, and the resulting display contents
|
||||||
|
validated.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor">
|
||||||
|
<description summary="destroy role">
|
||||||
|
This request destroys the test_surface role. Subsequently,
|
||||||
|
get_test_surface can be called again with its surface.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<event name="mapped">
|
||||||
|
<description summary="role initialized">
|
||||||
|
The map event is sent once the window is mapped and its
|
||||||
|
contents can be retrieved. The two arguments are the XID of
|
||||||
|
the window and the name of the display it is on.
|
||||||
|
|
||||||
|
If the surface is mapped, then unmapped (by having a nil
|
||||||
|
buffer attached) and then mapped again, without waiting for
|
||||||
|
the first mapped event, the delivery of subsequent mapped
|
||||||
|
events becomes undefined.
|
||||||
|
</description>
|
||||||
|
<arg name="xid" type="uint"/>
|
||||||
|
<arg name="display_string" type="string"/>
|
||||||
|
</event>
|
||||||
|
</interface>
|
||||||
|
</protocol>
|
146
buffer_release.c
Normal file
146
buffer_release.c
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
/* 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 "compositor.h"
|
||||||
|
|
||||||
|
/* Simple helper code for managing buffer release in surfaces. */
|
||||||
|
|
||||||
|
typedef struct _ReleaseLaterRecord ReleaseLaterRecord;
|
||||||
|
|
||||||
|
struct _ReleaseLaterRecord
|
||||||
|
{
|
||||||
|
/* A monotonically (overflow aside) increasing identifier. */
|
||||||
|
uint64_t id;
|
||||||
|
|
||||||
|
/* The buffer that should be released upon receiving this
|
||||||
|
message. */
|
||||||
|
ExtBuffer *buffer;
|
||||||
|
|
||||||
|
/* The idle callback, if any. */
|
||||||
|
IdleCallbackKey key;
|
||||||
|
|
||||||
|
/* The buffer release helper. */
|
||||||
|
BufferReleaseHelper *helper;
|
||||||
|
|
||||||
|
/* The next and last records. */
|
||||||
|
ReleaseLaterRecord *next, *last;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _BufferReleaseHelper
|
||||||
|
{
|
||||||
|
/* Queue of buffers pending release. */
|
||||||
|
ReleaseLaterRecord records;
|
||||||
|
|
||||||
|
/* Callback run upon all buffers being released. */
|
||||||
|
AllReleasedCallback callback;
|
||||||
|
|
||||||
|
/* Data for that callback. */
|
||||||
|
void *callback_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
BufferReleaseHelper *
|
||||||
|
MakeBufferReleaseHelper (AllReleasedCallback callback,
|
||||||
|
void *callback_data)
|
||||||
|
{
|
||||||
|
BufferReleaseHelper *helper;
|
||||||
|
|
||||||
|
helper = XLCalloc (1, sizeof *helper);
|
||||||
|
helper->records.next = &helper->records;
|
||||||
|
helper->records.last = &helper->records;
|
||||||
|
helper->callback = callback;
|
||||||
|
helper->callback_data = callback_data;
|
||||||
|
|
||||||
|
return helper;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
FreeBufferReleaseHelper (BufferReleaseHelper *helper)
|
||||||
|
{
|
||||||
|
ReleaseLaterRecord *next, *last;
|
||||||
|
|
||||||
|
/* Do an XSync, and then release all the records. */
|
||||||
|
XSync (compositor.display, False);
|
||||||
|
|
||||||
|
next = helper->records.next;
|
||||||
|
while (next != &helper->records)
|
||||||
|
{
|
||||||
|
last = next;
|
||||||
|
next = next->next;
|
||||||
|
|
||||||
|
/* Cancel the idle callback if it already exists. */
|
||||||
|
if (last->key)
|
||||||
|
RenderCancelIdleCallback (last->key);
|
||||||
|
|
||||||
|
/* Release the buffer now. */
|
||||||
|
XLReleaseBuffer (last->buffer);
|
||||||
|
|
||||||
|
/* Before freeing the record itself. */
|
||||||
|
XLFree (last);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free the helper. */
|
||||||
|
XLFree (helper);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
BufferIdleCallback (RenderBuffer buffer, void *data)
|
||||||
|
{
|
||||||
|
ReleaseLaterRecord *record;
|
||||||
|
BufferReleaseHelper *helper;
|
||||||
|
|
||||||
|
record = data;
|
||||||
|
helper = record->helper;
|
||||||
|
|
||||||
|
/* Release the buffer. */
|
||||||
|
XLReleaseBuffer (record->buffer);
|
||||||
|
|
||||||
|
/* Unlink and free the record. */
|
||||||
|
record->next->last = record->last;
|
||||||
|
record->last->next = record->next;
|
||||||
|
XLFree (record);
|
||||||
|
|
||||||
|
/* If there are no more records in the helper, run its
|
||||||
|
all-released-callback. */
|
||||||
|
if (helper->records.next == &helper->records)
|
||||||
|
helper->callback (helper->callback_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ReleaseBufferWithHelper (BufferReleaseHelper *helper, ExtBuffer *buffer,
|
||||||
|
RenderTarget target)
|
||||||
|
{
|
||||||
|
ReleaseLaterRecord *record;
|
||||||
|
RenderBuffer render_buffer;
|
||||||
|
|
||||||
|
render_buffer = XLRenderBufferFromBuffer (buffer);
|
||||||
|
|
||||||
|
record = XLCalloc (1, sizeof *record);
|
||||||
|
record->next = helper->records.next;
|
||||||
|
record->last = &helper->records;
|
||||||
|
helper->records.next->last = record;
|
||||||
|
helper->records.next = record;
|
||||||
|
|
||||||
|
/* Now, the record is linked into the list. Record the buffer and
|
||||||
|
add an idle callback. */
|
||||||
|
record->buffer = buffer;
|
||||||
|
record->key = RenderAddIdleCallback (render_buffer, target,
|
||||||
|
BufferIdleCallback,
|
||||||
|
record);
|
||||||
|
record->helper = helper;
|
||||||
|
}
|
534
test.c
Normal file
534
test.c
Normal file
|
@ -0,0 +1,534 @@
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
/* 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 test surface manager global. */
|
||||||
|
static struct wl_global *test_manager_global;
|
||||||
|
|
||||||
|
/* Hash table of all surfaces. */
|
||||||
|
static XLAssocTable *surfaces;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
test->bounds_width = bounds_width;
|
||||||
|
test->bounds_height = bounds_height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
NoteFrame (FrameMode mode, uint64_t id, void *data)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
|
||||||
|
/* Finally, do a subcompositor update if the surface is now
|
||||||
|
mapped. */
|
||||||
|
if (test->flags & IsSurfaceMapped)
|
||||||
|
SubcompositorUpdate (test->subcompositor);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 const struct test_surface_interface test_surface_impl =
|
||||||
|
{
|
||||||
|
.destroy = Destroy,
|
||||||
|
};
|
||||||
|
|
||||||
|
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
|
||||||
|
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 ();
|
||||||
|
flags = CWColormap | CWBorderPixel | CWEventMask | CWCursor;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
/* 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 const struct test_manager_interface test_manager_impl =
|
||||||
|
{
|
||||||
|
.get_test_surface = GetTestSurface,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue