Compare commits

..

119 Commits

Author SHA1 Message Date
Matthias Clasen e16c836f60 curve2: Show osculating circles 2020-12-27 00:26:44 -05:00
Matthias Clasen 55bfc167e3 Add gsk_path_measure_get_curvature 2020-12-27 00:26:44 -05:00
Matthias Clasen 5516ec2bf0 Add an interactive path test
This one is for interactive exploring of svg paths.

You can enter an SVG path in the entry and hit Enter
to see how GSK renders it. If you click the button
in the headerbar, you can see what GTK thinks the
closest point, tangent and distance are wrt. to the
mouse position, and the bounding box of the path.

There's also stroke parameters to play with.
2020-12-27 00:26:44 -05:00
Matthias Clasen 7e7d1987a3 xxx: Make gsk_stroke_to_cairo public
It comes in handy, and does no harm.
2020-12-27 00:26:44 -05:00
Matthias Clasen 304c3055ad Add a path editor demo
Add a simple demo for editing a poly-Bezier curve.
2020-12-27 00:26:44 -05:00
Matthias Clasen 226f556cf8 Implement gsk_path_offset
Implement offsetting of paths by reusing the
infrastructure of the stroker.
2020-12-27 00:10:08 -05:00
Matthias Clasen be7fead1fb Add gsk_path_offset
Add a function that takes a path, and offsets it
by some distance, applying line-join parameters
as needed.

This commit just adds the api, the implementation
will be in the following commit.
2020-12-27 00:10:08 -05:00
Matthias Clasen b871c2a3cb stroker: Cleanups
Move some utility functions around.
2020-12-26 23:57:41 -05:00
Matthias Clasen 7c67a9c76a stroker: Implement arcs
Implement arced joins as specified in SVG2.
2020-12-26 23:57:41 -05:00
Matthias Clasen 427e185bcf Add GSK_LINE_JOIN_ARCS
Implementation will follow.
2020-12-26 23:57:41 -05:00
Matthias Clasen 30552e9988 xxx: assorted fixes for special contours
Fix winding numbers, and strokes wrt to direction.
2020-12-26 23:57:41 -05:00
Matthias Clasen 08d8d6c029 Add another stroker test
Check that the outlines of random paths look as
expected. We currently have to exclude paths where
points get too close to each other.
2020-12-26 23:57:41 -05:00
Matthias Clasen 61a84956d4 Add basic tests for strokes
Add tests to check that any point on a path is
always at most half the line-width away from the
stroke path with that line-width.
2020-12-26 23:57:41 -05:00
Matthias Clasen e44780923a Implement stroking
Implement gsk_contour_default_add_stroke, which takes a contour
and stroke parameters, and adds contours to a path builder for
the outline that woul be produced by stroking the path with these
parameters.

The current implementation does not try to handle short segments
in the vicinity of sharp joins in any special way, so there can
be some artifacts in that situation.
2020-12-26 23:57:41 -05:00
Matthias Clasen dd1a2e0396 Special-case circles for strokes
The outline of a circle is just two circles.
2020-12-26 23:57:41 -05:00
Matthias Clasen 0100c33b71 Special-case rects for strokes
In many cases, the outline of a rectangle is just
two rectangles.
2020-12-26 23:57:41 -05:00
Matthias Clasen 8b8197a662 Add gsk_path_stroke
Add the plumbing that will let us do special-case stroking
for rectangles, circles and other special contours. There
is no implementation yet.
2020-12-26 23:57:41 -05:00
Matthias Clasen e293f10fa1 Add gsk_curve_get_curvature
This will be used in the stroker.
2020-12-26 23:56:26 -05:00
Matthias Clasen 3a4c3c3d8c Add gsk_curve_get_normal
Its easy but thats no reason not to have this api.
2020-12-26 23:56:26 -05:00
Matthias Clasen 944735071e Add a test for gsk_curve_offset
The stroker relies on offsetting.
This only tests a few very simple cases.
2020-12-26 23:56:26 -05:00
Matthias Clasen 91356de96a Add a test for gsk_curve_reverse
The stroker relies on this working.
2020-12-26 23:56:26 -05:00
Matthias Clasen 3f29c50800 Add a test for tangents of degenerate curves
The stroker relies on these to work.
2020-12-26 23:56:26 -05:00
Matthias Clasen 59b1f9e7b1 curve: Handle degenerate cases
Nothing prevents control points from being identical,
and if that happens, some of our constructions involving
tangents and normals break down. Handle these cases in
get_{start,end}_tangent and offset, for the case of
cubics.
2020-12-26 23:56:26 -05:00
Matthias Clasen dd3cf83a35 Add gsk_curve_reverse
This will be used in stroking.
2020-12-26 23:56:26 -05:00
Matthias Clasen 7f8a9e5543 Add gsk_curve_offset
This method creates an offset curve from an existing
curve by just moving the control points laterally.

This will be used in stroking.
2020-12-26 23:56:26 -05:00
Matthias Clasen 8523c6cb2d Add conic decomposition tests
We don't have good error bounds here, unfortunately.
2020-12-26 23:56:26 -05:00
Matthias Clasen 49b25424c5 path: support conic->curve in foreach 2020-12-26 23:56:26 -05:00
Matthias Clasen db2046cb67 Add gsk_curve_decompose_curve
This is mainly useful for decomposing a conic into
cubics. The criterion here for terminating the
subdivision is very improvised.
2020-12-26 23:56:26 -05:00
Matthias Clasen 61264165ff Add a performance test for curve eval
All curve types are equally fast here.
2020-12-26 23:56:26 -05:00
Matthias Clasen 7b3d283fc0 Add a performance test for curve intersection
This shows how much more expensive curve
intersections are.
2020-12-26 23:56:26 -05:00
Matthias Clasen 44ffed0283 Add curve split tests 2020-12-26 23:56:26 -05:00
Matthias Clasen e91ec1f26f Add another intersection testcase
This tests horizontal line/conic intersection,
which failed before the fix in the previous commit.
2020-12-26 23:56:26 -05:00
Matthias Clasen eace55daa6 xxx: work around bounding box problems
graphene_rect_intersect returns FALSE when you
intersect the bounding boxes of axis-aligned
lines, so we never find intersections with those.

Make things better by making the bounding boxes worse.
2020-12-26 23:56:26 -05:00
Matthias Clasen 097e58558b Add curve intersection tests
These tests check that gsk_curve_intersect finds
the intersections we want.
2020-12-26 23:56:26 -05:00
Matthias Clasen f0acb0f073 Add gsk_curve_intersect
Add a way to find the intersections of two curves.
This will be used in stroking.
2020-12-26 23:56:25 -05:00
Matthias Clasen b779ba19ca Add gsk_curve_get_bounds
Add getters for bounding boxes of curves.
2020-12-26 23:56:25 -05:00
Matthias Clasen aa0f1ce956 Only test conic weights between 1/20 and 20
The rest just give us no end of numeric trouble.
2020-12-26 23:56:25 -05:00
Benjamin Otte e9d01c1a63 Ottie: Add ottie-editor 2020-12-27 01:12:11 +01:00
Benjamin Otte e235392894 ottie: Add a snapshot testsuite test
The test takes a lottie file and a timestamp in seconds and produces a
rendernode at that timestamp.

It then serializes that node and compares it via diff(1) with a file
containing the expected output.

This is a lot stricter than it needs to be (because different node files
can generate the same output and updates to the rendering pipeline can
break everything) but I chose this method on purpose because it does a
good job at guarding against accidental changes in other parts of the
code.

It's also better than comparing image output because it avoids
antialiasing artifacts when using curves and things like that.
2020-12-27 01:12:11 +01:00
Benjamin Otte 73904f3034 ottie: Add a command-line tool
Supports:

 * Taking a screenie:
   ottie image file.lottie image.png

 * Recording a rendernode:
   ottie node file.lottie render.node

 * Encoding an image:
   ottie video file.lottie video.webm
2020-12-27 01:12:11 +01:00
Benjamin Otte c2ed71af7a Ottie: Add 2020-12-27 01:12:11 +01:00
Benjamin Otte aa2f6345c8 path: Add gsk_path_builder_add_ellipse() 2020-12-27 00:31:18 +01:00
Benjamin Otte cef1bc9098 path: Change semantics of gtk_path_builder_add_segment()
Allow start >= end to mean that the path continues at the beginning
after reaching the end until it reaches the point at @end.
2020-12-27 00:31:18 +01:00
Benjamin Otte 828ccc51d9 path: Add gsk_path_measure_is_closed () 2020-12-27 00:31:18 +01:00
Benjamin Otte 8397d10c20 path: Add gsk_path_measure_restrict_to_contour() 2020-12-27 00:31:18 +01:00
Matthias Clasen 78d06e58d0 Add gsk_path_measure_get_{path,tolerance}
These are just nice apis to have and avoid having to carry
these around as extra arguments in many places.

This was showing up as inconvenience in writing tests
for the measure apis.
2020-12-27 00:31:18 +01:00
Benjamin Otte a5e13cc96b xxx path)_fill 2020-12-27 00:31:18 +01:00
Matthias Clasen 776dc54c8a Add gsk_path_get_stroke_bounds
A relatively cheap way to get bounds for the area
that would be affected by stroking a path.
2020-12-27 00:31:18 +01:00
Benjamin Otte ee6879fba8 testsuite: Add tests for the dasher 2020-12-27 00:31:18 +01:00
Benjamin Otte 14f9395476 path: Add a foreach function that dashes a path 2020-12-27 00:31:18 +01:00
Benjamin Otte 1349cf2454 path: Deal with non-uniformness of progress parameter
The progress is non-uniform, so simple translation of progress doesn't work.
So check if larger and smaller values inch closer towards minimal distance.
2020-12-27 00:31:18 +01:00
Benjamin Otte d47ebd388c path: Always decompose conics into at least 2 segments
Conics are evil in that their parameter skews towards the center, and if
it's a very flat conic (weight almost equal to 0), then we'd approximate
it with a single segment and not subdivide, which would cause the
parameter to be wildly off around 0.25 or 0.75.

And that would cause offset calculations to fail.
2020-12-27 00:31:18 +01:00
Matthias Clasen 61214221b3 testsuite Add curve tangent tests 2020-12-27 00:31:18 +01:00
Benjamin Otte 7eb4ed8f86 testsuite: Add a test for the conic that got us segment() 2020-12-27 00:31:18 +01:00
Benjamin Otte 92b472fec1 path: Add gsk_curve_segment()
Using split() twice with scaled t values does not work with conics.
2020-12-27 00:31:18 +01:00
Benjamin Otte 7a6b008479 testsuite: Add a test for gsk_curve_decompose() 2020-12-27 00:31:18 +01:00
Benjamin Otte d8c26172a5 testuite: Add tests for gsk_curve_get_tangent() 2020-12-27 00:31:18 +01:00
Matthias Clasen da835321a5 testuite: Add tests for gsk_curve_get_point()
Add a few tests for gsk_curve_get_point().

Since GskCurve is not public api, we add gskcurve.c
as source to the test binary.
2020-12-27 00:31:18 +01:00
Benjamin Otte 30635d1cac curve: Split eval() into get_point() and get_tangent()
That's more in line with the get_start/end_point/tangent() functions.

Plus, those calls are independent and we usually want one or the other.
2020-12-27 00:31:18 +01:00
Matthias Clasen a60b72ba58 Add gsk_curve_get_{start,end}_tangent
Add a way to get the tangents at the start and end of the curve.
This will be used in stroking.
2020-12-27 00:31:18 +01:00
Benjamin Otte 36347892d8 testsuite: Add conics to the random paths 2020-12-27 00:31:18 +01:00
Benjamin Otte 924f0bc2c5 path: Add GskCurve
GskCurve is an abstraction for path operations. It's essentially a
collection of vfuncs per GskPathOperation.

GskStandardContour has been ported to use it where appropriate.
2020-12-27 00:31:18 +01:00
Benjamin Otte 23c5318de1 path: Introduce gskpathop
A gskpathop is a pointer to a graphene_point_t* with the low bits used
to encode the GskPathOperation. It's an easy way to introduce API for
operations.

So far it's just used to replace GskStandardOperation.
2020-12-27 00:31:18 +01:00
Benjamin Otte 678405bd22 WIP: css: Replace border rendering code with GskPath
The weight is wrong still, I need to compute the correct one to get real
45deg circle corners and not just roughly correct ones.
2020-12-27 00:31:18 +01:00
Benjamin Otte 946b76881b WIP: pathbuilder: Add gsk_path_builder_add_rounded_rect()
It works, but does not use a custom contour yet.
2020-12-27 00:31:18 +01:00
Benjamin Otte 061683344f path: Add conic curves
So far this just adds the API, if you use it, you'll get lots of
g_warnings().

This will be fixed in future commits.
2020-12-27 00:31:18 +01:00
Benjamin Otte af9a85616d path: Rename to gtk_path_builder_add_segment()
It's about bulding paths, not about measuring them.
2020-12-27 00:31:18 +01:00
Benjamin Otte 0f332de3d3 path: Split contours into their own file
I'm not sure I want to keep all contours in one file, but for now that's
how it is.
2020-12-27 00:31:18 +01:00
Benjamin Otte 15e8f2bacc path: Make all private contour APIs take a GskContour
... instead of a path, index tuple.
2020-12-27 00:31:18 +01:00
Benjamin Otte 25f9eadb0d stroke: Add support for dashes
... and hook it up in the node parser and for Cairo rendering.
2020-12-27 00:31:18 +01:00
Matthias Clasen ced2959cdd gsk: Implement parsing fill and stroke nodes
Make serialization and deserialization work for stroke and
fill nodes.
2020-12-27 00:31:18 +01:00
Benjamin Otte 3c363f00d5 path: Add flags to gsk_path_foreach()
This way we can default to the siplest possible foreach() output - like
cairo_copy_path_flat() decomposing everything into lines - and add flags
to get more and more fancy.

This will be useful to have conics automatically decomposed for Cairo
drawing or if we want to add more line types in the future.
2020-12-27 00:31:18 +01:00
Benjamin Otte a9ad37ed89 testsuite: Add an in_fill() test 2020-12-27 00:31:18 +01:00
Matthias Clasen acdf04287d Implement gsk_path_measure_in_fill
Implement this in the obvious way, using the decomposed form
of standard contours. Since the decomposed form is part of the
measure object, this api moves from gsk_path_in_fill to
gsk_path_measure_in_fill.
2020-12-27 00:31:18 +01:00
Benjamin Otte 34725c1207 testsuite: Add a parsing test
This test includes an implementation of a gsk_path_equal() func with
a tolerance that is necessary because parsing does not always work
100% exactly due to floating point rounding, so we can't just
compare the to_string() output.
2020-12-27 00:31:18 +01:00
Matthias Clasen cb10cf07e6 path: Special-case rects and circles
Write out the commands for rects and circles in a special
way, and add code in the parser to recognize this, so we
can successfully round-trip these through the SVG path format.

The special way - for people who want to use it for debugging -
for now is that we use uppercase "Z" to close standard paths, but
lowercase "z" to close our special paths.

A test is included, but the random path serializations should take care
of it, too.
2020-12-27 00:31:18 +01:00
Matthias Clasen a7ba2bde86 path: Fix serialization for circles
The svg A can not do a full circle, since it is a two point
parametrization - if the start and end point are the same,
it draws nothing. So, use two arcs.
2020-12-27 00:31:18 +01:00
Benjamin Otte 87b3cb1df8 testsuite: Add librsvg path tests 2020-12-27 00:31:18 +01:00
Matthias Clasen 2478963cbb path: Implement gsk_path_parse
Implement the SVG path syntax to read back the strings
that we generate when serializing paths. The tests for
this code are taken from librsvg.

This includes an elliptical arc implementation according
to the SVG spec. The code is mostly taken from librsvg,
but pretty directly follows the SVG spec implementation
notes. We don't export this, since the parametrization
is inconvenient. We do want an arc_to API, but
these are not the arcs we are looking for.
2020-12-27 00:31:18 +01:00
Matthias Clasen abbbaef7e8 path: Implement SVG arcs
This is elliptical arc implementation according to the SVG spec.
The code is mostly taken from librsvg, but pretty directly
follows the SVG spec implementation notes.

We don't export this, since the parametrization is inconvenient.
We do want an arc_to API, but these are not the arcs we are
looking for.

It will be used in parsing SVG path syntax.
2020-12-27 00:31:18 +01:00
Matthias Clasen 69177416a0 stroke: Add miter limit
Add a miter limit to GskStroke. This will be needed to
fully implement line joins.

Also introduce the GSK_LINE_JOIN_MITER_CLIP value,
following SVG 2.0. cairo does not have it, so translate
it to plain miter when using cairo.
2020-12-27 00:31:18 +01:00
Matthias Clasen ff47bfdbcf Documentation typo fixes 2020-12-27 00:31:18 +01:00
Benjamin Otte 120e449bdd testsuite: Add relative path functions
They're making the paths slightly weirder, but they test public API, so
woohoo!
2020-12-27 00:31:18 +01:00
Benjamin Otte 237c37f6c2 pathbuilder: Add relative path commands
And gsk_path_builder_get_current_point().

They will be needed by the string parser.
2020-12-27 00:31:18 +01:00
Benjamin Otte 683729c388 path: Add GSK_CIRCLE_POINT_INIT() to initialize points on the circle
This is just splitting out a commonly done operation into a macro.
2020-12-27 00:31:18 +01:00
Benjamin Otte 9fe71e7dae pathbuilder: Redo semantics for starting curves
We now always have a "current point" which is either the last point an
operation was made to, or (0, 0) if no drawing operation has
been made yet.

Adding a contour of any kind to the builder will always update the
current point to that contour's end point.
2020-12-27 00:31:18 +01:00
Benjamin Otte c74970e6c1 xxx: demo 2020-12-27 00:31:18 +01:00
Benjamin Otte 5225d01504 path: Split GskPathBuilder into its own file
... and add missing API docs.
2020-12-27 00:31:18 +01:00
Benjamin Otte 916d950069 testsuite: Add a test using get_point() and get_closest_point() 2020-12-27 00:31:18 +01:00
Benjamin Otte 76d6fed248 testsuite: Add a test for get_point() 2020-12-27 00:31:18 +01:00
Benjamin Otte 7ff6235ae6 testsuite: Update create_random_path()
1. Allow specifying the max number of contours
2. Be smarter about creating the paths:
   With 10% chance, create a "weird" path like the empty one or only
   points or things like that.
   Otherwise create a bunch of contours, with 2/3 a standard contour,
   with 1/3 a predetermined one.
2020-12-27 00:31:18 +01:00
Benjamin Otte 9b957c26cb gtk-demo: Add cute maze demo 2020-12-27 00:31:18 +01:00
Benjamin Otte 445b2382e6 testsuite: Add tests for gsk_path_measure_get_closest_point() 2020-12-27 00:31:18 +01:00
Benjamin Otte 790a4201c3 path: Add gsk_path_measure_get_closest_point()
... and gsk_path_measure_get_closest_point_full().

Those 2 functions allow finding the closest point on a path to a given
point.
2020-12-27 00:31:18 +01:00
Benjamin Otte 00b9af65b5 spline: Use Skia's tolerance checks
This avoids measuring being too far off (it's still off, but it's less
than a percent now.
2020-12-27 00:31:18 +01:00
Benjamin Otte f7c367338c testsuite: Add tests for gsk_path_measure_add_segment() 2020-12-27 00:31:18 +01:00
Benjamin Otte 5d69887618 gtk-demo: Add a text-on-path demo 2020-12-27 00:31:18 +01:00
Benjamin Otte b658c26a3e xxx: path_fill demo 2020-12-27 00:31:18 +01:00
Benjamin Otte e4c147cf2f path: Add gsk_path_measure_get_point()
Allows querying the coordinates and direction of any specific point on a
path.
2020-12-27 00:31:18 +01:00
Matthias Clasen fc2c6f1566 path: Add gsk_path_add_circle()
Adds a circle contour, too.
2020-12-27 00:31:18 +01:00
Benjamin Otte b0d6130905 pathmeasure: Implement support for beziers
Instead of treating bezier curves as lines, we properly decompose them
into line segments now so that we can treat those as lines.
2020-12-27 00:31:18 +01:00
Benjamin Otte a0b9238a15 path: Implement gsk_path_to_cairo() using foreach() 2020-12-27 00:31:18 +01:00
Benjamin Otte 801c63494c path: Add gsk_path_foreach() 2020-12-27 00:31:17 +01:00
Benjamin Otte d2cfe74140 path: Collect flags
We don't need them yet, but maybe later.
2020-12-27 00:31:17 +01:00
Benjamin Otte a3ac003546 testsuite: Add path tests 2020-12-27 00:31:17 +01:00
Benjamin Otte 483a4773cd pathmeasure: Add gsk_path_measure_add_segment()
This allows chunking paths, weeee.
2020-12-27 00:31:17 +01:00
Benjamin Otte c82ad0214e path: Add gsk_path_builder_add_path() 2020-12-27 00:31:17 +01:00
Benjamin Otte 207feeb7a9 gsk: Add GskPathMeasure
An object to do measuring operations on paths - determining their
length, cutting off subpaths, things like that.
2020-12-27 00:31:17 +01:00
Benjamin Otte 9a2e4ac9a9 path: Change data structure for standard path
Instead of the Cairo method and imitating cairo_path_data_t, use the
Skia method and keep points and operations separate.

That way we get a points array that includes the starting point -
because it's always the end point of the previous operation.
2020-12-27 00:31:17 +01:00
Benjamin Otte 2bd3aa3cbe popover: Use fill and stroke nodes instead of Cairo
... to render the arrow.

The arrow should really be turned into a real thing - maybe an icon?
2020-12-27 00:31:17 +01:00
Benjamin Otte 118e41f432 snapshot: Add gtk_snapshot_push_stroke() 2020-12-27 00:31:17 +01:00
Benjamin Otte 2d7897a769 gsk: Add GskStrokeNode 2020-12-27 00:31:17 +01:00
Benjamin Otte c279e1aa1e gsk: Add GskStroke
It's unused in this commit. This just prepares the new object.
2020-12-27 00:31:17 +01:00
Benjamin Otte 3c91d0be91 demos: Add a simple demo filling a path 2020-12-27 00:31:17 +01:00
Benjamin Otte e7bbed54b5 snapshot: Add gtk_snapshot_push_fill() 2020-12-27 00:31:17 +01:00
Benjamin Otte 4eb7d68970 gsk: Add GskFillNode
Take a rendernode as source and a GskPath and fill the region in the
path just like cairo_fill() would.
2020-12-27 00:31:17 +01:00
Benjamin Otte 98337f7115 gsk: Add GskPath 2020-12-27 00:31:17 +01:00
Benjamin Otte 08ee6c7acf mediafile: Load extension at startup with GTK_MEDIA
When the GTK_MEDIA env var is set, check at startup that it works, not
only when the first MeidaFile is instantiated.

This has the fortunate side effect that it prints help output for
GTK_MEDIA=help at startup, too.
2020-12-27 00:31:17 +01:00
Benjamin Otte c815496fe3 gtk: Build as static library first
This allows linking against the static libgtk from the testsuite.

We build the dynamic library by linking all the static libraries into
the final product.
2020-12-27 00:31:16 +01:00
841 changed files with 76931 additions and 46900 deletions
+1 -28
View File
@@ -24,7 +24,7 @@ variables:
BACKEND_FLAGS: "-Dx11-backend=true -Dwayland-backend=true -Dbroadway-backend=true"
FEATURE_FLAGS: "-Dvulkan=enabled -Dcloudproviders=enabled"
MESON_TEST_TIMEOUT_MULTIPLIER: 3
FEDORA_IMAGE: "registry.gitlab.gnome.org/gnome/gtk/fedora:v27"
FEDORA_IMAGE: "registry.gitlab.gnome.org/gnome/gtk/fedora:v25"
FLATPAK_IMAGE: "registry.gitlab.gnome.org/gnome/gnome-runtime-images/gnome:master"
DOCS_IMAGE: "registry.gitlab.gnome.org/gnome/gtk/fedora-docs:v25"
@@ -43,7 +43,6 @@ style-check-diff:
- .gitlab-ci/run-style-check-diff.sh
.build-fedora-default:
extends: .only-default
image: $FEDORA_IMAGE
artifacts:
when: always
@@ -122,7 +121,6 @@ installed-tests:
.mingw-defaults:
extends: .only-default
stage: build
tags:
- win32-ps
@@ -146,31 +144,6 @@ msys2-mingw64:
MSYSTEM: "MINGW64"
CHERE_INVOKING: "yes"
macos:
extends: .only-default
only:
- branches@GNOME/gtk
stage: build
tags:
- macos
needs: []
before_script:
- bash .gitlab-ci/show-execution-environment.sh
- pip3 install --user meson==0.56
- pip3 install --user ninja
- export PATH=/Users/gitlabrunner/Library/Python/3.7/bin:$PATH
script:
- meson -Dx11-backend=false
-Dintrospection=disabled
-Dcpp_std=c++11
-Dpixman:tests=disabled
_build
- ninja -C _build
artifacts:
when: always
paths:
- "${CI_PROJECT_DIR}/_build/meson-logs"
.flatpak-defaults:
image: $FLATPAK_IMAGE
stage: flatpak
-1
View File
@@ -65,7 +65,6 @@ RUN dnf -y install \
libxslt \
mesa-dri-drivers \
mesa-libEGL-devel \
mesa-libGLES-devel \
mesa-libwayland-egl-devel \
ninja-build \
pango-devel \
+1 -1
View File
@@ -1,4 +1,4 @@
FROM registry.gitlab.gnome.org/gnome/gtk/fedora-base:v27
FROM registry.gitlab.gnome.org/gnome/gtk/fedora-base:v25
# Enable sudo for wheel users
RUN sed -i -e 's/# %wheel/%wheel/' -e '0,/%wheel/{s/%wheel/# %wheel/}' /etc/sudoers
+1 -2
View File
@@ -8,8 +8,7 @@ builddir=$1
backend=$2
# Ignore memory leaks lower in dependencies
export LSAN_OPTIONS=suppressions=$srcdir/lsan.supp:print_suppressions=0
export G_SLICE=always-malloc
export LSAN_OPTIONS=suppressions=$srcdir/lsan.supp
case "${backend}" in
x11)
-8
View File
@@ -1,8 +0,0 @@
#!/bin/bash
set -eux -o pipefail
xcodebuild -version || :
xcodebuild -showsdks || :
system_profiler SPSoftwareDataType || :
-12
View File
@@ -1,13 +1,4 @@
<!--
Please, read the CONTRIBUTING.md guide on how to file a new issue.
https://gitlab.gnome.org/GNOME/gtk/-/blob/master/CONTRIBUTING.md
-->
## Steps to reproduce
<!--
Please, explain the sequence of actions necessary to reproduce the
bug
-->
1. ...
2. ...
@@ -41,8 +32,5 @@
## Additional information
<!--
- Screenshots or screen recordings are useful for visual errors
- Attaching a screenshot or a video without explaining the current
behavior and the actions necessary to reproduce the bug will lead
to the bug being closed
- Please report any warning or message printed on the terminal
-->
-10
View File
@@ -1,14 +1,4 @@
<!--
Please, read the CONTRIBUTING.md guide on how to file a new issue.
https://gitlab.gnome.org/GNOME/gtk/-/blob/master/CONTRIBUTING.md
-->
## Steps to reproduce
<!--
Please, explain the sequence of actions necessary to reproduce the
crash
-->
1. ...
2. ...
+9135 -139
View File
File diff suppressed because it is too large Load Diff
-9178
View File
File diff suppressed because it is too large Load Diff
-19
View File
@@ -1,19 +0,0 @@
#!/usr/bin/env python3
import os
from pathlib import PurePath
import subprocess
stylesheets = [ 'gtk/theme/Adwaita/Adwaita.css',
'gtk/theme/Adwaita/Adwaita-dark.css',
'gtk/theme/HighContrast/HighContrast.css',
'gtk/theme/HighContrast/HighContrast-dark.css' ]
sourceroot = os.environ.get('MESON_SOURCE_ROOT')
distroot = os.environ.get('MESON_DIST_ROOT')
for stylesheet in stylesheets:
stylesheet_path = PurePath(stylesheet)
src = PurePath(sourceroot, stylesheet_path.with_suffix('.scss'))
dst = PurePath(distroot, stylesheet_path)
subprocess.call(['sassc', '-a', '-M', '-t', 'compact', src, dst])
-3
View File
@@ -98,9 +98,6 @@ create_page1 (GtkWidget *assistant)
gtk_box_append (GTK_BOX (box), label);
entry = gtk_entry_new ();
gtk_accessible_update_relation (GTK_ACCESSIBLE (entry),
GTK_ACCESSIBLE_RELATION_LABELLED_BY, label, NULL,
-1);
gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
gtk_widget_set_valign (entry, GTK_ALIGN_CENTER);
gtk_box_append (GTK_BOX (box), entry);
+6
View File
@@ -319,6 +319,9 @@
<file>paintable_svg.c</file>
<file>panes.c</file>
<file>password_entry.c</file>
<file>path_fill.c</file>
<file>path_maze.c</file>
<file>path_text.c</file>
<file>peg_solitaire.c</file>
<file>pickers.c</file>
<file>printing.c</file>
@@ -403,6 +406,9 @@
<gresource prefix="/fontrendering">
<file>fontrendering.ui</file>
</gresource>
<gresource prefix="/path_text">
<file>path_text.ui</file>
</gresource>
<gresource prefix="/org/gtk/Demo4">
<file>icons/16x16/actions/application-exit.png</file>
<file>icons/16x16/actions/document-new.png</file>
+5 -4
View File
@@ -119,12 +119,13 @@ create_label (void)
static GtkWidget *
create_video (void)
{
GtkWidget *w = gtk_video_new ();
GtkMediaStream *stream = gtk_media_file_new_for_resource ("/images/gtk-logo.webm");
GtkWidget *w = gtk_picture_new_for_paintable (GDK_PAINTABLE (stream));
gtk_widget_set_size_request (w, 64, 64);
gtk_video_set_loop (GTK_VIDEO (w), TRUE);
gtk_video_set_autoplay (GTK_VIDEO (w), TRUE);
gtk_video_set_resource (GTK_VIDEO (w), "/images/gtk-logo.webm");
gtk_media_stream_set_loop (stream, TRUE);
gtk_media_stream_play (stream);
g_object_unref (stream);
return w;
}
-2
View File
@@ -787,8 +787,6 @@ gtk_gears_realize (GtkWidget *widget)
glLinkProgram(program);
glGetProgramInfoLog(program, sizeof msg, NULL, msg);
g_debug ("program info: %s\n", msg);
glDetachShader (program, v);
glDetachShader (program, f);
glDeleteShader (v);
glDeleteShader (f);
+3
View File
@@ -68,6 +68,9 @@ demos = files([
'paintable_mediastream.c',
'panes.c',
'password_entry.c',
'path_fill.c',
'path_maze.c',
'path_text.c',
'peg_solitaire.c',
'pickers.c',
'printing.c',
+359
View File
@@ -0,0 +1,359 @@
/* Path/Fill
*
* This demo shows how to use PangoCairo to draw text with more than
* just a single color.
*/
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#include "paintable.h"
#include "gsk/gskpathdashprivate.h"
#define GTK_TYPE_PATH_PAINTABLE (gtk_path_paintable_get_type ())
G_DECLARE_FINAL_TYPE (GtkPathPaintable, gtk_path_paintable, GTK, PATH_PAINTABLE, GObject)
struct _GtkPathPaintable
{
GObject parent_instance;
int width;
int height;
GskPath *path;
GdkPaintable *background;
};
struct _GtkPathPaintableClass
{
GObjectClass parent_class;
};
static int
gtk_path_paintable_get_intrinsic_width (GdkPaintable *paintable)
{
GtkPathPaintable *self = GTK_PATH_PAINTABLE (paintable);
if (self->background)
return MAX (gdk_paintable_get_intrinsic_width (self->background), self->width);
else
return self->width;
}
static int
gtk_path_paintable_get_intrinsic_height (GdkPaintable *paintable)
{
GtkPathPaintable *self = GTK_PATH_PAINTABLE (paintable);
if (self->background)
return MAX (gdk_paintable_get_intrinsic_height (self->background), self->height);
else
return self->height;
}
static void
gtk_path_paintable_snapshot (GdkPaintable *paintable,
GdkSnapshot *snapshot,
double width,
double height)
{
GtkPathPaintable *self = GTK_PATH_PAINTABLE (paintable);
#if 0
gtk_snapshot_push_fill (snapshot, self->path, GSK_FILL_RULE_WINDING);
#else
GskStroke *stroke = gsk_stroke_new (2.0);
gtk_snapshot_push_stroke (snapshot, self->path, stroke);
gsk_stroke_free (stroke);
#endif
if (self->background)
{
gdk_paintable_snapshot (self->background, snapshot, width, height);
}
else
{
gtk_snapshot_append_linear_gradient (snapshot,
&GRAPHENE_RECT_INIT (0, 0, width, height),
&GRAPHENE_POINT_INIT (0, 0),
&GRAPHENE_POINT_INIT (width, height),
(GskColorStop[8]) {
{ 0.0, { 1.0, 0.0, 0.0, 1.0 } },
{ 0.2, { 1.0, 0.0, 0.0, 1.0 } },
{ 0.3, { 1.0, 1.0, 0.0, 1.0 } },
{ 0.4, { 0.0, 1.0, 0.0, 1.0 } },
{ 0.6, { 0.0, 1.0, 1.0, 1.0 } },
{ 0.7, { 0.0, 0.0, 1.0, 1.0 } },
{ 0.8, { 1.0, 0.0, 1.0, 1.0 } },
{ 1.0, { 1.0, 0.0, 1.0, 1.0 } }
},
8);
}
gtk_snapshot_pop (snapshot);
}
static GdkPaintableFlags
gtk_path_paintable_get_flags (GdkPaintable *paintable)
{
GtkPathPaintable *self = GTK_PATH_PAINTABLE (paintable);
if (self->background)
return gdk_paintable_get_flags (self->background);
else
return GDK_PAINTABLE_STATIC_CONTENTS | GDK_PAINTABLE_STATIC_SIZE;
}
static void
gtk_path_paintable_paintable_init (GdkPaintableInterface *iface)
{
iface->get_intrinsic_width = gtk_path_paintable_get_intrinsic_width;
iface->get_intrinsic_height = gtk_path_paintable_get_intrinsic_height;
iface->snapshot = gtk_path_paintable_snapshot;
iface->get_flags = gtk_path_paintable_get_flags;
}
/* When defining the GType, we need to implement the GdkPaintable interface */
G_DEFINE_TYPE_WITH_CODE (GtkPathPaintable, gtk_path_paintable, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
gtk_path_paintable_paintable_init))
/* Here's the boilerplate for the GObject declaration.
* We don't need to do anything special here, because we keep no
* data of our own.
*/
static void
gtk_path_paintable_class_init (GtkPathPaintableClass *klass)
{
}
static void
gtk_path_paintable_init (GtkPathPaintable *self)
{
}
/* And finally, we add a simple constructor.
* It is declared in the header so that the other examples
* can use it.
*/
GdkPaintable *
gtk_path_paintable_new (GskPath *path,
GdkPaintable *background,
int width,
int height)
{
GtkPathPaintable *self;
self = g_object_new (GTK_TYPE_PATH_PAINTABLE, NULL);
self->path = path;
self->background = background;
if (self->background)
{
g_signal_connect_swapped (self->background, "invalidate-contents", G_CALLBACK (gdk_paintable_invalidate_contents), self);
g_signal_connect_swapped (self->background, "invalidate-size", G_CALLBACK (gdk_paintable_invalidate_size), self);
}
self->width = width;
self->height = height;
return GDK_PAINTABLE (self);
}
void
gtk_path_paintable_set_path (GtkPathPaintable *self,
GskPath *path)
{
g_clear_pointer (&self->path, gsk_path_unref);
self->path = gsk_path_ref (path);
gdk_paintable_invalidate_contents (GDK_PAINTABLE (self));
}
static GskPath *
create_hexagon (GtkWidget *widget)
{
GskPathBuilder *builder;
builder = gsk_path_builder_new ();
gsk_path_builder_move_to (builder, 120, 0);
gsk_path_builder_line_to (builder, 360, 0);
gsk_path_builder_line_to (builder, 480, 208);
gsk_path_builder_line_to (builder, 360, 416);
gsk_path_builder_line_to (builder, 120, 416);
gsk_path_builder_line_to (builder, 0, 208);
gsk_path_builder_close (builder);
return gsk_path_builder_free_to_path (builder);
}
static GskPath *
create_path_from_text (GtkWidget *widget)
{
cairo_surface_t *surface;
cairo_t *cr;
cairo_path_t *path;
PangoLayout *layout;
PangoFontDescription *desc;
GskPath *result;
surface = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL);
cr = cairo_create (surface);
layout = gtk_widget_create_pango_layout (widget, "Pango power!\nPango power!\nPango power!");
desc = pango_font_description_from_string ("sans bold 36");
pango_layout_set_font_description (layout, desc);
pango_font_description_free (desc);
pango_cairo_layout_path (cr, layout);
path = cairo_copy_path_flat (cr);
result = gsk_path_new_from_cairo (path);
cairo_path_destroy (path);
g_object_unref (layout);
cairo_destroy (cr);
cairo_surface_destroy (surface);
return result;
}
static gboolean
build_path (GskPathOperation op,
const graphene_point_t *pts,
gsize n_pts,
float weight,
gpointer user_data)
{
GskPathBuilder *builder = user_data;
switch (op)
{
case GSK_PATH_MOVE:
gsk_path_builder_move_to (builder, pts[0].x, pts[0].y);
break;
case GSK_PATH_CLOSE:
gsk_path_builder_close (builder);
break;
case GSK_PATH_LINE:
gsk_path_builder_line_to (builder, pts[1].x, pts[1].y);
break;
case GSK_PATH_CURVE:
gsk_path_builder_curve_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y, pts[3].x, pts[3].y);
break;
case GSK_PATH_CONIC:
gsk_path_builder_conic_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y, weight);
break;
default:
g_assert_not_reached ();
break;
}
return TRUE;
}
static gboolean
update_path (GtkWidget *widget,
GdkFrameClock *frame_clock,
gpointer measure)
{
float progress = gdk_frame_clock_get_frame_time (frame_clock) % (60 * G_USEC_PER_SEC) / (float) (30 * G_USEC_PER_SEC);
GskPathBuilder *builder;
GskPath *path;
graphene_point_t pos;
graphene_vec2_t tangent;
GskStroke *stroke;
builder = gsk_path_builder_new ();
gsk_path_builder_add_segment (builder,
measure,
#if 1
0.0, gsk_path_measure_get_length (measure));
#else
progress > 1 ? (progress - 1) * gsk_path_measure_get_length (measure) : 0.0,
(progress < 1 ? progress : 1.0) * gsk_path_measure_get_length (measure));
#endif
path = gsk_path_builder_free_to_path (builder);
stroke = gsk_stroke_new (1);
gsk_stroke_set_dash (stroke, (float[2]) { 10, 5 }, 2);
gsk_stroke_set_dash_offset (stroke, - (gdk_frame_clock_get_frame_time (frame_clock) % G_USEC_PER_SEC) * 15. / G_USEC_PER_SEC);
builder = gsk_path_builder_new ();
gsk_path_dash (path, stroke, 0.2, build_path, builder);
gsk_path_unref (path);
gsk_path_measure_get_point (measure,
(progress > 1 ? (progress - 1) : progress) * gsk_path_measure_get_length (measure),
&pos,
&tangent);
gsk_path_builder_move_to (builder, pos.x + 5 * graphene_vec2_get_x (&tangent), pos.y + 5 * graphene_vec2_get_y (&tangent));
gsk_path_builder_line_to (builder, pos.x + 3 * graphene_vec2_get_y (&tangent), pos.y + 3 * graphene_vec2_get_x (&tangent));
gsk_path_builder_line_to (builder, pos.x - 3 * graphene_vec2_get_y (&tangent), pos.y - 3 * graphene_vec2_get_x (&tangent));
gsk_path_builder_close (builder);
path = gsk_path_builder_free_to_path (builder);
gtk_path_paintable_set_path (GTK_PATH_PAINTABLE (gtk_picture_get_paintable (GTK_PICTURE (widget))),
path);
gsk_path_unref (path);
return G_SOURCE_CONTINUE;
}
GtkWidget *
do_path_fill (GtkWidget *do_widget)
{
static GtkWidget *window = NULL;
if (!window)
{
GtkWidget *picture;
GdkPaintable *paintable;
GtkMediaStream *stream;
GskPath *path;
graphene_rect_t bounds;
GskPathMeasure *measure;
window = gtk_window_new ();
gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
gtk_window_set_title (GTK_WINDOW (window), "Path Fill");
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
#if 0
stream = gtk_media_file_new_for_resource ("/images/gtk-logo.webm");
#else
stream = gtk_nuclear_media_stream_new ();
#endif
gtk_media_stream_play (stream);
gtk_media_stream_set_loop (stream, TRUE);
path = create_hexagon (window);
path = create_path_from_text (window);
gsk_path_get_bounds (path, &bounds);
paintable = gtk_path_paintable_new (path,
GDK_PAINTABLE (stream),
bounds.origin.x + bounds.size.width,
bounds.origin.y + bounds.size.height);
picture = gtk_picture_new_for_paintable (paintable);
measure = gsk_path_measure_new (path);
gtk_widget_add_tick_callback (picture, update_path, measure, (GDestroyNotify) gsk_path_measure_unref);
gtk_picture_set_keep_aspect_ratio (GTK_PICTURE (picture), FALSE);
gtk_picture_set_can_shrink (GTK_PICTURE (picture), FALSE);
g_object_unref (paintable);
gtk_window_set_child (GTK_WINDOW (window), picture);
}
if (!gtk_widget_get_visible (window))
gtk_widget_show (window);
else
gtk_window_destroy (GTK_WINDOW (window));
return window;
}
+338
View File
@@ -0,0 +1,338 @@
/* Path/Maze
*
* This demo shows how to use a GskPath to create a maze and use
* gsk_path_measure_get_closest_point() to check the mouse stays
* on the path.
*
* It also shows off the performance of GskPath (or not) as this
* is a rather complex path.
*/
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#include "paintable.h"
#define MAZE_GRID_SIZE 20
#define MAZE_STROKE_SIZE_ACTIVE (MAZE_GRID_SIZE - 4)
#define MAZE_STROKE_SIZE_INACTIVE (MAZE_GRID_SIZE - 12)
#define MAZE_WIDTH 31
#define MAZE_HEIGHT 21
#define GTK_TYPE_MAZE (gtk_maze_get_type ())
G_DECLARE_FINAL_TYPE (GtkMaze, gtk_maze, GTK, MAZE, GtkWidget)
struct _GtkMaze
{
GtkWidget parent_instance;
int width;
int height;
GskPath *path;
GskPathMeasure *measure;
GdkPaintable *background;
gboolean active;
};
struct _GtkMazeClass
{
GtkWidgetClass parent_class;
};
G_DEFINE_TYPE (GtkMaze, gtk_maze, GTK_TYPE_WIDGET)
static void
gtk_maze_measure (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
GtkMaze *self = GTK_MAZE (widget);
if (orientation == GTK_ORIENTATION_HORIZONTAL)
*minimum = *natural = self->width;
else
*minimum = *natural = self->height;
}
static void
gtk_maze_snapshot (GtkWidget *widget,
GdkSnapshot *snapshot)
{
GtkMaze *self = GTK_MAZE (widget);
double width = gtk_widget_get_width (widget);
double height = gtk_widget_get_height (widget);
GskStroke *stroke;
stroke = gsk_stroke_new (MAZE_STROKE_SIZE_INACTIVE);
if (self->active)
gsk_stroke_set_line_width (stroke, MAZE_STROKE_SIZE_ACTIVE);
gsk_stroke_set_line_join (stroke, GSK_LINE_JOIN_ROUND);
gsk_stroke_set_line_cap (stroke, GSK_LINE_CAP_ROUND);
gtk_snapshot_push_stroke (snapshot, self->path, stroke);
gsk_stroke_free (stroke);
if (self->background)
{
gdk_paintable_snapshot (self->background, snapshot, width, height);
}
else
{
gtk_snapshot_append_linear_gradient (snapshot,
&GRAPHENE_RECT_INIT (0, 0, width, height),
&GRAPHENE_POINT_INIT (0, 0),
&GRAPHENE_POINT_INIT (width, height),
(GskColorStop[8]) {
{ 0.0, { 1.0, 0.0, 0.0, 1.0 } },
{ 0.2, { 1.0, 0.0, 0.0, 1.0 } },
{ 0.3, { 1.0, 1.0, 0.0, 1.0 } },
{ 0.4, { 0.0, 1.0, 0.0, 1.0 } },
{ 0.6, { 0.0, 1.0, 1.0, 1.0 } },
{ 0.7, { 0.0, 0.0, 1.0, 1.0 } },
{ 0.8, { 1.0, 0.0, 1.0, 1.0 } },
{ 1.0, { 1.0, 0.0, 1.0, 1.0 } }
},
8);
}
gtk_snapshot_pop (snapshot);
}
static void
gtk_maze_dispose (GObject *object)
{
GtkMaze *self = GTK_MAZE (object);
g_clear_pointer (&self->path, gsk_path_unref);
g_clear_pointer (&self->measure, gsk_path_measure_unref);
if (self->background)
{
g_signal_handlers_disconnect_matched (self->background, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);
g_clear_object (&self->background);
}
G_OBJECT_CLASS (gtk_maze_parent_class)->dispose (object);
}
static void
gtk_maze_class_init (GtkMazeClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = gtk_maze_dispose;
widget_class->measure = gtk_maze_measure;
widget_class->snapshot = gtk_maze_snapshot;
}
static void
pointer_motion (GtkEventControllerMotion *controller,
double x,
double y,
GtkMaze *self)
{
if (!self->active)
return;
if (gsk_path_measure_get_closest_point (self->measure, &GRAPHENE_POINT_INIT (x, y), NULL) <= MAZE_STROKE_SIZE_ACTIVE / 2.0f)
return;
self->active = FALSE;
gtk_widget_queue_draw (GTK_WIDGET (self));
}
static void
pointer_leave (GtkEventControllerMotion *controller,
GtkMaze *self)
{
if (!self->active)
{
self->active = TRUE;
gtk_widget_queue_draw (GTK_WIDGET (self));
}
}
static void
gtk_maze_init (GtkMaze *self)
{
GtkEventController *controller;
controller = GTK_EVENT_CONTROLLER (gtk_event_controller_motion_new ());
g_signal_connect (controller, "motion", G_CALLBACK (pointer_motion), self);
g_signal_connect (controller, "leave", G_CALLBACK (pointer_leave), self);
gtk_widget_add_controller (GTK_WIDGET (self), controller);
self->active = TRUE;
}
static void
gtk_maze_set_path (GtkMaze *self,
GskPath *path)
{
g_clear_pointer (&self->path, gsk_path_unref);
g_clear_pointer (&self->measure, gsk_path_measure_unref);
self->path = gsk_path_ref (path);
self->measure = gsk_path_measure_new (path);
gtk_widget_queue_draw (GTK_WIDGET (self));
}
GtkWidget *
gtk_maze_new (GskPath *path,
GdkPaintable *background,
int width,
int height)
{
GtkMaze *self;
self = g_object_new (GTK_TYPE_MAZE, NULL);
gtk_maze_set_path (self, path);
gsk_path_unref (path);
self->background = background;
if (self->background)
{
g_signal_connect_swapped (self->background, "invalidate-contents", G_CALLBACK (gtk_widget_queue_draw), self);
g_signal_connect_swapped (self->background, "invalidate-size", G_CALLBACK (gtk_widget_queue_resize), self);
}
self->width = width;
self->height = height;
return GTK_WIDGET (self);
}
static void
add_point_to_maze (GtkBitset *maze,
GskPathBuilder *builder,
guint x,
guint y)
{
gboolean set[4] = { };
guint dir;
gtk_bitset_add (maze, y * MAZE_WIDTH + x);
while (TRUE)
{
set[0] = set[0] || x == 0 || gtk_bitset_contains (maze, y * MAZE_WIDTH + x - 1);
set[1] = set[1] || y == 0 || gtk_bitset_contains (maze, (y - 1) * MAZE_WIDTH + x);
set[2] = set[2] || x + 1 == MAZE_WIDTH || gtk_bitset_contains (maze, y * MAZE_WIDTH + x + 1);
set[3] = set[3] || y + 1 == MAZE_HEIGHT || gtk_bitset_contains (maze, (y + 1) * MAZE_WIDTH + x);
if (set[0] && set[1] && set[2] && set[3])
return;
do
{
dir = g_random_int_range (0, 4);
}
while (set[dir]);
switch (dir)
{
case 0:
gsk_path_builder_move_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
gsk_path_builder_line_to (builder, (x - 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
add_point_to_maze (maze, builder, x - 1, y);
break;
case 1:
gsk_path_builder_move_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
gsk_path_builder_line_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y - 0.5) * MAZE_GRID_SIZE);
add_point_to_maze (maze, builder, x, y - 1);
break;
case 2:
gsk_path_builder_move_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
gsk_path_builder_line_to (builder, (x + 1.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
add_point_to_maze (maze, builder, x + 1, y);
break;
case 3:
gsk_path_builder_move_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 0.5) * MAZE_GRID_SIZE);
gsk_path_builder_line_to (builder, (x + 0.5) * MAZE_GRID_SIZE, (y + 1.5) * MAZE_GRID_SIZE);
add_point_to_maze (maze, builder, x, y + 1);
break;
default:
g_assert_not_reached ();
break;
}
}
}
static GskPath *
create_path_for_maze (GtkWidget *widget)
{
GskPathBuilder *builder;
GtkBitset *maze;
builder = gsk_path_builder_new ();
maze = gtk_bitset_new_empty ();
/* make sure the outer lines are unreachable:
* Set the full range, then remove the center again. */
gtk_bitset_add_range (maze, 0, MAZE_WIDTH * MAZE_HEIGHT);
gtk_bitset_remove_rectangle (maze, MAZE_WIDTH + 1, MAZE_WIDTH - 2, MAZE_HEIGHT - 2, MAZE_WIDTH);
/* Fill the maze */
add_point_to_maze (maze, builder, MAZE_WIDTH / 2, MAZE_HEIGHT / 2);
/* Add start and stop lines */
gsk_path_builder_move_to (builder, 1.5 * MAZE_GRID_SIZE, -0.5 * MAZE_GRID_SIZE);
gsk_path_builder_line_to (builder, 1.5 * MAZE_GRID_SIZE, 1.5 * MAZE_GRID_SIZE);
gsk_path_builder_move_to (builder, (MAZE_WIDTH - 1.5) * MAZE_GRID_SIZE, (MAZE_HEIGHT - 1.5) * MAZE_GRID_SIZE);
gsk_path_builder_line_to (builder, (MAZE_WIDTH - 1.5) * MAZE_GRID_SIZE, (MAZE_HEIGHT + 0.5) * MAZE_GRID_SIZE);
gtk_bitset_unref (maze);
return gsk_path_builder_free_to_path (builder);
}
GtkWidget *
do_path_maze (GtkWidget *do_widget)
{
static GtkWidget *window = NULL;
if (!window)
{
GtkWidget *maze;
GtkMediaStream *stream;
GskPath *path;
window = gtk_window_new ();
gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
gtk_window_set_title (GTK_WINDOW (window), "Follow the maze with the mouse");
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
#if 0
stream = gtk_media_file_new_for_resource ("/images/gtk-logo.webm");
#else
stream = gtk_nuclear_media_stream_new ();
#endif
gtk_media_stream_play (stream);
gtk_media_stream_set_loop (stream, TRUE);
path = create_path_for_maze (window);
maze = gtk_maze_new (path,
GDK_PAINTABLE (stream),
MAZE_WIDTH * MAZE_GRID_SIZE,
MAZE_HEIGHT * MAZE_GRID_SIZE);
gtk_window_set_child (GTK_WINDOW (window), maze);
}
if (!gtk_widget_get_visible (window))
gtk_widget_show (window);
else
gtk_window_destroy (GTK_WINDOW (window));
return window;
}
+590
View File
@@ -0,0 +1,590 @@
/* Path/Text
*
* This demo shows how to use GskPath to animate a path along another path.
*/
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#define GTK_TYPE_PATH_WIDGET (gtk_path_widget_get_type ())
G_DECLARE_FINAL_TYPE (GtkPathWidget, gtk_path_widget, GTK, PATH_WIDGET, GtkWidget)
#define POINT_SIZE 8
enum {
PROP_0,
PROP_TEXT,
PROP_EDITABLE,
N_PROPS
};
struct _GtkPathWidget
{
GtkWidget parent_instance;
char *text;
gboolean editable;
graphene_point_t points[4];
guint active_point;
float line_closest;
GskPath *line_path;
GskPathMeasure *line_measure;
GskPath *text_path;
GdkPaintable *background;
};
struct _GtkPathWidgetClass
{
GtkWidgetClass parent_class;
};
static GParamSpec *properties[N_PROPS] = { NULL, };
G_DEFINE_TYPE (GtkPathWidget, gtk_path_widget, GTK_TYPE_WIDGET)
static GskPath *
create_path_from_text (GtkWidget *widget,
const char *text)
{
cairo_surface_t *surface;
cairo_t *cr;
cairo_path_t *path;
PangoLayout *layout;
PangoFontDescription *desc;
GskPath *result;
surface = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL);
cr = cairo_create (surface);
layout = gtk_widget_create_pango_layout (widget, text);
desc = pango_font_description_from_string ("sans bold 36");
pango_layout_set_font_description (layout, desc);
pango_font_description_free (desc);
cairo_move_to (cr, 0, - pango_layout_get_baseline (layout) / (double) PANGO_SCALE);
pango_cairo_layout_path (cr, layout);
path = cairo_copy_path_flat (cr);
result = gsk_path_new_from_cairo (path);
cairo_path_destroy (path);
g_object_unref (layout);
cairo_destroy (cr);
cairo_surface_destroy (surface);
return result;
}
typedef struct
{
GskPathMeasure *measure;
GskPathBuilder *builder;
double scale;
} GtkPathTransform;
static void
gtk_path_transform_point (GskPathMeasure *measure,
const graphene_point_t *pt,
float scale,
graphene_point_t *res)
{
graphene_vec2_t tangent;
gsk_path_measure_get_point (measure, pt->x * scale, res, &tangent);
res->x -= pt->y * scale * graphene_vec2_get_y (&tangent);
res->y += pt->y * scale * graphene_vec2_get_x (&tangent);
}
static gboolean
gtk_path_transform_op (GskPathOperation op,
const graphene_point_t *pts,
gsize n_pts,
float weight,
gpointer data)
{
GtkPathTransform *transform = data;
switch (op)
{
case GSK_PATH_MOVE:
{
graphene_point_t res;
gtk_path_transform_point (transform->measure, &pts[0], transform->scale, &res);
gsk_path_builder_move_to (transform->builder, res.x, res.y);
}
break;
case GSK_PATH_LINE:
{
graphene_point_t res;
gtk_path_transform_point (transform->measure, &pts[1], transform->scale, &res);
gsk_path_builder_line_to (transform->builder, res.x, res.y);
}
break;
case GSK_PATH_CURVE:
{
graphene_point_t res[3];
gtk_path_transform_point (transform->measure, &pts[1], transform->scale, &res[0]);
gtk_path_transform_point (transform->measure, &pts[2], transform->scale, &res[1]);
gtk_path_transform_point (transform->measure, &pts[3], transform->scale, &res[2]);
gsk_path_builder_curve_to (transform->builder, res[0].x, res[0].y, res[1].x, res[1].y, res[2].x, res[2].y);
}
break;
case GSK_PATH_CONIC:
{
graphene_point_t res[2];
gtk_path_transform_point (transform->measure, &pts[1], transform->scale, &res[0]);
gtk_path_transform_point (transform->measure, &pts[2], transform->scale, &res[1]);
gsk_path_builder_conic_to (transform->builder, res[0].x, res[0].y, res[1].x, res[1].y, weight);
}
break;
case GSK_PATH_CLOSE:
gsk_path_builder_close (transform->builder);
break;
default:
g_assert_not_reached();
return FALSE;
}
return TRUE;
}
static GskPath *
gtk_path_transform (GskPathMeasure *measure,
GskPath *path)
{
GtkPathTransform transform = { measure, gsk_path_builder_new () };
graphene_rect_t bounds;
gsk_path_get_bounds (path, &bounds);
if (bounds.origin.x + bounds.size.width > 0)
transform.scale = gsk_path_measure_get_length (measure) / (bounds.origin.x + bounds.size.width);
else
transform.scale = 1.0f;
gsk_path_foreach (path, GSK_PATH_FOREACH_ALLOW_CURVE, gtk_path_transform_op, &transform);
return gsk_path_builder_free_to_path (transform.builder);
}
static void
gtk_path_widget_clear_text_path (GtkPathWidget *self)
{
g_clear_pointer (&self->text_path, gsk_path_unref);
}
static void
gtk_path_widget_clear_paths (GtkPathWidget *self)
{
gtk_path_widget_clear_text_path (self);
g_clear_pointer (&self->line_path, gsk_path_unref);
g_clear_pointer (&self->line_measure, gsk_path_measure_unref);
}
static void
gtk_path_widget_create_text_path (GtkPathWidget *self)
{
GskPath *path;
gtk_path_widget_clear_text_path (self);
if (self->line_measure == NULL)
return;
path = create_path_from_text (GTK_WIDGET (self), self->text);
self->text_path = gtk_path_transform (self->line_measure, path);
gsk_path_unref (path);
}
static void
gtk_path_widget_create_paths (GtkPathWidget *self)
{
double width = gtk_widget_get_width (GTK_WIDGET (self));
double height = gtk_widget_get_height (GTK_WIDGET (self));
GskPathBuilder *builder;
gtk_path_widget_clear_paths (self);
if (width <= 0 || height <= 0)
return;
builder = gsk_path_builder_new ();
gsk_path_builder_move_to (builder,
self->points[0].x * width, self->points[0].y * height);
gsk_path_builder_curve_to (builder,
self->points[1].x * width, self->points[1].y * height,
self->points[2].x * width, self->points[2].y * height,
self->points[3].x * width, self->points[3].y * height);
self->line_path = gsk_path_builder_free_to_path (builder);
self->line_measure = gsk_path_measure_new (self->line_path);
gtk_path_widget_create_text_path (self);
}
static void
gtk_path_widget_allocate (GtkWidget *widget,
int width,
int height,
int baseline)
{
GtkPathWidget *self = GTK_PATH_WIDGET (widget);
GTK_WIDGET_CLASS (gtk_path_widget_parent_class)->size_allocate (widget, width, height, baseline);
gtk_path_widget_create_paths (self);
}
static void
gtk_path_widget_snapshot (GtkWidget *widget,
GtkSnapshot *snapshot)
{
GtkPathWidget *self = GTK_PATH_WIDGET (widget);
double width = gtk_widget_get_width (widget);
double height = gtk_widget_get_height (widget);
GskPath *path;
GskStroke *stroke;
gsize i;
/* frosted glass the background */
gtk_snapshot_push_blur (snapshot, 100);
gdk_paintable_snapshot (self->background, snapshot, width, height);
gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 1, 1, 1, 0.6 }, &GRAPHENE_RECT_INIT (0, 0, width, height));
gtk_snapshot_pop (snapshot);
/* draw the text */
if (self->text_path)
{
gtk_snapshot_push_fill (snapshot, self->text_path, GSK_FILL_RULE_WINDING);
gdk_paintable_snapshot (self->background, snapshot, width, height);
/* ... with an emboss effect */
stroke = gsk_stroke_new (2.0);
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT(1, 1));
gtk_snapshot_push_stroke (snapshot, self->text_path, stroke);
gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 0, 0, 0, 0.2 }, &GRAPHENE_RECT_INIT (0, 0, width, height));
gsk_stroke_free (stroke);
gtk_snapshot_pop (snapshot);
gtk_snapshot_pop (snapshot);
}
if (self->editable && self->line_path)
{
GskPathBuilder *builder;
/* draw the control line */
stroke = gsk_stroke_new (1.0);
gtk_snapshot_push_stroke (snapshot, self->line_path, stroke);
gsk_stroke_free (stroke);
gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 0, 0, 0, 1 }, &GRAPHENE_RECT_INIT (0, 0, width, height));
gtk_snapshot_pop (snapshot);
/* draw the points */
builder = gsk_path_builder_new ();
for (i = 0; i < 4; i++)
{
gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (self->points[i].x * width, self->points[i].y * height), POINT_SIZE);
}
path = gsk_path_builder_free_to_path (builder);
gtk_snapshot_push_fill (snapshot, path, GSK_FILL_RULE_WINDING);
gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 1, 1, 1, 1 }, &GRAPHENE_RECT_INIT (0, 0, width, height));
gtk_snapshot_pop (snapshot);
stroke = gsk_stroke_new (1.0);
gtk_snapshot_push_stroke (snapshot, path, stroke);
gsk_stroke_free (stroke);
gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 0, 0, 0, 1 }, &GRAPHENE_RECT_INIT (0, 0, width, height));
gtk_snapshot_pop (snapshot);
gsk_path_unref (path);
}
if (self->line_closest >= 0)
{
GskPathBuilder *builder;
graphene_point_t closest;
builder = gsk_path_builder_new ();
gsk_path_measure_get_point (self->line_measure, self->line_closest, &closest, NULL);
gsk_path_builder_add_circle (builder, &closest, POINT_SIZE);
path = gsk_path_builder_free_to_path (builder);
gtk_snapshot_push_fill (snapshot, path, GSK_FILL_RULE_WINDING);
gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 0, 0, 1, 1 }, &GRAPHENE_RECT_INIT (0, 0, width, height));
gtk_snapshot_pop (snapshot);
gsk_path_unref (path);
}
}
static void
gtk_path_widget_set_text (GtkPathWidget *self,
const char *text)
{
if (g_strcmp0 (self->text, text) == 0)
return;
g_free (self->text);
self->text = g_strdup (text);
gtk_path_widget_create_paths (self);
gtk_widget_queue_draw (GTK_WIDGET (self));
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TEXT]);
}
static void
gtk_path_widget_set_editable (GtkPathWidget *self,
gboolean editable)
{
if (self->editable == editable)
return;
self->editable = editable;
gtk_widget_queue_draw (GTK_WIDGET (self));
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_EDITABLE]);
}
static void
gtk_path_widget_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkPathWidget *self = GTK_PATH_WIDGET (object);
switch (prop_id)
{
case PROP_TEXT:
gtk_path_widget_set_text (self, g_value_get_string (value));
break;
case PROP_EDITABLE:
gtk_path_widget_set_editable (self, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_path_widget_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkPathWidget *self = GTK_PATH_WIDGET (object);
switch (prop_id)
{
case PROP_TEXT:
g_value_set_string (value, self->text);
break;
case PROP_EDITABLE:
g_value_set_boolean (value, self->editable);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_path_widget_dispose (GObject *object)
{
GtkPathWidget *self = GTK_PATH_WIDGET (object);
gtk_path_widget_clear_paths (self);
G_OBJECT_CLASS (gtk_path_widget_parent_class)->dispose (object);
}
static void
gtk_path_widget_class_init (GtkPathWidgetClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = gtk_path_widget_dispose;
object_class->set_property = gtk_path_widget_set_property;
object_class->get_property = gtk_path_widget_get_property;
widget_class->size_allocate = gtk_path_widget_allocate;
widget_class->snapshot = gtk_path_widget_snapshot;
properties[PROP_TEXT] =
g_param_spec_string ("text",
"text",
"Text transformed along a path",
NULL,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
properties[PROP_EDITABLE] =
g_param_spec_boolean ("editable",
"editable",
"If the path can be edited by the user",
FALSE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
drag_begin (GtkGestureDrag *gesture,
double x,
double y,
GtkPathWidget *self)
{
graphene_point_t mouse = GRAPHENE_POINT_INIT (x, y);
double width = gtk_widget_get_width (GTK_WIDGET (self));
double height = gtk_widget_get_height (GTK_WIDGET (self));
gsize i;
for (i = 0; i < 4; i++)
{
if (graphene_point_distance (&GRAPHENE_POINT_INIT (self->points[i].x * width, self->points[i].y * height), &mouse, NULL, NULL) <= POINT_SIZE)
{
self->active_point = i;
break;
}
}
if (i == 4)
{
gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
return;
}
gtk_widget_queue_draw (GTK_WIDGET (self));
}
static void
drag_update (GtkGestureDrag *drag,
double offset_x,
double offset_y,
GtkPathWidget *self)
{
double width = gtk_widget_get_width (GTK_WIDGET (self));
double height = gtk_widget_get_height (GTK_WIDGET (self));
double start_x, start_y;
gtk_gesture_drag_get_start_point (drag, &start_x, &start_y);
self->points[self->active_point] = GRAPHENE_POINT_INIT ((start_x + offset_x) / width,
(start_y + offset_y) / height);
self->points[self->active_point].x = CLAMP (self->points[self->active_point].x, 0, 1);
self->points[self->active_point].y = CLAMP (self->points[self->active_point].y, 0, 1);
gtk_path_widget_create_paths (self);
gtk_widget_queue_draw (GTK_WIDGET (self));
}
static void
pointer_motion (GtkEventControllerMotion *controller,
double x,
double y,
GtkPathWidget *self)
{
gsk_path_measure_get_closest_point_full (self->line_measure,
&GRAPHENE_POINT_INIT (x, y),
INFINITY,
&self->line_closest,
NULL, NULL, NULL);
gtk_widget_queue_draw (GTK_WIDGET (self));
}
static void
pointer_leave (GtkEventControllerMotion *controller,
GtkPathWidget *self)
{
self->line_closest = -1;
gtk_widget_queue_draw (GTK_WIDGET (self));
}
static void
gtk_path_widget_init (GtkPathWidget *self)
{
GtkEventController *controller;
controller = GTK_EVENT_CONTROLLER (gtk_gesture_drag_new ());
g_signal_connect (controller, "drag-begin", G_CALLBACK (drag_begin), self);
g_signal_connect (controller, "drag-update", G_CALLBACK (drag_update), self);
g_signal_connect (controller, "drag-end", G_CALLBACK (drag_update), self);
gtk_widget_add_controller (GTK_WIDGET (self), controller);
controller = GTK_EVENT_CONTROLLER (gtk_event_controller_motion_new ());
g_signal_connect (controller, "enter", G_CALLBACK (pointer_motion), self);
g_signal_connect (controller, "motion", G_CALLBACK (pointer_motion), self);
g_signal_connect (controller, "leave", G_CALLBACK (pointer_leave), self);
gtk_widget_add_controller (GTK_WIDGET (self), controller);
self->line_closest = -1;
self->points[0] = GRAPHENE_POINT_INIT (0.1, 0.9);
self->points[1] = GRAPHENE_POINT_INIT (0.3, 0.1);
self->points[2] = GRAPHENE_POINT_INIT (0.7, 0.1);
self->points[3] = GRAPHENE_POINT_INIT (0.9, 0.9);
self->background = GDK_PAINTABLE (gdk_texture_new_from_resource ("/sliding_puzzle/portland-rose.jpg"));
gtk_path_widget_set_text (self, "It's almost working");
}
GtkWidget *
gtk_path_widget_new (void)
{
GtkPathWidget *self;
self = g_object_new (GTK_TYPE_PATH_WIDGET, NULL);
return GTK_WIDGET (self);
}
GtkWidget *
do_path_text (GtkWidget *do_widget)
{
static GtkWidget *window = NULL;
if (!window)
{
GtkBuilder *builder;
g_type_ensure (GTK_TYPE_PATH_WIDGET);
builder = gtk_builder_new_from_resource ("/path_text/path_text.ui");
window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
gtk_window_set_display (GTK_WINDOW (window),
gtk_widget_get_display (do_widget));
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *) &window);
g_object_unref (builder);
}
if (!gtk_widget_get_visible (window))
gtk_widget_show (window);
else
gtk_window_destroy (GTK_WINDOW (window));
return window;
}
+38
View File
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<object class="GtkWindow" id="window">
<property name="title" translatable="yes">Text along a Path</property>
<child type="titlebar">
<object class="GtkHeaderBar">
<child type="end">
<object class="GtkToggleButton" id="edit-toggle">
<property name="icon-name">document-edit-symbolic</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<child>
<object class="GtkRevealer">
<property name="reveal-child" bind-source="edit-toggle" bind-property="active" bind-flags="sync-create"></property>
<child>
<object class="GtkEntry" id="text">
<property name="text">Through the looking glass</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkPathWidget" id="view">
<property name="editable" bind-source="edit-toggle" bind-property="active" bind-flags="sync-create"></property>
<property name="text" bind-source="text" bind-property="text" bind-flags="sync-create"></property>
<property name="hexpand">true</property>
<property name="vexpand">true</property>
</object>
</child>
</object>
</child>
</object>
</interface>
+1 -21
View File
@@ -279,31 +279,11 @@ drag_prepare_texture (GtkDragSource *source,
GtkWidget *widget)
{
GdkPaintable *paintable = get_image_paintable (GTK_IMAGE (widget));
GtkSnapshot *snapshot;
double width, height;
GskRenderNode *node;
GskRenderer *renderer;
GdkTexture *texture;
GdkContentProvider *ret;
if (!GDK_IS_PAINTABLE (paintable))
return NULL;
snapshot = gtk_snapshot_new ();
width = gdk_paintable_get_intrinsic_width (paintable);
height = gdk_paintable_get_intrinsic_height (paintable);
gdk_paintable_snapshot (paintable, snapshot, width, height);
node = gtk_snapshot_free_to_node (snapshot);
renderer = gtk_native_get_renderer (gtk_widget_get_native (widget));
texture = gsk_renderer_render_texture (renderer, node, &GRAPHENE_RECT_INIT (0, 0, width, height));
ret = gdk_content_provider_new_typed (GDK_TYPE_TEXTURE, texture);
g_object_unref (texture);
gsk_render_node_unref (node);
return ret;
return gdk_content_provider_new_typed (GDK_TYPE_PAINTABLE, paintable);
}
static GdkContentProvider *
-1
View File
@@ -13,7 +13,6 @@
<property name="right-margin">20</property>
<property name="top-margin">20</property>
<property name="bottom-margin">20</property>
<property name="monospace">1</property>
<property name="buffer">
<object class="GtkTextBuffer" id="buffer"/>
</property>
-31
View File
@@ -700,16 +700,6 @@ out:
g_free (source_dir);
}
static void
dark_mode_cb (GtkToggleButton *button,
GParamSpec *pspec,
NodeEditorWindow *self)
{
g_object_set (gtk_widget_get_settings (GTK_WIDGET (self)),
"gtk-application-prefer-dark-theme", gtk_toggle_button_get_active (button),
NULL);
}
static void
node_editor_window_finalize (GObject *object)
{
@@ -824,7 +814,6 @@ node_editor_window_class_init (NodeEditorWindowClass *class)
gtk_widget_class_bind_template_callback (widget_class, export_image_cb);
gtk_widget_class_bind_template_callback (widget_class, testcase_save_clicked_cb);
gtk_widget_class_bind_template_callback (widget_class, testcase_name_entry_changed_cb);
gtk_widget_class_bind_template_callback (widget_class, dark_mode_cb);
}
static GtkWidget *
@@ -921,26 +910,6 @@ node_editor_window_init (NodeEditorWindow *self)
self->text_buffer = gtk_text_buffer_new (self->tag_table);
g_signal_connect (self->text_buffer, "changed", G_CALLBACK (text_changed), self);
gtk_text_view_set_buffer (GTK_TEXT_VIEW (self->text_view), self->text_buffer);
/* Default */
gtk_text_buffer_set_text (self->text_buffer,
"shadow {\n"
" child: texture {\n"
" bounds: 0 0 128 128;\n"
" texture: url(\"resource:///org/gtk/gtk4/node-editor/icons/apps/org.gtk.gtk4.NodeEditor.Devel.svg\");\n"
" }\n"
" shadows: rgba(0,0,0,0.5) 0 1 12;\n"
"}\n"
"\n"
"transform {\n"
" child: text {\n"
" color: rgb(46,52,54);\n"
" font: \"Cantarell Bold 11\";\n"
" glyphs: \"GTK Node Editor\";\n"
" offset: 8 14.418;\n"
" }\n"
" transform: translate(0, 140);\n"
"}", -1);
}
NodeEditorWindow *
-9
View File
@@ -139,15 +139,6 @@
<property name="icon-name">open-menu-symbolic</property>
</object>
</child>
<child type="end">
<object class="GtkToggleButton" id="dark_bg_button">
<property name="valign">center</property>
<property name="has-frame">0</property>
<property name="icon-name">display-brightness-symbolic</property>
<property name="tooltip-text" translatable="yes">Use a dark background</property>
<signal name="notify::active" handler="dark_mode_cb" swapped="0"/>
</object>
</child>
</object>
</child>
<child>
+1
View File
@@ -3298,6 +3298,7 @@ bad things might happen.</property>
</object>
</child>
</object>
<object class="GtkSizeGroup" id="basement-indicators"/>
<menu id="new_style_menu_model">
<section>
<attribute name="display-hint">circular-buttons</attribute>
-3
View File
@@ -146,9 +146,6 @@ images = [
'images/nwse_resize_cursor.png',
'images/zoom_in_cursor.png',
'images/zoom_out_cursor.png',
'images/popup-anchors.png',
'images/popup-flip.png',
'images/popup-slide.png',
]
src_dir = [ gdkinc ]
+8
View File
@@ -23,6 +23,14 @@
<xi:include href="xml/GskGLShader.xml" />
</reference>
<part id="paths">
<title>Paths</title>
<xi:include href="xml/GskPath.xml" />
<xi:include href="xml/GskPathBuilder.xml" />
<xi:include href="xml/GskPathMeasure.xml" />
<xi:include href="xml/GskStroke.xml" />
</part>
<index id="api-index-full">
<title>Index of all symbols</title>
<xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
+116
View File
@@ -182,6 +182,12 @@ gsk_rounded_clip_node_new
gsk_rounded_clip_node_get_child
gsk_rounded_clip_node_get_clip
<SUBSECTION>
GskFillRule
gsk_fill_node_new
gsk_fill_node_get_child
gsk_fill_node_get_path
gsk_fill_node_get_fill_rule
<SUBSECTION>
GskShadow
gsk_shadow_node_new
gsk_shadow_node_get_shadow
@@ -235,6 +241,7 @@ GSK_TYPE_CONTAINER_NODE
GSK_TYPE_CONIC_GRADIENT_NODE
GSK_TYPE_CROSS_FADE_NODE
GSK_TYPE_DEBUG_NODE
GSK_TYPE_FILL_NODE
GSK_TYPE_GL_SHADER_NODE
GSK_TYPE_INSET_SHADOW_NODE
GSK_TYPE_LINEAR_GRADIENT_NODE
@@ -265,6 +272,7 @@ gsk_conic_gradient_node_get_type
gsk_container_node_get_type
gsk_cross_fade_node_get_type
gsk_debug_node_get_type
gsk_fill_node_get_type
gsk_gl_shader_node_get_type
gsk_inset_shadow_node_get_type
gsk_linear_gradient_node_get_type
@@ -306,6 +314,114 @@ gsk_rounded_rect_intersects_rect
GSK_TYPE_CORNER
</SECTION>
<SECTION>
<FILE>GskPath</FILE>
<SUBSECTION>
GskPath
gsk_path_ref
gsk_path_unref
gsk_path_new_rect
gsk_path_new_from_cairo
gsk_path_parse
<SUBSECTION>
gsk_path_print
gsk_path_to_string
gsk_path_to_cairo
<SUBSECTION>
gsk_path_is_empty
gsk_path_get_bounds
gsk_path_get_stroke_bounds
<SUBSECTION>
GskPathOperation
GskPathForeachFlags
GskPathForeachFunc
gsk_path_foreach
<SUBSECTION Private>
GSK_TYPE_PATH
gsk_path_get_type
</SECTION>
<SECTION>
<FILE>GskPathBuilder</FILE>
GskPathBuilder
gsk_path_builder_new
gsk_path_builder_ref
gsk_path_builder_unref
gsk_path_builder_to_path
gsk_path_builder_free_to_path
<SUBSECTION>
gsk_path_builder_get_current_point
<SUBSECTION>
gsk_path_builder_add_rect
gsk_path_builder_add_rounded_rect
gsk_path_builder_add_circle
gsk_path_builder_add_ellipse
gsk_path_builder_add_path
gtk_path_builder_add_segment
<SUBSECTION>
gsk_path_builder_move_to
gsk_path_builder_rel_move_to
gsk_path_builder_line_to
gsk_path_builder_rel_line_to
gsk_path_builder_curve_to
gsk_path_builder_rel_curve_to
gsk_path_builder_conic_to
gsk_path_builder_rel_conic_to
gsk_path_builder_close
<SUBSECTION Private>
GSK_TYPE_PATH_BUILDER
gsk_path_builder_get_type
</SECTION>
<SECTION>
<FILE>GskPathMeasure</FILE>
GskPathMeasure
gsk_path_measure_new
gsk_path_measure_new_with_tolerance
gsk_path_measure_ref
gsk_path_measure_unref
<SUBSECTION>
gsk_path_measure_get_path
gsk_path_measure_get_tolerance
gsk_path_measure_get_n_contours
gsk_path_measure_restrict_to_contour
<SUBSECTION>
gsk_path_measure_get_length
gsk_path_measure_is_closed
gsk_path_measure_get_point
gsk_path_measure_get_closest_point
gsk_path_measure_get_closest_point_full
gsk_path_measure_in_fill
<SUBSECTION Private>
GSK_TYPE_PATH_MEASURE
gsk_path_measure_get_type
</SECTION>
<SECTION>
<FILE>GskStroke</FILE>
GskLineCap
GskLineJoin
gsk_stroke_new
gsk_stroke_copy
gsk_stroke_free
gsk_stroke_equal
gsk_stroke_set_line_width
gsk_stroke_get_line_width
gsk_stroke_set_line_join
gsk_stroke_get_line_join
gsk_stroke_set_line_cap
gsk_stroke_get_line_cap
gsk_stroke_set_miter_limit
gsk_stroke_get_miter_limit
gsk_stroke_set_dash
gsk_stroke_get_dash
gsk_stroke_set_dash_offset
gsk_stroke_get_dash_offset
<SUBSECTION Private>
GSK_TYPE_STROKE
gsk_stroke_get_type
</SECTION>
<SECTION>
<FILE>GskTransform</FILE>
GskTransform
+8 -34
View File
@@ -18,9 +18,9 @@ Length
Percentage
: %, calc()
Angle
: deg, grad, turn, calc()
: deg | grad | turn, calc()
Time
: s, ms, calc()
: s | ms, calc()
Length values with the em or ex units are resolved using the font
size value, unless they occur in setting the font-size itself, in
@@ -32,7 +32,7 @@ not quite the same as the CSS definition of rem.
The calc() notation adds considerable expressive power. There are limits
on what types can be combined in such an expression (e.g. it does not make
sense to add a number and a time). For the full details, see the
[CSS3 Values and Units](https://www.w3.org/TR/css3-values/#calc-notation)
[CSS3 VAlues and Units](https://www.w3.org/TR/css3-values/#calc-notation)
spec.
A common pattern among shorthand properties (called 'four sides') is one
@@ -80,31 +80,6 @@ and in some cases a number as arguments.
## Images
CSS allows to specify images in various forms, the most simple one being
a url for an image file. Beyond that, images can be specified as
`linear-gradient(Angle, ColorStops)`
`repeating-linear-gradient(Angle, ColorStops)`
: creates a linear gradient.
`radial-gradient(Shape, ColorStops)`
`repeating-radial-gradient(Shape, ColorStops)`
: creates a radial gradient.
`conic-gradient(Angle, ColorStops)`
: creates a conic gradient.
`cross-fade(Percentage Image,…)`
: combines two or more images.
`image(Image,… Color)`
: falls back to the first valid image, or to a solid color.
`filter(Image, Filters)`
: applies filters to an image.
For more details, see [CSS Image Level 4](https://www.w3.org/TR/css-images-4/).
GTK extends the CSS syntax for images and also uses it for specifying icons.
To load a themed icon, use
@@ -176,7 +151,7 @@ done with
|caret-color|[CSS Basic User Interface Level 3](https://www.w3.org/TR/css3-ui/#caret-color) | CSS allows an auto value |
|-gtk-secondary-caret-color|[Color](https://www.w3.org/TR/css-color-3/#valuea-def-color) | used for the secondary caret in bidirectional text |
|letter-spacing| [CSS Text Level 3](https://www.w3.org/TR/css3-text/#letter-spacing) | |
|text-decoration-line| [CSS Text Decoration Level 3](https://www.w3.org/TR/css-text-decor-3/#text-decoration-line-property) | |
|text-decoration-line| [CSS Text Decoration Level 3](https://www.w3.org/TR/css-text-decor-3/#text-decoration-line-property) | CSS allows overline |
|text-decoration-color| [CSS Text Decoration Level 3](https://www.w3.org/TR/css-text-decor-3/#text-decoration-color-property) | |
|text-decoration-style| [CSS Text Decoration Level 3](https://www.w3.org/TR/css-text-decor-3/#text-decoration-style-property) | CSS allows dashed and dotted |
|text-shadow| [CSS Text Decoration Level 3](https://www.w3.org/TR/css-text-decor-3/#text-shadow-property) | |
@@ -184,12 +159,11 @@ done with
|-gtk-icon-source| [Image](https://www.w3.org/TR/css-backgrounds-3/#typedef-image), `builtin` or `none` | used for builtin icons in buttons and expanders |
|-gtk-icon-size| [Length](https://www.w3.org/TR/css3-values/#length-value) | size used for builtin icons in buttons and expanders |
|-gtk-icon-style| `requested`, `regular` or `symbolic` | preferred style for application-loaded icons |
|-gtk-icon-transform| [Transform list](https://www.w3.org/TR/css-transforms-1/#typedef-transform-list) or `none` | applied to builtin and application-loaded icons |
|-gtk-icon-transform| [Transform list](https://drafts.csswg.org/css-transforms-1/#typedef-transform-list) or `none` | applied to builtin and application-loaded icons |
|-gtk-icon-palette| Color palette, as explained above | used to recolor symbolic icons |
|-gtk-icon-shadow| [Shadow](https://www.w3.org/TR/css-backgrounds-3/#typedef-shadow) or `none` | applied to builtin and application-loaded icons |
|-gtk-icon-filter| [Filter value list](https://www.w3.org/TR/filter-effects-1/#typedef-filter-value-list) or `none` | applied to builtin and application-loaded icons |
|transform| [CSS Transforms Level 1](https://www.w3.org/TR/css-transforms-1/#transform-property) | |
|transform-origin| [CSS Transforms Level 1](https://www.w3.org/TR/css-transforms-1/#transform-origin-property) | CSS allows specifying a z component|
|transform| [CSS Transforms Level 2](https://drafts.csswg.org/css-transforms-2/) | |
|min-width| [CSS Box Model Level 3](https://www.w3.org/TR/css3-box/#min-width) | CSS allows percentages |
|min-height| [CSS Box Model Level 3](https://www.w3.org/TR/css3-box/#min-height) | CSS allows percentages |
|margin-top| [CSS Box Model Level 3](https://www.w3.org/TR/css3-box/#margin-top) | CSS allows percentages or auto |
@@ -243,7 +217,7 @@ done with
|background-size| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#background-size) | |
|background-position| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#background-position) | |
|background-repeat| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#background-repeat) | |
|background-image| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#background-image) | not supported: urls without quotes |
|background-image| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#background-image) | not supported: urls without quotes, colors in crossfades |
|box-shadow| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#box-shadow) | |
|background-blend-mode| [CSS Compositing and Blending Level 1](https://www.w3.org/TR/compositing-1/#propdef-background-blend-mode) | only affects multiple backgrounds |
|background| [CSS Backgrounds and Borders Level 3](https://www.w3.org/TR/css3-background/#background) | |
@@ -261,4 +235,4 @@ done with
|animation-delay| [CSS Animations Level 1](https://www.w3.org/TR/css3-animations/#animation-delay) | |
|animation-fill-mode| [CSS Animations Level 1](https://www.w3.org/TR/css3-animations/#animation-fill-mode) | |
|animation| [CSS Animations Level 1](https://www.w3.org/TR/css3-animations/#animation) | |
|border-spacing| [CSS Table Level 3](https://www.w3.org/TR/css-tables-3/#border-spacing-property) | respected by GtkBoxLayout, GtkGridLayout, GtkCenterLayout |
|border-spacing| [CSS Table Level 3](https://www.w3.org/TR/css-tables-3/#border-spacing-property) | respected by GtkBox and GtkGrid |
+9 -8
View File
@@ -343,19 +343,20 @@ Many widgets, like buttons, do all their drawing themselves. You just tell
them the label you want to see, and they figure out what font to use, draw
the button outline and focus rectangle, etc. Sometimes, it is necessary to
do some custom drawing. In that case, a GtkDrawingArea might be the right
widget to use. It offers a canvas on which you can draw by setting its
draw function.
widget to use. It offers a canvas on which you can draw by connecting to
the ::draw signal.
The contents of a widget often need to be partially or fully redrawn,
e.g. when another window is moved and uncovers part of the widget, or
when the window containing it is resized. It is also possible to explicitly
cause a widget to be redrawn, by calling gtk_widget_queue_draw(). GTK takes
care of most of the details by providing a ready-to-use cairo context to the
draw function.
cause part or all of the widget to be redrawn, by calling
gtk_widget_queue_draw() or its variants. GTK takes care of most of the
details by providing a ready-to-use cairo context to the ::draw signal
handler.
The following example shows how to use a draw function with GtkDrawingArea.
It is a bit more complicated than the previous examples, since it also
demonstrates input event handling with event controllers.
The following example shows a ::draw signal handler. It is a bit more
complicated than the previous examples, since it also demonstrates
input event handling by means of event controllers.
![Drawing](drawing.png)
+25 -31
View File
@@ -41,41 +41,35 @@
<command>gtk4-builder-tool</command> can perform various operations
on GtkBuilder .ui files.
</para>
<para>
The <option>validate</option> command validates the .ui file and reports
errors to stderr.
</para>
<para>
The <option>enumerate</option> command lists all the named objects that
are created in the .ui file.
</para>
<para>
The <option>preview</option> command displays the .ui file. This command
accepts options to specify the ID of the toplevel object and a .css file
to use.
</para>
<para>
The <option>simplify</option> command simplifies the .ui file by removing
properties that are set to their default values and writes the resulting XML
to stdout, or back to the input file.
</para>
<para>
When the <option>--3to4</option> is specified, <option>simplify</option>
interprets the input as a GTK 3 ui file and attempts to convert it to GTK 4
equivalents. It performs various conversions, such as renaming properties,
translating child properties to layout properties, rewriting the setup for
GtkNotebook, GtkStack, GtkAssistant or changing toolbars into boxes.
</para>
<para>
You should always test the modified .ui files produced by gtk4-builder-tool
before using them in production.
</para>
<para>
Note in particular that the conversion
done with <option>--3to4</option> is meant as a starting point for a port
from GTK 3 to GTK 4. It is expected that you will have to do manual fixups
after the initial conversion.
</para>
</refsect1>
<refsect1><title>Commands</title>
<para>The following commands are understood:</para>
<variablelist>
<varlistentry>
<term><option>validate</option></term>
<listitem><para>Validates the .ui file and report errors to stderr.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>simplify</option></term>
<listitem><para>Simplifies the .ui file by removing properties that
are set to their default values and write the resulting XML to stdout,
or back to the input file.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>enumerate</option></term>
<listitem><para>Lists all the named objects that are created in the .ui file.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>preview</option></term>
<listitem><para>Preview the .ui file. This command accepts options
to specify the ID of an object and a .css file to use.</para></listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1><title>Simplify Options</title>
@@ -61,13 +61,6 @@
<listitem><para>Write png files to <replaceable>DIRECTORY</replaceable>
instead of the current working directory.</para></listitem>
</varlistentry>
<varlistentry>
<term>--debug</term>
<listitem><para>Generate png files of the various channels during
the conversion. If these files are not monochrome green, they
are often helpful in pinpointing the problematic parts of
the source svg.</para></listitem>
</varlistentry>
</variablelist>
</refsect1>
+4
View File
@@ -1831,6 +1831,7 @@ GtkIMContextSimple
gtk_im_context_simple_new
gtk_im_context_simple_add_table
gtk_im_context_simple_add_compose_file
GTK_MAX_COMPOSE_LEN
<SUBSECTION Standard>
GTK_IM_CONTEXT_SIMPLE
GTK_IS_IM_CONTEXT_SIMPLE
@@ -4294,6 +4295,8 @@ gtk_snapshot_push_color_matrix
gtk_snapshot_push_repeat
gtk_snapshot_push_clip
gtk_snapshot_push_rounded_clip
gtk_snapshot_push_fill
gtk_snapshot_push_stroke
gtk_snapshot_push_cross_fade
gtk_snapshot_push_blend
gtk_snapshot_push_blur
@@ -4667,6 +4670,7 @@ GTK_WINDOW_GET_CLASS
<SUBSECTION Private>
GtkWindowPrivate
gtk_window_get_type
GtkWindowGeometryInfo
gtk_window_remove_embedded_xid
gtk_window_add_embedded_xid
GtkWindowKeysForeachFunc

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Before

Width:  |  Height:  |  Size: 126 KiB

After

Width:  |  Height:  |  Size: 126 KiB

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 45 KiB

+4
View File
@@ -413,6 +413,10 @@ images = [
'images/password-entry.png',
'images/picture.png',
'images/popover.png',
'images/popup-anchors.png',
'images/popup-at.svg',
'images/popup-flip.png',
'images/popup-slide.png',
'images/printdialog.png',
'images/progressbar.png',
'images/right-center.png',
+16 -132
View File
@@ -165,11 +165,11 @@ for this change.
| ::key-release-event | #GtkEventControllerKey |
| ::enter-notify-event | #GtkEventControllerMotion |
| ::leave-notify-event | #GtkEventControllerMotion |
| ::configure-event | - |
| ::configure-event | replaced by #GdkSurface::layout |
| ::focus-in-event | #GtkEventControllerFocus |
| ::focus-out-event | #GtkEventControllerFocus |
| ::map-event | - |
| ::unmap-event | - |
| ::map-event | replaced by #GdkSurface:mapped |
| ::unmap-event | replaced by #GdkSurface:mapped |
| ::property-notify-event | replaced by #GdkClipboard |
| ::selection-clear-event | replaced by #GdkClipboard |
| ::selection-request-event | replaced by #GdkClipboard |
@@ -178,17 +178,10 @@ for this change.
| ::proximity-in-event | #GtkGestureStylus |
| ::proximity-out-event | #GtkGestureStylus |
| ::visibility-notify-event | - |
| ::window-state-event | - |
| ::window-state-event | replaced by #GdkToplevel:state |
| ::damage-event | - |
| ::grab-broken-event | - |
Event signals which are not directly related to input have to be dealt
with on a one-by-one basis. If you were using ::configure-event and
::window-state-event to save window state, you should use property
notification for corresponding GtkWindow properties, such as
#GtkWindow:default-width, #GtkWindow:default-height, #GtkWindow:maximized
or #GtkWindow:fullscreened.
### Set a proper application ID
In GTK 4 we want the application's #GApplication 'application-id'
@@ -271,6 +264,14 @@ therefore can no longer be used to break reference cycles. A typical sign
of a reference cycle involving a toplevel window is when closing the window
does not make the application quit.
A good rule to follow is: If you set a widget pointer with
gtk_widget_class_bind_template_child() in class_init(), you need to
unparent it in dispose(). The slight complication here is that you need
to respect the widget hierarchy while doing so. Ie if you set both `field1`
and `field2`, but `field1` is an ancestor of `field2`, then you only need
to unparent `field1` — doing so will remove the the entire subtree below
`field1`, including `field2`.
### Stop using GdkScreen
The GdkScreen object has been removed in GTK 4. Most of its APIs already
@@ -406,7 +407,7 @@ and gdk_keymap_get_entries_for_keyval().
GTK 3 has the idea that use of modifiers may differ between different
platforms, and has a #GdkModifierIntent api to let platforms provide
hint about how modifiers are expected to be used. It also promoted
the use of `<Primary>` instead of `<Control>` to specify accelerators that
the use of <Primary> instead of <Control> to specify accelerators that
adapt to platform conventions.
In GTK 4, the meaning of modifiers has been fixed, and backends are
@@ -425,88 +426,13 @@ GDK_CONTROL_MASK|GDK_ALT_MASK
: Prevent text input
Consequently, #GdkModifierIntent and related APIs have been removed,
and `<Control>` is preferred over `<Primary>` in accelerators.
and <Control> is preferred over <Primary> in accelerators.
A related change is that GTK 4 no longer supports the use of archaic
X11 'real' modifiers with the names Mod1,..., Mod5, and %GDK_MOD1_MASK
has been renamed to %GDK_ALT_MASK.
### Replace GtkClipboard with GdkClipboard
The `GtkClipboard` API has been removed, and replaced by #GdkClipboard.
There is not direct 1:1 mapping between the old an the new API, so it cannot
be a mechanical replacement; the new API is based on object types and #GValue
like object properties, instead of opaque identifiers, so it should be easier
to use.
For instance, the example below copies the contents of an entry into the
clipboard:
```
static void
copy_text (GtkWidget *widget)
{
GtkEditable *editable = GTK_EDITABLE (widget);
// Initialize a GValue with the contents of the widget
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_STRING);
g_value_set_string (&value, gtk_editable_get_text (editable));
// Store the value in the clipboard object
GdkClipboard *clipboard = gtk_widget_get_clipboard (widget);
gdk_clipboard_set_value (clipboard, &value);
g_value_unset (&value);
}
```
whereas the example below pastes the contents into the entry:
```
static void
paste_text (GtkWidget *widget)
{
GtkEditable *editable = GTK_EDITABLE (widget);
// Initialize a GValue to receive text
GValue value = G_VALUE_INIT;
g_value_init (&value, G_TYPE_STRING);
// Get the content provider for the clipboard, and ask it for text
GdkClipboard *clipboard = gtk_widget_get_clipboard (widget);
GdkContentProvider *provider = gdk_clipboard_get_content (clipboard);
// If the content provider does not contain text, we are not interested
if (!gdk_content_provider_get_value (provider, &value, NULL))
return;
const char *str = g_value_get_string (&value);
gtk_editable_set_text (editable, str);
g_value_unset (&value);
}
```
The convenience API for specific target types in `GtkClipboard` has been
replaced by their corresponding GType:
| GtkClipboard | GType |
| ----------------------------------- | ---------------------- |
| `gtk_clipboard_request_text()` | `G_TYPE_STRING` |
| `gtk_clipboard_request_rich_text()` | `GTK_TYPE_TEXT_BUFFER` |
| `gtk_clipboard_request_image()` | `GDK_TYPE_PIXBUF` |
| `gtk_clipboard_request_uris()` |` GDK_TYPE_FILE_LIST` |
**Note**: Support for rich text serialization across different processes
for #GtkTextBuffer is not available any more.
If you are copying the contents of an image, it is recommended to use
GDK_TYPE_PAINTABLE instead of GDK_TYPE_PIXBUF, to minimize the amount of
potential copies.
### Stop using `gtk_get_current_...` APIs
### Stop using gtk_get_current_... APIs
The function gtk_get_current_event() and its variants have been
replaced by equivalent event controller APIs:
@@ -522,25 +448,6 @@ option. You should always review the resulting changes.
The <requires> tag now supports for the 'lib' attribute the
'gtk' value only, instead of the 'gtk+' one previously.
### Adapt to GtkBuilder API changes
gtk_builder_connect_signals() no longer exists. Instead, signals are
always connected automatically. If you need to add user data to your
signals, gtk_builder_set_current_object() must be called. An important
caveat is that you have to do this before loading any XML. This means if
you need to use gtk_builder_set_current_object(), you can no longer use
gtk_builder_new_from_file(), gtk_builder_new_from_resource(), or
gtk_builder_new_from_string(). Instead, you must use vanilla gtk_builder_new(),
then call gtk_builder_set_current_object(), then load the XML using
gtk_builder_add_from_file(), gtk_builder_add_from_resource(), or
gtk_builder_add_from_string(). You must check the return value for
failure and manually abort with g_error() if something went wrong.
You only have to worry about this if you were previously using
gtk_builder_connect_signals(). If you are using templates, then
gtk_widget_init_template() will call gtk_builder_set_current_object()
for you, so templates work like before.
### Adapt to event controller API changes
A few changes to the event controller and #GtkGesture APIs
@@ -551,23 +458,6 @@ Another is that #GtkGestureMultiPress has been renamed to #GtkGestureClick,
and has lost its area property. A #GtkEventControllerFocus has been
split off from #GtkEventcontrollerKey.
In GTK 3, #GtkEventController:widget was a construct-only property, so
a #GtkWidget was provided whenever constructing a #GtkEventController.
In GTK 4, #GtkEventController:widget is now read-only. Use
gtk_widget_add_controller() to add an event controller to a widget.
In GTK 3, widgets did not own their event controllers, and event
controllers did not own their widgets, so developers were responsible
for manually keeping event controllers alive for the lifetime of their
associated widgets. In GTK 4, widgets own their event controllers.
gtk_widget_add_controller() takes ownership of the event controller, so
there is no longer any need to store a reference to the event controller
after it has been added to a widget.
Although not normally needed, an event controller could be removed from
a widget in GTK 3 by destroying the event controller with g_object_unref().
In GTK 4, you must use gtk_widget_remove_controller().
### Focus handling changes
The semantics of the #GtkWidget:can-focus property have changed.
@@ -861,8 +751,7 @@ to get it.
The ::size-allocate signal has been removed, since it is easy
to misuse. If you need to learn about sizing changes of custom
drawing widgets, use the #GtkDrawingArea::resize or #GtkGLArea::resize
signals. If you want to track the size of toplevel windows, use
property notification for #GtkWindow:default-width and #GtkWindow:default-height.
signals.
### Switch to GtkWidget's children APIs
@@ -1079,11 +968,6 @@ that wants to override the default handling, you can provide an
implementation of the default.activate action in your widgets' action
groups.
### Stop using gtk_widget_grab_default()
The function gtk_widget_grab_default() has been removed. If you need
to mark a widget as default, use gtk_window_set_default_widget() directly.
### Stop setting ::has-default and ::has-focus in .ui files
The special handling for the ::has-default and ::has-focus properties
+293
View File
@@ -0,0 +1,293 @@
#include <gtk/gtk.h>
#include "exampleapp.h"
#include "exampleappwin.h"
struct _ExampleAppWindow
{
GtkApplicationWindow parent;
GSettings *settings;
GtkWidget *stack;
GtkWidget *search;
GtkWidget *searchbar;
GtkWidget *searchentry;
GtkWidget *gears;
GtkWidget *sidebar;
GtkWidget *words;
GtkWidget *lines;
GtkWidget *lines_label;
};
G_DEFINE_TYPE (ExampleAppWindow, example_app_window, GTK_TYPE_APPLICATION_WINDOW)
static void
search_text_changed (GtkEntry *entry,
ExampleAppWindow *win)
{
const char *text;
GtkWidget *tab;
GtkWidget *view;
GtkTextBuffer *buffer;
GtkTextIter start, match_start, match_end;
text = gtk_editable_get_text (GTK_EDITABLE (entry));
if (text[0] == '\0')
return;
tab = gtk_stack_get_visible_child (GTK_STACK (win->stack));
view = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (tab));
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
/* Very simple-minded search implementation */
gtk_text_buffer_get_start_iter (buffer, &start);
if (gtk_text_iter_forward_search (&start, text, GTK_TEXT_SEARCH_CASE_INSENSITIVE,
&match_start, &match_end, NULL))
{
gtk_text_buffer_select_range (buffer, &match_start, &match_end);
gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (view), &match_start,
0.0, FALSE, 0.0, 0.0);
}
}
static void
find_word (GtkButton *button,
ExampleAppWindow *win)
{
const char *word;
word = gtk_button_get_label (button);
gtk_editable_set_text (GTK_EDITABLE (win->searchentry), word);
}
static void
update_words (ExampleAppWindow *win)
{
GHashTable *strings;
GHashTableIter iter;
GtkWidget *tab, *view, *row;
GtkTextBuffer *buffer;
GtkTextIter start, end;
char *word, *key;
GtkWidget *child;
tab = gtk_stack_get_visible_child (GTK_STACK (win->stack));
if (tab == NULL)
return;
view = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (tab));
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
strings = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
gtk_text_buffer_get_start_iter (buffer, &start);
while (!gtk_text_iter_is_end (&start))
{
while (!gtk_text_iter_starts_word (&start))
{
if (!gtk_text_iter_forward_char (&start))
goto done;
}
end = start;
if (!gtk_text_iter_forward_word_end (&end))
goto done;
word = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
g_hash_table_add (strings, g_utf8_strdown (word, -1));
g_free (word);
start = end;
}
done:
while ((child = gtk_widget_get_first_child (win->words)))
gtk_list_box_remove (GTK_LIST_BOX (win->words), child);
g_hash_table_iter_init (&iter, strings);
while (g_hash_table_iter_next (&iter, (gpointer *)&key, NULL))
{
row = gtk_button_new_with_label (key);
g_signal_connect (row, "clicked",
G_CALLBACK (find_word), win);
gtk_box_append (GTK_BOX (win->words), row);
}
g_hash_table_unref (strings);
}
static void
update_lines (ExampleAppWindow *win)
{
GtkWidget *tab, *view;
GtkTextBuffer *buffer;
int count;
char *lines;
tab = gtk_stack_get_visible_child (GTK_STACK (win->stack));
if (tab == NULL)
return;
view = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (tab));
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
count = gtk_text_buffer_get_line_count (buffer);
lines = g_strdup_printf ("%d", count);
gtk_label_set_text (GTK_LABEL (win->lines), lines);
g_free (lines);
}
static void
visible_child_changed (GObject *stack,
GParamSpec *pspec,
ExampleAppWindow *win)
{
if (gtk_widget_in_destruction (GTK_WIDGET (stack)))
return;
gtk_search_bar_set_search_mode (GTK_SEARCH_BAR (win->searchbar), FALSE);
update_words (win);
update_lines (win);
}
static void
words_changed (GObject *sidebar,
GParamSpec *pspec,
ExampleAppWindow *win)
{
update_words (win);
}
static void
example_app_window_init (ExampleAppWindow *win)
{
GtkBuilder *builder;
GMenuModel *menu;
GAction *action;
gtk_widget_init_template (GTK_WIDGET (win));
win->settings = g_settings_new ("org.gtk.exampleapp");
g_settings_bind (win->settings, "transition",
win->stack, "transition-type",
G_SETTINGS_BIND_DEFAULT);
g_settings_bind (win->settings, "show-words",
win->sidebar, "reveal-child",
G_SETTINGS_BIND_DEFAULT);
g_object_bind_property (win->search, "active",
win->searchbar, "search-mode-enabled",
G_BINDING_BIDIRECTIONAL);
g_signal_connect (win->sidebar, "notify::reveal-child",
G_CALLBACK (words_changed), win);
builder = gtk_builder_new_from_resource ("/org/gtk/exampleapp/gears-menu.ui");
menu = G_MENU_MODEL (gtk_builder_get_object (builder, "menu"));
gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (win->gears), menu);
g_object_unref (builder);
action = g_settings_create_action (win->settings, "show-words");
g_action_map_add_action (G_ACTION_MAP (win), action);
g_object_unref (action);
action = (GAction*) g_property_action_new ("show-lines", win->lines, "visible");
g_action_map_add_action (G_ACTION_MAP (win), action);
g_object_unref (action);
g_object_bind_property (win->lines, "visible",
win->lines_label, "visible",
G_BINDING_DEFAULT);
g_object_set (gtk_settings_get_default (), "gtk-shell-shows-app-menu", FALSE, NULL);
gtk_application_window_set_show_menubar (GTK_APPLICATION_WINDOW (win), TRUE);
}
static void
example_app_window_dispose (GObject *object)
{
ExampleAppWindow *win;
win = EXAMPLE_APP_WINDOW (object);
g_clear_object (&win->settings);
G_OBJECT_CLASS (example_app_window_parent_class)->dispose (object);
}
static void
example_app_window_class_init (ExampleAppWindowClass *class)
{
G_OBJECT_CLASS (class)->dispose = example_app_window_dispose;
gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class),
"/org/gtk/exampleapp/window.ui");
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), ExampleAppWindow, stack);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), ExampleAppWindow, search);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), ExampleAppWindow, searchbar);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), ExampleAppWindow, searchentry);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), ExampleAppWindow, gears);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), ExampleAppWindow, words);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), ExampleAppWindow, sidebar);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), ExampleAppWindow, lines);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), ExampleAppWindow, lines_label);
gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), search_text_changed);
gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), visible_child_changed);
}
ExampleAppWindow *
example_app_window_new (ExampleApp *app)
{
return g_object_new (EXAMPLE_APP_WINDOW_TYPE, "application", app, NULL);
}
void
example_app_window_open (ExampleAppWindow *win,
GFile *file)
{
char *basename;
GtkWidget *scrolled, *view;
char *contents;
gsize length;
GtkTextBuffer *buffer;
GtkTextTag *tag;
GtkTextIter start_iter, end_iter;
basename = g_file_get_basename (file);
scrolled = gtk_scrolled_window_new ();
gtk_widget_set_hexpand (scrolled, TRUE);
gtk_widget_set_vexpand (scrolled, TRUE);
view = gtk_text_view_new ();
gtk_text_view_set_editable (GTK_TEXT_VIEW (view), FALSE);
gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view), FALSE);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scrolled), view);
gtk_stack_add_titled (GTK_STACK (win->stack), scrolled, basename, basename);
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
if (g_file_load_contents (file, NULL, &contents, &length, NULL, NULL))
{
gtk_text_buffer_set_text (buffer, contents, length);
g_free (contents);
}
tag = gtk_text_buffer_create_tag (buffer, NULL, NULL);
g_settings_bind (win->settings, "font",
tag, "font",
G_SETTINGS_BIND_DEFAULT);
gtk_text_buffer_get_start_iter (buffer, &start_iter);
gtk_text_buffer_get_end_iter (buffer, &end_iter);
gtk_text_buffer_apply_tag (buffer, tag, &start_iter, &end_iter);
g_free (basename);
gtk_widget_set_sensitive (win->search, TRUE);
update_words (win);
update_lines (win);
}
+1 -1
View File
@@ -173,7 +173,7 @@ stash_desktop_startup_notification_id (void)
if (!g_utf8_validate (desktop_startup_id, -1, NULL))
g_warning ("DESKTOP_STARTUP_ID contains invalid UTF-8");
else
startup_notification_id = g_strdup (desktop_startup_id);
startup_notification_id = g_strdup (desktop_startup_id ? desktop_startup_id : "");
}
/* Clear the environment variable so it won't be inherited by
+11 -1
View File
@@ -611,7 +611,17 @@ _gdk_device_set_associated_device (GdkDevice *device,
g_return_if_fail (GDK_IS_DEVICE (device));
g_return_if_fail (associated == NULL || GDK_IS_DEVICE (associated));
g_set_object (&device->associated, associated);
if (device->associated == associated)
return;
if (device->associated)
{
g_object_unref (device->associated);
device->associated = NULL;
}
if (associated)
device->associated = g_object_ref (associated);
}
/*
+1 -1
View File
@@ -280,7 +280,7 @@ gdk_draw_context_get_surface (GdkDrawContext *context)
* implementation must use gdk_draw_context_get_frame_region() to query the
* region that must be drawn.
*
* When using GTK, the widget system automatically places calls to
* When using GTK+, the widget system automatically places calls to
* gdk_draw_context_begin_frame() and gdk_draw_context_end_frame() via the
* use of #GskRenderers, so application code does not need to call these
* functions explicitly.
+1 -1
View File
@@ -12,7 +12,7 @@
GType
@enum_name@_get_type (void)
{
static gsize g_define_type_id__volatile = 0;
static volatile gsize g_define_type_id__volatile = 0;
if (g_once_init_enter (&g_define_type_id__volatile))
{
+3 -3
View File
@@ -210,7 +210,7 @@ gdk_event_init (GdkEvent *self)
GType
gdk_event_get_type (void)
{
static gsize event_type__volatile;
static volatile gsize event_type__volatile;
if (g_once_init_enter (&event_type__volatile))
{
@@ -374,7 +374,7 @@ static GType gdk_event_types[GDK_EVENT_LAST];
GType \
type_name ## _get_type (void) \
{ \
static gsize gdk_define_event_type_id__volatile; \
static volatile gsize gdk_define_event_type_id__volatile; \
if (g_once_init_enter (&gdk_define_event_type_id__volatile)) \
{ \
GType gdk_define_event_type_id = \
@@ -452,7 +452,7 @@ gdk_event_init_types_once (void)
void
gdk_event_init_types (void)
{
static gsize event_types__volatile;
static volatile gsize event_types__volatile;
if (g_once_init_enter (&event_types__volatile))
{
-3
View File
@@ -107,9 +107,6 @@ make_program (GdkGLContextProgram *program,
glLinkProgram (program->program);
glDetachShader (program->program, vertex_shader);
glDetachShader (program->program, fragment_shader);
glDeleteShader (vertex_shader);
glDeleteShader (fragment_shader);
-31
View File
@@ -30,37 +30,6 @@
* Popups are positioned relative to their parent surface.
* The GdkPopupLayout struct contains information that is
* necessary to do so.
*
* The positioning requires a negotiation with the windowing system,
* since it depends on external constraints, such as the position of
* the parent surface, and the screen dimensions.
*
* The basic ingredients are a rectangle on the parent surface,
* and the anchor on both that rectangle and the popup. The anchors
* specify a side or corner to place next to each other.
*
* ![Popup anchors](popup-anchors.png)
*
* For cases where placing the anchors next to each other would make
* the popup extend offscreen, the layout includes some hints for how
* to resolve this problem. The hints may suggest to flip the anchor
* position to the other side, or to 'slide' the popup along a side,
* or to resize it.
*
* ![Flipping popups](popup-flip.png)
*
* ![Sliding popups](popup-slide.png)
*
* These hints may be combined.
*
* Ultimatively, it is up to the windowing system to determine the position
* and size of the popup. You can learn about the result by calling
* gdk_popup_get_position_x(), gdk_popup_get_position_y(),
* gdk_popup_get_rect_anchor() and gdk_popup_get_surface_anchor() after the
* popup has been presented. This can be used to adjust the rendering. For
* example, GtkPopover changes its arrow position accordingly. But you have
* to be careful avoid changing the size of the popover, or it has to be
* presented again.
*/
struct _GdkPopupLayout
+4 -5
View File
@@ -436,10 +436,9 @@ gdk_seat_tool_removed (GdkSeat *seat,
}
GdkDeviceTool *
gdk_seat_get_tool (GdkSeat *seat,
guint64 serial,
guint64 hw_id,
GdkDeviceToolType type)
gdk_seat_get_tool (GdkSeat *seat,
guint64 serial,
guint64 hw_id)
{
GdkDeviceTool *match = NULL;
GList *tools, *l;
@@ -450,7 +449,7 @@ gdk_seat_get_tool (GdkSeat *seat,
{
GdkDeviceTool *tool = l->data;
if (tool->serial == serial && tool->hw_id == hw_id && tool->type == type)
if (tool->serial == serial && tool->hw_id == hw_id)
{
match = tool;
break;
+8 -4
View File
@@ -49,7 +49,7 @@ struct _GdkSeatDefaultPrivate
G_DEFINE_TYPE_WITH_PRIVATE (GdkSeatDefault, gdk_seat_default, GDK_TYPE_SEAT)
static void
gdk_seat_default_dispose (GObject *object)
gdk_seat_dispose (GObject *object)
{
GdkSeatDefault *seat = GDK_SEAT_DEFAULT (object);
GdkSeatDefaultPrivate *priv = gdk_seat_default_get_instance_private (seat);
@@ -79,7 +79,11 @@ gdk_seat_default_dispose (GObject *object)
g_object_unref (l->data);
}
g_clear_pointer (&priv->tools, g_ptr_array_unref);
if (priv->tools)
{
g_ptr_array_unref (priv->tools);
priv->tools = NULL;
}
g_list_free (priv->physical_pointers);
g_list_free (priv->physical_keyboards);
@@ -303,7 +307,7 @@ gdk_seat_default_class_init (GdkSeatDefaultClass *klass)
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GdkSeatClass *seat_class = GDK_SEAT_CLASS (klass);
object_class->dispose = gdk_seat_default_dispose;
object_class->dispose = gdk_seat_dispose;
seat_class->get_capabilities = gdk_seat_default_get_capabilities;
@@ -438,7 +442,7 @@ gdk_seat_default_remove_tool (GdkSeatDefault *seat,
priv = gdk_seat_default_get_instance_private (seat);
if (tool != gdk_seat_get_tool (GDK_SEAT (seat), tool->serial, tool->hw_id, tool->type))
if (tool != gdk_seat_get_tool (GDK_SEAT (seat), tool->serial, tool->hw_id))
return;
g_signal_emit_by_name (seat, "tool-removed", tool);
+3 -4
View File
@@ -75,10 +75,9 @@ void gdk_seat_tool_removed (GdkSeat *seat,
GdkDeviceTool *tool);
GdkDeviceTool *
gdk_seat_get_tool (GdkSeat *seat,
guint64 serial,
guint64 hw_id,
GdkDeviceToolType type);
gdk_seat_get_tool (GdkSeat *seat,
guint64 serial,
guint64 hw_id);
GdkGrabStatus gdk_seat_grab (GdkSeat *seat,
GdkSurface *surface,
+6 -29
View File
@@ -92,7 +92,6 @@ enum {
PROP_MAPPED,
PROP_WIDTH,
PROP_HEIGHT,
PROP_SCALE_FACTOR,
LAST_PROP
};
@@ -552,13 +551,6 @@ gdk_surface_class_init (GdkSurfaceClass *klass)
0, G_MAXINT, 0,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
properties[PROP_SCALE_FACTOR] =
g_param_spec_int ("scale-factor",
P_("Scale factor"),
P_("Scale factor"),
1, G_MAXINT, 1,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, LAST_PROP, properties);
/**
@@ -790,10 +782,6 @@ gdk_surface_get_property (GObject *object,
g_value_set_int (value, surface->height);
break;
case PROP_SCALE_FACTOR:
g_value_set_int (value, gdk_surface_get_scale_factor (surface));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -2557,7 +2545,8 @@ gdk_surface_get_frame_clock (GdkSurface *surface)
* value can be used to determine whether to use a pixel resource
* with higher resolution data.
*
* The scale of a surface may change during runtime.
* The scale of a surface may change during runtime, if this happens
* a configure event will be sent to the toplevel surface.
*
* Returns: the scale factor
*/
@@ -2960,8 +2949,8 @@ gdk_surface_request_motion (GdkSurface *surface)
* gdk_surface_translate_coordinates:
* @from: the origin surface
* @to: the target surface
* @x: (inout): coordinates to translate
* @y: (inout): coordinates to translate
* @x: coordinates to translate
* @y: coordinates to translate
*
* Translates the given coordinates from being
* relative to the @from surface to being relative
@@ -2980,18 +2969,9 @@ gdk_surface_translate_coordinates (GdkSurface *from,
double *x,
double *y)
{
double in_x, in_y, out_x, out_y;
int x1, y1, x2, y2;
GdkSurface *f, *t;
g_return_val_if_fail (GDK_IS_SURFACE (from), FALSE);
g_return_val_if_fail (GDK_IS_SURFACE (to), FALSE);
g_return_val_if_fail (x != NULL, FALSE);
g_return_val_if_fail (y != NULL, FALSE);
in_x = *x;
in_y = *y;
x1 = 0;
y1 = 0;
f = from;
@@ -3015,11 +2995,8 @@ gdk_surface_translate_coordinates (GdkSurface *from,
if (f != t)
return FALSE;
out_x = in_x + (x1 - x2);
out_y = in_y + (y1 - y2);
*x = out_x;
*y = out_y;
*x += x1 - x2;
*y += y1 - y2;
return TRUE;
}
+6 -6
View File
@@ -316,9 +316,9 @@ gdk_texture_new_for_pixbuf (GdkPixbuf *pixbuf)
* @resource_path: the path of the resource file
*
* Creates a new texture by loading an image from a resource.
* The file format is detected automatically.
* The supported formats are PNG and JPEG, though more formats might be
* available.
* The file format is detected automatically, and can be any
* format that is supported by the gdk-pixbuf library, such as
* JPEG or PNG.
*
* It is a fatal error if @resource_path does not specify a valid
* image resource and the program will abort if that happens.
@@ -352,9 +352,9 @@ gdk_texture_new_from_resource (const char *resource_path)
* @error: Return location for an error
*
* Creates a new texture by loading an image from a file.
* The file format is detected automatically.
* The supported formats are PNG and JPEG, though more formats might be
* available.
* The file format is detected automatically, and can be any
* format that is supported by the gdk-pixbuf library, such as
* JPEG or PNG.
*
* If %NULL is returned, then @error will be set.
*
+17
View File
@@ -149,6 +149,12 @@ gdk_toplevel_size_validate (GdkToplevelSize *size)
{
int geometry_width, geometry_height;
if (size->min_width > size->bounds_width ||
size->min_height > size->bounds_height)
g_warning ("GdkToplevelSize: min_size (%d, %d) exceeds bounds (%d, %d)",
size->min_width, size->min_height,
size->bounds_width, size->bounds_height);
geometry_width = size->width;
geometry_height = size->height;
if (size->shadow.is_valid)
@@ -156,4 +162,15 @@ gdk_toplevel_size_validate (GdkToplevelSize *size)
geometry_width -= size->shadow.left + size->shadow.right;
geometry_height -= size->shadow.top + size->shadow.bottom;
}
if (geometry_width > size->bounds_width ||
geometry_height > size->bounds_height)
g_warning ("GdkToplevelSize: geometry size (%d, %d) exceeds bounds (%d, %d)",
size->width, size->height,
size->bounds_width, size->bounds_height);
if (size->min_width > size->width ||
size->min_height > size->height)
g_warning ("GdkToplevelSize: min_size (%d, %d) exceeds size (%d, %d)",
size->min_width, size->min_height,
size->width, size->height);
}
-23
View File
@@ -78,14 +78,6 @@
*/
#define GDK_VERSION_4_0 (G_ENCODE_VERSION (4, 0))
/**
* GDK_VERSION_4_2:
*
* A macro that evaluates to the 4.2 version of GDK, in a format
* that can be used by the C pre-processor.
*/
#define GDK_VERSION_4_2 (G_ENCODE_VERSION (4, 2))
/* evaluates to the current stable version; for development cycles,
* this means the next stable target, with a hard backstop to the
@@ -183,19 +175,4 @@
# define GDK_DEPRECATED_IN_4_0_FOR(f) _GDK_EXTERN
#endif
#if GDK_VERSION_MAX_ALLOWED < GDK_VERSION_4_2
# define GDK_AVAILABLE_IN_4_2 GDK_UNAVAILABLE(4, 2)
#else
# define GDK_AVAILABLE_IN_4_2 _GDK_EXTERN
#endif
#if GDK_VERSION_MIN_REQUIRED >= GDK_VERSION_4_2
# define GDK_DEPRECATED_IN_4_2 GDK_DEPRECATED
# define GDK_DEPRECATED_IN_4_2_FOR(f) GDK_DEPRECATED_FOR(f)
#else
# define GDK_DEPRECATED_IN_4_2 _GDK_EXTERN
# define GDK_DEPRECATED_IN_4_2_FOR(f) _GDK_EXTERN
#endif
#endif /* __GDK_VERSION_MACROS_H__ */
+1 -1
View File
@@ -225,7 +225,7 @@ gdk_vulkan_strerror (VkResult result)
case VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT:
return "A requested pipeline creation would have required compilation, but the application requested compilation to not be performed.";
#endif
#if VK_HEADER_VERSION < 140
#if VK_HEADER_VERSION < 142
case VK_RESULT_RANGE_SIZE:
#endif
case VK_RESULT_MAX_ENUM:
+2 -6
View File
@@ -38,10 +38,6 @@
#include "gdkmonitorprivate.h"
#include "gdksurfaceprivate.h"
#ifndef AVAILABLE_MAC_OS_X_VERSION_10_15_AND_LATER
typedef NSString *CALayerContentsGravity;
#endif
@implementation GdkMacosWindow
-(BOOL)windowShouldClose:(id)sender
@@ -470,7 +466,7 @@ typedef NSString *CALayerContentsGravity;
inTrackManualResize = YES;
mouse_location = convert_nspoint_to_screen (self, [self mouseLocationOutsideOfEventStream]);
mouse_location = [self convertPointToScreen:[self mouseLocationOutsideOfEventStream]];
mdx = initialResizeLocation.x - mouse_location.x;
mdy = initialResizeLocation.y - mouse_location.y;
@@ -592,7 +588,7 @@ typedef NSString *CALayerContentsGravity;
}
initialResizeFrame = [self frame];
initialResizeLocation = convert_nspoint_to_screen (self, [self mouseLocationOutsideOfEventStream]);
initialResizeLocation = [self convertPointToScreen:[self mouseLocationOutsideOfEventStream]];
}
-(NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
-4
View File
@@ -27,10 +27,6 @@
G_BEGIN_DECLS
#ifndef AVAILABLE_MAC_OS_X_VERSION_10_13_AND_LATER
typedef NSString *NSPasteboardType;
#endif
#define GDK_TYPE_MACOS_CLIPBOARD (_gdk_macos_clipboard_get_type())
G_DECLARE_FINAL_TYPE (GdkMacosClipboard, _gdk_macos_clipboard, GDK, MACOS_CLIPBOARD, GdkClipboard)
+16 -64
View File
@@ -40,56 +40,8 @@ typedef struct
guint done : 1;
} WriteRequest;
enum {
TYPE_STRING,
TYPE_PBOARD,
TYPE_URL,
TYPE_FILE_URL,
TYPE_COLOR,
TYPE_TIFF,
TYPE_PNG,
TYPE_LAST
};
#define PTYPE(k) (get_pasteboard_type(TYPE_##k))
static NSPasteboardType pasteboard_types[TYPE_LAST];
G_DEFINE_TYPE (GdkMacosClipboard, _gdk_macos_clipboard, GDK_TYPE_CLIPBOARD)
static NSPasteboardType
get_pasteboard_type (int type)
{
static gsize initialized = FALSE;
g_assert (type >= 0);
g_assert (type < TYPE_LAST);
if (g_once_init_enter (&initialized))
{
pasteboard_types[TYPE_PNG] = NSPasteboardTypePNG;
pasteboard_types[TYPE_STRING] = NSPasteboardTypeString;
pasteboard_types[TYPE_TIFF] = NSPasteboardTypeTIFF;
pasteboard_types[TYPE_COLOR] = NSPasteboardTypeColor;
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
pasteboard_types[TYPE_PBOARD] = NSStringPboardType;
G_GNUC_END_IGNORE_DEPRECATIONS
#ifdef AVAILABLE_MAC_OS_X_VERSION_10_13_AND_LATER
pasteboard_types[TYPE_URL] = NSPasteboardTypeURL;
pasteboard_types[TYPE_FILE_URL] = NSPasteboardTypeFileURL;
#else
pasteboard_types[TYPE_URL] = [[NSString alloc] initWithUTF8String:"public.url"];
pasteboard_types[TYPE_FILE_URL] = [[NSString alloc] initWithUTF8String:"public.file-url"];
#endif
g_once_init_leave (&initialized, TRUE);
}
return pasteboard_types[type];
}
static void
write_request_free (WriteRequest *wr)
{
@@ -104,17 +56,17 @@ _gdk_macos_clipboard_from_ns_type (NSPasteboardType type)
{
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
if ([type isEqualToString:PTYPE(STRING)] ||
[type isEqualToString:PTYPE(PBOARD)])
if ([type isEqualToString:NSPasteboardTypeString] ||
[type isEqualToString:NSStringPboardType])
return g_intern_string ("text/plain;charset=utf-8");
else if ([type isEqualToString:PTYPE(URL)] ||
[type isEqualToString:PTYPE(FILE_URL)])
else if ([type isEqualToString:NSPasteboardTypeURL] ||
[type isEqualToString:NSPasteboardTypeFileURL])
return g_intern_string ("text/uri-list");
else if ([type isEqualToString:PTYPE(COLOR)])
else if ([type isEqualToString:NSPasteboardTypeColor])
return g_intern_string ("application/x-color");
else if ([type isEqualToString:PTYPE(TIFF)])
else if ([type isEqualToString:NSPasteboardTypeTIFF])
return g_intern_string ("image/tiff");
else if ([type isEqualToString:PTYPE(PNG)])
else if ([type isEqualToString:NSPasteboardTypePNG])
return g_intern_string ("image/png");
G_GNUC_END_IGNORE_DEPRECATIONS;
@@ -131,25 +83,25 @@ _gdk_macos_clipboard_to_ns_type (const char *mime_type,
if (g_strcmp0 (mime_type, "text/plain;charset=utf-8") == 0)
{
return PTYPE(STRING);
return NSPasteboardTypeString;
}
else if (g_strcmp0 (mime_type, "text/uri-list") == 0)
{
if (alternate)
*alternate = PTYPE(URL);
return PTYPE(FILE_URL);
*alternate = NSPasteboardTypeURL;
return NSPasteboardTypeFileURL;
}
else if (g_strcmp0 (mime_type, "application/x-color") == 0)
{
return PTYPE(COLOR);
return NSPasteboardTypeColor;
}
else if (g_strcmp0 (mime_type, "image/tiff") == 0)
{
return PTYPE(TIFF);
return NSPasteboardTypeTIFF;
}
else if (g_strcmp0 (mime_type, "image/png") == 0)
{
return PTYPE(PNG);
return NSPasteboardTypePNG;
}
return nil;
@@ -268,7 +220,7 @@ _gdk_macos_clipboard_read_async (GdkClipboard *clipboard,
{
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
if ([[self->pasteboard types] containsObject:PTYPE(FILE_URL)])
if ([[self->pasteboard types] containsObject:NSPasteboardTypeFileURL])
{
GString *str = g_string_new (NULL);
NSArray *files = [self->pasteboard propertyListForType:NSFilenamesPboardType];
@@ -315,12 +267,12 @@ _gdk_macos_clipboard_read_async (GdkClipboard *clipboard,
}
else if (strcmp (mime_type, "image/tiff") == 0)
{
NSData *data = [self->pasteboard dataForType:PTYPE(TIFF)];
NSData *data = [self->pasteboard dataForType:NSPasteboardTypeTIFF];
stream = create_stream_from_nsdata (data);
}
else if (strcmp (mime_type, "image/png") == 0)
{
NSData *data = [self->pasteboard dataForType:PTYPE(PNG)];
NSData *data = [self->pasteboard dataForType:NSPasteboardTypePNG];
stream = create_stream_from_nsdata (data);
}
+11 -47
View File
@@ -28,7 +28,6 @@
#include "gdkmacosdisplay-private.h"
#include "gdkmacoskeymap-private.h"
#include "gdkmacossurface-private.h"
#include "gdkmacosseat-private.h"
#define GDK_MOD2_MASK (1 << 4)
#define GRIP_WIDTH 15
@@ -206,9 +205,6 @@ fill_button_event (GdkMacosDisplay *display,
GdkSeat *seat;
GdkEventType type;
GdkModifierType state;
GdkDevice *pointer = NULL;
GdkDeviceTool *tool = NULL;
double *axes = NULL;
g_assert (GDK_IS_MACOS_DISPLAY (display));
g_assert (GDK_IS_MACOS_SURFACE (surface));
@@ -245,22 +241,16 @@ fill_button_event (GdkMacosDisplay *display,
y < 0 || y > GDK_SURFACE (surface)->height))
return NULL;
if (([nsevent subtype] == NSEventSubtypeTabletPoint) &&
_gdk_macos_seat_get_tablet (GDK_MACOS_SEAT (seat), &pointer, &tool))
axes = _gdk_macos_seat_get_tablet_axes_from_nsevent (GDK_MACOS_SEAT (seat), nsevent);
else
pointer = gdk_seat_get_pointer (seat);
return gdk_button_event_new (type,
GDK_SURFACE (surface),
pointer,
tool,
gdk_seat_get_pointer (seat),
NULL,
get_time_from_ns_event (nsevent),
state,
get_mouse_button_from_ns_event (nsevent),
x,
y,
axes);
NULL);
}
static GdkEvent *
@@ -567,9 +557,6 @@ fill_motion_event (GdkMacosDisplay *display,
{
GdkSeat *seat;
GdkModifierType state;
GdkDevice *pointer = NULL;
GdkDeviceTool *tool = NULL;
double *axes = NULL;
g_assert (GDK_IS_MACOS_SURFACE (surface));
g_assert (nsevent != NULL);
@@ -579,20 +566,14 @@ fill_motion_event (GdkMacosDisplay *display,
state = get_keyboard_modifiers_from_ns_event (nsevent) |
_gdk_macos_display_get_current_mouse_modifiers (display);
if (([nsevent subtype] == NSEventSubtypeTabletPoint) &&
_gdk_macos_seat_get_tablet (GDK_MACOS_SEAT (seat), &pointer, &tool))
axes = _gdk_macos_seat_get_tablet_axes_from_nsevent (GDK_MACOS_SEAT (seat), nsevent);
else
pointer = gdk_seat_get_pointer (seat);
return gdk_motion_event_new (GDK_SURFACE (surface),
pointer,
tool,
gdk_seat_get_pointer (seat),
NULL,
get_time_from_ns_event (nsevent),
state,
x,
y,
axes);
NULL);
}
static GdkEvent *
@@ -719,7 +700,7 @@ get_surface_point_from_screen_point (GdkSurface *surface,
NSPoint point;
nswindow = _gdk_macos_surface_get_native (GDK_MACOS_SURFACE (surface));
point = convert_nspoint_from_screen (nswindow, screen_point);
point = [nswindow convertPointFromScreen:screen_point];
*x = point.x;
*y = surface->height - point.y;
@@ -781,7 +762,7 @@ get_surface_from_ns_event (GdkMacosDisplay *self,
GdkSurface *surface = NULL;
NSWindow *nswindow = [nsevent window];
if (GDK_IS_MACOS_WINDOW (nswindow))
if (nswindow)
{
GdkMacosBaseView *view;
NSPoint point, view_point;
@@ -840,7 +821,7 @@ get_surface_from_ns_event (GdkMacosDisplay *self,
}
else
{
*screen_point = convert_nspoint_to_screen ([nsevent window], point);
*screen_point = [(GdkMacosWindow *)[nsevent window] convertPointToScreen:point];
*x = point.x;
*y = surface->height - point.y;
}
@@ -993,11 +974,11 @@ find_surface_for_ns_event (GdkMacosDisplay *self,
g_assert (x != NULL);
g_assert (y != NULL);
view = (GdkMacosBaseView *)[[nsevent window] contentView];
if (!(surface = get_surface_from_ns_event (self, nsevent, &point, x, y)))
return NULL;
view = (GdkMacosBaseView *)[GDK_MACOS_SURFACE (surface)->window contentView];
_gdk_macos_display_from_display_coords (self, point.x, point.y, &x_tmp, &y_tmp);
switch ((int)[nsevent type])
@@ -1070,23 +1051,6 @@ _gdk_macos_display_translate (GdkMacosDisplay *self,
return NULL;
}
/* We need to register the proximity event from any point on the screen
* to properly register the devices
* FIXME: is there a better way to detect if a tablet has been plugged?
*/
if (event_type == NSEventTypeTabletProximity)
{
GdkSeat *seat = gdk_display_get_default_seat (GDK_DISPLAY (self));
_gdk_macos_seat_handle_tablet_tool_event (GDK_MACOS_SEAT (seat), nsevent);
/* FIXME: we might want to cache this proximity event and propagate it
* but proximity events in gdk work at a window level while on macos
* works at a screen level. For now we just skip them.
*/
return NULL;
}
if (!(surface = find_surface_for_ns_event (self, nsevent, &x, &y)))
return NULL;
-17
View File
@@ -547,20 +547,6 @@ _gdk_macos_display_surface_resigned_key (GdkMacosDisplay *self,
_gdk_macos_display_clear_sorting (self);
}
/* Raises a transient window.
*/
static void
raise_transient (GdkMacosSurface *surface)
{
GdkMacosSurface *parent_surface = GDK_MACOS_SURFACE (GDK_SURFACE (surface)->transient_for);
NSWindow *parent = _gdk_macos_surface_get_native (parent_surface);
NSWindow *window = _gdk_macos_surface_get_native (surface);
[parent removeChildWindow:window];
[parent addChildWindow:window ordered:NSWindowAbove];
}
void
_gdk_macos_display_surface_became_main (GdkMacosDisplay *self,
GdkMacosSurface *surface)
@@ -573,9 +559,6 @@ _gdk_macos_display_surface_became_main (GdkMacosDisplay *self,
g_queue_push_head_link (&self->main_surfaces, &surface->main);
if (GDK_SURFACE (surface)->transient_for)
raise_transient (surface);
_gdk_macos_display_clear_sorting (self);
}
-4
View File
@@ -145,7 +145,6 @@ GetSubpixelLayout (CGDirectDisplayID screen_id)
static char *
GetLocalizedName (NSScreen *screen)
{
#ifdef AVAILABLE_MAC_OS_X_VERSION_10_15_AND_LATER
GDK_BEGIN_MACOS_ALLOC_POOL;
NSString *str;
@@ -159,9 +158,6 @@ GetLocalizedName (NSScreen *screen)
GDK_END_MACOS_ALLOC_POOL;
return g_steal_pointer (&name);
#else
return NULL;
#endif
}
static char *
-11
View File
@@ -23,7 +23,6 @@
#include <AppKit/AppKit.h>
#include "gdkmacosdisplay.h"
#include "gdkmacosseat.h"
#include "gdkseatprivate.h"
@@ -31,16 +30,6 @@ G_BEGIN_DECLS
GdkSeat *_gdk_macos_seat_new (GdkMacosDisplay *display);
void _gdk_macos_seat_handle_tablet_tool_event (GdkMacosSeat *seat,
NSEvent *nsevent);
gboolean _gdk_macos_seat_get_tablet (GdkMacosSeat *seat,
GdkDevice **device,
GdkDeviceTool **tool);
double *_gdk_macos_seat_get_tablet_axes_from_nsevent (GdkMacosSeat *seat,
NSEvent *nsevent);
G_END_DECLS
#endif /* __GDK_MACOS_SEAT_PRIVATE_H__ */
+21 -585
View File
@@ -1,6 +1,5 @@
/*
* Copyright © 2020 Red Hat, Inc.
* Copyright © 2021 Amazon.com, Inc. and its affiliates. All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -23,605 +22,42 @@
#include <gdk/gdk.h>
#include "gdkdeviceprivate.h"
#include "gdkdevicetoolprivate.h"
#include "gdkseatdefaultprivate.h"
#include "gdkmacosdevice.h"
#include "gdkmacosseat-private.h"
typedef struct
{
NSUInteger device_id;
char *name;
GdkDevice *logical_device;
GdkDevice *stylus_device;
GdkSeat *seat;
GdkDeviceTool *current_tool;
int axis_indices[GDK_AXIS_LAST];
double axes[GDK_AXIS_LAST];
} GdkMacosTabletData;
struct _GdkMacosSeat
{
GdkSeat parent_instance;
GdkMacosDisplay *display;
GdkDevice *logical_pointer;
GdkDevice *logical_keyboard;
GdkMacosTabletData *current_tablet;
GPtrArray *tablets;
GPtrArray *tools;
};
struct _GdkMacosSeatClass
{
GdkSeatClass parent_class;
};
G_DEFINE_TYPE (GdkMacosSeat, gdk_macos_seat, GDK_TYPE_SEAT)
#define KEYBOARD_EVENTS (GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | \
GDK_FOCUS_CHANGE_MASK)
#define TOUCH_EVENTS (GDK_TOUCH_MASK)
#define POINTER_EVENTS (GDK_POINTER_MOTION_MASK | \
GDK_BUTTON_PRESS_MASK | \
GDK_BUTTON_RELEASE_MASK | \
GDK_SCROLL_MASK | GDK_SMOOTH_SCROLL_MASK | \
GDK_ENTER_NOTIFY_MASK | \
GDK_LEAVE_NOTIFY_MASK | \
GDK_PROXIMITY_IN_MASK | \
GDK_PROXIMITY_OUT_MASK)
static void
gdk_macos_tablet_data_free (gpointer user_data)
{
GdkMacosTabletData *tablet = user_data;
gdk_seat_device_removed (GDK_SEAT (tablet->seat), tablet->stylus_device);
gdk_seat_device_removed (GDK_SEAT (tablet->seat), tablet->logical_device);
_gdk_device_set_associated_device (tablet->logical_device, NULL);
_gdk_device_set_associated_device (tablet->stylus_device, NULL);
g_object_unref (tablet->logical_device);
g_object_unref (tablet->stylus_device);
g_free (tablet->name);
g_free (tablet);
}
static void
gdk_macos_seat_dispose (GObject *object)
{
GdkMacosSeat *self = GDK_MACOS_SEAT (object);
if (self->logical_pointer)
{
gdk_seat_device_removed (GDK_SEAT (self), self->logical_pointer);
g_clear_object (&self->logical_pointer);
}
if (self->logical_keyboard)
{
gdk_seat_device_removed (GDK_SEAT (self), self->logical_keyboard);
g_clear_object (&self->logical_pointer);
}
g_clear_pointer (&self->tablets, g_ptr_array_unref);
g_clear_pointer (&self->tools, g_ptr_array_unref);
G_OBJECT_CLASS (gdk_macos_seat_parent_class)->dispose (object);
}
static GdkSeatCapabilities
gdk_macos_seat_get_capabilities (GdkSeat *seat)
{
GdkMacosSeat *self = GDK_MACOS_SEAT (seat);
GdkSeatCapabilities caps = 0;
if (self->logical_pointer)
caps |= GDK_SEAT_CAPABILITY_POINTER;
if (self->logical_keyboard)
caps |= GDK_SEAT_CAPABILITY_KEYBOARD;
return caps;
}
static GdkGrabStatus
gdk_macos_seat_grab (GdkSeat *seat,
GdkSurface *surface,
GdkSeatCapabilities capabilities,
gboolean owner_events,
GdkCursor *cursor,
GdkEvent *event,
GdkSeatGrabPrepareFunc prepare_func,
gpointer prepare_func_data)
{
GdkMacosSeat *self = GDK_MACOS_SEAT (seat);
guint32 evtime = event ? gdk_event_get_time (event) : GDK_CURRENT_TIME;
GdkGrabStatus status = GDK_GRAB_SUCCESS;
gboolean was_visible;
was_visible = gdk_surface_get_mapped (surface);
if (prepare_func)
(prepare_func) (seat, surface, prepare_func_data);
if (!gdk_surface_get_mapped (surface))
{
g_critical ("Surface %p has not been mapped in GdkSeatGrabPrepareFunc",
surface);
return GDK_GRAB_NOT_VIEWABLE;
}
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
if (capabilities & GDK_SEAT_CAPABILITY_ALL_POINTING)
{
/* ALL_POINTING spans 3 capabilities; get the mask for the ones we have */
GdkEventMask pointer_evmask = 0;
/* We let tablet styli take over the pointer cursor */
if (capabilities & (GDK_SEAT_CAPABILITY_POINTER |
GDK_SEAT_CAPABILITY_TABLET_STYLUS))
{
pointer_evmask |= POINTER_EVENTS;
}
if (capabilities & GDK_SEAT_CAPABILITY_TOUCH)
pointer_evmask |= TOUCH_EVENTS;
status = gdk_device_grab (self->logical_pointer, surface,
owner_events,
pointer_evmask, cursor,
evtime);
}
if (status == GDK_GRAB_SUCCESS &&
capabilities & GDK_SEAT_CAPABILITY_KEYBOARD)
{
status = gdk_device_grab (self->logical_keyboard, surface,
owner_events,
KEYBOARD_EVENTS, cursor,
evtime);
if (status != GDK_GRAB_SUCCESS)
{
if (capabilities & ~GDK_SEAT_CAPABILITY_KEYBOARD)
gdk_device_ungrab (self->logical_pointer, evtime);
}
}
if (status != GDK_GRAB_SUCCESS && !was_visible)
gdk_surface_hide (surface);
G_GNUC_END_IGNORE_DEPRECATIONS;
return status;
}
static void
gdk_macos_seat_ungrab (GdkSeat *seat)
{
GdkMacosSeat *self = GDK_MACOS_SEAT (seat);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
gdk_device_ungrab (self->logical_pointer, GDK_CURRENT_TIME);
gdk_device_ungrab (self->logical_keyboard, GDK_CURRENT_TIME);
G_GNUC_END_IGNORE_DEPRECATIONS;
}
static GdkDevice *
gdk_macos_seat_get_logical_device (GdkSeat *seat,
GdkSeatCapabilities capability)
{
GdkMacosSeat *self = GDK_MACOS_SEAT (seat);
/* There must be only one flag set */
switch ((guint) capability)
{
case GDK_SEAT_CAPABILITY_POINTER:
case GDK_SEAT_CAPABILITY_TOUCH:
return self->logical_pointer;
case GDK_SEAT_CAPABILITY_KEYBOARD:
return self->logical_keyboard;
default:
g_warning ("Unhandled capability %x", capability);
break;
}
return NULL;
}
static GList *
gdk_macos_seat_get_devices (GdkSeat *seat,
GdkSeatCapabilities capabilities)
{
GdkMacosSeat *self = GDK_MACOS_SEAT (seat);
GList *physical_devices = NULL;
if (self->logical_pointer && (capabilities & GDK_SEAT_CAPABILITY_POINTER))
physical_devices = g_list_prepend (physical_devices, self->logical_pointer);
if (self->logical_keyboard && (capabilities & GDK_SEAT_CAPABILITY_KEYBOARD))
physical_devices = g_list_prepend (physical_devices, self->logical_keyboard);
if (capabilities & GDK_SEAT_CAPABILITY_TABLET_STYLUS)
{
for (guint i = 0; i < self->tablets->len; i++)
{
GdkMacosTabletData *tablet = g_ptr_array_index (self->tablets, i);
physical_devices = g_list_prepend (physical_devices, tablet->stylus_device);
}
}
return physical_devices;
}
static GList *
gdk_macos_seat_get_tools (GdkSeat *seat)
{
GdkMacosSeat *self = GDK_MACOS_SEAT (seat);
GdkDeviceTool *tool;
GList *tools = NULL;
for (guint i = 0; i < self->tools->len; i++)
{
tool = g_ptr_array_index (self->tools, i);
tools = g_list_prepend (tools, tool);
}
return tools;
}
static void
gdk_macos_seat_class_init (GdkMacosSeatClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GdkSeatClass *seat_class = GDK_SEAT_CLASS (klass);
object_class->dispose = gdk_macos_seat_dispose;
seat_class->get_capabilities = gdk_macos_seat_get_capabilities;
seat_class->grab = gdk_macos_seat_grab;
seat_class->ungrab = gdk_macos_seat_ungrab;
seat_class->get_logical_device = gdk_macos_seat_get_logical_device;
seat_class->get_devices = gdk_macos_seat_get_devices;
seat_class->get_tools = gdk_macos_seat_get_tools;
}
static void
gdk_macos_seat_init (GdkMacosSeat *self)
{
self->tablets = g_ptr_array_new_with_free_func (gdk_macos_tablet_data_free);
self->tools = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
}
static void
init_devices (GdkMacosSeat *self)
{
/* pointer */
self->logical_pointer = g_object_new (GDK_TYPE_MACOS_DEVICE,
"name", "Core Pointer",
"source", GDK_SOURCE_MOUSE,
"has-cursor", TRUE,
"display", self->display,
"seat", self,
NULL);
/* keyboard */
self->logical_keyboard = g_object_new (GDK_TYPE_MACOS_DEVICE,
"name", "Core Keyboard",
"source", GDK_SOURCE_KEYBOARD,
"has-cursor", FALSE,
"display", self->display,
"seat", self,
NULL);
/* link both */
_gdk_device_set_associated_device (self->logical_pointer, self->logical_keyboard);
_gdk_device_set_associated_device (self->logical_keyboard, self->logical_pointer);
gdk_seat_device_added (GDK_SEAT (self), self->logical_pointer);
gdk_seat_device_added (GDK_SEAT (self), self->logical_keyboard);
}
GdkSeat *
_gdk_macos_seat_new (GdkMacosDisplay *display)
{
GdkMacosSeat *self;
GdkDevice *core_keyboard;
GdkDevice *core_pointer;
GdkSeat *seat;
g_return_val_if_fail (GDK_IS_MACOS_DISPLAY (display), NULL);
self = g_object_new (GDK_TYPE_MACOS_SEAT,
"display", display,
NULL);
self->display = display;
init_devices (self);
return g_steal_pointer (&self);
}
static GdkDeviceToolType
get_device_tool_type_from_nsevent (NSEvent *nsevent)
{
GdkDeviceToolType tool_type;
switch ([nsevent pointingDeviceType])
{
case NSPointingDeviceTypePen:
tool_type = GDK_DEVICE_TOOL_TYPE_PEN;
break;
case NSPointingDeviceTypeEraser:
tool_type = GDK_DEVICE_TOOL_TYPE_ERASER;
break;
case NSPointingDeviceTypeCursor:
tool_type = GDK_DEVICE_TOOL_TYPE_MOUSE;
break;
case NSPointingDeviceTypeUnknown:
default:
tool_type = GDK_DEVICE_TOOL_TYPE_UNKNOWN;
}
return tool_type;
}
static GdkAxisFlags
get_device_tool_axes_from_nsevent (NSEvent *nsevent)
{
/* TODO: do we need to be smarter about the capabilities? */
return GDK_AXIS_FLAG_XTILT | GDK_AXIS_FLAG_YTILT | GDK_AXIS_FLAG_PRESSURE |
GDK_AXIS_FLAG_ROTATION;
}
static GdkMacosTabletData *
create_tablet_data_from_nsevent (GdkMacosSeat *self,
NSEvent *nsevent)
{
GdkMacosTabletData *tablet;
GdkDisplay *display = gdk_seat_get_display (GDK_SEAT (self));
GdkDevice *logical_device, *stylus_device;
char *logical_name;
char *vid, *pid;
tablet = g_new0 (GdkMacosTabletData, 1);
tablet->seat = GDK_SEAT (self);
tablet->device_id = [nsevent deviceID];
/* FIXME: find a better name */
tablet->name = g_strdup_printf ("Tablet %lu", [nsevent deviceID]);
vid = g_strdup_printf ("%.4lx", [nsevent vendorID]);
pid = g_strdup_printf ("%.4lx", [nsevent tabletID]);
logical_name = g_strdup_printf ("Logical pointer for %s", tablet->name);
logical_device = g_object_new (GDK_TYPE_MACOS_DEVICE,
"name", logical_name,
"source", GDK_SOURCE_MOUSE,
"has-cursor", TRUE,
"display", display,
"seat", self,
NULL);
stylus_device = g_object_new (GDK_TYPE_MACOS_DEVICE,
"name", tablet->name,
"source", GDK_SOURCE_PEN,
core_pointer = g_object_new (GDK_TYPE_MACOS_DEVICE,
"name", "Core Pointer",
"source", GDK_SOURCE_MOUSE,
"has-cursor", TRUE,
"display", display,
NULL);
core_keyboard = g_object_new (GDK_TYPE_MACOS_DEVICE,
"name", "Core Keyboard",
"source", GDK_SOURCE_KEYBOARD,
"has-cursor", FALSE,
"display", display,
"seat", self,
"vendor-id", vid,
"product-id", pid,
NULL);
tablet->logical_device = logical_device;
tablet->stylus_device = stylus_device;
_gdk_device_set_associated_device (GDK_DEVICE (core_pointer),
GDK_DEVICE (core_keyboard));
_gdk_device_set_associated_device (GDK_DEVICE (core_keyboard),
GDK_DEVICE (core_pointer));
_gdk_device_set_associated_device (logical_device, self->logical_keyboard);
_gdk_device_set_associated_device (stylus_device, logical_device);
seat = gdk_seat_default_new_for_logical_pair (core_pointer, core_keyboard);
gdk_seat_device_added (GDK_SEAT (self), logical_device);
gdk_seat_device_added (GDK_SEAT (self), stylus_device);
g_object_unref (core_pointer);
g_object_unref (core_keyboard);
g_free (logical_name);
g_free (vid);
g_free (pid);
return tablet;
}
static GdkMacosTabletData *
get_tablet_data_from_nsevent (GdkMacosSeat *self,
NSEvent *nsevent)
{
GdkMacosTabletData *tablet = NULL;
for (guint i = 0; i < self->tablets->len; i++)
{
GdkMacosTabletData *t = g_ptr_array_index (self->tablets, i);
if (t->device_id == [nsevent deviceID])
{
tablet = t;
break;
}
}
if (!tablet)
tablet = create_tablet_data_from_nsevent (self, nsevent);
return tablet;
}
static void
device_tablet_clone_tool_axes (GdkMacosTabletData *tablet,
GdkDeviceTool *tool)
{
int axis_pos;
g_object_freeze_notify (G_OBJECT (tablet->stylus_device));
_gdk_device_reset_axes (tablet->stylus_device);
_gdk_device_add_axis (tablet->stylus_device, GDK_AXIS_X, 0, 0, 0);
_gdk_device_add_axis (tablet->stylus_device, GDK_AXIS_Y, 0, 0, 0);
if (tool->tool_axes & (GDK_AXIS_FLAG_XTILT | GDK_AXIS_FLAG_YTILT))
{
axis_pos = _gdk_device_add_axis (tablet->stylus_device,
GDK_AXIS_XTILT, -1.0, 1.0, 0);
tablet->axis_indices[GDK_AXIS_XTILT] = axis_pos;
axis_pos = _gdk_device_add_axis (tablet->stylus_device,
GDK_AXIS_YTILT, -1.0, 1.0, 0);
tablet->axis_indices[GDK_AXIS_YTILT] = axis_pos;
}
if (tool->tool_axes & GDK_AXIS_FLAG_PRESSURE)
{
axis_pos = _gdk_device_add_axis (tablet->stylus_device,
GDK_AXIS_PRESSURE, 0.0, 1.0, 0);
tablet->axis_indices[GDK_AXIS_PRESSURE] = axis_pos;
}
if (tool->tool_axes & GDK_AXIS_FLAG_ROTATION)
{
axis_pos = _gdk_device_add_axis (tablet->stylus_device,
GDK_AXIS_ROTATION, 0.0, 1.0, 0);
tablet->axis_indices[GDK_AXIS_ROTATION] = axis_pos;
}
g_object_thaw_notify (G_OBJECT (tablet->stylus_device));
}
static void
mimic_device_axes (GdkDevice *logical,
GdkDevice *physical)
{
double axis_min, axis_max, axis_resolution;
GdkAxisUse axis_use;
int axis_count;
g_object_freeze_notify (G_OBJECT (logical));
_gdk_device_reset_axes (logical);
axis_count = gdk_device_get_n_axes (physical);
for (int i = 0; i < axis_count; i++)
{
_gdk_device_get_axis_info (physical, i, &axis_use, &axis_min,
&axis_max, &axis_resolution);
_gdk_device_add_axis (logical, axis_use, axis_min,
axis_max, axis_resolution);
}
g_object_thaw_notify (G_OBJECT (logical));
}
void
_gdk_macos_seat_handle_tablet_tool_event (GdkMacosSeat *seat,
NSEvent *nsevent)
{
GdkDeviceToolType tool_type;
GdkMacosTabletData *tablet;
GdkDeviceTool *tool;
g_return_if_fail (GDK_IS_MACOS_SEAT (seat));
g_return_if_fail (nsevent != NULL);
tablet = get_tablet_data_from_nsevent (seat, nsevent);
tool_type = get_device_tool_type_from_nsevent (nsevent);
if (tool_type == GDK_DEVICE_TOOL_TYPE_UNKNOWN)
{
g_warning ("Unknown device tool detected");
return;
}
tool = gdk_seat_get_tool (GDK_SEAT (seat), [nsevent tabletID], [nsevent deviceID], tool_type);
if ([nsevent isEnteringProximity])
{
if (!tool)
{
tool = gdk_device_tool_new ([nsevent tabletID], [nsevent vendorID], tool_type,
get_device_tool_axes_from_nsevent (nsevent));
g_ptr_array_add (seat->tools, tool);
}
gdk_device_update_tool (tablet->stylus_device, tool);
tablet->current_tool = tool;
device_tablet_clone_tool_axes (tablet, tool);
mimic_device_axes (tablet->logical_device, tablet->stylus_device);
seat->current_tablet = tablet;
}
else
{
gdk_device_update_tool (tablet->stylus_device, NULL);
tablet->current_tool = NULL;
seat->current_tablet = NULL;
}
}
gboolean
_gdk_macos_seat_get_tablet (GdkMacosSeat *seat,
GdkDevice **logical_device,
GdkDeviceTool **tool)
{
g_return_val_if_fail (GDK_IS_MACOS_SEAT (seat), FALSE);
if (!seat->current_tablet)
return FALSE;
*logical_device = seat->current_tablet->logical_device;
*tool = seat->current_tablet->current_tool;
return TRUE;
}
double *
_gdk_macos_seat_get_tablet_axes_from_nsevent (GdkMacosSeat *seat,
NSEvent *nsevent)
{
GdkMacosTabletData *tablet;
int axis_index;
g_return_val_if_fail (GDK_IS_MACOS_SEAT (seat), NULL);
g_return_val_if_fail (nsevent != NULL, NULL);
tablet = seat->current_tablet;
if (!tablet || !tablet->current_tool)
return NULL;
if (tablet->current_tool->tool_axes & (GDK_AXIS_FLAG_XTILT | GDK_AXIS_FLAG_YTILT))
{
axis_index = tablet->axis_indices[GDK_AXIS_XTILT];
_gdk_device_translate_axis (tablet->stylus_device, axis_index,
[nsevent tilt].x, &tablet->axes[GDK_AXIS_XTILT]);
axis_index = tablet->axis_indices[GDK_AXIS_YTILT];
_gdk_device_translate_axis (tablet->stylus_device, axis_index,
[nsevent tilt].y, &tablet->axes[GDK_AXIS_YTILT]);
}
if (tablet->current_tool->tool_axes & GDK_AXIS_FLAG_PRESSURE)
{
axis_index = tablet->axis_indices[GDK_AXIS_PRESSURE];
_gdk_device_translate_axis (tablet->stylus_device, axis_index,
[nsevent pressure], &tablet->axes[GDK_AXIS_PRESSURE]);
}
if (tablet->current_tool->tool_axes & GDK_AXIS_FLAG_ROTATION)
{
axis_index = tablet->axis_indices[GDK_AXIS_ROTATION];
_gdk_device_translate_axis (tablet->stylus_device, axis_index,
[nsevent rotation], &tablet->axes[GDK_AXIS_ROTATION]);
}
return g_memdup (tablet->axes,
sizeof (double) * GDK_AXIS_LAST);
return g_steal_pointer (&seat);
}
-40
View File
@@ -1,40 +0,0 @@
/*
* Copyright © 2021 Amazon.com, Inc. and its affiliates. All Rights Reserved.
*
* 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.1 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/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#if !defined (__GDKMACOS_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gdk/macos/gdkmacos.h> can be included directly."
#endif
#include <gdk/gdk.h>
G_BEGIN_DECLS
#define GDK_TYPE_MACOS_SEAT (gdk_macos_seat_get_type ())
#define GDK_MACOS_SEAT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_MACOS_SEAT, GdkMacosSeat))
#define GDK_IS_MACOS_SEAT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_MACOS_SEAT))
typedef struct _GdkMacosSeat GdkMacosSeat;
typedef struct _GdkMacosSeatClass GdkMacosSeatClass;
GDK_AVAILABLE_IN_ALL
GType gdk_macos_seat_get_type (void) G_GNUC_CONST;
G_END_DECLS
-1
View File
@@ -61,7 +61,6 @@ struct _GdkMacosSurface
gint64 pending_frame_counter;
guint did_initial_present : 1;
guint geometry_dirty : 1;
};
struct _GdkMacosSurfaceClass
-6
View File
@@ -150,12 +150,6 @@ _gdk_macos_surface_set_shadow (GdkMacosSurface *surface,
g_assert (GDK_IS_MACOS_SURFACE (self));
if (self->shadow_top == top &&
self->shadow_right == right &&
self->shadow_bottom == bottom &&
self->shadow_left == left)
return;
self->shadow_top = top;
self->shadow_right = right;
self->shadow_bottom = bottom;
+10 -90
View File
@@ -81,7 +81,7 @@ _gdk_macos_toplevel_surface_unmaximize (GdkMacosToplevelSurface *self)
[window zoom:window];
}
static void
static gboolean
_gdk_macos_toplevel_surface_present (GdkToplevel *toplevel,
GdkToplevelLayout *layout)
{
@@ -107,7 +107,6 @@ _gdk_macos_toplevel_surface_present (GdkToplevel *toplevel,
style_mask = [nswindow styleMask];
monitor = gdk_display_get_monitor_at_surface (display, surface);
if (monitor)
{
GdkRectangle workarea;
@@ -159,11 +158,13 @@ _gdk_macos_toplevel_surface_present (GdkToplevel *toplevel,
[nswindow setStyleMask:style_mask];
if (size.shadow.is_valid)
_gdk_macos_surface_set_shadow (GDK_MACOS_SURFACE (surface),
size.shadow.top,
size.shadow.right,
size.shadow.bottom,
size.shadow.left);
{
_gdk_macos_surface_set_shadow (GDK_MACOS_SURFACE (surface),
size.shadow.top,
size.shadow.right,
size.shadow.bottom,
size.shadow.left);
}
_gdk_macos_surface_set_geometry_hints (GDK_MACOS_SURFACE (self), &geometry, mask);
gdk_surface_constrain_size (&geometry, mask, width, height, &width, &height);
@@ -229,6 +230,8 @@ _gdk_macos_toplevel_surface_present (GdkToplevel *toplevel,
_gdk_macos_surface_show (GDK_MACOS_SURFACE (self));
GDK_MACOS_SURFACE (self)->did_initial_present = TRUE;
return TRUE;
}
static gboolean
@@ -370,87 +373,6 @@ _gdk_macos_toplevel_surface_hide (GdkSurface *surface)
GDK_SURFACE_CLASS (_gdk_macos_toplevel_surface_parent_class)->hide (surface);
}
static gboolean
_gdk_macos_toplevel_surface_compute_size (GdkSurface *surface)
{
GdkMacosToplevelSurface *self = (GdkMacosToplevelSurface *)surface;
NSWindow *nswindow = _gdk_macos_surface_get_native (GDK_MACOS_SURFACE (self));
GdkToplevelSize size;
GdkDisplay *display;
GdkMonitor *monitor;
int bounds_width, bounds_height;
int width, height;
GdkGeometry geometry;
GdkSurfaceHints mask;
NSWindowStyleMask style_mask;
g_assert (GDK_IS_MACOS_TOPLEVEL_SURFACE (self));
if (!GDK_MACOS_SURFACE (surface)->geometry_dirty)
return FALSE;
GDK_MACOS_SURFACE (surface)->geometry_dirty = FALSE;
display = gdk_surface_get_display (surface);
monitor = gdk_display_get_monitor_at_surface (display, surface);
style_mask = [nswindow styleMask];
if (monitor)
{
GdkRectangle workarea;
gdk_macos_monitor_get_workarea (monitor, &workarea);
bounds_width = workarea.width;
bounds_height = workarea.height;
}
else
{
bounds_width = G_MAXINT;
bounds_height = G_MAXINT;
}
gdk_toplevel_size_init (&size, bounds_width, bounds_height);
gdk_toplevel_notify_compute_size (GDK_TOPLEVEL (surface), &size);
g_warn_if_fail (size.width > 0);
g_warn_if_fail (size.height > 0);
width = size.width;
height = size.height;
if (style_mask & NSWindowStyleMaskResizable)
{
geometry.min_width = size.min_width;
geometry.min_height = size.min_height;
mask = GDK_HINT_MIN_SIZE;
}
else
{
geometry.max_width = geometry.min_width = width;
geometry.max_height = geometry.min_height = height;
mask = GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE;
}
if (size.shadow.is_valid)
_gdk_macos_surface_set_shadow (GDK_MACOS_SURFACE (surface),
size.shadow.top,
size.shadow.right,
size.shadow.bottom,
size.shadow.left);
_gdk_macos_surface_set_geometry_hints (GDK_MACOS_SURFACE (self), &geometry, mask);
gdk_surface_constrain_size (&geometry, mask, width, height, &width, &height);
_gdk_macos_surface_resize (GDK_MACOS_SURFACE (self), width, height);
return FALSE;
}
static void
_gdk_macos_toplevel_surface_request_layout (GdkSurface *surface)
{
GDK_MACOS_SURFACE (surface)->geometry_dirty = TRUE;
}
static void
_gdk_macos_toplevel_surface_destroy (GdkSurface *surface,
gboolean foreign_destroy)
@@ -584,8 +506,6 @@ _gdk_macos_toplevel_surface_class_init (GdkMacosToplevelSurfaceClass *klass)
surface_class->destroy = _gdk_macos_toplevel_surface_destroy;
surface_class->hide = _gdk_macos_toplevel_surface_hide;
surface_class->compute_size = _gdk_macos_toplevel_surface_compute_size;
surface_class->request_layout = _gdk_macos_toplevel_surface_request_layout;
gdk_toplevel_install_properties (object_class, LAST_PROP);
}
-31
View File
@@ -40,36 +40,5 @@ struct _GdkPoint
};
typedef struct _GdkPoint GdkPoint;
static inline NSPoint
convert_nspoint_from_screen (NSWindow *window,
NSPoint point)
{
#ifdef AVAILABLE_MAC_OS_X_VERSION_10_15_AND_LATER
return [window convertPointFromScreen:point];
#else
/* Apple documentation claims that convertPointFromScreen is available
* on 10.12+. However, that doesn't seem to be the case when using it.
* Instead, we'll just use it on modern 10.15 systems and fallback to
* converting using rects on older systems.
*/
return [window convertRectFromScreen:NSMakeRect (point.x, point.y, 0, 0)].origin;
#endif
}
static inline NSPoint
convert_nspoint_to_screen (NSWindow *window,
NSPoint point)
{
#ifdef AVAILABLE_MAC_OS_X_VERSION_10_15_AND_LATER
return [window convertPointToScreen:point];
#else
/* Apple documentation claims that convertPointToScreen is available
* on 10.12+. However, that doesn't seem to be the case when using it.
* Instead, we'll just use it on modern 10.15 systems and fallback to
* converting using rects on older systems.
*/
return [window convertRectToScreen:NSMakeRect (point.x, point.y, 0, 0)].origin;
#endif
}
#endif /* __GDK_MACOS_UTILS_PRIVATE_H__ */
-1
View File
@@ -33,7 +33,6 @@ gdk_macos_public_headers = files([
'gdkmacosglcontext.h',
'gdkmacoskeymap.h',
'gdkmacosmonitor.h',
'gdkmacosseat.h',
'gdkmacossurface.h',
])
+11 -70
View File
@@ -2357,9 +2357,6 @@ touch_handle_down (void *data,
_gdk_wayland_display_update_serial (display, serial);
if (!wl_surface)
return;
touch = gdk_wayland_seat_add_touch (seat, id, wl_surface);
touch->x = wl_fixed_to_double (x);
touch->y = wl_fixed_to_double (y);
@@ -2407,9 +2404,6 @@ touch_handle_up (void *data,
_gdk_wayland_display_update_serial (display, serial);
touch = gdk_wayland_seat_get_touch (seat, id);
if (!touch)
return;
event = gdk_touch_event_new (GDK_TOUCH_END,
GDK_SLOT_TO_EVENT_SEQUENCE (touch->id),
touch->surface,
@@ -2448,9 +2442,6 @@ touch_handle_motion (void *data,
GdkEvent *event;
touch = gdk_wayland_seat_get_touch (seat, id);
if (!touch)
return;
touch->x = wl_fixed_to_double (x);
touch->y = wl_fixed_to_double (y);
@@ -3390,14 +3381,9 @@ tablet_tool_handle_proximity_in (void *data,
GdkWaylandTabletData *tablet = zwp_tablet_v2_get_user_data (wp_tablet);
GdkWaylandSeat *seat = GDK_WAYLAND_SEAT (tablet->seat);
GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (seat->display);
GdkSurface *surface;
GdkSurface *surface = wl_surface_get_user_data (wsurface);
GdkEvent *event;
if (!wsurface)
return;
surface = wl_surface_get_user_data (wsurface);
if (!surface)
return;
if (!GDK_IS_SURFACE (surface))
@@ -3441,9 +3427,6 @@ tablet_tool_handle_proximity_out (void *data,
GdkWaylandTabletData *tablet = tool->current_tablet;
GdkEvent *event;
if (!tablet)
return;
GDK_SEAT_NOTE (tool->seat, EVENTS,
g_message ("proximity out, seat %p, tool %d", tool->seat,
gdk_device_tool_get_tool_type (tool->tool)));
@@ -3507,7 +3490,7 @@ tablet_tool_handle_down (void *data,
GdkWaylandSeat *seat = GDK_WAYLAND_SEAT (tool->seat);
GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (seat->display);
if (!tablet || !tablet->pointer_info.focus)
if (!tablet->pointer_info.focus)
return;
_gdk_wayland_display_update_serial (display_wayland, serial);
@@ -3524,7 +3507,7 @@ tablet_tool_handle_up (void *data,
GdkWaylandTabletToolData *tool = data;
GdkWaylandTabletData *tablet = tool->current_tablet;
if (!tablet || !tablet->pointer_info.focus)
if (!tablet->pointer_info.focus)
return;
tablet_create_button_event_frame (tablet, GDK_BUTTON_RELEASE, GDK_BUTTON_PRIMARY);
@@ -3541,9 +3524,6 @@ tablet_tool_handle_motion (void *data,
GdkWaylandTabletData *tablet = tool->current_tablet;
GdkEvent *event;
if (!tablet)
return;
tablet->pointer_info.surface_x = wl_fixed_to_double (sx);
tablet->pointer_info.surface_y = wl_fixed_to_double (sy);
@@ -3571,12 +3551,7 @@ tablet_tool_handle_pressure (void *data,
{
GdkWaylandTabletToolData *tool = data;
GdkWaylandTabletData *tablet = tool->current_tablet;
int axis_index;
if (!tablet)
return;
axis_index = tablet->axis_indices[GDK_AXIS_PRESSURE];
int axis_index = tablet->axis_indices[GDK_AXIS_PRESSURE];
_gdk_device_translate_axis (tablet->stylus_device, axis_index,
pressure, &tablet->axes[GDK_AXIS_PRESSURE]);
@@ -3593,12 +3568,7 @@ tablet_tool_handle_distance (void *data,
{
GdkWaylandTabletToolData *tool = data;
GdkWaylandTabletData *tablet = tool->current_tablet;
int axis_index;
if (!tablet)
return;
axis_index = tablet->axis_indices[GDK_AXIS_DISTANCE];
int axis_index = tablet->axis_indices[GDK_AXIS_DISTANCE];
_gdk_device_translate_axis (tablet->stylus_device, axis_index,
distance, &tablet->axes[GDK_AXIS_DISTANCE]);
@@ -3616,14 +3586,8 @@ tablet_tool_handle_tilt (void *data,
{
GdkWaylandTabletToolData *tool = data;
GdkWaylandTabletData *tablet = tool->current_tablet;
int xtilt_axis_index;
int ytilt_axis_index;
if (!tablet)
return;
xtilt_axis_index = tablet->axis_indices[GDK_AXIS_XTILT];
ytilt_axis_index = tablet->axis_indices[GDK_AXIS_YTILT];
int xtilt_axis_index = tablet->axis_indices[GDK_AXIS_XTILT];
int ytilt_axis_index = tablet->axis_indices[GDK_AXIS_YTILT];
_gdk_device_translate_axis (tablet->stylus_device, xtilt_axis_index,
wl_fixed_to_double (xtilt),
@@ -3650,7 +3614,7 @@ tablet_tool_handle_button (void *data,
GdkEventType evtype;
guint n_button;
if (!tablet || !tablet->pointer_info.focus)
if (!tablet->pointer_info.focus)
return;
tablet->pointer_info.press_serial = serial;
@@ -3681,12 +3645,7 @@ tablet_tool_handle_rotation (void *data,
{
GdkWaylandTabletToolData *tool = data;
GdkWaylandTabletData *tablet = tool->current_tablet;
int axis_index;
if (!tablet)
return;
axis_index = tablet->axis_indices[GDK_AXIS_ROTATION];
int axis_index = tablet->axis_indices[GDK_AXIS_ROTATION];
_gdk_device_translate_axis (tablet->stylus_device, axis_index,
wl_fixed_to_double (degrees),
@@ -3705,12 +3664,7 @@ tablet_tool_handle_slider (void *data,
{
GdkWaylandTabletToolData *tool = data;
GdkWaylandTabletData *tablet = tool->current_tablet;
int axis_index;
if (!tablet)
return;
axis_index = tablet->axis_indices[GDK_AXIS_SLIDER];
int axis_index = tablet->axis_indices[GDK_AXIS_SLIDER];
_gdk_device_translate_axis (tablet->stylus_device, axis_index,
position, &tablet->axes[GDK_AXIS_SLIDER]);
@@ -3728,14 +3682,9 @@ tablet_tool_handle_wheel (void *data,
{
GdkWaylandTabletToolData *tool = data;
GdkWaylandTabletData *tablet = tool->current_tablet;
GdkWaylandSeat *seat;
GdkWaylandSeat *seat = GDK_WAYLAND_SEAT (tablet->seat);
GdkEvent *event;
if (!tablet)
return;
seat = GDK_WAYLAND_SEAT (tablet->seat);
GDK_SEAT_NOTE (seat, EVENTS,
g_message ("tablet tool %d wheel %d/%d",
gdk_device_tool_get_tool_type (tool->tool), degrees, clicks));
@@ -3775,9 +3724,6 @@ tablet_tool_handle_frame (void *data,
GdkWaylandTabletData *tablet = tool->current_tablet;
GdkEvent *frame_event;
if (!tablet)
return;
GDK_SEAT_NOTE (tablet->seat, EVENTS,
g_message ("tablet frame, time %d", time));
@@ -4366,9 +4312,6 @@ pointer_surface_update_scale (GdkDevice *device)
return;
}
if (!pointer->pointer_surface_outputs)
return;
scale = 1;
for (l = pointer->pointer_surface_outputs; l != NULL; l = l->next)
{
@@ -4376,8 +4319,6 @@ pointer_surface_update_scale (GdkDevice *device)
scale = MAX (scale, output_scale);
}
if (pointer->current_output_scale == scale)
return;
pointer->current_output_scale = scale;
gdk_wayland_device_update_surface_cursor (device);
+6 -9
View File
@@ -94,7 +94,7 @@
#define MIN_SYSTEM_BELL_DELAY_MS 20
#define GTK_SHELL1_VERSION 4
#define GTK_SHELL1_VERSION 3
#define OUTPUT_VERSION_WITH_DONE 2
#define NO_XDG_OUTPUT_DONE_SINCE_VERSION 3
@@ -1350,23 +1350,21 @@ _gdk_wayland_display_create_shm_surface (GdkWaylandDisplay *display,
data->buffer = NULL;
data->scale = scale;
stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, width * scale);
stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, width*scale);
data->pool = create_shm_pool (display->shm,
height * scale * stride,
height*scale*stride,
&data->buf_length,
&data->buf);
if (G_UNLIKELY (data->pool == NULL))
g_error ("Unable to create shared memory pool");
surface = cairo_image_surface_create_for_data (data->buf,
CAIRO_FORMAT_ARGB32,
width * scale,
height * scale,
width*scale,
height*scale,
stride);
data->buffer = wl_shm_pool_create_buffer (data->pool, 0,
width * scale, height * scale,
width*scale, height*scale,
stride, WL_SHM_FORMAT_ARGB8888);
cairo_surface_set_user_data (surface, &gdk_wayland_shm_surface_cairo_key,
@@ -1838,7 +1836,6 @@ settings_portal_changed (GDBusProxy *proxy,
char *a = g_variant_print (value, FALSE);
g_debug ("Using changed portal setting %s %s: %s", namespace, name, a);
g_free (a);
entry->valid = TRUE;
apply_portal_setting (entry, value, display);
gdk_display_setting_changed (display, entry->setting);
}
+37 -32
View File
@@ -242,22 +242,14 @@ gdk_wayland_gl_context_end_frame (GdkDrawContext *draw_context,
gdk_profiler_add_mark (GDK_PROFILER_CURRENT_TIME, 0, "wayland", "swap buffers");
if (display_wayland->have_egl_swap_buffers_with_damage)
{
EGLint stack_rects[4 * 4]; /* 4 rects */
EGLint *heap_rects = NULL;
int i, j, n_rects = cairo_region_num_rectangles (painted);
EGLint *rects = g_new (EGLint, n_rects * 4);
cairo_rectangle_int_t rect;
int surface_height = gdk_surface_get_height (surface);
int scale = gdk_surface_get_scale_factor (surface);
EGLint *rects;
if (n_rects < G_N_ELEMENTS (stack_rects) / 4)
rects = (EGLint *)&stack_rects;
else
heap_rects = rects = g_new (EGLint, n_rects * 4);
for (i = 0, j = 0; i < n_rects; i++)
{
cairo_rectangle_int_t rect;
cairo_region_get_rectangle (painted, i, &rect);
rects[j++] = rect.x * scale;
rects[j++] = (surface_height - rect.height - rect.y) * scale;
@@ -265,7 +257,7 @@ gdk_wayland_gl_context_end_frame (GdkDrawContext *draw_context,
rects[j++] = rect.height * scale;
}
eglSwapBuffersWithDamageEXT (display_wayland->egl_display, egl_surface, rects, n_rects);
g_free (heap_rects);
g_free (rects);
}
else
eglSwapBuffers (display_wayland->egl_display, egl_surface);
@@ -301,27 +293,27 @@ gdk_wayland_get_display (GdkWaylandDisplay *display_wayland)
if (epoxy_has_egl_extension (NULL, "EGL_KHR_platform_base"))
{
PFNEGLGETPLATFORMDISPLAYPROC getPlatformDisplay =
(void *) eglGetProcAddress ("eglGetPlatformDisplay");
(void *) eglGetProcAddress ("eglGetPlatformDisplay");
if (getPlatformDisplay)
dpy = getPlatformDisplay (EGL_PLATFORM_WAYLAND_EXT,
display_wayland->wl_display,
NULL);
dpy = getPlatformDisplay (EGL_PLATFORM_WAYLAND_EXT,
display_wayland->wl_display,
NULL);
if (dpy)
return dpy;
return dpy;
}
if (epoxy_has_egl_extension (NULL, "EGL_EXT_platform_base"))
{
PFNEGLGETPLATFORMDISPLAYEXTPROC getPlatformDisplay =
(void *) eglGetProcAddress ("eglGetPlatformDisplayEXT");
(void *) eglGetProcAddress ("eglGetPlatformDisplayEXT");
if (getPlatformDisplay)
dpy = getPlatformDisplay (EGL_PLATFORM_WAYLAND_EXT,
display_wayland->wl_display,
NULL);
dpy = getPlatformDisplay (EGL_PLATFORM_WAYLAND_EXT,
display_wayland->wl_display,
NULL);
if (dpy)
return dpy;
return dpy;
}
return eglGetDisplay ((EGLNativeDisplayType) display_wayland->wl_display);
@@ -394,7 +386,7 @@ find_eglconfig_for_surface (GdkSurface *surface,
GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display);
EGLint attrs[MAX_EGL_ATTRS];
EGLint count;
EGLConfig config;
EGLConfig *configs;
int i = 0;
attrs[i++] = EGL_SURFACE_TYPE;
@@ -415,8 +407,7 @@ find_eglconfig_for_surface (GdkSurface *surface,
attrs[i++] = EGL_NONE;
g_assert (i < MAX_EGL_ATTRS);
/* Pick first valid configuration i guess? */
if (!eglChooseConfig (display_wayland->egl_display, attrs, &config, 1, &count) || count < 1)
if (!eglChooseConfig (display_wayland->egl_display, attrs, NULL, 0, &count) || count < 1)
{
g_set_error_literal (error, GDK_GL_ERROR,
GDK_GL_ERROR_UNSUPPORTED_FORMAT,
@@ -424,17 +415,31 @@ find_eglconfig_for_surface (GdkSurface *surface,
return FALSE;
}
g_assert (egl_config_out);
*egl_config_out = config;
configs = g_new (EGLConfig, count);
if (!eglChooseConfig (display_wayland->egl_display, attrs, configs, count, &count) || count < 1)
{
g_set_error_literal (error, GDK_GL_ERROR,
GDK_GL_ERROR_UNSUPPORTED_FORMAT,
_("No available configurations for the given pixel format"));
return FALSE;
}
/* Pick first valid configuration i guess? */
if (egl_config_out != NULL)
*egl_config_out = configs[0];
g_free (configs);
return TRUE;
}
GdkGLContext *
gdk_wayland_surface_create_gl_context (GdkSurface *surface,
gboolean attached,
GdkGLContext *share,
GError **error)
gboolean attached,
GdkGLContext *share,
GError **error)
{
GdkDisplay *display = gdk_surface_get_display (surface);
GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display);
@@ -521,10 +526,10 @@ gdk_wayland_display_make_gl_context_current (GdkDisplay *display,
else
{
if (display_wayland->have_egl_surfaceless_context)
egl_surface = EGL_NO_SURFACE;
egl_surface = EGL_NO_SURFACE;
else
egl_surface = gdk_wayland_surface_get_dummy_egl_surface (surface,
context_wayland->egl_config);
egl_surface = gdk_wayland_surface_get_dummy_egl_surface (surface,
context_wayland->egl_config);
}
if (!eglMakeCurrent (display_wayland->egl_display, egl_surface,
+4 -36
View File
@@ -389,13 +389,10 @@ gdk_wayland_surface_update_size (GdkSurface *surface,
int scale)
{
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
gboolean width_changed, height_changed, scale_changed;
width_changed = surface->width != width;
height_changed = surface->height != height;
scale_changed = impl->scale != scale;
if (!width_changed && !height_changed && !scale_changed)
if ((surface->width == width) &&
(surface->height == height) &&
(impl->scale == scale))
return;
surface->width = width;
@@ -408,13 +405,6 @@ gdk_wayland_surface_update_size (GdkSurface *surface,
wl_surface_set_buffer_scale (impl->display_server.wl_surface, scale);
gdk_surface_invalidate_rect (surface, NULL);
if (width_changed)
g_object_notify (G_OBJECT (surface), "width");
if (height_changed)
g_object_notify (G_OBJECT (surface), "height");
if (scale_changed)
g_object_notify (G_OBJECT (surface), "scale-factor");
}
static const char *
@@ -2223,24 +2213,6 @@ gdk_wayland_toplevel_announce_csd (GdkToplevel *toplevel)
ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_CLIENT);
}
void
gdk_wayland_toplevel_announce_ssd (GdkToplevel *toplevel)
{
GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (GDK_SURFACE (toplevel)));
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (toplevel);
g_return_if_fail (GDK_IS_WAYLAND_TOPLEVEL (toplevel));
if (!display_wayland->server_decoration_manager)
return;
impl->display_server.server_decoration =
org_kde_kwin_server_decoration_manager_create (display_wayland->server_decoration_manager,
impl->display_server.wl_surface);
if (impl->display_server.server_decoration)
org_kde_kwin_server_decoration_request_mode (impl->display_server.server_decoration,
ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_SERVER);
}
gboolean
gdk_wayland_toplevel_inhibit_idle (GdkToplevel *toplevel)
{
@@ -2967,11 +2939,7 @@ gdk_wayland_surface_hide_surface (GdkSurface *surface)
if (impl->display_server.gtk_surface)
{
if (display_wayland->gtk_shell_version >=
GTK_SURFACE1_RELEASE_SINCE_VERSION)
gtk_surface1_release (impl->display_server.gtk_surface);
else
gtk_surface1_destroy (impl->display_server.gtk_surface);
gtk_surface1_destroy (impl->display_server.gtk_surface);
impl->display_server.gtk_surface = NULL;
impl->application.was_set = FALSE;
}
-1
View File
@@ -33,7 +33,6 @@ void gdk_wayland_toplevel_set_dbus_properties (GdkTopl
const char *unique_bus_name);
void gdk_wayland_toplevel_announce_csd (GdkToplevel *toplevel);
void gdk_wayland_toplevel_announce_ssd (GdkToplevel *toplevel);
gboolean gdk_wayland_toplevel_inhibit_idle (GdkToplevel *toplevel);
void gdk_wayland_toplevel_uninhibit_idle (GdkToplevel *toplevel);
+2 -5
View File
@@ -1,6 +1,6 @@
<protocol name="gtk">
<interface name="gtk_shell1" version="4">
<interface name="gtk_shell1" version="3">
<description summary="gtk specific extensions">
gtk_shell is a protocol extension providing additional features for
clients implementing it.
@@ -35,7 +35,7 @@
</request>
</interface>
<interface name="gtk_surface1" version="4">
<interface name="gtk_surface1" version="3">
<request name="set_dbus_properties">
<arg name="application_id" type="string" allow-null="true"/>
<arg name="app_menu_path" type="string" allow-null="true"/>
@@ -82,9 +82,6 @@
<request name="request_focus" since="3">
<arg name="startup_id" type="string" allow-null="true"/>
</request>
<!-- Version 4 additions -->
<request name="release" type="destructor" since="4"/>
</interface>
</protocol>
+76 -10
View File
@@ -31,6 +31,46 @@
G_DEFINE_TYPE (GdkWin32CairoContext, gdk_win32_cairo_context, GDK_TYPE_CAIRO_CONTEXT)
static cairo_surface_t *
create_cairo_surface_for_layered_window (GdkWin32Surface *impl,
int width,
int height,
int scale)
{
if (width > impl->dib_width ||
height > impl->dib_height)
{
cairo_surface_t *new_cache;
impl->dib_width = MAX (impl->dib_width, MAX (width, 1));
impl->dib_height = MAX (impl->dib_height, MAX (height, 1));
/* Create larger cache surface, copy old cache surface over it */
new_cache = cairo_win32_surface_create_with_dib (CAIRO_FORMAT_ARGB32,
impl->dib_width,
impl->dib_height);
if (impl->cache_surface)
{
cairo_t *cr = cairo_create (new_cache);
cairo_set_source_surface (cr, impl->cache_surface, 0, 0);
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
cairo_paint (cr);
cairo_destroy (cr);
cairo_surface_flush (new_cache);
cairo_surface_destroy (impl->cache_surface);
}
impl->cache_surface = new_cache;
cairo_surface_set_device_scale (impl->cache_surface,
scale,
scale);
}
return cairo_surface_reference (impl->cache_surface);
}
static cairo_surface_t *
create_cairo_surface_for_surface (GdkSurface *surface,
int scale)
@@ -74,11 +114,22 @@ gdk_win32_cairo_context_begin_frame (GdkDrawContext *draw_context,
width = MAX (width, 1);
height = MAX (height, 1);
self->window_surface = create_cairo_surface_for_surface (surface, scale);
if (self->layered)
self->window_surface = create_cairo_surface_for_layered_window (impl, width, height, scale);
else
self->window_surface = create_cairo_surface_for_surface (surface, scale);
if (!self->double_buffered)
/* Non-double-buffered windows paint on the window surface directly */
self->paint_surface = cairo_surface_reference (self->window_surface);
if (self->layered ||
!self->double_buffered)
{
/* Layered windows paint on the window_surface (which is itself
* an in-memory cache that the window maintains, since layered windows
* do not support incremental redraws.
* Non-double-buffered windows paint on the window surface directly
* as well.
*/
self->paint_surface = cairo_surface_reference (self->window_surface);
}
else
{
if (width > self->db_width ||
@@ -102,10 +153,11 @@ gdk_win32_cairo_context_begin_frame (GdkDrawContext *draw_context,
}
/* Clear the paint region.
* For non-double-buffered rendering we must clear it, otherwise
* semi-transparent pixels will "add up" with each repaint.
* We must also clear the old pixels from the DB cache surface
* that we're going to use as a buffer.
* For non-double-buffered and for layered rendering we must
* clear it, otherwise semi-transparent pixels will "add up"
* with each repaint.
* For double-buffered rendering we must clear the old pixels
* from the DB cache surface that we're going to use as a buffer.
*/
cr = cairo_create (self->paint_surface);
cairo_set_source_rgba (cr, 0, 0, 0, 00);
@@ -132,10 +184,12 @@ gdk_win32_cairo_context_end_frame (GdkDrawContext *draw_context,
* to be here.
*/
/* For double-buffered windows we need to blit
/* Layered windows have their own, special copying section
* further down. For double-buffered windows we need to blit
* the DB buffer contents into the window itself.
*/
if (self->double_buffered)
if (!self->layered &&
self->double_buffered)
{
cairo_t *cr;
@@ -153,6 +207,18 @@ gdk_win32_cairo_context_end_frame (GdkDrawContext *draw_context,
cairo_surface_flush (self->window_surface);
/* Update layered window, updating its contents, size and position
* in one call.
*/
if (self->layered)
{
RECT client_rect;
/* Get the position/size of the window that GDK wants. */
_gdk_win32_get_window_client_area_rect (surface, scale, &client_rect);
_gdk_win32_update_layered_window_from_cache (surface, &client_rect, TRUE, TRUE, TRUE);
}
g_clear_pointer (&self->paint_surface, cairo_surface_destroy);
g_clear_pointer (&self->window_surface, cairo_surface_destroy);
}
+8 -3
View File
@@ -44,6 +44,10 @@ struct _GdkWin32CairoContext
* code that is unaffected by this flag.
*/
guint double_buffered : 1;
/* Re-set to the same value as GdkSurfaceImplWin32->layered
* every frame (since layeredness can change at runtime).
*/
guint layered : 1;
/* The a surface for double-buffering. We keep it
* around between repaints, and only re-allocate it
@@ -52,11 +56,12 @@ struct _GdkWin32CairoContext
int db_width;
int db_height;
/* Surface for the window DC */
/* Surface for the window DC (in non-layered mode).
* A reference of the cache surface (in layered mode). */
cairo_surface_t *window_surface;
/* A reference to db_surface (when double-buffering).
* When not using double-buffering this is a reference
* to window_surface.
* When not using double-buffering or in layered mode
* this is a reference to window_surface.
*/
cairo_surface_t *paint_surface;
};
+1
View File
@@ -2113,6 +2113,7 @@ gdk_drag_anim_timeout (gpointer data)
(drag->start_y - drag->util_data.last_y) * t -
drag->hot_y);
gdk_win32_surface_move (drag->drag_surface, x, y);
gdk_win32_surface_set_opacity (drag->drag_surface, 1.0 - f);
return G_SOURCE_CONTINUE;
}
+14 -13
View File
@@ -1495,6 +1495,15 @@ handle_dpi_changed (GdkSurface *window,
monitor = gdk_display_get_monitor_at_surface (display, window);
gdk_monitor_set_scale_factor (monitor, impl->surface_scale);
if (impl->layered)
{
/* We only need to set the cairo surface device scale here ourselves for layered windows */
if (impl->cache_surface != NULL)
cairo_surface_set_device_scale (impl->cache_surface,
impl->surface_scale,
impl->surface_scale);
}
}
_gdk_win32_adjust_client_rect (window, rect);
@@ -1712,8 +1721,8 @@ _gdk_win32_surface_fill_min_max_info (GdkSurface *window,
mmi->ptMaxSize.y = nearest_info.rcWork.bottom - nearest_info.rcWork.top;
}
mmi->ptMaxTrackSize.x = GetSystemMetrics (SM_CXVIRTUALSCREEN) + impl->shadow_x * impl->surface_scale;
mmi->ptMaxTrackSize.y = GetSystemMetrics (SM_CYVIRTUALSCREEN) + impl->shadow_y * impl->surface_scale;
mmi->ptMaxTrackSize.x = GetSystemMetrics (SM_CXVIRTUALSCREEN) + impl->margins_x * impl->surface_scale;
mmi->ptMaxTrackSize.y = GetSystemMetrics (SM_CYVIRTUALSCREEN) + impl->margins_y * impl->surface_scale;
}
return TRUE;
@@ -2345,7 +2354,9 @@ gdk_event_translate (MSG *msg,
if (impl->drag_move_resize_context.op != GDK_WIN32_DRAGOP_NONE)
gdk_win32_surface_do_move_resize_drag (window, current_root_x, current_root_y);
{
gdk_win32_surface_do_move_resize_drag (window, current_root_x, current_root_y);
}
else if (_gdk_input_ignore_core == 0)
{
current_x = (gint16) GET_X_LPARAM (msg->lParam) / impl->surface_scale;
@@ -2824,16 +2835,6 @@ gdk_event_translate (MSG *msg,
else
unset_bits |= GDK_TOPLEVEL_STATE_MAXIMIZED;
/*
* If we are minizing, pause all surface layout computations, and re-start the
* computation once we are coming out of a minimized state
*/
if (!(old_state & GDK_TOPLEVEL_STATE_MINIMIZED) && set_bits & GDK_TOPLEVEL_STATE_MINIMIZED)
gdk_surface_freeze_updates (window);
if (old_state & GDK_TOPLEVEL_STATE_MINIMIZED && unset_bits & GDK_TOPLEVEL_STATE_MINIMIZED)
gdk_surface_thaw_updates (window);
gdk_surface_set_is_mapped (window, !!IsWindowVisible (msg->hwnd));
gdk_synthesize_surface_state (window, unset_bits, set_bits);
+24
View File
@@ -88,6 +88,19 @@ _gdk_win32_gl_context_dispose (GObject *gobject)
#endif
}
if (surface != NULL)
{
GdkWin32Surface *impl = GDK_WIN32_SURFACE (surface);
if (impl->suppress_layered > 0)
impl->suppress_layered--;
/* If we don't have any surface that forces layered windows off,
* trigger update_style_bits() to enable layered windows again
*/
if (impl->suppress_layered == 0)
_gdk_win32_surface_update_style_bits (surface);
}
G_OBJECT_CLASS (gdk_win32_gl_context_parent_class)->dispose (gobject);
}
@@ -988,6 +1001,17 @@ gdk_win32_gl_context_realize (GdkGLContext *context,
/* set whether we are using GLES */
gdk_gl_context_set_use_es (context, use_es);
/* OpenGL does not work with WS_EX_LAYERED enabled, so we need to
* disable WS_EX_LAYERED when we acquire a valid HGLRC
*/
impl->suppress_layered++;
/* if this is the first time a GL context is acquired for the surface,
* disable layered windows by triggering update_style_bits()
*/
if (impl->suppress_layered == 1)
_gdk_win32_surface_update_style_bits (surface);
/* Ensure that any other context is created with a legacy bit set */
gdk_gl_context_set_is_legacy (context, legacy_bit);
+1
View File
@@ -351,6 +351,7 @@ _gdk_win32_surface_exstyle_to_string (LONG style)
BIT (CONTEXTHELP);
BIT (CONTROLPARENT);
BIT (DLGMODALFRAME);
BIT (LAYERED);
BIT (LAYOUTRTL);
BIT (LEFTSCROLLBAR);
BIT (MDICHILD);
+304 -224
View File
@@ -53,10 +53,6 @@
#include <math.h>
static void gdk_surface_win32_finalize (GObject *object);
static void compute_toplevel_size (GdkSurface *surface,
gboolean update_geometry,
int *width,
int *height);
static gpointer parent_class = NULL;
static GSList *modal_window_stack = NULL;
@@ -275,6 +271,7 @@ _gdk_win32_adjust_client_rect (GdkSurface *window,
gboolean
_gdk_win32_surface_enable_transparency (GdkSurface *window)
{
GdkWin32Surface *impl;
DWM_BLURBEHIND blur_behind;
HRGN empty_region;
HRESULT call_result;
@@ -283,6 +280,12 @@ _gdk_win32_surface_enable_transparency (GdkSurface *window)
if (window == NULL || GDK_SURFACE_HWND (window) == NULL)
return FALSE;
impl = GDK_WIN32_SURFACE (window);
/* layered windows don't need blurbehind for transparency */
if (impl->layered)
return TRUE;
if (!gdk_display_is_composited (gdk_surface_get_display (window)))
return FALSE;
@@ -519,6 +522,9 @@ _gdk_win32_display_create_surface (GdkDisplay *display,
surface->width = width;
surface->height = height;
impl->layered = FALSE;
impl->layered_opacity = 1.0;
impl->surface_scale = _gdk_win32_display_get_monitor_scale_factor (display_win32, NULL, NULL, NULL);
impl->unscaled_width = width * impl->surface_scale;
impl->unscaled_height = height * impl->surface_scale;
@@ -1094,8 +1100,6 @@ gdk_win32_surface_resize (GdkSurface *window,
outer_rect.bottom - outer_rect.top,
SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER));
window->resize_count += 1;
gdk_surface_request_layout (window);
}
static void
@@ -1156,6 +1160,8 @@ gdk_win32_surface_move_resize_internal (GdkSurface *window,
{
GdkWin32Surface *surface = GDK_WIN32_SURFACE (window);
surface->inhibit_configure = TRUE;
/* We ignore changes to the window being moved or resized by the
user, as we don't want to fight the user */
if (GDK_SURFACE_HWND (window) == _modal_move_resize_window)
@@ -1180,6 +1186,8 @@ gdk_win32_surface_move_resize_internal (GdkSurface *window,
}
out:
surface->inhibit_configure = FALSE;
gdk_surface_request_layout (window);
}
@@ -1220,10 +1228,10 @@ gdk_win32_surface_layout_popup (GdkSurface *surface,
gdk_surface_layout_popup_helper (surface,
width,
height,
impl->shadow.left,
impl->shadow.right,
impl->shadow.top,
impl->shadow.bottom,
impl->margins.left,
impl->margins.right,
impl->margins.top,
impl->margins.bottom,
monitor,
&bounds,
layout,
@@ -1248,24 +1256,11 @@ gdk_win32_surface_layout_popup (GdkSurface *surface,
}
}
static void
maybe_notify_mapped (GdkSurface *surface)
{
if (surface->destroyed)
return;
if (!GDK_SURFACE_IS_MAPPED (surface))
{
gdk_surface_set_is_mapped (surface, TRUE);
gdk_surface_invalidate_rect (surface, NULL);
}
}
static void
show_popup (GdkSurface *surface)
{
gdk_win32_surface_raise (surface);
maybe_notify_mapped (surface);
gdk_surface_set_is_mapped (surface, TRUE);
show_window_internal (surface, FALSE, FALSE);
gdk_surface_invalidate_rect (surface, NULL);
}
@@ -1751,6 +1746,11 @@ update_single_bit (LONG *style,
* Returns TRUE if window has no decorations.
* Usually it means CSD windows, because GTK
* calls gdk_surface_set_decorations (window, 0);
* This is used to decide whether a toplevel should
* be made layered, thus it
* only returns TRUE for toplevels (until GTK minimal
* system requirements are lifted to Windows 8 or newer,
* because only toplevels can be layered).
*/
gboolean
_gdk_win32_surface_lacks_wm_decorations (GdkSurface *window)
@@ -1844,6 +1844,27 @@ _gdk_win32_surface_update_style_bits (GdkSurface *window)
new_exstyle &= ~WS_EX_TOOLWINDOW;
}
/* We can get away with using layered windows
* only when no decorations are needed. It can mean
* CSD or borderless non-CSD windows (tooltips?).
*
* If this window cannot use layered windows, disable it always.
* This currently applies to windows using OpenGL, which
* does not work with layered windows.
*/
if (impl->suppress_layered == 0)
{
if (_gdk_win32_surface_lacks_wm_decorations (window))
impl->layered = g_strcmp0 (g_getenv ("GDK_WIN32_LAYERED"), "0") != 0;
}
else
impl->layered = FALSE;
if (impl->layered)
new_exstyle |= WS_EX_LAYERED;
else
new_exstyle &= ~WS_EX_LAYERED;
if (get_effective_window_decorations (window, &decorations))
{
all = (decorations & GDK_DECOR_ALL);
@@ -2273,7 +2294,6 @@ snap_up (GdkSurface *window)
impl = GDK_WIN32_SURFACE (window);
impl->snap_state = GDK_WIN32_AEROSNAP_STATE_FULLUP;
impl->resized = FALSE;
stash_window (window, impl);
@@ -2284,18 +2304,10 @@ snap_up (GdkSurface *window)
y = 0;
height = maxysize;
x = x - impl->shadow.left;
y = y - impl->shadow.top;
width += impl->shadow_x;
height += impl->shadow_y;
/* XXX: FIXME, AeroSnap snap_up() not really working well,
*
* * The snap_up() puts the window at the top left corner.
* * Without the following call, the height maximizes but we see a spew of
* "GdkToplevelSize: geometry size (x,y) exceeds bounds" warnings
*/
compute_toplevel_size (window, TRUE, &width, &height);
x = x - impl->margins.left;
y = y - impl->margins.top;
width += impl->margins_x;
height += impl->margins_y;
gdk_win32_surface_move_resize (window, x, y, width, height);
}
@@ -2311,7 +2323,6 @@ snap_left (GdkSurface *window,
impl = GDK_WIN32_SURFACE (window);
impl->snap_state = GDK_WIN32_AEROSNAP_STATE_HALFLEFT;
impl->resized = FALSE;
gdk_win32_monitor_get_workarea (snap_monitor, &rect);
@@ -2319,10 +2330,10 @@ snap_left (GdkSurface *window,
rect.width = rect.width / 2;
rect.x = rect.x - impl->shadow.left;
rect.y = rect.y - impl->shadow.top;
rect.width = rect.width + impl->shadow_x;
rect.height = rect.height + impl->shadow_y;
rect.x = rect.x - impl->margins.left;
rect.y = rect.y - impl->margins.top;
rect.width = rect.width + impl->margins_x;
rect.height = rect.height + impl->margins_y;
gdk_win32_surface_move_resize (window,
rect.x, rect.y,
@@ -2340,7 +2351,6 @@ snap_right (GdkSurface *window,
impl = GDK_WIN32_SURFACE (window);
impl->snap_state = GDK_WIN32_AEROSNAP_STATE_HALFRIGHT;
impl->resized = FALSE;
gdk_win32_monitor_get_workarea (snap_monitor, &rect);
@@ -2349,10 +2359,10 @@ snap_right (GdkSurface *window,
rect.width = rect.width / 2;
rect.x += rect.width;
rect.x = rect.x - impl->shadow.left;
rect.y = rect.y - impl->shadow.top;
rect.width = rect.width + impl->shadow_x;
rect.height = rect.height + impl->shadow_y;
rect.x = rect.x - impl->margins.left;
rect.y = rect.y - impl->margins.top;
rect.width = rect.width + impl->margins_x;
rect.height = rect.height + impl->margins_y;
gdk_win32_surface_move_resize (window,
rect.x, rect.y,
@@ -3492,10 +3502,10 @@ setup_drag_move_resize_context (GdkSurface *window,
*/
if (op == GDK_WIN32_DRAGOP_MOVE && !maximized)
{
swx += impl->shadow.left / impl->surface_scale;
swy += impl->shadow.top / impl->surface_scale;
swwidth -= impl->shadow_x;
swheight -= impl->shadow_y;
swx += impl->margins.left / impl->surface_scale;
swy += impl->margins.top / impl->surface_scale;
swwidth -= impl->margins_x;
swheight -= impl->margins_y;
}
pointer_outside_of_window = root_x < swx || root_x > swx + swwidth ||
@@ -3545,23 +3555,23 @@ setup_drag_move_resize_context (GdkSurface *window,
unmax_width = placement.rcNormalPosition.right - placement.rcNormalPosition.left;
unmax_height = placement.rcNormalPosition.bottom - placement.rcNormalPosition.top;
shadow_unmax_width = unmax_width - impl->shadow_x * impl->surface_scale;
shadow_unmax_height = unmax_height - impl->shadow_y * impl->surface_scale;
shadow_unmax_width = unmax_width - impl->margins_x * impl->surface_scale;
shadow_unmax_height = unmax_height - impl->margins_y * impl->surface_scale;
if (offsetx * impl->surface_scale < (shadow_unmax_width / 2) &&
offsety * impl->surface_scale < (shadow_unmax_height / 2))
{
placement.rcNormalPosition.top = (root_y - offsety + impl->shadow.top - _gdk_offset_y) * impl->surface_scale;
placement.rcNormalPosition.top = (root_y - offsety + impl->margins.top - _gdk_offset_y) * impl->surface_scale;
placement.rcNormalPosition.bottom = placement.rcNormalPosition.top + unmax_height;
if (left_half)
{
placement.rcNormalPosition.left = (root_x - offsetx + impl->shadow.left - _gdk_offset_x) * impl->surface_scale;
placement.rcNormalPosition.left = (root_x - offsetx + impl->margins.left - _gdk_offset_x) * impl->surface_scale;
placement.rcNormalPosition.right = placement.rcNormalPosition.left + unmax_width;
}
else
{
placement.rcNormalPosition.right = (root_x + offsetx + impl->shadow.right - _gdk_offset_x) * impl->surface_scale;
placement.rcNormalPosition.right = (root_x + offsetx + impl->margins.right - _gdk_offset_x) * impl->surface_scale;
placement.rcNormalPosition.left = placement.rcNormalPosition.right - unmax_width;
}
}
@@ -3572,7 +3582,7 @@ setup_drag_move_resize_context (GdkSurface *window,
(_gdk_offset_x * impl->surface_scale);
if (offsety * impl->surface_scale < shadow_unmax_height / 2)
placement.rcNormalPosition.top = (root_y - offsety + impl->shadow.top - _gdk_offset_y) * impl->surface_scale;
placement.rcNormalPosition.top = (root_y - offsety + impl->margins.top - _gdk_offset_y) * impl->surface_scale;
else
placement.rcNormalPosition.top = (root_y * impl->surface_scale) -
(unmax_height / 2) -
@@ -3599,18 +3609,18 @@ setup_drag_move_resize_context (GdkSurface *window,
if (op == GDK_WIN32_DRAGOP_MOVE)
{
snew_pos.width -= impl->shadow_x;
snew_pos.height -= impl->shadow_y;
snew_pos.width -= impl->margins_x;
snew_pos.height -= impl->margins_y;
}
if (offsetx < snew_pos.width / 2 && offsety < snew_pos.height / 2)
{
new_pos.y = root_y - offsety + impl->shadow.top / impl->surface_scale;
new_pos.y = root_y - offsety + impl->margins.top / impl->surface_scale;
if (left_half)
new_pos.x = root_x - offsetx + impl->shadow.left / impl->surface_scale;
new_pos.x = root_x - offsetx + impl->margins.left / impl->surface_scale;
else
new_pos.x = root_x + offsetx + impl->shadow.left / impl->surface_scale - new_pos.width;
new_pos.x = root_x + offsetx + impl->margins.left / impl->surface_scale - new_pos.width;
}
else
{
@@ -3775,6 +3785,67 @@ gdk_win32_get_window_size_and_position_from_client_rect (GdkSurface *window,
window_size->cy = window_rect->bottom - window_rect->top;
}
void
_gdk_win32_update_layered_window_from_cache (GdkSurface *surface,
RECT *client_rect,
gboolean do_move,
gboolean do_resize,
gboolean do_paint)
{
POINT window_position;
SIZE window_size;
BLENDFUNCTION blender;
HDC hdc;
SIZE *window_size_ptr;
POINT source_point = { 0, 0 };
POINT *source_point_ptr;
GdkWin32Surface *impl = GDK_WIN32_SURFACE (surface);
gdk_win32_get_window_size_and_position_from_client_rect (surface,
client_rect,
&window_size,
&window_position);
blender.BlendOp = AC_SRC_OVER;
blender.BlendFlags = 0;
blender.AlphaFormat = AC_SRC_ALPHA;
blender.SourceConstantAlpha = impl->layered_opacity * 255;
/* Strictly speaking, we don't need to supply hdc, source_point and
* window_size to just move the window. However, without these arguments
* the window moves but does not update its contents on Windows 7 when
* desktop composition is off. This forces us to provide hdc and
* source_point. window_size is here to avoid the function
* inexplicably failing with error 317.
*/
hdc = cairo_win32_surface_get_dc (impl->cache_surface);
window_size_ptr = &window_size;
source_point_ptr = &source_point;
if (gdk_display_is_composited (gdk_surface_get_display (surface)))
{
if (!do_paint)
hdc = NULL;
if (!do_resize)
window_size_ptr = NULL;
if (!do_move)
source_point_ptr = NULL;
}
/* Don't use UpdateLayeredWindow on minimized windows */
if (IsIconic (GDK_SURFACE_HWND (surface)))
API_CALL (SetWindowPos, (GDK_SURFACE_HWND (surface),
SWP_NOZORDER_SPECIFIED,
window_position.x, window_position.y,
window_size.cx, window_size.cy,
SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOREDRAW));
else
API_CALL (UpdateLayeredWindow, (GDK_SURFACE_HWND (surface), NULL,
&window_position, window_size_ptr,
hdc, source_point_ptr,
0, &blender, ULW_ALPHA));
}
void
gdk_win32_surface_do_move_resize_drag (GdkSurface *window,
int x,
@@ -3944,47 +4015,42 @@ gdk_win32_surface_do_move_resize_drag (GdkSurface *window,
rect.top != new_rect.top ||
rect.bottom != new_rect.bottom))
{
if (GDK_IS_TOPLEVEL (window))
{
int scale = impl->surface_scale;
impl->unscaled_width = new_rect.right - new_rect.left;
impl->unscaled_height = new_rect.bottom - new_rect.top;
impl->next_layout.configured_width = (impl->unscaled_width + scale - 1) / scale;
impl->next_layout.configured_height = (impl->unscaled_height + scale - 1) / scale;
impl->resized = TRUE;
}
context->native_move_resize_pending = TRUE;
gdk_surface_request_layout (window);
}
else if (context->op == GDK_WIN32_DRAGOP_MOVE &&
(rect.left != new_rect.left ||
rect.top != new_rect.top))
{
SIZE window_size;
POINT window_position;
context->native_move_resize_pending = FALSE;
gdk_surface_request_layout (window);
gdk_win32_get_window_size_and_position_from_client_rect (window,
&new_rect,
&window_size,
&window_position);
if (impl->layered)
{
_gdk_win32_update_layered_window_from_cache (window, &new_rect, TRUE, FALSE, FALSE);
}
else
{
SIZE window_size;
POINT window_position;
API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window),
SWP_NOZORDER_SPECIFIED,
window_position.x, window_position.y,
0, 0,
SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE));
gdk_win32_get_window_size_and_position_from_client_rect (window,
&new_rect,
&window_size,
&window_position);
API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window),
SWP_NOZORDER_SPECIFIED,
window_position.x, window_position.y,
0, 0,
SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE));
}
}
if (context->op == GDK_WIN32_DRAGOP_RESIZE ||
context->op == GDK_WIN32_DRAGOP_MOVE)
handle_aerosnap_move_resize (window, context, x, y);
gdk_surface_request_layout (window);
}
static void
@@ -4093,7 +4159,6 @@ gdk_win32_surface_minimize (GdkSurface *window)
static void
gdk_win32_surface_maximize (GdkSurface *window)
{
GdkWin32Surface *impl;
g_return_if_fail (GDK_IS_SURFACE (window));
@@ -4104,9 +4169,6 @@ gdk_win32_surface_maximize (GdkSurface *window)
GDK_SURFACE_HWND (window),
_gdk_win32_surface_state_to_string (window->state)));
impl = GDK_WIN32_SURFACE (window);
impl->resized = FALSE;
if (GDK_SURFACE_IS_MAPPED (window))
GtkShowWindow (window, SW_MAXIMIZE);
else
@@ -4251,6 +4313,61 @@ gdk_win32_surface_lookup_for_display (GdkDisplay *display,
return (GdkSurface*) gdk_win32_handle_table_lookup (anid);
}
void
gdk_win32_surface_set_opacity (GdkSurface *window,
double opacity)
{
LONG exstyle;
typedef BOOL (WINAPI *PFN_SetLayeredWindowAttributes) (HWND, COLORREF, BYTE, DWORD);
PFN_SetLayeredWindowAttributes setLayeredWindowAttributes = NULL;
GdkWin32Surface *impl;
g_return_if_fail (GDK_IS_SURFACE (window));
if (GDK_SURFACE_DESTROYED (window))
return;
if (opacity < 0)
opacity = 0;
else if (opacity > 1)
opacity = 1;
impl = GDK_WIN32_SURFACE (window);
if (impl->layered)
{
if (impl->layered_opacity != opacity)
{
RECT window_rect;
impl->layered_opacity = opacity;
_gdk_win32_get_window_client_area_rect (window, impl->surface_scale, &window_rect);
_gdk_win32_update_layered_window_from_cache (window, &window_rect, TRUE, TRUE, TRUE);
}
return;
}
exstyle = GetWindowLong (GDK_SURFACE_HWND (window), GWL_EXSTYLE);
if (!(exstyle & WS_EX_LAYERED))
SetWindowLong (GDK_SURFACE_HWND (window),
GWL_EXSTYLE,
exstyle | WS_EX_LAYERED);
setLayeredWindowAttributes =
(PFN_SetLayeredWindowAttributes)GetProcAddress (GetModuleHandle ("user32.dll"), "SetLayeredWindowAttributes");
if (setLayeredWindowAttributes)
{
API_CALL (setLayeredWindowAttributes, (GDK_SURFACE_HWND (window),
0,
opacity * 0xff,
LWA_ALPHA));
}
}
gboolean
gdk_win32_surface_is_win32 (GdkSurface *window)
{
@@ -4330,6 +4447,9 @@ GtkShowWindow (GdkSurface *window,
if (IsWindowVisible (hwnd))
break;
if ((WS_EX_LAYERED & GetWindowLongPtr (hwnd, GWL_EXSTYLE)) != WS_EX_LAYERED)
break;
/* Window was hidden, will be shown. Erase it, GDK will repaint soon,
* but not soon enough, so it's possible to see old content before
* the next redraw, unless we erase the window first.
@@ -4394,17 +4514,17 @@ gdk_win32_surface_set_shadow_width (GdkSurface *window,
"left %d, top %d, right %d, bottom %d\n",
window, left, top, right, bottom));
impl->zero_shadow = left == 0 && right == 0 && top == 0 && bottom == 0;
impl->zero_margins = left == 0 && right == 0 && top == 0 && bottom == 0;
if (impl->zero_shadow)
if (impl->zero_margins)
return;
impl->shadow.left = left;
impl->shadow.right = right * impl->surface_scale;
impl->shadow.top = top;
impl->shadow.bottom = bottom * impl->surface_scale;
impl->shadow_x = left + right;
impl->shadow_y = top + bottom;
impl->margins.left = left;
impl->margins.right = right * impl->surface_scale;
impl->margins.top = top;
impl->margins.bottom = bottom * impl->surface_scale;
impl->margins_x = left + right;
impl->margins_y = top + bottom;
}
@@ -4477,118 +4597,6 @@ gdk_win32_surface_set_input_region (GdkSurface *window,
*/
}
static void
compute_toplevel_size (GdkSurface *surface,
gboolean update_geometry,
int *width,
int *height)
{
GdkDisplay *display = gdk_surface_get_display (surface);
GdkMonitor *monitor;
GdkToplevelSize size;
int bounds_width, bounds_height;
GdkWin32Surface *impl = GDK_WIN32_SURFACE (surface);
monitor = gdk_display_get_monitor_at_surface (display, surface);
if (monitor)
{
GdkRectangle workarea;
gdk_win32_monitor_get_workarea (monitor, &workarea);
bounds_width = workarea.width;
bounds_height = workarea.height;
}
else
{
bounds_width = G_MAXINT;
bounds_height = G_MAXINT;
}
gdk_toplevel_size_init (&size, bounds_width, bounds_height);
gdk_toplevel_notify_compute_size (GDK_TOPLEVEL (surface), &size);
g_warn_if_fail (size.width > 0);
g_warn_if_fail (size.height > 0);
*width = size.width;
*height = size.height;
if (size.shadow.is_valid)
{
gdk_win32_surface_set_shadow_width (surface,
size.shadow.left,
size.shadow.right,
size.shadow.top,
size.shadow.bottom);
}
if (update_geometry)
{
GdkGeometry geometry;
GdkSurfaceHints mask;
GdkToplevelLayout *layout = impl->toplevel_layout;
if (gdk_toplevel_layout_get_resizable (layout))
{
geometry.min_width = size.min_width;
geometry.min_height = size.min_height;
mask = GDK_HINT_MIN_SIZE;
}
else
{
geometry.max_width = geometry.min_width = *width;
geometry.max_height = geometry.min_height = *height;
mask = GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE;
}
gdk_win32_surface_set_geometry_hints (surface, &geometry, mask);
gdk_surface_constrain_size (&geometry, mask, *width, *height, width, height);
}
}
static void
_gdk_win32_surface_request_layout (GdkSurface *surface)
{
GdkWin32Surface *impl = GDK_WIN32_SURFACE (surface);
int scale = impl->surface_scale;
RECT rect;
if (GDK_IS_TOPLEVEL (surface) && impl->resized)
{
surface->width = impl->next_layout.configured_width;
surface->height = impl->next_layout.configured_height;
}
else
{
_gdk_win32_get_window_rect (surface, &rect);
impl->unscaled_width = rect.right - rect.left;
impl->unscaled_height = rect.bottom - rect.top;
impl->next_layout.configured_width = (impl->unscaled_width + scale - 1) / scale;
impl->next_layout.configured_height = (impl->unscaled_height + scale - 1) / scale;
surface->x = rect.left / scale;
surface->y = rect.top / scale;
}
}
static gboolean
_gdk_win32_surface_compute_size (GdkSurface *surface)
{
GdkWin32Surface *impl = GDK_WIN32_SURFACE (surface);
int width, height;
if (GDK_IS_TOPLEVEL (surface))
compute_toplevel_size (surface, TRUE, &width, &height);
if (!impl->resized)
{
surface->width = impl->next_layout.configured_width;
surface->height = impl->next_layout.configured_height;
_gdk_surface_update_size (surface);
}
return FALSE;
}
static void
gdk_win32_surface_class_init (GdkWin32SurfaceClass *klass)
{
@@ -4615,8 +4623,6 @@ gdk_win32_surface_class_init (GdkWin32SurfaceClass *klass)
impl_class->create_gl_context = _gdk_win32_surface_create_gl_context;
impl_class->get_scale_factor = _gdk_win32_surface_get_scale_factor;
impl_class->get_unscaled_size = _gdk_win32_surface_get_unscaled_size;
impl_class->request_layout = _gdk_win32_surface_request_layout;
impl_class->compute_size = _gdk_win32_surface_compute_size;
}
HGDIOBJ
@@ -4906,18 +4912,75 @@ gdk_win32_toplevel_class_init (GdkWin32ToplevelClass *class)
}
static void
show_surface (GdkSurface *surface)
{
gboolean was_mapped;
if (surface->destroyed)
return;
was_mapped = GDK_SURFACE_IS_MAPPED (surface);
if (!was_mapped)
gdk_surface_set_is_mapped (surface, TRUE);
gdk_win32_surface_show (surface, FALSE);
if (!was_mapped)
gdk_surface_invalidate_rect (surface, NULL);
}
static gboolean
gdk_win32_toplevel_present (GdkToplevel *toplevel,
GdkToplevelLayout *layout)
{
GdkSurface *surface = GDK_SURFACE (toplevel);
GdkWin32Surface *impl = GDK_WIN32_SURFACE (surface);
GdkDisplay *display = gdk_surface_get_display (surface);
GdkMonitor *monitor;
GdkToplevelSize size;
int bounds_width, bounds_height;
int width, height;
GdkGeometry geometry;
GdkSurfaceHints mask;
gboolean maximize;
gboolean fullscreen;
g_clear_pointer (&impl->toplevel_layout, gdk_toplevel_layout_unref);
impl->toplevel_layout = gdk_toplevel_layout_copy (layout);
compute_toplevel_size (surface, FALSE, &width, &height);
monitor = gdk_display_get_monitor_at_surface (display, surface);
if (monitor)
{
GdkRectangle workarea;
gdk_win32_monitor_get_workarea (monitor, &workarea);
bounds_width = workarea.width;
bounds_height = workarea.height;
}
else
{
bounds_width = G_MAXINT;
bounds_height = G_MAXINT;
}
gdk_toplevel_size_init (&size, bounds_width, bounds_height);
gdk_toplevel_notify_compute_size (toplevel, &size);
g_warn_if_fail (size.width > 0);
g_warn_if_fail (size.height > 0);
width = size.width;
height = size.height;
if (gdk_toplevel_layout_get_resizable (layout))
{
geometry.min_width = size.min_width;
geometry.min_height = size.min_height;
mask = GDK_HINT_MIN_SIZE;
}
else
{
geometry.max_width = geometry.min_width = width;
geometry.max_height = geometry.min_height = height;
mask = GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE;
}
gdk_win32_surface_set_geometry_hints (surface, &geometry, mask);
gdk_surface_constrain_size (&geometry, mask, width, height, &width, &height);
gdk_win32_surface_resize (surface, width, height);
if (gdk_toplevel_layout_get_maximized (layout, &maximize))
@@ -4936,8 +4999,18 @@ gdk_win32_toplevel_present (GdkToplevel *toplevel,
gdk_win32_surface_unfullscreen (surface);
}
gdk_win32_surface_show (surface, FALSE);
maybe_notify_mapped (surface);
show_surface (surface);
if (size.shadow.is_valid)
{
gdk_win32_surface_set_shadow_width (surface,
size.shadow.left,
size.shadow.right,
size.shadow.top,
size.shadow.bottom);
}
return TRUE;
}
static gboolean
@@ -5020,9 +5093,8 @@ gdk_win32_drag_surface_present (GdkDragSurface *drag_surface,
{
GdkSurface *surface = GDK_SURFACE (drag_surface);
gdk_win32_surface_resize (surface, width, height);
gdk_win32_surface_show (surface, FALSE);
maybe_notify_mapped (surface);
gdk_win32_surface_resize (surface, width, height);
show_surface (surface);
return TRUE;
}
@@ -5113,6 +5185,7 @@ gdk_win32_surface_apply_queued_move_resize (GdkSurface *surface,
RECT
gdk_win32_surface_handle_queued_move_resize (GdkDrawContext *draw_context)
{
GdkWin32CairoContext *cairo_ctx = NULL;
GdkSurface *surface;
GdkWin32Surface *impl;
int scale;
@@ -5122,16 +5195,23 @@ gdk_win32_surface_handle_queued_move_resize (GdkDrawContext *draw_context)
impl = GDK_WIN32_SURFACE (surface);
scale = gdk_surface_get_scale_factor (surface);
if (GDK_IS_WIN32_CAIRO_CONTEXT (draw_context))
{
cairo_ctx = GDK_WIN32_CAIRO_CONTEXT (draw_context);
cairo_ctx->layered = impl->layered;
}
gdk_win32_surface_get_queued_window_rect (surface, scale, &queued_window_rect);
/* Apply queued resizes for non-double-buffered windows
/* Apply queued resizes for non-double-buffered and non-layered windows
* before painting them (we paint on the window DC directly,
* it must have the right size).
* Due to some poorly-undetstood issue delayed
* resizing of double-buffered windows can produce weird
* artefacts, so these are also resized before we paint.
*/
if (impl->drag_move_resize_context.native_move_resize_pending)
if (impl->drag_move_resize_context.native_move_resize_pending &&
(cairo_ctx == NULL || !cairo_ctx->layered))
{
impl->drag_move_resize_context.native_move_resize_pending = FALSE;
gdk_win32_surface_apply_queued_move_resize (surface, queued_window_rect);
+28 -14
View File
@@ -265,18 +265,25 @@ struct _GdkWin32Surface
int initial_y;
/* left/right/top/bottom width of the shadow/resize-grip around the window */
RECT shadow;
RECT margins;
/* left+right and top+bottom from @shadow */
int shadow_x;
int shadow_y;
/* left+right and top+bottom from @margins */
int margins_x;
int margins_y;
/* Set to TRUE when GTK tells us that shadow are 0 everywhere.
* We don't actually set shadow to 0, we just set this bit.
/* Set to TRUE when GTK tells us that margins are 0 everywhere.
* We don't actually set margins to 0, we just set this bit.
*/
guint zero_shadow : 1;
guint zero_margins : 1;
guint inhibit_configure : 1;
/* Set to TRUE if window is using true layered mode adjustments
* via UpdateLayeredWindow().
* Layered windows that get SetLayeredWindowAttributes() called
* on them are not true layered windows.
*/
guint layered : 1;
/* If TRUE, the @temp_styles is set to the styles that were temporarily
* added to this window.
*/
@@ -302,6 +309,12 @@ struct _GdkWin32Surface
int dib_width;
int dib_height;
/* If the client wants uniformly-transparent window,
* we remember the opacity value here and apply it
* during UpdateLayredWindow() call, for layered windows.
*/
double layered_opacity;
HDC hdc;
int hdc_count;
HBITMAP saved_dc_bitmap; /* Original bitmap for dc */
@@ -327,6 +340,9 @@ struct _GdkWin32Surface
/* Enable all decorations? */
gboolean decorate_all;
/* No. of windows to force layered windows off */
guint suppress_layered;
/* Temporary styles that this window got for the purpose of
* handling WM_SYSMENU.
* They are removed at the first opportunity (usually WM_INITMENU).
@@ -338,13 +354,6 @@ struct _GdkWin32Surface
int unscaled_width;
int unscaled_height;
GdkToplevelLayout *toplevel_layout;
struct {
int configured_width;
int configured_height;
} next_layout;
gboolean resized;
#ifdef GDK_WIN32_ENABLE_EGL
EGLSurface egl_surface;
EGLSurface egl_dummy_surface;
@@ -366,6 +375,11 @@ int _gdk_win32_surface_get_scale_factor (GdkSurface *window);
void _gdk_win32_get_window_client_area_rect (GdkSurface *window,
int scale,
RECT *rect);
void _gdk_win32_update_layered_window_from_cache (GdkSurface *window,
RECT *client_rect,
gboolean do_move,
gboolean do_resize,
gboolean do_paint);
void gdk_win32_surface_move (GdkSurface *surface,
int x,
+6
View File
@@ -63,6 +63,12 @@ gdk_win32_vulkan_context_create_surface (GdkVulkanContext *context,
NULL,
surface);
if (result == VK_SUCCESS)
win32_surface->suppress_layered ++;
if (win32_surface->suppress_layered == 1)
_gdk_win32_surface_update_style_bits (window);
return result;
}
+5 -6
View File
@@ -1097,16 +1097,15 @@ handle_property_change (GdkX11DeviceManagerXI2 *device_manager,
if (ev->what != XIPropertyDeleted &&
device_get_tool_serial_and_id (device, &serial_id, &tool_id))
{
GdkDeviceToolType tool_type;
seat = gdk_device_get_seat (device);
tool_type = device_get_tool_type (device);
tool = gdk_seat_get_tool (seat, serial_id, tool_id);
if (tool_type != GDK_DEVICE_TOOL_TYPE_UNKNOWN)
if (!tool && serial_id > 0)
{
tool = gdk_seat_get_tool (seat, serial_id, tool_id, tool_type);
GdkDeviceToolType tool_type;
if (!tool && serial_id > 0)
tool_type = device_get_tool_type (device);
if (tool_type != GDK_DEVICE_TOOL_TYPE_UNKNOWN)
{
tool = gdk_device_tool_new (serial_id, tool_id, tool_type, 0);
gdk_seat_default_add_tool (GDK_SEAT_DEFAULT (seat), tool);
+5 -46
View File
@@ -39,7 +39,6 @@
#include "gdkscreen-x11.h"
#include "gdkselectioninputstream-x11.h"
#include "gdkselectionoutputstream-x11.h"
#include "gdktextlistconverter-x11.h"
#include <X11/Xlib.h>
#include <X11/Xutil.h>
@@ -104,49 +103,6 @@ static const struct {
G_DEFINE_TYPE (GdkX11Drop, gdk_x11_drop, GDK_TYPE_DROP)
static GInputStream *
text_list_convert (GdkDisplay *display,
GInputStream *stream,
const char *encoding,
int format)
{
GInputStream *converter_stream;
GConverter *converter;
converter = gdk_x11_text_list_converter_to_utf8_new (display, encoding, format);
converter_stream = g_converter_input_stream_new (stream, converter);
g_object_unref (converter);
g_object_unref (stream);
return converter_stream;
}
static GInputStream *
no_convert (GdkDisplay *display,
GInputStream *stream,
const char *encoding,
int format)
{
return stream;
}
static const struct {
const char *x_target;
const char *mime_type;
GInputStream * (* convert) (GdkDisplay *, GInputStream *, const char *, int);
const char *type;
int format;
} special_targets[] = {
{ "UTF8_STRING", "text/plain;charset=utf-8", no_convert, "UTF8_STRING", 8 },
{ "COMPOUND_TEXT", "text/plain;charset=utf-8", text_list_convert, "COMPOUND_TEXT", 8 },
{ "TEXT", "text/plain;charset=utf-8", text_list_convert, "STRING", 8 },
{ "STRING", "text/plain;charset=utf-8", text_list_convert, "STRING", 8 },
{ "TARGETS", NULL, NULL, "ATOM", 32 },
{ "TIMESTAMP", NULL, NULL, "INTEGER", 32 },
{ "SAVE_TARGETS", NULL, NULL, "NULL", 32 }
};
static void
gdk_x11_drop_read_got_stream (GObject *source,
GAsyncResult *res,
@@ -189,9 +145,9 @@ gdk_x11_drop_read_got_stream (GObject *source,
}
else
{
#if 0
gsize i;
const char *mime_type = ((GSList *) g_task_get_task_data (task))->data;
GdkDrop *drop = GDK_DROP (g_task_get_source_object (task));
for (i = 0; i < G_N_ELEMENTS (special_targets); i++)
{
@@ -199,12 +155,15 @@ gdk_x11_drop_read_got_stream (GObject *source,
{
g_assert (special_targets[i].mime_type != NULL);
GDK_DISPLAY_NOTE (CLIPBOARD, g_printerr ("%s: reading with converter from %s to %s\n",
cb->selection, mime_type, special_targets[i].mime_type));
mime_type = g_intern_string (special_targets[i].mime_type);
g_task_set_task_data (task, g_slist_prepend (NULL, (gpointer) mime_type), (GDestroyNotify) g_slist_free);
stream = special_targets[i].convert (gdk_drop_get_display (drop), stream, type, format);
stream = special_targets[i].convert (cb, stream, type, format);
break;
}
}
#endif
GDK_NOTE (DND, g_printerr ("reading DND as %s now\n",
(const char *)((GSList *) g_task_get_task_data (task))->data));
+1 -1
View File
@@ -1383,7 +1383,7 @@ gdk_x11_keymap_translate_keyboard_state (GdkKeymap *keymap,
tmp_keyval = translate_keysym (keymap_x11, hardware_keycode,
group, state,
effective_group, level);
level, effective_group);
}
if (consumed_modifiers)
+1 -3
View File
@@ -291,7 +291,7 @@ compute_toplevel_size (GdkSurface *surface,
GdkGeometry geometry;
GdkSurfaceHints mask;
if (!impl->toplevel_layout || gdk_toplevel_layout_get_resizable (impl->toplevel_layout))
if (gdk_toplevel_layout_get_resizable (impl->toplevel_layout))
{
geometry.min_width = size.min_width;
geometry.min_height = size.min_height;
@@ -2013,8 +2013,6 @@ _gdk_x11_surface_set_surface_scale (GdkSurface *surface,
surface->height * impl->surface_scale);
gdk_surface_invalidate_rect (surface, NULL);
g_object_notify (G_OBJECT (surface), "scale-factor");
}
void
+4
View File
@@ -269,6 +269,8 @@ collect_reused_child_nodes (GskRenderer *renderer,
case GSK_BLEND_NODE:
case GSK_CROSS_FADE_NODE:
case GSK_BLUR_NODE:
case GSK_FILL_NODE:
case GSK_STROKE_NODE:
default:
@@ -855,6 +857,8 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer,
case GSK_CROSS_FADE_NODE:
case GSK_BLUR_NODE:
case GSK_GL_SHADER_NODE:
case GSK_FILL_NODE:
case GSK_STROKE_NODE:
default:
break; /* Fallback */
}
+3 -3
View File
@@ -149,10 +149,10 @@ render_glyph (GlyphCacheKey *key,
glyph_info.glyph = key->data.glyph;
glyph_info.geometry.width = value->draw_width * 1024;
if (glyph_info.glyph & PANGO_GLYPH_UNKNOWN_FLAG)
glyph_info.geometry.x_offset = 250 * key->data.xshift;
glyph_info.geometry.x_offset = 0;
else
glyph_info.geometry.x_offset = 250 * key->data.xshift - value->draw_x * 1024;
glyph_info.geometry.y_offset = 250 * key->data.yshift - value->draw_y * 1024;
glyph_info.geometry.x_offset = - value->draw_x * 1024;
glyph_info.geometry.y_offset = - value->draw_y * 1024;
glyph_string.num_glyphs = 1;
glyph_string.glyphs = &glyph_info;
+58 -57
View File
@@ -1452,7 +1452,6 @@ render_linear_gradient_node (GskGLRenderer *self,
ops_set_linear_gradient (builder,
n_color_stops,
stops,
gsk_render_node_get_node_type (node) == GSK_REPEATING_LINEAR_GRADIENT_NODE,
builder->dx + start->x,
builder->dy + start->y,
builder->dx + end->x,
@@ -1486,7 +1485,6 @@ render_radial_gradient_node (GskGLRenderer *self,
ops_set_radial_gradient (builder,
n_color_stops,
stops,
gsk_render_node_get_node_type (node) == GSK_REPEATING_RADIAL_GRADIENT_NODE,
builder->dx + center->x,
builder->dy + center->y,
start, end,
@@ -1512,7 +1510,7 @@ render_conic_gradient_node (GskGLRenderer *self,
{
const GskColorStop *stops = gsk_conic_gradient_node_get_color_stops (node, NULL);
const graphene_point_t *center = gsk_conic_gradient_node_get_center (node);
const float angle = gsk_conic_gradient_node_get_angle (node);
const float rotation = gsk_conic_gradient_node_get_rotation (node);
ops_set_program (builder, &self->programs->conic_gradient_program);
ops_set_conic_gradient (builder,
@@ -1520,7 +1518,7 @@ render_conic_gradient_node (GskGLRenderer *self,
stops,
builder->dx + center->x,
builder->dy + center->y,
angle);
rotation);
load_vertex_data (ops_draw (builder, NULL), &node->bounds, builder);
}
@@ -1556,7 +1554,7 @@ rounded_inner_rect_contains_rect (const GskRoundedRect *rounded,
MAX (rounded->corner[GSK_CORNER_BOTTOM_LEFT].height,
rounded->corner[GSK_CORNER_BOTTOM_RIGHT].height);
return _graphene_rect_contains_rect (&inner, rect);
return graphene_rect_contains_rect (&inner, rect);
}
/* Current clip is NOT rounded but new one is definitely! */
@@ -1646,8 +1644,8 @@ render_clipped_child (GskGLRenderer *self,
/* well fuck */
const float scale_x = builder->scale_x;
const float scale_y = builder->scale_y;
const GskRoundedRect scaled_clip = GSK_ROUNDED_RECT_INIT ((builder->dx + clip->origin.x) * scale_x,
(builder->dy + clip->origin.y) * scale_y,
const GskRoundedRect scaled_clip = GSK_ROUNDED_RECT_INIT (clip->origin.x * scale_x,
clip->origin.y * scale_y,
clip->size.width * scale_x,
clip->size.height * scale_y);
gboolean is_offscreen;
@@ -1748,6 +1746,7 @@ render_rounded_clip_node (GskGLRenderer *self,
}
else
{
GskRoundedRect scaled_clip;
gboolean is_offscreen;
TextureRegion region;
/* NOTE: We are *not* transforming the clip by the current modelview here.
@@ -1756,7 +1755,19 @@ render_rounded_clip_node (GskGLRenderer *self,
*
* We do, however, apply the scale factor to the child clip of course.
*/
ops_push_clip (builder, &transformed_clip);
scaled_clip.bounds.origin.x = clip->bounds.origin.x * scale_x;
scaled_clip.bounds.origin.y = clip->bounds.origin.y * scale_y;
scaled_clip.bounds.size.width = clip->bounds.size.width * scale_x;
scaled_clip.bounds.size.height = clip->bounds.size.height * scale_y;
/* Increase corner radius size by scale factor */
for (i = 0; i < 4; i ++)
{
scaled_clip.corner[i].width = clip->corner[i].width * scale_x;
scaled_clip.corner[i].height = clip->corner[i].height * scale_y;
}
ops_push_clip (builder, &scaled_clip);
if (!add_offscreen_ops (self, builder, &node->bounds,
child,
&region, &is_offscreen,
@@ -1768,8 +1779,7 @@ render_rounded_clip_node (GskGLRenderer *self,
ops_set_program (builder, &self->programs->blit_program);
ops_set_texture (builder, region.texture_id);
load_vertex_data_with_region (ops_draw (builder, NULL), &node->bounds, builder,
&region, is_offscreen);
load_offscreen_vertex_data (ops_draw (builder, NULL), node, builder);
}
}
@@ -3043,19 +3053,14 @@ apply_linear_gradient_op (const Program *program,
op->n_color_stops.value * 5,
(float *)op->color_stops.value);
glUniform4f (program->linear_gradient.points_location,
op->start_point[0], op->start_point[1],
op->end_point[0] - op->start_point[0], op->end_point[1] - op->start_point[1]);
glUniform1i (program->linear_gradient.repeat_location, op->repeat);
glUniform2f (program->linear_gradient.start_point_location, op->start_point[0], op->start_point[1]);
glUniform2f (program->linear_gradient.end_point_location, op->end_point[0], op->end_point[1]);
}
static inline void
apply_radial_gradient_op (const Program *program,
const OpRadialGradient *op)
{
float scale;
float bias;
OP_PRINT (" -> Radial gradient");
if (op->n_color_stops.send)
glUniform1i (program->radial_gradient.num_color_stops_location, op->n_color_stops.value);
@@ -3065,23 +3070,16 @@ apply_radial_gradient_op (const Program *program,
op->n_color_stops.value * 5,
(float *)op->color_stops.value);
scale = 1.0f / (op->end - op->start);
bias = -op->start * scale;
glUniform1i (program->radial_gradient.repeat_location, op->repeat);
glUniform2f (program->radial_gradient.range_location, scale, bias);
glUniform4f (program->radial_gradient.geometry_location,
op->center[0], op->center[1],
1.0f / op->radius[0], 1.0f / op->radius[1]);
glUniform1f (program->radial_gradient.start_location, op->start);
glUniform1f (program->radial_gradient.end_location, op->end);
glUniform2f (program->radial_gradient.radius_location, op->radius[0], op->radius[1]);
glUniform2f (program->radial_gradient.center_location, op->center[0], op->center[1]);
}
static inline void
apply_conic_gradient_op (const Program *program,
const OpConicGradient *op)
{
float bias;
float scale;
OP_PRINT (" -> Conic gradient");
if (op->n_color_stops.send)
glUniform1i (program->conic_gradient.num_color_stops_location, op->n_color_stops.value);
@@ -3091,9 +3089,8 @@ apply_conic_gradient_op (const Program *program,
op->n_color_stops.value * 5,
(float *)op->color_stops.value);
scale = 0.5f * M_1_PI;
bias = op->angle * scale + 2.0f;
glUniform4f (program->conic_gradient.geometry_location, op->center[0], op->center[1], scale, bias);
glUniform1f (program->conic_gradient.rotation_location, op->rotation);
glUniform2f (program->conic_gradient.center_location, op->center[0], op->center[1]);
}
static inline void
@@ -3383,20 +3380,22 @@ gsk_gl_renderer_create_programs (GskGLRenderer *self,
/* linear gradient */
INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient, color_stops);
INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient, num_color_stops);
INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient, repeat);
INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient, points);
INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient, start_point);
INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient, end_point);
/* radial gradient */
INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, color_stops);
INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, num_color_stops);
INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, repeat);
INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, geometry);
INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, range);
INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, center);
INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, start);
INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, end);
INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, radius);
/* conic gradient */
INIT_PROGRAM_UNIFORM_LOCATION (conic_gradient, color_stops);
INIT_PROGRAM_UNIFORM_LOCATION (conic_gradient, num_color_stops);
INIT_PROGRAM_UNIFORM_LOCATION (conic_gradient, geometry);
INIT_PROGRAM_UNIFORM_LOCATION (conic_gradient, center);
INIT_PROGRAM_UNIFORM_LOCATION (conic_gradient, rotation);
/* blur */
INIT_PROGRAM_UNIFORM_LOCATION (blur, blur_radius);
@@ -3742,12 +3741,10 @@ gsk_gl_renderer_add_render_ops (GskGLRenderer *self,
break;
case GSK_LINEAR_GRADIENT_NODE:
case GSK_REPEATING_LINEAR_GRADIENT_NODE:
render_linear_gradient_node (self, node, builder);
break;
case GSK_RADIAL_GRADIENT_NODE:
case GSK_REPEATING_RADIAL_GRADIENT_NODE:
render_radial_gradient_node (self, node, builder);
break;
@@ -3814,6 +3811,10 @@ gsk_gl_renderer_add_render_ops (GskGLRenderer *self,
render_gl_shader_node (self, node, builder);
break;
case GSK_REPEATING_LINEAR_GRADIENT_NODE:
case GSK_REPEATING_RADIAL_GRADIENT_NODE:
case GSK_FILL_NODE:
case GSK_STROKE_NODE:
case GSK_CAIRO_NODE:
default:
{
@@ -3831,9 +3832,9 @@ add_offscreen_ops (GskGLRenderer *self,
gboolean *is_offscreen,
guint flags)
{
float width, height;
const float dx = builder->dx;
const float dy = builder->dy;
float scaled_width, scaled_height;
float scale_x;
float scale_y;
int render_target;
@@ -3889,6 +3890,8 @@ add_offscreen_ops (GskGLRenderer *self,
return TRUE;
}
width = bounds->size.width;
height = bounds->size.height;
scale_x = builder->scale_x;
scale_y = builder->scale_y;
@@ -3899,23 +3902,23 @@ add_offscreen_ops (GskGLRenderer *self,
{
const int max_texture_size = gsk_gl_driver_get_max_texture_size (self->gl_driver);
scaled_width = ceilf (bounds->size.width * scale_x);
if (scaled_width > max_texture_size)
width = ceilf (width * scale_x);
if (width > max_texture_size)
{
scale_x *= (float)max_texture_size / scaled_width;
scaled_width = max_texture_size;
scale_x *= (float)max_texture_size / width;
width = max_texture_size;
}
scaled_height = ceilf (bounds->size.height * scale_y);
if (scaled_height > max_texture_size)
height = ceilf (height * scale_y);
if (height > max_texture_size)
{
scale_y *= (float)max_texture_size / scaled_height;
scaled_height = max_texture_size;
scale_y *= (float)max_texture_size / height;
height = max_texture_size;
}
}
gsk_gl_driver_create_render_target (self->gl_driver,
scaled_width, scaled_height,
width, height,
filter, filter,
&texture_id, &render_target);
if (gdk_gl_context_has_debug (self->gl_context))
@@ -3930,11 +3933,9 @@ add_offscreen_ops (GskGLRenderer *self,
render_target);
}
ops_transform_bounds_modelview (builder, bounds, &viewport);
/* Code above will scale the size with the scale we use in the render ops,
* but for the viewport size, we need our own size limited by the texture size */
viewport.size.width = scaled_width;
viewport.size.height = scaled_height;
viewport = GRAPHENE_RECT_INIT (bounds->origin.x * scale_x,
bounds->origin.y * scale_y,
width, height);
init_projection_matrix (&item_proj, &viewport);
prev_render_target = ops_set_render_target (builder, render_target);
@@ -3946,8 +3947,8 @@ add_offscreen_ops (GskGLRenderer *self,
if (flags & RESET_CLIP)
ops_push_clip (builder, &GSK_ROUNDED_RECT_INIT_FROM_RECT (viewport));
builder->dx = dx;
builder->dy = dy;
builder->dx = 0;
builder->dy = 0;
prev_opacity = ops_set_opacity (builder, 1.0);
@@ -3962,7 +3963,7 @@ add_offscreen_ops (GskGLRenderer *self,
g_type_name_from_instance ((GTypeInstance *) child_node),
child_node,
k ++),
scaled_width, scaled_height);
width, height);
}
#endif
+2 -6
View File
@@ -859,7 +859,6 @@ void
ops_set_linear_gradient (RenderOpBuilder *self,
guint n_color_stops,
const GskColorStop *color_stops,
gboolean repeat,
float start_x,
float start_y,
float end_x,
@@ -913,7 +912,6 @@ ops_set_linear_gradient (RenderOpBuilder *self,
sizeof (GskColorStop) * real_n_color_stops);
}
op->repeat = repeat;
op->start_point[0] = start_x;
op->start_point[1] = start_y;
op->end_point[0] = end_x;
@@ -924,7 +922,6 @@ void
ops_set_radial_gradient (RenderOpBuilder *self,
guint n_color_stops,
const GskColorStop *color_stops,
gboolean repeat,
float center_x,
float center_y,
float start,
@@ -948,7 +945,6 @@ ops_set_radial_gradient (RenderOpBuilder *self,
op->radius[1] = vradius;
op->start = start;
op->end = end;
op->repeat = repeat;
}
void
@@ -957,7 +953,7 @@ ops_set_conic_gradient (RenderOpBuilder *self,
const GskColorStop *color_stops,
float center_x,
float center_y,
float angle)
float rotation)
{
const guint real_n_color_stops = MIN (GL_MAX_GRADIENT_STOPS, n_color_stops);
OpConicGradient *op;
@@ -971,6 +967,6 @@ ops_set_conic_gradient (RenderOpBuilder *self,
op->color_stops.send = true;
op->center[0] = center_x;
op->center[1] = center_y;
op->angle = angle;
op->rotation = rotation;
}
+9 -9
View File
@@ -118,20 +118,22 @@ struct _Program
struct {
int num_color_stops_location;
int color_stops_location;
int points_location;
int repeat_location;
int start_point_location;
int end_point_location;
} linear_gradient;
struct {
int num_color_stops_location;
int color_stops_location;
int geometry_location;
int range_location;
int repeat_location;
int center_location;
int start_location;
int end_location;
int radius_location;
} radial_gradient;
struct {
int num_color_stops_location;
int color_stops_location;
int geometry_location;
int center_location;
int rotation_location;
} conic_gradient;
struct {
int blur_radius_location;
@@ -317,7 +319,6 @@ void ops_set_unblurred_outset_shadow (RenderOpBuilder *se
void ops_set_linear_gradient (RenderOpBuilder *self,
guint n_color_stops,
const GskColorStop *color_stops,
gboolean repeat,
float start_x,
float start_y,
float end_x,
@@ -325,7 +326,6 @@ void ops_set_linear_gradient (RenderOpBuilder *self,
void ops_set_radial_gradient (RenderOpBuilder *self,
guint n_color_stops,
const GskColorStop *color_stops,
gboolean repeat,
float center_x,
float center_y,
float start,
@@ -337,7 +337,7 @@ void ops_set_conic_gradient (RenderOpBuilder *self,
const GskColorStop *color_stops,
float center_x,
float center_y,
float angle);
float rotation);
GskQuadVertex * ops_draw (RenderOpBuilder *builder,
const GskQuadVertex vertex_data[GL_N_VERTICES]);
+12 -13
View File
@@ -64,10 +64,8 @@ prepend_line_numbers (char *code,
}
static gboolean
check_shader_error (int shader_id,
int shader_type,
const char *resource_path,
GError **error)
check_shader_error (int shader_id,
GError **error)
{
int status;
int log_len;
@@ -93,9 +91,7 @@ check_shader_error (int shader_id,
prepend_line_numbers (code, s);
g_set_error (error, GDK_GL_ERROR, GDK_GL_ERROR_COMPILATION_FAILED,
"Compilation failure in %s shader %s.\nSource Code:\n%s\n\nError Message:\n%s\n\n",
(shader_type == GL_FRAGMENT_SHADER ? "fragment" : "vertex"),
resource_path,
"Compilation failure in shader.\nSource Code: %s\n\nError Message:\n%s\n\n",
s->str,
buffer);
@@ -188,7 +184,7 @@ gsk_gl_shader_builder_create_program (GskGLShaderBuilder *self,
});
glCompileShader (vertex_id);
if (!check_shader_error (vertex_id, GL_VERTEX_SHADER, resource_path, error))
if (!check_shader_error (vertex_id, error))
{
glDeleteShader (vertex_id);
goto out;
@@ -222,22 +218,20 @@ gsk_gl_shader_builder_create_program (GskGLShaderBuilder *self,
});
glCompileShader (fragment_id);
if (!check_shader_error (fragment_id, GL_FRAGMENT_SHADER, resource_path, error))
if (!check_shader_error (fragment_id, error))
{
glDeleteShader (fragment_id);
goto out;
}
print_shader_info ("Fragment shader", fragment_id, resource_path);
print_shader_info ("Fragment shader", vertex_id, resource_path);
program_id = glCreateProgram ();
glAttachShader (program_id, vertex_id);
glAttachShader (program_id, fragment_id);
glBindAttribLocation (program_id, 0, "aPosition");
glBindAttribLocation (program_id, 1, "aUv");
glBindAttribLocation (program_id, 1, "vUv");
glLinkProgram (program_id);
glDetachShader (program_id, vertex_id);
glDetachShader (program_id, fragment_id);
glGetProgramiv (program_id, GL_LINK_STATUS, &status);
if (status == GL_FALSE)
@@ -258,9 +252,14 @@ gsk_gl_shader_builder_create_program (GskGLShaderBuilder *self,
glDeleteProgram (program_id);
program_id = -1;
goto out;
}
glDetachShader (program_id, vertex_id);
glDeleteShader (vertex_id);
glDetachShader (program_id, fragment_id);
glDeleteShader (fragment_id);
out:
+1 -3
View File
@@ -146,7 +146,6 @@ typedef struct
IntUniformValue n_color_stops;
float start_point[2];
float end_point[2];
gboolean repeat;
} OpLinearGradient;
typedef struct
@@ -157,7 +156,6 @@ typedef struct
float end;
float radius[2];
float center[2];
gboolean repeat;
} OpRadialGradient;
typedef struct
@@ -165,7 +163,7 @@ typedef struct
ColorStopUniformValue color_stops;
IntUniformValue n_color_stops;
float center[2];
float angle;
float rotation;
} OpConicGradient;
typedef struct
+2
View File
@@ -22,6 +22,8 @@
#ifndef __GI_SCANNER__
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskPath, gsk_path_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskPathMeasure, gsk_path_measure_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskRenderer, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskRenderNode, gsk_render_node_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskTransform, gsk_transform_unref)
+5 -1
View File
@@ -21,11 +21,15 @@
#define __GSK_H_INSIDE__
#include <gsk/gskenums.h>
#include <gsk/gskglshader.h>
#include <gsk/gskpath.h>
#include <gsk/gskpathbuilder.h>
#include <gsk/gskpathmeasure.h>
#include <gsk/gskrenderer.h>
#include <gsk/gskrendernode.h>
#include <gsk/gskroundedrect.h>
#include <gsk/gskstroke.h>
#include <gsk/gsktransform.h>
#include <gsk/gskglshader.h>
#include <gsk/gskcairorenderer.h>
+1962
View File
File diff suppressed because it is too large Load Diff
+124
View File
@@ -0,0 +1,124 @@
/*
* Copyright © 2020 Benjamin Otte
*
* 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.1 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/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GSK_CONTOUR_PRIVATE_H__
#define __GSK_CONTOUR_PRIVATE_H__
#include <gskpath.h>
#include "gskpathopprivate.h"
G_BEGIN_DECLS
typedef enum
{
GSK_PATH_FLAT,
GSK_PATH_CLOSED
} GskPathFlags;
typedef struct _GskContour GskContour;
GskContour * gsk_rect_contour_new (const graphene_rect_t *rect);
GskContour * gsk_circle_contour_new (const graphene_point_t *center,
float radius,
float start_angle,
float end_angle);
GskContour * gsk_standard_contour_new (GskPathFlags flags,
const graphene_point_t *points,
gsize n_points,
const gskpathop *ops,
gsize n_ops,
gssize offset);
void gsk_contour_copy (GskContour * dest,
const GskContour *src);
GskContour * gsk_contour_dup (const GskContour *src);
gsize gsk_contour_get_size (const GskContour *self);
GskPathFlags gsk_contour_get_flags (const GskContour *self);
void gsk_contour_print (const GskContour *self,
GString *string);
gboolean gsk_contour_get_bounds (const GskContour *self,
graphene_rect_t *bounds);
gpointer gsk_contour_init_measure (const GskContour *self,
float tolerance,
float *out_length);
void gsk_contour_free_measure (const GskContour *self,
gpointer data);
gboolean gsk_contour_foreach (const GskContour *self,
float tolerance,
GskPathForeachFunc func,
gpointer user_data);
void gsk_contour_get_start_end (const GskContour *self,
graphene_point_t *start,
graphene_point_t *end);
void gsk_contour_get_point (const GskContour *self,
gpointer measure_data,
float distance,
graphene_point_t *pos,
graphene_vec2_t *tangent);
float gsk_contour_get_curvature (const GskContour *self,
gpointer measure_data,
float distance,
graphene_point_t *center);
gboolean gsk_contour_get_closest_point (const GskContour *self,
gpointer measure_data,
float tolerance,
const graphene_point_t *point,
float threshold,
float *out_distance,
graphene_point_t *out_pos,
float *out_offset,
graphene_vec2_t *out_tangent);
int gsk_contour_get_winding (const GskContour *self,
gpointer measure_data,
const graphene_point_t *point,
gboolean *on_edge);
void gsk_contour_add_segment (const GskContour *self,
GskPathBuilder *builder,
gpointer measure_data,
gboolean emit_move_to,
float start,
float end);
gboolean gsk_contour_get_stroke_bounds (const GskContour *self,
const GskStroke *stroke,
graphene_rect_t *bounds);
void gsk_contour_add_stroke (const GskContour *contour,
GskPathBuilder *builder,
GskStroke *stroke);
void gsk_contour_default_add_stroke (const GskContour *contour,
GskPathBuilder *builder,
GskStroke *stroke);
void gsk_contour_offset (const GskContour *contour,
GskPathBuilder *builder,
float distance,
GskLineJoin line_join,
float miter_limit);
void gsk_contour_default_offset (const GskContour *contour,
GskPathBuilder *builder,
float distance,
GskLineJoin line_join,
float miter_limit);
G_END_DECLS
#endif /* __GSK_CONTOUR_PRIVATE_H__ */
+1805
View File
File diff suppressed because it is too large Load Diff
+459
View File
@@ -0,0 +1,459 @@
/*
* 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.1 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/>.
*
* Authors: Matthias Clasen <mclasen@redhat.com>
*/
#include "config.h"
#include "gskcurveprivate.h"
static inline gboolean
acceptable (float t)
{
return 0 <= t && t <= 1;
}
static int
line_intersect (const GskCurve *curve1,
const GskCurve *curve2,
float *t1,
float *t2,
graphene_point_t *p)
{
const graphene_point_t *pts1 = curve1->line.points;
const graphene_point_t *pts2 = curve2->line.points;
float a1 = pts1[0].x - pts1[1].x;
float b1 = pts1[0].y - pts1[1].y;
float a2 = pts2[0].x - pts2[1].x;
float b2 = pts2[0].y - pts2[1].y;
float det = a1 * b2 - b1 * a2;
if (det != 0)
{
float tt = ((pts1[0].x - pts2[0].x) * b2 - (pts1[0].y - pts2[0].y) * a2) / det;
float ss = - ((pts1[0].y - pts2[0].y) * a1 - (pts1[0].x - pts2[0].x) * b1) / det;
if (acceptable (tt) && acceptable (ss))
{
p->x = pts1[0].x + tt * (pts1[1].x - pts1[0].x);
p->y = pts1[0].y + tt * (pts1[1].y - pts1[0].y);
*t1 = tt;
*t2 = ss;
return 1;
}
}
return 0;
}
static void
get_tangent (const graphene_point_t *p0,
const graphene_point_t *p1,
graphene_vec2_t *t)
{
graphene_vec2_init (t, p1->x - p0->x, p1->y - p0->y);
graphene_vec2_normalize (t, t);
}
static void
align_points (const graphene_point_t *p,
const graphene_point_t *a,
const graphene_point_t *b,
graphene_point_t *q,
int n)
{
graphene_vec2_t n1;
float angle;
float s, c;
get_tangent (a, b, &n1);
angle = - atan2 (graphene_vec2_get_y (&n1), graphene_vec2_get_x (&n1));
sincosf (angle, &s, &c);
for (int i = 0; i < n; i++)
{
q[i].x = (p[i].x - a->x) * c - (p[i].y - a->y) * s;
q[i].y = (p[i].x - a->x) * s + (p[i].y - a->y) * c;
}
}
static void
find_point_on_line (const graphene_point_t *p1,
const graphene_point_t *p2,
const graphene_point_t *q,
float *t)
{
float tx = p2->x - p1->x;
float ty = p2->y - p1->y;
float sx = q->x - p1->x;
float sy = q->y - p1->y;
*t = (tx*sx + ty*sy) / (tx*tx + ty*ty);
}
static float
cuberoot (float v)
{
if (v < 0)
return -pow (-v, 1.f / 3);
return pow (v, 1.f / 3);
}
/* Solve P = 0 where P is
* P = (1-t)^3*pa + 3*t*(1-t)^2*pb + 3*t^2*(1-t)*pc + t^3*pd
*/
static int
get_cubic_roots (float pa, float pb, float pc, float pd, float roots[3])
{
float a, b, c, d;
float q, q2;
float p, p3;
float discriminant;
float u1, v1, sd;
int n_roots = 0;
d = -pa + 3*pb - 3*pc + pd;
a = 3*pa - 6*pb + 3*pc;
b = -3*pa + 3*pb;
c = pa;
if (fabs (d) < 0.0001)
{
if (fabs (a) < 0.0001)
{
if (fabs (b) < 0.0001)
return 0;
if (acceptable (-c / b))
{
roots[0] = -c / b;
return 1;
}
return 0;
}
q = sqrt (b*b - 4*a*c);
roots[n_roots] = (-b + q) / (2 * a);
if (acceptable (roots[n_roots]))
n_roots++;
roots[n_roots] = (-b - q) / (2 * a);
if (acceptable (roots[n_roots]))
n_roots++;
return n_roots;
}
a /= d;
b /= d;
c /= d;
p = (3*b - a*a)/3;
p3 = p/3;
q = (2*a*a*a - 9*a*b + 27*c)/27;
q2 = q/2;
discriminant = q2*q2 + p3*p3*p3;
if (discriminant < 0)
{
float mp3 = -p/3;
float mp33 = mp3*mp3*mp3;
float r = sqrt (mp33);
float t = -q / (2*r);
float cosphi = t < -1 ? -1 : (t > 1 ? 1 : t);
float phi = acos (cosphi);
float crtr = cuberoot (r);
float t1 = 2*crtr;
roots[n_roots] = t1 * cos (phi/3) - a/3;
if (acceptable (roots[n_roots]))
n_roots++;
roots[n_roots] = t1 * cos ((phi + 2*M_PI) / 3) - a/3;
if (acceptable (roots[n_roots]))
n_roots++;
roots[n_roots] = t1 * cos ((phi + 4*M_PI) / 3) - a/3;
if (acceptable (roots[n_roots]))
n_roots++;
return n_roots;
}
if (discriminant == 0)
{
u1 = q2 < 0 ? cuberoot (-q2) : -cuberoot (q2);
roots[n_roots] = 2*u1 - a/3;
if (acceptable (roots[n_roots]))
n_roots++;
roots[n_roots] = -u1 - a/3;
if (acceptable (roots[n_roots]))
n_roots++;
return n_roots;
}
sd = sqrt (discriminant);
u1 = cuberoot (sd - q2);
v1 = cuberoot (sd + q2);
roots[n_roots] = u1 - v1 - a/3;
if (acceptable (roots[n_roots]))
n_roots++;
return n_roots;
}
static int
line_curve_intersect (const GskCurve *curve1,
const GskCurve *curve2,
float *t1,
float *t2,
graphene_point_t *p,
int n)
{
const graphene_point_t *a = &curve1->line.points[0];
const graphene_point_t *b = &curve1->line.points[1];
graphene_point_t pts[4];
float t[3];
int m, i;
/* Rotate things to place curve1 on the x axis,
* then solve curve2 for y == 0.
*/
align_points (curve2->curve.points, a, b, pts, 4);
m = get_cubic_roots (pts[0].y, pts[1].y, pts[2].y, pts[3].y, t);
m = MIN (m, n);
for (i = 0; i < m; i++)
{
t2[i] = t[i];
gsk_curve_get_point (curve2, t[i], &p[i]);
find_point_on_line (a, b, &p[i], &t1[i]);
}
return m;
}
static void
curve_intersect_recurse (const GskCurve *curve1,
const GskCurve *curve2,
float t1l,
float t1r,
float t2l,
float t2r,
float *t1,
float *t2,
graphene_point_t *p,
int n,
int *pos)
{
GskCurve p11, p12, p21, p22;
graphene_rect_t b1, b2;
float d1, d2;
if (*pos == n)
return;
gsk_curve_get_tight_bounds (curve1, &b1);
gsk_curve_get_tight_bounds (curve2, &b2);
if (!graphene_rect_intersection (&b1, &b2, NULL))
return;
d1 = (t1r - t1l) / 2;
d2 = (t2r - t2l) / 2;
if (b1.size.width < 0.1 && b1.size.height < 0.1 &&
b2.size.width < 0.1 && b2.size.height < 0.1)
{
graphene_point_t c;
t1[*pos] = t1l + d1;
t2[*pos] = t2l + d2;
gsk_curve_get_point (curve1, 0.5, &c);
for (int i = 0; i < *pos; i++)
{
if (graphene_point_near (&c, &p[i], 0.1))
return;
}
p[*pos] = c;
(*pos)++;
return;
}
gsk_curve_split (curve1, 0.5, &p11, &p12);
gsk_curve_split (curve2, 0.5, &p21, &p22);
curve_intersect_recurse (&p11, &p21, t1l, t1l + d1, t2l, t2l + d2, t1, t2, p, n, pos);
curve_intersect_recurse (&p11, &p22, t1l, t1l + d1, t2l + d2, t2r, t1, t2, p, n, pos);
curve_intersect_recurse (&p12, &p21, t1l + d1, t1r, t2l, t2l + d2, t1, t2, p, n, pos);
curve_intersect_recurse (&p12, &p22, t1l + d1, t1r, t2l + d2, t2r, t1, t2, p, n, pos);
}
static int
curve_intersect (const GskCurve *curve1,
const GskCurve *curve2,
float *t1,
float *t2,
graphene_point_t *p,
int n)
{
int pos = 0;
curve_intersect_recurse (curve1, curve2, 0, 1, 0, 1, t1, t2, p, n, &pos);
return pos;
}
static void
get_bounds (const GskCurve *curve,
float tl,
float tr,
graphene_rect_t *bounds)
{
GskCurve c;
gsk_curve_segment (curve, tl, tr, &c);
gsk_curve_get_tight_bounds (&c, bounds);
/* FIXME this is working around inadequacies of
* graphene_rect_t as bounding box
*/
bounds->size.width += 0.0001;
bounds->size.height += 0.0001;
}
static void
general_intersect_recurse (const GskCurve *curve1,
const GskCurve *curve2,
float t1l,
float t1r,
float t2l,
float t2r,
float *t1,
float *t2,
graphene_point_t *p,
int n,
int *pos)
{
graphene_rect_t b1, b2;
float d1, d2;
if (*pos == n)
return;
get_bounds (curve1, t1l, t1r, &b1);
get_bounds (curve2, t2l, t2r, &b2);
if (!graphene_rect_intersection (&b1, &b2, NULL))
return;
d1 = (t1r - t1l) / 2;
d2 = (t2r - t2l) / 2;
if (b1.size.width < 0.1 && b1.size.height < 0.1 &&
b2.size.width < 0.1 && b2.size.height < 0.1)
{
graphene_point_t c;
t1[*pos] = t1l + d1;
t2[*pos] = t2l + d2;
gsk_curve_get_point (curve1, t1[*pos], &c);
for (int i = 0; i < *pos; i++)
{
if (graphene_point_near (&c, &p[i], 0.1))
return;
}
p[*pos] = c;
(*pos)++;
return;
}
/* Note that in the conic case, we cannot just split the curves and
* pass the two halves down, since splitting changes the parametrization,
* and we need the t's to be valid parameters wrt to the original curve.
*
* So, instead, we determine the bounding boxes above by always starting
* from the original curve. That is a bit less efficient, but also works
* for conics.
*/
general_intersect_recurse (curve1, curve2, t1l, t1l + d1, t2l, t2l + d2, t1, t2, p, n, pos);
general_intersect_recurse (curve1, curve2, t1l, t1l + d1, t2l + d2, t2r, t1, t2, p, n, pos);
general_intersect_recurse (curve1, curve2, t1l + d1, t1r, t2l, t2l + d2, t1, t2, p, n, pos);
general_intersect_recurse (curve1, curve2, t1l + d1, t1r, t2l + d2, t2r, t1, t2, p, n, pos);
}
static int
general_intersect (const GskCurve *curve1,
const GskCurve *curve2,
float *t1,
float *t2,
graphene_point_t *p,
int n)
{
int pos = 0;
general_intersect_recurse (curve1, curve2, 0, 1, 0, 1, t1, t2, p, n, &pos);
return pos;
}
/* Place intersections between the curves in p, and their Bezier positions
* in t1 and t2, up to n. Return the number of intersections found.
*
* Note that two cubic Beziers can have up to 9 intersections.
*/
int
gsk_curve_intersect (const GskCurve *curve1,
const GskCurve *curve2,
float *t1,
float *t2,
graphene_point_t *p,
int n)
{
GskPathOperation op1 = curve1->op;
GskPathOperation op2 = curve2->op;
if (op1 == GSK_PATH_CLOSE)
op1 = GSK_PATH_LINE;
if (op2 == GSK_PATH_CLOSE)
op2 = GSK_PATH_LINE;
/* We special-case line-line and line-curve intersections,
* since we can solve them directly.
* Everything else is done via bisection.
*/
if (op1 == GSK_PATH_LINE && op2 == GSK_PATH_LINE)
return line_intersect (curve1, curve2, t1, t2, p);
else if (op1 == GSK_PATH_LINE && op2 == GSK_PATH_CURVE)
return line_curve_intersect (curve1, curve2, t1, t2, p, n);
else if (op1 == GSK_PATH_CURVE && op2 == GSK_PATH_LINE)
return line_curve_intersect (curve2, curve1, t2, t1, p, n);
else if (op1 == GSK_PATH_CURVE && op2 == GSK_PATH_CURVE)
return curve_intersect (curve1, curve2, t1, t2, p, n);
else
return general_intersect (curve1, curve2, t1, t2, p, n);
}

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