forked from 12to11/12to11

* 12to11.c (XLMain): Initialize primary selections. Transfer between X and Wayland programs is still incomplete. * Imakefile (SRCS, OBJS): Add primary selection related objects and sources. (primary-selection-unstable-v1.h): (primary-selection-unstable-v1.c): New targets. * README: Update what is not supported. * compositor.h: New prototypes. * data_device.c (XLDataDeviceSendEnter): Handle resource allocation failures. * mime1.awk: Update generated code for changes in target entry structures. * seat.c (SetFocusSurface): Handle focus change for primary selections as well. (FindSurfaceUnder): Cut off fractional portion instead of rounding the given coordinates, so the correct surface is found when the cursor is moved just inside the rightmost pixel. * surface.c (XLSurfaceRunFrameCallbacks): Handle timestamp overflow. * xdata.c (struct _TargetMapping): Rename atom to atom_flag, and use it to store flags. (MappingAtom, MappingFlag, MappingIsNextDuplicate, MappingSetFlag) (MappingUnsetFlag, MappingIs): New macros. (struct _TargetMappingTable): New structure. (Duplicate): New definition. (direct_transfer): Update duplicate types. (mapping_table): New hash table. (HashMimeString, SetupMappingTable): New functions. (FindTranslationForMimeType, Receive): Use the target mapping table to look up targets instead. (CheckDuplicate): New function. (SendOffers): Call CheckDuplicates. (XLInitXData): Set up duplicate relationship between UTF8_STRING and is conversion entry, and the targets mapping table.
1319 lines
31 KiB
C
1319 lines
31 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 <stdlib.h>
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
|
||
#include <inttypes.h>
|
||
|
||
#include "compositor.h"
|
||
|
||
/* List of all currently existing surfaces. */
|
||
Surface all_surfaces;
|
||
|
||
static DestroyCallback *
|
||
AddDestroyCallbackAfter (DestroyCallback *after)
|
||
{
|
||
DestroyCallback *callback;
|
||
|
||
callback = XLCalloc (1, sizeof *callback);
|
||
|
||
callback->next = after->next;
|
||
callback->last = after;
|
||
|
||
after->next->last = callback;
|
||
after->next = callback;
|
||
|
||
return callback;
|
||
}
|
||
|
||
static void
|
||
UnlinkDestroyCallback (DestroyCallback *callback)
|
||
{
|
||
callback->last->next = callback->next;
|
||
callback->next->last = callback->last;
|
||
|
||
callback->last = callback;
|
||
callback->next = callback;
|
||
}
|
||
|
||
static UnmapCallback *
|
||
AddUnmapCallbackAfter (UnmapCallback *after)
|
||
{
|
||
UnmapCallback *callback;
|
||
|
||
callback = XLCalloc (1, sizeof *callback);
|
||
|
||
callback->next = after->next;
|
||
callback->last = after;
|
||
|
||
after->next->last = callback;
|
||
after->next = callback;
|
||
|
||
return callback;
|
||
}
|
||
|
||
static void
|
||
UnlinkUnmapCallback (UnmapCallback *callback)
|
||
{
|
||
callback->last->next = callback->next;
|
||
callback->next->last = callback->last;
|
||
|
||
callback->last = callback;
|
||
callback->next = callback;
|
||
}
|
||
|
||
static CommitCallback *
|
||
AddCommitCallbackAfter (CommitCallback *after)
|
||
{
|
||
CommitCallback *callback;
|
||
|
||
callback = XLSafeMalloc (sizeof *callback);
|
||
|
||
if (!callback)
|
||
return callback;
|
||
|
||
callback->next = after->next;
|
||
callback->last = after;
|
||
|
||
after->next->last = callback;
|
||
after->next = callback;
|
||
|
||
return callback;
|
||
}
|
||
|
||
static void
|
||
UnlinkCommitCallback (CommitCallback *callback)
|
||
{
|
||
callback->last->next = callback->next;
|
||
callback->next->last = callback->last;
|
||
|
||
callback->last = callback;
|
||
callback->next = callback;
|
||
}
|
||
|
||
static void
|
||
RunCommitCallbacks (Surface *surface)
|
||
{
|
||
CommitCallback *callback;
|
||
|
||
/* first is a sentinel node. */
|
||
callback = surface->commit_callbacks.next;
|
||
|
||
while (callback != &surface->commit_callbacks)
|
||
{
|
||
callback->commit (surface, callback->data);
|
||
callback = callback->next;
|
||
}
|
||
}
|
||
|
||
static void
|
||
RunUnmapCallbacks (Surface *surface)
|
||
{
|
||
UnmapCallback *callback, *last;
|
||
|
||
/* first is a sentinel node. */
|
||
callback = surface->unmap_callbacks.next;
|
||
|
||
while (callback != &surface->unmap_callbacks)
|
||
{
|
||
last = callback;
|
||
callback = callback->next;
|
||
|
||
last->unmap (last->data);
|
||
}
|
||
}
|
||
|
||
static void
|
||
FreeCommitCallbacks (CommitCallback *first)
|
||
{
|
||
CommitCallback *callback, *last;
|
||
|
||
/* first is a sentinel node. */
|
||
callback = first->next;
|
||
|
||
while (callback != first)
|
||
{
|
||
last = callback;
|
||
callback = callback->next;
|
||
|
||
XLFree (last);
|
||
}
|
||
}
|
||
|
||
static void
|
||
FreeUnmapCallbacks (UnmapCallback *first)
|
||
{
|
||
UnmapCallback *callback, *last;
|
||
|
||
/* first is a sentinel node. */
|
||
callback = first->next;
|
||
|
||
while (callback != first)
|
||
{
|
||
last = callback;
|
||
callback = callback->next;
|
||
|
||
XLFree (last);
|
||
}
|
||
}
|
||
|
||
static void
|
||
FreeDestroyCallbacks (DestroyCallback *first)
|
||
{
|
||
DestroyCallback *callback, *last;
|
||
|
||
callback = first->next;
|
||
|
||
while (callback != first)
|
||
{
|
||
last = callback;
|
||
callback = callback->next;
|
||
|
||
last->destroy_func (last->data);
|
||
XLFree (last);
|
||
}
|
||
}
|
||
|
||
static FrameCallback *
|
||
AddCallbackAfter (FrameCallback *after)
|
||
{
|
||
FrameCallback *callback;
|
||
|
||
callback = XLSafeMalloc (sizeof *callback);
|
||
|
||
if (!callback)
|
||
return callback;
|
||
|
||
callback->next = after->next;
|
||
callback->last = after;
|
||
|
||
after->next->last = callback;
|
||
after->next = callback;
|
||
|
||
return callback;
|
||
}
|
||
|
||
static void
|
||
UnlinkCallbacks (FrameCallback *start, FrameCallback *end)
|
||
{
|
||
/* First, make the list skip past END. */
|
||
start->last->next = end->next;
|
||
end->next->last = start->last;
|
||
|
||
/* Then, unlink the list. */
|
||
start->last = end;
|
||
end->next = start;
|
||
}
|
||
|
||
static void
|
||
RelinkCallbacksAfter (FrameCallback *start, FrameCallback *end,
|
||
FrameCallback *dest)
|
||
{
|
||
end->next = dest->next;
|
||
start->last = dest;
|
||
|
||
dest->next->last = end;
|
||
dest->next = start;
|
||
}
|
||
|
||
static void
|
||
HandleCallbackResourceDestroy (struct wl_resource *resource)
|
||
{
|
||
FrameCallback *callback;
|
||
|
||
callback = wl_resource_get_user_data (resource);
|
||
UnlinkCallbacks (callback, callback);
|
||
XLFree (callback);
|
||
}
|
||
|
||
static void
|
||
FreeFrameCallbacks (FrameCallback *start)
|
||
{
|
||
FrameCallback *callback, *last;
|
||
|
||
callback = start->next;
|
||
|
||
while (callback != start)
|
||
{
|
||
last = callback;
|
||
callback = callback->next;
|
||
|
||
/* This will unlink last from its surroundings and free it. */
|
||
wl_resource_destroy (last->resource);
|
||
}
|
||
}
|
||
|
||
static void
|
||
RunFrameCallbacks (FrameCallback *start, uint32_t time)
|
||
{
|
||
FrameCallback *callback, *last;
|
||
|
||
callback = start->next;
|
||
|
||
while (callback != start)
|
||
{
|
||
last = callback;
|
||
callback = callback->next;
|
||
|
||
wl_callback_send_done (last->resource, time);
|
||
/* This will unlink last from its surroundings and free it. */
|
||
wl_resource_destroy (last->resource);
|
||
}
|
||
}
|
||
|
||
static void
|
||
AttachBuffer (State *state, ExtBuffer *buffer)
|
||
{
|
||
if (state->buffer)
|
||
XLDereferenceBuffer (state->buffer);
|
||
|
||
state->buffer = buffer;
|
||
XLRetainBuffer (buffer);
|
||
}
|
||
|
||
static void
|
||
ClearBuffer (State *state)
|
||
{
|
||
if (!state->buffer)
|
||
return;
|
||
|
||
XLDereferenceBuffer (state->buffer);
|
||
state->buffer = NULL;
|
||
}
|
||
|
||
static void
|
||
DestroySurface (struct wl_client *client,
|
||
struct wl_resource *resource)
|
||
{
|
||
wl_resource_destroy (resource);
|
||
}
|
||
|
||
static void
|
||
Attach (struct wl_client *client, struct wl_resource *resource,
|
||
struct wl_resource *buffer_resource, int32_t x, int32_t y)
|
||
{
|
||
Surface *surface;
|
||
ExtBuffer *buffer;
|
||
|
||
if (x != 0 && y != 0
|
||
&& wl_resource_get_version (resource) >= 5)
|
||
{
|
||
wl_resource_post_error (resource, WL_SURFACE_ERROR_INVALID_OFFSET,
|
||
"invalid offsets given to wl_surface_attach");
|
||
return;
|
||
}
|
||
|
||
surface = wl_resource_get_user_data (resource);
|
||
|
||
if (buffer_resource)
|
||
{
|
||
buffer = wl_resource_get_user_data (buffer_resource);
|
||
AttachBuffer (&surface->pending_state, buffer);
|
||
}
|
||
else
|
||
ClearBuffer (&surface->pending_state);
|
||
|
||
surface->pending_state.x = x;
|
||
surface->pending_state.y = y;
|
||
|
||
surface->pending_state.pending |= PendingBuffer;
|
||
surface->pending_state.pending |= PendingAttachments;
|
||
}
|
||
|
||
static void
|
||
Offset (struct wl_client *client, struct wl_resource *resource,
|
||
int32_t x, int32_t y)
|
||
{
|
||
Surface *surface;
|
||
|
||
surface = wl_resource_get_user_data (resource);
|
||
|
||
surface->pending_state.x = x;
|
||
surface->pending_state.y = y;
|
||
|
||
surface->pending_state.pending |= PendingAttachments;
|
||
}
|
||
|
||
static void
|
||
Damage (struct wl_client *client, struct wl_resource *resource,
|
||
int32_t x, int32_t y, int32_t width, int32_t height)
|
||
{
|
||
Surface *surface;
|
||
|
||
surface = wl_resource_get_user_data (resource);
|
||
|
||
/* Prevent integer overflow during later processing, since some
|
||
clients really set the damage region to INT_MAX. */
|
||
|
||
pixman_region32_union_rect (&surface->pending_state.surface,
|
||
&surface->pending_state.surface,
|
||
x, y, MIN (65535, width),
|
||
MIN (65535, height));
|
||
|
||
surface->pending_state.pending |= PendingSurfaceDamage;
|
||
}
|
||
|
||
static void
|
||
Frame (struct wl_client *client, struct wl_resource *resource,
|
||
uint32_t callback_id)
|
||
{
|
||
struct wl_resource *callback_resource;
|
||
FrameCallback *callback;
|
||
Surface *surface;
|
||
|
||
surface = wl_resource_get_user_data (resource);
|
||
callback = AddCallbackAfter (&surface->pending_state.frame_callbacks);
|
||
|
||
if (!callback)
|
||
{
|
||
wl_client_post_no_memory (client);
|
||
|
||
return;
|
||
}
|
||
|
||
callback_resource = wl_resource_create (client, &wl_callback_interface,
|
||
1, callback_id);
|
||
|
||
if (!callback_resource)
|
||
{
|
||
wl_client_post_no_memory (client);
|
||
UnlinkCallbacks (callback, callback);
|
||
XLFree (callback);
|
||
|
||
return;
|
||
}
|
||
|
||
wl_resource_set_implementation (callback_resource, NULL,
|
||
callback, HandleCallbackResourceDestroy);
|
||
|
||
callback->resource = callback_resource;
|
||
surface->pending_state.pending |= PendingFrameCallbacks;
|
||
}
|
||
|
||
static void
|
||
SetOpaqueRegion (struct wl_client *client, struct wl_resource *resource,
|
||
struct wl_resource *region_resource)
|
||
{
|
||
Surface *surface;
|
||
pixman_region32_t *region;
|
||
|
||
surface = wl_resource_get_user_data (resource);
|
||
|
||
if (region_resource)
|
||
{
|
||
region = wl_resource_get_user_data (region_resource);
|
||
|
||
/* Some ugly clients give the region ridiculous dimensions like
|
||
0, 0, INT_MAX, INT_MAX, which causes overflows later on. So
|
||
intersect it with the largest possible dimensions of a
|
||
view. */
|
||
pixman_region32_intersect_rect (&surface->pending_state.opaque,
|
||
region, 0, 0, 65535, 65535);
|
||
}
|
||
else
|
||
pixman_region32_clear (&surface->pending_state.opaque);
|
||
|
||
surface->pending_state.pending |= PendingOpaqueRegion;
|
||
}
|
||
|
||
static void
|
||
SetInputRegion (struct wl_client *client, struct wl_resource *resource,
|
||
struct wl_resource *region_resource)
|
||
{
|
||
Surface *surface;
|
||
pixman_region32_t *region;
|
||
|
||
surface = wl_resource_get_user_data (resource);
|
||
|
||
if (region_resource)
|
||
{
|
||
region = wl_resource_get_user_data (region_resource);
|
||
|
||
/* Some ugly clients give the region ridiculous dimensions like
|
||
0, 0, INT_MAX, INT_MAX, which causes overflows later on. So
|
||
intersect it with the largest possible dimensions of a
|
||
view. */
|
||
pixman_region32_intersect_rect (&surface->pending_state.input,
|
||
region, 0, 0, 65535, 65535);
|
||
}
|
||
else
|
||
{
|
||
pixman_region32_clear (&surface->pending_state.input);
|
||
pixman_region32_union_rect (&surface->pending_state.input,
|
||
&surface->pending_state.input,
|
||
0, 0, 65535, 65535);
|
||
}
|
||
|
||
surface->pending_state.pending |= PendingInputRegion;
|
||
}
|
||
|
||
void
|
||
XLDefaultCommit (Surface *surface)
|
||
{
|
||
/* Nothing has to be done here yet. */
|
||
}
|
||
|
||
static void
|
||
ApplyBuffer (Surface *surface)
|
||
{
|
||
if (surface->current_state.buffer)
|
||
ViewAttachBuffer (surface->view, surface->current_state.buffer);
|
||
else
|
||
ViewDetach (surface->view);
|
||
}
|
||
|
||
/* Return the effective scale.
|
||
|
||
The effective scale is a value by which to scale down the contents
|
||
of a surface on display. */
|
||
|
||
static int
|
||
GetEffectiveScale (int scale)
|
||
{
|
||
/* A "scale" is how many times to scale _down_ a surface, not up.
|
||
Negative values mean to scale the surface up instead of down. */
|
||
scale = scale - global_scale_factor;
|
||
|
||
return scale;
|
||
}
|
||
|
||
static void
|
||
ApplyScale (Surface *surface)
|
||
{
|
||
int scale, effective;
|
||
|
||
scale = surface->current_state.buffer_scale;
|
||
effective = GetEffectiveScale (scale);
|
||
|
||
ViewSetScale (surface->view, effective);
|
||
}
|
||
|
||
static void
|
||
ApplyOpaqueRegion (Surface *surface)
|
||
{
|
||
pixman_region32_t temp;
|
||
|
||
/* These regions, along with the global damage, must be multipled by
|
||
the global scale factor. */
|
||
if (global_scale_factor == 1)
|
||
ViewSetOpaque (surface->view,
|
||
&surface->current_state.opaque);
|
||
else
|
||
{
|
||
pixman_region32_init (&temp);
|
||
XLScaleRegion (&temp, &surface->current_state.opaque,
|
||
global_scale_factor, global_scale_factor);
|
||
ViewSetOpaque (surface->view, &temp);
|
||
pixman_region32_fini (&temp);
|
||
}
|
||
}
|
||
|
||
static void
|
||
ApplyInputRegion (Surface *surface)
|
||
{
|
||
pixman_region32_t temp;
|
||
|
||
/* These regions, along with the global damage, must be multipled by
|
||
the global scale factor. */
|
||
if (global_scale_factor == 1)
|
||
ViewSetInput (surface->view,
|
||
&surface->current_state.input);
|
||
else
|
||
{
|
||
pixman_region32_init (&temp);
|
||
XLScaleRegion (&temp, &surface->current_state.input,
|
||
global_scale_factor, global_scale_factor);
|
||
ViewSetInput (surface->view, &temp);
|
||
pixman_region32_fini (&temp);
|
||
}
|
||
}
|
||
|
||
static void
|
||
HandleScaleChanged (void *data, int new_scale)
|
||
{
|
||
Surface *surface;
|
||
Subcompositor *subcompositor;
|
||
|
||
surface = data;
|
||
|
||
/* First, reapply various regions that depend on the surface
|
||
scale. */
|
||
ApplyInputRegion (surface);
|
||
ApplyOpaqueRegion (surface);
|
||
ApplyScale (surface);
|
||
|
||
/* Next, call any role-specific hooks. */
|
||
if (surface->role && surface->role->funcs.rescale)
|
||
surface->role->funcs.rescale (surface, surface->role);
|
||
|
||
/* Then, redisplay the view if a subcompositor is already
|
||
attached. */
|
||
subcompositor = ViewGetSubcompositor (surface->view);
|
||
|
||
if (subcompositor)
|
||
{
|
||
/* When updating stuff out-of-band, a subframe must be started
|
||
around the update. */
|
||
|
||
if (surface->role && surface->role->funcs.subframe
|
||
&& surface->role->funcs.subframe (surface, surface->role))
|
||
{
|
||
SubcompositorUpdate (subcompositor);
|
||
|
||
if (surface->role && surface->role->funcs.end_subframe)
|
||
surface->role->funcs.end_subframe (surface, surface->role);
|
||
}
|
||
}
|
||
}
|
||
|
||
static void
|
||
ApplyDamage (Surface *surface)
|
||
{
|
||
pixman_region32_t temp;
|
||
int scale;
|
||
|
||
scale = GetEffectiveScale (surface->current_state.buffer_scale);
|
||
|
||
/* N.B. that this must come after the scale is applied. */
|
||
|
||
if (scale)
|
||
{
|
||
pixman_region32_init (&temp);
|
||
|
||
if (scale > 0)
|
||
XLScaleRegion (&temp, &surface->current_state.damage,
|
||
1.0 / (scale + 1), 1.0 / (scale + 1));
|
||
else
|
||
XLScaleRegion (&temp, &surface->current_state.damage,
|
||
abs (scale) + 1, abs (scale) + 1);
|
||
|
||
ViewDamage (surface->view, &temp);
|
||
|
||
pixman_region32_fini (&temp);
|
||
}
|
||
else
|
||
ViewDamage (surface->view,
|
||
&surface->current_state.damage);
|
||
}
|
||
|
||
static void
|
||
ApplySurfaceDamage (Surface *surface)
|
||
{
|
||
pixman_region32_t temp;
|
||
|
||
/* These regions, along with the global damage, must be multipled by
|
||
the global scale factor. */
|
||
|
||
if (global_scale_factor == 1)
|
||
ViewDamage (surface->view,
|
||
&surface->current_state.surface);
|
||
else
|
||
{
|
||
pixman_region32_init (&temp);
|
||
XLScaleRegion (&temp, &surface->current_state.surface,
|
||
global_scale_factor, global_scale_factor);
|
||
ViewDamage (surface->view, &temp);
|
||
pixman_region32_fini (&temp);
|
||
}
|
||
}
|
||
|
||
static void
|
||
SavePendingState (Surface *surface)
|
||
{
|
||
FrameCallback *start, *end;
|
||
|
||
/* Save pending state to cached state. Release any buffer
|
||
previously in the cached state. */
|
||
|
||
if (surface->pending_state.pending & PendingBuffer)
|
||
{
|
||
if (surface->cached_state.buffer
|
||
&& (surface->pending_state.buffer
|
||
!= surface->cached_state.buffer)
|
||
/* If the cached buffer has already been applied, releasing
|
||
it is a mistake! */
|
||
&& (surface->cached_state.buffer
|
||
!= surface->current_state.buffer))
|
||
{
|
||
if (surface->role)
|
||
surface->role->funcs.release_buffer (surface, surface->role,
|
||
surface->cached_state.buffer);
|
||
else
|
||
XLReleaseBuffer (surface->cached_state.buffer);
|
||
}
|
||
|
||
if (surface->pending_state.buffer)
|
||
{
|
||
AttachBuffer (&surface->cached_state,
|
||
surface->pending_state.buffer);
|
||
ClearBuffer (&surface->pending_state);
|
||
}
|
||
else
|
||
ClearBuffer (&surface->cached_state);
|
||
}
|
||
|
||
if (surface->pending_state.pending & PendingInputRegion)
|
||
pixman_region32_copy (&surface->cached_state.input,
|
||
&surface->pending_state.input);
|
||
|
||
if (surface->pending_state.pending & PendingOpaqueRegion)
|
||
pixman_region32_copy (&surface->cached_state.opaque,
|
||
&surface->pending_state.opaque);
|
||
|
||
if (surface->pending_state.pending & PendingBufferScale)
|
||
surface->cached_state.buffer_scale
|
||
= surface->pending_state.buffer_scale;
|
||
|
||
if (surface->pending_state.pending & PendingAttachments)
|
||
{
|
||
surface->cached_state.x = surface->pending_state.x;
|
||
surface->cached_state.y = surface->pending_state.y;
|
||
}
|
||
|
||
if (surface->pending_state.pending & PendingDamage)
|
||
{
|
||
pixman_region32_union (&surface->cached_state.damage,
|
||
&surface->cached_state.damage,
|
||
&surface->pending_state.damage);
|
||
pixman_region32_clear (&surface->pending_state.damage);
|
||
}
|
||
|
||
if (surface->pending_state.pending & PendingSurfaceDamage)
|
||
{
|
||
pixman_region32_union (&surface->cached_state.surface,
|
||
&surface->cached_state.surface,
|
||
&surface->pending_state.surface);
|
||
pixman_region32_clear (&surface->pending_state.surface);
|
||
}
|
||
|
||
if (surface->pending_state.pending & PendingFrameCallbacks
|
||
&& (surface->pending_state.frame_callbacks.next
|
||
!= &surface->pending_state.frame_callbacks))
|
||
{
|
||
start = surface->pending_state.frame_callbacks.next;
|
||
end = surface->pending_state.frame_callbacks.last;
|
||
|
||
UnlinkCallbacks (start, end);
|
||
RelinkCallbacksAfter (start, end,
|
||
&surface->cached_state.frame_callbacks);
|
||
}
|
||
|
||
surface->cached_state.pending |= surface->pending_state.pending;
|
||
surface->pending_state.pending = PendingNone;
|
||
}
|
||
|
||
static void
|
||
InternalCommit (Surface *surface, State *pending)
|
||
{
|
||
FrameCallback *start, *end;
|
||
|
||
if (pending->pending & PendingBuffer)
|
||
{
|
||
if ((surface->current_state.buffer != pending->buffer)
|
||
&& surface->current_state.buffer)
|
||
{
|
||
if (surface->role)
|
||
surface->role->funcs.release_buffer (surface, surface->role,
|
||
surface->current_state.buffer);
|
||
else
|
||
XLReleaseBuffer (surface->current_state.buffer);
|
||
}
|
||
|
||
if (pending->buffer)
|
||
{
|
||
AttachBuffer (&surface->current_state,
|
||
pending->buffer);
|
||
ApplyBuffer (surface);
|
||
ClearBuffer (pending);
|
||
}
|
||
else
|
||
{
|
||
ClearBuffer (&surface->current_state);
|
||
ApplyBuffer (surface);
|
||
ClearBuffer (pending);
|
||
}
|
||
}
|
||
|
||
if (pending->pending & PendingInputRegion)
|
||
{
|
||
pixman_region32_copy (&surface->current_state.input,
|
||
&pending->input);
|
||
ApplyInputRegion (surface);
|
||
}
|
||
|
||
if (pending->pending & PendingOpaqueRegion)
|
||
{
|
||
pixman_region32_copy (&surface->current_state.opaque,
|
||
&pending->opaque);
|
||
ApplyOpaqueRegion (surface);
|
||
}
|
||
|
||
if (pending->pending & PendingBufferScale)
|
||
{
|
||
surface->current_state.buffer_scale = pending->buffer_scale;
|
||
ApplyScale (surface);
|
||
}
|
||
|
||
if (pending->pending & PendingAttachments)
|
||
{
|
||
surface->current_state.x = pending->x;
|
||
surface->current_state.y = pending->y;
|
||
}
|
||
|
||
if (pending->pending & PendingDamage)
|
||
{
|
||
pixman_region32_copy (&surface->current_state.damage,
|
||
&pending->damage);
|
||
pixman_region32_clear (&pending->damage);
|
||
|
||
ApplyDamage (surface);
|
||
}
|
||
|
||
if (pending->pending & PendingSurfaceDamage)
|
||
{
|
||
pixman_region32_copy (&surface->current_state.surface,
|
||
&pending->surface);
|
||
pixman_region32_clear (&pending->surface);
|
||
|
||
ApplySurfaceDamage (surface);
|
||
}
|
||
|
||
if (pending->pending & PendingFrameCallbacks)
|
||
{
|
||
/* Insert the pending frame callbacks in front of the current
|
||
ones. */
|
||
|
||
if (pending->frame_callbacks.next != &pending->frame_callbacks)
|
||
{
|
||
start = pending->frame_callbacks.next;
|
||
end = pending->frame_callbacks.last;
|
||
|
||
UnlinkCallbacks (start, end);
|
||
RelinkCallbacksAfter (start, end,
|
||
&surface->current_state.frame_callbacks);
|
||
}
|
||
}
|
||
|
||
RunCommitCallbacks (surface);
|
||
|
||
if (surface->subsurfaces)
|
||
/* Pending surface stacking actions are stored on the parent so
|
||
they run in the right order. */
|
||
XLSubsurfaceHandleParentCommit (surface);
|
||
|
||
if (!surface->role)
|
||
{
|
||
XLDefaultCommit (surface);
|
||
pending->pending = PendingNone;
|
||
|
||
return;
|
||
}
|
||
|
||
surface->role->funcs.commit (surface, surface->role);
|
||
pending->pending = PendingNone;
|
||
}
|
||
|
||
static void
|
||
Commit (struct wl_client *client, struct wl_resource *resource)
|
||
{
|
||
Surface *surface;
|
||
|
||
surface = wl_resource_get_user_data (resource);
|
||
|
||
if (surface->role && surface->role->funcs.early_commit
|
||
/* The role chose to postpone the commit for a later time. */
|
||
&& !surface->role->funcs.early_commit (surface, surface->role))
|
||
{
|
||
/* So save the state for the role to commit later. */
|
||
SavePendingState (surface);
|
||
return;
|
||
}
|
||
|
||
InternalCommit (surface, &surface->pending_state);
|
||
}
|
||
|
||
static void
|
||
SetBufferTransform (struct wl_client *client, struct wl_resource *resource,
|
||
int32_t transform)
|
||
{
|
||
if (transform != WL_OUTPUT_TRANSFORM_NORMAL)
|
||
wl_resource_post_error (resource, WL_DISPLAY_ERROR_IMPLEMENTATION,
|
||
"this compositor does not support buffer transforms");
|
||
}
|
||
|
||
static void
|
||
SetBufferScale (struct wl_client *client, struct wl_resource *resource,
|
||
int32_t scale)
|
||
{
|
||
Surface *surface;
|
||
|
||
if (scale <= 0)
|
||
{
|
||
wl_resource_post_error (resource, WL_SURFACE_ERROR_INVALID_SCALE,
|
||
"invalid scale: %d", scale);
|
||
return;
|
||
}
|
||
|
||
surface = wl_resource_get_user_data (resource);
|
||
|
||
surface->pending_state.buffer_scale = scale;
|
||
surface->pending_state.pending |= PendingBufferScale;
|
||
}
|
||
|
||
static void
|
||
DamageBuffer (struct wl_client *client, struct wl_resource *resource,
|
||
int32_t x, int32_t y, int32_t width, int32_t height)
|
||
{
|
||
Surface *surface;
|
||
|
||
surface = wl_resource_get_user_data (resource);
|
||
|
||
/* Prevent integer overflow during later processing, since some
|
||
clients really set the damage region to INT_MAX. */
|
||
|
||
pixman_region32_union_rect (&surface->pending_state.damage,
|
||
&surface->pending_state.damage,
|
||
x, y, MIN (65535, width),
|
||
MIN (65535, height));
|
||
|
||
surface->pending_state.pending |= PendingDamage;
|
||
}
|
||
|
||
static const struct wl_surface_interface wl_surface_impl =
|
||
{
|
||
.destroy = DestroySurface,
|
||
.attach = Attach,
|
||
.damage = Damage,
|
||
.frame = Frame,
|
||
.set_opaque_region = SetOpaqueRegion,
|
||
.set_input_region = SetInputRegion,
|
||
.commit = Commit,
|
||
.set_buffer_transform = SetBufferTransform,
|
||
.set_buffer_scale = SetBufferScale,
|
||
.damage_buffer = DamageBuffer,
|
||
.offset = Offset,
|
||
};
|
||
|
||
static void
|
||
InitState (State *state)
|
||
{
|
||
pixman_region32_init (&state->damage);
|
||
pixman_region32_init (&state->opaque);
|
||
pixman_region32_init (&state->surface);
|
||
|
||
/* The initial state of the input region is always infinite. */
|
||
pixman_region32_init_rect (&state->input, 0, 0,
|
||
65535, 65535);
|
||
|
||
state->pending = PendingNone;
|
||
state->buffer = NULL;
|
||
state->buffer_scale = 1;
|
||
|
||
/* Initialize the sentinel node. */
|
||
state->frame_callbacks.next = &state->frame_callbacks;
|
||
state->frame_callbacks.last = &state->frame_callbacks;
|
||
state->frame_callbacks.resource = NULL;
|
||
}
|
||
|
||
static void
|
||
FinalizeState (State *state)
|
||
{
|
||
pixman_region32_fini (&state->damage);
|
||
pixman_region32_fini (&state->opaque);
|
||
pixman_region32_fini (&state->surface);
|
||
pixman_region32_fini (&state->input);
|
||
|
||
if (state->buffer)
|
||
XLDereferenceBuffer (state->buffer);
|
||
state->buffer = NULL;
|
||
|
||
/* Destroy any callbacks that might be remaining. */
|
||
FreeFrameCallbacks (&state->frame_callbacks);
|
||
}
|
||
|
||
static void
|
||
NotifySubsurfaceDestroyed (void *data)
|
||
{
|
||
Surface *surface;
|
||
|
||
surface = data;
|
||
|
||
if (surface->role)
|
||
XLSubsurfaceParentDestroyed (surface->role);
|
||
}
|
||
|
||
static void
|
||
HandleSurfaceDestroy (struct wl_resource *resource)
|
||
{
|
||
Surface *surface;
|
||
int i;
|
||
|
||
surface = wl_resource_get_user_data (resource);
|
||
|
||
if (surface->role)
|
||
XLSurfaceReleaseRole (surface, surface->role);
|
||
|
||
/* Keep surface->resource around until the role is released; some
|
||
code (such as dnd.c) assumes that surface->resource will always
|
||
be available in unmap callbacks. */
|
||
surface->resource = NULL;
|
||
|
||
/* First, free all subsurfaces. */
|
||
XLListFree (surface->subsurfaces,
|
||
NotifySubsurfaceDestroyed);
|
||
|
||
/* Then release all client data. */
|
||
for (i = 0; i < MaxClientData; ++i)
|
||
{
|
||
if (surface->client_data[i])
|
||
surface->free_client_data[i] (surface->client_data[i]);
|
||
XLFree (surface->client_data[i]);
|
||
|
||
surface->client_data[i] = NULL;
|
||
}
|
||
|
||
/* Release the output region. */
|
||
pixman_region32_fini (&surface->output_region);
|
||
|
||
/* Next, free the views. */
|
||
ViewFree (surface->view);
|
||
ViewFree (surface->under);
|
||
|
||
/* Then, unlink the surface from the list of all surfaces. */
|
||
surface->next->last = surface->last;
|
||
surface->last->next = surface->next;
|
||
|
||
/* Free outputs. */
|
||
XLFree (surface->outputs);
|
||
|
||
/* Free the window scaling factor callback. */
|
||
XLRemoveScaleChangeCallback (surface->scale_callback_key);
|
||
|
||
FinalizeState (&surface->pending_state);
|
||
FinalizeState (&surface->current_state);
|
||
FinalizeState (&surface->cached_state);
|
||
FreeCommitCallbacks (&surface->commit_callbacks);
|
||
FreeUnmapCallbacks (&surface->unmap_callbacks);
|
||
FreeDestroyCallbacks (&surface->destroy_callbacks);
|
||
XLFree (surface);
|
||
}
|
||
|
||
void
|
||
XLCreateSurface (struct wl_client *client,
|
||
struct wl_resource *resource,
|
||
uint32_t id)
|
||
{
|
||
Surface *surface;
|
||
|
||
surface = XLSafeMalloc (sizeof *surface);
|
||
|
||
if (!surface)
|
||
{
|
||
wl_resource_post_no_memory (resource);
|
||
return;
|
||
}
|
||
|
||
memset (surface, 0, sizeof *surface);
|
||
surface->resource
|
||
= wl_resource_create (client, &wl_surface_interface,
|
||
wl_resource_get_version (resource),
|
||
id);
|
||
|
||
if (!surface->resource)
|
||
{
|
||
wl_resource_post_no_memory (resource);
|
||
XLFree (surface);
|
||
return;
|
||
}
|
||
|
||
wl_resource_set_implementation (surface->resource, &wl_surface_impl,
|
||
surface, HandleSurfaceDestroy);
|
||
|
||
surface->role = NULL;
|
||
surface->view = MakeView ();
|
||
surface->under = MakeView ();
|
||
surface->subsurfaces = NULL;
|
||
|
||
/* Make it so that seat.c can associate the surface with the
|
||
view. */
|
||
ViewSetData (surface->view, surface);
|
||
|
||
/* Initialize the sentinel node for the commit callback list. */
|
||
surface->commit_callbacks.last = &surface->commit_callbacks;
|
||
surface->commit_callbacks.next = &surface->commit_callbacks;
|
||
surface->commit_callbacks.commit = NULL;
|
||
surface->commit_callbacks.data = NULL;
|
||
|
||
/* And the sentinel node for the unmap callback list. */
|
||
surface->unmap_callbacks.last = &surface->unmap_callbacks;
|
||
surface->unmap_callbacks.next = &surface->unmap_callbacks;
|
||
surface->unmap_callbacks.unmap = NULL;
|
||
surface->unmap_callbacks.data = NULL;
|
||
|
||
/* And the sentinel node for the destroy callback list. */
|
||
surface->destroy_callbacks.last = &surface->destroy_callbacks;
|
||
surface->destroy_callbacks.next = &surface->destroy_callbacks;
|
||
surface->destroy_callbacks.destroy_func = NULL;
|
||
surface->destroy_callbacks.data = NULL;
|
||
|
||
InitState (&surface->pending_state);
|
||
InitState (&surface->current_state);
|
||
InitState (&surface->cached_state);
|
||
|
||
/* Now the default input has been initialized, so apply it to the
|
||
view. */
|
||
ApplyInputRegion (surface);
|
||
|
||
/* Likewise for the scale. */
|
||
ApplyScale (surface);
|
||
|
||
/* Initially, allow surfaces to accept any kind of role. */
|
||
surface->role_type = AnythingType;
|
||
|
||
/* Initialize the output region. */
|
||
pixman_region32_init (&surface->output_region);
|
||
|
||
/* Link the surface onto the list of all surfaces. */
|
||
surface->next = all_surfaces.next;
|
||
surface->last = &all_surfaces;
|
||
all_surfaces.next->last = surface;
|
||
all_surfaces.next = surface;
|
||
|
||
/* Also add the scale change callback. */
|
||
surface->scale_callback_key
|
||
= XLAddScaleChangeCallback (surface, HandleScaleChanged);
|
||
|
||
/* Clear surface output coordinates. */
|
||
surface->output_x = INT_MIN;
|
||
surface->output_y = INT_MIN;
|
||
}
|
||
|
||
void
|
||
XLInitSurfaces (void)
|
||
{
|
||
all_surfaces.next = &all_surfaces;
|
||
all_surfaces.last = &all_surfaces;
|
||
}
|
||
|
||
|
||
/* Role management: XDG shells, wl_shells, et cetera. */
|
||
|
||
Bool
|
||
XLSurfaceAttachRole (Surface *surface, Role *role)
|
||
{
|
||
if (surface->role)
|
||
return False;
|
||
|
||
if (!role->funcs.setup (surface, role))
|
||
return False;
|
||
|
||
surface->role = role;
|
||
|
||
return True;
|
||
}
|
||
|
||
void
|
||
XLSurfaceReleaseRole (Surface *surface, Role *role)
|
||
{
|
||
role->funcs.teardown (surface, role);
|
||
|
||
if (surface->resource)
|
||
/* Now that the surface is unmapped, leave every output it
|
||
previously entered. */
|
||
XLClearOutputs (surface);
|
||
|
||
surface->role = NULL;
|
||
surface->output_x = INT_MIN;
|
||
surface->output_y = INT_MIN;
|
||
RunUnmapCallbacks (surface);
|
||
}
|
||
|
||
void
|
||
XLStateAttachBuffer (State *state, ExtBuffer *buffer)
|
||
{
|
||
AttachBuffer (state, buffer);
|
||
}
|
||
|
||
void
|
||
XLStateDetachBuffer (State *state)
|
||
{
|
||
ClearBuffer (state);
|
||
}
|
||
|
||
|
||
/* Various other functions exported for roles. */
|
||
|
||
void
|
||
XLSurfaceRunFrameCallbacks (Surface *surface, struct timespec time)
|
||
{
|
||
uint32_t ms_time;
|
||
XLList *list;
|
||
|
||
/* I don't know what else is reasonable in case of overflow. */
|
||
|
||
if (IntMultiplyWrapv (time.tv_sec, 1000, &ms_time))
|
||
ms_time = UINT32_MAX;
|
||
else if (IntAddWrapv (ms_time, time.tv_nsec / 1000000,
|
||
&ms_time))
|
||
ms_time = UINT32_MAX;
|
||
|
||
RunFrameCallbacks (&surface->current_state.frame_callbacks,
|
||
ms_time);
|
||
|
||
/* Run frame callbacks for each attached subsurface as well. */
|
||
for (list = surface->subsurfaces; list; list = list->next)
|
||
XLSurfaceRunFrameCallbacks (list->data, time);
|
||
}
|
||
|
||
CommitCallback *
|
||
XLSurfaceRunAtCommit (Surface *surface,
|
||
void (*commit_func) (Surface *, void *),
|
||
void *data)
|
||
{
|
||
CommitCallback *callback;
|
||
|
||
callback = AddCommitCallbackAfter (&surface->commit_callbacks);
|
||
callback->commit = commit_func;
|
||
callback->data = data;
|
||
|
||
return callback;
|
||
}
|
||
|
||
void
|
||
XLSurfaceCancelCommitCallback (CommitCallback *callback)
|
||
{
|
||
UnlinkCommitCallback (callback);
|
||
|
||
XLFree (callback);
|
||
}
|
||
|
||
UnmapCallback *
|
||
XLSurfaceRunAtUnmap (Surface *surface,
|
||
void (*unmap_func) (void *),
|
||
void *data)
|
||
{
|
||
UnmapCallback *callback;
|
||
|
||
callback = AddUnmapCallbackAfter (&surface->unmap_callbacks);
|
||
callback->unmap = unmap_func;
|
||
callback->data = data;
|
||
|
||
return callback;
|
||
}
|
||
|
||
void
|
||
XLSurfaceCancelUnmapCallback (UnmapCallback *callback)
|
||
{
|
||
UnlinkUnmapCallback (callback);
|
||
|
||
XLFree (callback);
|
||
}
|
||
|
||
void
|
||
XLCommitSurface (Surface *surface, Bool use_pending)
|
||
{
|
||
InternalCommit (surface, (use_pending
|
||
? &surface->pending_state
|
||
: &surface->cached_state));
|
||
}
|
||
|
||
DestroyCallback *
|
||
XLSurfaceRunOnFree (Surface *surface, void (*destroy_func) (void *),
|
||
void *data)
|
||
{
|
||
DestroyCallback *callback;
|
||
|
||
callback = AddDestroyCallbackAfter (&surface->destroy_callbacks);
|
||
callback->destroy_func = destroy_func;
|
||
callback->data = data;
|
||
|
||
return callback;
|
||
}
|
||
|
||
void
|
||
XLSurfaceCancelRunOnFree (DestroyCallback *callback)
|
||
{
|
||
UnlinkDestroyCallback (callback);
|
||
|
||
XLFree (callback);
|
||
}
|
||
|
||
void *
|
||
XLSurfaceGetClientData (Surface *surface, ClientDataType type,
|
||
size_t size, void (*free_func) (void *))
|
||
{
|
||
if (surface->client_data[type])
|
||
return surface->client_data[type];
|
||
|
||
surface->client_data[type] = XLCalloc (1, size);
|
||
surface->free_client_data[type] = free_func;
|
||
|
||
return surface->client_data[type];
|
||
}
|
||
|
||
Window
|
||
XLWindowFromSurface (Surface *surface)
|
||
{
|
||
if (!surface->role
|
||
|| !surface->role->funcs.get_window (surface,
|
||
surface->role))
|
||
return None;
|
||
|
||
return surface->role->funcs.get_window (surface,
|
||
surface->role);
|
||
}
|
||
|
||
Bool
|
||
XLSurfaceGetResizeDimensions (Surface *surface, int *width, int *height)
|
||
{
|
||
if (!surface->role
|
||
|| !surface->role->funcs.get_resize_dimensions)
|
||
return False;
|
||
|
||
surface->role->funcs.get_resize_dimensions (surface, surface->role,
|
||
width, height);
|
||
return True;
|
||
}
|
||
|
||
void
|
||
XLSurfacePostResize (Surface *surface, int west_motion, int north_motion,
|
||
int new_width, int new_height)
|
||
{
|
||
if (!surface->role
|
||
|| !surface->role->funcs.post_resize)
|
||
return;
|
||
|
||
surface->role->funcs.post_resize (surface, surface->role,
|
||
west_motion, north_motion,
|
||
new_width, new_height);
|
||
return;
|
||
}
|
||
|
||
void
|
||
XLSurfaceMoveBy (Surface *surface, int west, int north)
|
||
{
|
||
if (!surface->role
|
||
|| !surface->role->funcs.move_by)
|
||
return;
|
||
|
||
surface->role->funcs.move_by (surface, surface->role,
|
||
west, north);
|
||
}
|