Compare commits

...

350 Commits

Author SHA1 Message Date
af0478ae1e xwayland: Don't use abstract socket on non-Linux systems
Abstract socket is a Linux-only feature. On operating systems other than
Linux, we can only use the normal UNIX socket.

This patch allows mutter to run as a nested Wayland compositor under
Xorg on FreeBSD.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/954
2020-05-05 21:36:53 +08:00
aedf692e0c backends: move 'input_device' to HAVE_LIBWACOM scope
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1231
2020-05-03 23:35:03 +01:00
5dfd86ebe7 Bump version to 3.37.1
Update NEWS.
2020-04-29 18:47:11 +02:00
9e41f687a0 remote-access-controller: Make it build with -Dremote_desktop=false
It'll be all no-ops, which is fine, since there is nothing to
inhibit/uninhibit.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1223
2020-04-28 22:00:11 +02:00
61356caa06 keybindings: Mask out the reserved modifiers mask
When switching layouts, special modifiers bits may be be set for
internal use by Xkb.

As we now ignore a set of modifiers when processing the special
modifiers keys, we ought to also mask out those reserved modifiers
otherwise we would discard the [Super] key after switching layouts
in X11.

Closes: https://gitlab.gnome.org/GNOME/mutter/issues/1144
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1219
2020-04-27 15:51:47 +00:00
4300f1f91d remote-access-controller: Allow inhibiting remote access
Inhibiting remote access means any current remote access session is
terminated, and no new ones can be created, until remote access is
uninhibited. The inhibitation is ref counted, meaning there can be more
than one inhibitor.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1212
2020-04-27 14:31:06 +00:00
d26dc4ae44 compositor: Only include meta-window-actor-wayland.h when building with wayland
https://gitlab.gnome.org/GNOME/mutter/-/issues/1074
2020-04-27 13:28:28 +00:00
bd45a00fa3 window-actor/x11: Cache the frame bounds
When resizing an X11 window with client side decorations, the shadow is
clipped by the frame bounds so that we don't need to paint the shadow
under the opaque areas covered by the window and its frame.

When the X11 client uses the EMWH synchronization mechanism (like all
gtk-3 based clients), the actual window may not be updated so that the
actual window and it frame may be behind the expected window frame
bounds, which gives the impression of de-synchronized shadows.

To avoid the issue, keep a copy of the frame bounds as a cache and only
update it when the client is not frozen so that the clipping occurs on
the actual content.

Closes: https://gitlab.gnome.org/GNOME/mutter/issues/1178
https://gitlab.gnome.org/GNOME/mutter/merge_requests/1214
2020-04-27 13:04:38 +02:00
793a9d45e1 clutter/stage-cogl: Fix painting the redraw clip with the damage region
The redraw clip that's painted together with the damage region has to be
copied earlier than we do right now. That's because if
PAINT_DAMAGE_REGION is enabled, buffer age is disabled and thus
use_clipped_redraw is FALSE. That means the redraw_clip is updated and
set to the full view-rect. If we copy the queued_redraw_clip after that,
it's also going to be set to the full view-rect. So copy the redraw clip
a bit earlier to make sure we're actually passing the real redraw clip
to paint_damage_region().

Also keep the queued_redraw_clip around a bit longer so it can actually
be used by paint_damage_region() and isn't freed before that.

While at it, move paint_damage_region() from swap_framebuffer() into
clutter_stage_cogl_redraw_view() so we don't have to pass things to
swap_framebuffer() only for debugging.

Fixes https://gitlab.gnome.org/GNOME/mutter/-/issues/1104

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1208
2020-04-25 16:42:30 +02:00
43295bdcc4 Update German translation
(cherry picked from commit bfe9b333c0)
2020-04-24 22:38:04 +00:00
43e12dab7b backend: Remove support for META_DUMMY_MONITORS variable
Forcing a dummy monitor manager is unexpected and has been broken
since commit 315a6f43d.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1213
2020-04-23 21:31:46 +02:00
c4535fdf85 screen-cast: Add RecordArea for screen cast arbitrary area
It takes coordinates in stage coordinate space, and will result in
a screen cast stream consisting of that area, but scaled up by the scale
factor of the view that overlaps with the area and has the highest scale
factor.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1207
2020-04-23 14:45:53 +00:00
d2c3272eb7 clutter/paint-context: Add 'no-cursors' paint flag
Will be used by the stage to not paint the overlays. We skip all
overlays since overlays are only ever used for pointer cursors when the
hardware cursors cannot or should not be used.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1207
2020-04-23 14:45:53 +00:00
90c4b6492f stage: Only invoke paint phase callbacks when painting views
These phase callbacks are not intended to be inovked when something
secondary is painting the stage, such as a screen cast stream, or
similar. Thus, only invoke the callbacks when there is a view associated
with the paint context, which will not be the case for offscreen
painting.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1207
2020-04-23 14:45:53 +00:00
b7bf42e778 clutter/stage: Add API to paint to a custom target
Either onto a framebuffer, or into a CPU memory buffer. The latter will
use an former API and then copy the result to CPU memory. The former
allocates an offscreen framebuffer, sets up the relevant framebuffer
matrices and paints part of the stage defined by the passed rectangle.

This will be used by a RecordArea screen cast API. The former to paint
directly onto PipeWire handled dma-buf framebuffers, and the latter for
PipeWire handled shared memory buffers.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1207
2020-04-23 14:45:53 +00:00
e849667be6 screen-cast-stream-src: Add getter for stride
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1207
2020-04-23 14:45:53 +00:00
424016d66c stage: Pass paint context in phase callbacks
If there is a paint context available (i.e. for the phases that are
during the actual stage paint), pass it along the callbacks, so that
the callback implementations can change their operation depending on the
paint context state.

This also means we can get the current view from the paint context,
instead of the temporarily used field in the instance struct.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1207
2020-04-23 14:45:53 +00:00
a4f55d4986 clutter/paint-context: Allow passing redraw clip to offscreen paint context
So that we can mark the redraw clip of the part of the stage we're
painting.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1207
2020-04-23 14:45:53 +00:00
1b33a5a3a7 clutter/paint-context: Add paint flag
A paint flag affects a paint operation in ways defined by the flags.
Currently no flags are defined, so no semantical changes are defined
yet. Eventually a flag aiming to avoid painting of cursors is going to
be added, so that screen cast streams can decide whether to include a
cursor or not.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1207
2020-04-23 14:45:53 +00:00
36111270aa kms-impl/simple: Fix page_flip_data ref leak on fallback
If drmModePageFlip() or custom_page_flip_func fails, process_page_flip() was
forgetting to undo the ref taken for that call. This would leak page_flip_data.

The reference counting works like this:
- when created, ref count is 1
- when calling drmModePageFlip, ref count is increased to 2
- new: if flip failed, ref count is decreased back to 1
- if calling schedule_retry_page_flip(), it takes a ref internally
- if calling mode_set_fallback(), it takes a ref internally
- all return FALSE paths have an explicit unref
- return TRUE path has an explicit unref

This issue was found by code inspection and while debugging an unrelated issue
with debug prints sprinkled around. I am not aware of any end-user visible
issues being fixed by this, as the leak is small and probably very rare.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1209
2020-04-23 16:30:22 +03:00
6e0cfd3e55 kms: Make GSource ready by default
When testing a laptop with intel and DisplayLink devices, attempting to set the
DL output as the only active output resulted in GNOME/Wayland freezing. The
main event loop was running fine, but nothing on screen would get updated once
the DL output become the only one. This patch fixes that issue.

DisplayLink USB 3 devices use an out-of-tree kernel DRM driver called EVDI.
EVDI can sometimes fail drmModePageFlip(). For me, the flip fails reliably when
hotplugging the DL dock and when changing display configuration to DL only.
Mutter has a workaround for failing flips, it just calls drmModeSetCrtc() and
that succeeds.

What does not work reliably in the fallback path is Mutter keeping track of the
pageflip. Since drmModePageFlip() failed, there will not be a pageflip event
coming and instead Mutter queues a callback in its stead. When you have more
than one output, some other output repainting will attempt to swap buffers and
calls wait_for_pending_flips() which has the side-effect of dispatching any
queued flip callbacks. With multiple outputs, you don't get stuck (unless they
all fail the exact same way at the same time?). When you have only one output,
it cannot proceed to repaint and buffer swap because the pageflip is not marked
complete yet. Nothing dispatches the flip callback, leading to the freeze.

The flip callback is intended to be an idle callback, implemented with a
GSource. It is supposed to be called as soon as execution returns to the main
event loop. The setup of the GSource is incomplete, so it will never dispatch.

Fix the GSource setup by setting its ready-time to be always in the past. That
gets it dispatched on the next cycle of the main event loop. This is now the
default behavior for all sources created by meta_kms_add_source_in_impl().
Sources that need a delay continue to do that by overriding the ready-time
explicitly.

An alternative solution could have been to implement GSource prepare and check
callbacks returning TRUE. However, since meta_kms_add_source_in_impl() is used
by flip retry code as well, and that code needs a delay through the ready-time,
I was afraid I might break the flip retry code. Hence I decided to use
ready-time instead.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1209
2020-04-23 16:30:17 +03:00
5671f0a284 x11: Allow X11 clients to clear the selection
According to the XSetSelectionOwner libX11 documentation:

  [...] If the owner window it has specified in the request is later
  destroyed, the owner of the selection automatically reverts to None,
  but the last-change time is not affected.

This is indeed visible through the selection_timestamp field in
XFixesSelectionNotify events.

Use this to check whether the selection time is recent-ish (thus
likely coming from an explicit XSetSelectionOwner request) and honor
the client intent by setting a "NULL" owner. If the selection time
is too old, it's definitely an indication of the owner client being
closed, the scenario where we do want the clipboard manager to take
over.

This fixes two usecases:
- X11 LibreOffice / WPS clear the selection each time before copying
  its own content. Mutter's clipboard manager would see each of those
  as a hint to take over, competing with the client over selection
  ownership. This would simply no longer happen
- Password managers may want to clear the selection, which would be
  frustrated by our clipboard manager.

There's a slight window of opportunity for the heuristics to fail
though, if a X11 client sets the selection and closes within 50ms, we
would miss the clipboard manager taking over.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1206
2020-04-21 21:32:53 +00:00
a7e63bea6c x11: Generalize x11 selection owner checks
Shuffle things so the x11 selection can check the current owner directly,
instead of its type.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1206
2020-04-21 21:32:53 +00:00
94b3c334e5 x11: Clear X11 selection source after unsetting owner
The X11 selection source was being preserved after unsetting its
ownership. This is no leak as it would be eventually replaced by
another source, or destroyed on finalize. But it's pointless to
keep it.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1206
2020-04-21 21:32:53 +00:00
efb0addb62 tests/wayland: Add a test for meta-anonymous-file
Test the two modes of MetaAnonymousFile, MAPMODE_SHARED and
MAPMODE_PRIVATE and make sure they don't leak data to other FDs when
writing to an FD provided by `meta_anonymous_file_get_fd` even though
the data of both FDs is residing in the same chunk of memory.

We do all the reading tests using mmap instead of read() since using
read() on shared FDs is going to move the read cursor of the fd. That
means using read() once on the shared FD returned by
meta_anonymous_file_get_fd() in MAPMODE_PRIVATE breaks every subsequent
read() call.

Also test the fallback code of MetaAnonymousFile in case `memfd_create`
isn't used for the same issues.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1012
2020-04-21 17:52:08 +02:00
988da215c8 wayland/keyboard: Use MetaAnonymousFile to share keymap files
Since protocol version 7 clients must use MAP_PRIVATE to map the keymap
fd, that means we can use memfd_create() to create the fd by using
meta_anonymous_file_open_fd() with META_ANONYMOUS_FILE_MAPMODE_PRIVATE,
for older versions we use META_ANONYMOUS_FILE_MAPMODE_SHARED to be
compatibile with MAP_SHARED.

Pretty much all of this code was written for Weston by Sebastian Wick,
see https://gitlab.freedesktop.org/wayland/weston/merge_requests/240.

Co-authored-by: Sebastian Wick <sebastian@sebastianwick.net>

Fixes https://gitlab.gnome.org/GNOME/gnome-shell/issues/1734

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1012
2020-04-21 17:52:08 +02:00
551a57ed7f Add read-only anonymous file abstraction MetaAnonymousFile
Add MetaAnonymousFile, an abstraction around anonymous read-only files.
Files can be created by calling meta_anonymous_file_new(), passing the
data of the file. Subsequent calls to meta_anonymous_file_open_fd()
return a fd that's ready to be sent over the socket.

When mapmode is META_ANONYMOUS_FILE_MAPMODE_PRIVATE the fd is only
guaranteed to be mmap-able readonly with MAP_PRIVATE but does not
require duplicating the file for each resource when memfd_create is
available. META_ANONYMOUS_FILE_MAPMODE_SHARED may be used when the
client must be able to map the file with MAP_SHARED but it also means
that the file has to be duplicated even when memfd_create is available.

Pretty much all of this code was written for weston by Sebastian Wick,
see https://gitlab.freedesktop.org/wayland/weston/merge_requests/240.

Co-authored-by: Sebastian Wick <sebastian@sebastianwick.net>

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1012
2020-04-21 17:52:08 +02:00
b7366b5b53 wayland: Drop unused struct variable
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1193
2020-04-17 00:51:00 +02:00
5e8d8b9ade wayland: Move the primary data device manager to its own file
Instead of having everything clumped at MetaWaylandDataManager,
split the primary selection to its own struct. This manager is
handled separately from wl_data_device_manager and other selection
managers, so they would be able to interoperate between them, even.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1193
2020-04-17 00:50:57 +02:00
4726f3d5d3 wayland: Move primary data offers to their own file
Following the MetaWaylandDataOffer split.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1193
2020-04-17 00:46:23 +02:00
91ef7515de wayland: Move MetaWaylandDataOffer to its own file
This is still an openly defined struct, as we will need accessed
by "subclasses". Same principle applies than with the
MetaWaylandDataSource refactor, this is not meant to introduce
functional changes, so just go with it.

On the bright side, the interactions are now clearer, so it could
be made saner in the future.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1193
2020-04-17 00:46:23 +02:00
317f6c0910 wayland: Move MetaWaylandDataSourcePrimary to its own file
Following the MetaWaylandDataSource split, this goes next.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1193
2020-04-17 00:46:23 +02:00
6a3d521466 wayland: Split MetaWaylandDataSource into a separate file
The split wasn't 100% clean, and some extra private API had to be
added for it (but well, looking at the API, it's already evident
there's a cleanup/streamlining task due). This is meant to be a
refactor with no functional changes, so just go with it.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1193
2020-04-17 00:46:21 +02:00
1363246d44 wayland: Rely on MetaSelection::owner-changed for .selection event emission
We already have a signal callback that translates selection ownership changes to
data_device/primary .selection events. Given both will be run when a data source
is being replaced, and this event emission being deleted is kinda short sighted
in that in only knows about Wayland, rely entirely on MetaSelection::owner-changed
emission.

Fixes spurious .selection(null) events being sent when a compositor-local source
takes over the selection without the focus changing (eg. screenshot to clipboard).

Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/1160

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1204
2020-04-16 22:25:00 +00:00
0b6560fac4 wayland: Do not cancel old data source when setting new selection
This is taken care already by the MetaSelection machinery, by
deactivating the previous selection source when setting a new one.
That works across X11 and internal selection sources. This
only works when replacing one wayland source with another, and
actually results in doubly .cancelled events due to the other
paths.

Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/1177

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1203
2020-04-16 19:27:51 +00:00
d4c3870286 wayland: Shuffle wl_data_source.cancelled version checks on DnD
We are meant to send a .cancelled event after the drop is performed
in certain situations, but only for version>3 clients. Since this is
all version 3 business, only set the drop_performed flag for v3
clients. This drops the need to perform version checks at the time
of cancelling (which is present for other usecases in v1).

Fixes emission of wl_data_source.cancelled for v1 clients.

Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/1177

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1203
2020-04-16 19:27:51 +00:00
4bdf9a1e70 core: Cater for reading selection in chunks
For the cases where we read a fixed size from the selection (eg. imposing
limits for the clipboard manager), g_input_stream_read_bytes_async() might
not read up to this given size if the other side is spoonfeeding it content.

Cater for multiple read/write cycles here, until (maximum) transfer size is
reached.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1198
2020-04-16 16:26:04 +00:00
1909977a67 x11: Do not trust there is task in error paths
Flushing the X11 selection output stream may happen synchronously or
implicitly, in which case there is not a task to complete. Check there
is actually a task before returning errors. We additionally set the
pipe_error flag, so future operations will fail with an error, albeit
with a more generic message.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1198
2020-04-16 16:26:04 +00:00
655a783891 x11: Don't stall on write_async()
If a write_async() comes up while we are flushing on the background,
the task will be queued, but not deemed a reason on itself to keep
flushing (and finish the task) after a property delete event.

To fix this, do not ever queue up write_async tasks (this leaves
priv->pending_task only used for flush(), so the "flush to end"
behavior in the background is consistent). We only start a
background flush if there's reasons to do it, but the tasks are
immediately finished.

All data will still be ensured to be transfered on flush/close,
this makes the caller in this situation still able to reach to it.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1198
2020-04-16 16:26:04 +00:00
a4596becc4 x11: Fix iterative INCR property checks
It does not make sense to check for the stream not being closed,
this might happen multiple times during the lifetime of the stream
for a single transfer. We want to notify the INCR transfer just
once.

Check for the explicit conditions that we want, that the remaining
data is bigger than we can transfer at once, and that we are not
yet within the INCR transfer.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1198
2020-04-16 16:26:04 +00:00
7015bb3efd x11: Don't exceed transfer size in INCR chunks
The stream automatically flushes after data size exceeds the
size we deem for INCR chunks, but we still try to copy it all.
Actually limit the data we copy, and leave the rest for future
INCR chunks.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1198
2020-04-16 16:26:04 +00:00
d2c762cc66 x11: Don't invariably queue a pending delete request
We don't need doing this roundtrip for non-INCR transfers.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1198
2020-04-16 16:26:04 +00:00
04d429b743 x11: Finish INCR transfers properly
INCR transfers are mandated to finish with a final 0-size XChangeProperty
roundtrip after the final data chunk. Actually honor this and ensure we
iterate just once more for this.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1198
2020-04-16 16:26:04 +00:00
0b21dcfe08 x11: Wait till data is flushed before notifying on the pending task
It does not make sense to notify flushes mid-transfer. We should wait
till the data is actually finished before notifying on the pending
task.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1198
2020-04-16 16:26:04 +00:00
7c939d78c2 x11: Only send SelectionNotify on first INCR chunk
This should only be sent if the selection can be sent at once, or
if we are right about to notify on the first chunk of an INCR
transfer.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1198
2020-04-16 16:26:04 +00:00
8a2b82897d x11: Ensure flush() Flushes all output stream data
This seemed to work under the assumption that a flush() call can
only result in one INCR roundtrip. This is evidently not true, so
we should hold things off until all pending data is actually flushed.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1198
2020-04-16 16:26:04 +00:00
e95c365cf0 x11: Unset pending flush flag right before notifying on task
Together with some other state. We can do this altogether on task
notification, instead of lost somewhere in this function flow.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1198
2020-04-16 16:26:04 +00:00
a32cb7133b x11: Intern INCR atom
We want to use it, despite it not existing previously.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1198
2020-04-16 16:26:04 +00:00
967966cdee x11: Flag flushes despite having less than the element size
If say we want 32bit data, but have 2 bytes stored, we would simply
ignore flush requests. Allow (and don't clear) the needs_flush flag
if we have less than the element size accumulated.

Instead handle this in can_flush(), so it's triggered whenever we
have enough data to fill 1 element, or if the stream is closing
(seems a broken situation, but triggered by the caller).

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1198
2020-04-16 16:26:04 +00:00
06d67b6abf x11: XMaxRequestSize returns 4-byte units
XMaxRequestSize/XMaxExtendedRequestSize are documented to return
the maximum size in 4-byte units, whereas we are comparing this
to byte lenghts. We can afford 4x the data here.

Since I don't know the payload size of the XChangeProperty request,
be generous and allot 400 bytes for it, we have some to spare.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1198
2020-04-16 16:26:04 +00:00
f15ce01e2b monitor-unit-tests: Ensure configuration is preserved in laptop with closed lid
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1200
2020-04-16 15:14:03 +00:00
e48516679c monitor-config-manager: Fallback to closed laptop lid configuration
When closing the lid of a laptop, we reconfigure all the monitors in order
to update the CRTCs and (if enabled) the global UI scaling factor.

To do this, we try first to reuse the current configuration for the usable
monitors, but if we have only monitor enabled and this one is on the laptop
lid we just end up creating a new configuration where the primary monitor is
the laptop one (as per find_primary_monitor() in MetaMonitorConfigManager),
but ignoring the user parameters.

In case the user selected a different resolution / scaling compared to the
default one, while the laptop lid is closed we might change the monitors
layout, causing applications to rescale or reposition.

To avoid this, when creating the monitors configuration from the current
current state, in case we have only one monitor available and that one is
the laptop panel, let's just reuse this configuration.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1200
2020-04-16 15:14:03 +00:00
65a6c4c361 compositor: Add support for direct scanout of Wayland surfaces
Try to bypass compositing if there is a fullscreen toplevel window with
a buffer compatible with the primary plane of the monitor it is
fullscreen on. Only non-mirrored is currently supported; as well as
fullscreened on a single monitor. It should be possible to extend with
more cases, but this starts small.

It does this by introducing a new MetaCompositor sub type
MetaCompositorNative specific to the native backend, which derives from
MetaCompositorServer, containing functionality only relevant for when
running on top of the native backend.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/798
2020-04-16 15:05:52 +02:00
b51c468c0f later: Listen to MetaCompositor signal instead of clutter
We need to coordinate with MetaCompositor during pre-paint so that we
have control over whether MetaLater callbacks happen first, or the
MetaCompositor pre-paint logic.

In order to do so, make MetaLater listen to a new signal "pre-paint" on
MetaCompositor, that is called MetaCompositors own pre-paint handling.

This fixes an issue where the top window actor was calculated after the
MetaCompositor pre-paint handling, meaning the top actor being painted
was out-of-date.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/798
2020-04-16 15:05:52 +02:00
2e7d02f1ce later: Make MetaCompositor the owner of the MetaLaters state
Since the order of destruction during MetaDisplay tear down is a bit
unordered, there are pieces that try to destruct its compositing
dependent pieces (i.e. queued MetaLater callbacks) after MetaCompositor
has been cleaned up, meaning we need to put some slightly awkward NULL
checks to avoid crashing.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/798
2020-04-16 15:05:52 +02:00
dc4fe780f7 display: Initialize MetaCompositor in two steps
MetaCompositor is the place in mutter that manages the higher level
state of compositing, such as handling what happens before and after
paint. In order for other units that depend on having a compositor
instance active, but should be initialized before the X11 implementation
of MetaCompositor registers as a X11 compositing manager, split the
initialization of compositing into two steps:

 1) Instantiate the object - only construct the instance, making it
    possible for users to start listening to signals etc
 2) Manage - this e.g. establishes the compositor as the X11 compositing
    manager and similar things.

This will enable us to put compositing dependent scattered global
variables into a MetaCompositor owned object.

For now, compositor management is internally done by calling a new
`meta_compositor_do_manage()`, as right now we can't change the API of
`meta_compositor_manage()` as it is public. For the next version, manual
management of compositing will removed from the public API, and only
managed internally.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/798
2020-04-16 15:05:52 +02:00
d682cdb078 util: Move MetaLater to its own file
While at it, fix some style inconsistencies, for now use a single
singleton struct instead of multiple static variables, and
other non-functional cleanups. Semantically, there is no changes
introduced.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/798
2020-04-16 15:05:52 +02:00
ff7a42b8bc wayland: Add API to acquire a CoglScanout from a surface
This will check whether the current backing buffer is compatible with
the primary plane of the passed CoglOnscreen. Since this will extend the
time before a buffer is released, the MetaWaylandBufferRef is swapped
and orphaned if a new buffer is committed before the previous one was
released. It'll eventually be released, usually by the next page flip
callback.

Currently implemented for EGLImage and DMA-BUF buffer types.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/798
2020-04-16 15:05:52 +02:00
4b1805c306 wayland/dma-buf: Handle getting dma-buf from detached buffer handle
We might still have a MetaWaylandBuffer for a wl_buffer that was
destroyed. Handle trying to fetch the MetaWaylandDmaBufBuffer from such
a buffer gracefully.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/798
2020-04-16 15:05:52 +02:00
03c00e4944 wayland/dma-buf: Minor style fix
Indentation was off.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/798
2020-04-16 15:05:52 +02:00
cb05b16414 wayland/dma-buf: Don't advertise modifier support by default
Advertising support for modifiers means we will most likely not not be
able to scan out client buffers directly, meaning it just as likely that
we won't be able to scan out even fullscreen windows without atomic KMS.

When we have atomic support, we should advertise support for modifiers
if atomic is used to drive the CRTCs, as we by then can check whether we
can scan out directly, place in an overlay plane, etc.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/798
2020-04-16 15:05:52 +02:00
b9fe9c736a onscreen/native: Add API to check whether buffer is scanout compatible
While this is fairly incomplete, as to check things fully we need to use
TEST_ONLY in atomic to try out a complete assignment on the device, but
this works well enough for legacy non-modifier cases.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/798
2020-04-16 15:05:52 +02:00
3dd8861fbf renderer/native: Add API to get primary GPU
Will be used when acquiring scanouts from Wayland buffers.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/798
2020-04-16 15:05:52 +02:00
753066598f clutter/view: Make it possible to assign a temporary direct scanout
Make it possible to cause the next frame to scan out directly from the
passed CoglScannout. This makes it possible to completely bypass
compositing for the following frame.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/798
2020-04-16 15:05:52 +02:00
3da8c1bfdc cogl/onscreen: Add API to scanout a buffer directly
Instead of always swapping buffers and flipping the back buffer, make it
possible to scan out a provided buffer directly without swapping any EGL
buffers.

A buffer is passed as an object implementing the empty CoglScanout
interface. It is only possible to do this in the native backend; and the
interface is implemented by MetaDrmBufferGbm. When directly scanned out,
instead of calling gbm_surface_lock_front_buffer() to get the gbm_bo and
fbid, get it directly from the MetaDrmBufferGbm, and use that to create
the page flip KMS update.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/798
2020-04-16 15:05:15 +02:00
f8ee974628 wayland/buffer-ref: Add helpers for use count tracking
https://gitlab.gnome.org/GNOME/mutter/merge_requests/798
2020-04-16 14:08:19 +02:00
f36120757f wayland: Make MetaWaylandBufferRef reference counted
So that we can have a more dynamic ownership.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/798
2020-04-16 14:08:19 +02:00
bc178b711f clutter/actor: Add semi-private API to check for transitions
Transitions are used for animating actors when e.g. going from/to
fullscreen, and the like. We need to know such things when deciding
whether to avoid compositing a window actor, so make add API visible to
mutter that checks whether there are any transitions active.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/798
2020-04-16 14:08:19 +02:00
5dad87cfb9 surface-actor-x11: Move window related unredirect logic to MetaWindowX11
Better to have the relevant object figure out whether it is a good
position to be unredirectable other than the actor, which should be
responsible for being composited.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/798
2020-04-16 14:08:19 +02:00
0f8f607e4c window/x11: Use G_DECLARE_DERIVABLE_TYPE()
This removes the MetaWindowX11::priv pointer. It is replaced with a
meta_window_x11_get_private() helper function, and another method to get
the client rect without going through MetaWindowX11Private.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/798
2020-04-16 14:08:19 +02:00
282aada13a drm-buffer/gbm: Support both surface and standalone buffers
Surface buffers are created with meta_drm_buffer_new_acquire(), taking a
gbm_surface acquiring the gbm itself, and meta_drm_buffer_new_take()
that takes over ownership of a passed gbm_bo.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/798
2020-04-16 14:08:19 +02:00
47002bf0cd wayland/surface: Put buffer reference on heap
Currently a buffer use count always reaches zero before it is replaced.
This is due to the fact that at the point a new buffer is attached, the
last potential user releases it (the stage) since the currently
displayed frame has a composited copy of the buffer.

This may however change, if a buffer is scanned out directly, meaning it
should not be released until the page flip callback is invoked.

Prepare for this by making the buffer reference a heap allocated struct,
enabling us to keep a pointer to it longer than the buffer is attached.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/798
2020-04-16 10:43:34 +02:00
3d47c7edc1 cursor-renderer-native: Take CRTC transform into account
The CRTC level transform (not necessarily the hw transform) must be
taken into account when calculating the position of the CRTC in the
stage coordinate space, when placing the hw cursor, otherwise we'll
place the cursor as if the monitor was not rotated.

This wasn't a problem in the past, as with rotation, we always used the
OpenGL cursor, so the issue newer showed.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1199
2020-04-15 15:29:55 +00:00
bc350f37f5 renderer-native: Use CRTC layout in stage view
The port to per CRTC views was incomplete; we still used the logical
monitor layout as the stage view layout, while still using one view per
CRTC.

This worked fine for most cases, e.g. regular monitors, tiled or
non-tiled, transformed or non-transformed. Where it broke, however, was
when a monitor consists of multiple CRTCs. We already have the layout a
CRTC corresponds to on the stage kept with the CRTC metadata, so use
this directly.

Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/1170

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1199`
2020-04-15 15:29:55 +00:00
b55e2e1df9 tests/monitor-unit-tests: Test non-hw-transform rotated tiled monitors
Should affect the assigned transform, but not the layout, as that is the
layout on the stage, not the coordinates in any buffer.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1199
2020-04-15 15:29:55 +00:00
43baf643d4 monitor-config-manager: Only use crtc transform for assignment
The CRTC level transform (i.e. not necessarily the one set on the
hardware) is what is relevant for calculating the layout the CRTC will
have on the stage, so only use the one that can be handled by the
hardware for the CRTC assignment.

This makes the CRTC layout valid for tiled monitors.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1199
2020-04-15 15:29:55 +00:00
21b8ae10b8 monitor: Fix tile coordinate calculation
Previously the tile coordinate was used to offset a CRTC scanout
coordinate within a larger framebuffer. Since 3.36 we're always
scanning out from (0, 0) as we always have one framebuffer per CRTC; we
instead use the tile coordinate to calculate the coordinate the tile has
in the stage view. Adapt calculation to fulfil this promise instead of
the old one.

This also corrects the tiled custom monitor test case.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1199
2020-04-15 15:29:55 +00:00
425a10de11 clutter: Use #mesondefine
Use #mesondefine instead of manual concatenation.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1179
2020-04-15 12:56:34 +00:00
d0ef660ff6 clutter: fix memleak in test error path
If clutter_init fails then we will not free state.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1195
2020-04-12 21:40:20 +00:00
506e06589b test-utils: Only initialize client when we're returning it
test_client_new might return early if conditions are not met, leaving some
allocated data around without freeing it.

Since we're not using the client before, there's no need to initialize it early
and just initialize it when it's going to be returned.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1195
2020-04-12 21:40:20 +00:00
1d75d5aa2f group: Free group if returning early
If we get an error when fetching the window attributes, the group isn't ever
free'd, so use an autopointer instead, releasing the stolen one.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1195
2020-04-12 21:40:20 +00:00
645d596f9d cogl: Use autopointers to free structs on return
This is a potential leak discovered by static analysis, in fact if
_COGL_GET_CONTEXT returns, the newly allocated struct isn't released.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1195
2020-04-12 21:40:20 +00:00
3e967d731a input-settings: fix device list iteration
Dereference the loop variable rather than the original list head. This
fixes a regression introduced in 4413b86a3 ("backends: Replace
ClutterDeviceManager usage in favor of ClutterSeat", 2019-10-04) which
broke button scrolling with trackballs.

Closes:https://gitlab.gnome.org/GNOME/mutter/-/issues/1120
2020-04-11 18:59:14 +01:00
167fd07e01 x11: Forward current selection state when initializing X11 selections
Most visible with xwayland-on-demand, at the time of setting things up
for X11 selections, we don't forward the current state. This makes the
first started X11 app oblivious to eg. the current clipboard.

Syncing selections up at the time of initializing the X11 selection
stuff ensures that doesn't happen.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1186
2020-04-09 21:30:05 +00:00
fbd6366edd core: Add private function to get the current selection owner
This is a bit untidy to expose, however may be necessary internally.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1186
2020-04-09 21:30:05 +00:00
23d0bdd46d cogl: Remove unused fields from CoglContext
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1191
2020-04-09 11:48:03 -04:00
0d0834f87c Revert "clutter/click-action: Do not process captured event if action is disabled"
This reverts commit 5f5ce08ba4. There is
no way to reach this callback when the click action is disabled.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1188
2020-04-08 20:31:58 +00:00
676997e0af clutter/click-action: Make sure to never schedule more than one timeout
click_action_query_long_press() can potentially schedule more than
one timeout, since it doesn't clear any already-existing timeout.

Make sure to clear the long press timeout before scheduling a new
one.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1188
2020-04-08 20:31:58 +00:00
633d5d1b84 clutter/gesture-action: Cancel gesture when disabling the action
Like the click action, it makes sense to cancel the ongoing gesture
when the action is disabled. Do so by overriding our new friend,
ClutterActorMeta.set_enabled, and canceling the gesture when disabling
the action.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1188
2020-04-08 20:31:58 +00:00
f620f43353 clutter/click-action: Release when disabling
ClutterClickAction, like other actions, can potentially be disabled
at any time (that is not during painting). When that happens with
ClutterClickAction, it must release all timeouts and disconnect from
the stage's 'capture-event'.

Override ClutterActorMeta.set_enabled and release the click action
when the action is being disabled.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1188
2020-04-08 20:31:58 +00:00
eb6e1f694a clutter: Remove drag and drop actions
We aren't using those actions in the shell or anywhere in Mutter, our
DnD support is implemented on the shell side.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/789
2020-04-08 20:21:31 +00:00
6aead84d7a clutter/offscreen-effect: Override ClutterActorMeta.set_enabled
Again, the same case of the previous commits.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1187
2020-04-08 12:14:02 -03:00
5b984c1e53 clutter/constraint: Override ClutterActorMeta.set_enabled
Pretty much like the previous commit.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1187
2020-04-08 12:14:00 -03:00
7660ca2579 clutter/effect: Override ClutterActorMeta.set_enabled
Instead of using GObject.notify to queue a redraw, use the new
ClutterActorMeta.set_enabled vfunc.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1187
2020-04-08 12:13:57 -03:00
ac52631e8a clutter/actor-meta: Add a new 'set_enabled' vfunc
Various subclasses of ClutterActorMeta need to reacto to being
disabled. Right now, however, the only way to do that is by
overriding GObject's 'notify' vfunc, and doing a string comparison
against "enabled".

Add a new vfunc to ClutterActorMeta in order to replace this bad
practice.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1187
2020-04-08 12:13:47 -03:00
5b30a52bbd wayland: preserve xkb_state on VT switch
On VT switch, the devices are removed, which means for Wayland disabling
the keyboard.

When the keyboard is disabled, the associated `xkb_state` is freed and
recreated whenever the keyboard is re-enabled when switching back to the
compositor VT.

That means the `xkb_state` for Wayland is lost whereas the same for
clutter is kept, which causes to a discrepancy with locked modifiers on
VT switch.

To avoid that issue, preserve the XKB info only to dispose it when the
keyboard is eventually finalized.

Closes: https://gitlab.gnome.org/GNOME/mutter/issues/344
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1185
2020-04-08 13:16:25 +00:00
a5294ce55f cogl: Remove CoglPath and the tesselator
This was barely used, and doesn't represent the way we want to
do 2D rendering.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1126
2020-04-08 11:38:48 +02:00
553372ffb7 clutter: Drop CoglPaths handling from ClutterPaintNode
This seems unused.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1126
2020-04-08 11:38:46 +02:00
f672d6ee3b clutter/text: Drop usage of ClutterPath
In the unlikely case we have multiple rectangles in our selection
(selection spanning several lines, or across LTR/RTL bounds), paint each
of those instead of setting a CoglPath-based clip/fill.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1126
2020-04-08 11:37:11 +02:00
ba3417667f wayland/xdnd: Add error traps around Xdnd* IPC
Make all of them spew criticals, except for XdndLeave as it's feasible
to expect the window we are sending the event to did disappear in the
way (eg. if the window is destroyed while the DnD operation is ongoing
and the pointer is over the window).

Fixes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2590

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1184
2020-04-07 18:08:03 +00:00
50fa002a19 backends/native: Translate coordinates of absolute motion events
The motion events of tablets for example need to be mapped on the
selected screen area if the input device is configured to use only a
part of the active logical monitor.
To achieve this behavior each motion event is transformed using the
transformation matrix set for the input device.

Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/1118
2020-04-07 17:50:11 +00:00
f0718c7d95 backends/x11: Fix access to WacomDevice
At some point we crossed the streams... In a short timespan we had
1f00aba92c merged, pushing WacomDevice to a common parent object,
and dcaa45fc0c implementing device grouping for X11.

The latter did not rely on the former, and just happened to
merge/compile without issues, but would promptly trigger a crash
whenever the API would be used.

Drop all traces of the WacomDevice internal to MetaInputDeviceX11.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1183
2020-04-07 17:36:13 +00:00
e74c2e42cf clutter/stage: Don't assume stage relayouts reallocate everything
With the introduction of "shallow" relayouts, we are now able to enter
allocation cycles not only at the stage but also deeper down the
hierarchy if we know an actors allocation isn't affected by its children
since the NO_LAYOUT flag is set.

Now that means when queuing relayouts it's possible that
`priv->needs_allocation` gets set to TRUE for some actors down the
hierarchy, but not for actors higher up in the hierarchy. An actor tree
where that happens could look like that:

stage -> container -> container2 (NO_LAYOUT) -> textActor

With that tree, if the "textActor" queues a relayout, "container2" will
be added to the relayout hashtable of the stage and the actors "stage"
and "container" will have `priv->needs_allocation` set to FALSE.

Now if another relayout on the stage actor is queued,
`clutter_stage_queue_actor_relayout()` currently removes all the other
hashtable entries in favour of the stage entry, (wrongly) assuming that
will allocate everything. It doesn't allocate everything because in the
example above "container" has `priv->needs_allocation` set to FALSE,
which makes clutter_actor_allocate() return early before allocating its
children, so in the end "container2" will never get a new allocation.

To fix this, stop flushing the relayout hashtable when queuing a
stage-relayout and still add new entries to the hashtable if a stage
relayout is already queued to make sure we still go through all the
previously queued "shallow" relayouts. That shouldn't hurt performance,
too, because as soon as an actor got allocated once, it doesn't need an
allocation anymore and should bail out in clutter_actor_allocate() as
long as it's absolute position didn't change.

Fixes https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2538

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1173
2020-04-07 14:34:52 +00:00
ce64ab5449 ci: Rebase docker image to F32
We have branched now, time for a shiny new CI image.

Update the Dockerfile to:

 - switch to F32
 - use a single shared copr
 - drop dependencies that are now covered by builddep
 - do not include weak deps

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1176
2020-04-07 13:57:31 +00:00
8df3b21a51 window: Check aliveness a bit less aggressively
Currently we check whether a window is alive everytime it's focused.
This means that an application that doesn't respond to the check-alive
event during startup always showing the "application froze" dialog,
without the user ever trying to interact with it.

An example where this tends to to happen is with games, and for this
particular scenario, it's purely an annoyance, as I never tried to
interact with the game window in the first place, so I don't care that
it's not responding - it's loading.

To avoid these unnecessary particular "app-is-frozen" popups, remove the
alive check from the focus function, and instead move it back to the
"meta_window_activate_full()" call. To also trigger it slightly more
often, also add it to the path that triggers the window focus when a
user actively clicks on the window.

This means that we currently check whether a window is alive on:

  * Any time the window is activated. This means e.g. alt-tab or
    selecting the window in the overview.
  * The user clicks on the window.

Note that the second only works for an already focused window on
Wayland, as on X11, we don't refocus it. This particular case isn't
changed with this commit, as we didn't call meta_window_focus() to begin
with here.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1182
2020-04-07 10:46:01 +02:00
08431a127a clutter/stage: Remove ability to set custom perspective
Unused, and complicates things, so drop it.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1175
2020-04-06 14:08:26 +00:00
3f068d1138 clutter/stage: Remove unused paint_data/notify fields
It's usage was removed in ec911dc8b9 back
in 2014. Lets get rid of the left over fields.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1175
2020-04-06 14:08:26 +00:00
6f0e5b0b56 clutter/stage: Remove 'accept-focus' property
Also unused, only valid on X11. Meant for applications. Lets drop it.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1175
2020-04-06 14:08:26 +00:00
fe27a6ea3b clutter/stage: Remove hide/show cursor API
This removes it from the stage window API too. It's managed by the
mutter backends, so we don't need the stage window to do it as well.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1175
2020-04-06 14:08:25 +00:00
1301770dcb clutter/stage: Remove 'alpha' property
Was unused except for in a test, lets remove it. Half transparent
monitors will probably have to be dealt with some other way anyway.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1175
2020-04-06 14:08:25 +00:00
1551b6d386 Update Slovak translation
(cherry picked from commit b0709504ea)
2020-04-05 20:22:30 +00:00
a6f94696e2 window-actor: Set viewport when blitting to screencast fb
This fixes an issue where a non-maximized screen casted window would be
stretched to fill the whole screen cast stream, instead of just the crop
that corresponds to the current window size.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1174
2020-04-03 16:14:02 +00:00
c389aadff9 cogl: Defend against empty or unallocated framebuffers
It isn't immediately obvious that this is impossible, because there's some
"action at a distance" going on with framebuffers that have their size
set lazily, after their textures get allocated; so let's make this a
critical warning rather than crashing.

In particular, this works around a crash when gnome-shell tries to blur a
background that hasn't yet had any space allocated for it - which it seems
is really an actor layout bug, but more robustness seems good to have.

Workaround for <https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2538>.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1172

Signed-off-by: Simon McVittie <smcv@debian.org>
2020-04-03 10:32:02 +01:00
37eda498f2 cogl: Don't allow creating sized textures with 0 pixels
A texture with no pixels isn't a useful thing to have, and breaks
assumptions elsewhere. For example, CoglFramebuffer assumes that after
a texture has been allocated, it will have width and height both greater
than 0.

In particular, this works around a crash when gnome-shell tries to blur a
background that hasn't yet had any space allocated for it - which it seems
is really an actor layout bug, but more robustness seems good to have.

Workaround for <https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2538>.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1172

Signed-off-by: Simon McVittie <smcv@debian.org>
2020-04-03 10:31:54 +01:00
7d2df52336 clutter/offscreen-effect: Rename CoglPipeline field to 'pipeline'
This is the same case of the layer node: a CoglPipeline field that
is not called 'pipeline' makes it harder to figure out what it
really is.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1167
2020-04-02 15:50:40 -03:00
5d27e5415d clutter/layer-node: Use non-deprecated cogl_texture_2d_new_with_size()
Use the non-deprecated cogl_texture_2d_new_with_size() function to create
the texture backing the FBO, and set it to pre-multiplied before allocating
it.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1167
2020-04-02 15:50:14 -03:00
625773fba4 clutter/layer-node: Trivial code cleanup
Both the valid and the error code paths end up unreffing
the texture. Move the unref to the shared code path.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1167
2020-04-02 15:50:14 -03:00
7d79ae7b07 cluter/layer-node: Remove CoglTexture from structure
It is not used anywhere beyond the initializer, and won't be
used later on.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1167
2020-04-02 15:50:14 -03:00
5817779656 clutter/layer-node: Rename CoglPipeline field to 'pipeline'
So it clearly reads what it actually is.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1167
2020-04-02 15:50:14 -03:00
5f5ce08ba4 clutter/click-action: Do not process captured event if action is disabled
Disabling a click action after a button-press but before a
button-release is captured makes ClutterClickAction connect to
captured-event and never disconnect.

This change fixes it by making sure the captured-event is only
processed if the action is still enabled, otherwise releasing
the action (reset state) and propagating the event.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1170
2020-04-02 13:13:51 -03:00
223f033780 clutter/offscreen-effect: Don't ever early out out of post paint
If we would, we'd miss popping the framebuffer from the pant context
framebuffer stack.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1166
2020-04-01 18:40:39 +00:00
50e0ae3cf3 clutter/actor-meta: Warn if an actor modifier is altered mid paint
Nothing should ever disable an actor modifier (e.g. effect) during the
paint sequence, nor should any actor be set or unset on it. If this
would happen, log warnings so that it can be tracked down.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1166
2020-04-01 18:40:39 +00:00
a8f6cada88 x11: fix compilation if 'libwacom=false'
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1168
2020-04-01 15:23:43 +01:00
7f488e3e1d tests/actor-pick: Allocate actor before picking
Picking now only happens on allocated actors, but the
callback in the actor-pick test is not waiting for the
stage to run an allocation cycle. Ideally, we'd wait
for this cycle, but for now, forcing an allocation works
as well.

Allocate the overlay actor in the actor-pick test.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1169
2020-03-31 19:10:55 -03:00
059d2144b2 tests/actor-pick: Remove tabs
They're evil.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1169
2020-03-31 19:10:10 -03:00
902302a174 clutter/actor: Fix pick when actor is not allocated
When selecting the pick regions for an actor we were not considering
whether the actor was allocated and that was causing issues where the
preferred width/height of the actor was used when deciding whether
the actor should be considered as a pick target.

Check if the actor has a valid allocation, in addition to being mapped
and being in pick mode, in clutter_actor_should_pick_paint().

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1169
2020-03-31 19:10:06 -03:00
f98ca7683c build: Post-release version bump
Officially start the next development cycle \o/
2020-03-31 00:34:44 +02:00
479fb0549c Revert "Bump version to 3.36.1"
This reverts commit 52e5d6fc94.
2020-03-30 21:44:55 +02:00
52e5d6fc94 Bump version to 3.36.1
Update NEWS.
2020-03-30 21:29:03 +02:00
09a6031c69 window-actor: Force full actor geometry sync when mapping
Normally we bail out in `sync_actor_geometry()`. The comment there
states:
```
Normally we want freezing a window to also freeze its position; this allows
windows to atomically move and resize together, either under app control,
or because the user is resizing from the left/top. But on initial placement
we need to assign a position, since immediately after the window
is shown, the map effect will go into effect and prevent further geometry
updates.
```

The signal for the initial sync originates in `MetaWindow` though and predates
`xdg_toplevel_set_maximized`, which again calls `meta_window_force_placement`,
triggering the signal too early. As a result, Wayland clients that start up
maximized have a wrong map animation, starting in the top-left corner.

In order to fix this without changing big parts of the geometry logic and risking
regressions, force the initial sync again before mapping.

Solution suggested by Jonas Ådahl.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1164
2020-03-30 15:59:48 +00:00
dbe919ef92 wayland/surface: Check for surface role in meta_wayland_surface_get_window()
The function can get called without valid surface role, e.g. from
`zwp_xwayland_keyboard_grab_manager_grab()`.

Debugged by @piegamesde

Fixes https://gitlab.gnome.org/GNOME/mutter/-/issues/1147
2020-03-30 16:08:13 +02:00
2907ee93cc wayland/pointer-constraints: Fix typo
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1163
2020-03-29 19:48:33 +01:00
aa136f4515 cogl-pango: Special case alpha of 0 for color glyphs
Like ed10aea44d, but for color glyphs. Since they do use the alpha
component from the given color.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1161
2020-03-29 12:53:39 +00:00
8748841094 cogl: Export two more functions
cogl_object_[get|set]_value_object() are annotated as [get|set]-value-func
for objects and primitives, so they must be visible for any derived types
to be usable from introspection.

https://gitlab.gnome.org/GNOME/mutter/-/issues/1146
2020-03-29 12:17:11 +00:00
86f2885e98 cogl: Remove obsolete .map file
Obsolete since commit 6885c37784.

https://gitlab.gnome.org/GNOME/mutter/-/issues/1146
2020-03-29 12:17:11 +00:00
bb5ea0580f wayland: Translate delete-surrounding properly to protocols
IBusInputContext/ClutterInputFocus/GtkIMContext all go for offset+len
for their ::delete-surrounding signals, with offset being a signed int
(neg. to delete towards left of selection, pos. to delete towards right
of selection) and len being an unsigned int from the offset (and
presumably, skipping the current selection).

The text-input protocols however pass in this event two unsigned integers,
one being the length of text to delete towards the left of the selection,
and another the length of text to delete towards the right of the selection.

To translate properly these semantics, positive offsets shouldn't account
for before_length, and negative offset+len shouldn't account for after_length.
The offset/length approach may of course represent deletions that are
detached from the current cursor/selection, we simply delete the whole range
from the cursor/selection positions then.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/517
2020-03-29 11:37:27 +00:00
2cfdbbd730 clutter: Enable negative offsets in delete surrounding text
The input method can assign a negative value to
clutter_input_method_delete_surrounding() to move the cursor to the left.
But Wayland protocol accepts positive values in delete_surrounding() and
GTK converts the values to the negative ones in
text_input_delete_surrounding_text_apply().

https://gitlab.gnome.org/GNOME/mutter/issues/539
2020-03-29 11:37:27 +00:00
9f31e7252c backends/native: Release virtual buttons on dispose instead of finalize
GObject recommends to break references to other objects on dispose
instead of finalize, also we want to release the pressed virtual buttons
as early as possible if we know the object is getting destroyed.

So release the pressed buttons and unref our virtual
MetaInputDeviceNative when the dispose vfunc is called, which also
allows us to release the buttons immediately from javascript instead of
waiting for the garbage collector by calling run_dispose() on the
object.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1157
2020-03-28 16:55:59 +00:00
e8ea5ecd8a cogl-pango: Factor in default color alpha again
In commit d846fabda we moved to using the override color alpha, however
it was missed that the actor opacity is transferred to the PangoRenderer
through the default color alpha, and the reason it was used there.

We actually want to factor in both alpha values, in order to respect
both foreground color alpha and actor opacity. This is done on the
unpremultiplied color, so we just need to change the alpha value.

Fixes effects on text actors that involve actor opacity.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1158
2020-03-28 12:48:11 +01:00
ed10aea44d cogl-pango: Special case alpha of 0
pango_renderer_get_alpha() returns 0 to indicate that the alpha value
should be inherited from the environment, but we are passing it on
(and therefore making the text fully translucent).

Instead, make the text fully opaque as expected.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1156
2020-03-28 01:04:17 +01:00
fed5f4d9aa window-actor: Inhibit culling when blitting to screencast
This allows us to screencast any window continuously, even
without it being visible. Because it's still being painted,
clients continue to receive frame callbacks, and people
are happy again.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1129
2020-03-27 23:29:58 +00:00
73250b8f4c clutter/actor: Add culling inhibiting API
This will allow us to continue painting actors that are
outside the visible boundaries of the stage view.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1129
2020-03-27 23:29:58 +00:00
f6700f19a7 window-stream-src: Finish framebuffer after blitting
Just like what's done for monitor screencasting. Unfortunately, there's
no mechanism to share fences with PipeWire clients yet, which forces
us to guarantee that a frame is completed after blitting.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1129
2020-03-27 23:29:58 +00:00
ea34915df3 window-stream-src: Implement cursor blitting
A regression compared to the old code, we're not drawing the cursor
when on EMBEDDED mode.

Blit the cursor to the screencast framebuffer when on EMBEDDED mode.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1129
2020-03-27 23:29:58 +00:00
37742c5cde window-stream-src: Ensure initial frame is recorded
MetaScreenCastWindowStreamSrc connects to the "damaged" signal of
MetaWindowActor. This signal is not exactly tied to the paint cycle
of the stage, and a damage may take quite a while to arrive when
a client doesn't want to draw anything. For that reason, the window
screencast can start empty, waiting for a damage to arrive.

Ensure at least one frame is recorded when enabling the window stream.

Fixes: https://gitlab.gnome.org/GNOME/mutter/issues/1097

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1129
2020-03-27 23:29:57 +00:00
cdd27d0e53 window-actor: Clip before translate when blitting
cogl_framebuffer_push_rectangle_clip() acts on the current modelview
matrix. That means the result of clipping then translating will be
different of the result of translating then clipping.

What we want for window screencasting is the former, not the latter.
Move the translation code (and associated) to after clipping.

Fixes: https://gitlab.gnome.org/GNOME/mutter/issues/1097

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1129
2020-03-27 23:29:57 +00:00
82778f72a4 window-actor: Shuffle some lines around
Move the CoglColor assignment right above the cogl_framebuffer_clear() call,
and let these wonderful partners together to delight us with an easier to
read code.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1129
2020-03-27 23:29:57 +00:00
d846fabda2 cogl-pango: Forward alpha from correct color
Use the override color alpha, if set.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1155
2020-03-27 23:33:38 +01:00
2d94a34a14 cogl-pango: Honor foreground alpha PangoAttribute
Instead of hardcoding 0xff as alpha, forward this attribute.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1155
2020-03-27 23:33:35 +01:00
92710d8f89 clutter/stage-cogl: Check for buffer age early
Fix a regression that got introduced with
c483b52d24 where we started passing the
redraw_clip to paint_stage() instead of creating a temporary view_region
for unclipped redraws: In case we detect an invalid buffer age, we fall
back to doing an unclipped redraw after we passed the first check
setting up may_use_clipped_redraw. That means we didn't reset the
redraw_clip to the view_rect, and we're now going to redraw the stage
using the original redraw clip even though we're swapping the full
framebuffer without damage.

To fix that, check for the buffer age before setting up the
fb_clip_region and the redraw_clip and set may_use_clipped_redraw to
FALSE if the buffer age is invalid, too. This ensures the redraw_clip is
always going to be correctly set to the view rect when we want to force
a full redraw.

Fixes https://gitlab.gnome.org/GNOME/mutter/issues/1128
2020-03-27 16:37:45 +00:00
da600b8400 cursor-renderer-native: Take panel-orientation into account for sprite transform
When calculating the transform we should apply to the cursor sprite
before uploading it to the cursor plane, we must also take into
account non upright mounted LCD panels.

Otherwise the cursor ends up 90 degrees rotated on devices where the
LCD panel is mounted 90 degrees rotated in its enclosure.

This commit fixes this by calling meta_monitor_logical_to_crtc_transform
in get_common_crtc_sprite_transform_for_logical_monitors to adjust the
transform for each Monitor in the LogicalMonitor.

Fixes: https://gitlab.gnome.org/GNOME/mutter/-/issues/1123

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1153
2020-03-27 15:10:35 +00:00
6aa546145f core: Demote tiff and bmp image formats in the clipboard manager
Support for them appears to be way less common than e.g. png, which is
currently the preferred format from Firefox, Chromium, Libreoffice and others.
Adopt to that fact.

As a side effect, this works around a bug observed when copying images in
Firefox on Wayland.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1141
2020-03-27 14:37:29 +00:00
3956ffd5e8 cogl/driver: Remove GError from context_init
It is not used by anyone, let's just remove it.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1152
2020-03-27 09:01:43 -03:00
05341221d4 cogl/renderer: Remove documentation of nonexistent enum
We don't want to delve into the philosohical study of the not-being,
so let's just not document an enum value that doesn't exist anymore.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1151
2020-03-26 18:44:13 -03:00
95642d05a6 cogl/gl: Move shared functions to shared file
Cogl shares some GL functions between the GLES and the big
GL drivers. Namely, it shares _cogl_driver_gl_context_init
and _cogl_driver_gl_context_deinit between these two drivers.

The plot twist is: even though these functions are shared and
their prototypes are in cogl-util-gl-private.h, they're actually
implemented inside cogl-driver-gl.c, which is strictly only
about the big GL driver.

This is problematic when building Mutter on ARM v7, where we
need to disable OpenGL, but keep GLES enabled.

Fix this by moving the shared GL functions to a shared GL file.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1151
2020-03-26 18:43:32 -03:00
51cd8aed96 ci: Add a new 'build-without-opengl-and-glx' step
To make sure we don't regress with this specific set of flags.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1151
2020-03-26 18:24:54 -03:00
ac01e69a67 window-wayland: Don't use grab op for some other window when resizing
When resizing a window interactively, we'll set a grab operation and a
grab window, among other things. If we're resizing (including setting
initial size, i.e. mapping) another window, that didn't change position,
don't use the gravity of the grab operation when resizing our own
window.

This fixes an issue with jumpy popup position when moving a previously
mapped gtk popover.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/999
2020-03-26 17:44:09 +01:00
a68e6972a2 cursor-renderer-native: Set cursor hotspot metadata on plane assignment
This triggers the paths in the legacy KMS backend to use
drmModeSetCursor2(), making virtual machines using "seamless mouse mode"
behave correctly again.

Fixes: https://gitlab.gnome.org/GNOME/mutter/issues/1094

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1136
2020-03-26 16:18:28 +00:00
55cf1c1496 kms/plane-assignment: Add API to set cursor hotspot metadata
The transactional KMS API has been modelled after atomic KMS. Atomic KMS
currently doesn't support forwarding cursor hotspot metadata, thus it
was left out of the transactional KMS API having the user set the simply
create a plane assigment with the cursor sprite assigned to a cursor
plane using regular coordinates.

This, however, proved to be inadequate for virtual machines using
"seamless mouse mode" where they rely on the cursor position to
correspond to the actual cursor position of the virtual machine, not the
cursor plane. In effect, this caused cursor positions to look "shifted".

Fix this by adding back the hotspot metadata, right now as a optional
field to the plane assignment. In the legacy KMS implementation, this is
translated into drmModeSetCursor2() just as before, while still falling
back to drmModeSetCursor() with the plane coordinates, if either there
was no hotspot set, or if drmModeSetCursor2() failed.

Eventually, the atomic KMS API will learn about hotspots, but when
adding our own atomic KMS backend to the transacitonal KMS API, we must
until then still fall back to legacy KMS for virtual machines.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1136
2020-03-26 16:18:28 +00:00
343de21af5 monitor-transform: Add API to transform point
Transforms a point in a rectangle with the origin (0, 0). To be used to
transform cursor hotspots within a cursor sprite.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1136
2020-03-26 16:18:28 +00:00
3c157242fa cursor-sprite: Add API to get dimension
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1136
2020-03-26 16:18:28 +00:00
32c99513c8 clutter/actor: Inherit cloned painting when calculating resource scale
When calculating the resource scale of a clone source, we might end up
in situations where we fail to do so, even though we're in a paint. A
real world example when this may happen if this happens:

 * A client creates a toplevel window
 * A client creates a modal dialog for said toplevel window
 * Said client commits a buffer to the modal before the toplevel

If GNOME Shell is in overview mode, the window group is hidden, and the
toplevel window actor is hidden. When the clone tries to paint, it fails
to calculate the resource scale, as the parent of the parent (window
group) is not currently mapped. It would have succeeded if only the
clone source was unmapped, as it deals with the unmapped actor painting
by setting intermediate state while painting, but this does not work
when the *parent* of the source is unmapped as well.

Fix this by inheriting the unmapped clone paint even when calculating
the resource scale.

This also adds a test case that mimics the sequence of events otherwise
triggered by a client. We can't add a Wayland client to test this, where
we actually crash is in the offscreen redirect effect used by the window
dimming feature in GNOME Shell.

Fixes: https://gitlab.gnome.org/GNOME/mutter/-/issues/808

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1147
2020-03-26 11:42:23 +01:00
40c345d6f3 cursor-sprite-xcursor: Emulate Wayland hotspot limitations
For HiDPI pointer cursors backed by Wayland surfaces, the hotspot must
be placed using integers on the logical pixel grid. In practice what
this means is that if the client loads a cursor sprite with the buffer
scale 2, and it's hotspot is not dividable by 2, it will be rounded
down to an integer that can. E.g. a wl_surface with buffer scale 2 and a
cursor image with hotspot coordinate (7, 7) will have the coordinate
(3.5, 3.5) in surface coordinate space, and will in practice be rounded
down to (3, 3) as the hotspot position in wl_pointer only takes
integers.

To not potentially shift by 1 pixel on HiDPI monitors when switching
between wl_surface backend cursor sprites and built-in ones, make the
built in one emulate the restrictions put up by the Wayland protocol.

This also initializes the theme scale of the xcursor sprite instances to
1, as they may not have been set prior to being used, it'll only happen
in response to "prepare-at" signals being emitted prior to rendering.

Fixes: https://gitlab.gnome.org/GNOME/mutter/issues/1092

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1107
2020-03-26 08:47:37 +00:00
8beef8ccd0 shaped-texture: Fix use-nearest check when viewports are scaled
We checked that the content size was appropriately painted in the stage,
but didn't take into account that the size of the sampled texture
region, meaning that when stage views were scaled, we'd think that we
would draw a texture scaled, as e.g. a 200x200 sized texture with buffer
scale 2 would have the size 100x100. When stage views were not scaled,
we'd apply a geometry scale meaning it'd end up as 200x200 anyway, thus
pass the check, but when stage views are scaled, it'd still be painted
as a 100x100 shaped texture on the stage, thus failing the
are-we-unscaled test.

Fix this by comparing the transformed paint size with the sampled size,
instead of the paint size again, when checking whether we are being
painted scaled or not. For example, when stage views are scaled, our
200x200 buffer with buffer scale 2, thus content size 100x100 will
transform to a 200x200 paint command, thus passing the test. For
non-scaled stage views, our 200x200 buffer with buffer scale 2 thus
content size 100x100 will also transform into a 200x200 paint command,
and will also pass the check, as the texture sample region is still
200x200.

Fixes: https://gitlab.gnome.org/GNOME/mutter/issues/804

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1124
2020-03-26 08:32:46 +00:00
62d0dd907b clutter-utils: Fix a couple of coding style issues
Multiple assignments on the same line were split up, so was a super long
line.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1124
2020-03-26 08:32:46 +00:00
0462208d4e crtc-xrandr: Respect configured RANDR panning
A user may have configured an output to be panning, e.g. using xrandr
--output <output> --mode <mode> --panning <size>. Respect this by making
the logical monitor use the panning size, instead of the mode. This
makes e.g. makes the background cover the whole panning size, and panels
etc will cover the whole top of the panned area, instead of just the top
left part covering the monitor if having panned to (0, 0).

No support is added to configuring panning, i.e. a panned monitor
configuration cannot be stored in monitors.xml.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1085
2020-03-26 09:24:25 +01:00
6885c37784 cogl: Mark exported cogl symbols using COGL_EXPORT
Just like libmutter-clutter, and libmutter, mark exported symbols with
an COGL_EXPORT macro. This removes the .map and .map.in files previously
used, containing a list of semi private symbols. This symbol was out of
date, i.e. pointed to non-existing symbols, and was also replaced with
COGL_EXPORT macros.

unit_test_* symbols are exported by the help of the unit test defining
macro. test_* symbols are no longer supported as it proved unnecessary.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1059
2020-03-26 09:05:38 +01:00
238e41d493 cogl: Install cogl-trace.h and include from cogl.h
This is so that cogl-trace.h can start using things from cogl-macros.h,
and so that it doesn't leak cogl-config.h into the world, while exposing
it to e.g. gnome-shell so that it can make use of it as well. There is
no practical reason why we shouldn't just include cogl-trace.h via
cogl.h as we do with everything else.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1059
2020-03-26 09:05:38 +01:00
8699482475 backends: Check both input settings and mapper for tablet monitors
The upper layers (OSDs basically) want to know the monitor that a
tablet is currently assigned to, not the monitor just as configured
through settings.

This broke proper OSD positioning for display-attached tablets since
commit 87858a4e01, as the MetaInputMapper kicks in precisely when
there is no configured monitor for the given device.

Consulting both about the assigned output will make OSDs pop up
again in the right place.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/971
2020-03-25 19:09:32 +00:00
dcaa45fc0c backends/x11: Implement is_grouped for X11
If the devices have a wacom description, compare those. Otherwise,
look up the devices' VID:PID, if they match they should also be
grouped.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/971
2020-03-25 19:09:32 +00:00
3c4f5ddcb4 core: Let pad mode switch events always go through MetaInputSettings
We used to inhibit all pad actions while the OSD is shown, but one we
would actually want to handle are mode switches while the OSD is open.
So it has an opportunity to catch up to the mode switch.

This lets MetaInputSettings reflect the mode switch (eg. when querying
action labels), so the OSD has an opportunity to update the current
actions.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/975
2020-03-25 19:56:09 +01:00
3aece84499 cogl-pango: Make color glyphs unaffected by foreground color
Making color glyphs affected by the foreground color makes them become
"tinted" on any other color than white. Make it sure we always paint
those white by checking the cached glyph value, the foreground color
will be reset on the next iteration through glyphs.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/850

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1148
2020-03-25 11:14:33 +00:00
40fb06ca17 cogl-pango: Cache whether glyphs are backed up by a color font
This will be necessary later on.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1148
2020-03-25 11:14:33 +00:00
d4c070da88 window: Really propagate effective on-all-workspaces setting to transients
Commit cda9579034 fixed a corner case when setting the initial workspace
state of transient windows, but it still missed a case:

should_be_on_all_workspaces() returns whether the window should be on all
workspaces according to its properties/placement, but it doesn't take
transient relations into account.

That means in case of nested transients, we can still fail the assert:

 1. on-all-workspaces toplevel
 2. should_be_on_all_workspaces() is TRUE for the first transient's parent,
    as the window from (1) has on_all_workspaces_requested == TRUE
 3. should_be_on_all_workspaces() is FALSE for the second transient's
    parent, as the window from (2) is only on-all-workspace because
    of its parent

We can fix this by either using the state from the root ancestor
instead of the direct transient parent, or by using the parent's
on_all_workspaces_state.

The latter is simpler, so go with that.

https://gitlab.gnome.org/GNOME/mutter/issues/1083
2020-03-24 18:15:33 +00:00
d052f9c070 backends: Drop internal WacomDevice in MetaInputSettings
Use the one from MetaInputDevice instead. Since we no longer try
to ask for WacomDevices that weren't first retrieved:

Closes: https://gitlab.gnome.org/GNOME/mutter/issues/1086

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1109
2020-03-24 18:07:31 +00:00
3b88af94e3 backends/x11: Drop internal WacomDevice lookups
Just use the ones from MetaInputDevice.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1109
2020-03-24 18:07:31 +00:00
1f00aba92c backends: Add MetaInputDevice derivable class
This class sits between ClutterInputDevice and the backend implementations,
it will be the despositary of features we need across both backends, but
don't need to offer through Clutter's API.

As a first thing to have there, add a getter for a WacomDevice. This is
something scattered across and somewhat inconsistent (eg. different places
of the code create wacom devices for different device types). Just make it
here for all devices, so users can pick.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1109
2020-03-24 18:07:31 +00:00
ec1195e3ff backends: Fix configuration changes to tap[-and-drag]
Most people just see a harmless warning when applying this setting to
all touchpads (which this patch fixes). But tap[-and-drag] is supposed
to remain enabled for display-less Wacom tablets, despite configuration
changes.

Fix this by using the mapping function, so the setting is forced on for
wacom devices. This happens on a per-device basis, so the warning is
gone too.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1145
2020-03-24 17:05:12 +00:00
18b661cc93 backends: Add mapping function arg to settings_set_bool_setting()
This will be useful to actually determine on a per-device basis the
setting being applied, while still doing changes on a per-device-type
basis.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1145
2020-03-24 17:05:12 +00:00
8592a8591b wayland: Handle NULL preedit text
The preedit text may be NULL (eg. when unsetting it). This started
causing crashes since commit db9b60cc63, duh.

Fixes: https://gitlab.gnome.org/GNOME/mutter/-/issues/1132
2020-03-24 16:07:44 +00:00
7fa7c2aeb7 backends: use the enum name instead of a literal 0
No functional change.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1144
2020-03-24 15:46:43 +10:00
41130b08eb surface-actor: Add culling offset for geometry scale
This fixes a case that was overlooked in
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1036 - when we
have a geometry scale > 1 and Wayland subsurfaces that have an offset
to their parent surface (which is often the case when the toplevel surface
includes decoration/shadows etc.), we have to add extra offset to their
opaque regions so they match their 'visible' location.

This is necessary as `meta_cullable_cull_out_children` moves the coordinate
system during culling, but does not know about geometry scale.

Also, remove the redundant check for `window_actor` - we only hit this code
path if a `window_actor` culls out its children.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1108
2020-03-23 20:28:59 +01:00
1d20045247 surface-actor: Fix memory leak
When we create a new region for an opaque texture we need to free it.
While on it, simplify the check slightly.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1108
2020-03-23 18:10:28 +00:00
c131a9b7fa backends/x11: Observe multiple pad mode switch buttons in a group
Some tablets like the Cintiq 24HDT have several mode switch buttons
per group. Those are meant to jump straight to a given mode, however
we just handle cycling across modes (as most other tablets have a
single mode switch button per group).

So spice up the mode switch handling so we handle multiple mode
switch buttons, assigning each of them a mode. If the device only
has one mode switch button, we do the old-fashioned cycling.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/970
2020-03-20 21:30:30 +01:00
2ecbf6d746 x11: Handle windowing errors while writing selection INCR data
This error was just logged but not raised. Do as the code comment said
and raise a pipe error at that moment, and for subsequent operations
on the output stream (although none besides close() should be expected
after propagating the error properly).

Related: https://gitlab.gnome.org/GNOME/mutter/issues/1065
2020-03-20 16:14:07 +01:00
a13d60aae5 Update Romanian translation 2020-03-19 10:59:57 +00:00
ed4b80cee5 clutter/stage: Rename parameters to match documentation
To silence warnings during GIR generation.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1133
2020-03-18 02:45:43 +01:00
0a6034ef3a monitor-manager: Remove 'mirror' capability
With per-CRTC views, there is nothing stopping NVIDA EGLStream based
rendering to not support monitor mirroring, so lets remove that
restriction.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1098
2020-03-18 00:46:20 +00:00
c9a5b2b22f kms-impl-simple: Handle lack of cached mode set in flip fallback
When a page flip fails with a certain error code, we've treated this as
a hint that page flipping is broken and we should try to use mode
setting instead.

On some drivers, it seems that this error is also reported when there
was no mode set, which means we'll have no cached mode set to use in the
fallback. The lack of prior mode set tends to happen when we hit a race
when the DRM objects change before we have the time to process a hotplug
event.

Handle the lack a missing mode set in the flip fallback path, with the
assumption that we'll get a hotplug event that'll fix things up for us
eventually.

Closes: https://gitlab.gnome.org/GNOME/mutter/issues/917
2020-03-18 00:33:03 +00:00
db9b60cc63 wayland: Represent preedit string cursor offset in bytes
Both IBus and ClutterInputFocus work in character offsets for the cursor
position in the preedit string. However the zwp_text_input protocol does
define the preedit string cursor offset to be in bytes.

Fixes client bugs in representing the caret within the preedit string,
as we were clearly giving the wrong offset.

Fixes: https://gitlab.gnome.org/GNOME/gtk/issues/2517

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1132
2020-03-17 22:15:52 +00:00
fa74da0039 wayland/window: Ignore state changes for popups
We send configure events for state changes e.g. for `appears-focused`,
etc. What we don't want to do is to do this for popup windows, as in
Wayland don't care about this state.

When the focus mode was configured to "sloppy focus" we'd get
`appears-focused` state changes for the popup window only by moving the
mouse cursor around, and while a popup may care about focus, it does not
care about related appearance, as there is no such state in xdg_popup.

What these state changes instead resulted in was absolute window
configuration events, intended for toplevel (xdg_toplevel) windows. In
the end this caused the popup to be positioned aginst at (0, 0) of the
parent window, as the assumptions when the configuration of the popup
was acknowledged is that it had received a relative position window
configuration.

Fix this by simply ignoring any state changes of the window if it is a
popup, meaning we won't send any configuration events intended for
toplevels for state changes. Currently we don't have any way to know
this other than checking whether it has a placement rule. Cleaning up
MetaWindow creation is left to be dealt with another day.

Fixes: https://gitlab.gnome.org/GNOME/mutter/issues/1103

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1122
2020-03-17 21:59:02 +00:00
b310e1d9d7 clutter-stage: Add annotations to clutter_stage_capture
Especially document that out_captures is an array that is given as output.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1131
2020-03-17 15:14:57 +01:00
0053ef2e16 cogl-texture: Add some missing array annotations
This allows bindings to correctly understand that it is an array

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1130
2020-03-17 13:30:31 +01:00
4133b73632 cursor-renderer/native: Skip hw cursor upload if we can't use it
If the CRTCs the cursor is visible on do not share a common scale
and transform, we can't use the cursor hardware plane as we only have one.
We therefore fall back to software / gl cursor.

The check for that currently happens after we tried to upload the cursor image
to the hardware plane though.
This is made worse by the fact that in the scaling step, where we scale the
cursor image to the desired size, until now we expected a valid common scale -
otherwise scaling the image by an uninitialized float.

Make sure we bail out early during the scale/upload step if we don't have common
scales and transforms - to avoid that bug and save some unnecessary work.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1125
2020-03-16 21:51:30 +01:00
074f4974dd input-settings: Specify middle-click-emulation key
Which exists, unlike `emulate-middle`.

Closes: https://gitlab.gnome.org/GNOME/mutter/issues/1124
2020-03-16 15:09:00 +08:00
0700f3749f input-settings/x11: Fix typo in has_udev_property
https://gitlab.gnome.org/GNOME/mutter/merge_requests/256
2020-03-15 13:04:32 +09:00
0487e6f11f input-settings: Wire up middle-emulation
This allows emulating middle click via simultaneous left and right
click.  Fixes #238.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/256
2020-03-15 13:04:32 +09:00
23da6c2426 keybindings: Check the special modifiers specifically
Make sure it is only the special modifier (hardcoded to 1 currently)
which is being pressed (not counting locked modifiers) before notifying
that the special modifier is pressed, as we are interested in it being
pressed alone and not in combination with other modifier keys.

This helps in two ways:
- Pressing alt, then ctrl, then releasing both won't trigger the locate
  pointer action.
- Pressing alt, then ctrl, then down/up to switch workspace won't interpret
  the last up/down keypress as an additional key on top of the special ctrl
  modifier, thus won't be forwarded down to the focused client in the last
  second.

Closes: https://gitlab.gnome.org/GNOME/mutter/issues/812

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1014
2020-03-13 21:37:32 +01:00
67dd0b4fec keybindings: Avoid double calls to process_event() on the same event
If you first press a key that triggers the "special modifier key" paths
(ctrl, super), and then press another key that doesn't match (yet?) any
keybindings (eg. ctrl+alt, super+x), the second key press goes twice
through process_event(), once in the processing of this so far special
combination and another while we let the event through.

In order to keep things consistent, handle it differently depending on
whether we are a wayland compositor or not. For X11, consider the event
handled after the call to process_event() in process_special_modifier_key().
For Wayland, as XIAllowEvents is not the mechanism that allows clients see
the key event, we can just fall through the regular paths, without this
special handling.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1014
2020-03-13 21:22:28 +01:00
6989fea767 cogl: Fix build error when GL_ARB_sync is not defined
Commit 41992757e0 introduced a change to use CoglContext.glFenceSync
but this method is only available when GL_ARB_sync is defined (as
defined on gl-prototypes/cogl-all-functions.h).

This change fixes that.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1123
2020-03-12 18:05:10 -03:00
df33255162 cogl: Add main header for Cogl
This allows bindings linking to the C header to actually have the right one.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1101
2020-03-12 13:35:25 +00:00
5319949a45 kms-impl-device: Clean up state if drm resources disappear
It may happen that drmModeGetResources() starts returning NULL. Handle
this gracefully by removing all connectors, CRTCs and planes making the
device in practice defunct.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1121
2020-03-12 13:08:46 +00:00
aba0b9ef64 keybindings: Move common window grab code out of X-only if statement
`3c8d4171` moved some common codes into X11-only code blocks by mistake,
and it prevents keyboard window resize/move mode under Wayland because
those variables are unset. This commit fixed it via moving such common
codes out of X11-only code blocks.

Fixes: https://gitlab.gnome.org/GNOME/mutter/issues/949

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/997
2020-03-12 08:42:04 +00:00
512bb7d1cd wayland: Don't crash when trying to fullscreen on inert wl_output
There is a race where an output can be used as a fullscreen target, but
it has already been removed due to a hotplug. Handle this gracefully by
ignoring said output in such situations.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1120
2020-03-11 14:37:09 +00:00
d2a12ee0fa crtc-xrandr: Compare right coordinate when checking assignment
Compare x with x, and y with y, not y with x.

Fixes an issue where only changing the scale doesn't actually apply the
new scale.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1119
2020-03-11 13:02:50 +00:00
531a195cf1 monitor-config-manager: Respect layout mode when calculating CRTC layout
The scale used when calculating the CRTC layout should only come from
the logical monitor scale if the layout mode of the corresponding
configuration is 'logical'.

This fixes an issue where the X11 screen size accidentally got set to a
size scaled down by the configured global UI scale.

Fixes: https://gitlab.gnome.org/GNOME/mutter/issues/1107
Fixes: https://gitlab.gnome.org/GNOME/mutter/issues/1109

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1118
2020-03-11 12:55:03 +00:00
509e9ca5a0 xwayland: Fix mime type atom list leak on DnD with more than 3 types
Found using the clang static analyzer

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1117
2020-03-11 03:21:36 +01:00
0743381573 window/x11: Rename meta_window_x11_buffer_rect_to_frame_rect
To keep consistent and avoid confusion, rename the function:
    `meta_window_x11_buffer_rect_to_frame_rect()`
to:
    `meta_window_x11_surface_rect_to_frame_rect()`

As this function doesn't deal with the `window->buffer_rect` at all.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1091
2020-03-10 14:52:26 +01:00
267f712068 window-actor/x11: Use the new MetaShapedTexture API
The code in `build_and_scan_frame_mask` predates the introduction of the
`MetaShapedTexture` API to get the texture width hand height.

Use the new `meta_shaped_texture_get_width/height` API instead of using
the CoGL paint texture.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1091
2020-03-10 14:52:26 +01:00
0b102afb53 xwayland: Update regions on texture updates
For X11 clients running on Wayland, the actual texture is set by
Xwayland.

The shape, input and opaque regions, however are driven by X11
properties meaning that those may come at a different time than the
actual update of the content.

This results in black areas being visible at times on resize with
Xwayland clients.

To make sure we update all the regions at the same time the buffer is
updated, update the shape, input and opaque regions when the texture is
committed from when the Xwayland surface state is synchronized.

That fixes the remaining black areas being sometimes visible when
resizing client-side decorations windows on Xwayland.

Closes: https://gitlab.gnome.org/GNOME/mutter/issues/1007
https://gitlab.gnome.org/GNOME/mutter/merge_requests/1091
2020-03-10 14:52:26 +01:00
304a103659 window-actor: Add API to update regions
For X11 clients running on Xwayland, the opaque, input and shape regions
are processed from different properties and may occur at a different
time, before the actual buffer is eventually committed by Xwayland.

Add a new API `update_regions` to window actor to trigger the update of
those regions when needed.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1091
2020-03-10 14:52:26 +01:00
2d09e95934 window-actor/x11: Compute client area from surface size
Commit 7dbb4bc3 cached the client area when the client was frozen.

This is not sufficient though, because the buffer size might still be
lagging waiting for the buffer from Xwayland to be committed.

So instead of caching the client size from the expected size, deduce the
client area rectangle from the surface size, like we did for the frame
bounds in commit 1ce933e2.

This partly reverts commit 7dbb4bc3 - "window-actor/x11: Cache the
client area"

https://gitlab.gnome.org/GNOME/mutter/issues/1007
https://gitlab.gnome.org/GNOME/mutter/merge_requests/1091
2020-03-10 14:52:26 +01:00
be11525b28 window/x11: Add function to convert the surface to client area
Add a convenient function to get the client area rectangle from a given
surface rectangle.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1091
2020-03-10 14:52:26 +01:00
adc38f902a window-actor/X11: Update shape, input and opaque region in order
As they depend on each other to be correct, we should set all of them
in the correct order. As we do already have a function for that, use it.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1091
2020-03-10 14:52:26 +01:00
8abdf16a39 cursor-renderer/native: Handle GPU hotplug
Listen for GPU hotplug events to initialize their cursor support.

This fixes one reason for why DisplayLink devices may not be using a hardware
cursor. Particularly, when a DisplayLink device is hotplugged for the first
time such that EVDI creates a new DRM device node after gnome-shell has already
started, we used to forget to initialize the cursor support.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1097
2020-03-10 08:26:17 +00:00
4cc29cfb61 cursor-renderer/native: Refactor init to per-gpu
Extract the code to initialize a single GPU cursor support into its own
function. The new function will be used by GPU hotplug in the future.

This is a pure refactoring without any behavioral changes.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1097
2020-03-10 08:26:17 +00:00
121c5d2a92 meson: Expand on xwayland_initfd option description
https://gitlab.gnome.org/GNOME/mutter/merge_requests/1104
2020-03-09 17:59:25 +00:00
50ff30bf2b xwayland: Log actual error message if available
If X11 initialization fails, print the actual error message if the error
is set, to help with debugging.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1102
2020-03-09 17:49:51 +00:00
26e1e495a0 screen-cast-stream-src: Don't leak GSource
For every stream src, we created and attached a GSource. Upon stream
src destruction, we g_source_destroy():ed the GSource. What
g_source_destroy() does, hawever, is not really "destroy" it but only
detaches it from the main context removing the reference the context had
added for it via g_source_attach(). This caused the GSource to leak,
although in a detached state, as the reference taken on creation was
still held.

Fix this by also removing our own reference to it when finalizing.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1106
2020-03-09 17:31:23 +00:00
480e7d44be screen-cast-stream-src: Don't complain when we can't dequeue buffer
PipeWire will be unable to dequeue a buffer if all are already busy.
This can happen for valid reasons, e.g. the stream consumer not being
fast enough, so don't complain in the journal if it happens.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1115
2020-03-09 17:46:54 +01:00
1c1adb0036 Update Serbian translation 2020-03-08 20:20:38 +00:00
6b852e6cb3 Bump version to 3.36.0
Update NEWS.
2020-03-07 22:51:06 +01:00
6e966e47f2 cursor-renderer-native: Handle lack of cursor planes gracefully
While we will always have cursor planes, as we'll currently create fake
ones when real ones are missing (See #1058), eventually we will run into
situations where we can't create fake ones, for example for atomic KMS
drivers that don't advertise any cursor planes.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1079
2020-03-07 21:40:50 +00:00
227eea1e31 kms-impl-simple: Add fake cursor planes if no real ones
Non-atomic drivers may support drmModeSetCursor() even if no cursor
plane is advertised. To deal with this, add a fake cursor plane for
every CRTC when using MetaKmsImplSimple. This will eventually be
translated to drmModeSetCursor() calls without any explicit cursor plane
usage.

Fixes: https://gitlab.gnome.org/GNOME/mutter/issues/1058

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1079
2020-03-07 21:40:50 +00:00
79920d66d4 Update Dutch translation 2020-03-07 21:11:51 +00:00
6cd0aa429f window: Force placement for first placement rule
If we don't force the placement, we enter the constrain machinery with
the position (0, 0), meaning we always get the "current work area" setup
to correspond to whatever logical monitor was at that position.

Avoid this by doing the same as "meta_window_force_placement()" and set
"window->calc_placement" to TRUE while move-resizing, causing the
move-resize to first calculate the initial position.

Fixes: https://gitlab.gnome.org/GNOME/mutter/issues/1098

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1110
2020-03-06 17:28:09 +01:00
15d3de0099 Update Punjabi translation 2020-03-05 03:53:57 +00:00
38fb59a6a6 Fix broken markup in Welsh UI translation 2020-03-05 00:43:05 +01:00
748f5bb3e6 Update Ukrainian translation 2020-03-04 16:47:45 +00:00
3c55475391 Update Italian translation 2020-03-03 10:43:50 +00:00
dd6e371bac Update French translation 2020-03-03 08:31:16 +00:00
00d900e46d Update Swedish translation 2020-03-02 18:50:50 +00:00
a4f11bef47 Update Croatian translation 2020-03-01 15:55:04 +00:00
c4f76622a6 Updated Lithuanian translation 2020-03-01 14:48:05 +02:00
f80e5dd8e4 Bump version to 3.35.92
Update NEWS.
2020-03-01 02:25:34 +01:00
f484efaac1 Updated Slovenian translation 2020-02-29 22:48:56 +01:00
f97804f4f4 wayland/xdg-shell: Add support for explicit popup repositioning
This commit completes the implementation of `xdg_wm_base` version 3,
which introduces support for synchronized implicit and explicit popup
repositioning.

Explicit repositioning works by the client providing a new
`xdg_positioner` object via a new request `xdg_popup.reposition`. If the
repositioning is done in combination with the parent itself being
reconfigured, the to be committed state of the parent is provided by the
client via the `xdg_positioner` object, using
`xdg_positioner.set__parent_configure`.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/705
2020-02-29 21:01:50 +00:00
5c37f5104e wayland/xdg-shell: Add support implicit popup moving
This sets the `is_reactive` flag on the window placement rules, causing
the popups to be reconfigured as they are affected by environmental
changes, such as the parent moving in a way making the popup partially
offscreen.

As with synchronization, the implementation is dormant, as the
version of the advertised global isn't bumped yet, as the new protocol
version is not yet fully implemented.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/705
2020-02-29 21:01:50 +00:00
d08a8de265 window: Implement asynchronous popup moving
This commits adds support on the MetaWindow and constraints engine side
for asynchronously repositioning a window with a placement rule, either
due to environmental changes (e.g. parent moved) or explicitly done so
via `meta_window_update_placement_rule()`.

This is so far unused, as placement rules where this functionality is
triggered are not yet constructed by the xdg-shell implementation, and
no users of `meta_window_update_placement_rule()` exists yet.

To summarize, it works by making it possible to produce placement rules
with the parent rectangle a window should be placed against, while
creating a pending configuration that is not applied until acknowledged
by the client using the xdg-shell configure/ack_configure mechanisms.

An "temporary" constrain result is added to deal with situations
where the client window *must* move immediately even though it has not yet
acknowledged a new configuration that was sent. This happens for example
when the parent window is moved, causing the popup window to change its
relative position e.g. because it ended up partially off-screen. In this
situation, the temporary position corresponds to the result of the
movement of the parent, while the pending (asynchronously configured)
position is the relative one given the new constraining result.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/705
2020-02-29 21:01:50 +00:00
6c82feb1b8 wayland/window-configuration: Track resize flags and gravity too
Will later be used to determine in what way a pending configuration will
resize.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/705
2020-02-29 21:01:50 +00:00
0dac91cffc Add MetaGravity and replace X11 equivalent with it
MetaGravity is an enum, where the values match the X11 macros used for
gravity, with the exception that `ForgetGravity` was renamed
`META_GRAVITY_NONE` to have less of a obscure name.

The motivation for this is to rely less on libX11 data types and macros
in generic code.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/705
2020-02-29 21:01:50 +00:00
ff381d1d52 constraints: Pass constrained relative coordinates to window impl
A placement rule placed window positions itself relative to its parent,
thus converting between relative coordinates to absolute coordinates,
then back to relative coordinates implies unwanted restrictions for
example when the absolute coordinate should not be calculated againts
the current parent window position.

Deal with this by keeping track of the relative position all the way
from the constraining engine to the move-resize window implementation.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/705
2020-02-29 21:01:50 +00:00
05e9d6ab9e window: Put placement related fields in a anynomous struct
To organize things a bit better, put the fields related to the placement
rule state in its own anonymous struct inside MetaWindow. While at it,
rename the somewhat oddly named variable that in practice means the
current relative window position.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/705
2020-02-29 21:01:50 +00:00
d22f947bf5 wayland/window: Pass popup configuration using relative coordinates
After popup placement rules have gone through the constraints engine has
ended up resulting in an actual move, pass the window configuration down
the path using relative coordinates, as that is what the next layer
(xdg-shell implementation) actually cares about.

In the future, this will also be helpful when the configured position is
not against the current state of the parent.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/705
2020-02-29 21:01:50 +00:00
9b97e5ed58 place: Make placement rule processing provide relative coordinates
A placement rule is always about placing a window relative to its
parent. In order to eventually place it against predicted future parent
positions, make the placement rule processing output relative
coordinates, having the caller deal with turning them into absolute.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/705
2020-02-29 21:01:50 +00:00
7f9fac2ba2 window/wayland: Don't inhibit finishing moves if any window is resized
meta_window_wayland_finish_move_resize() inhibited window moves to be
finished if there was a resize grab active at the time, in order to
handle window resizing. Change this to only affect the grabbed window
itself, so that e.g. a popup can be positioned according to a pending
configuration while there is an active resize grab.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/705
2020-02-29 21:01:50 +00:00
64eaf70279 xwayland: Allow setting up maintenance processes
This is made a signal, so the upper layers (read: gnome-shell) may
decide what services to spawn. The signal argument contains a task
that will resume MetaX11Display startup after it is returned upon.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/945
2020-02-29 20:41:26 +00:00
e3149e6021 wayland: Pass private X connection on GNOME_SETUP_DISPLAY
This envvar will be picked up by the services spawned together with Xwayland
startup, and used instead of the regular DISPLAY meant for regular clients.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/945
2020-02-29 20:41:26 +00:00
166a464515 wayland: Set up initialization X11 socket
This is used by GDK and the X11 bits, but may also be used for
other initialization services we might need to run along with
Xwayland initialization.

However, as the -initfd argument in Xwayland is a fairly new
feature, add some meson build-time checks so that the feature
is handled transparently while allowing to explicitly set/unset
it.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/945
2020-02-29 20:41:26 +00:00
38e58b837b core: Use DISPLAY envvar to forward in the launch context
The meta_x11_get_display_name() will return our private connection, which
is not what we want to transfer to clients being launched.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/945
2020-02-29 20:41:26 +00:00
39a8c047d1 background-actor: Do not copy empty clip/unobscured regions
Clip and unobscured regions stricly shrink during culling. If they
are already empty, simply reference the empty region to reduce allocations.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1082
2020-02-29 19:44:01 +00:00
c979cd95aa surface-actor: Do not copy empty clip/unobscured regions
Clip and unobscured regions stricly shrink during culling. If they
are already empty, simply reference the empty region to reduce allocations.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1082
2020-02-29 19:44:01 +00:00
6831f2edb4 window-actor/x11: Disable culling on window actor level
This shape region culling was wrongly implemented in f5a28aa9, as it
does not take frame offsets into account, and is also redundant, as
we already set the opaque region of the underlying surface accordingly.

The other parts were implemented in ac7aa114, the reason given in
the commit message:
```
Wayland clients do this through the opaque region in the surface
actor. However X11 clients were considered fully transparent for
culling purposes, which may result in mutter painting other bits
of the background or other windows that will be painted over in
reality.
```

is wrong though - culling on X11 actors works just fine and did only
not work in Wayland sessions because of a bug that got fixed in
19814497.

In conclusion the whole part appears to be redundand and some testing
done suggests the same. Drop it.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1082
2020-02-29 19:44:01 +00:00
372d73e275 surface-actor: Cull out surfaces without alpha channel
If a opaque region is explicitly set we should not consider the surface
opaque, as that implies e.g. a shape region is set.

If no opque region is set but the texture does not have an alpha channel,
we can savely cull it out.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1082
2020-02-29 19:44:01 +00:00
0ada90024f wayland/actor-surface: Check for MetaXwaylandSurface instead of window type
The previous check would not ignore subsurfaces. Use the chance to directly
check for the surface role instead - it's much cleaner.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1082
2020-02-29 19:44:01 +00:00
46e38ff61a cogl/clip-stack: Set color and depth mask when drawing rectangle clips
Just like with the other functions drawing to the stencil buffer, we
should make sure the depth and color masks are set to FALSE during
drawing.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1096
2020-02-29 19:29:31 +00:00
1e0b015ce8 cogl/clip-stack: Explicitely initialize the stencil mask
We need the stencil buffer to consist of binary values of 0 and 1
because we're doing additions and subtractions on the buffer, so even
though this is the default, explicitely set the stencil mask to 0x1.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1096
2020-02-29 19:29:31 +00:00
72054332c5 cogl/clip-stack: Restore old matrices after drawing to stencil buffer
When using a region clip and something has a rectangle clip pushed, a
special drawing method for ClutterTexts (emit_vertex_buffer_geometry()
in cogl-pango-display-list.c) starts to fail and clipping issues with
long texts (because emit_vertex_buffer_geometry() is only used for texts
longer than 25 characters) start to appear. This specifically happened
in Looking Glass, where the StViewport of the ScrollView sets a
rectangle clips and the texts are usually longer than 25 characters.

This is caused by the changing of the perspective and modelview matrix
when drawing to the stencil buffer and started happening when
region-clipping was introduced with commit 8598b654. Even though the
changing of the matrices was done before that, too, the issue probably
didn't happen because `rect->can_be_scissor` was TRUE and no stencil
buffer clipping was used at all.

To fix this, temporarily save the old matrices, then set the new ones
and restore the old ones when we're done drawing to the stencil buffer.

Fixes https://gitlab.gnome.org/GNOME/gnome-shell/issues/2246

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1096
2020-02-29 19:29:31 +00:00
d30ef0dd3b cogl/clip-stack: Use "merge" parameter for rectangle clips
Just like for the other sorts of clips, use a "merge" parameter instead
of a "first" parameter.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1096
2020-02-29 19:29:31 +00:00
84275688f3 Update Japanese translation 2020-02-29 16:06:38 +00:00
90a1eb4275 Update Japanese translation 2020-02-29 15:54:35 +00:00
73e3207a85 clutter-actor: Add detail to captured-event
So that subscribers can choose the class of events they're interested in
and not be woken by everything else.

Needed by: https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/925

Related to: https://gitlab.gnome.org/GNOME/mutter/issues/283

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1000
2020-02-29 13:34:00 +00:00
8a6673bb55 build: ensure absolute path to sysprof dbus interface dir
We want sysprof's exact datadir for compatability with
platforms where software is installed into their own
individual immutable prefix's. Such that, mutter's prefix will
never equate to sysprof's. This depends on a MR in sysprof [0]
which adds datadir to its pkgconfig files, as these files will always
have the proper path we want.

This adds version a constraint on sysprof_dep, as datadir was added to
the .pc in this version.

[0]: https://gitlab.gnome.org/GNOME/sysprof/merge_requests/19

https://gitlab.gnome.org/GNOME/mutter/merge_requests/957
2020-02-29 13:17:40 +01:00
35c16a9f4b Update Polish translation 2020-02-29 13:17:07 +01:00
390fd7ddcf cursor-renderer-native: Fix hw cursor for non-square sprites
wl_shm_buffer_get_width() was used where wl_shm_buffer_get_height()
should have used, resulting in only square cursors working. Make
rectangular cursors work again.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1099
2020-02-28 21:49:58 +00:00
4b513a31ae compositor: Shuffle x11 compositing bits in MetaCompositor
Given that on Wayland we are pretty much guaranteed to finish MetaX11Display
setup after the MetaCompositor is enabled, we may drop the
meta_compositor_manage() x11 initialization bits, and move them into the
MetaX11Compositor subclass where it's actually needed.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/944
2020-02-28 20:20:35 +00:00
25c9e66c73 xwayland: Do not block on Xwayland initialization
We artificially made Xwayland initialization synchronous, as we used
to rely on MetaX11Display and other bits during meta_display_open().
With support for Xwayland on demand and --no-x11, this is certainly
not the case.

So drop the main loop surrounding Xwayland initialization, and turn
it into an async operation called from meta_display_init_x11(). This
function is turned then into the high-level entry point that will
get you from no X server to having a MetaX11Display.

The role of meta_init() in Xwayland initialization is thus reduced
to setting up the sockets. Notably no processes are spawned from here,
deferring that till there is a MetaDisplay to poke.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/944
2020-02-28 20:20:35 +00:00
649911b6b3 core: Make meta_display_init_x11() an async function
This ATM completes the task right away, but we will want to do
further things here that are asynchronous in nature, so prepare
for this operation being async.

Since the X11 backend doesn't really need this, make it go on
the fast lane and open the MetaX11Display right away, the case
of mandatory Xwayland on a wayland session is now handled
separately.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/944
2020-02-28 20:20:35 +00:00
8c332a4704 tests: Set fatal log handler on clutter tests
With Xwayland initialization going async, these errors will seep
into the parts controlled by g_test*(), resulting in the harmless
errors about DBus names not acquired turned fatal.

Set an error log handler, and specifically ignore those.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/944
2020-02-28 20:20:35 +00:00
21994bb00d tests: Ensure MetaX11Display is initialized before running tests
It might not be available right on initialization time if X11 is started
asynchronously. As this is a requirement for our tests, ensure it is there
before proceeding with the test.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/944
2020-02-28 20:20:35 +00:00
87a06c63ad x11: Set up the compositing manager selection on meta_display_init_x11()
This used to be set on meta_compositor_manage(), but only if there is a
MetaX11Display. Given meta_display_init_x11() is Wayland only, and we can
always assume compositing to be enabled, just have it invariably set after
the X server is up.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/944
2020-02-28 20:20:35 +00:00
09026e5f0e monitor-stream-src: Use cogl_framebuffer_finish()
Even though cogl_framebuffer_flush() was supposed to be enough,
it ends up creating streams with odd visual glitches that look
very much like unfinished frames.

Switch back to cogl_framebuffer_finish(), which is admittedly
an overkill, but it's what works for now. There is anedoctal
evidence showing it doesn't incur in worse performance.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1086
2020-02-28 15:52:19 -03:00
1f190a7cfe screen-cast-stream-src: Remove unused parameter
The 'data' parameter is not used in maybe_record_cursor(), so remove
it.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1086
2020-02-28 15:52:19 -03:00
86168b945c window-stream-source: Draw into DMA buffer image
Much like monitor streaming, implement window streaming by
making the window actor draw itself with a paint context
that used the passed framebuffer.

Now that all MetaScreenCastStreamSrc subclasses implement
blit_to_framebuffer, remove the conditional check from
meta_screen_cast_stream_src_blit_to_framebuffer().

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1086
2020-02-28 15:52:19 -03:00
680a54aff6 monitor-stream-src: Implement blitting view framebuffers
Add the vfunc override that actually consume the new Cogl API. Every
view that fits into the logical monitor is rendered.

Fixes https://gitlab.gnome.org/GNOME/mutter/issues/639

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1086
2020-02-28 15:52:03 -03:00
548073ec27 screen-cast-stream-src: Support DMA buffer sharing
Implement PipeWire's add_buffer and remove buffer, try and export
a DMA buffer first and, on failure, fallback to memfd.

When DMA buffers are successfully created and shared, blit the
framebuffer contents when drawing instead of downloading the pixels.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1086
2020-02-28 15:29:03 -03:00
d366fb335d renderer-native: Implement DMA buffer creation
Create a new gbm_bo using the same given geometry, and export the new
bo's DMA buffer fd. The new bo lives as long as necessary to be used,
and reused, by PipeWire.

Unfortunately, PipeWire doesn't support modifiers properly, so use the
linear format for now. For now, a hardcoded format of DRM_FORMAT_XRGB8888
is set, so we don't need to negotiate the format with PipeWire early.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1086
2020-02-28 15:29:02 -03:00
7fbb47b2bb renderer-native: Move DMA buffer creation to an auxiliary function
This will be reused by the DMA buffer exporting function.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1086
2020-02-28 15:28:22 -03:00
3881c1f952 cogl/context: Add cogl_renderer_create_dma_buf() and family
This is a winsys-specific API that allows exporting a DMA buffer fd.
The CoglDmaBufHandle structure allows passing the ownership of the
DMA buffer to whoever is using it, so the winsys doesn't need to
manually track it.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1086
2020-02-28 14:54:48 -03:00
038a3170bf cogl/framebuffer: Add cogl_framebuffer_flush
In future patches, we'll create additional CoglFramebuffers that
will be shared via DMA-Buf with PipeWire. When recording frames,
we'll blit the current onscreen framebuffer into the shared one.

However, that presents a problem: cogl_framebuffer_blit() mimics
glBlitFramebuffer() semantics, and doesn't do an implicit flush
of the GPU command stream. As a consequence, clients may receive
unblitted or incomplete framebuffers.

We could use cogl_framebuffer_finish() to ensure the commands were
submitted to the GPU, but it is too harsh -- it blocks the CPU
completely until the commands are finished!

Add cogl_framebuffer_flush(), which ensures the command stream is
submitted to the GPU without blocking the CPU. Even though we don't
use the framebuffer specifically, it may be useful in the future
for e.g. a potential Vulkan backend to have access to the framebuffer.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1086
2020-02-28 10:52:34 -03:00
f27de9620a Updated Danish translation 2020-02-28 14:01:26 +01:00
969ad54feb cursor-renderer/native: Remove unnecessary endian check
Both cases have the same content.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/526
2020-02-28 10:22:11 +00:00
987ce5bec0 cursor-renderer/native: Implement scaled/transformed hardware cursors
If the cursor sprite does not match the scale factor or transformation
of the monintor, we currently fall back to a software cursor, causing
redraws of the shell. This commit implements scaling and transforming
of the cursor sprite, so we can use it with hardware planes, too.

This commit does the following steps:

1. Make sure we reupload the cursor image if the cursor is over
a logical monitor not matching the scale or transform from the previous
update.
2. Before upload to the hardware plane, scale and transform the cursor
image if possible and necessary.
3. Make sure we always use the hardware cursor if possible (only fall
back to software/OGL cursor if it is visible on multiple logical monitors
with differet scales/transforms).
4. Transform or scale the cursor coordinates if necessary.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/526
2020-02-28 10:22:11 +00:00
d600cd9aee monitor-transform: Add generic function to get relative transform
It takes transforms A and B and returns the transform to get from
A to B.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/526
2020-02-28 10:22:11 +00:00
1fc7935858 backends/cursor: Add API to set and get the texture buffer transform
In Wayland clients can commit transformed surfaces, so the compositor
can directly use them on hardware planes. We already support that
for other surfaces, this is the first step to also support it on
cursor sprites.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/526
2020-02-28 10:22:11 +00:00
b5354fb3cd Update Indonesian translation 2020-02-28 02:13:12 +00:00
01aaced129 crtc: Don't leak MetaCrtcConfig
We never freed it except when CRTC disabled during runtime.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1095
2020-02-27 20:35:07 +00:00
ff59b5d041 monitor: Move logical <-> CRTC transform helpers to MetaOutput
So that we avoid leaking the internal guts of MetaOutput into
MetaMonitor, while also making it possible to use it without a
MetaMonitor at hand.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1064
2020-02-27 09:47:22 +01:00
92f0eb9d14 monitor: Use transform helper when dealing with panel orientation
Replace the open coded monitor transform math with the new helper.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1064
2020-02-27 09:47:08 +01:00
e6913d1471 monitor-transform: Add meta_monitor_transform_transform() helper
Intended to replace various manual monitor transform enum math here and
there. Tests added as well, to test some hand picked transforms.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1064
2020-02-27 09:07:25 +01:00
d495dc6c63 Update Galician translation 2020-02-26 20:57:31 +00:00
88bb24f66e backends/native: Shuffle udev client initialization in MetaSeatNative
This may be used indirectly before creation as we dispatch libinput events
right after creation (to let input devices be known), so those device
additions would trigger the touch-mode checks.

Creating it in advance results in checks being correctly performed, although
redundantly.

Spotted by Bastien Nocera.

Closes: https://gitlab.gnome.org/GNOME/mutter/issues/1067
2020-02-26 18:32:05 +00:00
6e7316ef11 build: Use dbus interface dir from pkg-config
Using 'datadir' breaks prefixed builds (as we look for interface files
in the prefix we are going to install to).

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1094
2020-02-26 15:37:00 +00:00
9fa56176fd monitor-manager-xrandr: Don't try to disable disabled CRTC
When applying a configuration to XRANDR, we first disable CRTCs that
happen to extend outside of the to-be X11 screen size. While doing so,
we fail to actually check whether the CRTC is active or not, meaning
we'll try to query the content of the CRTC configuration even though it
has none, leading to a NULL pointer dereference.

Fix this by simply ignoring non-configured CRTCs.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1093
2020-02-26 16:14:04 +01:00
fb81199dec Updated Czech translation 2020-02-26 14:13:21 +01:00
b7f47ea17f clutter: add tracing for frameclock cycle
This adds a new frameclock tracing mark for a single cycle of the frame
clock. Doing so allows Sysprof to potentially do more with the information
that happens during the frameclock. For example, we can now find
allocations that happen while the frame clock is advancing.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1088
2020-02-26 12:33:00 +00:00
71101cab82 clutter/cogl/stage: Remove duplicate region union
https://gitlab.gnome.org/GNOME/mutter/merge_requests/1089
2020-02-26 07:26:10 +00:00
c4b28b0939 clutter/cogl/stage: Don't leak FB damage region on redraw
offset_scale_and_clamp_region() creates a new region resulting in
view_damage which at this point is the only thing left pointing to what
originally was fb_damage getting overwritten and being leaked.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1089
2020-02-26 07:26:10 +00:00
38954de11c ci: Adjust URL check
While the old merge request URLs still work, gitlab recently started
including an additional /- for merge requests.

Adjust the regex to account for that, so that simply copying the URL
from gitlab works again.

https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1090
2020-02-26 06:28:46 +00:00
964ea60090 Update Korean translation 2020-02-26 03:00:37 +00:00
159c83b914 clutter: Move redraw clip management to views
The stage window handled the redraw clip in a global manner; this would
interfere if we want to paint views individually as it'd mean
intersecting views (i.e. mirrored monitors) would loose the redraw clip
once the first view was painted. It also is awkward to have a global
state for something that is built up before redrawing, and only really
valid during paint, due to buffer damage history.

This commits removes all redraw clip management from the stage window,
moving it all into the stage views. When a redraw clip is added to the
stage, every affected view will get the same redraw clip added to it,
and eventually when painted, the stage window (ClutterStageCogl) will
retrieve the redraw clip for each view as it repaints them.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1042
2020-02-25 19:18:54 +01:00
b68c630329 clutter/cogl/stage: Use g_clear_pointer() to clean up regions
https://gitlab.gnome.org/GNOME/mutter/merge_requests/1042
2020-02-25 19:18:54 +01:00
fe1ccea1e1 clutter: Pass redraw clip via paint context
Instead of users fetching it via `clutter_stage_get_redraw_clip()`, pass
it via the paint context. This is helpful as it is only valid during a
paint, making it more obvious that it needs to be handled differently
when there is no redraw clip (i.e. we're painting off-screen).

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1042
2020-02-25 19:18:54 +01:00
c483b52d24 clutter/stage: Pass redraw clip instead of extents when painting view
That's the struct we have ready, the callee can just call
cairo_region_get_extents() if it only cares about the extents rectangle.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1042
2020-02-25 19:18:54 +01:00
f3dcba27f5 clutter/cogl/stage: Simplify region transform code
Add a helper that scales and clamps a region, aimed to be used when
transforming between framebuffer coordinate space and view coordinate
spaces.

This helps readability by moving out the verbose for loops that deals
with the individual rects of a region to the helper, making the logic
where it's used much simpler.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1042
2020-02-25 19:18:54 +01:00
86ccc28413 clutter/cogl/stage: Rename have_clip to is_full_redraw
The 'have_clip' variable has repeatedly confused me to meaning that
there is a clip. What it actually means is that the effective clip
covers the whole view; the 'redraw_clip == NULL' meaning full redraw is
an important implementation detail for the context, and makes the
intention of the variable unclear; especially since we will after a
couple of blocks will *always* have a clip, just that it covers the
whole view.

Rename the variable to 'is_full_redraw' and negate the meaning, aiming
to make things a lot more clear.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1042
2020-02-25 18:39:51 +01:00
0d039c3ba3 clutter/view: Don't take paint rect after paint
It was unused, and will be become unused by the primary paint function
as well, so go ahead and remove it here first.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1042
2020-02-25 18:39:51 +01:00
adcbc2aa5f clutter/cogl/stage: Use fb size for for fallback fb clip region
When calculating the fallback framebuffer clip region, which should be
the region in framebuffer coordinates, we didn't scale the view layout
with the view framebuffer scale, meaning for any other scale than 1,
we'd draw a too small region of the view. Fix this by just using the
size of the framebuffer directly, avoiding any scale dependent
calculation all together.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1042
2020-02-25 18:39:51 +01:00
860906246e clutter/cogl/stage: Simplify swap_event boolean logic
We'll expect a swap event if any of the view paints resulted in a swap;
make the logic dealing with this clearer by making changing the less
vilible '|| swap_event' postfix with a up front '|=' operator.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1042
2020-02-25 18:39:51 +01:00
37d7df612b clutter/stage/cogl: Name variable to make coordinate space obvious
The ambiguous "clip" was renamed to "fb_clip_region", as it was called
at the call site. This should make it more clear that the clip is in
buffer coordinate space.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1042
2020-02-25 18:39:51 +01:00
0baf4578c8 clutter/stage/cogl: Remove some leftover whitespace
https://gitlab.gnome.org/GNOME/mutter/merge_requests/1042
2020-02-25 18:39:51 +01:00
2d6665950c crtc: Move logical monitor pointer to MetaMonitor
Since the last code fetching the logical monitor state directly from the
CRTC has been removed, we can move the logical monitor pointer to a more
natural place.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1042
2020-02-25 18:39:51 +01:00
1c98f01a65 renderer-native: Draw stage separately per CRTC
Prior to this commit the stage was drawn separately for each logical
monitor. This allowed to draw different parts of the stage with
different transformations, e.g. with a different viewport to implement
HiDPI support.

Go even further and have one view per CRTC. This causes the stage to
e.g. draw two mirrored monitors twice, instead of using the same
framebuffer on both. This enables us to do two things: one is to support
tiled monitors and monitor mirroring using the EGLStreams backend; the
other is that it'll enable us to tie rendering directly to the CRTC it
will render for. It is also a requirement for rendering being affected
by CRTC state, such as gamma.

It'll be possible to still inhibit re-drawing of the same content
twice, but it should be implemented differently, so that it will still
be possible to implement features requiring the CRTC split.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1042
2020-02-25 18:39:51 +01:00
e3f30371aa renderer-native: Fix a couple of style misses
Removed stray newline, and fixed an incorrect indentation.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1042
2020-02-25 18:39:51 +01:00
1a14c4d3c9 renderer-x11-nested: Remove stray newline
https://gitlab.gnome.org/GNOME/mutter/merge_requests/1042
2020-02-25 18:39:51 +01:00
292d2754ae clutter/stage: Remove stray newline
https://gitlab.gnome.org/GNOME/mutter/merge_requests/1042
2020-02-25 18:39:51 +01:00
fe42d56db3 crtc: Move configured state to separate struct
To make it more reliable to distinguish between values that are read
from the backend implementation (which is likely to be irrelevant for
anything but the backend implementation), split out those values (e.g.
layout).

This changes the meaning of what was MetaCrtc::rect, to a
MetaCrtcConfig::layout which is the layout the CRTC has in the global
coordinate space.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1042
2020-02-25 18:39:51 +01:00
a11f9bd513 boxes: Add 'round' rounding strategy
It just calls roundf(), and is intended to be used when the graphene
rectangle is approximately integer aligned.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1042
2020-02-25 18:39:51 +01:00
7f6cafa847 logical-monitor: Pass monitor in the for each CRTC helper callback
Will be used in later commits.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1042
2020-02-25 18:39:51 +01:00
1b67f49f7f monitor: Move logical to CRTC transform helper to MetaOutput
So that it can be used on a per output basis in the future.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1042
2020-02-25 18:39:51 +01:00
c447d76cd4 Update Hungarian translation 2020-02-25 06:32:01 +00:00
d327cedb83 clutter: avoid redundant _clutter_paint_node_init_types()
This only needs to be initialized once but is in the hot path of creating
new paint nodes (for which we create many). Instead, do this as part of
the clutter_init() workflow to keep it out of the hot path.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1087
2020-02-25 01:14:09 +00:00
0c55e87d8f cogl: reduce temporary allocations calculating redraw regions
When calculating regions, a lot of temporary allocations are created. For
the array of rects (which is often a short number of them) we can use
stack allocations up to 1 page (256 cairo_rectangle_int_t). For building
a region of rectangles, cairo and pixman are much faster if you have all
of the rectangles up front or else it mallocs quite a bit of temporary
memory.

If we re-use the cairo_rectangle_int_t array we've already allocated (and
preferably on the stack), we can delay the creation of regions until after
the tight loop.

Additionally, it requires fewer allocations to union two cairo_region_t
than to incrementally union the rectangles into the region.

Before (percentages are of total number of allocations)

     TOTAL    FUNCTION
[ 100.00%]    [Everything]
[ 100.00%]      [gnome-shell --wayland --display-server]
[  99.67%]        _start
[  99.67%]          __libc_start_main
[  99.67%]            main
[  98.60%]              meta_run
[  96.90%]                g_main_loop_run
[  96.90%]                  g_main_context_iterate.isra.0
[  96.90%]                    g_main_context_dispatch
[  90.27%]                      clutter_clock_dispatch
[  86.54%]                        _clutter_stage_do_update
[  85.00%]                          clutter_stage_cogl_redraw
[  84.98%]                            clutter_stage_cogl_redraw_view
[  81.09%]                              cairo_region_union_rectangle

After (overhead has much dropped)

     TOTAL    FUNCTION
[ 100.00%]    [Everything]
[  99.80%]      [gnome-shell --wayland --display-server]
[  99.48%]        _start
[  99.48%]          __libc_start_main
[  99.48%]            main
[  92.37%]              meta_run
[  81.49%]                g_main_loop_run
[  81.49%]                  g_main_context_iterate.isra.0
[  81.43%]                    g_main_context_dispatch
[  39.40%]                      clutter_clock_dispatch
[  26.93%]                        _clutter_stage_do_update
[  25.80%]                          clutter_stage_cogl_redraw
[  25.60%]                            clutter_stage_cogl_redraw_view

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1071
2020-02-24 22:44:35 +00:00
81d11ac0a8 clutter: avoid g_signal_emit_by_name() from ClutterActor
g_signal_emit_by_name() is used to emit signals on ClutterContainer when
actors are removed or added. It happens to do various interface lookups
which are a bit unneccessary and can allocate memory.

Simply using emission wrappers makes all of that go away.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1083
2020-02-24 22:36:27 +00:00
84261d1db3 Update Basque translation 2020-02-24 16:41:35 +00:00
a0441ad666 Updated Spanish translation 2020-02-24 16:48:17 +01:00
67152916e1 Update Turkish translation 2020-02-24 15:06:58 +00:00
b98ebe60b2 clutter/x11: Stop tracking stage changes on crossing events
On x11 we emulate pointer events from touch events as long as there's
only one touchpoint on screen, this obviously leads to x11 sending us
crossing events triggered by the emulated pointer. Now if we get a leave
event and set the stage of the ClutterInputDevice to NULL, new touch
events will be discarded by clutters backend because the core pointer
doesn't have a stage associated. This means Mutter completely loses
state of a touchpoint as soon as it crosses a shell actor.

An easy reproducer for this issue is to start the four-finger-workspace
gesture above a window and to move the pointer emulating touch outside
of the window, this will freeze the gesture as the gesture no longer
receives touch events.

To fix this, stop tracking stage changes on crossing events and simply
leave the ClutterInputDevice stage as-is. In our case there is only one
stage anyway and that won't change in the future.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/423
2020-02-24 10:54:56 +00:00
ad8ba69423 wayland/pointer: Use g_signal_connect_swapped for one signal
Remove the rather useless callback function that's currently used for
handling the "visibility-changed" signal and instead connect to the
signal using `g_signal_connect_swapped()`.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1077
2020-02-24 09:52:06 +00:00
bf24b816c2 wayland/pointer: Add support for the new ClutterSeat inhibit-unfocus API
The last commit added a new API to ClutterSeat to inhibit setting the
focus-surface of the MetaWaylandPointer to NULL, let's do that.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1077
2020-02-24 09:52:06 +00:00
a9b1134dfb clutter/seat: Add API to inhibit unfocus of the cursor surface/actor
Add API to ClutterSeat that allows inhibiting the unsetting of the
pointer focus surface. This can be useful for drawing custom cursor
textures like the magnifier of gnome-shell does.

In the future this API should also control unsetting of Clutters
focus-actor, not just the focus surface, that's not really needed right
now since we never unset the focus-actor anyway.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1077
2020-02-24 09:52:06 +00:00
6fb6a23389 Update Brazilian Portuguese translation 2020-02-24 09:40:26 +00:00
2da27720ca display: Make check-alive timeout configureable
The check-alive feature is there for the user to be able to terminate
frozen applications more easily. However, sometimes applications are
implemented in a way where they fail to be reply to ping requests in a
timely manner, resulting in that, to the compositor, they are
indistinguishable from clients that have frozen indefinitely.

When using an application that has these issues, the GUI showed in
response to the failure to respond to ping requests can become annoying,
as it disrupts the visual presentation of the application.

To allow users to work-around these issues, add a setting allowing them
to configure the timeout waited until an application is considered
frozen, or disabling the check completely.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1080
2020-02-23 17:28:57 +00:00
f9326cfa3d wayland/data-device: Fix crash with offer from X11 client
If a data offer comes from an X11 client, the Wayland resource would be
NULL, causing a crash in `data_offer_choose_action()` trying to get the
resource version.

So instead of doing the version check in `data_offer_choose_action()`,
do it early when creating the data source.

Closes: https://gitlab.gnome.org/GNOME/mutter/issues/1057
https://gitlab.gnome.org/GNOME/mutter/merge_requests/1073
2020-02-23 16:36:16 +00:00
963108a9a6 wayland/data-device: Check resource version on cancel
For clarity, check the resource version needs the "cancelled" message in
the actual vmethod rather than from the caller function.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1073
2020-02-23 16:36:15 +00:00
b8355a6686 xwayland: Not all xwayland surface have a window
`meta_xwayland_surface_get_relative_coordinates()` may cause a crash if
the Xwayland surface has no window associated.

That can be observed when using drag and drop from an X11 window to a
Wayland native window:

```
    at src/core/window.c:4503
    at src/wayland/meta-xwayland-surface.c:200
    at src/wayland/meta-wayland-surface.c:1517
    at src/wayland/meta-wayland-pointer.c:1048
    at src/wayland/meta-wayland-pointer.c:840
    at src/wayland/meta-wayland-pointer.c:865
    at src/wayland/meta-wayland-pointer.c:954
    at src/wayland/meta-wayland-pointer.c:456
    at src/wayland/meta-wayland-pointer.c:993
    at src/wayland/meta-wayland-data-device.c:1004
    at src/wayland/meta-wayland-data-device.c:1278
    at src/wayland/meta-xwayland-dnd.c:326
```

Check if the xwayland surface has an associated MetaWindow prior to get
its buffer rect.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1073
2020-02-23 16:36:15 +00:00
4c421959dc clutter: fix hole in ClutterPaintNode
Fixing the missalignment takes the structure from 80 bytes down to 72.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1081
2020-02-21 22:36:31 +00:00
aedcfcd010 sound-player: Fix invalid write after playback is cancelled early
The cancellable of a request might already be cancelled by the time
the cancelled_cb is connected resulting in finish_cb being called via
ca_context_cancel before g_cancellable_connect returns. In this case
the request that is written to has already been freed.

Fixes https://gitlab.gnome.org/GNOME/mutter/issues/1060
2020-02-21 16:06:18 +00:00
d7c7311ceb clutter/seat: Add gtkDoc comments for touch-mode property
It's not that easy to find out when the touch-mode is enabled without
reading the code, so document that.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1075
2020-02-21 15:24:58 +00:00
41d72e86e0 backends/native: Also check touch-mode on object initialization
On a Surface Pro 2017, touch-mode is currently only detected correctly
after detaching and attaching the Type Cover (detachable keyboard) once,
it seems that `has_external_keyboard` is only set to the correct value
after MetaSeatNative is initialized.

So fix that and call `update_touch_mode()` once again when the object is
initialized and the `has_external_keyboard` and `has_touchscreen`
properties have been finally updated.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1075
2020-02-21 15:24:58 +00:00
282b09c17e clutter/actor: Add CLUTTER_OFFSCREEN_REDIRECT_ON_IDLE
Which offscreens actor rendering only in cases where it hasn't changed for
2 frames or more. This avoids the performance penalty of offscreening an
actor whose content is trying to animate at full frame rate. It will
switch automatically.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1069
2020-02-21 18:07:46 +08:00
c5d2fc856a screen-cast: Update to PipeWire 0.3 API
Update to 0.3 API

[jadahl: update Dockerfile to include new enough pipewire]

Fixes: https://gitlab.gnome.org/GNOME/mutter/issues/1051

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1062
2020-02-20 18:45:31 +01:00
f5a28aa9e4 window-actor: Make culling of opaque windows X11 only
It is only useful for clients that do not set an opaque region but
still can be detected as being opaque. This is helpful for X11 clients
as opaque regions only got introduced around 2012 and only as part of EWMH
and are thus not used in many cases.
On Wayland however opaque regions have been part of the core protocol from the
beginnig and we can assume they are used more commonly.

As the current implementation in `MetaWindowActor` does not handle Wayland
subsurfaces well, instead of adding more complexity just move it to
`MetaWindowActorX11`.

While on it, take the shape region into account that is set when clients
use the X Nonrectangular Window Shape Extension Protocol, so we have exact
culling with those clients.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/1058
2020-02-20 15:57:51 +00:00
6206511846 Update Basque translation 2020-02-20 15:42:44 +00:00
a209a14898 renderer-native: Use hardware acceleration check in generic layer
No need to duplicate it.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/838
2020-02-20 10:40:42 +00:00
3e6a55aff0 renderer: Add API to check whether renderer is hardware accelerated
Also expose an introspected variant via the MetaBackend.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/838
2020-02-20 10:40:42 +00:00
704fea6323 renderer-native: Move 'backend' field to MetaRenderer
So that it can be used by the generic MetaRenderer class, as well as
other sub types, as well.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/838
2020-02-20 10:40:42 +00:00
8c1e6ebde0 remote-access-handle: Expose disable-animations property
Set to TRUE if a screen cast session asked for animations to be
disabled.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/838
2020-02-20 10:40:42 +00:00
81512ad0dc screen-cast-session: Add 'disable-animations' property
Allow screen casters (e.g. VNC remote desktop services) to ask for
animations to be inhibited, in order to lower the number of frames sent
over the network.

Currently only sets a field on the screen cast session object. Later
it'll be exposed via the remote access handle and via D-Bus by
gnome-shell.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/838
2020-02-20 10:40:42 +00:00
d122b66abc clutter/actor: avoid transform node for identity matrix
If the transform matrix is an identity, then positioning wont change and
we can avoid creating the transform node altogether. This is based on
a similar find in GTK today while reducing temporary allocations.

This cuts the number of transforms created in clutter_actor_paint() by
about half under light testing of GNOME Shell from 6.8% to 2.4% of
allocations.

Before:

    ALLOCATED      TOTAL    FUNCTION
[   20.4 MiB] [  21.20%]    clutter_actor_paint
[   11.0 MiB] [  11.45%]      clutter_paint_node_paint
[    6.6 MiB] [   6.84%]      clutter_transform_node_new
[    2.5 MiB] [   2.61%]      clutter_actor_node_new

After:

    ALLOCATED      TOTAL    FUNCTION
[   33.4 MiB] [  24.12%]    clutter_actor_paint
[   26.2 MiB] [  18.91%]      clutter_paint_node_paint
[    3.4 MiB] [   2.43%]      clutter_actor_node_new
[    3.3 MiB] [   2.41%]      clutter_transform_node_new

Allocation amounts will have differed due to different amounts of running
time, but the % of allocations has now dropped below
clutter_actor_node_new() which should be expected.

https://gitlab.gnome.org/GNOME/mutter/issues/1056
2020-02-20 06:41:34 +00:00
473 changed files with 15568 additions and 22252 deletions

View File

@ -1,4 +1,4 @@
image: registry.gitlab.gnome.org/gnome/mutter/master:v3
image: registry.gitlab.gnome.org/gnome/mutter/master:v4
stages:
- review
@ -28,6 +28,20 @@ build-mutter:
- merge_requests
- /^.*$/
build-without-opengl-and-glx:
stage: build
script:
- meson . build -Dbuildtype=debugoptimized -Dopengl=false -Dglx=false -Degl_device=true -Dwayland_eglstream=true --werror --prefix /usr
- ninja -C build
- ninja -C build install
artifacts:
expire_in: 1 day
paths:
- build
only:
- merge_requests
- /^.*$/
build-without-native-backend-and-wayland:
stage: build
script:

View File

@ -1,29 +1,27 @@
# Rebuild and push with
#
# cd .gitlab-ci/
# podman build --format docker --no-cache -t registry.gitlab.gnome.org/gnome/mutter/master:v3 .
# podman push registry.gitlab.gnome.org/gnome/mutter/master:v3
# podman build --format docker --no-cache -t registry.gitlab.gnome.org/gnome/mutter/master:v4 .
# podman push registry.gitlab.gnome.org/gnome/mutter/master:v4
#
FROM fedora:31
FROM fedora:32
RUN dnf -y update && dnf -y upgrade && \
dnf install -y 'dnf-command(builddep)' && \
dnf install -y 'dnf-command(copr)' && \
dnf copr enable -y fmuellner/gnome-shell-ci && \
dnf -y update && dnf -y upgrade && \
dnf copr enable -y jadahl/mutter-ci && \
dnf builddep -y mutter && \
# Until Fedora catches up with new build-deps
dnf install -y 'pkgconfig(graphene-gobject-1.0)' 'pkgconfig(sysprof-capture-3)' && \
dnf builddep -y mutter --setopt=install_weak_deps=False && \
# For running unit tests
dnf install -y xorg-x11-server-Xvfb mesa-dri-drivers dbus dbus-x11 '*/xvfb-run' gdm-lib accountsservice-libs gnome-control-center && \
dnf install -y xorg-x11-server-Xvfb mesa-dri-drivers dbus dbus-x11 \
'*/xvfb-run' gdm-lib accountsservice-libs gnome-control-center \
--setopt=install_weak_deps=False && \
# GNOME Shell
dnf builddep -y gnome-shell --setopt=install_weak_deps=False && \
dnf remove -y gnome-bluetooth-libs-devel dbus-glib-devel upower-devel python3-devel && \
dnf remove -y gnome-bluetooth-libs-devel && \
dnf remove -y --noautoremove mutter mutter-devel && \
dnf clean all

View File

@ -19,7 +19,7 @@ fi
function commit_message_has_url() {
commit=$1
commit_message=$(git show -s --format='format:%b' $commit)
echo "$commit_message" | grep -qe "\($CI_MERGE_REQUEST_PROJECT_URL/\(issues\|merge_requests\)/[0-9]\+\|https://bugzilla.gnome.org/show_bug.cgi?id=[0-9]\+\)"
echo "$commit_message" | grep -qe "\($CI_MERGE_REQUEST_PROJECT_URL/\(-/\)\?\(issues\|merge_requests\)/[0-9]\+\|https://bugzilla.gnome.org/show_bug.cgi?id=[0-9]\+\)"
return $?
}

85
NEWS
View File

@ -1,3 +1,88 @@
3.37.1
======
* Fix screencasting non-maximized windows [Jonas Å.; !1174]
* Make window-aliveness checks less aggressive [Jonas Å.; !1182]
* Fix stylus coordinates when using screen rotation [Jonas T.; #1118]
* Preserve keyboard state on VT switch [Olivier; !1185]
* Remove Clutter's drag and drop actions [Jonas D.; !789]
* Cancel clicks/gestures actions on disable [Georges; !1188]
* Fix various clipboard issues [Carlos; !1186, !1198, !1203, !1204, !1206]
* Fix trackball button scrolling [Phillip; #1120]
* Fix tiled monitor support [Jonas; !1199]
* Support unredirecting fullscreen wayland surfaces [Jonas Å.; !798]
* Support area screencasts [Jonas Å.; !1207]
* Synchronize shadows to server-side decorations [Olivier; !1214]
* Allow inhibiting remote access [Jonas Å.; !1212]
* Fix overview key on X11 when using multiple keyboard layouts [Olivier; !1219]
* Fixed crashes [Jonas, D., Carlos; !1173, !1183, !1012]
* Misc. bug fixes and cleanups [Andre, Georges, Christian, Jonas Å., Andre,
Simon, Florian, Carlos, Adam, Marco, Thomas, Elias, Pekka, Jonas D.,
Laurent; !1169, !1168, !1166, !1170, !1167, !1172, !1175, !1176, !1184,
!1126, !1187, !1191, !1195, !1179, !1200, !1193, !1209, !1213, !1208,
#1074, !1223]
Contributors:
Marco Trevisan (Treviño), Elias Aebi, Thomas Hindoe Paaboel Andersen,
Laurent Bigonville, Jonas Dreßler, Olivier Fourdan, Carlos Garnacho,
Adam Jackson, Andre Moreira Magalhaes, Simon McVittie, Florian Müllner,
Georges Basile Stavracas Neto, Pekka Paalanen, Christian Rauch, Jonas Troeger,
Phillip Wood, Jonas Ådahl
Translators:
Dušan Kazik [sk], Christian Kirbach [de]
3.36.0
======
* Fix placement of popup windows in multi-monitor setups [Jonas; !1110]
* Fix invisible mouse cursor on some hardware [Jonas; !1079]
Contributors:
Jonas Ådahl
Translators:
Aurimas Černius [lt], Goran Vidović [hr], Anders Jonsson [sv],
Guillaume Bernard [fr], Milo Casagrande [it], Daniel Korostil [uk],
Andre Klapper [cy], Aman Alam [pa], Nathan Follens [nl]
3.35.92
=======
* Fix visibility of initially hidden windows [Jonas Å.; !1066]
* Avoid flicker when (un)redirecting windows [Sebastian; #997]
* Let BindConstraints update the preferred size [Emmanuele; !1070]
* Learn about GLES3 [Adam; !882]
* Ping windows on every window focus [Jonas D.; !891]
* Remove overhead from hot code paths [Christian;
#1056, !1081, !1083, !1071, !1087]
* Allow remote desktop services to inhibit animations [Jonas Å.; !838]
* Update screen-cast code to PipeWire 0.3 API [Wim; !1062]
* Make check-alive timeouts configurable [Jonas Å.; !1080]
* Make each stage view correspond to a single CRTC [Jonas Å.; !1042]
* Implement scaled/transformed hardware cursors [Robert; !526]
* Use DMA buffers for screencasting if possible [Georges; !1086]
* Make Xwayland startup asynchronous [Carlos; !944]
* Fix clipping glitches in long text entries [Jonas D.; !1096]
* Add side channel for starting required X11 services [Carlos; !945]
* Support synchronized wayland popup moving [Jonas Å.; !705]
* Fixed crashes [Olivier, Jonas Å.; !1073, !1093]
* Plugged memory leaks [Sebastian, Jonas Å.; !1089, !1095]
* Misc. bug fixes and cleanups [Jonas Å, Olivier, Florian, Daniel, Jonas D.,
Robert, Sebastian, Christian, Arun, Carlos, worldofpeace; !1061, #1043,
!1067, !1068, !1065, !835, !1058, !1069, !1075, #1060, !1077, !423, !1090,
!1088, !1094, #1067, !1064, !1099, !957, !1000, !1082]
Contributors:
Emmanuele Bassi, Jonas Dreßler, Olivier Fourdan, Carlos Garnacho,
Christian Hergert, Adam Jackson, Sebastian Keller, Robert Mader,
Florian Müllner, Georges Basile Stavracas Neto, Arun Raghavan, Wim Taymans,
Daniel van Vugt, worldofpeace, Jonas Ådahl
Translators:
Yi-Jyun Pan [zh_TW], Asier Sarasua Garmendia [eu], Rafael Fontenelle [pt_BR],
Emin Tufan Çetin [tr], Daniel Mustieles [es], Balázs Úr [hu],
Gwan-gyeong Mun [ko], Marek Černocký [cs], Fran Dieguez [gl],
Kukuh Syafaat [id], Alan Mortensen [da], Piotr Drąg [pl], sicklylife [ja],
Matej Urbančič [sl]
3.35.91
=======
* Honor accelerometer orientation on monitor config changes [Hans; !959]

View File

@ -600,10 +600,11 @@ cally_actor_real_remove_actor (ClutterActor *container,
g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), 0);
atk_parent = ATK_OBJECT (data);
atk_child = clutter_actor_get_accessible (actor);
if (atk_child)
if (clutter_actor_has_accessible (actor))
{
atk_child = clutter_actor_get_accessible (actor);
g_value_init (&values.old_value, G_TYPE_POINTER);
g_value_set_pointer (&values.old_value, atk_parent);

View File

@ -88,6 +88,10 @@ static void
clutter_actor_meta_real_set_actor (ClutterActorMeta *meta,
ClutterActor *actor)
{
g_warn_if_fail (!meta->priv->actor ||
!CLUTTER_ACTOR_IN_PAINT (meta->priv->actor));
g_warn_if_fail (!actor || !CLUTTER_ACTOR_IN_PAINT (actor));
if (meta->priv->actor == actor)
return;
@ -101,6 +105,18 @@ clutter_actor_meta_real_set_actor (ClutterActorMeta *meta,
meta);
}
static void
clutter_actor_meta_real_set_enabled (ClutterActorMeta *meta,
gboolean is_enabled)
{
g_warn_if_fail (!meta->priv->actor ||
!CLUTTER_ACTOR_IN_PAINT (meta->priv->actor));
meta->priv->is_enabled = is_enabled;
g_object_notify_by_pspec (G_OBJECT (meta), obj_props[PROP_ENABLED]);
}
static void
clutter_actor_meta_set_property (GObject *gobject,
guint prop_id,
@ -172,6 +188,7 @@ clutter_actor_meta_class_init (ClutterActorMetaClass *klass)
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
klass->set_actor = clutter_actor_meta_real_set_actor;
klass->set_enabled = clutter_actor_meta_real_set_enabled;
/**
* ClutterActorMeta:actor:
@ -298,9 +315,7 @@ clutter_actor_meta_set_enabled (ClutterActorMeta *meta,
if (meta->priv->is_enabled == is_enabled)
return;
meta->priv->is_enabled = is_enabled;
g_object_notify_by_pspec (G_OBJECT (meta), obj_props[PROP_ENABLED]);
CLUTTER_ACTOR_META_GET_CLASS (meta)->set_enabled (meta, is_enabled);
}
/**

View File

@ -87,6 +87,9 @@ struct _ClutterActorMetaClass
void (* set_actor) (ClutterActorMeta *meta,
ClutterActor *actor);
void (* set_enabled) (ClutterActorMeta *meta,
gboolean is_enabled);
/*< private >*/
void (* _clutter_meta1) (void);
void (* _clutter_meta2) (void);
@ -94,7 +97,6 @@ struct _ClutterActorMetaClass
void (* _clutter_meta4) (void);
void (* _clutter_meta5) (void);
void (* _clutter_meta6) (void);
void (* _clutter_meta7) (void);
};
CLUTTER_EXPORT

View File

@ -631,7 +631,7 @@
#include "clutter-color-static.h"
#include "clutter-color.h"
#include "clutter-constraint-private.h"
#include "clutter-container.h"
#include "clutter-container-private.h"
#include "clutter-content-private.h"
#include "clutter-debug.h"
#include "clutter-easing.h"
@ -710,6 +710,7 @@ struct _ClutterActorPrivate
guint8 opacity;
gint opacity_override;
unsigned int inhibit_culling_counter;
ClutterOffscreenRedirect offscreen_redirect;
@ -1124,6 +1125,20 @@ static GQuark quark_actor_layout_info = 0;
static GQuark quark_actor_transform_info = 0;
static GQuark quark_actor_animation_info = 0;
static GQuark quark_key = 0;
static GQuark quark_motion = 0;
static GQuark quark_pointer_focus = 0;
static GQuark quark_button = 0;
static GQuark quark_scroll = 0;
static GQuark quark_stage = 0;
static GQuark quark_destroy = 0;
static GQuark quark_client = 0;
static GQuark quark_delete = 0;
static GQuark quark_touch = 0;
static GQuark quark_touchpad = 0;
static GQuark quark_proximity = 0;
static GQuark quark_pad = 0;
G_DEFINE_TYPE_WITH_CODE (ClutterActor,
clutter_actor,
G_TYPE_INITIALLY_UNOWNED,
@ -2395,6 +2410,7 @@ clutter_actor_should_pick_paint (ClutterActor *self)
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
if (CLUTTER_ACTOR_IS_MAPPED (self) &&
clutter_actor_has_allocation (self) &&
(_clutter_context_get_pick_mode () == CLUTTER_PICK_ALL ||
CLUTTER_ACTOR_IS_REACTIVE (self)))
return TRUE;
@ -3474,8 +3490,8 @@ _clutter_actor_draw_paint_volume_full (ClutterActor *self,
cogl_pipeline_set_color (outline, &cogl_color);
pipeline_node = clutter_pipeline_node_new (outline);
clutter_paint_node_set_name (pipeline_node,
"ClutterActor (paint volume outline)");
clutter_paint_node_set_static_name (pipeline_node,
"ClutterActor (paint volume outline)");
clutter_paint_node_add_primitive (pipeline_node, prim);
clutter_paint_node_add_child (node, pipeline_node);
cogl_object_unref (prim);
@ -3489,8 +3505,8 @@ _clutter_actor_draw_paint_volume_full (ClutterActor *self,
pango_layout_set_text (layout, label, -1);
text_node = clutter_text_node_new (layout, color);
clutter_paint_node_set_name (text_node,
"ClutterActor (paint volume label)");
clutter_paint_node_set_static_name (text_node,
"ClutterActor (paint volume label)");
clutter_paint_node_add_rectangle (text_node,
&(ClutterActorBox) {
.x1 = pv->vertices[0].x,
@ -3586,8 +3602,8 @@ _clutter_actor_paint_cull_result (ClutterActor *self,
pango_layout_set_text (layout, label, -1);
text_node = clutter_text_node_new (layout, &color);
clutter_paint_node_set_name (text_node,
"ClutterActor (paint volume text)");
clutter_paint_node_set_static_name (text_node,
"ClutterActor (paint volume text)");
clutter_paint_node_add_rectangle (text_node,
&(ClutterActorBox) {
.x1 = 0.f,
@ -3743,7 +3759,13 @@ needs_flatten_effect (ClutterActor *self)
CLUTTER_DEBUG_DISABLE_OFFSCREEN_REDIRECT))
return FALSE;
if (priv->offscreen_redirect & CLUTTER_OFFSCREEN_REDIRECT_ALWAYS)
/* We need to enable the effect immediately even in ON_IDLE because that can
* only be implemented efficiently within the effect itself.
* If it was implemented here using just priv->is_dirty then we would lose
* the ability to animate opacity without repaints.
*/
if ((priv->offscreen_redirect & CLUTTER_OFFSCREEN_REDIRECT_ALWAYS) ||
(priv->offscreen_redirect & CLUTTER_OFFSCREEN_REDIRECT_ON_IDLE))
return TRUE;
else if (priv->offscreen_redirect & CLUTTER_OFFSCREEN_REDIRECT_AUTOMATIC_FOR_OPACITY)
{
@ -3844,14 +3866,7 @@ clutter_actor_paint_node (ClutterActor *actor,
fb = clutter_paint_context_get_base_framebuffer (paint_context);
if (clutter_stage_get_use_alpha (CLUTTER_STAGE (actor)))
{
bg_color.alpha = priv->opacity
* priv->bg_color.alpha
/ 255;
}
else
bg_color.alpha = 255;
bg_color.alpha = 255;
CLUTTER_NOTE (PAINT, "Stage clear color: (%d, %d, %d, %d)",
bg_color.red,
@ -3862,7 +3877,7 @@ clutter_actor_paint_node (ClutterActor *actor,
clear_flags = COGL_BUFFER_BIT_DEPTH;
node = clutter_root_node_new (fb, &bg_color, clear_flags);
clutter_paint_node_set_name (node, "stageClear");
clutter_paint_node_set_static_name (node, "stageClear");
clutter_paint_node_add_rectangle (node, &box);
clutter_paint_node_add_child (root, node);
clutter_paint_node_unref (node);
@ -3877,7 +3892,7 @@ clutter_actor_paint_node (ClutterActor *actor,
/ 255;
node = clutter_color_node_new (&bg_color);
clutter_paint_node_set_name (node, "backgroundColor");
clutter_paint_node_set_static_name (node, "backgroundColor");
clutter_paint_node_add_rectangle (node, &box);
clutter_paint_node_add_child (root, node);
clutter_paint_node_unref (node);
@ -3931,6 +3946,7 @@ clutter_actor_paint (ClutterActor *self,
g_autoptr (ClutterPaintNode) root_node = NULL;
ClutterActorPrivate *priv;
ClutterActorBox clip;
gboolean culling_inhibited;
gboolean clip_set = FALSE;
g_return_if_fail (CLUTTER_IS_ACTOR (self));
@ -3997,11 +4013,14 @@ clutter_actor_paint (ClutterActor *self,
clutter_actor_get_transform (self, &transform);
transform_node = clutter_transform_node_new (&transform);
clutter_paint_node_add_child (transform_node, root_node);
clutter_paint_node_unref (root_node);
if (!cogl_matrix_is_identity (&transform))
{
transform_node = clutter_transform_node_new (&transform);
clutter_paint_node_add_child (transform_node, root_node);
clutter_paint_node_unref (root_node);
root_node = g_steal_pointer (&transform_node);
root_node = g_steal_pointer (&transform_node);
}
#ifdef CLUTTER_ENABLE_DEBUG
/* Catch when out-of-band transforms have been made by actors not as part
@ -4076,7 +4095,8 @@ clutter_actor_paint (ClutterActor *self,
* paint then the last-paint-volume would likely represent the new
* actor position not the old.
*/
if (!in_clone_paint ())
culling_inhibited = priv->inhibit_culling_counter > 0;
if (!culling_inhibited && !in_clone_paint ())
{
gboolean success;
/* annoyingly gcc warns if uninitialized even though
@ -4158,7 +4178,7 @@ clutter_actor_continue_paint (ClutterActor *self,
*/
framebuffer = clutter_paint_context_get_base_framebuffer (paint_context);
dummy = _clutter_dummy_node_new (self, framebuffer);
clutter_paint_node_set_name (dummy, "Root");
clutter_paint_node_set_static_name (dummy, "Root");
/* XXX - for 1.12, we use the return value of paint_node() to
* decide whether we should emit the ::paint signal.
@ -4198,6 +4218,11 @@ clutter_actor_continue_paint (ClutterActor *self,
run_flags |= CLUTTER_EFFECT_PAINT_ACTOR_DIRTY;
}
if (priv->current_effect == priv->flatten_effect &&
priv->offscreen_redirect & CLUTTER_OFFSCREEN_REDIRECT_ON_IDLE &&
run_flags & CLUTTER_EFFECT_PAINT_ACTOR_DIRTY)
run_flags |= CLUTTER_EFFECT_PAINT_BYPASS_EFFECT;
_clutter_effect_paint (priv->current_effect, paint_context, run_flags);
priv->current_effect = old_current_effect;
@ -4568,7 +4593,7 @@ clutter_actor_remove_child_internal (ClutterActor *self,
/* we need to emit the signal before dropping the reference */
if (emit_actor_removed)
g_signal_emit_by_name (self, "actor-removed", child);
_clutter_container_emit_actor_removed (CLUTTER_CONTAINER (self), child);
if (notify_first_last)
{
@ -6531,6 +6556,20 @@ clutter_actor_class_init (ClutterActorClass *klass)
quark_actor_transform_info = g_quark_from_static_string ("-clutter-actor-transform-info");
quark_actor_animation_info = g_quark_from_static_string ("-clutter-actor-animation-info");
quark_key = g_quark_from_static_string ("key");
quark_motion = g_quark_from_static_string ("motion");
quark_pointer_focus = g_quark_from_static_string ("pointer-focus");
quark_button = g_quark_from_static_string ("button");
quark_scroll = g_quark_from_static_string ("scroll");
quark_stage = g_quark_from_static_string ("stage");
quark_destroy = g_quark_from_static_string ("destroy");
quark_client = g_quark_from_static_string ("client");
quark_delete = g_quark_from_static_string ("delete");
quark_touch = g_quark_from_static_string ("touch");
quark_touchpad = g_quark_from_static_string ("touchpad");
quark_proximity = g_quark_from_static_string ("proximity");
quark_pad = g_quark_from_static_string ("pad");
object_class->constructor = clutter_actor_constructor;
object_class->set_property = clutter_actor_set_property;
object_class->get_property = clutter_actor_get_property;
@ -8566,7 +8605,7 @@ clutter_actor_class_init (ClutterActorClass *klass)
actor_signals[CAPTURED_EVENT] =
g_signal_new (I_("captured-event"),
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
G_STRUCT_OFFSET (ClutterActorClass, captured_event),
_clutter_boolean_handled_accumulator, NULL,
_clutter_marshal_BOOLEAN__BOXED,
@ -13212,7 +13251,7 @@ clutter_actor_add_child_internal (ClutterActor *self,
}
if (emit_actor_added)
g_signal_emit_by_name (self, "actor-added", child);
_clutter_container_emit_actor_added (CLUTTER_CONTAINER (self), child);
if (notify_first_last)
{
@ -13876,8 +13915,70 @@ clutter_actor_event (ClutterActor *actor,
if (capture)
{
g_signal_emit (actor, actor_signals[CAPTURED_EVENT], 0,
event,
GQuark detail = 0;
switch (event->type)
{
case CLUTTER_NOTHING:
break;
case CLUTTER_KEY_PRESS:
case CLUTTER_KEY_RELEASE:
detail = quark_key;
break;
case CLUTTER_MOTION:
detail = quark_motion;
break;
case CLUTTER_ENTER:
case CLUTTER_LEAVE:
detail = quark_pointer_focus;
break;
case CLUTTER_BUTTON_PRESS:
case CLUTTER_BUTTON_RELEASE:
detail = quark_button;
break;
case CLUTTER_SCROLL:
detail = quark_scroll;
break;
case CLUTTER_STAGE_STATE:
detail = quark_stage;
break;
case CLUTTER_DESTROY_NOTIFY:
detail = quark_destroy;
break;
case CLUTTER_CLIENT_MESSAGE:
detail = quark_client;
break;
case CLUTTER_DELETE:
detail = quark_delete;
break;
case CLUTTER_TOUCH_BEGIN:
case CLUTTER_TOUCH_UPDATE:
case CLUTTER_TOUCH_END:
case CLUTTER_TOUCH_CANCEL:
detail = quark_touch;
break;
case CLUTTER_TOUCHPAD_PINCH:
case CLUTTER_TOUCHPAD_SWIPE:
detail = quark_touchpad;
break;
case CLUTTER_PROXIMITY_IN:
case CLUTTER_PROXIMITY_OUT:
detail = quark_proximity;
break;
case CLUTTER_PAD_BUTTON_PRESS:
case CLUTTER_PAD_BUTTON_RELEASE:
case CLUTTER_PAD_STRIP:
case CLUTTER_PAD_RING:
detail = quark_pad;
break;
case CLUTTER_EVENT_LAST: /* Just keep compiler warnings quiet */
break;
}
g_signal_emit (actor,
actor_signals[CAPTURED_EVENT],
detail,
event,
&retval);
goto out;
}
@ -15898,6 +15999,63 @@ clutter_actor_get_opacity_override (ClutterActor *self)
return self->priv->opacity_override;
}
/**
* clutter_actor_inhibit_culling:
* @actor: a #ClutterActor
*
* Increases the culling inhibitor counter. Inhibiting culling
* forces the actor to be painted even when outside the visible
* bounds of the stage view.
*
* This is usually necessary when an actor is being painted on
* another paint context.
*
* Pair with clutter_actor_uninhibit_culling() when the actor doesn't
* need to be painted anymore.
*/
void
clutter_actor_inhibit_culling (ClutterActor *actor)
{
ClutterActorPrivate *priv;
g_return_if_fail (CLUTTER_IS_ACTOR (actor));
priv = actor->priv;
priv->inhibit_culling_counter++;
_clutter_actor_set_enable_paint_unmapped (actor, TRUE);
}
/**
* clutter_actor_uninhibit_culling:
* @actor: a #ClutterActor
*
* Decreases the culling inhibitor counter. See clutter_actor_inhibit_culling()
* for when inhibit culling is necessary.
*
* Calling this function without a matching call to
* clutter_actor_inhibit_culling() is a programming error.
*/
void
clutter_actor_uninhibit_culling (ClutterActor *actor)
{
ClutterActorPrivate *priv;
g_return_if_fail (CLUTTER_IS_ACTOR (actor));
priv = actor->priv;
if (priv->inhibit_culling_counter == 0)
{
g_critical ("Unpaired call to clutter_actor_uninhibit_culling");
return;
}
priv->inhibit_culling_counter--;
if (priv->inhibit_culling_counter == 0)
_clutter_actor_set_enable_paint_unmapped (actor, FALSE);
}
/* Allows you to disable applying the actors model view transform during
* a paint. Used by ClutterClone. */
void
@ -17611,10 +17769,42 @@ _clutter_actor_compute_resource_scale (ClutterActor *self,
resource_scale))
{
if (priv->parent)
return _clutter_actor_compute_resource_scale (priv->parent,
resource_scale);
{
gboolean in_clone_paint;
gboolean was_parent_in_clone_paint;
gboolean was_parent_unmapped;
gboolean was_parent_paint_unmapped;
gboolean ret;
in_clone_paint = clutter_actor_is_in_clone_paint (self);
was_parent_unmapped = !clutter_actor_is_mapped (priv->parent);
was_parent_in_clone_paint =
clutter_actor_is_in_clone_paint (priv->parent);
was_parent_paint_unmapped = priv->parent->priv->enable_paint_unmapped;
if (in_clone_paint && was_parent_unmapped)
{
_clutter_actor_set_in_clone_paint (priv->parent, TRUE);
_clutter_actor_set_enable_paint_unmapped (priv->parent, TRUE);
}
ret = _clutter_actor_compute_resource_scale (priv->parent,
resource_scale);
if (in_clone_paint && was_parent_unmapped)
{
_clutter_actor_set_in_clone_paint (priv->parent,
was_parent_in_clone_paint);
_clutter_actor_set_enable_paint_unmapped (priv->parent,
was_parent_paint_unmapped);
}
return ret;
}
else
return FALSE;
{
return FALSE;
}
}
return TRUE;
@ -19770,6 +19960,23 @@ clutter_actor_get_transition (ClutterActor *self,
return clos->transition;
}
/**
* clutter_actor_has_transitions: (skip)
*/
gboolean
clutter_actor_has_transitions (ClutterActor *self)
{
const ClutterAnimationInfo *info;
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
info = _clutter_actor_get_animation_info_or_defaults (self);
if (info->transitions == NULL)
return FALSE;
return g_hash_table_size (info->transitions) > 0;
}
/**
* clutter_actor_save_easing_state:
* @self: a #ClutterActor
@ -21157,7 +21364,7 @@ clutter_actor_create_texture_paint_node (ClutterActor *self,
color.alpha = clutter_actor_get_paint_opacity_internal (self);
node = clutter_texture_node_new (texture, &color, priv->min_filter, priv->mag_filter);
clutter_paint_node_set_name (node, "Texture");
clutter_paint_node_set_static_name (node, "Texture");
if (priv->content_repeat == CLUTTER_REPEAT_NONE)
clutter_paint_node_add_rectangle (node, &box);
@ -21178,3 +21385,14 @@ clutter_actor_create_texture_paint_node (ClutterActor *self,
return node;
}
gboolean
clutter_actor_has_accessible (ClutterActor *actor)
{
g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), FALSE);
if (CLUTTER_ACTOR_GET_CLASS (actor)->has_accessible)
return CLUTTER_ACTOR_GET_CLASS (actor)->has_accessible (actor);
return TRUE;
}

View File

@ -299,10 +299,11 @@ struct _ClutterActorClass
gboolean (* touch_event) (ClutterActor *self,
ClutterTouchEvent *event);
gboolean (* has_accessible) (ClutterActor *self);
/*< private >*/
/* padding for future expansion */
gpointer _padding_dummy[26];
gpointer _padding_dummy[25];
};
/**
@ -380,6 +381,8 @@ CLUTTER_EXPORT
const gchar * clutter_actor_get_name (ClutterActor *self);
CLUTTER_EXPORT
AtkObject * clutter_actor_get_accessible (ClutterActor *self);
CLUTTER_EXPORT
gboolean clutter_actor_has_accessible (ClutterActor *self);
CLUTTER_EXPORT
gboolean clutter_actor_is_visible (ClutterActor *self);
@ -881,6 +884,11 @@ void clutter_actor_set_opacity_override
CLUTTER_EXPORT
gint clutter_actor_get_opacity_override (ClutterActor *self);
CLUTTER_EXPORT
void clutter_actor_inhibit_culling (ClutterActor *actor);
CLUTTER_EXPORT
void clutter_actor_uninhibit_culling (ClutterActor *actor);
/**
* ClutterActorCreateChildFunc:
* @item: (type GObject): the item in the model

View File

@ -50,8 +50,6 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterConstraint, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterContainer, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterDeformEffect, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterDesaturateEffect, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterDragAction, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterDropAction, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterEffect, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterFixedLayout, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterFlowLayout, g_object_unref)

View File

@ -352,7 +352,7 @@ clutter_canvas_paint_content (ClutterContent *content,
return;
node = clutter_actor_create_texture_paint_node (actor, priv->texture);
clutter_paint_node_set_name (node, "Canvas Content");
clutter_paint_node_set_static_name (node, "Canvas Content");
clutter_paint_node_add_child (root, node);
clutter_paint_node_unref (node);

View File

@ -238,6 +238,7 @@ click_action_query_long_press (ClutterClickAction *action)
if (result)
{
g_clear_handle_id (&priv->long_press_id, g_source_remove);
priv->long_press_id =
clutter_threads_add_timeout (timeout,
click_action_emit_long_press,
@ -467,6 +468,20 @@ clutter_click_action_set_actor (ClutterActorMeta *meta,
CLUTTER_ACTOR_META_CLASS (clutter_click_action_parent_class)->set_actor (meta, actor);
}
static void
clutter_click_action_set_enabled (ClutterActorMeta *meta,
gboolean is_enabled)
{
ClutterClickAction *click_action = CLUTTER_CLICK_ACTION (meta);
ClutterActorMetaClass *parent_class =
CLUTTER_ACTOR_META_CLASS (clutter_click_action_parent_class);
if (!is_enabled)
clutter_click_action_release (click_action);
parent_class->set_enabled (meta, is_enabled);
}
static void
clutter_click_action_set_property (GObject *gobject,
guint prop_id,
@ -546,6 +561,7 @@ clutter_click_action_class_init (ClutterClickActionClass *klass)
ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (klass);
meta_class->set_actor = clutter_click_action_set_actor;
meta_class->set_enabled = clutter_click_action_set_enabled;
gobject_class->dispose = clutter_click_action_dispose;
gobject_class->set_property = clutter_click_action_set_property;

View File

@ -9,7 +9,13 @@
G_BEGIN_DECLS
@CLUTTER_CONFIG_DEFINES@
#mesondefine CLUTTER_HAS_WAYLAND_COMPOSITOR_SUPPORT
#mesondefine CLUTTER_WINDOWING_X11
#mesondefine CLUTTER_INPUT_X11
#mesondefine CLUTTER_WINDOWING_GLX
#mesondefine CLUTTER_WINDOWING_EGL
#mesondefine CLUTTER_INPUT_EVDEV
#mesondefine CLUTTER_INPUT_NULL
G_END_DECLS

View File

@ -160,28 +160,26 @@ constraint_update_preferred_size (ClutterConstraint *constraint,
}
static void
clutter_constraint_notify (GObject *gobject,
GParamSpec *pspec)
clutter_constraint_set_enabled (ClutterActorMeta *meta,
gboolean is_enabled)
{
if (strcmp (pspec->name, "enabled") == 0)
{
ClutterActorMeta *meta = CLUTTER_ACTOR_META (gobject);
ClutterActor *actor = clutter_actor_meta_get_actor (meta);
ClutterActorMetaClass *parent_class =
CLUTTER_ACTOR_META_CLASS (clutter_constraint_parent_class);
ClutterActor *actor;
if (actor != NULL)
clutter_actor_queue_relayout (actor);
}
actor = clutter_actor_meta_get_actor (meta);
if (actor)
clutter_actor_queue_relayout (actor);
if (G_OBJECT_CLASS (clutter_constraint_parent_class)->notify != NULL)
G_OBJECT_CLASS (clutter_constraint_parent_class)->notify (gobject, pspec);
parent_class->set_enabled (meta, is_enabled);
}
static void
clutter_constraint_class_init (ClutterConstraintClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
ClutterActorMetaClass *actor_meta_class = CLUTTER_ACTOR_META_CLASS (klass);
gobject_class->notify = clutter_constraint_notify;
actor_meta_class->set_enabled = clutter_constraint_set_enabled;
klass->update_allocation = constraint_update_allocation;
klass->update_preferred_size = constraint_update_preferred_size;

View File

@ -0,0 +1,36 @@
/*
* Clutter.
*
* An OpenGL based 'interactive canvas' library.
*
* Copyright 2020 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __CLUTTER_CONTAINER_PRIVATE_H__
#define __CLUTTER_CONTAINER_PRIVATE_H__
#include <clutter/clutter-container.h>
G_BEGIN_DECLS
void _clutter_container_emit_actor_added (ClutterContainer *container,
ClutterActor *actor);
void _clutter_container_emit_actor_removed (ClutterContainer *container,
ClutterActor *actor);
G_END_DECLS
#endif /* __CLUTTER_CONTAINER_PRIVATE_H__ */

View File

@ -37,6 +37,7 @@
#include "clutter-actor-private.h"
#include "clutter-child-meta.h"
#include "clutter-container-private.h"
#include "clutter-debug.h"
#include "clutter-main.h"
#include "clutter-marshal.h"
@ -1250,3 +1251,23 @@ clutter_container_child_notify (ClutterContainer *container,
child,
pspec);
}
void
_clutter_container_emit_actor_added (ClutterContainer *container,
ClutterActor *actor)
{
g_return_if_fail (CLUTTER_IS_CONTAINER (container));
g_return_if_fail (CLUTTER_IS_ACTOR (actor));
g_signal_emit (container, container_signals[ACTOR_ADDED], 0, actor);
}
void
_clutter_container_emit_actor_removed (ClutterContainer *container,
ClutterActor *actor)
{
g_return_if_fail (CLUTTER_IS_CONTAINER (container));
g_return_if_fail (CLUTTER_IS_ACTOR (actor));
g_signal_emit (container, container_signals[ACTOR_REMOVED], 0, actor);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,152 +0,0 @@
/*
* Clutter.
*
* An OpenGL based 'interactive canvas' library.
*
* Copyright (C) 2010 Intel Corporation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Author:
* Emmanuele Bassi <ebassi@linux.intel.com>
*/
#ifndef __CLUTTER_DRAG_ACTION_H__
#define __CLUTTER_DRAG_ACTION_H__
#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
#error "Only <clutter/clutter.h> can be included directly."
#endif
#include <clutter/clutter-action.h>
#include <clutter/clutter-event.h>
G_BEGIN_DECLS
#define CLUTTER_TYPE_DRAG_ACTION (clutter_drag_action_get_type ())
#define CLUTTER_DRAG_ACTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_DRAG_ACTION, ClutterDragAction))
#define CLUTTER_IS_DRAG_ACTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_DRAG_ACTION))
#define CLUTTER_DRAG_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_DRAG_ACTION, ClutterDragActionClass))
#define CLUTTER_IS_DRAG_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_DRAG_ACTION))
#define CLUTTER_DRAG_ACTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_DRAG_ACTION, ClutterDragActionClass))
typedef struct _ClutterDragAction ClutterDragAction;
typedef struct _ClutterDragActionPrivate ClutterDragActionPrivate;
typedef struct _ClutterDragActionClass ClutterDragActionClass;
/**
* ClutterDragAction:
*
* The #ClutterDragAction structure contains only
* private data and should be accessed using the provided API
*
* Since: 1.4
*/
struct _ClutterDragAction
{
/*< private >*/
ClutterAction parent_instance;
ClutterDragActionPrivate *priv;
};
/**
* ClutterDragActionClass:
* @drag_begin: class handler of the #ClutterDragAction::drag-begin signal
* @drag_motion: class handler of the #ClutterDragAction::drag-motion signal
* @drag_end: class handler of the #ClutterDragAction::drag-end signal
* @drag_progress: class handler of the #ClutterDragAction::drag-progress signal
*
* The #ClutterDragActionClass structure contains
* only private data
*
* Since: 1.4
*/
struct _ClutterDragActionClass
{
/*< private >*/
ClutterActionClass parent_class;
/*< public >*/
void (* drag_begin) (ClutterDragAction *action,
ClutterActor *actor,
gfloat event_x,
gfloat event_y,
ClutterModifierType modifiers);
void (* drag_motion) (ClutterDragAction *action,
ClutterActor *actor,
gfloat delta_x,
gfloat delta_y);
void (* drag_end) (ClutterDragAction *action,
ClutterActor *actor,
gfloat event_x,
gfloat event_y,
ClutterModifierType modifiers);
gboolean (* drag_progress) (ClutterDragAction *action,
ClutterActor *actor,
gfloat delta_x,
gfloat delta_y);
/*< private >*/
void (* _clutter_drag_action1) (void);
void (* _clutter_drag_action2) (void);
void (* _clutter_drag_action3) (void);
void (* _clutter_drag_action4) (void);
};
CLUTTER_EXPORT
GType clutter_drag_action_get_type (void) G_GNUC_CONST;
CLUTTER_EXPORT
ClutterAction * clutter_drag_action_new (void);
CLUTTER_EXPORT
void clutter_drag_action_set_drag_threshold (ClutterDragAction *action,
gint x_threshold,
gint y_threshold);
CLUTTER_EXPORT
void clutter_drag_action_get_drag_threshold (ClutterDragAction *action,
guint *x_threshold,
guint *y_threshold);
CLUTTER_EXPORT
void clutter_drag_action_set_drag_handle (ClutterDragAction *action,
ClutterActor *handle);
CLUTTER_EXPORT
ClutterActor * clutter_drag_action_get_drag_handle (ClutterDragAction *action);
CLUTTER_EXPORT
void clutter_drag_action_set_drag_axis (ClutterDragAction *action,
ClutterDragAxis axis);
CLUTTER_EXPORT
ClutterDragAxis clutter_drag_action_get_drag_axis (ClutterDragAction *action);
CLUTTER_EXPORT
void clutter_drag_action_get_press_coords (ClutterDragAction *action,
gfloat *press_x,
gfloat *press_y);
CLUTTER_EXPORT
void clutter_drag_action_get_motion_coords (ClutterDragAction *action,
gfloat *motion_x,
gfloat *motion_y);
CLUTTER_EXPORT
gboolean clutter_drag_action_get_drag_area (ClutterDragAction *action,
graphene_rect_t *drag_area);
CLUTTER_EXPORT
void clutter_drag_action_set_drag_area (ClutterDragAction *action,
const graphene_rect_t *drag_area);
G_END_DECLS
#endif /* __CLUTTER_DRAG_ACTION_H__ */

View File

@ -1,527 +0,0 @@
/*
* Clutter.
*
* An OpenGL based 'interactive canvas' library.
*
* Copyright © 2011 Intel Corporation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Author:
* Emmanuele Bassi <ebassi@linux.intel.com>
*/
/**
* SECTION:clutter-drop-action
* @Title: ClutterDropAction
* @short_description: An action for drop targets
*
* #ClutterDropAction is a #ClutterAction that allows a #ClutterActor
* implementation to control what happens when an actor dragged using
* a #ClutterDragAction crosses the target area or when a dragged actor
* is released (or "dropped") on the target area.
*
* A trivial use of #ClutterDropAction consists in connecting to the
* #ClutterDropAction::drop signal and handling the drop from there,
* for instance:
*
* |[<!-- language="C" -->
* ClutterAction *action = clutter_drop_action ();
*
* g_signal_connect (action, "drop", G_CALLBACK (on_drop), NULL);
* clutter_actor_add_action (an_actor, action);
* ]|
*
* The #ClutterDropAction::can-drop can be used to control whether the
* #ClutterDropAction::drop signal is going to be emitted; returning %FALSE
* from a handler connected to the #ClutterDropAction::can-drop signal will
* cause the #ClutterDropAction::drop signal to be skipped when the input
* device button is released.
*
* It's important to note that #ClutterDropAction will only work with
* actors dragged using #ClutterDragAction.
*
* See [drop-action.c](https://git.gnome.org/browse/clutter/tree/examples/drop-action.c?h=clutter-1.18)
* for an example of how to use #ClutterDropAction.
*
* #ClutterDropAction is available since Clutter 1.8
*/
#include "clutter-build-config.h"
#include "clutter-drop-action.h"
#include "clutter-actor-meta-private.h"
#include "clutter-actor-private.h"
#include "clutter-drag-action.h"
#include "clutter-main.h"
#include "clutter-marshal.h"
#include "clutter-stage-private.h"
struct _ClutterDropActionPrivate
{
ClutterActor *actor;
ClutterActor *stage;
gulong mapped_id;
};
typedef struct _DropTarget {
ClutterActor *stage;
gulong capture_id;
GHashTable *actions;
ClutterDropAction *last_action;
} DropTarget;
enum
{
CAN_DROP,
OVER_IN,
OVER_OUT,
DROP,
DROP_CANCEL,
LAST_SIGNAL
};
static guint drop_signals[LAST_SIGNAL] = { 0, };
G_DEFINE_TYPE_WITH_PRIVATE (ClutterDropAction, clutter_drop_action, CLUTTER_TYPE_ACTION)
static void
drop_target_free (gpointer _data)
{
DropTarget *data = _data;
g_clear_signal_handler (&data->capture_id, data->stage);
g_hash_table_destroy (data->actions);
g_free (data);
}
static gboolean
on_stage_capture (ClutterStage *stage,
ClutterEvent *event,
gpointer user_data)
{
DropTarget *data = user_data;
gfloat event_x, event_y;
ClutterActor *actor, *drag_actor;
ClutterDropAction *drop_action;
ClutterInputDevice *device;
gboolean was_reactive;
switch (clutter_event_type (event))
{
case CLUTTER_MOTION:
case CLUTTER_BUTTON_RELEASE:
if (clutter_event_type (event) == CLUTTER_MOTION &&
!(clutter_event_get_state (event) & CLUTTER_BUTTON1_MASK))
return CLUTTER_EVENT_PROPAGATE;
if (clutter_event_type (event) == CLUTTER_BUTTON_RELEASE &&
clutter_event_get_button (event) != CLUTTER_BUTTON_PRIMARY)
return CLUTTER_EVENT_PROPAGATE;
device = clutter_event_get_device (event);
drag_actor = _clutter_stage_get_pointer_drag_actor (stage, device);
if (drag_actor == NULL)
return CLUTTER_EVENT_PROPAGATE;
break;
case CLUTTER_TOUCH_UPDATE:
case CLUTTER_TOUCH_END:
drag_actor = _clutter_stage_get_touch_drag_actor (stage,
clutter_event_get_event_sequence (event));
if (drag_actor == NULL)
return CLUTTER_EVENT_PROPAGATE;
break;
default:
return CLUTTER_EVENT_PROPAGATE;
}
clutter_event_get_coords (event, &event_x, &event_y);
/* get the actor under the cursor, excluding the dragged actor; we
* use reactivity because it won't cause any scene invalidation
*/
was_reactive = clutter_actor_get_reactive (drag_actor);
clutter_actor_set_reactive (drag_actor, FALSE);
actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_REACTIVE,
event_x,
event_y);
if (actor == NULL || actor == CLUTTER_ACTOR (stage))
{
if (data->last_action != NULL)
{
ClutterActorMeta *meta = CLUTTER_ACTOR_META (data->last_action);
g_signal_emit (data->last_action, drop_signals[OVER_OUT], 0,
clutter_actor_meta_get_actor (meta));
data->last_action = NULL;
}
goto out;
}
drop_action = g_hash_table_lookup (data->actions, actor);
if (drop_action == NULL)
{
if (data->last_action != NULL)
{
ClutterActorMeta *meta = CLUTTER_ACTOR_META (data->last_action);
g_signal_emit (data->last_action, drop_signals[OVER_OUT], 0,
clutter_actor_meta_get_actor (meta));
data->last_action = NULL;
}
goto out;
}
else
{
if (data->last_action != drop_action)
{
ClutterActorMeta *meta;
if (data->last_action != NULL)
{
meta = CLUTTER_ACTOR_META (data->last_action);
g_signal_emit (data->last_action, drop_signals[OVER_OUT], 0,
clutter_actor_meta_get_actor (meta));
}
meta = CLUTTER_ACTOR_META (drop_action);
g_signal_emit (drop_action, drop_signals[OVER_IN], 0,
clutter_actor_meta_get_actor (meta));
}
data->last_action = drop_action;
}
out:
if (clutter_event_type (event) == CLUTTER_BUTTON_RELEASE ||
clutter_event_type (event) == CLUTTER_TOUCH_END)
{
if (data->last_action != NULL)
{
ClutterActorMeta *meta = CLUTTER_ACTOR_META (data->last_action);
gboolean can_drop = FALSE;
g_signal_emit (data->last_action, drop_signals[CAN_DROP], 0,
clutter_actor_meta_get_actor (meta),
event_x, event_y,
&can_drop);
if (can_drop)
{
g_signal_emit (data->last_action, drop_signals[DROP], 0,
clutter_actor_meta_get_actor (meta),
event_x, event_y);
}
else
{
g_signal_emit (data->last_action, drop_signals[DROP_CANCEL], 0,
clutter_actor_meta_get_actor (meta),
event_x, event_y);
}
}
data->last_action = NULL;
}
if (drag_actor != NULL)
clutter_actor_set_reactive (drag_actor, was_reactive);
return CLUTTER_EVENT_PROPAGATE;
}
static void
drop_action_register (ClutterDropAction *self)
{
ClutterDropActionPrivate *priv = self->priv;
DropTarget *data;
g_assert (priv->stage != NULL);
data = g_object_get_data (G_OBJECT (priv->stage), "__clutter_drop_targets");
if (data == NULL)
{
data = g_new0 (DropTarget, 1);
data->stage = priv->stage;
data->actions = g_hash_table_new (NULL, NULL);
data->capture_id = g_signal_connect (priv->stage, "captured-event",
G_CALLBACK (on_stage_capture),
data);
g_object_set_data_full (G_OBJECT (priv->stage), "__clutter_drop_targets",
data,
drop_target_free);
}
g_hash_table_replace (data->actions, priv->actor, self);
}
static void
drop_action_unregister (ClutterDropAction *self)
{
ClutterDropActionPrivate *priv = self->priv;
DropTarget *data = NULL;
if (priv->stage != NULL)
data = g_object_get_data (G_OBJECT (priv->stage), "__clutter_drop_targets");
if (data == NULL)
return;
g_hash_table_remove (data->actions, priv->actor);
if (g_hash_table_size (data->actions) == 0)
g_object_set_data (G_OBJECT (data->stage), "__clutter_drop_targets", NULL);
}
static void
on_actor_mapped (ClutterActor *actor,
GParamSpec *pspec,
ClutterDropAction *self)
{
if (clutter_actor_is_mapped (actor))
{
if (self->priv->stage == NULL)
self->priv->stage = clutter_actor_get_stage (actor);
drop_action_register (self);
}
else
drop_action_unregister (self);
}
static void
clutter_drop_action_set_actor (ClutterActorMeta *meta,
ClutterActor *actor)
{
ClutterDropActionPrivate *priv = CLUTTER_DROP_ACTION (meta)->priv;
if (priv->actor != NULL)
{
drop_action_unregister (CLUTTER_DROP_ACTION (meta));
g_clear_signal_handler (&priv->mapped_id, priv->actor);
priv->stage = NULL;
priv->actor = NULL;
}
priv->actor = actor;
if (priv->actor != NULL)
{
priv->stage = clutter_actor_get_stage (actor);
priv->mapped_id = g_signal_connect (actor, "notify::mapped",
G_CALLBACK (on_actor_mapped),
meta);
if (priv->stage != NULL)
drop_action_register (CLUTTER_DROP_ACTION (meta));
}
CLUTTER_ACTOR_META_CLASS (clutter_drop_action_parent_class)->set_actor (meta, actor);
}
static gboolean
signal_accumulator (GSignalInvocationHint *ihint,
GValue *return_accu,
const GValue *handler_return,
gpointer user_data)
{
gboolean continue_emission;
continue_emission = g_value_get_boolean (handler_return);
g_value_set_boolean (return_accu, continue_emission);
return continue_emission;
}
static gboolean
clutter_drop_action_real_can_drop (ClutterDropAction *action,
ClutterActor *actor,
gfloat event_x,
gfloat event_y)
{
return TRUE;
}
static void
clutter_drop_action_class_init (ClutterDropActionClass *klass)
{
ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (klass);
meta_class->set_actor = clutter_drop_action_set_actor;
klass->can_drop = clutter_drop_action_real_can_drop;
/**
* ClutterDropAction::can-drop:
* @action: the #ClutterDropAction that emitted the signal
* @actor: the #ClutterActor attached to the @action
* @event_x: the X coordinate (in stage space) of the drop event
* @event_y: the Y coordinate (in stage space) of the drop event
*
* The ::can-drop signal is emitted when the dragged actor is dropped
* on @actor. The return value of the ::can-drop signal will determine
* whether or not the #ClutterDropAction::drop signal is going to be
* emitted on @action.
*
* The default implementation of #ClutterDropAction returns %TRUE for
* this signal.
*
* Return value: %TRUE if the drop is accepted, and %FALSE otherwise
*
* Since: 1.8
*/
drop_signals[CAN_DROP] =
g_signal_new (I_("can-drop"),
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ClutterDropActionClass, can_drop),
signal_accumulator, NULL,
_clutter_marshal_BOOLEAN__OBJECT_FLOAT_FLOAT,
G_TYPE_BOOLEAN, 3,
CLUTTER_TYPE_ACTOR,
G_TYPE_FLOAT,
G_TYPE_FLOAT);
/**
* ClutterDropAction::over-in:
* @action: the #ClutterDropAction that emitted the signal
* @actor: the #ClutterActor attached to the @action
*
* The ::over-in signal is emitted when the dragged actor crosses
* into @actor.
*
* Since: 1.8
*/
drop_signals[OVER_IN] =
g_signal_new (I_("over-in"),
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ClutterDropActionClass, over_in),
NULL, NULL, NULL,
G_TYPE_NONE, 1,
CLUTTER_TYPE_ACTOR);
/**
* ClutterDropAction::over-out:
* @action: the #ClutterDropAction that emitted the signal
* @actor: the #ClutterActor attached to the @action
*
* The ::over-out signal is emitted when the dragged actor crosses
* outside @actor.
*
* Since: 1.8
*/
drop_signals[OVER_OUT] =
g_signal_new (I_("over-out"),
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ClutterDropActionClass, over_out),
NULL, NULL, NULL,
G_TYPE_NONE, 1,
CLUTTER_TYPE_ACTOR);
/**
* ClutterDropAction::drop:
* @action: the #ClutterDropAction that emitted the signal
* @actor: the #ClutterActor attached to the @action
* @event_x: the X coordinate (in stage space) of the drop event
* @event_y: the Y coordinate (in stage space) of the drop event
*
* The ::drop signal is emitted when the dragged actor is dropped
* on @actor. This signal is only emitted if at least an handler of
* #ClutterDropAction::can-drop returns %TRUE.
*
* Since: 1.8
*/
drop_signals[DROP] =
g_signal_new (I_("drop"),
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ClutterDropActionClass, drop),
NULL, NULL,
_clutter_marshal_VOID__OBJECT_FLOAT_FLOAT,
G_TYPE_NONE, 3,
CLUTTER_TYPE_ACTOR,
G_TYPE_FLOAT,
G_TYPE_FLOAT);
/**
* ClutterDropAction::drop-cancel:
* @action: the #ClutterDropAction that emitted the signal
* @actor: the #ClutterActor attached to the @action
* @event_x: the X coordinate (in stage space) of the drop event
* @event_y: the Y coordinate (in stage space) of the drop event
*
* The ::drop-cancel signal is emitted when the drop is refused
* by an emission of the #ClutterDropAction::can-drop signal.
*
* After the ::drop-cancel signal is fired the active drag is
* terminated.
*
* Since: 1.12
*/
drop_signals[DROP_CANCEL] =
g_signal_new (I_("drop-cancel"),
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ClutterDropActionClass, drop),
NULL, NULL,
_clutter_marshal_VOID__OBJECT_FLOAT_FLOAT,
G_TYPE_NONE, 3,
CLUTTER_TYPE_ACTOR,
G_TYPE_FLOAT,
G_TYPE_FLOAT);
}
static void
clutter_drop_action_init (ClutterDropAction *self)
{
self->priv = clutter_drop_action_get_instance_private (self);
}
/**
* clutter_drop_action_new:
*
* Creates a new #ClutterDropAction.
*
* Use clutter_actor_add_action() to add the action to a #ClutterActor.
*
* Return value: the newly created #ClutterDropAction
*
* Since: 1.8
*/
ClutterAction *
clutter_drop_action_new (void)
{
return g_object_new (CLUTTER_TYPE_DROP_ACTION, NULL);
}

View File

@ -1,115 +0,0 @@
/*
* Clutter.
*
* An OpenGL based 'interactive canvas' library.
*
* Copyright © 2011 Intel Corporation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Author:
* Emmanuele Bassi <ebassi@linux.intel.com>
*/
#ifndef __CLUTTER_DROP_ACTION_H__
#define __CLUTTER_DROP_ACTION_H__
#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
#error "Only <clutter/clutter.h> can be directly included."
#endif
#include <clutter/clutter-action.h>
G_BEGIN_DECLS
#define CLUTTER_TYPE_DROP_ACTION (clutter_drop_action_get_type ())
#define CLUTTER_DROP_ACTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_DROP_ACTION, ClutterDropAction))
#define CLUTTER_IS_DROP_ACTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_DROP_ACTION))
#define CLUTTER_DROP_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_DROP_ACTION, ClutterDropActionClass))
#define CLUTTER_IS_DROP_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_DROP_ACTION))
#define CLUTTER_DROP_ACTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_DROP_ACTION, ClutterDropActionClass))
typedef struct _ClutterDropAction ClutterDropAction;
typedef struct _ClutterDropActionPrivate ClutterDropActionPrivate;
typedef struct _ClutterDropActionClass ClutterDropActionClass;
/**
* ClutterDropAction:
*
* The #ClutterDropAction structure contains only
* private data and should be accessed using the provided API.
*
* Since: 1.8
*/
struct _ClutterDropAction
{
/*< private >*/
ClutterAction parent_instance;
ClutterDropActionPrivate *priv;
};
/**
* ClutterDropActionClass:
* @can_drop: class handler for the #ClutterDropAction::can-drop signal
* @over_in: class handler for the #ClutterDropAction::over-in signal
* @over_out: class handler for the #ClutterDropAction::over-out signal
* @drop: class handler for the #ClutterDropAction::drop signal
*
* The #ClutterDropActionClass structure contains
* only private data.
*
* Since: 1.8
*/
struct _ClutterDropActionClass
{
/*< private >*/
ClutterActionClass parent_class;
/*< public >*/
gboolean (* can_drop) (ClutterDropAction *action,
ClutterActor *actor,
gfloat event_x,
gfloat event_y);
void (* over_in) (ClutterDropAction *action,
ClutterActor *actor);
void (* over_out) (ClutterDropAction *action,
ClutterActor *actor);
void (* drop) (ClutterDropAction *action,
ClutterActor *actor,
gfloat event_x,
gfloat event_y);
/*< private >*/
void (*_clutter_drop_action1) (void);
void (*_clutter_drop_action2) (void);
void (*_clutter_drop_action3) (void);
void (*_clutter_drop_action4) (void);
void (*_clutter_drop_action5) (void);
void (*_clutter_drop_action6) (void);
void (*_clutter_drop_action7) (void);
void (*_clutter_drop_action8) (void);
};
CLUTTER_EXPORT
GType clutter_drop_action_get_type (void) G_GNUC_CONST;
CLUTTER_EXPORT
ClutterAction * clutter_drop_action_new (void);
G_END_DECLS
#endif /* __CLUTTER_DROP_ACTION_H__ */

View File

@ -230,28 +230,26 @@ clutter_effect_real_pick (ClutterEffect *effect,
}
static void
clutter_effect_notify (GObject *gobject,
GParamSpec *pspec)
clutter_effect_set_enabled (ClutterActorMeta *meta,
gboolean is_enabled)
{
if (strcmp (pspec->name, "enabled") == 0)
{
ClutterActorMeta *meta = CLUTTER_ACTOR_META (gobject);
ClutterActor *actor = clutter_actor_meta_get_actor (meta);
ClutterActorMetaClass *parent_class =
CLUTTER_ACTOR_META_CLASS (clutter_effect_parent_class);
ClutterActor *actor;
if (actor != NULL)
clutter_actor_queue_redraw (actor);
}
actor = clutter_actor_meta_get_actor (meta);
if (actor)
clutter_actor_queue_redraw (actor);
if (G_OBJECT_CLASS (clutter_effect_parent_class)->notify != NULL)
G_OBJECT_CLASS (clutter_effect_parent_class)->notify (gobject, pspec);
parent_class->set_enabled (meta, is_enabled);
}
static void
clutter_effect_class_init (ClutterEffectClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
ClutterActorMetaClass *actor_meta_class = CLUTTER_ACTOR_META_CLASS (klass);
gobject_class->notify = clutter_effect_notify;
actor_meta_class->set_enabled = clutter_effect_set_enabled;
klass->pre_paint = clutter_effect_real_pre_paint;
klass->post_paint = clutter_effect_real_post_paint;

View File

@ -538,6 +538,10 @@ typedef enum /*< prefix=CLUTTER_ACTOR >*/
* virtual returns %TRUE.
* @CLUTTER_OFFSCREEN_REDIRECT_ALWAYS: Always redirect the actor to an
* offscreen buffer even if it is fully opaque.
* @CLUTTER_OFFSCREEN_REDIRECT_ON_IDLE: Only redirect the actor if it is the
* most efficient thing to do based on its recent repaint behaviour. That
* means when its contents are changing less frequently than it's being used
* on stage.
*
* Possible flags to pass to clutter_actor_set_offscreen_redirect().
*
@ -545,8 +549,9 @@ typedef enum /*< prefix=CLUTTER_ACTOR >*/
*/
typedef enum /*< prefix=CLUTTER_OFFSCREEN_REDIRECT >*/
{
CLUTTER_OFFSCREEN_REDIRECT_AUTOMATIC_FOR_OPACITY = 1<<0,
CLUTTER_OFFSCREEN_REDIRECT_ALWAYS = 1<<1
CLUTTER_OFFSCREEN_REDIRECT_AUTOMATIC_FOR_OPACITY = 1 << 0,
CLUTTER_OFFSCREEN_REDIRECT_ALWAYS = 1 << 1,
CLUTTER_OFFSCREEN_REDIRECT_ON_IDLE = 1 << 2
} ClutterOffscreenRedirect;
/**
@ -673,12 +678,15 @@ typedef enum /*< prefix=CLUTTER_BIND >*/
* has queued a redraw before this paint. This implies that the effect
* should call clutter_actor_continue_paint() to chain to the next
* effect and can not cache any results from a previous paint.
* @CLUTTER_EFFECT_PAINT_BYPASS_EFFECT: The effect should not be used
* on this frame, but it will be asked to paint the actor still.
*
* Flags passed to the paint or pick method of #ClutterEffect.
*/
typedef enum /*< prefix=CLUTTER_EFFECT_PAINT >*/
{
CLUTTER_EFFECT_PAINT_ACTOR_DIRTY = (1 << 0)
CLUTTER_EFFECT_PAINT_ACTOR_DIRTY = (1 << 0),
CLUTTER_EFFECT_PAINT_BYPASS_EFFECT = (1 << 1)
} ClutterEffectPaintFlags;
/**

View File

@ -556,6 +556,21 @@ clutter_gesture_action_set_actor (ClutterActorMeta *meta,
meta_class->set_actor (meta, actor);
}
static void
clutter_gesture_action_set_enabled (ClutterActorMeta *meta,
gboolean is_enabled)
{
ClutterActorMetaClass *meta_class =
CLUTTER_ACTOR_META_CLASS (clutter_gesture_action_parent_class);
ClutterGestureAction *gesture_action = CLUTTER_GESTURE_ACTION (meta);
ClutterGestureActionPrivate *priv = gesture_action->priv;
if (!is_enabled && priv->in_gesture)
cancel_gesture (gesture_action);
meta_class->set_enabled (meta, is_enabled);
}
static gboolean
default_event_handler (ClutterGestureAction *action,
ClutterActor *actor)
@ -654,6 +669,7 @@ clutter_gesture_action_class_init (ClutterGestureActionClass *klass)
gobject_class->get_property = clutter_gesture_action_get_property;
meta_class->set_actor = clutter_gesture_action_set_actor;
meta_class->set_enabled = clutter_gesture_action_set_enabled;
klass->gesture_begin = default_event_handler;
klass->gesture_progress = default_event_handler;

View File

@ -130,7 +130,7 @@ clutter_image_paint_content (ClutterContent *content,
return;
node = clutter_actor_create_texture_paint_node (actor, priv->texture);
clutter_paint_node_set_name (node, "Image Content");
clutter_paint_node_set_static_name (node, "Image Content");
clutter_paint_node_add_child (root, node);
clutter_paint_node_unref (node);
}

View File

@ -29,7 +29,7 @@ void clutter_input_focus_focus_out (ClutterInputFocus *focus);
void clutter_input_focus_commit (ClutterInputFocus *focus,
const gchar *text);
void clutter_input_focus_delete_surrounding (ClutterInputFocus *focus,
guint offset,
int offset,
guint len);
void clutter_input_focus_request_surrounding (ClutterInputFocus *focus);

View File

@ -217,7 +217,7 @@ clutter_input_focus_commit (ClutterInputFocus *focus,
void
clutter_input_focus_delete_surrounding (ClutterInputFocus *focus,
guint offset,
int offset,
guint len)
{
g_return_if_fail (CLUTTER_IS_INPUT_FOCUS (focus));

View File

@ -41,7 +41,7 @@ struct _ClutterInputFocusClass
void (* request_surrounding) (ClutterInputFocus *focus);
void (* delete_surrounding) (ClutterInputFocus *focus,
guint offset,
int offset,
guint len);
void (* commit_text) (ClutterInputFocus *focus,
const gchar *text);

View File

@ -168,7 +168,7 @@ clutter_input_method_class_init (ClutterInputMethodClass *klass)
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
0, NULL, NULL, NULL,
G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_UINT);
signals[REQUEST_SURROUNDING] =
g_signal_new ("request-surrounding",
G_TYPE_FROM_CLASS (object_class),
@ -292,7 +292,7 @@ clutter_input_method_commit (ClutterInputMethod *im,
void
clutter_input_method_delete_surrounding (ClutterInputMethod *im,
guint offset,
int offset,
guint len)
{
ClutterInputMethodPrivate *priv;

View File

@ -68,7 +68,7 @@ void clutter_input_method_commit (ClutterInputMethod *im,
const gchar *text);
CLUTTER_EXPORT
void clutter_input_method_delete_surrounding (ClutterInputMethod *im,
guint offset,
int offset,
guint len);
CLUTTER_EXPORT
void clutter_input_method_request_surrounding (ClutterInputMethod *im);

View File

@ -63,6 +63,7 @@
#include "clutter-main.h"
#include "clutter-master-clock.h"
#include "clutter-mutter.h"
#include "clutter-paint-node-private.h"
#include "clutter-private.h"
#include "clutter-settings-private.h"
#include "clutter-stage-manager.h"
@ -970,6 +971,9 @@ clutter_init_real (GError **error)
if (clutter_enable_accessibility)
cally_accessibility_init ();
/* Initialize types required for paint nodes */
_clutter_paint_node_init_types ();
return CLUTTER_INIT_SUCCESS;
}

View File

@ -31,6 +31,8 @@
#include "clutter-build-config.h"
#include <cogl/cogl.h>
#include "clutter-master-clock.h"
#include "clutter-master-clock-default.h"
#include "clutter-debug.h"
@ -461,6 +463,8 @@ clutter_clock_dispatch (GSource *source,
_clutter_threads_acquire_lock ();
COGL_TRACE_BEGIN (ClutterMasterClockTick, "Master Clock (tick)");
/* Get the time to use for this frame */
master_clock->cur_tick = g_source_get_time (source);
@ -492,6 +496,8 @@ clutter_clock_dispatch (GSource *source,
g_slist_free_full (stages, g_object_unref);
COGL_TRACE_END (ClutterMasterClockTick);
_clutter_threads_release_lock ();
return TRUE;

View File

@ -36,6 +36,9 @@
#include "cogl/clutter-stage-cogl.h"
#include "clutter/x11/clutter-backend-x11.h"
CLUTTER_EXPORT
GList * clutter_stage_peek_stage_views (ClutterStage *stage);
CLUTTER_EXPORT
void clutter_set_custom_backend_func (ClutterBackend *(* func) (void));
@ -48,6 +51,23 @@ void clutter_stage_capture_into (ClutterStage *stage,
cairo_rectangle_int_t *rect,
uint8_t *data);
CLUTTER_EXPORT
void clutter_stage_paint_to_framebuffer (ClutterStage *stage,
CoglFramebuffer *framebuffer,
const cairo_rectangle_int_t *rect,
float scale,
ClutterPaintFlag paint_flags);
CLUTTER_EXPORT
gboolean clutter_stage_paint_to_buffer (ClutterStage *stage,
const cairo_rectangle_int_t *rect,
float scale,
uint8_t *data,
int stride,
CoglPixelFormat format,
ClutterPaintFlag paint_flags,
GError **error);
CLUTTER_EXPORT
void clutter_stage_freeze_updates (ClutterStage *stage);
@ -57,9 +77,16 @@ void clutter_stage_thaw_updates (ClutterStage *stage);
CLUTTER_EXPORT
void clutter_stage_update_resource_scales (ClutterStage *stage);
CLUTTER_EXPORT
void clutter_stage_view_assign_next_scanout (ClutterStageView *stage_view,
CoglScanout *scanout);
CLUTTER_EXPORT
gboolean clutter_actor_has_damage (ClutterActor *actor);
CLUTTER_EXPORT
gboolean clutter_actor_has_transitions (ClutterActor *actor);
#undef __CLUTTER_H_INSIDE__
#endif /* __CLUTTER_MUTTER_H__ */

View File

@ -81,7 +81,7 @@
struct _ClutterOffscreenEffectPrivate
{
CoglHandle offscreen;
CoglPipeline *target;
CoglPipeline *pipeline;
CoglHandle texture;
ClutterActor *actor;
@ -140,7 +140,7 @@ ensure_pipeline_filter_for_scale (ClutterOffscreenEffect *self,
{
CoglPipelineFilter filter;
if (!self->priv->target)
if (!self->priv->pipeline)
return;
/* If no fractional scaling is set, we're always going to render the texture
@ -154,7 +154,7 @@ ensure_pipeline_filter_for_scale (ClutterOffscreenEffect *self,
else
filter = COGL_PIPELINE_FILTER_LINEAR;
cogl_pipeline_set_layer_filters (self->priv->target, 0 /* layer_index */,
cogl_pipeline_set_layer_filters (self->priv->pipeline, 0 /* layer_index */,
filter, filter);
}
@ -185,12 +185,12 @@ update_fbo (ClutterEffect *effect,
return TRUE;
}
if (priv->target == NULL)
if (priv->pipeline == NULL)
{
CoglContext *ctx =
clutter_backend_get_cogl_context (clutter_get_default_backend ());
priv->target = cogl_pipeline_new (ctx);
priv->pipeline = cogl_pipeline_new (ctx);
ensure_pipeline_filter_for_scale (self, resource_scale);
}
@ -202,7 +202,7 @@ update_fbo (ClutterEffect *effect,
if (priv->texture == NULL)
return FALSE;
cogl_pipeline_set_layer_texture (priv->target, 0, priv->texture);
cogl_pipeline_set_layer_texture (priv->pipeline, 0, priv->texture);
priv->target_width = target_width;
priv->target_height = target_height;
@ -212,8 +212,8 @@ update_fbo (ClutterEffect *effect,
{
g_warning ("%s: Unable to create an Offscreen buffer", G_STRLOC);
cogl_object_unref (priv->target);
priv->target = NULL;
cogl_object_unref (priv->pipeline);
priv->pipeline = NULL;
priv->target_width = 0;
priv->target_height = 0;
@ -380,7 +380,7 @@ clutter_offscreen_effect_real_paint_target (ClutterOffscreenEffect *effect,
paint_opacity = clutter_actor_get_paint_opacity (priv->actor);
cogl_pipeline_set_color4ub (priv->target,
cogl_pipeline_set_color4ub (priv->pipeline,
paint_opacity,
paint_opacity,
paint_opacity,
@ -392,7 +392,7 @@ clutter_offscreen_effect_real_paint_target (ClutterOffscreenEffect *effect,
* hadn't been redirected offscreen.
*/
cogl_framebuffer_draw_textured_rectangle (framebuffer,
priv->target,
priv->pipeline,
0, 0,
cogl_texture_get_width (priv->texture),
cogl_texture_get_height (priv->texture),
@ -446,13 +446,16 @@ clutter_offscreen_effect_post_paint (ClutterEffect *effect,
ClutterOffscreenEffectPrivate *priv = self->priv;
CoglFramebuffer *framebuffer;
if (priv->offscreen == NULL ||
priv->target == NULL ||
priv->actor == NULL)
return;
g_warn_if_fail (priv->offscreen);
g_warn_if_fail (priv->pipeline);
g_warn_if_fail (priv->actor);
/* Restore the previous opacity override */
clutter_actor_set_opacity_override (priv->actor, priv->old_opacity_override);
if (priv->actor)
{
clutter_actor_set_opacity_override (priv->actor,
priv->old_opacity_override);
}
framebuffer = clutter_paint_context_get_framebuffer (paint_context);
cogl_framebuffer_pop_matrix (framebuffer);
@ -469,6 +472,13 @@ clutter_offscreen_effect_paint (ClutterEffect *effect,
ClutterOffscreenEffect *self = CLUTTER_OFFSCREEN_EFFECT (effect);
ClutterOffscreenEffectPrivate *priv = self->priv;
if (flags & CLUTTER_EFFECT_PAINT_BYPASS_EFFECT)
{
clutter_actor_continue_paint (priv->actor, paint_context);
cogl_clear_object (&priv->offscreen);
return;
}
/* If we've already got a cached image and the actor hasn't been redrawn
* then we can just use the cached image in the FBO.
*/
@ -491,16 +501,17 @@ clutter_offscreen_effect_paint (ClutterEffect *effect,
}
static void
clutter_offscreen_effect_notify (GObject *gobject,
GParamSpec *pspec)
clutter_offscreen_effect_set_enabled (ClutterActorMeta *meta,
gboolean is_enabled)
{
ClutterOffscreenEffect *offscreen_effect = CLUTTER_OFFSCREEN_EFFECT (gobject);
ClutterActorMetaClass *parent_class =
CLUTTER_ACTOR_META_CLASS (clutter_offscreen_effect_parent_class);
ClutterOffscreenEffect *offscreen_effect = CLUTTER_OFFSCREEN_EFFECT (meta);
ClutterOffscreenEffectPrivate *priv = offscreen_effect->priv;
if (strcmp (pspec->name, "enabled") == 0)
g_clear_pointer (&priv->offscreen, cogl_object_unref);
g_clear_pointer (&priv->offscreen, cogl_object_unref);
G_OBJECT_CLASS (clutter_offscreen_effect_parent_class)->notify (gobject, pspec);
parent_class->set_enabled (meta, is_enabled);
}
static void
@ -511,7 +522,7 @@ clutter_offscreen_effect_finalize (GObject *gobject)
g_clear_pointer (&priv->offscreen, cogl_object_unref);
g_clear_pointer (&priv->texture, cogl_object_unref);
g_clear_pointer (&priv->target, cogl_object_unref);
g_clear_pointer (&priv->pipeline, cogl_object_unref);
G_OBJECT_CLASS (clutter_offscreen_effect_parent_class)->finalize (gobject);
}
@ -527,13 +538,13 @@ clutter_offscreen_effect_class_init (ClutterOffscreenEffectClass *klass)
klass->paint_target = clutter_offscreen_effect_real_paint_target;
meta_class->set_actor = clutter_offscreen_effect_set_actor;
meta_class->set_enabled = clutter_offscreen_effect_set_enabled;
effect_class->pre_paint = clutter_offscreen_effect_pre_paint;
effect_class->post_paint = clutter_offscreen_effect_post_paint;
effect_class->paint = clutter_offscreen_effect_paint;
gobject_class->finalize = clutter_offscreen_effect_finalize;
gobject_class->notify = clutter_offscreen_effect_notify;
}
static void
@ -593,7 +604,7 @@ clutter_offscreen_effect_get_target (ClutterOffscreenEffect *effect)
g_return_val_if_fail (CLUTTER_IS_OFFSCREEN_EFFECT (effect),
NULL);
return (CoglMaterial *)effect->priv->target;
return (CoglMaterial *)effect->priv->pipeline;
}
/**

View File

@ -20,7 +20,9 @@
#include "clutter-paint-context.h"
ClutterPaintContext * clutter_paint_context_new_for_view (ClutterStageView *view);
ClutterPaintContext * clutter_paint_context_new_for_view (ClutterStageView *view,
const cairo_region_t *redraw_clip,
ClutterPaintFlag paint_flags);
gboolean clutter_paint_context_is_drawing_off_stage (ClutterPaintContext *paint_context);

View File

@ -23,9 +23,13 @@ struct _ClutterPaintContext
{
grefcount ref_count;
ClutterPaintFlag paint_flags;
GList *framebuffers;
ClutterStageView *view;
cairo_region_t *redraw_clip;
};
G_DEFINE_BOXED_TYPE (ClutterPaintContext, clutter_paint_context,
@ -33,7 +37,9 @@ G_DEFINE_BOXED_TYPE (ClutterPaintContext, clutter_paint_context,
clutter_paint_context_unref)
ClutterPaintContext *
clutter_paint_context_new_for_view (ClutterStageView *view)
clutter_paint_context_new_for_view (ClutterStageView *view,
const cairo_region_t *redraw_clip,
ClutterPaintFlag paint_flags)
{
ClutterPaintContext *paint_context;
CoglFramebuffer *framebuffer;
@ -41,6 +47,8 @@ clutter_paint_context_new_for_view (ClutterStageView *view)
paint_context = g_new0 (ClutterPaintContext, 1);
g_ref_count_init (&paint_context->ref_count);
paint_context->view = view;
paint_context->redraw_clip = cairo_region_copy (redraw_clip);
paint_context->paint_flags = paint_flags;
framebuffer = clutter_stage_view_get_framebuffer (view);
clutter_paint_context_push_framebuffer (paint_context, framebuffer);
@ -52,12 +60,16 @@ clutter_paint_context_new_for_view (ClutterStageView *view)
* clutter_paint_context_new_for_framebuffer: (skip)
*/
ClutterPaintContext *
clutter_paint_context_new_for_framebuffer (CoglFramebuffer *framebuffer)
clutter_paint_context_new_for_framebuffer (CoglFramebuffer *framebuffer,
const cairo_region_t *redraw_clip,
ClutterPaintFlag paint_flags)
{
ClutterPaintContext *paint_context;
paint_context = g_new0 (ClutterPaintContext, 1);
g_ref_count_init (&paint_context->ref_count);
paint_context->redraw_clip = cairo_region_copy (redraw_clip);
paint_context->paint_flags = paint_flags;
clutter_paint_context_push_framebuffer (paint_context, framebuffer);
@ -77,6 +89,7 @@ clutter_paint_context_dispose (ClutterPaintContext *paint_context)
g_list_free_full (paint_context->framebuffers,
cogl_object_unref);
paint_context->framebuffers = NULL;
g_clear_pointer (&paint_context->redraw_clip, cairo_region_destroy);
}
void
@ -115,6 +128,12 @@ clutter_paint_context_pop_framebuffer (ClutterPaintContext *paint_context)
paint_context->framebuffers);
}
const cairo_region_t *
clutter_paint_context_get_redraw_clip (ClutterPaintContext *paint_context)
{
return paint_context->redraw_clip;
}
/**
* clutter_paint_context_get_framebuffer:
* @paint_context: The #ClutterPaintContext
@ -159,3 +178,12 @@ clutter_paint_context_is_drawing_off_stage (ClutterPaintContext *paint_context)
return !paint_context->view;
}
/**
* clutter_paint_context_get_paint_flags: (skip)
*/
ClutterPaintFlag
clutter_paint_context_get_paint_flags (ClutterPaintContext *paint_context)
{
return paint_context->paint_flags;
}

View File

@ -29,13 +29,21 @@
typedef struct _ClutterPaintContext ClutterPaintContext;
typedef enum _ClutterPaintFlag
{
CLUTTER_PAINT_FLAG_NONE = 0,
CLUTTER_PAINT_FLAG_NO_CURSORS = 1 << 0,
} ClutterPaintFlag;
#define CLUTTER_TYPE_PAINT_CONTEXT (clutter_paint_context_get_type ())
CLUTTER_EXPORT
GType clutter_paint_context_get_type (void);
CLUTTER_EXPORT
ClutterPaintContext * clutter_paint_context_new_for_framebuffer (CoglFramebuffer *framebuffer);
ClutterPaintContext * clutter_paint_context_new_for_framebuffer (CoglFramebuffer *framebuffer,
const cairo_region_t *redraw_clip,
ClutterPaintFlag paint_flags);
CLUTTER_EXPORT
ClutterPaintContext * clutter_paint_context_ref (ClutterPaintContext *paint_context);
@ -59,4 +67,10 @@ void clutter_paint_context_push_framebuffer (ClutterPaintContext *paint_context,
CLUTTER_EXPORT
void clutter_paint_context_pop_framebuffer (ClutterPaintContext *paint_context);
CLUTTER_EXPORT
const cairo_region_t * clutter_paint_context_get_redraw_clip (ClutterPaintContext *paint_context);
CLUTTER_EXPORT
ClutterPaintFlag clutter_paint_context_get_paint_flags (ClutterPaintContext *paint_context);
#endif /* CLUTTER_PAINT_CONTEXT_H */

View File

@ -49,11 +49,11 @@ struct _ClutterPaintNode
ClutterPaintNode *next_sibling;
ClutterPaintNode *last_child;
guint n_children;
GArray *operations;
gchar *name;
const gchar *name;
guint n_children;
volatile int ref_count;
};
@ -83,7 +83,6 @@ typedef enum
PAINT_OP_INVALID = 0,
PAINT_OP_TEX_RECT,
PAINT_OP_MULTITEX_RECT,
PAINT_OP_PATH,
PAINT_OP_PRIMITIVE
} PaintOpCode;
@ -96,8 +95,6 @@ struct _ClutterPaintOperation
union {
float texrect[8];
CoglPath *path;
CoglPrimitive *primitive;
} op;
};

View File

@ -171,8 +171,6 @@ clutter_paint_node_real_finalize (ClutterPaintNode *node)
{
ClutterPaintNode *iter;
g_free (node->name);
if (node->operations != NULL)
{
guint i;
@ -297,7 +295,8 @@ clutter_paint_node_get_type (void)
*
* The @name will be used for debugging purposes.
*
* The @node will copy the passed string.
* The @node will intern @name using g_intern_string(). If you have access to a
* static string, use clutter_paint_node_set_static_name() instead.
*
* Since: 1.10
*/
@ -307,8 +306,22 @@ clutter_paint_node_set_name (ClutterPaintNode *node,
{
g_return_if_fail (CLUTTER_IS_PAINT_NODE (node));
g_free (node->name);
node->name = g_strdup (name);
node->name = g_intern_string (name);
}
/**
* clutter_paint_node_set_static_name: (skip)
*
* Like clutter_paint_node_set_name() but uses a static or interned string
* containing the name.
*/
void
clutter_paint_node_set_static_name (ClutterPaintNode *node,
const char *name)
{
g_return_if_fail (CLUTTER_IS_PAINT_NODE (node));
node->name = name;
}
/**
@ -769,11 +782,6 @@ clutter_paint_operation_clear (ClutterPaintOperation *op)
g_array_unref (op->multitex_coords);
break;
case PAINT_OP_PATH:
if (op->op.path != NULL)
cogl_object_unref (op->op.path);
break;
case PAINT_OP_PRIMITIVE:
if (op->op.primitive != NULL)
cogl_object_unref (op->op.primitive);
@ -823,16 +831,6 @@ clutter_paint_op_init_multitex_rect (ClutterPaintOperation *op,
op->op.texrect[3] = rect->y2;
}
static inline void
clutter_paint_op_init_path (ClutterPaintOperation *op,
CoglPath *path)
{
clutter_paint_operation_clear (op);
op->opcode = PAINT_OP_PATH;
op->op.path = cogl_object_ref (path);
}
static inline void
clutter_paint_op_init_primitive (ClutterPaintOperation *op,
CoglPrimitive *primitive)
@ -937,34 +935,6 @@ clutter_paint_node_add_multitexture_rectangle (ClutterPaintNode *node,
g_array_append_val (node->operations, operation);
}
/**
* clutter_paint_node_add_path: (skip)
* @node: a #ClutterPaintNode
* @path: a Cogl path
*
* Adds a region described as a path to the @node.
*
* This function acquires a reference on the passed @path, so it
* is safe to call cogl_object_unref() when it returns.
*
* Since: 1.10
* Stability: unstable
*/
void
clutter_paint_node_add_path (ClutterPaintNode *node,
CoglPath *path)
{
ClutterPaintOperation operation = PAINT_OP_INIT;
g_return_if_fail (CLUTTER_IS_PAINT_NODE (node));
g_return_if_fail (cogl_is_path (path));
clutter_paint_node_maybe_init_operations (node);
clutter_paint_op_init_path (&operation, path);
g_array_append_val (node->operations, operation);
}
/**
* clutter_paint_node_add_primitive: (skip)
* @node: a #ClutterPaintNode
@ -1101,11 +1071,6 @@ clutter_paint_node_to_json (ClutterPaintNode *node)
json_builder_end_array (builder);
break;
case PAINT_OP_PATH:
json_builder_set_member_name (builder, "path");
json_builder_add_int_value (builder, (intptr_t) op->op.path);
break;
case PAINT_OP_PRIMITIVE:
json_builder_set_member_name (builder, "primitive");
json_builder_add_int_value (builder, (intptr_t) op->op.primitive);
@ -1181,8 +1146,6 @@ _clutter_paint_node_create (GType gtype)
{
g_return_val_if_fail (g_type_is_a (gtype, CLUTTER_TYPE_PAINT_NODE), NULL);
_clutter_paint_node_init_types ();
return (gpointer) g_type_create_instance (gtype);
}

View File

@ -56,6 +56,9 @@ void clutter_paint_node_paint (Clutter
CLUTTER_EXPORT
void clutter_paint_node_set_name (ClutterPaintNode *node,
const char *name);
CLUTTER_EXPORT
void clutter_paint_node_set_static_name (ClutterPaintNode *node,
const char *name);
CLUTTER_EXPORT
CoglFramebuffer * clutter_paint_node_get_framebuffer (ClutterPaintNode *node);
@ -81,9 +84,6 @@ void clutter_paint_node_add_multitexture_rectangle (ClutterP
unsigned int text_coords_len);
CLUTTER_EXPORT
void clutter_paint_node_add_path (ClutterPaintNode *node,
CoglPath *path);
CLUTTER_EXPORT
void clutter_paint_node_add_primitive (ClutterPaintNode *node,
CoglPrimitive *primitive);

View File

@ -477,10 +477,6 @@ clutter_pipeline_node_draw (ClutterPaintNode *node,
op->multitex_coords->len);
break;
case PAINT_OP_PATH:
cogl_framebuffer_fill_path (fb, pnode->pipeline, op->op.path);
break;
case PAINT_OP_PRIMITIVE:
cogl_framebuffer_draw_primitive (fb,
pnode->pipeline,
@ -876,7 +872,6 @@ clutter_text_node_draw (ClutterPaintNode *node,
break;
case PAINT_OP_MULTITEX_RECT:
case PAINT_OP_PATH:
case PAINT_OP_PRIMITIVE:
case PAINT_OP_INVALID:
break;
@ -1037,11 +1032,6 @@ clutter_clip_node_pre_draw (ClutterPaintNode *node,
retval = TRUE;
break;
case PAINT_OP_PATH:
cogl_framebuffer_push_path_clip (fb, op->op.path);
retval = TRUE;
break;
case PAINT_OP_MULTITEX_RECT:
case PAINT_OP_PRIMITIVE:
case PAINT_OP_INVALID:
@ -1072,7 +1062,6 @@ clutter_clip_node_post_draw (ClutterPaintNode *node,
switch (op->opcode)
{
case PAINT_OP_PATH:
case PAINT_OP_TEX_RECT:
cogl_framebuffer_pop_clip (fb);
break;
@ -1242,9 +1231,8 @@ struct _ClutterLayerNode
float fbo_width;
float fbo_height;
CoglPipeline *state;
CoglPipeline *pipeline;
CoglFramebuffer *offscreen;
CoglTexture *texture;
guint8 opacity;
};
@ -1334,7 +1322,7 @@ clutter_layer_node_post_draw (ClutterPaintNode *node,
case PAINT_OP_TEX_RECT:
/* now we need to paint the texture */
cogl_framebuffer_draw_textured_rectangle (fb,
lnode->state,
lnode->pipeline,
op->op.texrect[0],
op->op.texrect[1],
op->op.texrect[2],
@ -1347,7 +1335,7 @@ clutter_layer_node_post_draw (ClutterPaintNode *node,
case PAINT_OP_MULTITEX_RECT:
cogl_framebuffer_draw_multitextured_rectangle (fb,
lnode->state,
lnode->pipeline,
op->op.texrect[0],
op->op.texrect[1],
op->op.texrect[2],
@ -1356,12 +1344,10 @@ clutter_layer_node_post_draw (ClutterPaintNode *node,
op->multitex_coords->len);
break;
case PAINT_OP_PATH:
cogl_framebuffer_fill_path (fb, lnode->state, op->op.path);
break;
case PAINT_OP_PRIMITIVE:
cogl_framebuffer_draw_primitive (fb, lnode->state, op->op.primitive);
cogl_framebuffer_draw_primitive (fb,
lnode->pipeline,
op->op.primitive);
break;
}
}
@ -1372,8 +1358,8 @@ clutter_layer_node_finalize (ClutterPaintNode *node)
{
ClutterLayerNode *lnode = CLUTTER_LAYER_NODE (node);
if (lnode->state != NULL)
cogl_object_unref (lnode->state);
if (lnode->pipeline != NULL)
cogl_object_unref (lnode->pipeline);
if (lnode->offscreen != NULL)
cogl_object_unref (lnode->offscreen);
@ -1425,6 +1411,8 @@ clutter_layer_node_new (const CoglMatrix *projection,
guint8 opacity)
{
ClutterLayerNode *res;
CoglContext *context;
CoglTexture *texture;
CoglColor color;
res = _clutter_paint_node_create (CLUTTER_TYPE_LAYER_NODE);
@ -1436,19 +1424,17 @@ clutter_layer_node_new (const CoglMatrix *projection,
res->opacity = opacity;
/* the texture backing the FBO */
res->texture = cogl_texture_new_with_size (MAX (res->fbo_width, 1),
MAX (res->fbo_height, 1),
COGL_TEXTURE_NO_SLICING,
COGL_PIXEL_FORMAT_RGBA_8888_PRE);
context = clutter_backend_get_cogl_context (clutter_get_default_backend ());
res->offscreen = COGL_FRAMEBUFFER (cogl_offscreen_new_to_texture (res->texture));
texture = cogl_texture_2d_new_with_size (context,
MAX (res->fbo_width, 1),
MAX (res->fbo_height, 1));
cogl_texture_set_premultiplied (texture, TRUE);
res->offscreen = COGL_FRAMEBUFFER (cogl_offscreen_new_to_texture (texture));
if (res->offscreen == NULL)
{
g_critical ("%s: Unable to create an offscreen buffer", G_STRLOC);
cogl_object_unref (res->texture);
res->texture = NULL;
goto out;
}
@ -1458,14 +1444,15 @@ clutter_layer_node_new (const CoglMatrix *projection,
* interpolation filters because the texture is always
* going to be painted at a 1:1 texel:pixel ratio
*/
res->state = cogl_pipeline_copy (default_texture_pipeline);
cogl_pipeline_set_layer_filters (res->state, 0,
res->pipeline = cogl_pipeline_copy (default_texture_pipeline);
cogl_pipeline_set_layer_filters (res->pipeline, 0,
COGL_PIPELINE_FILTER_NEAREST,
COGL_PIPELINE_FILTER_NEAREST);
cogl_pipeline_set_layer_texture (res->state, 0, res->texture);
cogl_pipeline_set_color (res->state, &color);
cogl_object_unref (res->texture);
cogl_pipeline_set_layer_texture (res->pipeline, 0, texture);
cogl_pipeline_set_color (res->pipeline, &color);
out:
cogl_object_unref (texture);
return (ClutterPaintNode *) res;
}

View File

@ -248,6 +248,9 @@ gboolean _clutter_util_rectangle_intersection (const cairo_rectangle_int_t *src1
const cairo_rectangle_int_t *src2,
cairo_rectangle_int_t *dest);
gboolean clutter_util_rectangle_equal (const cairo_rectangle_int_t *src1,
const cairo_rectangle_int_t *src2);
struct _ClutterVertex4
{

View File

@ -41,6 +41,7 @@ enum
PTR_A11Y_DWELL_CLICK_TYPE_CHANGED,
PTR_A11Y_TIMEOUT_STARTED,
PTR_A11Y_TIMEOUT_STOPPED,
IS_UNFOCUS_INHIBITED_CHANGED,
N_SIGNALS,
};
@ -62,6 +63,8 @@ struct _ClutterSeatPrivate
{
ClutterBackend *backend;
unsigned int inhibit_unfocus_count;
/* Keyboard a11y */
ClutterKbdA11ySettings kbd_a11y_settings;
@ -275,6 +278,22 @@ clutter_seat_class_init (ClutterSeatClass *klass)
G_TYPE_FROM_CLASS (object_class),
_clutter_marshal_VOID__OBJECT_FLAGS_BOOLEANv);
/**
* ClutterSeat::is-unfocus-inhibited-changed:
* @seat: the #ClutterSeat that emitted the signal
*
* The ::is-unfocus-inhibited-changed signal is emitted when the
* property to inhibit the unsetting of the focus-surface of the
* #ClutterSeat changed. To get the current state of this property,
* use clutter_seat_is_unfocus_inhibited().
*/
signals[IS_UNFOCUS_INHIBITED_CHANGED] =
g_signal_new (I_("is-unfocus-inhibited-changed"),
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0, NULL, NULL, NULL,
G_TYPE_NONE, 0);
props[PROP_BACKEND] =
g_param_spec_object ("backend",
P_("Backend"),
@ -282,6 +301,12 @@ clutter_seat_class_init (ClutterSeatClass *klass)
CLUTTER_TYPE_BACKEND,
CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
/**
* ClutterSeat:touch-mode:
*
* The current touch-mode of the #ClutterSeat, it is set to %TRUE if the
* requirements documented in clutter_seat_get_touch_mode() are fulfilled.
**/
props[PROP_TOUCH_MODE] =
g_param_spec_boolean ("touch-mode",
P_("Touch mode"),
@ -508,6 +533,85 @@ clutter_seat_set_pointer_a11y_dwell_click_type (ClutterSeat
priv->pointer_a11y_settings.dwell_click_type = click_type;
}
/**
* clutter_seat_inhibit_unfocus:
* @seat: a #ClutterSeat
*
* Inhibits unsetting of the pointer focus-surface for the #ClutterSeat @seat,
* this allows to keep using the pointer even when it's hidden.
*
* This property is refcounted, so clutter_seat_uninhibit_unfocus() must be
* called the exact same number of times as clutter_seat_inhibit_unfocus()
* was called before.
**/
void
clutter_seat_inhibit_unfocus (ClutterSeat *seat)
{
ClutterSeatPrivate *priv;
g_return_if_fail (CLUTTER_IS_SEAT (seat));
priv = clutter_seat_get_instance_private (seat);
priv->inhibit_unfocus_count++;
if (priv->inhibit_unfocus_count == 1)
g_signal_emit (G_OBJECT (seat), signals[IS_UNFOCUS_INHIBITED_CHANGED], 0);
}
/**
* clutter_seat_uninhibit_unfocus:
* @seat: a #ClutterSeat
*
* Disables the inhibiting of unsetting of the pointer focus-surface
* previously enabled by calling clutter_seat_inhibit_unfocus().
*
* This property is refcounted, so clutter_seat_uninhibit_unfocus() must be
* called the exact same number of times as clutter_seat_inhibit_unfocus()
* was called before.
**/
void
clutter_seat_uninhibit_unfocus (ClutterSeat *seat)
{
ClutterSeatPrivate *priv;
g_return_if_fail (CLUTTER_IS_SEAT (seat));
priv = clutter_seat_get_instance_private (seat);
if (priv->inhibit_unfocus_count == 0)
{
g_warning ("Called clutter_seat_uninhibit_unfocus without inhibiting before");
return;
}
priv->inhibit_unfocus_count--;
if (priv->inhibit_unfocus_count == 0)
g_signal_emit (G_OBJECT (seat), signals[IS_UNFOCUS_INHIBITED_CHANGED], 0);
}
/**
* clutter_seat_is_unfocus_inhibited:
* @seat: a #ClutterSeat
*
* Gets whether unsetting of the pointer focus-surface is inhibited
* for the #ClutterSeat @seat.
*
* Returns: %TRUE if unsetting is inhibited, %FALSE otherwise
**/
gboolean
clutter_seat_is_unfocus_inhibited (ClutterSeat *seat)
{
ClutterSeatPrivate *priv;
g_return_val_if_fail (CLUTTER_IS_SEAT (seat), FALSE);
priv = clutter_seat_get_instance_private (seat);
return priv->inhibit_unfocus_count > 0;
}
/**
* clutter_seat_create_virtual_device:
* @seat: a #ClutterSeat
@ -569,6 +673,21 @@ clutter_seat_warp_pointer (ClutterSeat *seat,
CLUTTER_SEAT_GET_CLASS (seat)->warp_pointer (seat, x, y);
}
/**
* clutter_seat_get_touch_mode:
* @seat: a #ClutterSeat
*
* Gets the current touch-mode state of the #ClutterSeat @seat.
* The #ClutterSeat:touch-mode property is set to %TRUE if the following
* requirements are fulfilled:
*
* - A touchscreen is available
* - No external keyboard is attached to the device
* - A tablet mode switch, if present, is enabled
*
* Returns: %TRUE if the device is a tablet that doesn't have an external
* keyboard attached, %FALSE otherwise.
**/
gboolean
clutter_seat_get_touch_mode (ClutterSeat *seat)
{

View File

@ -159,6 +159,16 @@ void clutter_seat_get_pointer_a11y_settings (ClutterSeat *seat,
CLUTTER_EXPORT
void clutter_seat_set_pointer_a11y_dwell_click_type (ClutterSeat *seat,
ClutterPointerA11yDwellClickType click_type);
CLUTTER_EXPORT
void clutter_seat_inhibit_unfocus (ClutterSeat *seat);
CLUTTER_EXPORT
void clutter_seat_uninhibit_unfocus (ClutterSeat *seat);
CLUTTER_EXPORT
gboolean clutter_seat_is_unfocus_inhibited (ClutterSeat *seat);
CLUTTER_EXPORT
ClutterVirtualInputDevice *clutter_seat_create_virtual_device (ClutterSeat *seat,
ClutterInputDeviceType device_type);

View File

@ -36,9 +36,9 @@ typedef struct _ClutterStageQueueRedrawEntry ClutterStageQueueRedrawEntry;
/* stage */
ClutterStageWindow *_clutter_stage_get_default_window (void);
void _clutter_stage_paint_view (ClutterStage *stage,
ClutterStageView *view,
const cairo_rectangle_int_t *clip);
void clutter_stage_paint_view (ClutterStage *stage,
ClutterStageView *view,
const cairo_region_t *redraw_clip);
void _clutter_stage_emit_after_paint (ClutterStage *stage);
@ -139,8 +139,6 @@ void _clutter_stage_presented (ClutterStage *stag
CoglFrameEvent frame_event,
ClutterFrameInfo *frame_info);
GList * _clutter_stage_peek_stage_views (ClutterStage *stage);
void clutter_stage_queue_actor_relayout (ClutterStage *stage,
ClutterActor *actor);

View File

@ -20,8 +20,7 @@
#include "clutter/clutter-stage-view.h"
void clutter_stage_view_after_paint (ClutterStageView *view,
const cairo_rectangle_int_t *clip);
void clutter_stage_view_after_paint (ClutterStageView *view);
gboolean clutter_stage_view_is_dirty_viewport (ClutterStageView *view);
@ -33,7 +32,17 @@ gboolean clutter_stage_view_is_dirty_projection (ClutterStageView *view);
void clutter_stage_view_set_dirty_projection (ClutterStageView *view,
gboolean dirty);
void clutter_stage_view_add_redraw_clip (ClutterStageView *view,
cairo_rectangle_int_t *clip);
void clutter_stage_view_add_redraw_clip (ClutterStageView *view,
const cairo_rectangle_int_t *clip);
gboolean clutter_stage_view_has_full_redraw_clip (ClutterStageView *view);
gboolean clutter_stage_view_has_redraw_clip (ClutterStageView *view);
const cairo_region_t * clutter_stage_view_peek_redraw_clip (ClutterStageView *view);
cairo_region_t * clutter_stage_view_take_redraw_clip (ClutterStageView *view);
CoglScanout * clutter_stage_view_take_scanout (ClutterStageView *view);
#endif /* __CLUTTER_STAGE_VIEW_PRIVATE_H__ */

View File

@ -23,6 +23,10 @@
#include <cairo-gobject.h>
#include <math.h>
#include "clutter/clutter-private.h"
#include "clutter/clutter-mutter.h"
#include "cogl/cogl.h"
enum
{
PROP_0,
@ -50,6 +54,11 @@ typedef struct _ClutterStageViewPrivate
CoglOffscreen *shadowfb;
CoglPipeline *shadowfb_pipeline;
CoglScanout *next_scanout;
gboolean has_redraw_clip;
cairo_region_t *redraw_clip;
guint dirty_viewport : 1;
guint dirty_projection : 1;
} ClutterStageViewPrivate;
@ -166,12 +175,11 @@ clutter_stage_view_invalidate_offscreen_blit_pipeline (ClutterStageView *view)
}
static void
clutter_stage_view_copy_to_framebuffer (ClutterStageView *view,
const cairo_rectangle_int_t *rect,
CoglPipeline *pipeline,
CoglFramebuffer *src_framebuffer,
CoglFramebuffer *dst_framebuffer,
gboolean can_blit)
clutter_stage_view_copy_to_framebuffer (ClutterStageView *view,
CoglPipeline *pipeline,
CoglFramebuffer *src_framebuffer,
CoglFramebuffer *dst_framebuffer,
gboolean can_blit)
{
CoglMatrix matrix;
@ -204,8 +212,7 @@ clutter_stage_view_copy_to_framebuffer (ClutterStageView *view,
}
void
clutter_stage_view_after_paint (ClutterStageView *view,
const cairo_rectangle_int_t *rect)
clutter_stage_view_after_paint (ClutterStageView *view)
{
ClutterStageViewPrivate *priv =
clutter_stage_view_get_instance_private (view);
@ -222,7 +229,6 @@ clutter_stage_view_after_paint (ClutterStageView *view,
if (priv->shadowfb)
{
clutter_stage_view_copy_to_framebuffer (view,
rect,
priv->offscreen_pipeline,
priv->offscreen,
priv->shadowfb,
@ -231,7 +237,6 @@ clutter_stage_view_after_paint (ClutterStageView *view,
else
{
clutter_stage_view_copy_to_framebuffer (view,
rect,
priv->offscreen_pipeline,
priv->offscreen,
priv->framebuffer,
@ -243,7 +248,6 @@ clutter_stage_view_after_paint (ClutterStageView *view,
{
clutter_stage_view_ensure_shadowfb_blit_pipeline (view);
clutter_stage_view_copy_to_framebuffer (view,
rect,
priv->shadowfb_pipeline,
priv->shadowfb,
priv->framebuffer,
@ -307,6 +311,86 @@ clutter_stage_view_get_offscreen_transformation_matrix (ClutterStageView *view,
view_class->get_offscreen_transformation_matrix (view, matrix);
}
void
clutter_stage_view_add_redraw_clip (ClutterStageView *view,
const cairo_rectangle_int_t *clip)
{
ClutterStageViewPrivate *priv =
clutter_stage_view_get_instance_private (view);
if (priv->has_redraw_clip && !priv->redraw_clip)
return;
if (!clip)
{
g_clear_pointer (&priv->redraw_clip, cairo_region_destroy);
priv->has_redraw_clip = TRUE;
return;
}
if (clip->width == 0 || clip->height == 0)
return;
if (!priv->redraw_clip)
{
if (!clutter_util_rectangle_equal (&priv->layout, clip))
priv->redraw_clip = cairo_region_create_rectangle (clip);
}
else
{
cairo_region_union_rectangle (priv->redraw_clip, clip);
if (cairo_region_num_rectangles (priv->redraw_clip) == 1)
{
cairo_rectangle_int_t redraw_clip_extents;
cairo_region_get_extents (priv->redraw_clip, &redraw_clip_extents);
if (clutter_util_rectangle_equal (&priv->layout, &redraw_clip_extents))
g_clear_pointer (&priv->redraw_clip, cairo_region_destroy);
}
}
priv->has_redraw_clip = TRUE;
}
gboolean
clutter_stage_view_has_redraw_clip (ClutterStageView *view)
{
ClutterStageViewPrivate *priv =
clutter_stage_view_get_instance_private (view);
return priv->has_redraw_clip;
}
gboolean
clutter_stage_view_has_full_redraw_clip (ClutterStageView *view)
{
ClutterStageViewPrivate *priv =
clutter_stage_view_get_instance_private (view);
return priv->has_redraw_clip && !priv->redraw_clip;
}
const cairo_region_t *
clutter_stage_view_peek_redraw_clip (ClutterStageView *view)
{
ClutterStageViewPrivate *priv =
clutter_stage_view_get_instance_private (view);
return priv->redraw_clip;
}
cairo_region_t *
clutter_stage_view_take_redraw_clip (ClutterStageView *view)
{
ClutterStageViewPrivate *priv =
clutter_stage_view_get_instance_private (view);
priv->has_redraw_clip = FALSE;
return g_steal_pointer (&priv->redraw_clip);
}
void
clutter_stage_view_transform_to_onscreen (ClutterStageView *view,
gfloat *x,
@ -327,6 +411,25 @@ clutter_stage_default_get_offscreen_transformation_matrix (ClutterStageView *vie
cogl_matrix_init_identity (matrix);
}
void
clutter_stage_view_assign_next_scanout (ClutterStageView *view,
CoglScanout *scanout)
{
ClutterStageViewPrivate *priv =
clutter_stage_view_get_instance_private (view);
g_set_object (&priv->next_scanout, scanout);
}
CoglScanout *
clutter_stage_view_take_scanout (ClutterStageView *view)
{
ClutterStageViewPrivate *priv =
clutter_stage_view_get_instance_private (view);
return g_steal_pointer (&priv->next_scanout);
}
static void
clutter_stage_view_get_property (GObject *object,
guint prop_id,
@ -419,6 +522,7 @@ clutter_stage_view_dispose (GObject *object)
g_clear_pointer (&priv->offscreen, cogl_object_unref);
g_clear_pointer (&priv->offscreen_pipeline, cogl_object_unref);
g_clear_pointer (&priv->shadowfb_pipeline, cogl_object_unref);
g_clear_pointer (&priv->redraw_clip, cairo_region_destroy);
G_OBJECT_CLASS (clutter_stage_view_parent_class)->dispose (object);
}

View File

@ -62,16 +62,6 @@ _clutter_stage_window_set_title (ClutterStageWindow *window,
iface->set_title (window, title);
}
void
_clutter_stage_window_set_cursor_visible (ClutterStageWindow *window,
gboolean is_visible)
{
ClutterStageWindowInterface *iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window);
if (iface->set_cursor_visible)
iface->set_cursor_visible (window, is_visible);
}
gboolean
_clutter_stage_window_realize (ClutterStageWindow *window)
{
@ -178,90 +168,6 @@ _clutter_stage_window_clear_update_time (ClutterStageWindow *window)
iface->clear_update_time (window);
}
void
_clutter_stage_window_add_redraw_clip (ClutterStageWindow *window,
cairo_rectangle_int_t *stage_clip)
{
ClutterStageWindowInterface *iface;
g_return_if_fail (CLUTTER_IS_STAGE_WINDOW (window));
iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window);
if (iface->add_redraw_clip != NULL)
iface->add_redraw_clip (window, stage_clip);
}
/* Determines if the backend will clip the rendering of the next
* frame.
*
* Note: at the start of each new frame there is an implied clip that
* clips everything (i.e. nothing would be drawn) so this function
* will return True at the start of a new frame if the backend
* supports clipped redraws.
*/
gboolean
_clutter_stage_window_has_redraw_clips (ClutterStageWindow *window)
{
ClutterStageWindowInterface *iface;
g_return_val_if_fail (CLUTTER_IS_STAGE_WINDOW (window), FALSE);
iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window);
if (iface->has_redraw_clips != NULL)
return iface->has_redraw_clips (window);
return FALSE;
}
/* Determines if the backend will discard any additional redraw clips
* and instead promote them to a full stage redraw.
*
* The ideas is that backend may have some heuristics that cause it to
* give up tracking redraw clips so this can be used to avoid the cost
* of calculating a redraw clip when we know it's going to be ignored
* anyway.
*/
gboolean
_clutter_stage_window_ignoring_redraw_clips (ClutterStageWindow *window)
{
ClutterStageWindowInterface *iface;
g_return_val_if_fail (CLUTTER_IS_STAGE_WINDOW (window), FALSE);
iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window);
if (iface->ignoring_redraw_clips != NULL)
return iface->ignoring_redraw_clips (window);
return TRUE;
}
cairo_region_t *
_clutter_stage_window_get_redraw_clip (ClutterStageWindow *window)
{
ClutterStageWindowInterface *iface;
g_return_val_if_fail (CLUTTER_IS_STAGE_WINDOW (window), FALSE);
iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window);
if (iface->get_redraw_clip != NULL)
return iface->get_redraw_clip (window);
return NULL;
}
void
_clutter_stage_window_set_accept_focus (ClutterStageWindow *window,
gboolean accept_focus)
{
ClutterStageWindowInterface *iface;
g_return_if_fail (CLUTTER_IS_STAGE_WINDOW (window));
iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window);
if (iface->set_accept_focus)
iface->set_accept_focus (window, accept_focus);
}
void
_clutter_stage_window_redraw (ClutterStageWindow *window)
{

View File

@ -30,8 +30,6 @@ struct _ClutterStageWindowInterface
void (* set_title) (ClutterStageWindow *stage_window,
const gchar *title);
void (* set_cursor_visible) (ClutterStageWindow *stage_window,
gboolean cursor_visible);
gboolean (* realize) (ClutterStageWindow *stage_window);
void (* unrealize) (ClutterStageWindow *stage_window);
@ -51,15 +49,6 @@ struct _ClutterStageWindowInterface
gint64 (* get_update_time) (ClutterStageWindow *stage_window);
void (* clear_update_time) (ClutterStageWindow *stage_window);
void (* add_redraw_clip) (ClutterStageWindow *stage_window,
cairo_rectangle_int_t *stage_rectangle);
gboolean (* has_redraw_clips) (ClutterStageWindow *stage_window);
gboolean (* ignoring_redraw_clips) (ClutterStageWindow *stage_window);
cairo_region_t * (* get_redraw_clip) (ClutterStageWindow *stage_window);
void (* set_accept_focus) (ClutterStageWindow *stage_window,
gboolean accept_focus);
void (* redraw) (ClutterStageWindow *stage_window);
gboolean (* can_clip_redraws) (ClutterStageWindow *stage_window);
@ -94,12 +83,6 @@ void _clutter_stage_window_schedule_update (ClutterStageWin
gint64 _clutter_stage_window_get_update_time (ClutterStageWindow *window);
void _clutter_stage_window_clear_update_time (ClutterStageWindow *window);
void _clutter_stage_window_add_redraw_clip (ClutterStageWindow *window,
cairo_rectangle_int_t *stage_clip);
gboolean _clutter_stage_window_has_redraw_clips (ClutterStageWindow *window);
gboolean _clutter_stage_window_ignoring_redraw_clips (ClutterStageWindow *window);
cairo_region_t * _clutter_stage_window_get_redraw_clip (ClutterStageWindow *window);
void _clutter_stage_window_set_accept_focus (ClutterStageWindow *window,
gboolean accept_focus);

View File

@ -78,7 +78,6 @@
#include "clutter-private.h"
#include "cogl/cogl.h"
#include "cogl/cogl-trace.h"
struct _ClutterStageQueueRedrawEntry
{
@ -141,21 +140,12 @@ struct _ClutterStagePrivate
ClutterStageState current_state;
gpointer paint_data;
GDestroyNotify paint_notify;
cairo_rectangle_int_t view_clip;
int update_freeze_count;
guint redraw_pending : 1;
guint is_cursor_visible : 1;
guint throttle_motion_events : 1;
guint use_alpha : 1;
guint min_size_changed : 1;
guint accept_focus : 1;
guint motion_events_enabled : 1;
guint has_custom_perspective : 1;
guint stage_was_relayout : 1;
};
@ -164,12 +154,9 @@ enum
PROP_0,
PROP_COLOR,
PROP_CURSOR_VISIBLE,
PROP_PERSPECTIVE,
PROP_TITLE,
PROP_USE_ALPHA,
PROP_KEY_FOCUS,
PROP_ACCEPT_FOCUS,
PROP_LAST
};
@ -559,6 +546,33 @@ pick_record_contains_point (ClutterStage *stage,
return TRUE;
}
static void
clutter_stage_add_redraw_clip (ClutterStage *stage,
cairo_rectangle_int_t *clip)
{
GList *l;
for (l = clutter_stage_peek_stage_views (stage); l; l = l->next)
{
ClutterStageView *view = l->data;
if (!clip)
{
clutter_stage_view_add_redraw_clip (view, NULL);
}
else
{
cairo_rectangle_int_t view_layout;
cairo_rectangle_int_t intersection;
clutter_stage_view_get_layout (view, &view_layout);
if (_clutter_util_rectangle_intersection (&view_layout, clip,
&intersection))
clutter_stage_view_add_redraw_clip (view, &intersection);
}
}
}
static inline void
queue_full_redraw (ClutterStage *stage)
{
@ -577,7 +591,7 @@ queue_full_redraw (ClutterStage *stage)
if (stage_window == NULL)
return;
_clutter_stage_window_add_redraw_clip (stage_window, NULL);
clutter_stage_add_redraw_clip (stage, NULL);
}
static gboolean
@ -903,15 +917,19 @@ setup_view_for_pick_or_paint (ClutterStage *stage,
}
static void
clutter_stage_do_paint_view (ClutterStage *stage,
ClutterStageView *view,
const cairo_rectangle_int_t *clip)
clutter_stage_do_paint_view (ClutterStage *stage,
ClutterStageView *view,
const cairo_region_t *redraw_clip)
{
ClutterPaintContext *paint_context;
cairo_rectangle_int_t clip_rect;
paint_context = clutter_paint_context_new_for_view (view);
paint_context = clutter_paint_context_new_for_view (view, redraw_clip,
CLUTTER_PAINT_FLAG_NONE);
cairo_region_get_extents (redraw_clip, &clip_rect);
setup_view_for_pick_or_paint (stage, view, &clip_rect);
setup_view_for_pick_or_paint (stage, view, clip);
clutter_actor_paint (CLUTTER_ACTOR (stage), paint_context);
clutter_paint_context_destroy (paint_context);
}
@ -920,9 +938,9 @@ clutter_stage_do_paint_view (ClutterStage *stage,
* for picking or painting...
*/
void
_clutter_stage_paint_view (ClutterStage *stage,
ClutterStageView *view,
const cairo_rectangle_int_t *clip)
clutter_stage_paint_view (ClutterStage *stage,
ClutterStageView *view,
const cairo_region_t *redraw_clip)
{
ClutterStagePrivate *priv = stage->priv;
@ -931,15 +949,11 @@ _clutter_stage_paint_view (ClutterStage *stage,
COGL_TRACE_BEGIN_SCOPED (ClutterStagePaintView, "Paint (view)");
priv->view_clip = *clip;
if (g_signal_has_handler_pending (stage, stage_signals[PAINT_VIEW],
0, TRUE))
g_signal_emit (stage, stage_signals[PAINT_VIEW], 0, view);
g_signal_emit (stage, stage_signals[PAINT_VIEW], 0, view, redraw_clip);
else
CLUTTER_STAGE_GET_CLASS (stage)->paint_view (stage, view);
priv->view_clip = (cairo_rectangle_int_t) { 0 };
CLUTTER_STAGE_GET_CLASS (stage)->paint_view (stage, view, redraw_clip);
}
void
@ -1297,15 +1311,9 @@ clutter_stage_queue_actor_relayout (ClutterStage *stage,
{
ClutterStagePrivate *priv = stage->priv;
if (g_hash_table_contains (priv->pending_relayouts, stage))
return;
if (g_hash_table_size (priv->pending_relayouts) == 0)
_clutter_stage_schedule_update (stage);
if (actor == (ClutterActor *) stage)
g_hash_table_remove_all (priv->pending_relayouts);
g_hash_table_add (priv->pending_relayouts, g_object_ref (actor));
priv->pending_relayouts_version++;
}
@ -1416,16 +1424,12 @@ clutter_stage_do_redraw (ClutterStage *stage)
static GSList *
_clutter_stage_check_updated_pointers (ClutterStage *stage)
{
ClutterStagePrivate *priv = stage->priv;
ClutterBackend *backend;
ClutterSeat *seat;
GSList *updating = NULL;
GList *l, *devices;
cairo_region_t *clip;
graphene_point_t point;
clip = _clutter_stage_window_get_redraw_clip (priv->impl);
backend = clutter_get_default_backend ();
seat = clutter_backend_get_default_seat (backend);
devices = clutter_seat_list_devices (seat);
@ -1433,6 +1437,8 @@ _clutter_stage_check_updated_pointers (ClutterStage *stage)
for (l = devices; l; l = l->next)
{
ClutterInputDevice *dev = l->data;
ClutterStageView *view;
const cairo_region_t *clip;
if (clutter_input_device_get_device_mode (dev) !=
CLUTTER_INPUT_MODE_MASTER)
@ -1448,6 +1454,11 @@ _clutter_stage_check_updated_pointers (ClutterStage *stage)
if (!clutter_input_device_get_coords (dev, NULL, &point))
continue;
view = clutter_stage_get_view_at (stage, point.x, point.y);
if (!view)
continue;
clip = clutter_stage_view_peek_redraw_clip (view);
if (!clip || cairo_region_contains_point (clip, point.x, point.y))
updating = g_slist_prepend (updating, dev);
break;
@ -1557,6 +1568,22 @@ clutter_stage_real_queue_relayout (ClutterActor *self)
parent_class->queue_relayout (self);
}
static gboolean
is_full_stage_redraw_queued (ClutterStage *stage)
{
GList *l;
for (l = clutter_stage_peek_stage_views (stage); l; l = l->next)
{
ClutterStageView *view = l->data;
if (!clutter_stage_view_has_full_redraw_clip (view))
return FALSE;
}
return TRUE;
}
static gboolean
clutter_stage_real_queue_redraw (ClutterActor *actor,
ClutterActor *leaf,
@ -1578,12 +1605,12 @@ clutter_stage_real_queue_redraw (ClutterActor *actor,
if (stage_window == NULL)
return TRUE;
if (_clutter_stage_window_ignoring_redraw_clips (stage_window))
if (is_full_stage_redraw_queued (stage))
return FALSE;
if (redraw_clip == NULL)
{
_clutter_stage_window_add_redraw_clip (stage_window, NULL);
clutter_stage_add_redraw_clip (stage, NULL);
return FALSE;
}
@ -1615,43 +1642,20 @@ clutter_stage_real_queue_redraw (ClutterActor *actor,
stage_clip.width = intersection_box.x2 - stage_clip.x;
stage_clip.height = intersection_box.y2 - stage_clip.y;
_clutter_stage_window_add_redraw_clip (stage_window, &stage_clip);
clutter_stage_add_redraw_clip (stage, &stage_clip);
return FALSE;
}
gboolean
_clutter_stage_has_full_redraw_queued (ClutterStage *stage)
{
ClutterStageWindow *stage_window = _clutter_stage_get_window (stage);
if (CLUTTER_ACTOR_IN_DESTRUCTION (stage) || stage_window == NULL)
if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
return FALSE;
if (stage->priv->redraw_pending &&
!_clutter_stage_window_has_redraw_clips (stage_window))
return TRUE;
else
if (!stage->priv->redraw_pending)
return FALSE;
}
cairo_region_t *
clutter_stage_get_redraw_clip (ClutterStage *stage)
{
ClutterStagePrivate *priv;
cairo_rectangle_int_t clip;
cairo_region_t *region;
g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);
priv = stage->priv;
region = _clutter_stage_window_get_redraw_clip (priv->impl);
if (region)
return region;
/* Set clip to the full extents of the stage */
_clutter_stage_window_get_geometry (priv->impl, &clip);
return cairo_region_create_rectangle (&clip);
return is_full_stage_redraw_queued (stage);
}
static ClutterActor *
@ -1833,33 +1837,14 @@ clutter_stage_set_property (GObject *object,
clutter_value_get_color (value));
break;
case PROP_CURSOR_VISIBLE:
if (g_value_get_boolean (value))
clutter_stage_show_cursor (stage);
else
clutter_stage_hide_cursor (stage);
break;
case PROP_PERSPECTIVE:
clutter_stage_set_perspective (stage, g_value_get_boxed (value));
break;
case PROP_TITLE:
clutter_stage_set_title (stage, g_value_get_string (value));
break;
case PROP_USE_ALPHA:
clutter_stage_set_use_alpha (stage, g_value_get_boolean (value));
break;
case PROP_KEY_FOCUS:
clutter_stage_set_key_focus (stage, g_value_get_object (value));
break;
case PROP_ACCEPT_FOCUS:
clutter_stage_set_accept_focus (stage, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -1886,10 +1871,6 @@ clutter_stage_get_property (GObject *gobject,
}
break;
case PROP_CURSOR_VISIBLE:
g_value_set_boolean (value, priv->is_cursor_visible);
break;
case PROP_PERSPECTIVE:
g_value_set_boxed (value, &priv->perspective);
break;
@ -1898,18 +1879,10 @@ clutter_stage_get_property (GObject *gobject,
g_value_set_string (value, priv->title);
break;
case PROP_USE_ALPHA:
g_value_set_boolean (value, priv->use_alpha);
break;
case PROP_KEY_FOCUS:
g_value_set_object (value, priv->key_focused_actor);
break;
case PROP_ACCEPT_FOCUS:
g_value_set_boolean (value, priv->accept_focus);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
@ -1973,20 +1946,15 @@ clutter_stage_finalize (GObject *object)
if (priv->fps_timer != NULL)
g_timer_destroy (priv->fps_timer);
if (priv->paint_notify != NULL)
priv->paint_notify (priv->paint_data);
G_OBJECT_CLASS (clutter_stage_parent_class)->finalize (object);
}
static void
clutter_stage_real_paint_view (ClutterStage *stage,
ClutterStageView *view)
clutter_stage_real_paint_view (ClutterStage *stage,
ClutterStageView *view,
const cairo_region_t *redraw_clip)
{
ClutterStagePrivate *priv = stage->priv;
const cairo_rectangle_int_t *clip = &priv->view_clip;
clutter_stage_do_paint_view (stage, view, clip);
clutter_stage_do_paint_view (stage, view, redraw_clip);
}
static void
@ -2019,18 +1987,6 @@ clutter_stage_class_init (ClutterStageClass *klass)
klass->paint_view = clutter_stage_real_paint_view;
/**
* ClutterStage:cursor-visible:
*
* Whether the mouse pointer should be visible
*/
obj_props[PROP_CURSOR_VISIBLE] =
g_param_spec_boolean ("cursor-visible",
P_("Cursor Visible"),
P_("Whether the mouse pointer is visible on the main stage"),
TRUE,
CLUTTER_PARAM_READWRITE);
/**
* ClutterStage:color:
*
@ -2060,7 +2016,7 @@ clutter_stage_class_init (ClutterStageClass *klass)
P_("Perspective"),
P_("Perspective projection parameters"),
CLUTTER_TYPE_PERSPECTIVE,
CLUTTER_PARAM_READWRITE);
CLUTTER_PARAM_READABLE);
/**
* ClutterStage:title:
@ -2076,23 +2032,6 @@ clutter_stage_class_init (ClutterStageClass *klass)
NULL,
CLUTTER_PARAM_READWRITE);
/**
* ClutterStage:use-alpha:
*
* Whether the #ClutterStage should honour the alpha component of the
* #ClutterStage:color property when painting. If Clutter is run under
* a compositing manager this will result in the stage being blended
* with the underlying window(s)
*
* Since: 1.2
*/
obj_props[PROP_USE_ALPHA] =
g_param_spec_boolean ("use-alpha",
P_("Use Alpha"),
P_("Whether to honour the alpha component of the stage color"),
FALSE,
CLUTTER_PARAM_READWRITE);
/**
* ClutterStage:key-focus:
*
@ -2110,20 +2049,6 @@ clutter_stage_class_init (ClutterStageClass *klass)
CLUTTER_TYPE_ACTOR,
CLUTTER_PARAM_READWRITE);
/**
* ClutterStage:accept-focus:
*
* Whether the #ClutterStage should accept key focus when shown.
*
* Since: 1.6
*/
obj_props[PROP_ACCEPT_FOCUS] =
g_param_spec_boolean ("accept-focus",
P_("Accept Focus"),
P_("Whether the stage should accept focus on show"),
TRUE,
CLUTTER_PARAM_READWRITE);
g_object_class_install_properties (gobject_class, PROP_LAST, obj_props);
/**
@ -2212,6 +2137,7 @@ clutter_stage_class_init (ClutterStageClass *klass)
* ClutterStage::paint-view:
* @stage: the stage that received the event
* @view: a #ClutterStageView
* @redraw_clip: a #cairo_region_t with the redraw clip
*
* The ::paint-view signal is emitted before a #ClutterStageView is being
* painted.
@ -2226,8 +2152,9 @@ clutter_stage_class_init (ClutterStageClass *klass)
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ClutterStageClass, paint_view),
NULL, NULL, NULL,
G_TYPE_NONE, 1,
CLUTTER_TYPE_STAGE_VIEW);
G_TYPE_NONE, 2,
CLUTTER_TYPE_STAGE_VIEW,
G_TYPE_POINTER);
/**
* ClutterStage::presented: (skip)
@ -2296,7 +2223,6 @@ clutter_stage_init (ClutterStage *self)
priv->event_queue = g_queue_new ();
priv->is_cursor_visible = TRUE;
priv->throttle_motion_events = TRUE;
priv->min_size_changed = FALSE;
priv->sync_delay = -1;
@ -2419,8 +2345,8 @@ clutter_stage_get_color (ClutterStage *stage,
}
static void
clutter_stage_set_perspective_internal (ClutterStage *stage,
ClutterPerspective *perspective)
clutter_stage_set_perspective (ClutterStage *stage,
ClutterPerspective *perspective)
{
ClutterStagePrivate *priv = stage->priv;
@ -2445,36 +2371,6 @@ clutter_stage_set_perspective_internal (ClutterStage *stage,
clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
}
/**
* clutter_stage_set_perspective:
* @stage: A #ClutterStage
* @perspective: A #ClutterPerspective
*
* Sets the stage perspective. Using this function is not recommended
* because it will disable Clutter's attempts to generate an
* appropriate perspective based on the size of the stage.
*/
void
clutter_stage_set_perspective (ClutterStage *stage,
ClutterPerspective *perspective)
{
ClutterStagePrivate *priv;
g_return_if_fail (CLUTTER_IS_STAGE (stage));
g_return_if_fail (perspective != NULL);
g_return_if_fail (perspective->z_far - perspective->z_near != 0);
priv = stage->priv;
/* If the application ever calls this function then we'll stop
automatically updating the perspective when the stage changes
size */
priv->has_custom_perspective = TRUE;
clutter_stage_set_perspective_internal (stage, perspective);
clutter_stage_update_view_perspective (stage);
}
/**
* clutter_stage_get_perspective:
* @stage: A #ClutterStage
@ -2501,7 +2397,7 @@ clutter_stage_get_perspective (ClutterStage *stage,
* @stage.
*
* Retrieves the @stage's projection matrix. This is derived from the
* current perspective set using clutter_stage_set_perspective().
* current perspective.
*
* Since: 1.6
*/
@ -2665,72 +2561,6 @@ _clutter_stage_get_viewport (ClutterStage *stage,
*height = priv->viewport[3];
}
/**
* clutter_stage_show_cursor:
* @stage: a #ClutterStage
*
* Shows the cursor on the stage window
*/
void
clutter_stage_show_cursor (ClutterStage *stage)
{
ClutterStagePrivate *priv;
g_return_if_fail (CLUTTER_IS_STAGE (stage));
priv = stage->priv;
if (!priv->is_cursor_visible)
{
ClutterStageWindow *impl = CLUTTER_STAGE_WINDOW (priv->impl);
ClutterStageWindowInterface *iface;
iface = CLUTTER_STAGE_WINDOW_GET_IFACE (impl);
if (iface->set_cursor_visible)
{
priv->is_cursor_visible = TRUE;
iface->set_cursor_visible (impl, TRUE);
g_object_notify_by_pspec (G_OBJECT (stage),
obj_props[PROP_CURSOR_VISIBLE]);
}
}
}
/**
* clutter_stage_hide_cursor:
* @stage: a #ClutterStage
*
* Makes the cursor invisible on the stage window
*
* Since: 0.4
*/
void
clutter_stage_hide_cursor (ClutterStage *stage)
{
ClutterStagePrivate *priv;
g_return_if_fail (CLUTTER_IS_STAGE (stage));
priv = stage->priv;
if (priv->is_cursor_visible)
{
ClutterStageWindow *impl = CLUTTER_STAGE_WINDOW (priv->impl);
ClutterStageWindowInterface *iface;
iface = CLUTTER_STAGE_WINDOW_GET_IFACE (impl);
if (iface->set_cursor_visible)
{
priv->is_cursor_visible = FALSE;
iface->set_cursor_visible (impl, FALSE);
g_object_notify_by_pspec (G_OBJECT (stage),
obj_props[PROP_CURSOR_VISIBLE]);
}
}
}
/**
* clutter_stage_read_pixels:
* @stage: A #ClutterStage
@ -2803,13 +2633,17 @@ clutter_stage_read_pixels (ClutterStage *stage,
.height = height,
});
cairo_region_get_extents (clip, &clip_rect);
cairo_region_destroy (clip);
if (clip_rect.width == 0 || clip_rect.height == 0)
return NULL;
{
cairo_region_destroy (clip);
return NULL;
}
framebuffer = clutter_stage_view_get_framebuffer (view);
clutter_stage_do_paint_view (stage, view, &clip_rect);
clutter_stage_do_paint_view (stage, view, clip);
cairo_region_destroy (clip);
view_scale = clutter_stage_view_get_scale (view);
pixel_width = roundf (clip_rect.width * view_scale);
@ -3278,30 +3112,20 @@ clutter_stage_update_view_perspective (ClutterStage *stage)
perspective = priv->perspective;
/* Ideally we want to regenerate the perspective matrix whenever
* the size changes but if the user has provided a custom matrix
* then we don't want to override it */
if (!priv->has_custom_perspective)
{
perspective.fovy = 60.0; /* 60 Degrees */
perspective.z_near = 0.1;
perspective.aspect = priv->viewport[2] / priv->viewport[3];
z_2d = calculate_z_translation (perspective.z_near);
perspective.fovy = 60.0; /* 60 Degrees */
perspective.z_near = 0.1;
perspective.aspect = priv->viewport[2] / priv->viewport[3];
z_2d = calculate_z_translation (perspective.z_near);
/* NB: z_2d is only enough room for 85% of the stage_height between
* the stage and the z_near plane. For behind the stage plane we
* want a more consistent gap of 10 times the stage_height before
* hitting the far plane so we calculate that relative to the final
* height of the stage plane at the z_2d_distance we got... */
perspective.z_far = z_2d +
tanf (_DEG_TO_RAD (perspective.fovy / 2.0f)) * z_2d * 20.0f;
/* NB: z_2d is only enough room for 85% of the stage_height between
* the stage and the z_near plane. For behind the stage plane we
* want a more consistent gap of 10 times the stage_height before
* hitting the far plane so we calculate that relative to the final
* height of the stage plane at the z_2d_distance we got... */
perspective.z_far = z_2d +
tanf (_DEG_TO_RAD (perspective.fovy / 2.0f)) * z_2d * 20.0f;
clutter_stage_set_perspective_internal (stage, &perspective);
}
else
{
z_2d = calculate_z_translation (perspective.z_near);
}
clutter_stage_set_perspective (stage, &perspective);
cogl_matrix_init_identity (&priv->view);
cogl_matrix_view_2d_in_perspective (&priv->view,
@ -3531,56 +3355,6 @@ clutter_stage_get_throttle_motion_events (ClutterStage *stage)
return stage->priv->throttle_motion_events;
}
/**
* clutter_stage_set_use_alpha:
* @stage: a #ClutterStage
* @use_alpha: whether the stage should honour the opacity or the
* alpha channel of the stage color
*
* Sets whether the @stage should honour the #ClutterActor:opacity and
* the alpha channel of the #ClutterStage:color
*
* Since: 1.2
*/
void
clutter_stage_set_use_alpha (ClutterStage *stage,
gboolean use_alpha)
{
ClutterStagePrivate *priv;
g_return_if_fail (CLUTTER_IS_STAGE (stage));
priv = stage->priv;
if (priv->use_alpha != use_alpha)
{
priv->use_alpha = use_alpha;
clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
g_object_notify_by_pspec (G_OBJECT (stage), obj_props[PROP_USE_ALPHA]);
}
}
/**
* clutter_stage_get_use_alpha:
* @stage: a #ClutterStage
*
* Retrieves the value set using clutter_stage_set_use_alpha()
*
* Return value: %TRUE if the stage should honour the opacity and the
* alpha channel of the stage color
*
* Since: 1.2
*/
gboolean
clutter_stage_get_use_alpha (ClutterStage *stage)
{
g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE);
return stage->priv->use_alpha;
}
/**
* clutter_stage_set_minimum_size:
* @stage: a #ClutterStage
@ -3927,56 +3701,6 @@ clutter_stage_maybe_finish_queue_redraws (ClutterStage *stage)
}
}
/**
* clutter_stage_set_accept_focus:
* @stage: a #ClutterStage
* @accept_focus: %TRUE to accept focus on show
*
* Sets whether the @stage should accept the key focus when shown.
*
* This function should be called before showing @stage using
* clutter_actor_show().
*
* Since: 1.6
*/
void
clutter_stage_set_accept_focus (ClutterStage *stage,
gboolean accept_focus)
{
ClutterStagePrivate *priv;
g_return_if_fail (CLUTTER_IS_STAGE (stage));
accept_focus = !!accept_focus;
priv = stage->priv;
if (priv->accept_focus != accept_focus)
{
_clutter_stage_window_set_accept_focus (priv->impl, accept_focus);
g_object_notify_by_pspec (G_OBJECT (stage), obj_props[PROP_ACCEPT_FOCUS]);
}
}
/**
* clutter_stage_get_accept_focus:
* @stage: a #ClutterStage
*
* Retrieves the value set with clutter_stage_set_accept_focus().
*
* Return value: %TRUE if the #ClutterStage should accept focus, and %FALSE
* otherwise
*
* Since: 1.6
*/
gboolean
clutter_stage_get_accept_focus (ClutterStage *stage)
{
g_return_val_if_fail (CLUTTER_IS_STAGE (stage), TRUE);
return stage->priv->accept_focus;
}
/**
* clutter_stage_set_motion_events_enabled:
* @stage: a #ClutterStage
@ -4316,7 +4040,6 @@ capture_view (ClutterStage *stage,
texture_width, texture_height);
cairo_surface_set_device_scale (image, view_scale, view_scale);
data = cairo_image_surface_get_data (image);
stride = cairo_image_surface_get_stride (image);
@ -4326,6 +4049,20 @@ capture_view (ClutterStage *stage,
cairo_surface_mark_dirty (capture->image);
}
/**
* clutter_stage_capture:
* @stage: a #ClutterStage
* @paint: whether to pain the frame
* @rect: a #cairo_rectangle_int_t in stage coordinates
* @out_captures: (out) (array length=out_n_captures): an array of
* #ClutterCapture
* @out_n_captures: (out): the number of captures in @out_captures
*
* Captures the stage pixels of @rect into @captures. @rect is in stage
* coordinates.
*
* Returns: %TRUE if a #ClutterCapture has been created, %FALSE otherwise
*/
gboolean
clutter_stage_capture (ClutterStage *stage,
gboolean paint,
@ -4427,6 +4164,97 @@ clutter_stage_get_capture_final_size (ClutterStage *stage,
return TRUE;
}
void
clutter_stage_paint_to_framebuffer (ClutterStage *stage,
CoglFramebuffer *framebuffer,
const cairo_rectangle_int_t *rect,
float scale,
ClutterPaintFlag paint_flags)
{
ClutterStagePrivate *priv = stage->priv;
ClutterPaintContext *paint_context;
cairo_region_t *redraw_clip;
redraw_clip = cairo_region_create_rectangle (rect);
paint_context =
clutter_paint_context_new_for_framebuffer (framebuffer,
redraw_clip,
paint_flags);
cairo_region_destroy (redraw_clip);
cogl_framebuffer_push_matrix (framebuffer);
cogl_framebuffer_set_projection_matrix (framebuffer, &priv->projection);
cogl_framebuffer_set_viewport (framebuffer,
-(rect->x * scale),
-(rect->y * scale),
priv->viewport[2] * scale,
priv->viewport[3] * scale);
clutter_actor_paint (CLUTTER_ACTOR (stage), paint_context);
cogl_framebuffer_pop_matrix (framebuffer);
clutter_paint_context_destroy (paint_context);
}
gboolean
clutter_stage_paint_to_buffer (ClutterStage *stage,
const cairo_rectangle_int_t *rect,
float scale,
uint8_t *data,
int stride,
CoglPixelFormat format,
ClutterPaintFlag paint_flags,
GError **error)
{
ClutterBackend *clutter_backend = clutter_get_default_backend ();
CoglContext *cogl_context =
clutter_backend_get_cogl_context (clutter_backend);
int texture_width, texture_height;
CoglTexture2D *texture;
CoglOffscreen *offscreen;
CoglFramebuffer *framebuffer;
CoglBitmap *bitmap;
texture_width = (int) ceilf (rect->width * scale);
texture_height = (int) ceilf (rect->height * scale);
texture = cogl_texture_2d_new_with_size (cogl_context,
texture_width,
texture_height);
if (!texture)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Failed to create %dx%d texture",
texture_width, texture_height);
return FALSE;
}
offscreen = cogl_offscreen_new_with_texture (COGL_TEXTURE (texture));
framebuffer = COGL_FRAMEBUFFER (offscreen);
cogl_object_unref (texture);
if (!cogl_framebuffer_allocate (framebuffer, error))
return FALSE;
clutter_stage_paint_to_framebuffer (stage, framebuffer,
rect, scale, paint_flags);
bitmap = cogl_bitmap_new_for_data (cogl_context,
texture_width, texture_height,
format,
stride,
data);
cogl_framebuffer_read_pixels_into_bitmap (framebuffer,
0, 0,
COGL_READ_PIXELS_COLOR_BUFFER,
bitmap);
cogl_object_unref (bitmap);
cogl_object_unref (framebuffer);
return TRUE;
}
static void
capture_view_into (ClutterStage *stage,
gboolean paint,
@ -4450,8 +4278,12 @@ capture_view_into (ClutterStage *stage,
if (paint)
{
cairo_region_t *region;
_clutter_stage_maybe_setup_viewport (stage, view);
clutter_stage_do_paint_view (stage, view, rect);
region = cairo_region_create_rectangle (rect);
clutter_stage_do_paint_view (stage, view, region);
cairo_region_destroy (region);
}
view_scale = clutter_stage_view_get_scale (view);
@ -4477,49 +4309,42 @@ capture_view_into (ClutterStage *stage,
cogl_object_unref (bitmap);
}
static ClutterStageView *
get_view_at_rect (ClutterStage *stage,
cairo_rectangle_int_t *rect)
{
ClutterStagePrivate *priv = stage->priv;
GList *views = _clutter_stage_window_get_views (priv->impl);
GList *l;
for (l = views; l; l = l->next)
{
ClutterStageView *view = l->data;
cairo_rectangle_int_t view_layout;
cairo_region_t *region;
cairo_rectangle_int_t view_capture_rect;
clutter_stage_view_get_layout (view, &view_layout);
region = cairo_region_create_rectangle (&view_layout);
cairo_region_intersect_rectangle (region, rect);
cairo_region_get_extents (region, &view_capture_rect);
cairo_region_destroy (region);
if (view_capture_rect.width == 0 || view_capture_rect.height == 0)
continue;
g_assert (view_capture_rect.width == rect->width &&
view_capture_rect.height == rect->height);
return view;
}
return NULL;
}
void
clutter_stage_capture_into (ClutterStage *stage,
gboolean paint,
cairo_rectangle_int_t *rect,
uint8_t *data)
{
ClutterStageView *view;
ClutterStagePrivate *priv = stage->priv;
GList *l;
int bpp = 4;
int stride;
view = get_view_at_rect (stage, rect);
capture_view_into (stage, paint, view, rect, data, rect->width * bpp);
stride = rect->width * 4;
for (l = _clutter_stage_window_get_views (priv->impl); l; l = l->next)
{
ClutterStageView *view = l->data;
cairo_rectangle_int_t view_layout;
cairo_region_t *region;
cairo_rectangle_int_t capture_rect;
int x_offset, y_offset;
clutter_stage_view_get_layout (view, &view_layout);
region = cairo_region_create_rectangle (&view_layout);
cairo_region_intersect_rectangle (region, rect);
cairo_region_get_extents (region, &capture_rect);
cairo_region_destroy (region);
x_offset = capture_rect.x - rect->x;
y_offset = capture_rect.y - rect->y;
capture_view_into (stage, paint, view,
&capture_rect,
data + (x_offset * bpp) + (y_offset * stride),
stride);
}
}
/**
@ -4577,8 +4402,11 @@ clutter_stage_thaw_updates (ClutterStage *stage)
}
}
/**
* clutter_stage_peek_stage_views: (skip)
*/
GList *
_clutter_stage_peek_stage_views (ClutterStage *stage)
clutter_stage_peek_stage_views (ClutterStage *stage)
{
ClutterStagePrivate *priv = stage->priv;

View File

@ -84,8 +84,9 @@ struct _ClutterStageClass
gboolean (* delete_event) (ClutterStage *stage,
ClutterEvent *event);
void (* paint_view) (ClutterStage *stage,
ClutterStageView *view);
void (* paint_view) (ClutterStage *stage,
ClutterStageView *view,
const cairo_region_t *redraw_clip);
/*< private >*/
/* padding for future expansion */
@ -102,8 +103,7 @@ struct _ClutterStageClass
* @z_far: the distance from the viewer to the far clipping
* plane (always positive)
*
* Stage perspective definition. #ClutterPerspective is only used by
* the fixed point version of clutter_stage_set_perspective().
* Stage perspective definition.
*
* Since: 0.4
*/
@ -139,17 +139,10 @@ GType clutter_stage_get_type (void) G_GNUC_CONST;
CLUTTER_EXPORT
ClutterActor * clutter_stage_new (void);
CLUTTER_EXPORT
void clutter_stage_set_perspective (ClutterStage *stage,
ClutterPerspective *perspective);
CLUTTER_EXPORT
void clutter_stage_get_perspective (ClutterStage *stage,
ClutterPerspective *perspective);
CLUTTER_EXPORT
void clutter_stage_show_cursor (ClutterStage *stage);
CLUTTER_EXPORT
void clutter_stage_hide_cursor (ClutterStage *stage);
CLUTTER_EXPORT
void clutter_stage_set_title (ClutterStage *stage,
const gchar *title);
CLUTTER_EXPORT
@ -185,11 +178,6 @@ void clutter_stage_set_motion_events_enabled (ClutterStage
CLUTTER_EXPORT
gboolean clutter_stage_get_motion_events_enabled (ClutterStage *stage);
CLUTTER_EXPORT
void clutter_stage_set_accept_focus (ClutterStage *stage,
gboolean accept_focus);
CLUTTER_EXPORT
gboolean clutter_stage_get_accept_focus (ClutterStage *stage);
CLUTTER_EXPORT
gboolean clutter_stage_event (ClutterStage *stage,
ClutterEvent *event);
@ -205,8 +193,6 @@ guchar * clutter_stage_read_pixels (ClutterStage
gint width,
gint height);
CLUTTER_EXPORT
cairo_region_t * clutter_stage_get_redraw_clip (ClutterStage *stage);
CLUTTER_EXPORT
void clutter_stage_ensure_viewport (ClutterStage *stage);
CLUTTER_EXPORT
@ -234,8 +220,8 @@ CLUTTER_EXPORT
gboolean clutter_stage_capture (ClutterStage *stage,
gboolean paint,
cairo_rectangle_int_t *rect,
ClutterCapture **captures,
int *n_captures);
ClutterCapture **out_captures,
int *out_n_captures);
CLUTTER_EXPORT
ClutterStageView * clutter_stage_get_view_at (ClutterStage *stage,
float x,

View File

@ -348,13 +348,23 @@ clutter_text_input_focus_request_surrounding (ClutterInputFocus *focus)
static void
clutter_text_input_focus_delete_surrounding (ClutterInputFocus *focus,
guint offset,
int offset,
guint len)
{
ClutterText *clutter_text = CLUTTER_TEXT_INPUT_FOCUS (focus)->text;
int cursor;
int start;
cursor = clutter_text_get_cursor_position (clutter_text);
start = cursor + offset;
if (start < 0)
{
g_warning ("The offset '%d' of deleting surrounding is larger than the cursor pos '%d'",
offset, cursor);
return;
}
if (clutter_text_get_editable (clutter_text))
clutter_text_delete_text (clutter_text, offset, len);
clutter_text_delete_text (clutter_text, start, len);
}
static void
@ -1897,14 +1907,6 @@ clutter_text_foreach_selection_rectangle (ClutterText *self,
g_free (utf8);
}
static void
add_selection_rectangle_to_path (ClutterText *text,
const ClutterActorBox *box,
gpointer user_data)
{
cogl_path_rectangle (user_data, box->x1, box->y1, box->x2, box->y2);
}
static void
clutter_text_foreach_selection_rectangle_prescaled (ClutterText *self,
ClutterTextSelectionFunc func,
@ -1913,6 +1915,60 @@ clutter_text_foreach_selection_rectangle_prescaled (ClutterText *se
clutter_text_foreach_selection_rectangle (self, 1.0f, func, user_data);
}
static void
paint_selection_rectangle (ClutterText *self,
const ClutterActorBox *box,
gpointer user_data)
{
CoglFramebuffer *fb = user_data;
ClutterTextPrivate *priv = self->priv;
ClutterActor *actor = CLUTTER_ACTOR (self);
guint8 paint_opacity = clutter_actor_get_paint_opacity (actor);
CoglPipeline *color_pipeline = cogl_pipeline_copy (default_color_pipeline);
PangoLayout *layout = clutter_text_get_layout (self);
CoglColor cogl_color = { 0, };
const ClutterColor *color;
/* Paint selection background */
if (priv->selection_color_set)
color = &priv->selection_color;
else if (priv->cursor_color_set)
color = &priv->cursor_color;
else
color = &priv->text_color;
cogl_color_init_from_4ub (&cogl_color,
color->red,
color->green,
color->blue,
paint_opacity * color->alpha / 255);
cogl_color_premultiply (&cogl_color);
cogl_pipeline_set_color (color_pipeline, &cogl_color);
cogl_framebuffer_push_rectangle_clip (fb,
box->x1, box->y1,
box->x2, box->y2);
cogl_framebuffer_draw_rectangle (fb, color_pipeline,
box->x1, box->y1,
box->x2, box->y2);
if (priv->selected_text_color_set)
color = &priv->selected_text_color;
else
color = &priv->text_color;
cogl_color_init_from_4ub (&cogl_color,
color->red,
color->green,
color->blue,
paint_opacity * color->alpha / 255);
cogl_pango_show_layout (fb, layout, priv->text_x, 0, &cogl_color);
cogl_framebuffer_pop_clip (fb);
cogl_object_unref (color_pipeline);
}
/* Draws the selected text, its background, and the cursor */
static void
selection_paint (ClutterText *self,
@ -1955,52 +2011,9 @@ selection_paint (ClutterText *self,
}
else
{
/* Paint selection background first */
CoglPipeline *color_pipeline = cogl_pipeline_copy (default_color_pipeline);
PangoLayout *layout = clutter_text_get_layout (self);
CoglPath *selection_path = cogl_path_new ();
CoglColor cogl_color = { 0, };
/* Paint selection background */
if (priv->selection_color_set)
color = &priv->selection_color;
else if (priv->cursor_color_set)
color = &priv->cursor_color;
else
color = &priv->text_color;
cogl_color_init_from_4ub (&cogl_color,
color->red,
color->green,
color->blue,
paint_opacity * color->alpha / 255);
cogl_color_premultiply (&cogl_color);
cogl_pipeline_set_color (color_pipeline, &cogl_color);
clutter_text_foreach_selection_rectangle_prescaled (self,
add_selection_rectangle_to_path,
selection_path);
cogl_framebuffer_fill_path (fb, color_pipeline, selection_path);
/* Paint selected text */
cogl_framebuffer_push_path_clip (fb, selection_path);
cogl_object_unref (selection_path);
if (priv->selected_text_color_set)
color = &priv->selected_text_color;
else
color = &priv->text_color;
cogl_color_init_from_4ub (&cogl_color,
color->red,
color->green,
color->blue,
paint_opacity * color->alpha / 255);
cogl_pango_show_layout (fb, layout, priv->text_x, 0, &cogl_color);
cogl_framebuffer_pop_clip (fb);
paint_selection_rectangle,
fb);
}
}

View File

@ -214,6 +214,16 @@ _clutter_util_rectangle_intersection (const cairo_rectangle_int_t *src1,
}
}
gboolean
clutter_util_rectangle_equal (const cairo_rectangle_int_t *src1,
const cairo_rectangle_int_t *src2)
{
return ((src1->x == src2->x) &&
(src1->y == src2->y) &&
(src1->width == src2->width) &&
(src1->height == src2->height));
}
float
_clutter_util_matrix_determinant (const ClutterMatrix *matrix)
{

View File

@ -56,8 +56,6 @@
#include "clutter-content.h"
#include "clutter-deform-effect.h"
#include "clutter-desaturate-effect.h"
#include "clutter-drag-action.h"
#include "clutter-drop-action.h"
#include "clutter-effect.h"
#include "clutter-enums.h"
#include "clutter-enum-types.h"

View File

@ -47,7 +47,7 @@
#include "clutter-stage-private.h"
#include "clutter-stage-view-private.h"
#include "cogl/cogl-trace.h"
#define MAX_STACK_RECTS 256
typedef struct _ClutterStageViewCoglPrivate
{
@ -288,97 +288,6 @@ clutter_stage_cogl_resize (ClutterStageWindow *stage_window,
{
}
static gboolean
clutter_stage_cogl_has_redraw_clips (ClutterStageWindow *stage_window)
{
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
/* NB: at the start of each new frame there is an implied clip that
* clips everything (i.e. nothing would be drawn) so we need to make
* sure we return True in the un-initialized case here.
*/
if (!stage_cogl->initialized_redraw_clip ||
(stage_cogl->initialized_redraw_clip &&
stage_cogl->redraw_clip))
return TRUE;
else
return FALSE;
}
static gboolean
clutter_stage_cogl_ignoring_redraw_clips (ClutterStageWindow *stage_window)
{
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
/* NB: a NULL clip means a full stage redraw is required */
if (stage_cogl->initialized_redraw_clip &&
!stage_cogl->redraw_clip)
return TRUE;
else
return FALSE;
}
/* A redraw clip represents (in stage coordinates) the bounding box of
* something that needs to be redrawn. Typically they are added to the
* StageWindow as a result of clutter_actor_queue_clipped_redraw() by
* actors such as ClutterGLXTexturePixmap. All redraw clips are
* discarded after the next paint.
*
* A NULL stage_clip means the whole stage needs to be redrawn.
*
* What we do with this information:
* - we keep track of the bounding box for all redraw clips
* - when we come to redraw; we scissor the redraw to that box and use
* glBlitFramebuffer to present the redraw to the front
* buffer.
*/
static void
clutter_stage_cogl_add_redraw_clip (ClutterStageWindow *stage_window,
cairo_rectangle_int_t *stage_clip)
{
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
/* If we are already forced to do a full stage redraw then bail early */
if (clutter_stage_cogl_ignoring_redraw_clips (stage_window))
return;
/* A NULL stage clip means a full stage redraw has been queued and
* we keep track of this by setting a NULL redraw_clip.
*/
if (stage_clip == NULL)
{
g_clear_pointer (&stage_cogl->redraw_clip, cairo_region_destroy);
stage_cogl->initialized_redraw_clip = TRUE;
return;
}
/* Ignore requests to add degenerate/empty clip rectangles */
if (stage_clip->width == 0 || stage_clip->height == 0)
return;
if (!stage_cogl->redraw_clip)
{
stage_cogl->redraw_clip = cairo_region_create_rectangle (stage_clip);
}
else
{
cairo_region_union_rectangle (stage_cogl->redraw_clip, stage_clip);
}
stage_cogl->initialized_redraw_clip = TRUE;
}
static cairo_region_t *
clutter_stage_cogl_get_redraw_clip (ClutterStageWindow *stage_window)
{
ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
if (stage_cogl->using_clipped_redraw && stage_cogl->redraw_clip)
return cairo_region_copy (stage_cogl->redraw_clip);
return NULL;
}
static inline gboolean
valid_buffer_age (ClutterStageViewCogl *view_cogl,
int age)
@ -395,7 +304,8 @@ valid_buffer_age (ClutterStageViewCogl *view_cogl,
static void
paint_damage_region (ClutterStageWindow *stage_window,
ClutterStageView *view,
cairo_region_t *swap_region)
cairo_region_t *swap_region,
cairo_region_t *queued_redraw_clip)
{
CoglFramebuffer *framebuffer = clutter_stage_view_get_onscreen (view);
CoglContext *ctx = cogl_framebuffer_get_context (framebuffer);
@ -432,8 +342,7 @@ paint_damage_region (ClutterStageWindow *stage_window,
}
/* Red for the clip */
if (stage_cogl->initialized_redraw_clip &&
stage_cogl->redraw_clip)
if (queued_redraw_clip)
{
static CoglPipeline *overlay_red = NULL;
@ -443,13 +352,13 @@ paint_damage_region (ClutterStageWindow *stage_window,
cogl_pipeline_set_color4ub (overlay_red, 0x33, 0x00, 0x00, 0x33);
}
n_rects = cairo_region_num_rectangles (stage_cogl->redraw_clip);
n_rects = cairo_region_num_rectangles (queued_redraw_clip);
for (i = 0; i < n_rects; i++)
{
cairo_rectangle_int_t rect;
float x_1, x_2, y_1, y_2;
cairo_region_get_rectangle (stage_cogl->redraw_clip, i, &rect);
cairo_region_get_rectangle (queued_redraw_clip, i, &rect);
x_1 = rect.x;
x_2 = rect.x + rect.width;
y_1 = rect.y;
@ -471,9 +380,6 @@ swap_framebuffer (ClutterStageWindow *stage_window,
CoglFramebuffer *framebuffer = clutter_stage_view_get_onscreen (view);
int *damage, n_rects, i;
if (G_UNLIKELY ((clutter_paint_debug_flags & CLUTTER_DEBUG_PAINT_DAMAGE_REGION)))
paint_damage_region (stage_window, view, swap_region);
n_rects = cairo_region_num_rectangles (swap_region);
damage = g_newa (int, n_rects * 4);
for (i = 0; i < n_rects; i++)
@ -536,34 +442,52 @@ scale_and_clamp_rect (const graphene_rect_t *rect,
_clutter_util_rectangle_int_extents (&tmp, dest);
}
static cairo_region_t *
offset_scale_and_clamp_region (const cairo_region_t *region,
int offset_x,
int offset_y,
float scale)
{
int n_rects, i;
cairo_rectangle_int_t *rects;
g_autofree cairo_rectangle_int_t *freeme = NULL;
n_rects = cairo_region_num_rectangles (region);
if (n_rects == 0)
return cairo_region_create ();
if (n_rects < MAX_STACK_RECTS)
rects = g_newa (cairo_rectangle_int_t, n_rects);
else
rects = freeme = g_new (cairo_rectangle_int_t, n_rects);
for (i = 0; i < n_rects; i++)
cairo_region_get_rectangle (region, i, &rects[i]);
for (i = 0; i < n_rects; i++)
{
graphene_rect_t tmp;
_clutter_util_rect_from_rectangle (&rects[i], &tmp);
graphene_rect_offset (&tmp, offset_x, offset_y);
scale_and_clamp_rect (&tmp, scale, &rects[i]);
}
return cairo_region_create_rectangles (rects, n_rects);
}
static void
paint_stage (ClutterStageCogl *stage_cogl,
ClutterStageView *view,
cairo_region_t *clip)
paint_stage (ClutterStageCogl *stage_cogl,
ClutterStageView *view,
cairo_region_t *redraw_clip)
{
ClutterStage *stage = stage_cogl->wrapper;
cairo_rectangle_int_t clip_rect;
cairo_rectangle_int_t paint_rect;
cairo_rectangle_int_t view_rect;
graphene_rect_t rect;
float fb_scale;
clutter_stage_view_get_layout (view, &view_rect);
fb_scale = clutter_stage_view_get_scale (view);
cairo_region_get_extents (clip, &clip_rect);
_clutter_util_rect_from_rectangle (&clip_rect, &rect);
scale_and_clamp_rect (&rect, 1.0f / fb_scale, &paint_rect);
_clutter_util_rectangle_offset (&paint_rect,
view_rect.x,
view_rect.y,
&paint_rect);
_clutter_stage_maybe_setup_viewport (stage, view);
_clutter_stage_paint_view (stage, view, &paint_rect);
clutter_stage_paint_view (stage, view, redraw_clip);
clutter_stage_view_after_paint (view, &paint_rect);
clutter_stage_view_after_paint (view);
}
static void
@ -683,7 +607,7 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
clutter_stage_view_cogl_get_instance_private (view_cogl);
CoglFramebuffer *fb = clutter_stage_view_get_framebuffer (view);
cairo_rectangle_int_t view_rect;
gboolean have_clip;
gboolean is_full_redraw;
gboolean may_use_clipped_redraw;
gboolean use_clipped_redraw;
gboolean can_blit_sub_buffer;
@ -691,7 +615,8 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
gboolean do_swap_buffer;
gboolean swap_with_damage;
ClutterActor *wrapper;
cairo_region_t *redraw_clip = NULL;
cairo_region_t *redraw_clip;
cairo_region_t *queued_redraw_clip = NULL;
cairo_region_t *fb_clip_region;
cairo_region_t *swap_region;
cairo_rectangle_int_t redraw_rect;
@ -699,6 +624,7 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
float fb_scale;
int subpixel_compensation = 0;
int fb_width, fb_height;
int buffer_age;
wrapper = CLUTTER_ACTOR (stage_cogl->wrapper);
@ -713,55 +639,46 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
has_buffer_age = cogl_is_onscreen (fb) && is_buffer_age_enabled ();
redraw_clip = clutter_stage_view_take_redraw_clip (view);
if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_PAINT_DAMAGE_REGION))
queued_redraw_clip = cairo_region_copy (redraw_clip);
/* NB: a NULL redraw clip == full stage redraw */
if (!stage_cogl->redraw_clip)
have_clip = FALSE;
if (!redraw_clip)
is_full_redraw = TRUE;
else
is_full_redraw = FALSE;
may_use_clipped_redraw =
_clutter_stage_window_can_clip_redraws (stage_window) &&
(can_blit_sub_buffer || has_buffer_age) &&
!is_full_redraw &&
/* some drivers struggle to get going and produce some junk
* frames when starting up... */
cogl_onscreen_get_frame_counter (COGL_ONSCREEN (fb)) > 3;
if (has_buffer_age)
{
cairo_region_t *view_region;
redraw_clip = cairo_region_copy (stage_cogl->redraw_clip);
view_region = cairo_region_create_rectangle (&view_rect);
cairo_region_intersect (redraw_clip, view_region);
have_clip = !cairo_region_equal (redraw_clip, view_region);
cairo_region_destroy (view_region);
buffer_age = cogl_onscreen_get_buffer_age (COGL_ONSCREEN (fb));
if (!valid_buffer_age (view_cogl, buffer_age))
{
CLUTTER_NOTE (CLIPPING, "Invalid back buffer(age=%d): forcing full redraw\n", buffer_age);
may_use_clipped_redraw = FALSE;
}
}
may_use_clipped_redraw = FALSE;
if (_clutter_stage_window_can_clip_redraws (stage_window) &&
(can_blit_sub_buffer || has_buffer_age) &&
have_clip &&
/* some drivers struggle to get going and produce some junk
* frames when starting up... */
cogl_onscreen_get_frame_counter (COGL_ONSCREEN (fb)) > 3)
if (may_use_clipped_redraw)
{
graphene_rect_t rect;
cairo_rectangle_int_t *rects;
int n_rects, i;
may_use_clipped_redraw = TRUE;
fb_clip_region = cairo_region_create ();
n_rects = cairo_region_num_rectangles (redraw_clip);
rects = g_new (cairo_rectangle_int_t, n_rects);
for (i = 0; i < n_rects; i++)
{
cairo_rectangle_int_t new_fb_clip_rect;
cairo_region_get_rectangle (redraw_clip, i, &rects[i]);
_clutter_util_rect_from_rectangle (&rects[i], &rect);
graphene_rect_offset (&rect, -view_rect.x, -view_rect.y);
scale_and_clamp_rect (&rect, fb_scale, &new_fb_clip_rect);
cairo_region_union_rectangle (fb_clip_region, &new_fb_clip_rect);
}
g_free (rects);
fb_clip_region = offset_scale_and_clamp_region (redraw_clip,
-view_rect.x,
-view_rect.y,
fb_scale);
if (fb_scale != floorf (fb_scale))
{
int n_rects, i;
cairo_rectangle_int_t *rects;
subpixel_compensation = ceilf (fb_scale);
n_rects = cairo_region_num_rectangles (fb_clip_region);
@ -780,10 +697,16 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
}
else
{
cairo_rectangle_int_t rect = { 0, 0, view_rect.width, view_rect.height };
fb_clip_region = cairo_region_create_rectangle (&rect);
cairo_rectangle_int_t fb_rect;
fb_rect = (cairo_rectangle_int_t) {
.width = fb_width,
.height = fb_height,
};
fb_clip_region = cairo_region_create_rectangle (&fb_rect);
g_clear_pointer (&redraw_clip, cairo_region_destroy);
redraw_clip = cairo_region_copy (fb_clip_region);
redraw_clip = cairo_region_create_rectangle (&view_rect);
}
if (may_use_clipped_redraw &&
@ -799,62 +722,43 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
{
if (use_clipped_redraw && !clip_region_empty)
{
int age;
cairo_region_t *fb_damage;
cairo_region_t *view_damage;
int i;
age = cogl_onscreen_get_buffer_age (COGL_ONSCREEN (fb));
fill_current_damage_history (view, fb_clip_region);
if (valid_buffer_age (view_cogl, age))
fb_damage = cairo_region_create ();
for (i = 1; i <= buffer_age; i++)
{
graphene_rect_t rect;
cairo_rectangle_int_t damage_region;
cairo_rectangle_int_t *rects;
int n_rects, i;
int damage_index;
fill_current_damage_history (view, fb_clip_region);
for (i = 1; i <= age; i++)
{
cairo_region_t *fb_damage =
view_priv->damage_history[DAMAGE_HISTORY (view_priv->damage_index - i - 1)];
cairo_region_union (fb_clip_region, fb_damage);
}
/* Update the redraw clip state with the extra damage. */
n_rects = cairo_region_num_rectangles (fb_clip_region);
rects = g_newa (cairo_rectangle_int_t, n_rects);
for (i = 0; i < n_rects; i++)
{
cairo_region_get_rectangle (fb_clip_region, i, &rects[i]);
_clutter_util_rect_from_rectangle (&rects[i], &rect);
scale_and_clamp_rect (&rect, 1.0f / fb_scale, &damage_region);
_clutter_util_rectangle_offset (&damage_region,
view_rect.x,
view_rect.y,
&damage_region);
cairo_region_union_rectangle (stage_cogl->redraw_clip,
&damage_region);
}
CLUTTER_NOTE (CLIPPING, "Reusing back buffer(age=%d) - repairing region: num rects: %d\n",
age,
cairo_region_num_rectangles (fb_clip_region));
swap_with_damage = TRUE;
damage_index = DAMAGE_HISTORY (view_priv->damage_index - i - 1);
cairo_region_union (fb_damage,
view_priv->damage_history[damage_index]);
}
else
{
cairo_rectangle_int_t fb_damage;
CLUTTER_NOTE (CLIPPING, "Invalid back buffer(age=%d): forcing full redraw\n", age);
use_clipped_redraw = FALSE;
fb_damage = (cairo_rectangle_int_t) {
.x = 0,
.y = 0,
.width = ceilf (view_rect.width * fb_scale),
.height = ceilf (view_rect.height * fb_scale)
};
fill_current_damage_history_rectangle (view, &fb_damage);
}
/* Update the fb clip region with the extra damage. */
cairo_region_union (fb_clip_region, fb_damage);
view_damage = offset_scale_and_clamp_region (fb_damage,
0, 0,
1.0f / fb_scale);
cairo_region_translate (view_damage, view_rect.x, view_rect.y);
cairo_region_intersect_rectangle (view_damage, &view_rect);
/* Update the redraw clip region with the extra damage. */
cairo_region_union (redraw_clip, view_damage);
cairo_region_destroy (view_damage);
cairo_region_destroy (fb_damage);
CLUTTER_NOTE (CLIPPING, "Reusing back buffer(age=%d) - repairing region: num rects: %d\n",
buffer_age,
cairo_region_num_rectangles (fb_clip_region));
swap_with_damage = TRUE;
}
else if (!use_clipped_redraw)
{
@ -879,8 +783,6 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
cairo_rectangle_int_t clip_rect;
cairo_rectangle_int_t scissor_rect;
stage_cogl->using_clipped_redraw = TRUE;
if (cairo_region_num_rectangles (fb_clip_region) == 1)
{
cairo_region_get_extents (fb_clip_region, &clip_rect);
@ -908,11 +810,9 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
cogl_framebuffer_push_region_clip (fb, fb_clip_region);
}
paint_stage (stage_cogl, view, fb_clip_region);
paint_stage (stage_cogl, view, redraw_clip);
cogl_framebuffer_pop_clip (fb);
stage_cogl->using_clipped_redraw = FALSE;
}
else
{
@ -940,24 +840,13 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
scissor_rect.width,
scissor_rect.height);
paint_stage (stage_cogl, view, fb_clip_region);
paint_stage (stage_cogl, view, redraw_clip);
cogl_framebuffer_pop_clip (fb);
}
else
{
cairo_rectangle_int_t clip;
cairo_region_t *view_region;
clip = (cairo_rectangle_int_t) {
.x = 0,
.y = 0,
.width = ceilf (view_rect.width * fb_scale),
.height = ceilf (view_rect.height * fb_scale)
};
view_region = cairo_region_create_rectangle (&clip);
paint_stage (stage_cogl, view, view_region);
cairo_region_destroy (view_region);
paint_stage (stage_cogl, view, redraw_clip);
}
}
@ -1028,10 +917,8 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
do_swap_buffer = TRUE;
}
if (redraw_clip)
cairo_region_destroy (redraw_clip);
if (fb_clip_region)
cairo_region_destroy (fb_clip_region);
g_clear_pointer (&redraw_clip, cairo_region_destroy);
g_clear_pointer (&fb_clip_region, cairo_region_destroy);
if (do_swap_buffer)
{
@ -1051,6 +938,13 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
swap_region = transformed_swap_region;
}
if (queued_redraw_clip)
{
paint_damage_region (stage_window, view,
swap_region, queued_redraw_clip);
cairo_region_destroy (queued_redraw_clip);
}
res = swap_framebuffer (stage_window,
view,
swap_region,
@ -1062,10 +956,25 @@ clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window,
}
else
{
g_clear_pointer (&queued_redraw_clip, cairo_region_destroy);
return FALSE;
}
}
static void
clutter_stage_cogl_scanout_view (ClutterStageCogl *stage_cogl,
ClutterStageView *view,
CoglScanout *scanout)
{
CoglFramebuffer *framebuffer = clutter_stage_view_get_framebuffer (view);
CoglOnscreen *onscreen;
g_return_if_fail (cogl_is_onscreen (framebuffer));
onscreen = COGL_ONSCREEN (framebuffer);
cogl_onscreen_direct_scanout (onscreen, scanout);
}
static void
clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
{
@ -1078,9 +987,23 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
for (l = _clutter_stage_window_get_views (stage_window); l; l = l->next)
{
ClutterStageView *view = l->data;
g_autoptr (CoglScanout) scanout = NULL;
swap_event =
clutter_stage_cogl_redraw_view (stage_window, view) || swap_event;
if (!clutter_stage_view_has_redraw_clip (view))
continue;
scanout = clutter_stage_view_take_scanout (view);
if (scanout)
{
clutter_stage_cogl_scanout_view (stage_cogl,
view,
scanout);
swap_event = TRUE;
}
else
{
swap_event |= clutter_stage_cogl_redraw_view (stage_window, view);
}
}
_clutter_stage_emit_after_paint (stage_cogl->wrapper);
@ -1096,10 +1019,6 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window)
stage_cogl->pending_swaps++;
}
/* reset the redraw clipping for the next paint... */
stage_cogl->initialized_redraw_clip = FALSE;
g_clear_pointer (&stage_cogl->redraw_clip, cairo_region_destroy);
stage_cogl->frame_count++;
COGL_TRACE_END (ClutterStageCoglRedraw);
@ -1117,10 +1036,6 @@ clutter_stage_window_iface_init (ClutterStageWindowInterface *iface)
iface->schedule_update = clutter_stage_cogl_schedule_update;
iface->get_update_time = clutter_stage_cogl_get_update_time;
iface->clear_update_time = clutter_stage_cogl_clear_update_time;
iface->add_redraw_clip = clutter_stage_cogl_add_redraw_clip;
iface->has_redraw_clips = clutter_stage_cogl_has_redraw_clips;
iface->ignoring_redraw_clips = clutter_stage_cogl_ignoring_redraw_clips;
iface->get_redraw_clip = clutter_stage_cogl_get_redraw_clip;
iface->redraw = clutter_stage_cogl_redraw;
}

View File

@ -55,14 +55,6 @@ struct _ClutterStageCogl
unsigned int frame_count;
gint last_sync_delay;
cairo_region_t *redraw_clip;
guint initialized_redraw_clip : 1;
/* TRUE if the current paint cycle has a clipped redraw. In that
case bounding_redraw_clip specifies the the bounds. */
guint using_clipped_redraw : 1;
};
struct _ClutterStageCoglClass

View File

@ -30,8 +30,6 @@ clutter_headers = [
'clutter-deform-effect.h',
'clutter-deprecated.h',
'clutter-desaturate-effect.h',
'clutter-drag-action.h',
'clutter-drop-action.h',
'clutter-effect.h',
'clutter-enums.h',
'clutter-event.h',
@ -118,8 +116,6 @@ clutter_sources = [
'clutter-content.c',
'clutter-deform-effect.c',
'clutter-desaturate-effect.c',
'clutter-drag-action.c',
'clutter-drop-action.c',
'clutter-effect.c',
'clutter-event.c',
'clutter-feature.c',
@ -341,35 +337,20 @@ clutter_build_config_h = configure_file(
)
clutter_built_private_headers += clutter_build_config_h
clutter_config_defines = []
cdata = configuration_data()
if have_wayland
clutter_config_defines += [
'#define CLUTTER_HAS_WAYLAND_COMPOSITOR_SUPPORT 1',
]
cdata.set10('CLUTTER_HAS_WAYLAND_COMPOSITOR_SUPPORT', true)
endif
if have_x11
clutter_config_defines += [
'#define CLUTTER_WINDOWING_X11 "x11"',
'#define CLUTTER_INPUT_X11 "x11"',
'#define CLUTTER_WINDOWING_GLX "glx"',
]
cdata.set_quoted('CLUTTER_WINDOWING_X11', 'x11')
cdata.set_quoted('CLUTTER_INPUT_X11', 'x11')
cdata.set_quoted('CLUTTER_WINDOWING_GLX', 'glx')
endif
if have_native_backend
clutter_config_defines += [
'#define CLUTTER_WINDOWING_EGL "eglnative"',
'#define CLUTTER_INPUT_EVDEV "evdev"',
]
cdata.set_quoted('CLUTTER_WINDOWING_EGL', 'eglnative')
cdata.set_quoted('CLUTTER_INPUT_EVDEV', 'evdev')
endif
clutter_config_defines += [
'#define CLUTTER_INPUT_NULL "null"',
]
clutter_config_defines_string = ''
foreach clutter_config_define : clutter_config_defines
clutter_config_defines_string += clutter_config_define + '\n'
endforeach
cdata = configuration_data()
cdata.set('CLUTTER_CONFIG_DEFINES', clutter_config_defines_string)
cdata.set_quoted('CLUTTER_INPUT_NULL', 'null')
clutter_config_h = configure_file(
input: 'clutter-config.h.in',
@ -429,7 +410,6 @@ libmutter_clutter = shared_library(libmutter_clutter_name,
link_with: [
libmutter_cogl,
libmutter_cogl_pango,
libmutter_cogl_path,
],
install_rpath: pkglibdir,
install_dir: pkglibdir,

2
cogl/.gitignore vendored
View File

@ -36,8 +36,6 @@ cogl-egl-defines.h
cogl-enum-types.c
cogl-enum-types.h
cogl-gl-header.h
cogl-path-enum-types.c
cogl-path-enum-types.h
cogl-config.h
cogl-config.h.in
cogl-mutter-config.h

View File

@ -81,6 +81,7 @@ struct _CoglPangoDisplayListNode
GArray *rectangles;
/* A primitive representing those vertices */
CoglPrimitive *primitive;
guint has_color : 1;
} texture;
struct
@ -420,7 +421,9 @@ _cogl_pango_display_list_render (CoglFramebuffer *fb,
cogl_color_get_red_byte (&node->color),
cogl_color_get_green_byte (&node->color),
cogl_color_get_blue_byte (&node->color),
cogl_color_get_alpha_byte (color));
(cogl_color_get_alpha_byte (&node->color) *
cogl_color_get_alpha_byte (color) /
255));
else
draw_color = *color;
cogl_color_premultiply (&draw_color);

View File

@ -74,7 +74,7 @@ PangoFontMap *
cogl_pango_font_map_new (void)
{
PangoFontMap *fm = pango_cairo_font_map_new ();
CoglPangoFontMapPriv *priv = g_new0 (CoglPangoFontMapPriv, 1);
g_autofree CoglPangoFontMapPriv *priv = g_new0 (CoglPangoFontMapPriv, 1);
_COGL_GET_CONTEXT (context, NULL);
@ -85,7 +85,7 @@ cogl_pango_font_map_new (void)
* for now. */
g_object_set_qdata_full (G_OBJECT (fm),
cogl_pango_font_map_get_priv_key (),
priv,
g_steal_pointer (&priv),
free_priv);
return fm;

View File

@ -58,27 +58,29 @@ struct _CoglPangoGlyphCacheValue
/* This will be set to TRUE when the glyph atlas is reorganized
which means the glyph will need to be redrawn */
gboolean dirty;
guint dirty : 1;
/* Set to TRUE if the glyph has colors (eg. emoji) */
guint has_color : 1;
};
typedef void (* CoglPangoGlyphCacheDirtyFunc) (PangoFont *font,
PangoGlyph glyph,
CoglPangoGlyphCacheValue *value);
CoglPangoGlyphCache *
COGL_EXPORT CoglPangoGlyphCache *
cogl_pango_glyph_cache_new (CoglContext *ctx,
gboolean use_mipmapping);
void
COGL_EXPORT void
cogl_pango_glyph_cache_free (CoglPangoGlyphCache *cache);
CoglPangoGlyphCacheValue *
COGL_EXPORT CoglPangoGlyphCacheValue *
cogl_pango_glyph_cache_lookup (CoglPangoGlyphCache *cache,
gboolean create,
PangoFont *font,
PangoGlyph glyph);
void
COGL_EXPORT void
cogl_pango_glyph_cache_clear (CoglPangoGlyphCache *cache);
void

View File

@ -50,6 +50,7 @@
#include <pango/pangocairo.h>
#include <pango/pango-renderer.h>
#include <cairo.h>
#include <cairo-ft.h>
#include "cogl/cogl-debug.h"
#include "cogl/cogl-context-private.h"
@ -526,6 +527,24 @@ cogl_pango_renderer_get_cached_glyph (PangoRenderer *renderer,
create, font, glyph);
}
static gboolean
font_has_color_glyphs (const PangoFont *font)
{
cairo_scaled_font_t *scaled_font;
gboolean has_color = FALSE;
scaled_font = pango_cairo_font_get_scaled_font ((PangoCairoFont *) font);
if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_FT)
{
FT_Face ft_face = cairo_ft_scaled_font_lock_face (scaled_font);
has_color = (FT_HAS_COLOR (ft_face) != 0);
cairo_ft_scaled_font_unlock_face (scaled_font);
}
return has_color;
}
static void
cogl_pango_renderer_set_dirty_glyph (PangoFont *font,
PangoGlyph glyph,
@ -600,6 +619,8 @@ cogl_pango_renderer_set_dirty_glyph (PangoFont *font,
cairo_image_surface_get_data (surface));
cairo_surface_destroy (surface);
value->has_color = font_has_color_glyphs (font);
}
static void
@ -698,6 +719,7 @@ cogl_pango_renderer_set_color_for_part (PangoRenderer *renderer,
PangoRenderPart part)
{
PangoColor *pango_color = pango_renderer_get_color (renderer, part);
uint16_t alpha = pango_renderer_get_alpha (renderer, part);
CoglPangoRenderer *priv = COGL_PANGO_RENDERER (renderer);
if (pango_color)
@ -708,7 +730,7 @@ cogl_pango_renderer_set_color_for_part (PangoRenderer *renderer,
pango_color->red >> 8,
pango_color->green >> 8,
pango_color->blue >> 8,
0xff);
alpha ? alpha >> 8 : 0xff);
_cogl_pango_display_list_set_color_override (priv->display_list, &color);
}
@ -820,14 +842,13 @@ cogl_pango_renderer_draw_glyphs (PangoRenderer *renderer,
CoglPangoGlyphCacheValue *cache_value;
int i;
cogl_pango_renderer_set_color_for_part (renderer,
PANGO_RENDER_PART_FOREGROUND);
for (i = 0; i < glyphs->num_glyphs; i++)
{
PangoGlyphInfo *gi = glyphs->glyphs + i;
float x, y;
cogl_pango_renderer_set_color_for_part (renderer,
PANGO_RENDER_PART_FOREGROUND);
cogl_pango_renderer_get_device_units (renderer,
xi + gi->geometry.x_offset,
yi + gi->geometry.y_offset,
@ -884,6 +905,19 @@ cogl_pango_renderer_draw_glyphs (PangoRenderer *renderer,
x += (float)(cache_value->draw_x);
y += (float)(cache_value->draw_y);
/* Do not override color if the glyph/font provide its own */
if (cache_value->has_color)
{
CoglColor color;
uint16_t alpha;
alpha = pango_renderer_get_alpha (renderer,
PANGO_RENDER_PART_FOREGROUND);
cogl_color_init_from_4ub (&color, 0xff, 0xff, 0xff,
alpha ? alpha >> 8 : 0xff);
_cogl_pango_display_list_set_color_override (priv->display_list, &color);
}
cogl_pango_renderer_draw_glyph (priv, cache_value, x, y);
}
}

View File

@ -75,7 +75,7 @@ typedef PangoCairoFontMap CoglPangoFontMap;
*
* Since: 1.14
*/
PangoFontMap *
COGL_EXPORT PangoFontMap *
cogl_pango_font_map_new (void);
/**
@ -86,7 +86,7 @@ cogl_pango_font_map_new (void);
*
* Returns: (transfer full): the newly created context: free with g_object_unref().
*/
PangoContext *
COGL_EXPORT PangoContext *
cogl_pango_font_map_create_context (CoglPangoFontMap *font_map);
/**
@ -102,7 +102,7 @@ cogl_pango_font_map_create_context (CoglPangoFontMap *font_map);
*
* Since: 1.14
*/
void
COGL_EXPORT void
cogl_pango_font_map_set_resolution (CoglPangoFontMap *font_map,
double dpi);
@ -114,7 +114,7 @@ cogl_pango_font_map_set_resolution (CoglPangoFontMap *font_map,
*
* Since: 1.0
*/
void
COGL_EXPORT void
cogl_pango_font_map_clear_glyph_cache (CoglPangoFontMap *font_map);
/**
@ -129,7 +129,7 @@ cogl_pango_font_map_clear_glyph_cache (CoglPangoFontMap *font_map);
*
* Since: 1.0
*/
void
COGL_EXPORT void
cogl_pango_ensure_glyph_cache_for_layout (PangoLayout *layout);
/**
@ -142,7 +142,7 @@ cogl_pango_ensure_glyph_cache_for_layout (PangoLayout *layout);
*
* Since: 1.0
*/
void
COGL_EXPORT void
cogl_pango_font_map_set_use_mipmapping (CoglPangoFontMap *font_map,
gboolean value);
@ -157,7 +157,7 @@ cogl_pango_font_map_set_use_mipmapping (CoglPangoFontMap *font_map,
*
* Since: 1.0
*/
gboolean
COGL_EXPORT gboolean
cogl_pango_font_map_get_use_mipmapping (CoglPangoFontMap *font_map);
/**
@ -170,7 +170,7 @@ cogl_pango_font_map_get_use_mipmapping (CoglPangoFontMap *font_map);
*
* Since: 1.0
*/
PangoRenderer *
COGL_EXPORT PangoRenderer *
cogl_pango_font_map_get_renderer (CoglPangoFontMap *font_map);
/**
@ -187,7 +187,7 @@ cogl_pango_font_map_get_renderer (CoglPangoFontMap *font_map);
*
* Since: 1.14
*/
void
COGL_EXPORT void
cogl_pango_show_layout (CoglFramebuffer *framebuffer,
PangoLayout *layout,
float x,
@ -208,7 +208,7 @@ cogl_pango_show_layout (CoglFramebuffer *framebuffer,
*
* Since: 1.14
*/
void
COGL_EXPORT void
cogl_pango_show_layout_line (CoglFramebuffer *framebuffer,
PangoLayoutLine *line,
float x,
@ -227,7 +227,7 @@ cogl_pango_show_layout_line (CoglFramebuffer *framebuffer,
typedef struct _CoglPangoRenderer CoglPangoRenderer;
typedef struct _CoglPangoRendererClass CoglPangoRendererClass;
GType cogl_pango_renderer_get_type (void) G_GNUC_CONST;
COGL_EXPORT GType cogl_pango_renderer_get_type (void) G_GNUC_CONST;
G_END_DECLS

View File

@ -1,6 +0,0 @@
{
global:
cogl_pango_*;
local:
*;
};

View File

@ -20,19 +20,13 @@ cogl_pango_deps = [
libmutter_cogl_dep,
]
libmutter_cogl_pango_map = 'libmutter-cogl-pango.map'
libmutter_cogl_pango_link_args = [
'-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(),
libmutter_cogl_pango_map),
]
libmutter_cogl_pango = shared_library('mutter-cogl-pango-' + libmutter_api_version,
sources: [cogl_pango_sources, cogl_pango_public_headers],
version: '0.0.0',
soversion: 0,
c_args: cogl_c_args,
include_directories: [cogl_includepath, cogl_path_includepath],
link_depends: libmutter_cogl_pango_map,
link_args: libmutter_cogl_pango_link_args,
include_directories: [cogl_includepath],
gnu_symbol_visibility: 'hidden',
dependencies: [cogl_pango_deps],
install_rpath: pkglibdir,
install_dir: pkglibdir,

View File

@ -1,48 +0,0 @@
/*** BEGIN file-header ***/
#include "cogl-config.h"
/* We need to undefine this so that we will be sure to include
* cogl-path.h instead of cogl2-path.h when we include the framebuffer
* header. Otherwise it will include both headers and it won't
* compile. */
#undef COGL_ENABLE_EXPERIMENTAL_2_0_API
#include "cogl-path-enum-types.h"
/*** END file-header ***/
/*** BEGIN file-production ***/
/* enumerations from "@filename@" */
#include "@filename@"
/*** END file-production ***/
/*** BEGIN value-header ***/
GType
@enum_name@_get_type (void)
{
static volatile gsize g_enum_type_id__volatile = 0;
if (g_once_init_enter (&g_enum_type_id__volatile))
{
static const G@Type@Value values[] = {
/*** END value-header ***/
/*** BEGIN value-production ***/
{ @VALUENAME@, "@VALUENAME@", "@valuenick@" },
/*** END value-production ***/
/*** BEGIN value-tail ***/
{ 0, NULL, NULL }
};
GType g_enum_type_id;
g_enum_type_id =
g_@type@_register_static (g_intern_static_string ("@EnumName@"), values);
g_once_init_leave (&g_enum_type_id__volatile, g_enum_type_id);
}
return g_enum_type_id__volatile;
}
/*** END value-tail ***/

View File

@ -1,25 +0,0 @@
/*** BEGIN file-header ***/
#ifndef __COGL_PATH_ENUM_TYPES_H__
#define __COGL_PATH_ENUM_TYPES_H__
#include <glib-object.h>
G_BEGIN_DECLS
/*** END file-header ***/
/*** BEGIN file-production ***/
/* enumerations from "@basename@" */
/*** END file-production ***/
/*** BEGIN file-tail ***/
G_END_DECLS
#endif /* __COGL_PATH_ENUM_TYPES_H__ */
/*** END file-tail ***/
/*** BEGIN value-header ***/
GType @enum_name@_get_type (void) G_GNUC_CONST;
#define COGL_TYPE_@ENUMSHORT@ (@enum_name@_get_type())
/*** END value-header ***/

View File

@ -1,486 +0,0 @@
/*
* Cogl
*
* A Low Level GPU Graphics and Utilities API
*
* Copyright (C) 2008,2009,2013 Intel Corporation.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
#error "Only <cogl/cogl.h> can be included directly."
#endif
#ifndef __COGL_PATH_FUNCTIONS_H__
#define __COGL_PATH_FUNCTIONS_H__
#include <cogl/cogl-types.h>
#ifdef COGL_COMPILATION
#include "cogl-context.h"
#else
#include <cogl/cogl.h>
#endif
#include <glib-object.h>
G_BEGIN_DECLS
/**
* cogl_path_get_gtype:
*
* Returns: a #GType that can be used with the GLib type system.
*/
GType cogl_path_get_gtype (void);
#define cogl_path_new cogl2_path_new
/**
* cogl_path_new:
*
* Creates a new, empty path object. The default fill rule is
* %COGL_PATH_FILL_RULE_EVEN_ODD.
*
* Return value: A pointer to a newly allocated #CoglPath, which can
* be freed using cogl_object_unref().
*
* Since: 2.0
*/
CoglPath *
cogl_path_new (void);
/**
* cogl_path_copy:
* @path: A #CoglPath object
*
* Returns a new copy of the path in @path. The new path has a
* reference count of 1 so you should unref it with
* cogl_object_unref() if you no longer need it.
*
* Internally the path will share the data until one of the paths is
* modified so copying paths should be relatively cheap.
*
* Return value: (transfer full): a copy of the path in @path.
*
* Since: 2.0
*/
CoglPath *
cogl_path_copy (CoglPath *path);
/**
* cogl_is_path:
* @object: A #CoglObject
*
* Gets whether the given object references an existing path object.
*
* Return value: %TRUE if the object references a #CoglPath,
* %FALSE otherwise.
*
* Since: 2.0
*/
gboolean
cogl_is_path (void *object);
#define cogl_path_move_to cogl2_path_move_to
/**
* cogl_path_move_to:
* @x: X coordinate of the pen location to move to.
* @y: Y coordinate of the pen location to move to.
*
* Moves the pen to the given location. If there is an existing path
* this will start a new disjoint subpath.
*
* Since: 2.0
*/
void
cogl_path_move_to (CoglPath *path,
float x,
float y);
#define cogl_path_rel_move_to cogl2_path_rel_move_to
/**
* cogl_path_rel_move_to:
* @x: X offset from the current pen location to move the pen to.
* @y: Y offset from the current pen location to move the pen to.
*
* Moves the pen to the given offset relative to the current pen
* location. If there is an existing path this will start a new
* disjoint subpath.
*
* Since: 2.0
*/
void
cogl_path_rel_move_to (CoglPath *path,
float x,
float y);
#define cogl_path_line_to cogl2_path_line_to
/**
* cogl_path_line_to:
* @x: X coordinate of the end line vertex
* @y: Y coordinate of the end line vertex
*
* Adds a straight line segment to the current path that ends at the
* given coordinates.
*
* Since: 2.0
*/
void
cogl_path_line_to (CoglPath *path,
float x,
float y);
#define cogl_path_rel_line_to cogl2_path_rel_line_to
/**
* cogl_path_rel_line_to:
* @x: X offset from the current pen location of the end line vertex
* @y: Y offset from the current pen location of the end line vertex
*
* Adds a straight line segment to the current path that ends at the
* given coordinates relative to the current pen location.
*
* Since: 2.0
*/
void
cogl_path_rel_line_to (CoglPath *path,
float x,
float y);
#define cogl_path_arc cogl2_path_arc
/**
* cogl_path_arc:
* @center_x: X coordinate of the elliptical arc center
* @center_y: Y coordinate of the elliptical arc center
* @radius_x: X radius of the elliptical arc
* @radius_y: Y radius of the elliptical arc
* @angle_1: Angle in degrees at which the arc begin
* @angle_2: Angle in degrees at which the arc ends
*
* Adds an elliptical arc segment to the current path. A straight line
* segment will link the current pen location with the first vertex
* of the arc. If you perform a move_to to the arcs start just before
* drawing it you create a free standing arc.
*
* The angles are measured in degrees where 0° is in the direction of
* the positive X axis and 90° is in the direction of the positive Y
* axis. The angle of the arc begins at @angle_1 and heads towards
* @angle_2 (so if @angle_2 is less than @angle_1 it will decrease,
* otherwise it will increase).
*
* Since: 2.0
*/
void
cogl_path_arc (CoglPath *path,
float center_x,
float center_y,
float radius_x,
float radius_y,
float angle_1,
float angle_2);
#define cogl_path_curve_to cogl2_path_curve_to
/**
* cogl_path_curve_to:
* @x_1: X coordinate of the second bezier control point
* @y_1: Y coordinate of the second bezier control point
* @x_2: X coordinate of the third bezier control point
* @y_2: Y coordinate of the third bezier control point
* @x_3: X coordinate of the fourth bezier control point
* @y_3: Y coordinate of the fourth bezier control point
*
* Adds a cubic bezier curve segment to the current path with the given
* second, third and fourth control points and using current pen location
* as the first control point.
*
* Since: 2.0
*/
void
cogl_path_curve_to (CoglPath *path,
float x_1,
float y_1,
float x_2,
float y_2,
float x_3,
float y_3);
#define cogl_path_rel_curve_to cogl2_path_rel_curve_to
/**
* cogl_path_rel_curve_to:
* @x_1: X coordinate of the second bezier control point
* @y_1: Y coordinate of the second bezier control point
* @x_2: X coordinate of the third bezier control point
* @y_2: Y coordinate of the third bezier control point
* @x_3: X coordinate of the fourth bezier control point
* @y_3: Y coordinate of the fourth bezier control point
*
* Adds a cubic bezier curve segment to the current path with the given
* second, third and fourth control points and using current pen location
* as the first control point. The given coordinates are relative to the
* current pen location.
*
* Since: 2.0
*/
void
cogl_path_rel_curve_to (CoglPath *path,
float x_1,
float y_1,
float x_2,
float y_2,
float x_3,
float y_3);
#define cogl_path_close cogl2_path_close
/**
* cogl_path_close:
*
* Closes the path being constructed by adding a straight line segment
* to it that ends at the first vertex of the path.
*
* Since: 2.0
*/
void
cogl_path_close (CoglPath *path);
#define cogl_path_line cogl2_path_line
/**
* cogl_path_line:
* @x_1: X coordinate of the start line vertex
* @y_1: Y coordinate of the start line vertex
* @x_2: X coordinate of the end line vertex
* @y_2: Y coordinate of the end line vertex
*
* Constructs a straight line shape starting and ending at the given
* coordinates. If there is an existing path this will start a new
* disjoint sub-path.
*
* Since: 2.0
*/
void
cogl_path_line (CoglPath *path,
float x_1,
float y_1,
float x_2,
float y_2);
#define cogl_path_polyline cogl2_path_polyline
/**
* cogl_path_polyline:
* @coords: (in) (array) (transfer none): A pointer to the first element of an
* array of fixed-point values that specify the vertex coordinates.
* @num_points: The total number of vertices.
*
* Constructs a series of straight line segments, starting from the
* first given vertex coordinate. If there is an existing path this
* will start a new disjoint sub-path. Each subsequent segment starts
* where the previous one ended and ends at the next given vertex
* coordinate.
*
* The coords array must contain 2 * num_points values. The first value
* represents the X coordinate of the first vertex, the second value
* represents the Y coordinate of the first vertex, continuing in the same
* fashion for the rest of the vertices. (num_points - 1) segments will
* be constructed.
*
* Since: 2.0
*/
void
cogl_path_polyline (CoglPath *path,
const float *coords,
int num_points);
#define cogl_path_polygon cogl2_path_polygon
/**
* cogl_path_polygon:
* @coords: (in) (array) (transfer none): A pointer to the first element of
* an array of fixed-point values that specify the vertex coordinates.
* @num_points: The total number of vertices.
*
* Constructs a polygonal shape of the given number of vertices. If
* there is an existing path this will start a new disjoint sub-path.
*
* The coords array must contain 2 * num_points values. The first value
* represents the X coordinate of the first vertex, the second value
* represents the Y coordinate of the first vertex, continuing in the same
* fashion for the rest of the vertices.
*
* Since: 2.0
*/
void
cogl_path_polygon (CoglPath *path,
const float *coords,
int num_points);
#define cogl_path_rectangle cogl2_path_rectangle
/**
* cogl_path_rectangle:
* @x_1: X coordinate of the top-left corner.
* @y_1: Y coordinate of the top-left corner.
* @x_2: X coordinate of the bottom-right corner.
* @y_2: Y coordinate of the bottom-right corner.
*
* Constructs a rectangular shape at the given coordinates. If there
* is an existing path this will start a new disjoint sub-path.
*
* Since: 2.0
*/
void
cogl_path_rectangle (CoglPath *path,
float x_1,
float y_1,
float x_2,
float y_2);
#define cogl_path_ellipse cogl2_path_ellipse
/**
* cogl_path_ellipse:
* @center_x: X coordinate of the ellipse center
* @center_y: Y coordinate of the ellipse center
* @radius_x: X radius of the ellipse
* @radius_y: Y radius of the ellipse
*
* Constructs an ellipse shape. If there is an existing path this will
* start a new disjoint sub-path.
*
* Since: 2.0
*/
void
cogl_path_ellipse (CoglPath *path,
float center_x,
float center_y,
float radius_x,
float radius_y);
#define cogl_path_round_rectangle cogl2_path_round_rectangle
/**
* cogl_path_round_rectangle:
* @x_1: X coordinate of the top-left corner.
* @y_1: Y coordinate of the top-left corner.
* @x_2: X coordinate of the bottom-right corner.
* @y_2: Y coordinate of the bottom-right corner.
* @radius: Radius of the corner arcs.
* @arc_step: Angle increment resolution for subdivision of
* the corner arcs.
*
* Constructs a rectangular shape with rounded corners. If there is an
* existing path this will start a new disjoint sub-path.
*
* Since: 2.0
*/
void
cogl_path_round_rectangle (CoglPath *path,
float x_1,
float y_1,
float x_2,
float y_2,
float radius,
float arc_step);
#define cogl_path_set_fill_rule cogl2_path_set_fill_rule
/**
* cogl_path_set_fill_rule:
* @fill_rule: The new fill rule.
*
* Sets the fill rule of the current path to @fill_rule. This will
* affect how the path is filled when cogl_path_fill() is later
* called. Note that the fill rule state is attached to the path so
* calling cogl_get_path() will preserve the fill rule and calling
* cogl_path_new() will reset the fill rule back to the default.
*
* Since: 2.0
*/
void
cogl_path_set_fill_rule (CoglPath *path, CoglPathFillRule fill_rule);
#define cogl_path_get_fill_rule cogl2_path_get_fill_rule
/**
* cogl_path_get_fill_rule:
*
* Retrieves the fill rule set using cogl_path_set_fill_rule().
*
* Return value: the fill rule that is used for the current path.
*
* Since: 2.0
*/
CoglPathFillRule
cogl_path_get_fill_rule (CoglPath *path);
/**
* cogl_framebuffer_fill_path:
* @framebuffer: A #CoglFramebuffer
* @pipeline: A #CoglPipeline to render with
* @path: The #CoglPath to fill
*
* Fills the interior of the path using the fragment operations
* defined by the pipeline.
*
* The interior of the shape is determined using the fill rule of the
* path. See %CoglPathFillRule for details.
*
* <note>The result of referencing sliced textures in your current
* pipeline when filling a path are undefined. You should pass
* the %COGL_TEXTURE_NO_SLICING flag when loading any texture you will
* use while filling a path.</note>
*
* Stability: unstable
*/
void
cogl_framebuffer_fill_path (CoglFramebuffer *framebuffer,
CoglPipeline *pipeline,
CoglPath *path);
/**
* cogl_framebuffer_stroke_path:
* @framebuffer: A #CoglFramebuffer
* @pipeline: A #CoglPipeline to render with
* @path: The #CoglPath to stroke
*
* Strokes the edge of the path using the fragment operations defined
* by the pipeline. The stroke line will have a width of 1 pixel
* regardless of the current transformation matrix.
*
* Stability: unstable
*/
void
cogl_framebuffer_stroke_path (CoglFramebuffer *framebuffer,
CoglPipeline *pipeline,
CoglPath *path);
/**
* cogl_framebuffer_push_path_clip:
* @framebuffer: A #CoglFramebuffer pointer
* @path: The path to clip with.
*
* Sets a new clipping area using the silhouette of the specified,
* filled @path. The clipping area is intersected with the previous
* clipping area. To restore the previous clipping area, call
* cogl_framebuffer_pop_clip().
*
* Since: 1.0
* Stability: unstable
*/
void
cogl_framebuffer_push_path_clip (CoglFramebuffer *framebuffer,
CoglPath *path);
G_END_DECLS
#endif /* __COGL_PATH_FUNCTIONS_H__ */

View File

@ -1,126 +0,0 @@
/*
* Cogl
*
* A Low Level GPU Graphics and Utilities API
*
* Copyright (C) 2010 Intel Corporation.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
#ifndef __COGL_PATH_PRIVATE_H
#define __COGL_PATH_PRIVATE_H
#include "cogl-object.h"
#include "cogl-attribute-private.h"
typedef struct _floatVec2
{
float x;
float y;
} floatVec2;
typedef struct _CoglPathNode
{
float x;
float y;
unsigned int path_size;
} CoglPathNode;
typedef struct _CoglBezQuad
{
floatVec2 p1;
floatVec2 p2;
floatVec2 p3;
} CoglBezQuad;
typedef struct _CoglBezCubic
{
floatVec2 p1;
floatVec2 p2;
floatVec2 p3;
floatVec2 p4;
} CoglBezCubic;
typedef struct _CoglPathData CoglPathData;
struct _CoglPath
{
CoglObject _parent;
CoglPathData *data;
};
#define COGL_PATH_N_ATTRIBUTES 2
struct _CoglPathData
{
unsigned int ref_count;
CoglContext *context;
CoglPathFillRule fill_rule;
GArray *path_nodes;
floatVec2 path_start;
floatVec2 path_pen;
unsigned int last_path;
floatVec2 path_nodes_min;
floatVec2 path_nodes_max;
CoglAttributeBuffer *fill_attribute_buffer;
CoglIndices *fill_vbo_indices;
unsigned int fill_vbo_n_indices;
CoglAttribute *fill_attributes[COGL_PATH_N_ATTRIBUTES + 1];
CoglPrimitive *fill_primitive;
CoglAttributeBuffer *stroke_attribute_buffer;
CoglAttribute **stroke_attributes;
unsigned int stroke_n_attributes;
/* This is used as an optimisation for when the path contains a
single contour specified using cogl2_path_rectangle. Cogl is more
optimised to handle rectangles than paths so we can detect this
case and divert to the journal or a rectangle clip. If it is TRUE
then the entire path can be described by calling
_cogl_path_get_bounds */
gboolean is_rectangle;
};
void
_cogl_add_path_to_stencil_buffer (CoglPath *path,
gboolean merge,
gboolean need_clear);
void
_cogl_path_get_bounds (CoglPath *path,
float *min_x,
float *min_y,
float *max_x,
float *max_y);
gboolean
_cogl_path_is_rectangle (CoglPath *path);
#endif /* __COGL_PATH_PRIVATE_H */

View File

@ -1,86 +0,0 @@
/*
* Cogl
*
* A Low Level GPU Graphics and Utilities API
*
* Copyright (C) 2008,2009,2013 Intel Corporation.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
#error "Only <cogl/cogl.h> can be included directly."
#endif
#ifndef __COGL_PATH_TYPES_H__
#define __COGL_PATH_TYPES_H__
#include <cogl/cogl-types.h>
G_BEGIN_DECLS
typedef struct _CoglPath CoglPath;
/**
* CoglPathFillRule:
* @COGL_PATH_FILL_RULE_NON_ZERO: Each time the line crosses an edge of
* the path from left to right one is added to a counter and each time
* it crosses from right to left the counter is decremented. If the
* counter is non-zero then the point will be filled. See <xref
* linkend="fill-rule-non-zero"/>.
* @COGL_PATH_FILL_RULE_EVEN_ODD: If the line crosses an edge of the
* path an odd number of times then the point will filled, otherwise
* it won't. See <xref linkend="fill-rule-even-odd"/>.
*
* #CoglPathFillRule is used to determine how a path is filled. There
* are two options - 'non-zero' and 'even-odd'. To work out whether any
* point will be filled imagine drawing an infinetely long line in any
* direction from that point. The number of times and the direction
* that the edges of the path crosses this line determines whether the
* line is filled as described below. Any open sub paths are treated
* as if there was an extra line joining the first point and the last
* point.
*
* The default fill rule when creating a path is %COGL_PATH_FILL_RULE_EVEN_ODD.
*
* <figure id="fill-rule-non-zero">
* <title>Example of filling various paths using the non-zero rule</title>
* <graphic fileref="fill-rule-non-zero.png" format="PNG"/>
* </figure>
*
* <figure id="fill-rule-even-odd">
* <title>Example of filling various paths using the even-odd rule</title>
* <graphic fileref="fill-rule-even-odd.png" format="PNG"/>
* </figure>
*
* Since: 1.4
*/
typedef enum
{
COGL_PATH_FILL_RULE_NON_ZERO,
COGL_PATH_FILL_RULE_EVEN_ODD
} CoglPathFillRule;
G_END_DECLS
#endif /* __COGL_PATH_TYPES_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -1,60 +0,0 @@
/*
* Cogl
*
* A Low Level GPU Graphics and Utilities API
*
* Copyright (C) 2008,2009,2013 Intel Corporation.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
#ifndef __COGL_PATH_H__
#define __COGL_PATH_H__
/**
* SECTION:cogl-paths
* @short_description: Functions for constructing and drawing 2D paths.
*
* There are two levels on which drawing with cogl-paths can be used.
* The highest level functions construct various simple primitive
* shapes to be either filled or stroked. Using a lower-level set of
* functions more complex and arbitrary paths can be constructed by
* concatenating straight line, bezier curve and arc segments.
*
* When constructing arbitrary paths, the current pen location is
* initialized using the move_to command. The subsequent path segments
* implicitly use the last pen location as their first vertex and move
* the pen location to the last vertex they produce at the end. Also
* there are special versions of functions that allow specifying the
* vertices of the path segments relative to the last pen location
* rather then in the absolute coordinates.
*/
#include <cogl/cogl-defines.h>
#include <cogl-path/cogl-path-enum-types.h>
#include <cogl-path/cogl-path-types.h>
#include <cogl-path/cogl-path-functions.h>
#endif /* __COGL_PATH_H__ */

View File

@ -1,59 +0,0 @@
/* cogl1-path-functions.h */
cogl_clip_push_from_path
cogl_clip_push_from_path_preserve
cogl_get_path
cogl_is_path
cogl_path_arc
cogl_path_close
cogl_path_copy
cogl_path_curve_to
cogl_path_ellipse
cogl_path_fill
cogl_path_fill_preserve
cogl_path_get_fill_rule
#ifdef COGL_HAS_GTYPE_SUPPORT
cogl_path_get_gtype
#endif
cogl_path_line
cogl_path_line_to
cogl_path_move_to
cogl_path_new
cogl_path_polygon
cogl_path_polyline
cogl_path_rectangle
cogl_path_rel_curve_to
cogl_path_rel_line_to
cogl_path_rel_move_to
cogl_path_round_rectangle
cogl_path_set_fill_rule
cogl_path_stroke
cogl_path_stroke_preserve
cogl_set_path
/* cogl2-path-functions.h */
cogl_framebuffer_fill_path
cogl_framebuffer_push_path_clip
cogl_framebuffer_stroke_path
cogl2_clip_push_from_path
cogl2_path_arc
cogl2_path_close
cogl2_path_curve_to
cogl2_path_ellipse
cogl2_path_fill
cogl2_path_get_fill_rule
cogl2_path_line
cogl2_path_line_to
cogl2_path_move_to
cogl2_path_new
cogl2_path_polygon
cogl2_path_polyline
cogl2_path_rectangle
cogl2_path_rel_curve_to
cogl2_path_rel_line_to
cogl2_path_rel_move_to
cogl2_path_round_rectangle
cogl2_path_set_fill_rule
cogl2_path_stroke
/* cogl-path-enums.h-contents may change as header is generated */
cogl_path_fill_rule_get_type

View File

@ -1,17 +0,0 @@
{
global:
cogl_framebuffer_*;
cogl_path_*;
cogl_is_*;
cogl_clip_*;
cogl_get_*;
cogl_set_*;
cogl2_framebuffer_*;
cogl2_path_*;
cogl2_is_*;
cogl2_clip_*;
cogl2_get_*;
cogl2_set_*;
local:
*;
};

View File

@ -1,93 +0,0 @@
cogl_path_includesubdir = join_paths(cogl_includesubdir, 'cogl-path')
cogl_path_includedir = join_paths(cogl_includedir, 'cogl-path')
cogl_path_public_headers = [
'cogl-path.h',
'cogl-path-functions.h',
'cogl-path-types.h',
]
cogl_path_sources = [
'cogl-path.c',
'cogl-path-private.h',
'tesselator/dict-list.h',
'tesselator/dict.c',
'tesselator/dict.h',
'tesselator/geom.c',
'tesselator/geom.h',
'tesselator/gluos.h',
'tesselator/memalloc.h',
'tesselator/mesh.c',
'tesselator/mesh.h',
'tesselator/normal.c',
'tesselator/normal.h',
'tesselator/priorityq-heap.h',
'tesselator/priorityq-sort.h',
'tesselator/priorityq.c',
'tesselator/priorityq.h',
'tesselator/render.c',
'tesselator/render.h',
'tesselator/sweep.c',
'tesselator/sweep.h',
'tesselator/tess.c',
'tesselator/tess.h',
'tesselator/tesselator.h',
'tesselator/tessmono.c',
'tesselator/tessmono.h',
]
cogl_path_includepath = include_directories('.')
libmutter_cogl_path_enum_types = gnome.mkenums('cogl-path-enum-types',
sources: 'cogl-path-types.h',
c_template: 'cogl-path-enum-types.c.in',
h_template: 'cogl-path-enum-types.h.in',
install_dir: cogl_path_includedir,
install_header: true,
)
libmutter_cogl_path_enum_types_h = libmutter_cogl_path_enum_types[1]
cogl_path_sources += libmutter_cogl_path_enum_types
cogl_path_c_args = [
cogl_c_args,
]
libmutter_cogl_path_map = 'libmutter-cogl-path.map'
libmutter_cogl_path_link_args = [
'-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(),
libmutter_cogl_path_map),
]
libmutter_cogl_path = shared_library('mutter-cogl-path-' + libmutter_api_version,
sources: [cogl_path_sources, cogl_path_public_headers],
version: '0.0.0',
soversion: 0,
c_args: cogl_path_c_args,
include_directories: [cogl_includepath, cogl_path_includepath],
link_depends: libmutter_cogl_path_map,
link_args: libmutter_cogl_path_link_args,
dependencies: libmutter_cogl_dep,
install_rpath: pkglibdir,
install_dir: pkglibdir,
install: true,
)
libmutter_cogl_path_dep = declare_dependency(
sources: [libmutter_cogl_path_enum_types_h],
link_with: libmutter_cogl_path
)
install_headers(cogl_path_public_headers,
subdir: cogl_path_includesubdir)
pkg.generate(libmutter_cogl_path,
name: 'CoglPath',
filebase: 'mutter-cogl-path-' + libmutter_api_version,
description: 'A 2D path drawing library for Cogl in mutter',
subdirs: join_paths(pkgname, 'cogl'),
requires: [cogl_pkg_deps, libmutter_cogl_name],
version: meson.project_version(),
variables: [
'apiversion=' + libmutter_api_version,
],
install_dir: pcdir,
)

View File

@ -1,13 +0,0 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
apiversion=@LIBMUTTER_API_VERSION@
libdir=@libdir@/mutter-${apiversion}
includedir=@includedir@/mutter-${apiversion}
requires=@COGL_PKG_REQUIRES@ mutter-cogl-${apiversion}
Name: Cogl
Description: A 2D path drawing library for Cogl
Version: @MUTTER_VERSION@
Libs: -L${libdir} -lmutter-cogl-path-${apiversion}
Cflags: -I${includedir}/cogl
Requires: ${requires}

View File

@ -1,446 +0,0 @@
/*
*/
General Polygon Tesselation
---------------------------
This note describes a tesselator for polygons consisting of one or
more closed contours. It is backward-compatible with the current
OpenGL Utilities tesselator, and is intended to replace it. Here is
a summary of the major differences:
- input contours can be intersecting, self-intersecting, or degenerate.
- supports a choice of several winding rules for determining which parts
of the polygon are on the "interior". This makes it possible to do
CSG operations on polygons.
- boundary extraction: instead of tesselating the polygon, returns a
set of closed contours which separate the interior from the exterior.
- returns the output as a small number of triangle fans and strips,
rather than a list of independent triangles (when possible).
- output is available as an explicit mesh (a quad-edge structure),
in addition to the normal callback interface.
- the algorithm used is extremely robust.
The interface
-------------
The tesselator state is maintained in a "tesselator object".
These are allocated and destroyed using
GLUtesselator *gluNewTess( void );
void gluDeleteTess( GLUtesselator *tess );
Several tesselator objects may be used simultaneously.
Inputs
------
The input contours are specified with the following routines:
void gluTessBeginPolygon( GLUtesselator *tess );
void gluTessBeginContour( GLUtesselator *tess );
void gluTessVertex( GLUtesselator *tess, GLUcoord coords[3], void *data );
void gluTessEndContour( GLUtesselator *tess );
void gluTessEndPolygon( GLUtesselator *tess );
Within each BeginPolygon/EndPolygon pair, there can be zero or more
calls to BeginContour/EndContour. Within each contour, there are zero
or more calls to gluTessVertex(). The vertices specify a closed
contour (the last vertex of each contour is automatically linked to
the first).
"coords" give the coordinates of the vertex in 3-space. For useful
results, all vertices should lie in some plane, since the vertices
are projected onto a plane before tesselation. "data" is a pointer
to a user-defined vertex structure, which typically contains other
information such as color, texture coordinates, normal, etc. It is
used to refer to the vertex during rendering.
The library can be compiled in single- or double-precision; the type
GLUcoord represents either "float" or "double" accordingly. The GLU
version will be available in double-precision only. Compile with
GLU_TESS_API_FLOAT defined to get the single-precision version.
When EndPolygon is called, the tesselation algorithm determines
which regions are interior to the given contours, according to one
of several "winding rules" described below. The interior regions
are then tesselated, and the output is provided as callbacks.
Rendering Callbacks
-------------------
Callbacks are specified by the client using
void gluTessCallback( GLUtesselator *tess, GLenum which, void (*fn)());
If "fn" is NULL, any previously defined callback is discarded.
The callbacks used to provide output are: /* which == */
void begin( GLenum type ); /* GLU_TESS_BEGIN */
void edgeFlag( GLboolean flag ); /* GLU_TESS_EDGE_FLAG */
void vertex( void *data ); /* GLU_TESS_VERTEX */
void end( void ); /* GLU_TESS_END */
Any of the callbacks may be left undefined; if so, the corresponding
information will not be supplied during rendering.
The "begin" callback indicates the start of a primitive; type is one
of GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, or GL_TRIANGLES (but see the
notes on "boundary extraction" below).
It is followed by any number of "vertex" callbacks, which supply the
vertices in the same order as expected by the corresponding glBegin()
call. After the last vertex of a given primitive, there is a callback
to "end".
If the "edgeFlag" callback is provided, no triangle fans or strips
will be used. When edgeFlag is called, if "flag" is GL_TRUE then each
vertex which follows begins an edge which lies on the polygon boundary
(ie. an edge which separates an interior region from an exterior one).
If "flag" is GL_FALSE, each vertex which follows begins an edge which lies
in the polygon interior. "edgeFlag" will be called before the first
call to "vertex".
Other Callbacks
---------------
void mesh( GLUmesh *mesh ); /* GLU_TESS_MESH */
- Returns an explicit mesh, represented using the quad-edge structure
(Guibas/Stolfi '85). Other implementations of this interface might
use a different mesh structure, so this is available only only as an
SGI extension. When the mesh is no longer needed, it should be freed
using
void gluDeleteMesh( GLUmesh *mesh );
There is a brief description of this data structure in the include
file "mesh.h". For the full details, see L. Guibas and J. Stolfi,
Primitives for the manipulation of general subdivisions and the
computation of Voronoi diagrams, ACM Transactions on Graphics,
4(2):74-123, April 1985. For an introduction, see the course notes
for CS348a, "Mathematical Foundations of Computer Graphics",
available at the Stanford bookstore (and taught during the fall
quarter).
void error( GLenum errno ); /* GLU_TESS_ERROR */
- errno is one of GLU_TESS_MISSING_BEGIN_POLYGON,
GLU_TESS_MISSING_END_POLYGON,
GLU_TESS_MISSING_BEGIN_CONTOUR,
GLU_TESS_MISSING_END_CONTOUR,
GLU_TESS_COORD_TOO_LARGE,
GLU_TESS_NEED_COMBINE_CALLBACK
The first four are obvious. The interface recovers from these
errors by inserting the missing call(s).
GLU_TESS_COORD_TOO_LARGE says that some vertex coordinate exceeded
the predefined constant GLU_TESS_MAX_COORD in absolute value, and
that the value has been clamped. (Coordinate values must be small
enough so that two can be multiplied together without overflow.)
GLU_TESS_NEED_COMBINE_CALLBACK says that the algorithm detected an
intersection between two edges in the input data, and the "combine"
callback (below) was not provided. No output will be generated.
void combine( GLUcoord coords[3], void *data[4], /* GLU_TESS_COMBINE */
GLUcoord weight[4], void **outData );
- When the algorithm detects an intersection, or wishes to merge
features, it needs to create a new vertex. The vertex is defined
as a linear combination of up to 4 existing vertices, referenced
by data[0..3]. The coefficients of the linear combination are
given by weight[0..3]; these weights always sum to 1.0. All vertex
pointers are valid even when some of the weights are zero.
"coords" gives the location of the new vertex.
The user must allocate another vertex, interpolate parameters
using "data" and "weights", and return the new vertex pointer in
"outData". This handle is supplied during rendering callbacks.
For example, if the polygon lies in an arbitrary plane in 3-space,
and we associate a color with each vertex, the combine callback might
look like this:
void myCombine( GLUcoord coords[3], VERTEX *d[4],
GLUcoord w[4], VERTEX **dataOut )
{
VERTEX *new = new_vertex();
new->x = coords[0];
new->y = coords[1];
new->z = coords[2];
new->r = w[0]*d[0]->r + w[1]*d[1]->r + w[2]*d[2]->r + w[3]*d[3]->r;
new->g = w[0]*d[0]->g + w[1]*d[1]->g + w[2]*d[2]->g + w[3]*d[3]->g;
new->b = w[0]*d[0]->b + w[1]*d[1]->b + w[2]*d[2]->b + w[3]*d[3]->b;
new->a = w[0]*d[0]->a + w[1]*d[1]->a + w[2]*d[2]->a + w[3]*d[3]->a;
*dataOut = new;
}
If the algorithm detects an intersection, then the "combine" callback
must be defined, and must write a non-NULL pointer into "dataOut".
Otherwise the GLU_TESS_NEED_COMBINE_CALLBACK error occurs, and no
output is generated. This is the only error that can occur during
tesselation and rendering.
Control over Tesselation
------------------------
void gluTessProperty( GLUtesselator *tess, GLenum which, GLUcoord value );
Properties defined:
- GLU_TESS_WINDING_RULE. Possible values:
GLU_TESS_WINDING_ODD
GLU_TESS_WINDING_NONZERO
GLU_TESS_WINDING_POSITIVE
GLU_TESS_WINDING_NEGATIVE
GLU_TESS_WINDING_ABS_GEQ_TWO
The input contours parition the plane into regions. A winding
rule determines which of these regions are inside the polygon.
For a single contour C, the winding number of a point x is simply
the signed number of revolutions we make around x as we travel
once around C (where CCW is positive). When there are several
contours, the individual winding numbers are summed. This
procedure associates a signed integer value with each point x in
the plane. Note that the winding number is the same for all
points in a single region.
The winding rule classifies a region as "inside" if its winding
number belongs to the chosen category (odd, nonzero, positive,
negative, or absolute value of at least two). The current GLU
tesselator implements the "odd" rule. The "nonzero" rule is another
common way to define the interior. The other three rules are
useful for polygon CSG operations (see below).
- GLU_TESS_BOUNDARY_ONLY. Values: TRUE (non-zero) or FALSE (zero).
If TRUE, returns a set of closed contours which separate the
polygon interior and exterior (rather than a tesselation).
Exterior contours are oriented CCW with respect to the normal,
interior contours are oriented CW. The GLU_TESS_BEGIN callback
uses the type GL_LINE_LOOP for each contour.
- GLU_TESS_TOLERANCE. Value: a real number between 0.0 and 1.0.
This specifies a tolerance for merging features to reduce the size
of the output. For example, two vertices which are very close to
each other might be replaced by a single vertex. The tolerance
is multiplied by the largest coordinate magnitude of any input vertex;
this specifies the maximum distance that any feature can move as the
result of a single merge operation. If a single feature takes part
in several merge operations, the total distance moved could be larger.
Feature merging is completely optional; the tolerance is only a hint.
The implementation is free to merge in some cases and not in others,
or to never merge features at all. The default tolerance is zero.
The current implementation merges vertices only if they are exactly
coincident, regardless of the current tolerance. A vertex is
spliced into an edge only if the implementation is unable to
distinguish which side of the edge the vertex lies on.
Two edges are merged only when both endpoints are identical.
void gluTessNormal( GLUtesselator *tess,
GLUcoord x, GLUcoord y, GLUcoord z )
- Lets the user supply the polygon normal, if known. All input data
is projected into a plane perpendicular to the normal before
tesselation. All output triangles are oriented CCW with
respect to the normal (CW orientation can be obtained by
reversing the sign of the supplied normal). For example, if
you know that all polygons lie in the x-y plane, call
"gluTessNormal(tess, 0.0, 0.0, 1.0)" before rendering any polygons.
- If the supplied normal is (0,0,0) (the default value), the
normal is determined as follows. The direction of the normal,
up to its sign, is found by fitting a plane to the vertices,
without regard to how the vertices are connected. It is
expected that the input data lies approximately in plane;
otherwise projection perpendicular to the computed normal may
substantially change the geometry. The sign of the normal is
chosen so that the sum of the signed areas of all input contours
is non-negative (where a CCW contour has positive area).
- The supplied normal persists until it is changed by another
call to gluTessNormal.
Backward compatibility with the GLU tesselator
----------------------------------------------
The preferred interface is the one described above. The following
routines are obsolete, and are provided only for backward compatibility:
typedef GLUtesselator GLUtriangulatorObj; /* obsolete name */
void gluBeginPolygon( GLUtesselator *tess );
void gluNextContour( GLUtesselator *tess, GLenum type );
void gluEndPolygon( GLUtesselator *tess );
"type" is one of GLU_EXTERIOR, GLU_INTERIOR, GLU_CCW, GLU_CW, or
GLU_UNKNOWN. It is ignored by the current GLU tesselator.
GLU_BEGIN, GLU_VERTEX, GLU_END, GLU_ERROR, and GLU_EDGE_FLAG are defined
as synonyms for GLU_TESS_BEGIN, GLU_TESS_VERTEX, GLU_TESS_END,
GLU_TESS_ERROR, and GLU_TESS_EDGE_FLAG.
Polygon CSG operations
----------------------
The features of the tesselator make it easy to find the union, difference,
or intersection of several polygons.
First, assume that each polygon is defined so that the winding number
is 0 for each exterior region, and 1 for each interior region. Under
this model, CCW contours define the outer boundary of the polygon, and
CW contours define holes. Contours may be nested, but a nested
contour must be oriented oppositely from the contour that contains it.
If the original polygons do not satisfy this description, they can be
converted to this form by first running the tesselator with the
GLU_TESS_BOUNDARY_ONLY property turned on. This returns a list of
contours satisfying the restriction above. By allocating two
tesselator objects, the callbacks from one tesselator can be fed
directly to the input of another.
Given two or more polygons of the form above, CSG operations can be
implemented as follows:
Union
Draw all the input contours as a single polygon. The winding number
of each resulting region is the number of original polygons
which cover it. The union can be extracted using the
GLU_TESS_WINDING_NONZERO or GLU_TESS_WINDING_POSITIVE winding rules.
Note that with the nonzero rule, we would get the same result if
all contour orientations were reversed.
Intersection (two polygons at a time only)
Draw a single polygon using the contours from both input polygons.
Extract the result using GLU_TESS_WINDING_ABS_GEQ_TWO. (Since this
winding rule looks at the absolute value, reversing all contour
orientations does not change the result.)
Difference
Suppose we want to compute A \ (B union C union D). Draw a single
polygon consisting of the unmodified contours from A, followed by
the contours of B,C,D with the vertex order reversed (this changes
the winding number of the interior regions to -1). To extract the
result, use the GLU_TESS_WINDING_POSITIVE rule.
If B,C,D are the result of a GLU_TESS_BOUNDARY_ONLY call, an
alternative to reversing the vertex order is to reverse the sign of
the supplied normal. For example in the x-y plane, call
gluTessNormal( tess, 0.0, 0.0, -1.0 ).
Performance
-----------
The tesselator is not intended for immediate-mode rendering; when
possible the output should be cached in a user structure or display
list. General polygon tesselation is an inherently difficult problem,
especially given the goal of extreme robustness.
The implementation makes an effort to output a small number of fans
and strips; this should improve the rendering performance when the
output is used in a display list.
Single-contour input polygons are first tested to see whether they can
be rendered as a triangle fan with respect to the first vertex (to
avoid running the full decomposition algorithm on convex polygons).
Non-convex polygons may be rendered by this "fast path" as well, if
the algorithm gets lucky in its choice of a starting vertex.
For best performance follow these guidelines:
- supply the polygon normal, if available, using gluTessNormal().
This represents about 10% of the computation time. For example,
if all polygons lie in the x-y plane, use gluTessNormal(tess,0,0,1).
- render many polygons using the same tesselator object, rather than
allocating a new tesselator for each one. (In a multi-threaded,
multi-processor environment you may get better performance using
several tesselators.)
Comparison with the GLU tesselator
----------------------------------
On polygons which make it through the "fast path", the tesselator is
3 to 5 times faster than the GLU tesselator.
On polygons which don't make it through the fast path (but which don't
have self-intersections or degeneracies), it is about 2 times slower.
On polygons with self-intersections or degeneraces, there is nothing
to compare against.
The new tesselator generates many more fans and strips, reducing the
number of vertices that need to be sent to the hardware.
Key to the statistics:
vert number of input vertices on all contours
cntr number of input contours
tri number of triangles in all output primitives
strip number of triangle strips
fan number of triangle fans
ind number of independent triangles
ms number of milliseconds for tesselation
(on a 150MHz R4400 Indy)
Convex polygon examples:
New: 3 vert, 1 cntr, 1 tri, 0 strip, 0 fan, 1 ind, 0.0459 ms
Old: 3 vert, 1 cntr, 1 tri, 0 strip, 0 fan, 1 ind, 0.149 ms
New: 4 vert, 1 cntr, 2 tri, 0 strip, 1 fan, 0 ind, 0.0459 ms
Old: 4 vert, 1 cntr, 2 tri, 0 strip, 0 fan, 2 ind, 0.161 ms
New: 36 vert, 1 cntr, 34 tri, 0 strip, 1 fan, 0 ind, 0.153 ms
Old: 36 vert, 1 cntr, 34 tri, 0 strip, 0 fan, 34 ind, 0.621 ms
Concave single-contour polygons:
New: 5 vert, 1 cntr, 3 tri, 0 strip, 1 fan, 0 ind, 0.052 ms
Old: 5 vert, 1 cntr, 3 tri, 0 strip, 0 fan, 3 ind, 0.252 ms
New: 19 vert, 1 cntr, 17 tri, 2 strip, 2 fan, 1 ind, 0.911 ms
Old: 19 vert, 1 cntr, 17 tri, 0 strip, 0 fan, 17 ind, 0.529 ms
New: 151 vert, 1 cntr, 149 tri, 13 strip, 18 fan, 3 ind, 6.82 ms
Old: 151 vert, 1 cntr, 149 tri, 0 strip, 3 fan, 143 ind, 2.7 ms
New: 574 vert, 1 cntr, 572 tri, 59 strip, 54 fan, 11 ind, 26.6 ms
Old: 574 vert, 1 cntr, 572 tri, 0 strip, 31 fan, 499 ind, 12.4 ms
Multiple contours, but no intersections:
New: 7 vert, 2 cntr, 7 tri, 1 strip, 0 fan, 0 ind, 0.527 ms
Old: 7 vert, 2 cntr, 7 tri, 0 strip, 0 fan, 7 ind, 0.274 ms
New: 81 vert, 6 cntr, 89 tri, 9 strip, 7 fan, 6 ind, 3.88 ms
Old: 81 vert, 6 cntr, 89 tri, 0 strip, 13 fan, 61 ind, 2.2 ms
New: 391 vert, 19 cntr, 413 tri, 37 strip, 32 fan, 26 ind, 20.2 ms
Old: 391 vert, 19 cntr, 413 tri, 0 strip, 25 fan, 363 ind, 8.68 ms
Self-intersecting and degenerate examples:
Bowtie: 4 vert, 1 cntr, 2 tri, 0 strip, 0 fan, 2 ind, 0.483 ms
Star: 5 vert, 1 cntr, 5 tri, 0 strip, 0 fan, 5 ind, 0.91 ms
Random: 24 vert, 7 cntr, 46 tri, 2 strip, 12 fan, 7 ind, 5.32 ms
Font: 333 vert, 2 cntr, 331 tri, 32 strip, 16 fan, 3 ind, 14.1 ms
: 167 vert, 35 cntr, 254 tri, 8 strip, 56 fan, 52 ind, 46.3 ms
: 78 vert, 1 cntr, 2675 tri, 148 strip, 207 fan, 180 ind, 243 ms
: 12480 vert, 2 cntr, 12478 tri, 736 strip,1275 fan, 5 ind, 1010 ms

View File

@ -1,100 +0,0 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#ifndef __dict_list_h_
#define __dict_list_h_
/* Use #define's so that another heap implementation can use this one */
#define DictKey DictListKey
#define Dict DictList
#define DictNode DictListNode
#define dictNewDict(frame,leq) __gl_dictListNewDict(frame,leq)
#define dictDeleteDict(dict) __gl_dictListDeleteDict(dict)
#define dictSearch(dict,key) __gl_dictListSearch(dict,key)
#define dictInsert(dict,key) __gl_dictListInsert(dict,key)
#define dictInsertBefore(dict,node,key) __gl_dictListInsertBefore(dict,node,key)
#define dictDelete(dict,node) __gl_dictListDelete(dict,node)
#define dictKey(n) __gl_dictListKey(n)
#define dictSucc(n) __gl_dictListSucc(n)
#define dictPred(n) __gl_dictListPred(n)
#define dictMin(d) __gl_dictListMin(d)
#define dictMax(d) __gl_dictListMax(d)
typedef void *DictKey;
typedef struct Dict Dict;
typedef struct DictNode DictNode;
Dict *dictNewDict(
void *frame,
int (*leq)(void *frame, DictKey key1, DictKey key2) );
void dictDeleteDict( Dict *dict );
/* Search returns the node with the smallest key greater than or equal
* to the given key. If there is no such key, returns a node whose
* key is NULL. Similarly, Succ(Max(d)) has a NULL key, etc.
*/
DictNode *dictSearch( Dict *dict, DictKey key );
DictNode *dictInsertBefore( Dict *dict, DictNode *node, DictKey key );
void dictDelete( Dict *dict, DictNode *node );
#define __gl_dictListKey(n) ((n)->key)
#define __gl_dictListSucc(n) ((n)->next)
#define __gl_dictListPred(n) ((n)->prev)
#define __gl_dictListMin(d) ((d)->head.next)
#define __gl_dictListMax(d) ((d)->head.prev)
#define __gl_dictListInsert(d,k) (dictInsertBefore((d),&(d)->head,(k)))
/*** Private data structures ***/
struct DictNode {
DictKey key;
DictNode *next;
DictNode *prev;
};
struct Dict {
DictNode head;
void *frame;
int (*leq)(void *frame, DictKey key1, DictKey key2);
};
#endif

View File

@ -1,111 +0,0 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#include <stddef.h>
#include "dict-list.h"
#include "memalloc.h"
/* really __gl_dictListNewDict */
Dict *dictNewDict( void *frame,
int (*leq)(void *frame, DictKey key1, DictKey key2) )
{
Dict *dict = (Dict *) memAlloc( sizeof( Dict ));
DictNode *head;
if (dict == NULL) return NULL;
head = &dict->head;
head->key = NULL;
head->next = head;
head->prev = head;
dict->frame = frame;
dict->leq = leq;
return dict;
}
/* really __gl_dictListDeleteDict */
void dictDeleteDict( Dict *dict )
{
DictNode *node, *next;
for( node = dict->head.next; node != &dict->head; node = next ) {
next = node->next;
memFree( node );
}
memFree( dict );
}
/* really __gl_dictListInsertBefore */
DictNode *dictInsertBefore( Dict *dict, DictNode *node, DictKey key )
{
DictNode *newNode;
do {
node = node->prev;
} while( node->key != NULL && ! (*dict->leq)(dict->frame, node->key, key));
newNode = (DictNode *) memAlloc( sizeof( DictNode ));
if (newNode == NULL) return NULL;
newNode->key = key;
newNode->next = node->next;
node->next->prev = newNode;
newNode->prev = node;
node->next = newNode;
return newNode;
}
/* really __gl_dictListDelete */
void dictDelete( Dict *dict, DictNode *node ) /*ARGSUSED*/
{
node->next->prev = node->prev;
node->prev->next = node->next;
memFree( node );
}
/* really __gl_dictListSearch */
DictNode *dictSearch( Dict *dict, DictKey key )
{
DictNode *node = &dict->head;
do {
node = node->next;
} while( node->key != NULL && ! (*dict->leq)(dict->frame, key, node->key));
return node;
}

View File

@ -1,100 +0,0 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#ifndef __dict_list_h_
#define __dict_list_h_
/* Use #define's so that another heap implementation can use this one */
#define DictKey DictListKey
#define Dict DictList
#define DictNode DictListNode
#define dictNewDict(frame,leq) __gl_dictListNewDict(frame,leq)
#define dictDeleteDict(dict) __gl_dictListDeleteDict(dict)
#define dictSearch(dict,key) __gl_dictListSearch(dict,key)
#define dictInsert(dict,key) __gl_dictListInsert(dict,key)
#define dictInsertBefore(dict,node,key) __gl_dictListInsertBefore(dict,node,key)
#define dictDelete(dict,node) __gl_dictListDelete(dict,node)
#define dictKey(n) __gl_dictListKey(n)
#define dictSucc(n) __gl_dictListSucc(n)
#define dictPred(n) __gl_dictListPred(n)
#define dictMin(d) __gl_dictListMin(d)
#define dictMax(d) __gl_dictListMax(d)
typedef void *DictKey;
typedef struct Dict Dict;
typedef struct DictNode DictNode;
Dict *dictNewDict(
void *frame,
int (*leq)(void *frame, DictKey key1, DictKey key2) );
void dictDeleteDict( Dict *dict );
/* Search returns the node with the smallest key greater than or equal
* to the given key. If there is no such key, returns a node whose
* key is NULL. Similarly, Succ(Max(d)) has a NULL key, etc.
*/
DictNode *dictSearch( Dict *dict, DictKey key );
DictNode *dictInsertBefore( Dict *dict, DictNode *node, DictKey key );
void dictDelete( Dict *dict, DictNode *node );
#define __gl_dictListKey(n) ((n)->key)
#define __gl_dictListSucc(n) ((n)->next)
#define __gl_dictListPred(n) ((n)->prev)
#define __gl_dictListMin(d) ((d)->head.next)
#define __gl_dictListMax(d) ((d)->head.prev)
#define __gl_dictListInsert(d,k) (dictInsertBefore((d),&(d)->head,(k)))
/*** Private data structures ***/
struct DictNode {
DictKey key;
DictNode *next;
DictNode *prev;
};
struct Dict {
DictNode head;
void *frame;
int (*leq)(void *frame, DictKey key1, DictKey key2);
};
#endif

View File

@ -1,264 +0,0 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#include "gluos.h"
#include <assert.h>
#include "mesh.h"
#include "geom.h"
int __gl_vertLeq( GLUvertex *u, GLUvertex *v )
{
/* Returns TRUE if u is lexicographically <= v. */
return VertLeq( u, v );
}
GLdouble __gl_edgeEval( GLUvertex *u, GLUvertex *v, GLUvertex *w )
{
/* Given three vertices u,v,w such that VertLeq(u,v) && VertLeq(v,w),
* evaluates the t-coord of the edge uw at the s-coord of the vertex v.
* Returns v->t - (uw)(v->s), ie. the signed distance from uw to v.
* If uw is vertical (and thus passes thru v), the result is zero.
*
* The calculation is extremely accurate and stable, even when v
* is very close to u or w. In particular if we set v->t = 0 and
* let r be the negated result (this evaluates (uw)(v->s)), then
* r is guaranteed to satisfy MIN(u->t,w->t) <= r <= MAX(u->t,w->t).
*/
GLdouble gapL, gapR;
assert( VertLeq( u, v ) && VertLeq( v, w ));
gapL = v->s - u->s;
gapR = w->s - v->s;
if( gapL + gapR > 0 ) {
if( gapL < gapR ) {
return (v->t - u->t) + (u->t - w->t) * (gapL / (gapL + gapR));
} else {
return (v->t - w->t) + (w->t - u->t) * (gapR / (gapL + gapR));
}
}
/* vertical line */
return 0;
}
GLdouble __gl_edgeSign( GLUvertex *u, GLUvertex *v, GLUvertex *w )
{
/* Returns a number whose sign matches EdgeEval(u,v,w) but which
* is cheaper to evaluate. Returns > 0, == 0 , or < 0
* as v is above, on, or below the edge uw.
*/
GLdouble gapL, gapR;
assert( VertLeq( u, v ) && VertLeq( v, w ));
gapL = v->s - u->s;
gapR = w->s - v->s;
if( gapL + gapR > 0 ) {
return (v->t - w->t) * gapL + (v->t - u->t) * gapR;
}
/* vertical line */
return 0;
}
/***********************************************************************
* Define versions of EdgeSign, EdgeEval with s and t transposed.
*/
GLdouble __gl_transEval( GLUvertex *u, GLUvertex *v, GLUvertex *w )
{
/* Given three vertices u,v,w such that TransLeq(u,v) && TransLeq(v,w),
* evaluates the t-coord of the edge uw at the s-coord of the vertex v.
* Returns v->s - (uw)(v->t), ie. the signed distance from uw to v.
* If uw is vertical (and thus passes thru v), the result is zero.
*
* The calculation is extremely accurate and stable, even when v
* is very close to u or w. In particular if we set v->s = 0 and
* let r be the negated result (this evaluates (uw)(v->t)), then
* r is guaranteed to satisfy MIN(u->s,w->s) <= r <= MAX(u->s,w->s).
*/
GLdouble gapL, gapR;
assert( TransLeq( u, v ) && TransLeq( v, w ));
gapL = v->t - u->t;
gapR = w->t - v->t;
if( gapL + gapR > 0 ) {
if( gapL < gapR ) {
return (v->s - u->s) + (u->s - w->s) * (gapL / (gapL + gapR));
} else {
return (v->s - w->s) + (w->s - u->s) * (gapR / (gapL + gapR));
}
}
/* vertical line */
return 0;
}
GLdouble __gl_transSign( GLUvertex *u, GLUvertex *v, GLUvertex *w )
{
/* Returns a number whose sign matches TransEval(u,v,w) but which
* is cheaper to evaluate. Returns > 0, == 0 , or < 0
* as v is above, on, or below the edge uw.
*/
GLdouble gapL, gapR;
assert( TransLeq( u, v ) && TransLeq( v, w ));
gapL = v->t - u->t;
gapR = w->t - v->t;
if( gapL + gapR > 0 ) {
return (v->s - w->s) * gapL + (v->s - u->s) * gapR;
}
/* vertical line */
return 0;
}
int __gl_vertCCW( GLUvertex *u, GLUvertex *v, GLUvertex *w )
{
/* For almost-degenerate situations, the results are not reliable.
* Unless the floating-point arithmetic can be performed without
* rounding errors, *any* implementation will give incorrect results
* on some degenerate inputs, so the client must have some way to
* handle this situation.
*/
return (u->s*(v->t - w->t) + v->s*(w->t - u->t) + w->s*(u->t - v->t)) >= 0;
}
/* Given parameters a,x,b,y returns the value (b*x+a*y)/(a+b),
* or (x+y)/2 if a==b==0. It requires that a,b >= 0, and enforces
* this in the rare case that one argument is slightly negative.
* The implementation is extremely stable numerically.
* In particular it guarantees that the result r satisfies
* MIN(x,y) <= r <= MAX(x,y), and the results are very accurate
* even when a and b differ greatly in magnitude.
*/
#define RealInterpolate(a,x,b,y) \
(a = (a < 0) ? 0 : a, b = (b < 0) ? 0 : b, \
((a <= b) ? ((b == 0) ? ((x+y) / 2) \
: (x + (y-x) * (a/(a+b)))) \
: (y + (x-y) * (b/(a+b)))))
#ifndef FOR_TRITE_TEST_PROGRAM
#define Interpolate(a,x,b,y) RealInterpolate(a,x,b,y)
#else
/* Claim: the ONLY property the sweep algorithm relies on is that
* MIN(x,y) <= r <= MAX(x,y). This is a nasty way to test that.
*/
#include <stdlib.h>
extern int RandomInterpolate;
GLdouble Interpolate( GLdouble a, GLdouble x, GLdouble b, GLdouble y)
{
printf("*********************%d\n",RandomInterpolate);
if( RandomInterpolate ) {
a = 1.2 * drand48() - 0.1;
a = (a < 0) ? 0 : ((a > 1) ? 1 : a);
b = 1.0 - a;
}
return RealInterpolate(a,x,b,y);
}
#endif
#define Swap(a,b) do { GLUvertex *t = a; a = b; b = t; } while (0)
void __gl_edgeIntersect( GLUvertex *o1, GLUvertex *d1,
GLUvertex *o2, GLUvertex *d2,
GLUvertex *v )
/* Given edges (o1,d1) and (o2,d2), compute their point of intersection.
* The computed point is guaranteed to lie in the intersection of the
* bounding rectangles defined by each edge.
*/
{
GLdouble z1, z2;
/* This is certainly not the most efficient way to find the intersection
* of two line segments, but it is very numerically stable.
*
* Strategy: find the two middle vertices in the VertLeq ordering,
* and interpolate the intersection s-value from these. Then repeat
* using the TransLeq ordering to find the intersection t-value.
*/
if( ! VertLeq( o1, d1 )) { Swap( o1, d1 ); }
if( ! VertLeq( o2, d2 )) { Swap( o2, d2 ); }
if( ! VertLeq( o1, o2 )) { Swap( o1, o2 ); Swap( d1, d2 ); }
if( ! VertLeq( o2, d1 )) {
/* Technically, no intersection -- do our best */
v->s = (o2->s + d1->s) / 2;
} else if( VertLeq( d1, d2 )) {
/* Interpolate between o2 and d1 */
z1 = EdgeEval( o1, o2, d1 );
z2 = EdgeEval( o2, d1, d2 );
if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; }
v->s = Interpolate( z1, o2->s, z2, d1->s );
} else {
/* Interpolate between o2 and d2 */
z1 = EdgeSign( o1, o2, d1 );
z2 = -EdgeSign( o1, d2, d1 );
if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; }
v->s = Interpolate( z1, o2->s, z2, d2->s );
}
/* Now repeat the process for t */
if( ! TransLeq( o1, d1 )) { Swap( o1, d1 ); }
if( ! TransLeq( o2, d2 )) { Swap( o2, d2 ); }
if( ! TransLeq( o1, o2 )) { Swap( o1, o2 ); Swap( d1, d2 ); }
if( ! TransLeq( o2, d1 )) {
/* Technically, no intersection -- do our best */
v->t = (o2->t + d1->t) / 2;
} else if( TransLeq( d1, d2 )) {
/* Interpolate between o2 and d1 */
z1 = TransEval( o1, o2, d1 );
z2 = TransEval( o2, d1, d2 );
if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; }
v->t = Interpolate( z1, o2->t, z2, d1->t );
} else {
/* Interpolate between o2 and d2 */
z1 = TransSign( o1, o2, d1 );
z2 = -TransSign( o1, d2, d1 );
if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; }
v->t = Interpolate( z1, o2->t, z2, d2->t );
}
}

View File

@ -1,84 +0,0 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#ifndef __geom_h_
#define __geom_h_
#include "mesh.h"
#ifdef NO_BRANCH_CONDITIONS
/* MIPS architecture has special instructions to evaluate boolean
* conditions -- more efficient than branching, IF you can get the
* compiler to generate the right instructions (SGI compiler doesn't)
*/
#define VertEq(u,v) (((u)->s == (v)->s) & ((u)->t == (v)->t))
#define VertLeq(u,v) (((u)->s < (v)->s) | \
((u)->s == (v)->s & (u)->t <= (v)->t))
#else
#define VertEq(u,v) ((u)->s == (v)->s && (u)->t == (v)->t)
#define VertLeq(u,v) (((u)->s < (v)->s) || \
((u)->s == (v)->s && (u)->t <= (v)->t))
#endif
#define EdgeEval(u,v,w) __gl_edgeEval(u,v,w)
#define EdgeSign(u,v,w) __gl_edgeSign(u,v,w)
/* Versions of VertLeq, EdgeSign, EdgeEval with s and t transposed. */
#define TransLeq(u,v) (((u)->t < (v)->t) || \
((u)->t == (v)->t && (u)->s <= (v)->s))
#define TransEval(u,v,w) __gl_transEval(u,v,w)
#define TransSign(u,v,w) __gl_transSign(u,v,w)
#define EdgeGoesLeft(e) VertLeq( (e)->Dst, (e)->Org )
#define EdgeGoesRight(e) VertLeq( (e)->Org, (e)->Dst )
#undef ABS
#define ABS(x) ((x) < 0 ? -(x) : (x))
#define VertL1dist(u,v) (ABS(u->s - v->s) + ABS(u->t - v->t))
#define VertCCW(u,v,w) __gl_vertCCW(u,v,w)
int __gl_vertLeq( GLUvertex *u, GLUvertex *v );
GLdouble __gl_edgeEval( GLUvertex *u, GLUvertex *v, GLUvertex *w );
GLdouble __gl_edgeSign( GLUvertex *u, GLUvertex *v, GLUvertex *w );
GLdouble __gl_transEval( GLUvertex *u, GLUvertex *v, GLUvertex *w );
GLdouble __gl_transSign( GLUvertex *u, GLUvertex *v, GLUvertex *w );
int __gl_vertCCW( GLUvertex *u, GLUvertex *v, GLUvertex *w );
void __gl_edgeIntersect( GLUvertex *o1, GLUvertex *d1,
GLUvertex *o2, GLUvertex *d2,
GLUvertex *v );
#endif

View File

@ -1 +0,0 @@
/* This is a stub header to avoid having to change tess.c */

View File

@ -1,49 +0,0 @@
/*
* Cogl
*
* A Low Level GPU Graphics and Utilities API
*
* Copyright (C) 2010 Intel Corporation.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
/* This is a simple replacement for memalloc from the SGI tesselator
code to force it to use glib's allocation instead */
#ifndef __MEMALLOC_H__
#define __MEMALLOC_H__
#include <glib.h>
#define memRealloc g_realloc
#define memAlloc g_malloc
#define memFree g_free
#define memInit(x) 1
/* tess.c defines TRUE and FALSE itself unconditionally so we need to
undefine it from the glib headers */
#undef TRUE
#undef FALSE
#endif /* __MEMALLOC_H__ */

View File

@ -1,798 +0,0 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#include "gluos.h"
#include <stddef.h>
#include <assert.h>
#include "mesh.h"
#include "memalloc.h"
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
static GLUvertex *allocVertex(void)
{
return (GLUvertex *)memAlloc( sizeof( GLUvertex ));
}
static GLUface *allocFace(void)
{
return (GLUface *)memAlloc( sizeof( GLUface ));
}
/************************ Utility Routines ************************/
/* Allocate and free half-edges in pairs for efficiency.
* The *only* place that should use this fact is allocation/free.
*/
typedef struct { GLUhalfEdge e, eSym; } EdgePair;
/* MakeEdge creates a new pair of half-edges which form their own loop.
* No vertex or face structures are allocated, but these must be assigned
* before the current edge operation is completed.
*/
static GLUhalfEdge *MakeEdge( GLUhalfEdge *eNext )
{
GLUhalfEdge *e;
GLUhalfEdge *eSym;
GLUhalfEdge *ePrev;
EdgePair *pair = (EdgePair *)memAlloc( sizeof( EdgePair ));
if (pair == NULL) return NULL;
e = &pair->e;
eSym = &pair->eSym;
/* Make sure eNext points to the first edge of the edge pair */
if( eNext->Sym < eNext ) { eNext = eNext->Sym; }
/* Insert in circular doubly-linked list before eNext.
* Note that the prev pointer is stored in Sym->next.
*/
ePrev = eNext->Sym->next;
eSym->next = ePrev;
ePrev->Sym->next = e;
e->next = eNext;
eNext->Sym->next = eSym;
e->Sym = eSym;
e->Onext = e;
e->Lnext = eSym;
e->Org = NULL;
e->Lface = NULL;
e->winding = 0;
e->activeRegion = NULL;
eSym->Sym = e;
eSym->Onext = eSym;
eSym->Lnext = e;
eSym->Org = NULL;
eSym->Lface = NULL;
eSym->winding = 0;
eSym->activeRegion = NULL;
return e;
}
/* Splice( a, b ) is best described by the Guibas/Stolfi paper or the
* CS348a notes (see mesh.h). Basically it modifies the mesh so that
* a->Onext and b->Onext are exchanged. This can have various effects
* depending on whether a and b belong to different face or vertex rings.
* For more explanation see __gl_meshSplice() below.
*/
static void Splice( GLUhalfEdge *a, GLUhalfEdge *b )
{
GLUhalfEdge *aOnext = a->Onext;
GLUhalfEdge *bOnext = b->Onext;
aOnext->Sym->Lnext = b;
bOnext->Sym->Lnext = a;
a->Onext = bOnext;
b->Onext = aOnext;
}
/* MakeVertex( newVertex, eOrig, vNext ) attaches a new vertex and makes it the
* origin of all edges in the vertex loop to which eOrig belongs. "vNext" gives
* a place to insert the new vertex in the global vertex list. We insert
* the new vertex *before* vNext so that algorithms which walk the vertex
* list will not see the newly created vertices.
*/
static void MakeVertex( GLUvertex *newVertex,
GLUhalfEdge *eOrig, GLUvertex *vNext )
{
GLUhalfEdge *e;
GLUvertex *vPrev;
GLUvertex *vNew = newVertex;
assert(vNew != NULL);
/* insert in circular doubly-linked list before vNext */
vPrev = vNext->prev;
vNew->prev = vPrev;
vPrev->next = vNew;
vNew->next = vNext;
vNext->prev = vNew;
vNew->anEdge = eOrig;
vNew->data = NULL;
/* leave coords, s, t undefined */
/* fix other edges on this vertex loop */
e = eOrig;
do {
e->Org = vNew;
e = e->Onext;
} while( e != eOrig );
}
/* MakeFace( newFace, eOrig, fNext ) attaches a new face and makes it the left
* face of all edges in the face loop to which eOrig belongs. "fNext" gives
* a place to insert the new face in the global face list. We insert
* the new face *before* fNext so that algorithms which walk the face
* list will not see the newly created faces.
*/
static void MakeFace( GLUface *newFace, GLUhalfEdge *eOrig, GLUface *fNext )
{
GLUhalfEdge *e;
GLUface *fPrev;
GLUface *fNew = newFace;
assert(fNew != NULL);
/* insert in circular doubly-linked list before fNext */
fPrev = fNext->prev;
fNew->prev = fPrev;
fPrev->next = fNew;
fNew->next = fNext;
fNext->prev = fNew;
fNew->anEdge = eOrig;
fNew->data = NULL;
fNew->trail = NULL;
fNew->marked = FALSE;
/* The new face is marked "inside" if the old one was. This is a
* convenience for the common case where a face has been split in two.
*/
fNew->inside = fNext->inside;
/* fix other edges on this face loop */
e = eOrig;
do {
e->Lface = fNew;
e = e->Lnext;
} while( e != eOrig );
}
/* KillEdge( eDel ) destroys an edge (the half-edges eDel and eDel->Sym),
* and removes from the global edge list.
*/
static void KillEdge( GLUhalfEdge *eDel )
{
GLUhalfEdge *ePrev, *eNext;
/* Half-edges are allocated in pairs, see EdgePair above */
if( eDel->Sym < eDel ) { eDel = eDel->Sym; }
/* delete from circular doubly-linked list */
eNext = eDel->next;
ePrev = eDel->Sym->next;
eNext->Sym->next = ePrev;
ePrev->Sym->next = eNext;
memFree( eDel );
}
/* KillVertex( vDel ) destroys a vertex and removes it from the global
* vertex list. It updates the vertex loop to point to a given new vertex.
*/
static void KillVertex( GLUvertex *vDel, GLUvertex *newOrg )
{
GLUhalfEdge *e, *eStart = vDel->anEdge;
GLUvertex *vPrev, *vNext;
/* change the origin of all affected edges */
e = eStart;
do {
e->Org = newOrg;
e = e->Onext;
} while( e != eStart );
/* delete from circular doubly-linked list */
vPrev = vDel->prev;
vNext = vDel->next;
vNext->prev = vPrev;
vPrev->next = vNext;
memFree( vDel );
}
/* KillFace( fDel ) destroys a face and removes it from the global face
* list. It updates the face loop to point to a given new face.
*/
static void KillFace( GLUface *fDel, GLUface *newLface )
{
GLUhalfEdge *e, *eStart = fDel->anEdge;
GLUface *fPrev, *fNext;
/* change the left face of all affected edges */
e = eStart;
do {
e->Lface = newLface;
e = e->Lnext;
} while( e != eStart );
/* delete from circular doubly-linked list */
fPrev = fDel->prev;
fNext = fDel->next;
fNext->prev = fPrev;
fPrev->next = fNext;
memFree( fDel );
}
/****************** Basic Edge Operations **********************/
/* __gl_meshMakeEdge creates one edge, two vertices, and a loop (face).
* The loop consists of the two new half-edges.
*/
GLUhalfEdge *__gl_meshMakeEdge( GLUmesh *mesh )
{
GLUvertex *newVertex1= allocVertex();
GLUvertex *newVertex2= allocVertex();
GLUface *newFace= allocFace();
GLUhalfEdge *e;
/* if any one is null then all get freed */
if (newVertex1 == NULL || newVertex2 == NULL || newFace == NULL) {
if (newVertex1 != NULL) memFree(newVertex1);
if (newVertex2 != NULL) memFree(newVertex2);
if (newFace != NULL) memFree(newFace);
return NULL;
}
e = MakeEdge( &mesh->eHead );
if (e == NULL) {
memFree(newVertex1);
memFree(newVertex2);
memFree(newFace);
return NULL;
}
MakeVertex( newVertex1, e, &mesh->vHead );
MakeVertex( newVertex2, e->Sym, &mesh->vHead );
MakeFace( newFace, e, &mesh->fHead );
return e;
}
/* __gl_meshSplice( eOrg, eDst ) is the basic operation for changing the
* mesh connectivity and topology. It changes the mesh so that
* eOrg->Onext <- OLD( eDst->Onext )
* eDst->Onext <- OLD( eOrg->Onext )
* where OLD(...) means the value before the meshSplice operation.
*
* This can have two effects on the vertex structure:
* - if eOrg->Org != eDst->Org, the two vertices are merged together
* - if eOrg->Org == eDst->Org, the origin is split into two vertices
* In both cases, eDst->Org is changed and eOrg->Org is untouched.
*
* Similarly (and independently) for the face structure,
* - if eOrg->Lface == eDst->Lface, one loop is split into two
* - if eOrg->Lface != eDst->Lface, two distinct loops are joined into one
* In both cases, eDst->Lface is changed and eOrg->Lface is unaffected.
*
* Some special cases:
* If eDst == eOrg, the operation has no effect.
* If eDst == eOrg->Lnext, the new face will have a single edge.
* If eDst == eOrg->Lprev, the old face will have a single edge.
* If eDst == eOrg->Onext, the new vertex will have a single edge.
* If eDst == eOrg->Oprev, the old vertex will have a single edge.
*/
int __gl_meshSplice( GLUhalfEdge *eOrg, GLUhalfEdge *eDst )
{
int joiningLoops = FALSE;
int joiningVertices = FALSE;
if( eOrg == eDst ) return 1;
if( eDst->Org != eOrg->Org ) {
/* We are merging two disjoint vertices -- destroy eDst->Org */
joiningVertices = TRUE;
KillVertex( eDst->Org, eOrg->Org );
}
if( eDst->Lface != eOrg->Lface ) {
/* We are connecting two disjoint loops -- destroy eDst->Lface */
joiningLoops = TRUE;
KillFace( eDst->Lface, eOrg->Lface );
}
/* Change the edge structure */
Splice( eDst, eOrg );
if( ! joiningVertices ) {
GLUvertex *newVertex= allocVertex();
if (newVertex == NULL) return 0;
/* We split one vertex into two -- the new vertex is eDst->Org.
* Make sure the old vertex points to a valid half-edge.
*/
MakeVertex( newVertex, eDst, eOrg->Org );
eOrg->Org->anEdge = eOrg;
}
if( ! joiningLoops ) {
GLUface *newFace= allocFace();
if (newFace == NULL) return 0;
/* We split one loop into two -- the new loop is eDst->Lface.
* Make sure the old face points to a valid half-edge.
*/
MakeFace( newFace, eDst, eOrg->Lface );
eOrg->Lface->anEdge = eOrg;
}
return 1;
}
/* __gl_meshDelete( eDel ) removes the edge eDel. There are several cases:
* if (eDel->Lface != eDel->Rface), we join two loops into one; the loop
* eDel->Lface is deleted. Otherwise, we are splitting one loop into two;
* the newly created loop will contain eDel->Dst. If the deletion of eDel
* would create isolated vertices, those are deleted as well.
*
* This function could be implemented as two calls to __gl_meshSplice
* plus a few calls to memFree, but this would allocate and delete
* unnecessary vertices and faces.
*/
int __gl_meshDelete( GLUhalfEdge *eDel )
{
GLUhalfEdge *eDelSym = eDel->Sym;
int joiningLoops = FALSE;
/* First step: disconnect the origin vertex eDel->Org. We make all
* changes to get a consistent mesh in this "intermediate" state.
*/
if( eDel->Lface != eDel->Rface ) {
/* We are joining two loops into one -- remove the left face */
joiningLoops = TRUE;
KillFace( eDel->Lface, eDel->Rface );
}
if( eDel->Onext == eDel ) {
KillVertex( eDel->Org, NULL );
} else {
/* Make sure that eDel->Org and eDel->Rface point to valid half-edges */
eDel->Rface->anEdge = eDel->Oprev;
eDel->Org->anEdge = eDel->Onext;
Splice( eDel, eDel->Oprev );
if( ! joiningLoops ) {
GLUface *newFace= allocFace();
if (newFace == NULL) return 0;
/* We are splitting one loop into two -- create a new loop for eDel. */
MakeFace( newFace, eDel, eDel->Lface );
}
}
/* Claim: the mesh is now in a consistent state, except that eDel->Org
* may have been deleted. Now we disconnect eDel->Dst.
*/
if( eDelSym->Onext == eDelSym ) {
KillVertex( eDelSym->Org, NULL );
KillFace( eDelSym->Lface, NULL );
} else {
/* Make sure that eDel->Dst and eDel->Lface point to valid half-edges */
eDel->Lface->anEdge = eDelSym->Oprev;
eDelSym->Org->anEdge = eDelSym->Onext;
Splice( eDelSym, eDelSym->Oprev );
}
/* Any isolated vertices or faces have already been freed. */
KillEdge( eDel );
return 1;
}
/******************** Other Edge Operations **********************/
/* All these routines can be implemented with the basic edge
* operations above. They are provided for convenience and efficiency.
*/
/* __gl_meshAddEdgeVertex( eOrg ) creates a new edge eNew such that
* eNew == eOrg->Lnext, and eNew->Dst is a newly created vertex.
* eOrg and eNew will have the same left face.
*/
GLUhalfEdge *__gl_meshAddEdgeVertex( GLUhalfEdge *eOrg )
{
GLUhalfEdge *eNewSym;
GLUhalfEdge *eNew = MakeEdge( eOrg );
if (eNew == NULL) return NULL;
eNewSym = eNew->Sym;
/* Connect the new edge appropriately */
Splice( eNew, eOrg->Lnext );
/* Set the vertex and face information */
eNew->Org = eOrg->Dst;
{
GLUvertex *newVertex= allocVertex();
if (newVertex == NULL) return NULL;
MakeVertex( newVertex, eNewSym, eNew->Org );
}
eNew->Lface = eNewSym->Lface = eOrg->Lface;
return eNew;
}
/* __gl_meshSplitEdge( eOrg ) splits eOrg into two edges eOrg and eNew,
* such that eNew == eOrg->Lnext. The new vertex is eOrg->Dst == eNew->Org.
* eOrg and eNew will have the same left face.
*/
GLUhalfEdge *__gl_meshSplitEdge( GLUhalfEdge *eOrg )
{
GLUhalfEdge *eNew;
GLUhalfEdge *tempHalfEdge= __gl_meshAddEdgeVertex( eOrg );
if (tempHalfEdge == NULL) return NULL;
eNew = tempHalfEdge->Sym;
/* Disconnect eOrg from eOrg->Dst and connect it to eNew->Org */
Splice( eOrg->Sym, eOrg->Sym->Oprev );
Splice( eOrg->Sym, eNew );
/* Set the vertex and face information */
eOrg->Dst = eNew->Org;
eNew->Dst->anEdge = eNew->Sym; /* may have pointed to eOrg->Sym */
eNew->Rface = eOrg->Rface;
eNew->winding = eOrg->winding; /* copy old winding information */
eNew->Sym->winding = eOrg->Sym->winding;
return eNew;
}
/* __gl_meshConnect( eOrg, eDst ) creates a new edge from eOrg->Dst
* to eDst->Org, and returns the corresponding half-edge eNew.
* If eOrg->Lface == eDst->Lface, this splits one loop into two,
* and the newly created loop is eNew->Lface. Otherwise, two disjoint
* loops are merged into one, and the loop eDst->Lface is destroyed.
*
* If (eOrg == eDst), the new face will have only two edges.
* If (eOrg->Lnext == eDst), the old face is reduced to a single edge.
* If (eOrg->Lnext->Lnext == eDst), the old face is reduced to two edges.
*/
GLUhalfEdge *__gl_meshConnect( GLUhalfEdge *eOrg, GLUhalfEdge *eDst )
{
GLUhalfEdge *eNewSym;
int joiningLoops = FALSE;
GLUhalfEdge *eNew = MakeEdge( eOrg );
if (eNew == NULL) return NULL;
eNewSym = eNew->Sym;
if( eDst->Lface != eOrg->Lface ) {
/* We are connecting two disjoint loops -- destroy eDst->Lface */
joiningLoops = TRUE;
KillFace( eDst->Lface, eOrg->Lface );
}
/* Connect the new edge appropriately */
Splice( eNew, eOrg->Lnext );
Splice( eNewSym, eDst );
/* Set the vertex and face information */
eNew->Org = eOrg->Dst;
eNewSym->Org = eDst->Org;
eNew->Lface = eNewSym->Lface = eOrg->Lface;
/* Make sure the old face points to a valid half-edge */
eOrg->Lface->anEdge = eNewSym;
if( ! joiningLoops ) {
GLUface *newFace= allocFace();
if (newFace == NULL) return NULL;
/* We split one loop into two -- the new loop is eNew->Lface */
MakeFace( newFace, eNew, eOrg->Lface );
}
return eNew;
}
/******************** Other Operations **********************/
/* __gl_meshZapFace( fZap ) destroys a face and removes it from the
* global face list. All edges of fZap will have a NULL pointer as their
* left face. Any edges which also have a NULL pointer as their right face
* are deleted entirely (along with any isolated vertices this produces).
* An entire mesh can be deleted by zapping its faces, one at a time,
* in any order. Zapped faces cannot be used in further mesh operations!
*/
void __gl_meshZapFace( GLUface *fZap )
{
GLUhalfEdge *eStart = fZap->anEdge;
GLUhalfEdge *e, *eNext, *eSym;
GLUface *fPrev, *fNext;
/* walk around face, deleting edges whose right face is also NULL */
eNext = eStart->Lnext;
do {
e = eNext;
eNext = e->Lnext;
e->Lface = NULL;
if( e->Rface == NULL ) {
/* delete the edge -- see __gl_MeshDelete above */
if( e->Onext == e ) {
KillVertex( e->Org, NULL );
} else {
/* Make sure that e->Org points to a valid half-edge */
e->Org->anEdge = e->Onext;
Splice( e, e->Oprev );
}
eSym = e->Sym;
if( eSym->Onext == eSym ) {
KillVertex( eSym->Org, NULL );
} else {
/* Make sure that eSym->Org points to a valid half-edge */
eSym->Org->anEdge = eSym->Onext;
Splice( eSym, eSym->Oprev );
}
KillEdge( e );
}
} while( e != eStart );
/* delete from circular doubly-linked list */
fPrev = fZap->prev;
fNext = fZap->next;
fNext->prev = fPrev;
fPrev->next = fNext;
memFree( fZap );
}
/* __gl_meshNewMesh() creates a new mesh with no edges, no vertices,
* and no loops (what we usually call a "face").
*/
GLUmesh *__gl_meshNewMesh( void )
{
GLUvertex *v;
GLUface *f;
GLUhalfEdge *e;
GLUhalfEdge *eSym;
GLUmesh *mesh = (GLUmesh *)memAlloc( sizeof( GLUmesh ));
if (mesh == NULL) {
return NULL;
}
v = &mesh->vHead;
f = &mesh->fHead;
e = &mesh->eHead;
eSym = &mesh->eHeadSym;
v->next = v->prev = v;
v->anEdge = NULL;
v->data = NULL;
f->next = f->prev = f;
f->anEdge = NULL;
f->data = NULL;
f->trail = NULL;
f->marked = FALSE;
f->inside = FALSE;
e->next = e;
e->Sym = eSym;
e->Onext = NULL;
e->Lnext = NULL;
e->Org = NULL;
e->Lface = NULL;
e->winding = 0;
e->activeRegion = NULL;
eSym->next = eSym;
eSym->Sym = e;
eSym->Onext = NULL;
eSym->Lnext = NULL;
eSym->Org = NULL;
eSym->Lface = NULL;
eSym->winding = 0;
eSym->activeRegion = NULL;
return mesh;
}
/* __gl_meshUnion( mesh1, mesh2 ) forms the union of all structures in
* both meshes, and returns the new mesh (the old meshes are destroyed).
*/
GLUmesh *__gl_meshUnion( GLUmesh *mesh1, GLUmesh *mesh2 )
{
GLUface *f1 = &mesh1->fHead;
GLUvertex *v1 = &mesh1->vHead;
GLUhalfEdge *e1 = &mesh1->eHead;
GLUface *f2 = &mesh2->fHead;
GLUvertex *v2 = &mesh2->vHead;
GLUhalfEdge *e2 = &mesh2->eHead;
/* Add the faces, vertices, and edges of mesh2 to those of mesh1 */
if( f2->next != f2 ) {
f1->prev->next = f2->next;
f2->next->prev = f1->prev;
f2->prev->next = f1;
f1->prev = f2->prev;
}
if( v2->next != v2 ) {
v1->prev->next = v2->next;
v2->next->prev = v1->prev;
v2->prev->next = v1;
v1->prev = v2->prev;
}
if( e2->next != e2 ) {
e1->Sym->next->Sym->next = e2->next;
e2->next->Sym->next = e1->Sym->next;
e2->Sym->next->Sym->next = e1;
e1->Sym->next = e2->Sym->next;
}
memFree( mesh2 );
return mesh1;
}
#ifdef DELETE_BY_ZAPPING
/* __gl_meshDeleteMesh( mesh ) will free all storage for any valid mesh.
*/
void __gl_meshDeleteMesh( GLUmesh *mesh )
{
GLUface *fHead = &mesh->fHead;
while( fHead->next != fHead ) {
__gl_meshZapFace( fHead->next );
}
assert( mesh->vHead.next == &mesh->vHead );
memFree( mesh );
}
#else
/* __gl_meshDeleteMesh( mesh ) will free all storage for any valid mesh.
*/
void __gl_meshDeleteMesh( GLUmesh *mesh )
{
GLUface *f, *fNext;
GLUvertex *v, *vNext;
GLUhalfEdge *e, *eNext;
for( f = mesh->fHead.next; f != &mesh->fHead; f = fNext ) {
fNext = f->next;
memFree( f );
}
for( v = mesh->vHead.next; v != &mesh->vHead; v = vNext ) {
vNext = v->next;
memFree( v );
}
for( e = mesh->eHead.next; e != &mesh->eHead; e = eNext ) {
/* One call frees both e and e->Sym (see EdgePair above) */
eNext = e->next;
memFree( e );
}
memFree( mesh );
}
#endif
#ifndef NDEBUG
/* __gl_meshCheckMesh( mesh ) checks a mesh for self-consistency.
*/
void __gl_meshCheckMesh( GLUmesh *mesh )
{
GLUface *fHead = &mesh->fHead;
GLUvertex *vHead = &mesh->vHead;
GLUhalfEdge *eHead = &mesh->eHead;
GLUface *f, *fPrev;
GLUvertex *v, *vPrev;
GLUhalfEdge *e, *ePrev;
fPrev = fHead;
for( fPrev = fHead ; (f = fPrev->next) != fHead; fPrev = f) {
assert( f->prev == fPrev );
e = f->anEdge;
do {
assert( e->Sym != e );
assert( e->Sym->Sym == e );
assert( e->Lnext->Onext->Sym == e );
assert( e->Onext->Sym->Lnext == e );
assert( e->Lface == f );
e = e->Lnext;
} while( e != f->anEdge );
}
assert( f->prev == fPrev && f->anEdge == NULL && f->data == NULL );
vPrev = vHead;
for( vPrev = vHead ; (v = vPrev->next) != vHead; vPrev = v) {
assert( v->prev == vPrev );
e = v->anEdge;
do {
assert( e->Sym != e );
assert( e->Sym->Sym == e );
assert( e->Lnext->Onext->Sym == e );
assert( e->Onext->Sym->Lnext == e );
assert( e->Org == v );
e = e->Onext;
} while( e != v->anEdge );
}
assert( v->prev == vPrev && v->anEdge == NULL && v->data == NULL );
ePrev = eHead;
for( ePrev = eHead ; (e = ePrev->next) != eHead; ePrev = e) {
assert( e->Sym->next == ePrev->Sym );
assert( e->Sym != e );
assert( e->Sym->Sym == e );
assert( e->Org != NULL );
assert( e->Dst != NULL );
assert( e->Lnext->Onext->Sym == e );
assert( e->Onext->Sym->Lnext == e );
}
assert( e->Sym->next == ePrev->Sym
&& e->Sym == &mesh->eHeadSym
&& e->Sym->Sym == e
&& e->Org == NULL && e->Dst == NULL
&& e->Lface == NULL && e->Rface == NULL );
}
#endif

View File

@ -1,266 +0,0 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#ifndef __mesh_h_
#define __mesh_h_
#include <GL/gl.h>
typedef struct GLUmesh GLUmesh;
typedef struct GLUvertex GLUvertex;
typedef struct GLUface GLUface;
typedef struct GLUhalfEdge GLUhalfEdge;
typedef struct ActiveRegion ActiveRegion; /* Internal data */
/* The mesh structure is similar in spirit, notation, and operations
* to the "quad-edge" structure (see L. Guibas and J. Stolfi, Primitives
* for the manipulation of general subdivisions and the computation of
* Voronoi diagrams, ACM Transactions on Graphics, 4(2):74-123, April 1985).
* For a simplified description, see the course notes for CS348a,
* "Mathematical Foundations of Computer Graphics", available at the
* Stanford bookstore (and taught during the fall quarter).
* The implementation also borrows a tiny subset of the graph-based approach
* use in Mantyla's Geometric Work Bench (see M. Mantyla, An Introduction
* to Sold Modeling, Computer Science Press, Rockville, Maryland, 1988).
*
* The fundamental data structure is the "half-edge". Two half-edges
* go together to make an edge, but they point in opposite directions.
* Each half-edge has a pointer to its mate (the "symmetric" half-edge Sym),
* its origin vertex (Org), the face on its left side (Lface), and the
* adjacent half-edges in the CCW direction around the origin vertex
* (Onext) and around the left face (Lnext). There is also a "next"
* pointer for the global edge list (see below).
*
* The notation used for mesh navigation:
* Sym = the mate of a half-edge (same edge, but opposite direction)
* Onext = edge CCW around origin vertex (keep same origin)
* Dnext = edge CCW around destination vertex (keep same dest)
* Lnext = edge CCW around left face (dest becomes new origin)
* Rnext = edge CCW around right face (origin becomes new dest)
*
* "prev" means to substitute CW for CCW in the definitions above.
*
* The mesh keeps global lists of all vertices, faces, and edges,
* stored as doubly-linked circular lists with a dummy header node.
* The mesh stores pointers to these dummy headers (vHead, fHead, eHead).
*
* The circular edge list is special; since half-edges always occur
* in pairs (e and e->Sym), each half-edge stores a pointer in only
* one direction. Starting at eHead and following the e->next pointers
* will visit each *edge* once (ie. e or e->Sym, but not both).
* e->Sym stores a pointer in the opposite direction, thus it is
* always true that e->Sym->next->Sym->next == e.
*
* Each vertex has a pointer to next and previous vertices in the
* circular list, and a pointer to a half-edge with this vertex as
* the origin (NULL if this is the dummy header). There is also a
* field "data" for client data.
*
* Each face has a pointer to the next and previous faces in the
* circular list, and a pointer to a half-edge with this face as
* the left face (NULL if this is the dummy header). There is also
* a field "data" for client data.
*
* Note that what we call a "face" is really a loop; faces may consist
* of more than one loop (ie. not simply connected), but there is no
* record of this in the data structure. The mesh may consist of
* several disconnected regions, so it may not be possible to visit
* the entire mesh by starting at a half-edge and traversing the edge
* structure.
*
* The mesh does NOT support isolated vertices; a vertex is deleted along
* with its last edge. Similarly when two faces are merged, one of the
* faces is deleted (see __gl_meshDelete below). For mesh operations,
* all face (loop) and vertex pointers must not be NULL. However, once
* mesh manipulation is finished, __gl_MeshZapFace can be used to delete
* faces of the mesh, one at a time. All external faces can be "zapped"
* before the mesh is returned to the client; then a NULL face indicates
* a region which is not part of the output polygon.
*/
struct GLUvertex {
GLUvertex *next; /* next vertex (never NULL) */
GLUvertex *prev; /* previous vertex (never NULL) */
GLUhalfEdge *anEdge; /* a half-edge with this origin */
void *data; /* client's data */
/* Internal data (keep hidden) */
GLdouble coords[3]; /* vertex location in 3D */
GLdouble s, t; /* projection onto the sweep plane */
long pqHandle; /* to allow deletion from priority queue */
};
struct GLUface {
GLUface *next; /* next face (never NULL) */
GLUface *prev; /* previous face (never NULL) */
GLUhalfEdge *anEdge; /* a half edge with this left face */
void *data; /* room for client's data */
/* Internal data (keep hidden) */
GLUface *trail; /* "stack" for conversion to strips */
GLboolean marked; /* flag for conversion to strips */
GLboolean inside; /* this face is in the polygon interior */
};
struct GLUhalfEdge {
GLUhalfEdge *next; /* doubly-linked list (prev==Sym->next) */
GLUhalfEdge *Sym; /* same edge, opposite direction */
GLUhalfEdge *Onext; /* next edge CCW around origin */
GLUhalfEdge *Lnext; /* next edge CCW around left face */
GLUvertex *Org; /* origin vertex (Overtex too long) */
GLUface *Lface; /* left face */
/* Internal data (keep hidden) */
ActiveRegion *activeRegion; /* a region with this upper edge (sweep.c) */
int winding; /* change in winding number when crossing
from the right face to the left face */
};
#define Rface Sym->Lface
#define Dst Sym->Org
#define Oprev Sym->Lnext
#define Lprev Onext->Sym
#define Dprev Lnext->Sym
#define Rprev Sym->Onext
#define Dnext Rprev->Sym /* 3 pointers */
#define Rnext Oprev->Sym /* 3 pointers */
struct GLUmesh {
GLUvertex vHead; /* dummy header for vertex list */
GLUface fHead; /* dummy header for face list */
GLUhalfEdge eHead; /* dummy header for edge list */
GLUhalfEdge eHeadSym; /* and its symmetric counterpart */
};
/* The mesh operations below have three motivations: completeness,
* convenience, and efficiency. The basic mesh operations are MakeEdge,
* Splice, and Delete. All the other edge operations can be implemented
* in terms of these. The other operations are provided for convenience
* and/or efficiency.
*
* When a face is split or a vertex is added, they are inserted into the
* global list *before* the existing vertex or face (ie. e->Org or e->Lface).
* This makes it easier to process all vertices or faces in the global lists
* without worrying about processing the same data twice. As a convenience,
* when a face is split, the "inside" flag is copied from the old face.
* Other internal data (v->data, v->activeRegion, f->data, f->marked,
* f->trail, e->winding) is set to zero.
*
* ********************** Basic Edge Operations **************************
*
* __gl_meshMakeEdge( mesh ) creates one edge, two vertices, and a loop.
* The loop (face) consists of the two new half-edges.
*
* __gl_meshSplice( eOrg, eDst ) is the basic operation for changing the
* mesh connectivity and topology. It changes the mesh so that
* eOrg->Onext <- OLD( eDst->Onext )
* eDst->Onext <- OLD( eOrg->Onext )
* where OLD(...) means the value before the meshSplice operation.
*
* This can have two effects on the vertex structure:
* - if eOrg->Org != eDst->Org, the two vertices are merged together
* - if eOrg->Org == eDst->Org, the origin is split into two vertices
* In both cases, eDst->Org is changed and eOrg->Org is untouched.
*
* Similarly (and independently) for the face structure,
* - if eOrg->Lface == eDst->Lface, one loop is split into two
* - if eOrg->Lface != eDst->Lface, two distinct loops are joined into one
* In both cases, eDst->Lface is changed and eOrg->Lface is unaffected.
*
* __gl_meshDelete( eDel ) removes the edge eDel. There are several cases:
* if (eDel->Lface != eDel->Rface), we join two loops into one; the loop
* eDel->Lface is deleted. Otherwise, we are splitting one loop into two;
* the newly created loop will contain eDel->Dst. If the deletion of eDel
* would create isolated vertices, those are deleted as well.
*
* ********************** Other Edge Operations **************************
*
* __gl_meshAddEdgeVertex( eOrg ) creates a new edge eNew such that
* eNew == eOrg->Lnext, and eNew->Dst is a newly created vertex.
* eOrg and eNew will have the same left face.
*
* __gl_meshSplitEdge( eOrg ) splits eOrg into two edges eOrg and eNew,
* such that eNew == eOrg->Lnext. The new vertex is eOrg->Dst == eNew->Org.
* eOrg and eNew will have the same left face.
*
* __gl_meshConnect( eOrg, eDst ) creates a new edge from eOrg->Dst
* to eDst->Org, and returns the corresponding half-edge eNew.
* If eOrg->Lface == eDst->Lface, this splits one loop into two,
* and the newly created loop is eNew->Lface. Otherwise, two disjoint
* loops are merged into one, and the loop eDst->Lface is destroyed.
*
* ************************ Other Operations *****************************
*
* __gl_meshNewMesh() creates a new mesh with no edges, no vertices,
* and no loops (what we usually call a "face").
*
* __gl_meshUnion( mesh1, mesh2 ) forms the union of all structures in
* both meshes, and returns the new mesh (the old meshes are destroyed).
*
* __gl_meshDeleteMesh( mesh ) will free all storage for any valid mesh.
*
* __gl_meshZapFace( fZap ) destroys a face and removes it from the
* global face list. All edges of fZap will have a NULL pointer as their
* left face. Any edges which also have a NULL pointer as their right face
* are deleted entirely (along with any isolated vertices this produces).
* An entire mesh can be deleted by zapping its faces, one at a time,
* in any order. Zapped faces cannot be used in further mesh operations!
*
* __gl_meshCheckMesh( mesh ) checks a mesh for self-consistency.
*/
GLUhalfEdge *__gl_meshMakeEdge( GLUmesh *mesh );
int __gl_meshSplice( GLUhalfEdge *eOrg, GLUhalfEdge *eDst );
int __gl_meshDelete( GLUhalfEdge *eDel );
GLUhalfEdge *__gl_meshAddEdgeVertex( GLUhalfEdge *eOrg );
GLUhalfEdge *__gl_meshSplitEdge( GLUhalfEdge *eOrg );
GLUhalfEdge *__gl_meshConnect( GLUhalfEdge *eOrg, GLUhalfEdge *eDst );
GLUmesh *__gl_meshNewMesh( void );
GLUmesh *__gl_meshUnion( GLUmesh *mesh1, GLUmesh *mesh2 );
void __gl_meshDeleteMesh( GLUmesh *mesh );
void __gl_meshZapFace( GLUface *fZap );
#ifdef NDEBUG
#define __gl_meshCheckMesh( mesh )
#else
void __gl_meshCheckMesh( GLUmesh *mesh );
#endif
#endif

View File

@ -1,257 +0,0 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#include "gluos.h"
#include "mesh.h"
#include "tess.h"
#include "normal.h"
#include <math.h>
#include <assert.h>
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#define Dot(u,v) (u[0]*v[0] + u[1]*v[1] + u[2]*v[2])
#if 0
static void Normalize( GLdouble v[3] )
{
GLdouble len = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
assert( len > 0 );
len = sqrt( len );
v[0] /= len;
v[1] /= len;
v[2] /= len;
}
#endif
#undef ABS
#define ABS(x) ((x) < 0 ? -(x) : (x))
static int LongAxis( GLdouble v[3] )
{
int i = 0;
if( ABS(v[1]) > ABS(v[0]) ) { i = 1; }
if( ABS(v[2]) > ABS(v[i]) ) { i = 2; }
return i;
}
static void ComputeNormal( GLUtesselator *tess, GLdouble norm[3] )
{
GLUvertex *v, *v1, *v2;
GLdouble c, tLen2, maxLen2;
GLdouble maxVal[3], minVal[3], d1[3], d2[3], tNorm[3];
GLUvertex *maxVert[3], *minVert[3];
GLUvertex *vHead = &tess->mesh->vHead;
int i;
maxVal[0] = maxVal[1] = maxVal[2] = -2 * GLU_TESS_MAX_COORD;
minVal[0] = minVal[1] = minVal[2] = 2 * GLU_TESS_MAX_COORD;
for( v = vHead->next; v != vHead; v = v->next ) {
for( i = 0; i < 3; ++i ) {
c = v->coords[i];
if( c < minVal[i] ) { minVal[i] = c; minVert[i] = v; }
if( c > maxVal[i] ) { maxVal[i] = c; maxVert[i] = v; }
}
}
/* Find two vertices separated by at least 1/sqrt(3) of the maximum
* distance between any two vertices
*/
i = 0;
if( maxVal[1] - minVal[1] > maxVal[0] - minVal[0] ) { i = 1; }
if( maxVal[2] - minVal[2] > maxVal[i] - minVal[i] ) { i = 2; }
if( minVal[i] >= maxVal[i] ) {
/* All vertices are the same -- normal doesn't matter */
norm[0] = 0; norm[1] = 0; norm[2] = 1;
return;
}
/* Look for a third vertex which forms the triangle with maximum area
* (Length of normal == twice the triangle area)
*/
maxLen2 = 0;
v1 = minVert[i];
v2 = maxVert[i];
d1[0] = v1->coords[0] - v2->coords[0];
d1[1] = v1->coords[1] - v2->coords[1];
d1[2] = v1->coords[2] - v2->coords[2];
for( v = vHead->next; v != vHead; v = v->next ) {
d2[0] = v->coords[0] - v2->coords[0];
d2[1] = v->coords[1] - v2->coords[1];
d2[2] = v->coords[2] - v2->coords[2];
tNorm[0] = d1[1]*d2[2] - d1[2]*d2[1];
tNorm[1] = d1[2]*d2[0] - d1[0]*d2[2];
tNorm[2] = d1[0]*d2[1] - d1[1]*d2[0];
tLen2 = tNorm[0]*tNorm[0] + tNorm[1]*tNorm[1] + tNorm[2]*tNorm[2];
if( tLen2 > maxLen2 ) {
maxLen2 = tLen2;
norm[0] = tNorm[0];
norm[1] = tNorm[1];
norm[2] = tNorm[2];
}
}
if( maxLen2 <= 0 ) {
/* All points lie on a single line -- any decent normal will do */
norm[0] = norm[1] = norm[2] = 0;
norm[LongAxis(d1)] = 1;
}
}
static void CheckOrientation( GLUtesselator *tess )
{
GLdouble area;
GLUface *f, *fHead = &tess->mesh->fHead;
GLUvertex *v, *vHead = &tess->mesh->vHead;
GLUhalfEdge *e;
/* When we compute the normal automatically, we choose the orientation
* so that the sum of the signed areas of all contours is non-negative.
*/
area = 0;
for( f = fHead->next; f != fHead; f = f->next ) {
e = f->anEdge;
if( e->winding <= 0 ) continue;
do {
area += (e->Org->s - e->Dst->s) * (e->Org->t + e->Dst->t);
e = e->Lnext;
} while( e != f->anEdge );
}
if( area < 0 ) {
/* Reverse the orientation by flipping all the t-coordinates */
for( v = vHead->next; v != vHead; v = v->next ) {
v->t = - v->t;
}
tess->tUnit[0] = - tess->tUnit[0];
tess->tUnit[1] = - tess->tUnit[1];
tess->tUnit[2] = - tess->tUnit[2];
}
}
#ifdef FOR_TRITE_TEST_PROGRAM
#include <stdlib.h>
extern int RandomSweep;
#define S_UNIT_X (RandomSweep ? (2*drand48()-1) : 1.0)
#define S_UNIT_Y (RandomSweep ? (2*drand48()-1) : 0.0)
#else
#if defined(SLANTED_SWEEP)
/* The "feature merging" is not intended to be complete. There are
* special cases where edges are nearly parallel to the sweep line
* which are not implemented. The algorithm should still behave
* robustly (ie. produce a reasonable tesselation) in the presence
* of such edges, however it may miss features which could have been
* merged. We could minimize this effect by choosing the sweep line
* direction to be something unusual (ie. not parallel to one of the
* coordinate axes).
*/
#define S_UNIT_X 0.50941539564955385 /* Pre-normalized */
#define S_UNIT_Y 0.86052074622010633
#else
#define S_UNIT_X 1.0
#define S_UNIT_Y 0.0
#endif
#endif
/* Determine the polygon normal and project vertices onto the plane
* of the polygon.
*/
void __gl_projectPolygon( GLUtesselator *tess )
{
GLUvertex *v, *vHead = &tess->mesh->vHead;
GLdouble norm[3];
GLdouble *sUnit, *tUnit;
int i, computedNormal = FALSE;
norm[0] = tess->normal[0];
norm[1] = tess->normal[1];
norm[2] = tess->normal[2];
if( norm[0] == 0 && norm[1] == 0 && norm[2] == 0 ) {
ComputeNormal( tess, norm );
computedNormal = TRUE;
}
sUnit = tess->sUnit;
tUnit = tess->tUnit;
i = LongAxis( norm );
#if defined(FOR_TRITE_TEST_PROGRAM) || defined(TRUE_PROJECT)
/* Choose the initial sUnit vector to be approximately perpendicular
* to the normal.
*/
Normalize( norm );
sUnit[i] = 0;
sUnit[(i+1)%3] = S_UNIT_X;
sUnit[(i+2)%3] = S_UNIT_Y;
/* Now make it exactly perpendicular */
w = Dot( sUnit, norm );
sUnit[0] -= w * norm[0];
sUnit[1] -= w * norm[1];
sUnit[2] -= w * norm[2];
Normalize( sUnit );
/* Choose tUnit so that (sUnit,tUnit,norm) form a right-handed frame */
tUnit[0] = norm[1]*sUnit[2] - norm[2]*sUnit[1];
tUnit[1] = norm[2]*sUnit[0] - norm[0]*sUnit[2];
tUnit[2] = norm[0]*sUnit[1] - norm[1]*sUnit[0];
Normalize( tUnit );
#else
/* Project perpendicular to a coordinate axis -- better numerically */
sUnit[i] = 0;
sUnit[(i+1)%3] = S_UNIT_X;
sUnit[(i+2)%3] = S_UNIT_Y;
tUnit[i] = 0;
tUnit[(i+1)%3] = (norm[i] > 0) ? -S_UNIT_Y : S_UNIT_Y;
tUnit[(i+2)%3] = (norm[i] > 0) ? S_UNIT_X : -S_UNIT_X;
#endif
/* Project the vertices onto the sweep plane */
for( v = vHead->next; v != vHead; v = v->next ) {
v->s = Dot( v->coords, sUnit );
v->t = Dot( v->coords, tUnit );
}
if( computedNormal ) {
CheckOrientation( tess );
}
}

View File

@ -1,46 +0,0 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#ifndef __normal_h_
#define __normal_h_
#include "tess.h"
#include "tesselator.h"
/* __gl_projectPolygon( tess ) determines the polygon normal
* and project vertices onto the plane of the polygon.
*/
void __gl_projectPolygon( GLUtesselator *tess );
#endif

View File

@ -1,256 +0,0 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#include <stddef.h>
#include <assert.h>
#include "priorityq-heap.h"
#include "memalloc.h"
#define INIT_SIZE 32
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifdef FOR_TRITE_TEST_PROGRAM
#define LEQ(x,y) (*pq->leq)(x,y)
#else
/* Violates modularity, but a little faster */
#include "geom.h"
#define LEQ(x,y) VertLeq((GLUvertex *)x, (GLUvertex *)y)
#endif
/* really __gl_pqHeapNewPriorityQ */
PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) )
{
PriorityQ *pq = (PriorityQ *)memAlloc( sizeof( PriorityQ ));
if (pq == NULL) return NULL;
pq->size = 0;
pq->max = INIT_SIZE;
pq->nodes = (PQnode *)memAlloc( (INIT_SIZE + 1) * sizeof(pq->nodes[0]) );
if (pq->nodes == NULL) {
memFree(pq);
return NULL;
}
pq->handles = (PQhandleElem *)memAlloc( (INIT_SIZE + 1) * sizeof(pq->handles[0]) );
if (pq->handles == NULL) {
memFree(pq->nodes);
memFree(pq);
return NULL;
}
pq->initialized = FALSE;
pq->freeList = 0;
pq->leq = leq;
pq->nodes[1].handle = 1; /* so that Minimum() returns NULL */
pq->handles[1].key = NULL;
return pq;
}
/* really __gl_pqHeapDeletePriorityQ */
void pqDeletePriorityQ( PriorityQ *pq )
{
memFree( pq->handles );
memFree( pq->nodes );
memFree( pq );
}
static void FloatDown( PriorityQ *pq, long curr )
{
PQnode *n = pq->nodes;
PQhandleElem *h = pq->handles;
PQhandle hCurr, hChild;
long child;
hCurr = n[curr].handle;
for( ;; ) {
child = curr << 1;
if( child < pq->size && LEQ( h[n[child+1].handle].key,
h[n[child].handle].key )) {
++child;
}
assert(child <= pq->max);
hChild = n[child].handle;
if( child > pq->size || LEQ( h[hCurr].key, h[hChild].key )) {
n[curr].handle = hCurr;
h[hCurr].node = curr;
break;
}
n[curr].handle = hChild;
h[hChild].node = curr;
curr = child;
}
}
static void FloatUp( PriorityQ *pq, long curr )
{
PQnode *n = pq->nodes;
PQhandleElem *h = pq->handles;
PQhandle hCurr, hParent;
long parent;
hCurr = n[curr].handle;
for( ;; ) {
parent = curr >> 1;
hParent = n[parent].handle;
if( parent == 0 || LEQ( h[hParent].key, h[hCurr].key )) {
n[curr].handle = hCurr;
h[hCurr].node = curr;
break;
}
n[curr].handle = hParent;
h[hParent].node = curr;
curr = parent;
}
}
/* really __gl_pqHeapInit */
void pqInit( PriorityQ *pq )
{
long i;
/* This method of building a heap is O(n), rather than O(n lg n). */
for( i = pq->size; i >= 1; --i ) {
FloatDown( pq, i );
}
pq->initialized = TRUE;
}
/* really __gl_pqHeapInsert */
/* returns LONG_MAX iff out of memory */
PQhandle pqInsert( PriorityQ *pq, PQkey keyNew )
{
long curr;
PQhandle free_handle;
curr = ++ pq->size;
if( (curr*2) > pq->max ) {
PQnode *saveNodes= pq->nodes;
PQhandleElem *saveHandles= pq->handles;
/* If the heap overflows, double its size. */
pq->max <<= 1;
pq->nodes = (PQnode *)memRealloc( pq->nodes,
(size_t)
((pq->max + 1) * sizeof( pq->nodes[0] )));
if (pq->nodes == NULL) {
pq->nodes = saveNodes; /* restore ptr to free upon return */
return LONG_MAX;
}
pq->handles = (PQhandleElem *)memRealloc( pq->handles,
(size_t)
((pq->max + 1) *
sizeof( pq->handles[0] )));
if (pq->handles == NULL) {
pq->handles = saveHandles; /* restore ptr to free upon return */
return LONG_MAX;
}
}
if( pq->freeList == 0 ) {
free_handle = curr;
} else {
free_handle = pq->freeList;
pq->freeList = pq->handles[free_handle].node;
}
pq->nodes[curr].handle = free_handle;
pq->handles[free_handle].node = curr;
pq->handles[free_handle].key = keyNew;
if( pq->initialized ) {
FloatUp( pq, curr );
}
assert(free_handle != LONG_MAX);
return free_handle;
}
/* really __gl_pqHeapExtractMin */
PQkey pqExtractMin( PriorityQ *pq )
{
PQnode *n = pq->nodes;
PQhandleElem *h = pq->handles;
PQhandle hMin = n[1].handle;
PQkey min = h[hMin].key;
if( pq->size > 0 ) {
n[1].handle = n[pq->size].handle;
h[n[1].handle].node = 1;
h[hMin].key = NULL;
h[hMin].node = pq->freeList;
pq->freeList = hMin;
if( -- pq->size > 0 ) {
FloatDown( pq, 1 );
}
}
return min;
}
/* really __gl_pqHeapDelete */
void pqDelete( PriorityQ *pq, PQhandle hCurr )
{
PQnode *n = pq->nodes;
PQhandleElem *h = pq->handles;
long curr;
assert( hCurr >= 1 && hCurr <= pq->max && h[hCurr].key != NULL );
curr = h[hCurr].node;
n[curr].handle = n[pq->size].handle;
h[n[curr].handle].node = curr;
if( curr <= -- pq->size ) {
if( curr <= 1 || LEQ( h[n[curr>>1].handle].key, h[n[curr].handle].key )) {
FloatDown( pq, curr );
} else {
FloatUp( pq, curr );
}
}
h[hCurr].key = NULL;
h[hCurr].node = pq->freeList;
pq->freeList = hCurr;
}

View File

@ -1,107 +0,0 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#ifndef __priorityq_heap_h_
#define __priorityq_heap_h_
/* Use #define's so that another heap implementation can use this one */
#define PQkey PQHeapKey
#define PQhandle PQHeapHandle
#define PriorityQ PriorityQHeap
#define pqNewPriorityQ(leq) __gl_pqHeapNewPriorityQ(leq)
#define pqDeletePriorityQ(pq) __gl_pqHeapDeletePriorityQ(pq)
/* The basic operations are insertion of a new key (pqInsert),
* and examination/extraction of a key whose value is minimum
* (pqMinimum/pqExtractMin). Deletion is also allowed (pqDelete);
* for this purpose pqInsert returns a "handle" which is supplied
* as the argument.
*
* An initial heap may be created efficiently by calling pqInsert
* repeatedly, then calling pqInit. In any case pqInit must be called
* before any operations other than pqInsert are used.
*
* If the heap is empty, pqMinimum/pqExtractMin will return a NULL key.
* This may also be tested with pqIsEmpty.
*/
#define pqInit(pq) __gl_pqHeapInit(pq)
#define pqInsert(pq,key) __gl_pqHeapInsert(pq,key)
#define pqMinimum(pq) __gl_pqHeapMinimum(pq)
#define pqExtractMin(pq) __gl_pqHeapExtractMin(pq)
#define pqDelete(pq,handle) __gl_pqHeapDelete(pq,handle)
#define pqIsEmpty(pq) __gl_pqHeapIsEmpty(pq)
/* Since we support deletion the data structure is a little more
* complicated than an ordinary heap. "nodes" is the heap itself;
* active nodes are stored in the range 1..pq->size. When the
* heap exceeds its allocated size (pq->max), its size doubles.
* The children of node i are nodes 2i and 2i+1.
*
* Each node stores an index into an array "handles". Each handle
* stores a key, plus a pointer back to the node which currently
* represents that key (ie. nodes[handles[i].node].handle == i).
*/
typedef void *PQkey;
typedef long PQhandle;
typedef struct PriorityQ PriorityQ;
typedef struct { PQhandle handle; } PQnode;
typedef struct { PQkey key; PQhandle node; } PQhandleElem;
struct PriorityQ {
PQnode *nodes;
PQhandleElem *handles;
long size, max;
PQhandle freeList;
int initialized;
int (*leq)(PQkey key1, PQkey key2);
};
PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) );
void pqDeletePriorityQ( PriorityQ *pq );
void pqInit( PriorityQ *pq );
PQhandle pqInsert( PriorityQ *pq, PQkey key );
PQkey pqExtractMin( PriorityQ *pq );
void pqDelete( PriorityQ *pq, PQhandle handle );
#define __gl_pqHeapMinimum(pq) ((pq)->handles[(pq)->nodes[1].handle].key)
#define __gl_pqHeapIsEmpty(pq) ((pq)->size == 0)
#endif

View File

@ -1,117 +0,0 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#ifndef __priorityq_sort_h_
#define __priorityq_sort_h_
#include "priorityq-heap.h"
#undef PQkey
#undef PQhandle
#undef PriorityQ
#undef pqNewPriorityQ
#undef pqDeletePriorityQ
#undef pqInit
#undef pqInsert
#undef pqMinimum
#undef pqExtractMin
#undef pqDelete
#undef pqIsEmpty
/* Use #define's so that another heap implementation can use this one */
#define PQkey PQSortKey
#define PQhandle PQSortHandle
#define PriorityQ PriorityQSort
#define pqNewPriorityQ(leq) __gl_pqSortNewPriorityQ(leq)
#define pqDeletePriorityQ(pq) __gl_pqSortDeletePriorityQ(pq)
/* The basic operations are insertion of a new key (pqInsert),
* and examination/extraction of a key whose value is minimum
* (pqMinimum/pqExtractMin). Deletion is also allowed (pqDelete);
* for this purpose pqInsert returns a "handle" which is supplied
* as the argument.
*
* An initial heap may be created efficiently by calling pqInsert
* repeatedly, then calling pqInit. In any case pqInit must be called
* before any operations other than pqInsert are used.
*
* If the heap is empty, pqMinimum/pqExtractMin will return a NULL key.
* This may also be tested with pqIsEmpty.
*/
#define pqInit(pq) __gl_pqSortInit(pq)
#define pqInsert(pq,key) __gl_pqSortInsert(pq,key)
#define pqMinimum(pq) __gl_pqSortMinimum(pq)
#define pqExtractMin(pq) __gl_pqSortExtractMin(pq)
#define pqDelete(pq,handle) __gl_pqSortDelete(pq,handle)
#define pqIsEmpty(pq) __gl_pqSortIsEmpty(pq)
/* Since we support deletion the data structure is a little more
* complicated than an ordinary heap. "nodes" is the heap itself;
* active nodes are stored in the range 1..pq->size. When the
* heap exceeds its allocated size (pq->max), its size doubles.
* The children of node i are nodes 2i and 2i+1.
*
* Each node stores an index into an array "handles". Each handle
* stores a key, plus a pointer back to the node which currently
* represents that key (ie. nodes[handles[i].node].handle == i).
*/
typedef PQHeapKey PQkey;
typedef PQHeapHandle PQhandle;
typedef struct PriorityQ PriorityQ;
struct PriorityQ {
PriorityQHeap *heap;
PQkey *keys;
PQkey **order;
PQhandle size, max;
int initialized;
int (*leq)(PQkey key1, PQkey key2);
};
PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) );
void pqDeletePriorityQ( PriorityQ *pq );
int pqInit( PriorityQ *pq );
PQhandle pqInsert( PriorityQ *pq, PQkey key );
PQkey pqExtractMin( PriorityQ *pq );
void pqDelete( PriorityQ *pq, PQhandle handle );
PQkey pqMinimum( PriorityQ *pq );
int pqIsEmpty( PriorityQ *pq );
#endif

View File

@ -1,261 +0,0 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#include "gluos.h"
#include <stddef.h>
#include <assert.h>
#include <limits.h> /* LONG_MAX */
#include "memalloc.h"
/* Include all the code for the regular heap-based queue here. */
#include "priorityq-heap.c"
/* Now redefine all the function names to map to their "Sort" versions. */
#include "priorityq-sort.h"
/* really __gl_pqSortNewPriorityQ */
PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) )
{
PriorityQ *pq = (PriorityQ *)memAlloc( sizeof( PriorityQ ));
if (pq == NULL) return NULL;
pq->heap = __gl_pqHeapNewPriorityQ( leq );
if (pq->heap == NULL) {
memFree(pq);
return NULL;
}
pq->keys = (PQHeapKey *)memAlloc( INIT_SIZE * sizeof(pq->keys[0]) );
if (pq->keys == NULL) {
__gl_pqHeapDeletePriorityQ(pq->heap);
memFree(pq);
return NULL;
}
pq->order = NULL;
pq->size = 0;
pq->max = INIT_SIZE;
pq->initialized = FALSE;
pq->leq = leq;
return pq;
}
/* really __gl_pqSortDeletePriorityQ */
void pqDeletePriorityQ( PriorityQ *pq )
{
assert(pq != NULL);
if (pq->heap != NULL) __gl_pqHeapDeletePriorityQ( pq->heap );
if (pq->order != NULL) memFree( pq->order );
if (pq->keys != NULL) memFree( pq->keys );
memFree( pq );
}
#define LT(x,y) (! LEQ(y,x))
#define GT(x,y) (! LEQ(x,y))
#define Swap(a,b) do{PQkey *tmp = *a; *a = *b; *b = tmp;}while(0)
/* really __gl_pqSortInit */
int pqInit( PriorityQ *pq )
{
PQkey **p, **r, **i, **j, *piv;
struct { PQkey **p, **r; } Stack[50], *top = Stack;
unsigned long seed = 2016473283;
/* Create an array of indirect pointers to the keys, so that we
* the handles we have returned are still valid.
*/
/*
pq->order = (PQHeapKey **)memAlloc( (size_t)
(pq->size * sizeof(pq->order[0])) );
*/
pq->order = (PQHeapKey **)memAlloc( (size_t)
((pq->size+1) * sizeof(pq->order[0])) );
/* the previous line is a patch to compensate for the fact that IBM */
/* machines return a null on a malloc of zero bytes (unlike SGI), */
/* so we have to put in this defense to guard against a memory */
/* fault four lines down. from fossum@austin.ibm.com. */
if (pq->order == NULL) return 0;
p = pq->order;
r = p + pq->size - 1;
for( piv = pq->keys, i = p; i <= r; ++piv, ++i ) {
*i = piv;
}
/* Sort the indirect pointers in descending order,
* using randomized Quicksort
*/
top->p = p; top->r = r; ++top;
while( --top >= Stack ) {
p = top->p;
r = top->r;
while( r > p + 10 ) {
seed = seed * 1539415821 + 1;
i = p + seed % (r - p + 1);
piv = *i;
*i = *p;
*p = piv;
i = p - 1;
j = r + 1;
do {
do { ++i; } while( GT( **i, *piv ));
do { --j; } while( LT( **j, *piv ));
Swap( i, j );
} while( i < j );
Swap( i, j ); /* Undo last swap */
if( i - p < r - j ) {
top->p = j+1; top->r = r; ++top;
r = i-1;
} else {
top->p = p; top->r = i-1; ++top;
p = j+1;
}
}
/* Insertion sort small lists */
for( i = p+1; i <= r; ++i ) {
piv = *i;
for( j = i; j > p && LT( **(j-1), *piv ); --j ) {
*j = *(j-1);
}
*j = piv;
}
}
pq->max = pq->size;
pq->initialized = TRUE;
__gl_pqHeapInit( pq->heap ); /* always succeeds */
#ifndef NDEBUG
p = pq->order;
r = p + pq->size - 1;
for( i = p; i < r; ++i ) {
assert( LEQ( **(i+1), **i ));
}
#endif
return 1;
}
/* really __gl_pqSortInsert */
/* returns LONG_MAX iff out of memory */
PQhandle pqInsert( PriorityQ *pq, PQkey keyNew )
{
long curr;
if( pq->initialized ) {
return __gl_pqHeapInsert( pq->heap, keyNew );
}
curr = pq->size;
if( ++ pq->size >= pq->max ) {
PQkey *saveKey= pq->keys;
/* If the heap overflows, double its size. */
pq->max <<= 1;
pq->keys = (PQHeapKey *)memRealloc( pq->keys,
(size_t)
(pq->max * sizeof( pq->keys[0] )));
if (pq->keys == NULL) {
pq->keys = saveKey; /* restore ptr to free upon return */
return LONG_MAX;
}
}
assert(curr != LONG_MAX);
pq->keys[curr] = keyNew;
/* Negative handles index the sorted array. */
return -(curr+1);
}
/* really __gl_pqSortExtractMin */
PQkey pqExtractMin( PriorityQ *pq )
{
PQkey sortMin, heapMin;
if( pq->size == 0 ) {
return __gl_pqHeapExtractMin( pq->heap );
}
sortMin = *(pq->order[pq->size-1]);
if( ! __gl_pqHeapIsEmpty( pq->heap )) {
heapMin = __gl_pqHeapMinimum( pq->heap );
if( LEQ( heapMin, sortMin )) {
return __gl_pqHeapExtractMin( pq->heap );
}
}
do {
-- pq->size;
} while( pq->size > 0 && *(pq->order[pq->size-1]) == NULL );
return sortMin;
}
/* really __gl_pqSortMinimum */
PQkey pqMinimum( PriorityQ *pq )
{
PQkey sortMin, heapMin;
if( pq->size == 0 ) {
return __gl_pqHeapMinimum( pq->heap );
}
sortMin = *(pq->order[pq->size-1]);
if( ! __gl_pqHeapIsEmpty( pq->heap )) {
heapMin = __gl_pqHeapMinimum( pq->heap );
if( LEQ( heapMin, sortMin )) {
return heapMin;
}
}
return sortMin;
}
/* really __gl_pqSortIsEmpty */
int pqIsEmpty( PriorityQ *pq )
{
return (pq->size == 0) && __gl_pqHeapIsEmpty( pq->heap );
}
/* really __gl_pqSortDelete */
void pqDelete( PriorityQ *pq, PQhandle curr )
{
if( curr >= 0 ) {
__gl_pqHeapDelete( pq->heap, curr );
return;
}
curr = -(curr+1);
assert( curr < pq->max && pq->keys[curr] != NULL );
pq->keys[curr] = NULL;
while( pq->size > 0 && *(pq->order[pq->size-1]) == NULL ) {
-- pq->size;
}
}

View File

@ -1,117 +0,0 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#ifndef __priorityq_sort_h_
#define __priorityq_sort_h_
#include "priorityq-heap.h"
#undef PQkey
#undef PQhandle
#undef PriorityQ
#undef pqNewPriorityQ
#undef pqDeletePriorityQ
#undef pqInit
#undef pqInsert
#undef pqMinimum
#undef pqExtractMin
#undef pqDelete
#undef pqIsEmpty
/* Use #define's so that another heap implementation can use this one */
#define PQkey PQSortKey
#define PQhandle PQSortHandle
#define PriorityQ PriorityQSort
#define pqNewPriorityQ(leq) __gl_pqSortNewPriorityQ(leq)
#define pqDeletePriorityQ(pq) __gl_pqSortDeletePriorityQ(pq)
/* The basic operations are insertion of a new key (pqInsert),
* and examination/extraction of a key whose value is minimum
* (pqMinimum/pqExtractMin). Deletion is also allowed (pqDelete);
* for this purpose pqInsert returns a "handle" which is supplied
* as the argument.
*
* An initial heap may be created efficiently by calling pqInsert
* repeatedly, then calling pqInit. In any case pqInit must be called
* before any operations other than pqInsert are used.
*
* If the heap is empty, pqMinimum/pqExtractMin will return a NULL key.
* This may also be tested with pqIsEmpty.
*/
#define pqInit(pq) __gl_pqSortInit(pq)
#define pqInsert(pq,key) __gl_pqSortInsert(pq,key)
#define pqMinimum(pq) __gl_pqSortMinimum(pq)
#define pqExtractMin(pq) __gl_pqSortExtractMin(pq)
#define pqDelete(pq,handle) __gl_pqSortDelete(pq,handle)
#define pqIsEmpty(pq) __gl_pqSortIsEmpty(pq)
/* Since we support deletion the data structure is a little more
* complicated than an ordinary heap. "nodes" is the heap itself;
* active nodes are stored in the range 1..pq->size. When the
* heap exceeds its allocated size (pq->max), its size doubles.
* The children of node i are nodes 2i and 2i+1.
*
* Each node stores an index into an array "handles". Each handle
* stores a key, plus a pointer back to the node which currently
* represents that key (ie. nodes[handles[i].node].handle == i).
*/
typedef PQHeapKey PQkey;
typedef PQHeapHandle PQhandle;
typedef struct PriorityQ PriorityQ;
struct PriorityQ {
PriorityQHeap *heap;
PQkey *keys;
PQkey **order;
PQhandle size, max;
int initialized;
int (*leq)(PQkey key1, PQkey key2);
};
PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) );
void pqDeletePriorityQ( PriorityQ *pq );
int pqInit( PriorityQ *pq );
PQhandle pqInsert( PriorityQ *pq, PQkey key );
PQkey pqExtractMin( PriorityQ *pq );
void pqDelete( PriorityQ *pq, PQhandle handle );
PQkey pqMinimum( PriorityQ *pq );
int pqIsEmpty( PriorityQ *pq );
#endif

View File

@ -1,502 +0,0 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#include "gluos.h"
#include <assert.h>
#include <stddef.h>
#include "mesh.h"
#include "tess.h"
#include "render.h"
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
/* This structure remembers the information we need about a primitive
* to be able to render it later, once we have determined which
* primitive is able to use the most triangles.
*/
struct FaceCount {
long size; /* number of triangles used */
GLUhalfEdge *eStart; /* edge where this primitive starts */
void (*render)(GLUtesselator *, GLUhalfEdge *, long);
/* routine to render this primitive */
};
static struct FaceCount MaximumFan( GLUhalfEdge *eOrig );
static struct FaceCount MaximumStrip( GLUhalfEdge *eOrig );
static void RenderFan( GLUtesselator *tess, GLUhalfEdge *eStart, long size );
static void RenderStrip( GLUtesselator *tess, GLUhalfEdge *eStart, long size );
static void RenderTriangle( GLUtesselator *tess, GLUhalfEdge *eStart,
long size );
static void RenderMaximumFaceGroup( GLUtesselator *tess, GLUface *fOrig );
static void RenderLonelyTriangles( GLUtesselator *tess, GLUface *head );
/************************ Strips and Fans decomposition ******************/
/* __gl_renderMesh( tess, mesh ) takes a mesh and breaks it into triangle
* fans, strips, and separate triangles. A substantial effort is made
* to use as few rendering primitives as possible (ie. to make the fans
* and strips as large as possible).
*
* The rendering output is provided as callbacks (see the api).
*/
void __gl_renderMesh( GLUtesselator *tess, GLUmesh *mesh )
{
GLUface *f;
/* Make a list of separate triangles so we can render them all at once */
tess->lonelyTriList = NULL;
for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next ) {
f->marked = FALSE;
}
for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next ) {
/* We examine all faces in an arbitrary order. Whenever we find
* an unprocessed face F, we output a group of faces including F
* whose size is maximum.
*/
if( f->inside && ! f->marked ) {
RenderMaximumFaceGroup( tess, f );
assert( f->marked );
}
}
if( tess->lonelyTriList != NULL ) {
RenderLonelyTriangles( tess, tess->lonelyTriList );
tess->lonelyTriList = NULL;
}
}
static void RenderMaximumFaceGroup( GLUtesselator *tess, GLUface *fOrig )
{
/* We want to find the largest triangle fan or strip of unmarked faces
* which includes the given face fOrig. There are 3 possible fans
* passing through fOrig (one centered at each vertex), and 3 possible
* strips (one for each CCW permutation of the vertices). Our strategy
* is to try all of these, and take the primitive which uses the most
* triangles (a greedy approach).
*/
GLUhalfEdge *e = fOrig->anEdge;
struct FaceCount max, newFace;
max.size = 1;
max.eStart = e;
max.render = &RenderTriangle;
if( ! tess->flagBoundary ) {
newFace = MaximumFan( e ); if( newFace.size > max.size ) { max = newFace; }
newFace = MaximumFan( e->Lnext ); if( newFace.size > max.size ) { max = newFace; }
newFace = MaximumFan( e->Lprev ); if( newFace.size > max.size ) { max = newFace; }
newFace = MaximumStrip( e ); if( newFace.size > max.size ) { max = newFace; }
newFace = MaximumStrip( e->Lnext ); if( newFace.size > max.size ) { max = newFace; }
newFace = MaximumStrip( e->Lprev ); if( newFace.size > max.size ) { max = newFace; }
}
(*(max.render))( tess, max.eStart, max.size );
}
/* Macros which keep track of faces we have marked temporarily, and allow
* us to backtrack when necessary. With triangle fans, this is not
* really necessary, since the only awkward case is a loop of triangles
* around a single origin vertex. However with strips the situation is
* more complicated, and we need a general tracking method like the
* one here.
*/
#define Marked(f) (! (f)->inside || (f)->marked)
#define AddToTrail(f,t) ((f)->trail = (t), (t) = (f), (f)->marked = TRUE)
#define FreeTrail(t) do { \
while( (t) != NULL ) { \
(t)->marked = FALSE; t = (t)->trail; \
} \
} while(0) /* absorb trailing semicolon */
static struct FaceCount MaximumFan( GLUhalfEdge *eOrig )
{
/* eOrig->Lface is the face we want to render. We want to find the size
* of a maximal fan around eOrig->Org. To do this we just walk around
* the origin vertex as far as possible in both directions.
*/
struct FaceCount newFace = { 0, NULL, &RenderFan };
GLUface *trail = NULL;
GLUhalfEdge *e;
for( e = eOrig; ! Marked( e->Lface ); e = e->Onext ) {
AddToTrail( e->Lface, trail );
++newFace.size;
}
for( e = eOrig; ! Marked( e->Rface ); e = e->Oprev ) {
AddToTrail( e->Rface, trail );
++newFace.size;
}
newFace.eStart = e;
/*LINTED*/
FreeTrail( trail );
return newFace;
}
#define IsEven(n) (((n) & 1) == 0)
static struct FaceCount MaximumStrip( GLUhalfEdge *eOrig )
{
/* Here we are looking for a maximal strip that contains the vertices
* eOrig->Org, eOrig->Dst, eOrig->Lnext->Dst (in that order or the
* reverse, such that all triangles are oriented CCW).
*
* Again we walk forward and backward as far as possible. However for
* strips there is a twist: to get CCW orientations, there must be
* an *even* number of triangles in the strip on one side of eOrig.
* We walk the strip starting on a side with an even number of triangles;
* if both side have an odd number, we are forced to shorten one side.
*/
struct FaceCount newFace = { 0, NULL, &RenderStrip };
long headSize = 0, tailSize = 0;
GLUface *trail = NULL;
GLUhalfEdge *e, *eTail, *eHead;
for( e = eOrig; ! Marked( e->Lface ); ++tailSize, e = e->Onext ) {
AddToTrail( e->Lface, trail );
++tailSize;
e = e->Dprev;
if( Marked( e->Lface )) break;
AddToTrail( e->Lface, trail );
}
eTail = e;
for( e = eOrig; ! Marked( e->Rface ); ++headSize, e = e->Dnext ) {
AddToTrail( e->Rface, trail );
++headSize;
e = e->Oprev;
if( Marked( e->Rface )) break;
AddToTrail( e->Rface, trail );
}
eHead = e;
newFace.size = tailSize + headSize;
if( IsEven( tailSize )) {
newFace.eStart = eTail->Sym;
} else if( IsEven( headSize )) {
newFace.eStart = eHead;
} else {
/* Both sides have odd length, we must shorten one of them. In fact,
* we must start from eHead to guarantee inclusion of eOrig->Lface.
*/
--newFace.size;
newFace.eStart = eHead->Onext;
}
/*LINTED*/
FreeTrail( trail );
return newFace;
}
static void RenderTriangle( GLUtesselator *tess, GLUhalfEdge *e, long size )
{
/* Just add the triangle to a triangle list, so we can render all
* the separate triangles at once.
*/
assert( size == 1 );
AddToTrail( e->Lface, tess->lonelyTriList );
}
static void RenderLonelyTriangles( GLUtesselator *tess, GLUface *f )
{
/* Now we render all the separate triangles which could not be
* grouped into a triangle fan or strip.
*/
GLUhalfEdge *e;
int newState;
int edgeState = -1; /* force edge state output for first vertex */
CALL_BEGIN_OR_BEGIN_DATA( GL_TRIANGLES );
for( ; f != NULL; f = f->trail ) {
/* Loop once for each edge (there will always be 3 edges) */
e = f->anEdge;
do {
if( tess->flagBoundary ) {
/* Set the "edge state" to TRUE just before we output the
* first vertex of each edge on the polygon boundary.
*/
newState = ! e->Rface->inside;
if( edgeState != newState ) {
edgeState = newState;
CALL_EDGE_FLAG_OR_EDGE_FLAG_DATA( edgeState );
}
}
CALL_VERTEX_OR_VERTEX_DATA( e->Org->data );
e = e->Lnext;
} while( e != f->anEdge );
}
CALL_END_OR_END_DATA();
}
static void RenderFan( GLUtesselator *tess, GLUhalfEdge *e, long size )
{
/* Render as many CCW triangles as possible in a fan starting from
* edge "e". The fan *should* contain exactly "size" triangles
* (otherwise we've goofed up somewhere).
*/
CALL_BEGIN_OR_BEGIN_DATA( GL_TRIANGLE_FAN );
CALL_VERTEX_OR_VERTEX_DATA( e->Org->data );
CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data );
while( ! Marked( e->Lface )) {
e->Lface->marked = TRUE;
--size;
e = e->Onext;
CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data );
}
assert( size == 0 );
CALL_END_OR_END_DATA();
}
static void RenderStrip( GLUtesselator *tess, GLUhalfEdge *e, long size )
{
/* Render as many CCW triangles as possible in a strip starting from
* edge "e". The strip *should* contain exactly "size" triangles
* (otherwise we've goofed up somewhere).
*/
CALL_BEGIN_OR_BEGIN_DATA( GL_TRIANGLE_STRIP );
CALL_VERTEX_OR_VERTEX_DATA( e->Org->data );
CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data );
while( ! Marked( e->Lface )) {
e->Lface->marked = TRUE;
--size;
e = e->Dprev;
CALL_VERTEX_OR_VERTEX_DATA( e->Org->data );
if( Marked( e->Lface )) break;
e->Lface->marked = TRUE;
--size;
e = e->Onext;
CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data );
}
assert( size == 0 );
CALL_END_OR_END_DATA();
}
/************************ Boundary contour decomposition ******************/
/* __gl_renderBoundary( tess, mesh ) takes a mesh, and outputs one
* contour for each face marked "inside". The rendering output is
* provided as callbacks (see the api).
*/
void __gl_renderBoundary( GLUtesselator *tess, GLUmesh *mesh )
{
GLUface *f;
GLUhalfEdge *e;
for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next ) {
if( f->inside ) {
CALL_BEGIN_OR_BEGIN_DATA( GL_LINE_LOOP );
e = f->anEdge;
do {
CALL_VERTEX_OR_VERTEX_DATA( e->Org->data );
e = e->Lnext;
} while( e != f->anEdge );
CALL_END_OR_END_DATA();
}
}
}
/************************ Quick-and-dirty decomposition ******************/
#define SIGN_INCONSISTENT 2
static int ComputeNormal( GLUtesselator *tess, GLdouble norm[3], int check )
/*
* If check==FALSE, we compute the polygon normal and place it in norm[].
* If check==TRUE, we check that each triangle in the fan from v0 has a
* consistent orientation with respect to norm[]. If triangles are
* consistently oriented CCW, return 1; if CW, return -1; if all triangles
* are degenerate return 0; otherwise (no consistent orientation) return
* SIGN_INCONSISTENT.
*/
{
CachedVertex *v0 = tess->cache;
CachedVertex *vn = v0 + tess->cacheCount;
CachedVertex *vc;
GLdouble dot, xc, yc, zc, xp, yp, zp, n[3];
int sign = 0;
/* Find the polygon normal. It is important to get a reasonable
* normal even when the polygon is self-intersecting (eg. a bowtie).
* Otherwise, the computed normal could be very tiny, but perpendicular
* to the true plane of the polygon due to numerical noise. Then all
* the triangles would appear to be degenerate and we would incorrectly
* decompose the polygon as a fan (or simply not render it at all).
*
* We use a sum-of-triangles normal algorithm rather than the more
* efficient sum-of-trapezoids method (used in CheckOrientation()
* in normal.c). This lets us explicitly reverse the signed area
* of some triangles to get a reasonable normal in the self-intersecting
* case.
*/
if( ! check ) {
norm[0] = norm[1] = norm[2] = 0.0;
}
vc = v0 + 1;
xc = vc->coords[0] - v0->coords[0];
yc = vc->coords[1] - v0->coords[1];
zc = vc->coords[2] - v0->coords[2];
while( ++vc < vn ) {
xp = xc; yp = yc; zp = zc;
xc = vc->coords[0] - v0->coords[0];
yc = vc->coords[1] - v0->coords[1];
zc = vc->coords[2] - v0->coords[2];
/* Compute (vp - v0) cross (vc - v0) */
n[0] = yp*zc - zp*yc;
n[1] = zp*xc - xp*zc;
n[2] = xp*yc - yp*xc;
dot = n[0]*norm[0] + n[1]*norm[1] + n[2]*norm[2];
if( ! check ) {
/* Reverse the contribution of back-facing triangles to get
* a reasonable normal for self-intersecting polygons (see above)
*/
if( dot >= 0 ) {
norm[0] += n[0]; norm[1] += n[1]; norm[2] += n[2];
} else {
norm[0] -= n[0]; norm[1] -= n[1]; norm[2] -= n[2];
}
} else if( dot != 0 ) {
/* Check the new orientation for consistency with previous triangles */
if( dot > 0 ) {
if( sign < 0 ) return SIGN_INCONSISTENT;
sign = 1;
} else {
if( sign > 0 ) return SIGN_INCONSISTENT;
sign = -1;
}
}
}
return sign;
}
/* __gl_renderCache( tess ) takes a single contour and tries to render it
* as a triangle fan. This handles convex polygons, as well as some
* non-convex polygons if we get lucky.
*
* Returns TRUE if the polygon was successfully rendered. The rendering
* output is provided as callbacks (see the api).
*/
GLboolean __gl_renderCache( GLUtesselator *tess )
{
CachedVertex *v0 = tess->cache;
CachedVertex *vn = v0 + tess->cacheCount;
CachedVertex *vc;
GLdouble norm[3];
int sign;
if( tess->cacheCount < 3 ) {
/* Degenerate contour -- no output */
return TRUE;
}
norm[0] = tess->normal[0];
norm[1] = tess->normal[1];
norm[2] = tess->normal[2];
if( norm[0] == 0 && norm[1] == 0 && norm[2] == 0 ) {
ComputeNormal( tess, norm, FALSE );
}
sign = ComputeNormal( tess, norm, TRUE );
if( sign == SIGN_INCONSISTENT ) {
/* Fan triangles did not have a consistent orientation */
return FALSE;
}
if( sign == 0 ) {
/* All triangles were degenerate */
return TRUE;
}
/* Make sure we do the right thing for each winding rule */
switch( tess->windingRule ) {
case GLU_TESS_WINDING_ODD:
case GLU_TESS_WINDING_NONZERO:
break;
case GLU_TESS_WINDING_POSITIVE:
if( sign < 0 ) return TRUE;
break;
case GLU_TESS_WINDING_NEGATIVE:
if( sign > 0 ) return TRUE;
break;
case GLU_TESS_WINDING_ABS_GEQ_TWO:
return TRUE;
}
CALL_BEGIN_OR_BEGIN_DATA( tess->boundaryOnly ? GL_LINE_LOOP
: (tess->cacheCount > 3) ? GL_TRIANGLE_FAN
: GL_TRIANGLES );
CALL_VERTEX_OR_VERTEX_DATA( v0->data );
if( sign > 0 ) {
for( vc = v0+1; vc < vn; ++vc ) {
CALL_VERTEX_OR_VERTEX_DATA( vc->data );
}
} else {
for( vc = vn-1; vc > v0; --vc ) {
CALL_VERTEX_OR_VERTEX_DATA( vc->data );
}
}
CALL_END_OR_END_DATA();
return TRUE;
}

View File

@ -1,53 +0,0 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#ifndef __render_h_
#define __render_h_
#include "mesh.h"
#include "tesselator.h"
/* __gl_renderMesh( tess, mesh ) takes a mesh and breaks it into triangle
* fans, strips, and separate triangles. A substantial effort is made
* to use as few rendering primitives as possible (ie. to make the fans
* and strips as large as possible).
*
* The rendering output is provided as callbacks (see the api).
*/
void __gl_renderMesh( GLUtesselator *tess, GLUmesh *mesh );
void __gl_renderBoundary( GLUtesselator *tess, GLUmesh *mesh );
GLboolean __gl_renderCache( GLUtesselator *tess );
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,78 +0,0 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#ifndef __sweep_h_
#define __sweep_h_
#include "mesh.h"
#include "tesselator.h"
/* __gl_computeInterior( tess ) computes the planar arrangement specified
* by the given contours, and further subdivides this arrangement
* into regions. Each region is marked "inside" if it belongs
* to the polygon, according to the rule given by tess->windingRule.
* Each interior region is guaranteed be monotone.
*/
int __gl_computeInterior( GLUtesselator *tess );
/* The following is here *only* for access by debugging routines */
#include "dict.h"
/* For each pair of adjacent edges crossing the sweep line, there is
* an ActiveRegion to represent the region between them. The active
* regions are kept in sorted order in a dynamic dictionary. As the
* sweep line crosses each vertex, we update the affected regions.
*/
struct ActiveRegion {
GLUhalfEdge *eUp; /* upper edge, directed right to left */
DictNode *nodeUp; /* dictionary node corresponding to eUp */
int windingNumber; /* used to determine which regions are
* inside the polygon */
GLboolean inside; /* is this region inside the polygon? */
GLboolean sentinel; /* marks fake edges at t = +/-infinity */
GLboolean dirty; /* marks regions where the upper or lower
* edge has changed, but we haven't checked
* whether they intersect yet */
GLboolean fixUpperEdge; /* marks temporary edges introduced when
* we process a "right vertex" (one without
* any edges leaving to the right) */
};
#define RegionBelow(r) ((ActiveRegion *) dictKey(dictPred((r)->nodeUp)))
#define RegionAbove(r) ((ActiveRegion *) dictKey(dictSucc((r)->nodeUp)))
#endif

View File

@ -1,632 +0,0 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#include "gluos.h"
#include <stddef.h>
#include <assert.h>
#include <setjmp.h>
#include "memalloc.h"
#include "tess.h"
#include "mesh.h"
#include "normal.h"
#include "sweep.h"
#include "tessmono.h"
#include "render.h"
#define GLU_TESS_DEFAULT_TOLERANCE 0.0
#define GLU_TESS_MESH 100112 /* void (*)(GLUmesh *mesh) */
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
/*ARGSUSED*/ static void GLAPIENTRY noBegin( GLenum type ) {}
/*ARGSUSED*/ static void GLAPIENTRY noEdgeFlag( GLboolean boundaryEdge ) {}
/*ARGSUSED*/ static void GLAPIENTRY noVertex( void *data ) {}
/*ARGSUSED*/ static void GLAPIENTRY noEnd( void ) {}
/*ARGSUSED*/ static void GLAPIENTRY noError( GLenum errnum ) {}
/*ARGSUSED*/ static void GLAPIENTRY noCombine( GLdouble coords[3], void *data[4],
GLfloat weight[4], void **dataOut ) {}
/*ARGSUSED*/ static void GLAPIENTRY noMesh( GLUmesh *mesh ) {}
/*ARGSUSED*/ void GLAPIENTRY __gl_noBeginData( GLenum type,
void *polygonData ) {}
/*ARGSUSED*/ void GLAPIENTRY __gl_noEdgeFlagData( GLboolean boundaryEdge,
void *polygonData ) {}
/*ARGSUSED*/ void GLAPIENTRY __gl_noVertexData( void *data,
void *polygonData ) {}
/*ARGSUSED*/ void GLAPIENTRY __gl_noEndData( void *polygonData ) {}
/*ARGSUSED*/ void GLAPIENTRY __gl_noErrorData( GLenum errnum,
void *polygonData ) {}
/*ARGSUSED*/ void GLAPIENTRY __gl_noCombineData( GLdouble coords[3],
void *data[4],
GLfloat weight[4],
void **outData,
void *polygonData ) {}
/* Half-edges are allocated in pairs (see mesh.c) */
typedef struct { GLUhalfEdge e, eSym; } EdgePair;
#undef MAX
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define MAX_FAST_ALLOC (MAX(sizeof(EdgePair), \
MAX(sizeof(GLUvertex),sizeof(GLUface))))
GLUtesselator * GLAPIENTRY
gluNewTess( void )
{
GLUtesselator *tess;
/* Only initialize fields which can be changed by the api. Other fields
* are initialized where they are used.
*/
if (memInit( MAX_FAST_ALLOC ) == 0) {
return 0; /* out of memory */
}
tess = (GLUtesselator *)memAlloc( sizeof( GLUtesselator ));
if (tess == NULL) {
return 0; /* out of memory */
}
tess->state = T_DORMANT;
tess->normal[0] = 0;
tess->normal[1] = 0;
tess->normal[2] = 0;
tess->relTolerance = GLU_TESS_DEFAULT_TOLERANCE;
tess->windingRule = GLU_TESS_WINDING_ODD;
tess->flagBoundary = FALSE;
tess->boundaryOnly = FALSE;
tess->callBegin = &noBegin;
tess->callEdgeFlag = &noEdgeFlag;
tess->callVertex = &noVertex;
tess->callEnd = &noEnd;
tess->callError = &noError;
tess->callCombine = &noCombine;
tess->callMesh = &noMesh;
tess->callBeginData= &__gl_noBeginData;
tess->callEdgeFlagData= &__gl_noEdgeFlagData;
tess->callVertexData= &__gl_noVertexData;
tess->callEndData= &__gl_noEndData;
tess->callErrorData= &__gl_noErrorData;
tess->callCombineData= &__gl_noCombineData;
tess->polygonData= NULL;
return tess;
}
static void MakeDormant( GLUtesselator *tess )
{
/* Return the tessellator to its original dormant state. */
if( tess->mesh != NULL ) {
__gl_meshDeleteMesh( tess->mesh );
}
tess->state = T_DORMANT;
tess->lastEdge = NULL;
tess->mesh = NULL;
}
#define RequireState( tess, s ) if( tess->state != s ) GotoState(tess,s)
static void GotoState( GLUtesselator *tess, enum TessState newState )
{
while( tess->state != newState ) {
/* We change the current state one level at a time, to get to
* the desired state.
*/
if( tess->state < newState ) {
switch( tess->state ) {
case T_DORMANT:
CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_BEGIN_POLYGON );
gluTessBeginPolygon( tess, NULL );
break;
case T_IN_POLYGON:
CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_BEGIN_CONTOUR );
gluTessBeginContour( tess );
break;
default:
;
}
} else {
switch( tess->state ) {
case T_IN_CONTOUR:
CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_END_CONTOUR );
gluTessEndContour( tess );
break;
case T_IN_POLYGON:
CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_END_POLYGON );
/* gluTessEndPolygon( tess ) is too much work! */
MakeDormant( tess );
break;
default:
;
}
}
}
}
void GLAPIENTRY
gluDeleteTess( GLUtesselator *tess )
{
RequireState( tess, T_DORMANT );
memFree( tess );
}
void GLAPIENTRY
gluTessProperty( GLUtesselator *tess, GLenum which, GLdouble value )
{
GLenum windingRule;
switch( which ) {
case GLU_TESS_TOLERANCE:
if( value < 0.0 || value > 1.0 ) break;
tess->relTolerance = value;
return;
case GLU_TESS_WINDING_RULE:
windingRule = (GLenum) value;
if( windingRule != value ) break; /* not an integer */
switch( windingRule ) {
case GLU_TESS_WINDING_ODD:
case GLU_TESS_WINDING_NONZERO:
case GLU_TESS_WINDING_POSITIVE:
case GLU_TESS_WINDING_NEGATIVE:
case GLU_TESS_WINDING_ABS_GEQ_TWO:
tess->windingRule = windingRule;
return;
default:
break;
}
case GLU_TESS_BOUNDARY_ONLY:
tess->boundaryOnly = (value != 0);
return;
default:
CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_ENUM );
return;
}
CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_VALUE );
}
/* Returns tessellator property */
void GLAPIENTRY
gluGetTessProperty( GLUtesselator *tess, GLenum which, GLdouble *value )
{
switch (which) {
case GLU_TESS_TOLERANCE:
/* tolerance should be in range [0..1] */
assert(0.0 <= tess->relTolerance && tess->relTolerance <= 1.0);
*value= tess->relTolerance;
break;
case GLU_TESS_WINDING_RULE:
assert(tess->windingRule == GLU_TESS_WINDING_ODD ||
tess->windingRule == GLU_TESS_WINDING_NONZERO ||
tess->windingRule == GLU_TESS_WINDING_POSITIVE ||
tess->windingRule == GLU_TESS_WINDING_NEGATIVE ||
tess->windingRule == GLU_TESS_WINDING_ABS_GEQ_TWO);
*value= tess->windingRule;
break;
case GLU_TESS_BOUNDARY_ONLY:
assert(tess->boundaryOnly == TRUE || tess->boundaryOnly == FALSE);
*value= tess->boundaryOnly;
break;
default:
*value= 0.0;
CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_ENUM );
break;
}
} /* gluGetTessProperty() */
void GLAPIENTRY
gluTessNormal( GLUtesselator *tess, GLdouble x, GLdouble y, GLdouble z )
{
tess->normal[0] = x;
tess->normal[1] = y;
tess->normal[2] = z;
}
void GLAPIENTRY
gluTessCallback( GLUtesselator *tess, GLenum which, _GLUfuncptr fn)
{
switch( which ) {
case GLU_TESS_BEGIN:
tess->callBegin = (fn == NULL) ? &noBegin : (void (GLAPIENTRY *)(GLenum)) fn;
return;
case GLU_TESS_BEGIN_DATA:
tess->callBeginData = (fn == NULL) ?
&__gl_noBeginData : (void (GLAPIENTRY *)(GLenum, void *)) fn;
return;
case GLU_TESS_EDGE_FLAG:
tess->callEdgeFlag = (fn == NULL) ? &noEdgeFlag :
(void (GLAPIENTRY *)(GLboolean)) fn;
/* If the client wants boundary edges to be flagged,
* we render everything as separate triangles (no strips or fans).
*/
tess->flagBoundary = (fn != NULL);
return;
case GLU_TESS_EDGE_FLAG_DATA:
tess->callEdgeFlagData= (fn == NULL) ?
&__gl_noEdgeFlagData : (void (GLAPIENTRY *)(GLboolean, void *)) fn;
/* If the client wants boundary edges to be flagged,
* we render everything as separate triangles (no strips or fans).
*/
tess->flagBoundary = (fn != NULL);
return;
case GLU_TESS_VERTEX:
tess->callVertex = (fn == NULL) ? &noVertex :
(void (GLAPIENTRY *)(void *)) fn;
return;
case GLU_TESS_VERTEX_DATA:
tess->callVertexData = (fn == NULL) ?
&__gl_noVertexData : (void (GLAPIENTRY *)(void *, void *)) fn;
return;
case GLU_TESS_END:
tess->callEnd = (fn == NULL) ? &noEnd : (void (GLAPIENTRY *)(void)) fn;
return;
case GLU_TESS_END_DATA:
tess->callEndData = (fn == NULL) ? &__gl_noEndData :
(void (GLAPIENTRY *)(void *)) fn;
return;
case GLU_TESS_ERROR:
tess->callError = (fn == NULL) ? &noError : (void (GLAPIENTRY *)(GLenum)) fn;
return;
case GLU_TESS_ERROR_DATA:
tess->callErrorData = (fn == NULL) ?
&__gl_noErrorData : (void (GLAPIENTRY *)(GLenum, void *)) fn;
return;
case GLU_TESS_COMBINE:
tess->callCombine = (fn == NULL) ? &noCombine :
(void (GLAPIENTRY *)(GLdouble [3],void *[4], GLfloat [4], void ** )) fn;
return;
case GLU_TESS_COMBINE_DATA:
tess->callCombineData = (fn == NULL) ? &__gl_noCombineData :
(void (GLAPIENTRY *)(GLdouble [3],
void *[4],
GLfloat [4],
void **,
void *)) fn;
return;
case GLU_TESS_MESH:
tess->callMesh = (fn == NULL) ? &noMesh : (void (GLAPIENTRY *)(GLUmesh *)) fn;
return;
default:
CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_ENUM );
return;
}
}
static int AddVertex( GLUtesselator *tess, GLdouble coords[3], void *data )
{
GLUhalfEdge *e;
e = tess->lastEdge;
if( e == NULL ) {
/* Make a self-loop (one vertex, one edge). */
e = __gl_meshMakeEdge( tess->mesh );
if (e == NULL) return 0;
if ( !__gl_meshSplice( e, e->Sym ) ) return 0;
} else {
/* Create a new vertex and edge which immediately follow e
* in the ordering around the left face.
*/
if (__gl_meshSplitEdge( e ) == NULL) return 0;
e = e->Lnext;
}
/* The new vertex is now e->Org. */
e->Org->data = data;
e->Org->coords[0] = coords[0];
e->Org->coords[1] = coords[1];
e->Org->coords[2] = coords[2];
/* The winding of an edge says how the winding number changes as we
* cross from the edge''s right face to its left face. We add the
* vertices in such an order that a CCW contour will add +1 to
* the winding number of the region inside the contour.
*/
e->winding = 1;
e->Sym->winding = -1;
tess->lastEdge = e;
return 1;
}
static void CacheVertex( GLUtesselator *tess, GLdouble coords[3], void *data )
{
CachedVertex *v = &tess->cache[tess->cacheCount];
v->data = data;
v->coords[0] = coords[0];
v->coords[1] = coords[1];
v->coords[2] = coords[2];
++tess->cacheCount;
}
static int EmptyCache( GLUtesselator *tess )
{
CachedVertex *v = tess->cache;
CachedVertex *vLast;
tess->mesh = __gl_meshNewMesh();
if (tess->mesh == NULL) return 0;
for( vLast = v + tess->cacheCount; v < vLast; ++v ) {
if ( !AddVertex( tess, v->coords, v->data ) ) return 0;
}
tess->cacheCount = 0;
tess->emptyCache = FALSE;
return 1;
}
void GLAPIENTRY
gluTessVertex( GLUtesselator *tess, GLdouble coords[3], void *data )
{
int i, tooLarge = FALSE;
GLdouble x, clamped[3];
RequireState( tess, T_IN_CONTOUR );
if( tess->emptyCache ) {
if ( !EmptyCache( tess ) ) {
CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY );
return;
}
tess->lastEdge = NULL;
}
for( i = 0; i < 3; ++i ) {
x = coords[i];
if( x < - GLU_TESS_MAX_COORD ) {
x = - GLU_TESS_MAX_COORD;
tooLarge = TRUE;
}
if( x > GLU_TESS_MAX_COORD ) {
x = GLU_TESS_MAX_COORD;
tooLarge = TRUE;
}
clamped[i] = x;
}
if( tooLarge ) {
CALL_ERROR_OR_ERROR_DATA( GLU_TESS_COORD_TOO_LARGE );
}
if( tess->mesh == NULL ) {
if( tess->cacheCount < TESS_MAX_CACHE ) {
CacheVertex( tess, clamped, data );
return;
}
if ( !EmptyCache( tess ) ) {
CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY );
return;
}
}
if ( !AddVertex( tess, clamped, data ) ) {
CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY );
}
}
void GLAPIENTRY
gluTessBeginPolygon( GLUtesselator *tess, void *data )
{
RequireState( tess, T_DORMANT );
tess->state = T_IN_POLYGON;
tess->cacheCount = 0;
tess->emptyCache = FALSE;
tess->mesh = NULL;
tess->polygonData= data;
}
void GLAPIENTRY
gluTessBeginContour( GLUtesselator *tess )
{
RequireState( tess, T_IN_POLYGON );
tess->state = T_IN_CONTOUR;
tess->lastEdge = NULL;
if( tess->cacheCount > 0 ) {
/* Just set a flag so we don't get confused by empty contours
* -- these can be generated accidentally with the obsolete
* NextContour() interface.
*/
tess->emptyCache = TRUE;
}
}
void GLAPIENTRY
gluTessEndContour( GLUtesselator *tess )
{
RequireState( tess, T_IN_CONTOUR );
tess->state = T_IN_POLYGON;
}
void GLAPIENTRY
gluTessEndPolygon( GLUtesselator *tess )
{
GLUmesh *mesh;
if (setjmp(tess->env) != 0) {
/* come back here if out of memory */
CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY );
return;
}
RequireState( tess, T_IN_POLYGON );
tess->state = T_DORMANT;
if( tess->mesh == NULL ) {
if( ! tess->flagBoundary && tess->callMesh == &noMesh ) {
/* Try some special code to make the easy cases go quickly
* (eg. convex polygons). This code does NOT handle multiple contours,
* intersections, edge flags, and of course it does not generate
* an explicit mesh either.
*/
if( __gl_renderCache( tess )) {
tess->polygonData= NULL;
return;
}
}
if ( !EmptyCache( tess ) ) longjmp(tess->env,1); /* could've used a label*/
}
/* Determine the polygon normal and project vertices onto the plane
* of the polygon.
*/
__gl_projectPolygon( tess );
/* __gl_computeInterior( tess ) computes the planar arrangement specified
* by the given contours, and further subdivides this arrangement
* into regions. Each region is marked "inside" if it belongs
* to the polygon, according to the rule given by tess->windingRule.
* Each interior region is guaranteed be monotone.
*/
if ( !__gl_computeInterior( tess ) ) {
longjmp(tess->env,1); /* could've used a label */
}
mesh = tess->mesh;
if( ! tess->fatalError ) {
int rc = 1;
/* If the user wants only the boundary contours, we throw away all edges
* except those which separate the interior from the exterior.
* Otherwise we tessellate all the regions marked "inside".
*/
if( tess->boundaryOnly ) {
rc = __gl_meshSetWindingNumber( mesh, 1, TRUE );
} else {
rc = __gl_meshTessellateInterior( mesh );
}
if (rc == 0) longjmp(tess->env,1); /* could've used a label */
__gl_meshCheckMesh( mesh );
if( tess->callBegin != &noBegin || tess->callEnd != &noEnd
|| tess->callVertex != &noVertex || tess->callEdgeFlag != &noEdgeFlag
|| tess->callBeginData != &__gl_noBeginData
|| tess->callEndData != &__gl_noEndData
|| tess->callVertexData != &__gl_noVertexData
|| tess->callEdgeFlagData != &__gl_noEdgeFlagData )
{
if( tess->boundaryOnly ) {
__gl_renderBoundary( tess, mesh ); /* output boundary contours */
} else {
__gl_renderMesh( tess, mesh ); /* output strips and fans */
}
}
if( tess->callMesh != &noMesh ) {
/* Throw away the exterior faces, so that all faces are interior.
* This way the user doesn't have to check the "inside" flag,
* and we don't need to even reveal its existence. It also leaves
* the freedom for an implementation to not generate the exterior
* faces in the first place.
*/
__gl_meshDiscardExterior( mesh );
(*tess->callMesh)( mesh ); /* user wants the mesh itself */
tess->mesh = NULL;
tess->polygonData= NULL;
return;
}
}
__gl_meshDeleteMesh( mesh );
tess->polygonData= NULL;
tess->mesh = NULL;
}
/*XXXblythe unused function*/
#if 0
void GLAPIENTRY
gluDeleteMesh( GLUmesh *mesh )
{
__gl_meshDeleteMesh( mesh );
}
#endif
/*******************************************************/
/* Obsolete calls -- for backward compatibility */
void GLAPIENTRY
gluBeginPolygon( GLUtesselator *tess )
{
gluTessBeginPolygon( tess, NULL );
gluTessBeginContour( tess );
}
/*ARGSUSED*/
void GLAPIENTRY
gluNextContour( GLUtesselator *tess, GLenum type )
{
gluTessEndContour( tess );
gluTessBeginContour( tess );
}
void GLAPIENTRY
gluEndPolygon( GLUtesselator *tess )
{
gluTessEndContour( tess );
gluTessEndPolygon( tess );
}

View File

@ -1,164 +0,0 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#ifndef __tess_h_
#define __tess_h_
#include <setjmp.h>
#include "mesh.h"
#include "dict.h"
#include "priorityq.h"
/* The begin/end calls must be properly nested. We keep track of
* the current state to enforce the ordering.
*/
enum TessState { T_DORMANT, T_IN_POLYGON, T_IN_CONTOUR };
/* We cache vertex data for single-contour polygons so that we can
* try a quick-and-dirty decomposition first.
*/
#define TESS_MAX_CACHE 100
typedef struct CachedVertex {
GLdouble coords[3];
void *data;
} CachedVertex;
struct GLUtesselator {
/*** state needed for collecting the input data ***/
enum TessState state; /* what begin/end calls have we seen? */
GLUhalfEdge *lastEdge; /* lastEdge->Org is the most recent vertex */
GLUmesh *mesh; /* stores the input contours, and eventually
the tessellation itself */
void (GLAPIENTRY *callError)( GLenum errnum );
/*** state needed for projecting onto the sweep plane ***/
GLdouble normal[3]; /* user-specified normal (if provided) */
GLdouble sUnit[3]; /* unit vector in s-direction (debugging) */
GLdouble tUnit[3]; /* unit vector in t-direction (debugging) */
/*** state needed for the line sweep ***/
GLdouble relTolerance; /* tolerance for merging features */
GLenum windingRule; /* rule for determining polygon interior */
GLboolean fatalError; /* fatal error: needed combine callback */
Dict *dict; /* edge dictionary for sweep line */
PriorityQ *pq; /* priority queue of vertex events */
GLUvertex *event; /* current sweep event being processed */
void (GLAPIENTRY *callCombine)( GLdouble coords[3], void *data[4],
GLfloat weight[4], void **outData );
/*** state needed for rendering callbacks (see render.c) ***/
GLboolean flagBoundary; /* mark boundary edges (use EdgeFlag) */
GLboolean boundaryOnly; /* Extract contours, not triangles */
GLUface *lonelyTriList;
/* list of triangles which could not be rendered as strips or fans */
void (GLAPIENTRY *callBegin)( GLenum type );
void (GLAPIENTRY *callEdgeFlag)( GLboolean boundaryEdge );
void (GLAPIENTRY *callVertex)( void *data );
void (GLAPIENTRY *callEnd)( void );
void (GLAPIENTRY *callMesh)( GLUmesh *mesh );
/*** state needed to cache single-contour polygons for renderCache() */
GLboolean emptyCache; /* empty cache on next vertex() call */
int cacheCount; /* number of cached vertices */
CachedVertex cache[TESS_MAX_CACHE]; /* the vertex data */
/*** rendering callbacks that also pass polygon data ***/
void (GLAPIENTRY *callBeginData)( GLenum type, void *polygonData );
void (GLAPIENTRY *callEdgeFlagData)( GLboolean boundaryEdge,
void *polygonData );
void (GLAPIENTRY *callVertexData)( void *data, void *polygonData );
void (GLAPIENTRY *callEndData)( void *polygonData );
void (GLAPIENTRY *callErrorData)( GLenum errnum, void *polygonData );
void (GLAPIENTRY *callCombineData)( GLdouble coords[3], void *data[4],
GLfloat weight[4], void **outData,
void *polygonData );
jmp_buf env; /* place to jump to when memAllocs fail */
void *polygonData; /* client data for current polygon */
};
void GLAPIENTRY __gl_noBeginData( GLenum type, void *polygonData );
void GLAPIENTRY __gl_noEdgeFlagData( GLboolean boundaryEdge, void *polygonData );
void GLAPIENTRY __gl_noVertexData( void *data, void *polygonData );
void GLAPIENTRY __gl_noEndData( void *polygonData );
void GLAPIENTRY __gl_noErrorData( GLenum errnum, void *polygonData );
void GLAPIENTRY __gl_noCombineData( GLdouble coords[3], void *data[4],
GLfloat weight[4], void **outData,
void *polygonData );
#define CALL_BEGIN_OR_BEGIN_DATA(a) \
if (tess->callBeginData != &__gl_noBeginData) \
(*tess->callBeginData)((a),tess->polygonData); \
else (*tess->callBegin)((a));
#define CALL_VERTEX_OR_VERTEX_DATA(a) \
if (tess->callVertexData != &__gl_noVertexData) \
(*tess->callVertexData)((a),tess->polygonData); \
else (*tess->callVertex)((a));
#define CALL_EDGE_FLAG_OR_EDGE_FLAG_DATA(a) \
if (tess->callEdgeFlagData != &__gl_noEdgeFlagData) \
(*tess->callEdgeFlagData)((a),tess->polygonData); \
else (*tess->callEdgeFlag)((a));
#define CALL_END_OR_END_DATA() \
if (tess->callEndData != &__gl_noEndData) \
(*tess->callEndData)(tess->polygonData); \
else (*tess->callEnd)();
#define CALL_COMBINE_OR_COMBINE_DATA(a,b,c,d) \
if (tess->callCombineData != &__gl_noCombineData) \
(*tess->callCombineData)((a),(b),(c),(d),tess->polygonData); \
else (*tess->callCombine)((a),(b),(c),(d));
#define CALL_ERROR_OR_ERROR_DATA(a) \
if (tess->callErrorData != &__gl_noErrorData) \
(*tess->callErrorData)((a),tess->polygonData); \
else (*tess->callError)((a));
#endif

View File

@ -1,122 +0,0 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
* Copyright (C) 2010 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
#ifndef __TESSELATOR_H__
#define __TESSELATOR_H__
/* This just includes the defines needed by the tesselator code */
#include "cogl/cogl-defines.h"
#include "cogl/cogl-gl-header.h"
typedef struct GLUtesselator GLUtesselator;
#define GLU_TESS_MAX_COORD 1.0e150
void gluBeginPolygon (GLUtesselator* tess);
void gluDeleteTess (GLUtesselator* tess);
void gluEndPolygon (GLUtesselator* tess);
typedef void (_GLUfuncptr)(void);
void gluGetTessProperty (GLUtesselator* tess, GLenum which, double* data);
GLUtesselator *gluNewTess (void);
void gluNextContour (GLUtesselator* tess, GLenum type);
void gluTessBeginContour (GLUtesselator* tess);
void gluTessBeginPolygon (GLUtesselator* tess, GLvoid* data);
void gluTessCallback (GLUtesselator* tess, GLenum which, _GLUfuncptr CallBackFunc);
void gluTessEndContour (GLUtesselator* tess);
void gluTessEndPolygon (GLUtesselator* tess);
void gluTessNormal (GLUtesselator* tess, double valueX, double valueY, double valueZ);
void gluTessProperty (GLUtesselator* tess, GLenum which, double data);
void gluTessVertex (GLUtesselator* tess, double *location, GLvoid* data);
/* ErrorCode */
#define GLU_INVALID_ENUM 100900
#define GLU_INVALID_VALUE 100901
#define GLU_OUT_OF_MEMORY 100902
/* TessCallback */
#define GLU_TESS_BEGIN 100100
#define GLU_BEGIN 100100
#define GLU_TESS_VERTEX 100101
#define GLU_VERTEX 100101
#define GLU_TESS_END 100102
#define GLU_END 100102
#define GLU_TESS_ERROR 100103
#define GLU_TESS_EDGE_FLAG 100104
#define GLU_EDGE_FLAG 100104
#define GLU_TESS_COMBINE 100105
#define GLU_TESS_BEGIN_DATA 100106
#define GLU_TESS_VERTEX_DATA 100107
#define GLU_TESS_END_DATA 100108
#define GLU_TESS_ERROR_DATA 100109
#define GLU_TESS_EDGE_FLAG_DATA 100110
#define GLU_TESS_COMBINE_DATA 100111
/* TessContour */
#define GLU_CW 100120
#define GLU_CCW 100121
#define GLU_INTERIOR 100122
#define GLU_EXTERIOR 100123
#define GLU_UNKNOWN 100124
/* TessProperty */
#define GLU_TESS_WINDING_RULE 100140
#define GLU_TESS_BOUNDARY_ONLY 100141
#define GLU_TESS_TOLERANCE 100142
/* TessError */
#define GLU_TESS_ERROR1 100151
#define GLU_TESS_ERROR2 100152
#define GLU_TESS_ERROR3 100153
#define GLU_TESS_ERROR4 100154
#define GLU_TESS_ERROR5 100155
#define GLU_TESS_ERROR6 100156
#define GLU_TESS_ERROR7 100157
#define GLU_TESS_ERROR8 100158
#define GLU_TESS_MISSING_BEGIN_POLYGON 100151
#define GLU_TESS_MISSING_BEGIN_CONTOUR 100152
#define GLU_TESS_MISSING_END_POLYGON 100153
#define GLU_TESS_MISSING_END_CONTOUR 100154
#define GLU_TESS_COORD_TOO_LARGE 100155
#define GLU_TESS_NEED_COMBINE_CALLBACK 100156
/* TessWinding */
#define GLU_TESS_WINDING_ODD 100130
#define GLU_TESS_WINDING_NONZERO 100131
#define GLU_TESS_WINDING_POSITIVE 100132
#define GLU_TESS_WINDING_NEGATIVE 100133
#define GLU_TESS_WINDING_ABS_GEQ_TWO 100134
#endif /* __TESSELATOR_H__ */

View File

@ -1,201 +0,0 @@
/*
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
* Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice including the dates of first publication and
* either this permission notice or a reference to
* http://oss.sgi.com/projects/FreeB/
* shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of Silicon Graphics, Inc.
* shall not be used in advertising or otherwise to promote the sale, use or
* other dealings in this Software without prior written authorization from
* Silicon Graphics, Inc.
*/
/*
** Author: Eric Veach, July 1994.
**
*/
#include "gluos.h"
#include <stdlib.h>
#include "geom.h"
#include "mesh.h"
#include "tessmono.h"
#include <assert.h>
#define AddWinding(eDst,eSrc) (eDst->winding += eSrc->winding, \
eDst->Sym->winding += eSrc->Sym->winding)
/* __gl_meshTessellateMonoRegion( face ) tessellates a monotone region
* (what else would it do??) The region must consist of a single
* loop of half-edges (see mesh.h) oriented CCW. "Monotone" in this
* case means that any vertical line intersects the interior of the
* region in a single interval.
*
* Tessellation consists of adding interior edges (actually pairs of
* half-edges), to split the region into non-overlapping triangles.
*
* The basic idea is explained in Preparata and Shamos (which I don''t
* have handy right now), although their implementation is more
* complicated than this one. The are two edge chains, an upper chain
* and a lower chain. We process all vertices from both chains in order,
* from right to left.
*
* The algorithm ensures that the following invariant holds after each
* vertex is processed: the untessellated region consists of two
* chains, where one chain (say the upper) is a single edge, and
* the other chain is concave. The left vertex of the single edge
* is always to the left of all vertices in the concave chain.
*
* Each step consists of adding the rightmost unprocessed vertex to one
* of the two chains, and forming a fan of triangles from the rightmost
* of two chain endpoints. Determining whether we can add each triangle
* to the fan is a simple orientation test. By making the fan as large
* as possible, we restore the invariant (check it yourself).
*/
int __gl_meshTessellateMonoRegion( GLUface *face )
{
GLUhalfEdge *up, *lo;
/* All edges are oriented CCW around the boundary of the region.
* First, find the half-edge whose origin vertex is rightmost.
* Since the sweep goes from left to right, face->anEdge should
* be close to the edge we want.
*/
up = face->anEdge;
assert( up->Lnext != up && up->Lnext->Lnext != up );
for( ; VertLeq( up->Dst, up->Org ); up = up->Lprev )
;
for( ; VertLeq( up->Org, up->Dst ); up = up->Lnext )
;
lo = up->Lprev;
while( up->Lnext != lo ) {
if( VertLeq( up->Dst, lo->Org )) {
/* up->Dst is on the left. It is safe to form triangles from lo->Org.
* The EdgeGoesLeft test guarantees progress even when some triangles
* are CW, given that the upper and lower chains are truly monotone.
*/
while( lo->Lnext != up && (EdgeGoesLeft( lo->Lnext )
|| EdgeSign( lo->Org, lo->Dst, lo->Lnext->Dst ) <= 0 )) {
GLUhalfEdge *tempHalfEdge= __gl_meshConnect( lo->Lnext, lo );
if (tempHalfEdge == NULL) return 0;
lo = tempHalfEdge->Sym;
}
lo = lo->Lprev;
} else {
/* lo->Org is on the left. We can make CCW triangles from up->Dst. */
while( lo->Lnext != up && (EdgeGoesRight( up->Lprev )
|| EdgeSign( up->Dst, up->Org, up->Lprev->Org ) >= 0 )) {
GLUhalfEdge *tempHalfEdge= __gl_meshConnect( up, up->Lprev );
if (tempHalfEdge == NULL) return 0;
up = tempHalfEdge->Sym;
}
up = up->Lnext;
}
}
/* Now lo->Org == up->Dst == the leftmost vertex. The remaining region
* can be tessellated in a fan from this leftmost vertex.
*/
assert( lo->Lnext != up );
while( lo->Lnext->Lnext != up ) {
GLUhalfEdge *tempHalfEdge= __gl_meshConnect( lo->Lnext, lo );
if (tempHalfEdge == NULL) return 0;
lo = tempHalfEdge->Sym;
}
return 1;
}
/* __gl_meshTessellateInterior( mesh ) tessellates each region of
* the mesh which is marked "inside" the polygon. Each such region
* must be monotone.
*/
int __gl_meshTessellateInterior( GLUmesh *mesh )
{
GLUface *f, *next;
/*LINTED*/
for( f = mesh->fHead.next; f != &mesh->fHead; f = next ) {
/* Make sure we don''t try to tessellate the new triangles. */
next = f->next;
if( f->inside ) {
if ( !__gl_meshTessellateMonoRegion( f ) ) return 0;
}
}
return 1;
}
/* __gl_meshDiscardExterior( mesh ) zaps (ie. sets to NULL) all faces
* which are not marked "inside" the polygon. Since further mesh operations
* on NULL faces are not allowed, the main purpose is to clean up the
* mesh so that exterior loops are not represented in the data structure.
*/
void __gl_meshDiscardExterior( GLUmesh *mesh )
{
GLUface *f, *next;
/*LINTED*/
for( f = mesh->fHead.next; f != &mesh->fHead; f = next ) {
/* Since f will be destroyed, save its next pointer. */
next = f->next;
if( ! f->inside ) {
__gl_meshZapFace( f );
}
}
}
#define MARKED_FOR_DELETION 0x7fffffff
/* __gl_meshSetWindingNumber( mesh, value, keepOnlyBoundary ) resets the
* winding numbers on all edges so that regions marked "inside" the
* polygon have a winding number of "value", and regions outside
* have a winding number of 0.
*
* If keepOnlyBoundary is TRUE, it also deletes all edges which do not
* separate an interior region from an exterior one.
*/
int __gl_meshSetWindingNumber( GLUmesh *mesh, int value,
GLboolean keepOnlyBoundary )
{
GLUhalfEdge *e, *eNext;
for( e = mesh->eHead.next; e != &mesh->eHead; e = eNext ) {
eNext = e->next;
if( e->Rface->inside != e->Lface->inside ) {
/* This is a boundary edge (one side is interior, one is exterior). */
e->winding = (e->Lface->inside) ? value : -value;
} else {
/* Both regions are interior, or both are exterior. */
if( ! keepOnlyBoundary ) {
e->winding = 0;
} else {
if ( !__gl_meshDelete( e ) ) return 0;
}
}
}
return 1;
}

Some files were not shown because too many files have changed in this diff Show More