Compare commits
313 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9d4f2414ba | |||
| 1c8c852c1b | |||
| 5923766623 | |||
| da59d7080e | |||
| 53f6766bb3 | |||
| 7725c8439c | |||
| 5a9c81ab6f | |||
| 32ef5bed92 | |||
| 5e42e78f2d | |||
| 751ccf460d | |||
| 0f43857676 | |||
| d389c113e9 | |||
| 97e34ed859 | |||
| 512580ba0d | |||
| dcde46ab30 | |||
| 9a9d20e274 | |||
| 4fcb46a387 | |||
| 3e4f8dc356 | |||
| e1295d3db1 | |||
| f64304503c | |||
| 1a4eebe022 | |||
| 2963468019 | |||
| bfbd95d7d6 | |||
| c1c1d4431d | |||
| 155a4fac5c | |||
| 76f481eb7b | |||
| 3d27ff1177 | |||
| 3ada664a89 | |||
| 643a91bd08 | |||
| c86733b4fa | |||
| 55099b7d0e | |||
| db3858a204 | |||
| 07bff74544 | |||
| b4262883a8 | |||
| 563c85e222 | |||
| d75246a543 | |||
| b3856a3ea0 | |||
| bd4d24f8ed | |||
| 65ce3eb207 | |||
| 865ac44963 | |||
| 55a935db53 | |||
| 6fefae3dbb | |||
| f7c84ddc7f | |||
| f4a4dd2615 | |||
| b718d99d1f | |||
| c7215d1199 | |||
| 24d69ef125 | |||
| 7c3fbdecf2 | |||
| b77938f40f | |||
| f703744564 | |||
| a3ccff3773 | |||
| 9869082c1c | |||
| 754212d0f9 | |||
| a9b81577c6 | |||
| eaa2dada54 | |||
| ce2b2128bb | |||
| 41c25b8809 | |||
| b5ff35ff3e | |||
| 06423d807f | |||
| 0395395d52 | |||
| c3ca48b249 | |||
| 464e0aed04 | |||
| d46e4fcecd | |||
| 363fb96e81 | |||
| 2382bd3bb2 | |||
| 3b0a9e84ab | |||
| 850aebea5d | |||
| 26e632e549 | |||
| 64d2d7074f | |||
| 062a15310a | |||
| 299c7c3514 | |||
| 6599cb001f | |||
| f1347f5841 | |||
| f3ccf62463 | |||
| 44fea33c5d | |||
| 8b48cf11f9 | |||
| e681fdd958 | |||
| d4b7a78c54 | |||
| a45cbad553 | |||
| 16b9a30655 | |||
| 82a184a7b7 | |||
| 9e198a59b6 | |||
| c002678085 | |||
| 486cffc361 | |||
| 67495fcc77 | |||
| b26a370ce4 | |||
| 4dc0e67ac7 | |||
| 2c060663cf | |||
| 76a8eb4960 | |||
| ad3dad1965 | |||
| 172d97de05 | |||
| 4c967d5b45 | |||
| b244f31337 | |||
| 8550a04bf4 | |||
| cb38ead48e | |||
| 9713c3394d | |||
| fa57b006c9 | |||
| 9f73f0234a | |||
| a4f067481c | |||
| 733fb527fa | |||
| 3667c6b053 | |||
| 1071818df8 | |||
| 11c2d9ea30 | |||
| 52cdf3056d | |||
| 32899a1edd | |||
| 119458f13e | |||
| 2863095f06 | |||
| cbc050b9ed | |||
| 18affbd390 | |||
| 0b7a36ce33 | |||
| 1957915940 | |||
| 552b71d4e2 | |||
| 480112f9aa | |||
| 9a2f4d8026 | |||
| 3b15f32335 | |||
| 7404a6fc4f | |||
| 91d1ec41c2 | |||
| 9237c8be67 | |||
| 6c87d362c5 | |||
| 4f31d3587d | |||
| 29a2f4021a | |||
| e2d321a16a | |||
| 7366f5099a | |||
| 4fbef5f466 | |||
| 2e48ff3362 | |||
| 7cdbdb663c | |||
| 1b390d3857 | |||
| 2808f9c75b | |||
| 49d109c29e | |||
| 36dd959bf9 | |||
| 2f9a67b8a1 | |||
| 6d5d9ea073 | |||
| 1cfd340cae | |||
| 369ccfc34f | |||
| e556513de6 | |||
| 79dc1a2a4d | |||
| 9e5a501c73 | |||
| 2db8d7f6a2 | |||
| 94c3f4d4fc | |||
| 3fa26861cd | |||
| ef2b0ccd6c | |||
| f1702d24e7 | |||
| 3a50ff57d8 | |||
| 8ac2e8d495 | |||
| e56cf85fd1 | |||
| 1c6aef1862 | |||
| 4653cbe56e | |||
| 93148d302f | |||
| a443ba91ce | |||
| d8daef2de7 | |||
| 845d43c77a | |||
| f8dbcc472b | |||
| 0f0f75f6b1 | |||
| 4e2e2d84fc | |||
| 887885b6e0 | |||
| a868a5c434 | |||
| a0c147d246 | |||
| 38040c96d6 | |||
| 989ecf69ad | |||
| 9d26399143 | |||
| b9cad7cc54 | |||
| 357b97b5a6 | |||
| 318160d836 | |||
| 5e90d63373 | |||
| 0b7f1e488f | |||
| e281bbbb55 | |||
| 892bacee52 | |||
| 2f1db4b77c | |||
| f90e9b26f8 | |||
| ad71f454bd | |||
| c66bd791c4 | |||
| 40bceef3ca | |||
| 9d79f55dc9 | |||
| c5c1cf6d55 | |||
| 9eb8aa9a32 | |||
| 95931a7e6c | |||
| d9daaed43f | |||
| b9c51e65c1 | |||
| da5eb92f47 | |||
| 03ed585f6f | |||
| 51f953a1aa | |||
| 93fb07d808 | |||
| 5e311d4d85 | |||
| e652054d50 | |||
| 58c31b8627 | |||
| b945033b41 | |||
| 92d2867170 | |||
| 5d38c8c558 | |||
| 552a4b2c82 | |||
| 7a4afb08bd | |||
| c5cd1ac630 | |||
| 90edb76415 | |||
| 8f397502df | |||
| 1cb17d8613 | |||
| 88e796bb45 | |||
| 1310bf0218 | |||
| 4efeaa844f | |||
| 24685612a9 | |||
| bf21df4195 | |||
| 417b3f9c6b | |||
| 2d84a1c63e | |||
| 5ab9a29bc4 | |||
| 383ea0d86a | |||
| df8f75bb8f | |||
| ba95ef63da | |||
| bea4aa31ea | |||
| 6c19a8352f | |||
| e3df89b524 | |||
| a9003f7ac0 | |||
| 2cc06f60c5 | |||
| 4759afcc3e | |||
| 66b297e408 | |||
| bbd2b255a3 | |||
| 8b2f28eee3 | |||
| ae767dc5bf | |||
| d923402934 | |||
| 9667f889b3 | |||
| d2ea7af335 | |||
| f95e9407a9 | |||
| 7be3c2974a | |||
| 5444f604cb | |||
| 94e68b0e36 | |||
| 910d86f958 | |||
| fd885e42b0 | |||
| ea31ae31c1 | |||
| 00763e5af3 | |||
| 1bf5aab18c | |||
| 210a709246 | |||
| 5feba67a3d | |||
| e9e373913e | |||
| f1f197e3b9 | |||
| cedb6183e9 | |||
| 0415d46c0f | |||
| 68388331c7 | |||
| 92817b0603 | |||
| f8a1726ffa | |||
| be34f27026 | |||
| 12c5518e2b | |||
| d8ab5c3c45 | |||
| 36d1c9e8b7 | |||
| 45c047f5c0 | |||
| da1232caaf | |||
| fff5a83957 | |||
| f5b6488eb2 | |||
| c02bae9e08 | |||
| 58e65ae7dd | |||
| 3977518f00 | |||
| 8adee3dace | |||
| 09ad930da4 | |||
| 52f7bb6950 | |||
| 68db945e47 | |||
| b54f4cf5d4 | |||
| 9a8a9451b1 | |||
| fffa903ce9 | |||
| ab08885a32 | |||
| ad3995b1b9 | |||
| 75cc0710ac | |||
| 7cec7054e2 | |||
| fe280e578f | |||
| a32973f56b | |||
| 7762311911 | |||
| edd73ffbed | |||
| fca87d93e9 | |||
| c2fe2c0385 | |||
| 8aa25046dc | |||
| 27a07ed89c | |||
| 071a6bcc06 | |||
| f98ac6590d | |||
| 6b733d2943 | |||
| 6f165efcdb | |||
| 7bc1c9a562 | |||
| 1596fdeba3 | |||
| 1599b659cf | |||
| bb65564c7d | |||
| 03031b3dc1 | |||
| b7636ebbc0 | |||
| 5707b80a69 | |||
| 81e5d4c327 | |||
| f8111125ef | |||
| c020e83890 | |||
| f6659fea8d | |||
| a06858ccd9 | |||
| c6c48d327c | |||
| 5d0f188615 | |||
| 78fcc8feb8 | |||
| 27cad85247 | |||
| ad0c1d4dbe | |||
| 00abaed89a | |||
| 342f02711b | |||
| d9c48a8d01 | |||
| d757696116 | |||
| 528ebfabf0 | |||
| 2ebde276d1 | |||
| 04f3c8054b | |||
| ea07bf7536 | |||
| 80e6f1ca8c | |||
| 54d29568ec | |||
| 4e1ea58503 | |||
| 90c429a91c | |||
| 4b28e038e0 | |||
| 8df694a358 | |||
| eb8778e1cd | |||
| 256f3a0d60 | |||
| 54c087e7ec | |||
| 69a99b89f9 | |||
| f328ab9d83 | |||
| 8acce5f294 | |||
| bdbe0acd1f | |||
| fc701baef8 | |||
| 4cc7977d36 | |||
| 5d7ecb7a6e | |||
| 7fc90aed26 | |||
| bc386c9a60 |
+1
-1
@@ -25,7 +25,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:v32"
|
||||
FEDORA_IMAGE: "registry.gitlab.gnome.org/gnome/gtk/fedora:v33"
|
||||
FLATPAK_IMAGE: "registry.gitlab.gnome.org/gnome/gnome-runtime-images/gnome:master"
|
||||
|
||||
.only-default:
|
||||
|
||||
@@ -48,7 +48,9 @@ RUN dnf -y install \
|
||||
libcloudproviders-devel \
|
||||
libepoxy-devel \
|
||||
libffi-devel \
|
||||
libjpeg-turbo-devel \
|
||||
libmount-devel \
|
||||
libpng-devel \
|
||||
librsvg2 \
|
||||
libselinux-devel \
|
||||
libubsan \
|
||||
|
||||
@@ -40,7 +40,7 @@ export CCACHE_DIR="${CCACHE_BASEDIR}/_ccache"
|
||||
# https://gitlab.gnome.org/GNOME/gtk/-/issues/2243
|
||||
# https://gitlab.gnome.org/GNOME/gtk/-/issues/3002
|
||||
|
||||
if ! pkg-config --atleast-version=2.65.0 glib-2.0; then
|
||||
if ! pkg-config --atleast-version=2.66.0 glib-2.0; then
|
||||
git clone https://gitlab.gnome.org/GNOME/glib.git _glib
|
||||
meson setup _glib_build _glib
|
||||
meson compile -C _glib_build
|
||||
@@ -48,7 +48,7 @@ if ! pkg-config --atleast-version=2.65.0 glib-2.0; then
|
||||
fi
|
||||
pkg-config --modversion glib-2.0
|
||||
|
||||
if ! pkg-config --atleast-version=1.47.0 pango; then
|
||||
if ! pkg-config --atleast-version=1.49.1 pango; then
|
||||
git clone https://gitlab.gnome.org/GNOME/pango.git _pango
|
||||
meson setup _pango_build _pango
|
||||
meson compile -C _pango_build
|
||||
|
||||
@@ -1,3 +1,84 @@
|
||||
Overview of Changes
|
||||
===================
|
||||
|
||||
* gsk:
|
||||
- Drop the GL renderer in favor of NGL
|
||||
|
||||
* css:
|
||||
- Add support for line-height
|
||||
- Add support for text-transform
|
||||
|
||||
* GtkTextView:
|
||||
- Add support for line height
|
||||
- Add support for text transforms
|
||||
|
||||
* Build:
|
||||
- Require Pango 1.49
|
||||
|
||||
|
||||
Overview of Changes in 4.4.0
|
||||
============================
|
||||
|
||||
* Input:
|
||||
- Match IBus for display of Compose sequences
|
||||
- Match IBus for handling of mismatches
|
||||
- Handle Escape in Compose sequences
|
||||
- Allow multiple dead keys
|
||||
- Support 32bit keysyms
|
||||
|
||||
* GtkCheckButton:
|
||||
- Activate when moving focus
|
||||
|
||||
* GtkLabel:
|
||||
- Propertly ignore double underscores for mnemonics
|
||||
|
||||
* GtkPopoverMenu:
|
||||
- Fix focus cycling
|
||||
|
||||
* GtkTextView:
|
||||
- Improve word selection
|
||||
- Fix block cursors on empty lines
|
||||
|
||||
* GdkToplevel:
|
||||
- Support the gnome-shell titlebar gesture protocol
|
||||
|
||||
* GdkDropTarget:
|
||||
- Allow creating drop targets in ui files
|
||||
|
||||
* gsk:
|
||||
- Handle partial color fonts correctly
|
||||
- Use harfbuzz for color font information
|
||||
- Avoid pango for glyph cache rendering
|
||||
- Shrink shadow extents
|
||||
|
||||
* Settings:
|
||||
- Change the default for gtk-split-cursor to FALSE
|
||||
|
||||
* Demos:
|
||||
- Small improvements to widget-factory
|
||||
- gtk-demo: Improve the hypertext demo
|
||||
- gtk-dem: Improve the clipboard demo
|
||||
|
||||
* X11:
|
||||
- Set WM_CLASS on toplevels
|
||||
|
||||
* Wayland:
|
||||
- Support wl_seat v7
|
||||
|
||||
* Windows:
|
||||
- Drop the local DND protocol
|
||||
- Avoid WGL if shaders don't work
|
||||
- Use WinPointer API
|
||||
|
||||
* Translation updates:
|
||||
Belarusian
|
||||
Friulian
|
||||
Hebrew
|
||||
Khmer
|
||||
Persian
|
||||
Polish
|
||||
|
||||
|
||||
Overview of Changes in 4.3.2
|
||||
============================
|
||||
|
||||
|
||||
-286
@@ -1,286 +0,0 @@
|
||||
/* always defined to indicate that i18n is enabled */
|
||||
#define ENABLE_NLS 1
|
||||
|
||||
/* Use structured logging */
|
||||
#define G_LOG_STRUCTURED 1
|
||||
|
||||
/* The prefix for our gettext translation domains. */
|
||||
#mesondefine GETTEXT_PACKAGE
|
||||
|
||||
/* Disable deprecation warnings from glib */
|
||||
#mesondefine GLIB_DISABLE_DEPRECATION_WARNINGS
|
||||
|
||||
/* Define the location where the catalogs will be installed */
|
||||
#mesondefine GTK_LOCALEDIR
|
||||
|
||||
/* Define to 1 if you have the `bind_textdomain_codeset' function. */
|
||||
#mesondefine HAVE_BIND_TEXTDOMAIN_CODESET
|
||||
|
||||
/* Have the cloudproviders library */
|
||||
#mesondefine HAVE_CLOUDPROVIDERS
|
||||
|
||||
/* define if we have colord */
|
||||
#mesondefine HAVE_COLORD
|
||||
|
||||
/* Define to 1 if you have the <crt_externs.h> header file. */
|
||||
#mesondefine HAVE_CRT_EXTERNS_H
|
||||
|
||||
/* Define to 1 if you have the `dcgettext' function. */
|
||||
#mesondefine HAVE_DCGETTEXT
|
||||
|
||||
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||
#mesondefine HAVE_DLFCN_H
|
||||
|
||||
/* Have the ffmpeg library */
|
||||
#mesondefine HAVE_FFMPEG
|
||||
|
||||
/* Define to 1 if you have the <ftw.h> header file. */
|
||||
#mesondefine HAVE_FTW_H
|
||||
|
||||
/* Define to 1 if you have the `getpagesize' function. */
|
||||
#mesondefine HAVE_GETPAGESIZE
|
||||
|
||||
/* Define to 1 if you have the `getresuid' function. */
|
||||
#mesondefine HAVE_GETRESUID
|
||||
|
||||
/* Define if gio-unix is available */
|
||||
#mesondefine HAVE_GIO_UNIX
|
||||
|
||||
/* Define if GStreamer support is available */
|
||||
#mesondefine HAVE_GSTREAMER
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#mesondefine HAVE_INTTYPES_H
|
||||
|
||||
/* Define to 1 if the system has the type `IPrintDialogCallback'. */
|
||||
#mesondefine HAVE_IPRINTDIALOGCALLBACK
|
||||
|
||||
/* Define to 1 if you have the <locale.h> header file. */
|
||||
#mesondefine HAVE_LOCALE_H
|
||||
|
||||
/* Define to 1 if you have the `lstat' function. */
|
||||
#mesondefine HAVE_LSTAT
|
||||
|
||||
/* Define to 1 if you have the `mallinfo' function. */
|
||||
#mesondefine HAVE_MALLINFO
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#mesondefine HAVE_MEMORY_H
|
||||
|
||||
/* Define to 1 if you have the `mkstemp' function. */
|
||||
#mesondefine HAVE_MKSTEMP
|
||||
|
||||
/* Define to 1 if you have the `mlock` function. */
|
||||
#mesondefine HAVE_MLOCK
|
||||
|
||||
/* Define to 1 if you have a working `mmap' system call. */
|
||||
#mesondefine HAVE_MMAP
|
||||
|
||||
/* Define to 1 if you have a working `madvise' system call. */
|
||||
#mesondefine HAVE_MADVISE
|
||||
|
||||
/* Define to 1 if you have the `posix_fallocate' function. */
|
||||
#mesondefine HAVE_POSIX_FALLOCATE
|
||||
|
||||
/* Have the Xrandr extension library */
|
||||
#mesondefine HAVE_RANDR
|
||||
|
||||
/* Have the Xrandr 1.5 extension library */
|
||||
#mesondefine HAVE_RANDR15
|
||||
|
||||
/* Define to 1 if you have the `sincos' function. */
|
||||
#mesondefine HAVE_SINCOS
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#mesondefine HAVE_STDINT_H
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#mesondefine HAVE_STDLIB_H
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#mesondefine HAVE_STRINGS_H
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#mesondefine HAVE_STRING_H
|
||||
|
||||
/* Define to 1 if you have the <sys/mman.h> header file. */
|
||||
#mesondefine HAVE_SYS_MMAN_H
|
||||
|
||||
/* Define to 1 if you have the <sys/param.h> header file. */
|
||||
#mesondefine HAVE_SYS_PARAM_H
|
||||
|
||||
/* Have the sysprof-capture library */
|
||||
#mesondefine HAVE_SYSPROF
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#mesondefine HAVE_SYS_STAT_H
|
||||
|
||||
/* Define to 1 if you have the <sys/time.h> header file. */
|
||||
#mesondefine HAVE_SYS_TIME_H
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#mesondefine HAVE_SYS_TYPES_H
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#mesondefine HAVE_UNISTD_H
|
||||
|
||||
/* Have the Xcursor library */
|
||||
#mesondefine HAVE_XCURSOR
|
||||
|
||||
/* Have the XDAMAGE X extension */
|
||||
#mesondefine HAVE_XDAMAGE
|
||||
|
||||
/* Have the XFIXES X extension */
|
||||
#mesondefine HAVE_XFIXES
|
||||
|
||||
/* Define to 1 if XFree Xinerama is available */
|
||||
#mesondefine HAVE_XFREE_XINERAMA
|
||||
|
||||
/* Have XGenericEvent */
|
||||
#mesondefine HAVE_XGENERICEVENTS
|
||||
|
||||
/* Define to use XKB extension */
|
||||
#mesondefine HAVE_XKB
|
||||
|
||||
/* Have the SYNC extension library */
|
||||
#mesondefine HAVE_XSYNC
|
||||
|
||||
/* Define to 1 if you have the `_lock_file' function */
|
||||
#mesondefine HAVE__LOCK_FILE
|
||||
|
||||
/* Define to 1 if you have the `flockfile' function */
|
||||
#mesondefine HAVE_FLOCKFILE
|
||||
|
||||
/* Define if _NL_MEASUREMENT_MEASUREMENT is available */
|
||||
#mesondefine HAVE__NL_MEASUREMENT_MEASUREMENT
|
||||
|
||||
/* Define if _NL_PAPER_HEIGHT is available */
|
||||
#mesondefine HAVE__NL_PAPER_HEIGHT
|
||||
|
||||
/* Define if _NL_PAPER_WIDTH is available */
|
||||
#mesondefine HAVE__NL_PAPER_WIDTH
|
||||
|
||||
/* Define if _NL_TIME_FIRST_WEEKDAY is available */
|
||||
#mesondefine HAVE__NL_TIME_FIRST_WEEKDAY
|
||||
|
||||
/* Define to the sub-directory where libtool stores uninstalled libraries. */
|
||||
#mesondefine LT_OBJDIR
|
||||
|
||||
/* Define to 1 if your C compiler doesn't accept -c and -o together. */
|
||||
#mesondefine NO_MINUS_C_MINUS_O
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#mesondefine PACKAGE_BUGREPORT
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#mesondefine PACKAGE_NAME
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#mesondefine PACKAGE_STRING
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#mesondefine PACKAGE_TARNAME
|
||||
|
||||
/* Define to the home page for this package. */
|
||||
#mesondefine PACKAGE_URL
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#mesondefine PACKAGE_VERSION
|
||||
|
||||
/* Use NSBundle functions to determine load paths for libraries, translations,
|
||||
etc. */
|
||||
#mesondefine QUARTZ_RELOCATION
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#mesondefine STDC_HEADERS
|
||||
|
||||
/* Enable extensions on AIX 3, Interix. */
|
||||
#ifndef _ALL_SOURCE
|
||||
# undef _ALL_SOURCE
|
||||
#endif
|
||||
/* Enable GNU extensions on systems that have them. */
|
||||
#ifndef _GNU_SOURCE
|
||||
# undef _GNU_SOURCE
|
||||
#endif
|
||||
/* Enable threading extensions on Solaris. */
|
||||
#ifndef _POSIX_PTHREAD_SEMANTICS
|
||||
# undef _POSIX_PTHREAD_SEMANTICS
|
||||
#endif
|
||||
/* Enable extensions on HP NonStop. */
|
||||
#ifndef _TANDEM_SOURCE
|
||||
# undef _TANDEM_SOURCE
|
||||
#endif
|
||||
/* Enable general extensions on Solaris. */
|
||||
#ifndef __EXTENSIONS__
|
||||
# undef __EXTENSIONS__
|
||||
#endif
|
||||
|
||||
|
||||
/* Define to 1 if XInput 2.2 is available */
|
||||
#mesondefine XINPUT_2_2
|
||||
|
||||
/* Define to 1 if the X Window System is missing or not being used. */
|
||||
#mesondefine X_DISPLAY_MISSING
|
||||
|
||||
/* Enable large inode numbers on Mac OS X 10.5. */
|
||||
#ifndef _DARWIN_USE_64_BIT_INODE
|
||||
# define _DARWIN_USE_64_BIT_INODE 1
|
||||
#endif
|
||||
|
||||
/* Number of bits in a file offset, on hosts where this is settable. */
|
||||
#mesondefine _FILE_OFFSET_BITS
|
||||
|
||||
/* defines how to decorate public symbols while building */
|
||||
#mesondefine _GDK_EXTERN
|
||||
|
||||
/* Define for large files, on AIX-style hosts. */
|
||||
#mesondefine _LARGE_FILES
|
||||
|
||||
/* Define to 1 if on MINIX. */
|
||||
#mesondefine _MINIX
|
||||
|
||||
/* Define to 2 if the system does not provide POSIX.1 features except with
|
||||
this defined. */
|
||||
#mesondefine _POSIX_1_SOURCE
|
||||
|
||||
/* Define to 1 if you need to in order for `stat' and other things to work. */
|
||||
#mesondefine _POSIX_SOURCE
|
||||
|
||||
/* Define to `int' if <sys/types.h> doesn't define. */
|
||||
#mesondefine gid_t
|
||||
|
||||
/* Define to `int' if <sys/types.h> doesn't define. */
|
||||
#mesondefine uid_t
|
||||
|
||||
/* Define to 1 if linux/memfd.h exists */
|
||||
#mesondefine HAVE_LINUX_MEMFD_H
|
||||
|
||||
#mesondefine HAVE_LINUX_INPUT_H
|
||||
|
||||
#mesondefine HAVE_DEV_EVDEV_INPUT_H
|
||||
|
||||
#mesondefine GTK_SYSCONFDIR
|
||||
|
||||
#mesondefine GTK_LOCALEDIR
|
||||
|
||||
#mesondefine GTK_DATADIR
|
||||
|
||||
#mesondefine GTK_LIBDIR
|
||||
|
||||
#mesondefine GTK_PRINT_BACKENDS
|
||||
|
||||
#mesondefine HAVE_CAIRO_SCRIPT_INTERPRETER
|
||||
|
||||
#mesondefine HAVE_HARFBUZZ
|
||||
|
||||
#mesondefine HAVE_PANGOFT
|
||||
|
||||
#mesondefine ISO_CODES_PREFIX
|
||||
|
||||
/* Define if tracker3 is available */
|
||||
#mesondefine HAVE_TRACKER3
|
||||
|
||||
#mesondefine HAVE_F16C
|
||||
|
||||
/* Does the OS support GDesktopAppInfo? */
|
||||
#mesondefine HAVE_DESKTOPAPPINFO
|
||||
@@ -321,6 +321,7 @@
|
||||
<file>paintable_emblem.c</file>
|
||||
<file>paintable_mediastream.c</file>
|
||||
<file>paintable_svg.c</file>
|
||||
<file>paintable_symbolic.c</file>
|
||||
<file>panes.c</file>
|
||||
<file>password_entry.c</file>
|
||||
<file>peg_solitaire.c</file>
|
||||
|
||||
+42
-12
@@ -100,7 +100,11 @@ prepare_drag (GtkDragSource *source,
|
||||
DemoImage *demo = DEMO_IMAGE (widget);
|
||||
GdkPaintable *paintable = get_image_paintable (GTK_IMAGE (demo->image));
|
||||
|
||||
return gdk_content_provider_new_typed (GDK_TYPE_PAINTABLE, paintable);
|
||||
/* Textures can be serialized, paintables can't, so special case the textures */
|
||||
if (GDK_IS_TEXTURE (paintable))
|
||||
return gdk_content_provider_new_typed (GDK_TYPE_TEXTURE, paintable);
|
||||
else
|
||||
return gdk_content_provider_new_typed (GDK_TYPE_PAINTABLE, paintable);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@@ -129,7 +133,11 @@ copy_image (GtkWidget *widget,
|
||||
GdkPaintable *paintable = get_image_paintable (GTK_IMAGE (demo->image));
|
||||
GValue value = G_VALUE_INIT;
|
||||
|
||||
g_value_init (&value, GDK_TYPE_PAINTABLE);
|
||||
/* Textures can be serialized, paintables can't, so special case the textures */
|
||||
if (GDK_IS_TEXTURE (paintable))
|
||||
g_value_init (&value, GDK_TYPE_TEXTURE);
|
||||
else
|
||||
g_value_init (&value, GDK_TYPE_PAINTABLE);
|
||||
g_value_set_object (&value, paintable);
|
||||
gdk_clipboard_set_value (clipboard, &value);
|
||||
g_value_unset (&value);
|
||||
@@ -138,24 +146,46 @@ copy_image (GtkWidget *widget,
|
||||
g_object_unref (paintable);
|
||||
}
|
||||
|
||||
static void
|
||||
paste_image_cb (GObject *source,
|
||||
GAsyncResult *result,
|
||||
gpointer data)
|
||||
{
|
||||
GdkClipboard *clipboard = GDK_CLIPBOARD (source);
|
||||
DemoImage *demo = DEMO_IMAGE (data);
|
||||
const GValue *value;
|
||||
|
||||
value = gdk_clipboard_read_value_finish (clipboard, result, NULL);
|
||||
if (value == NULL)
|
||||
{
|
||||
gtk_widget_error_bell (GTK_WIDGET (demo));
|
||||
g_object_unref (demo);
|
||||
return;
|
||||
}
|
||||
|
||||
gtk_image_set_from_paintable (GTK_IMAGE (demo->image), g_value_get_object (value));
|
||||
g_object_unref (demo);
|
||||
}
|
||||
|
||||
static void
|
||||
paste_image (GtkWidget *widget,
|
||||
const char *action_name,
|
||||
GVariant *parameter)
|
||||
{
|
||||
GdkClipboard *clipboard = gtk_widget_get_clipboard (widget);
|
||||
DemoImage *demo = DEMO_IMAGE (widget);
|
||||
GdkContentProvider *content = gdk_clipboard_get_content (clipboard);
|
||||
GValue value = G_VALUE_INIT;
|
||||
GdkPaintable *paintable;
|
||||
GType type;
|
||||
|
||||
g_value_init (&value, GDK_TYPE_PAINTABLE);
|
||||
if (!gdk_content_provider_get_value (content, &value, NULL))
|
||||
return;
|
||||
if (gdk_content_formats_contain_gtype (gdk_clipboard_get_formats (clipboard), GDK_TYPE_TEXTURE))
|
||||
type = GDK_TYPE_TEXTURE;
|
||||
else
|
||||
type = GDK_TYPE_PAINTABLE;
|
||||
|
||||
paintable = GDK_PAINTABLE (g_value_get_object (&value));
|
||||
gtk_image_set_from_paintable (GTK_IMAGE (demo->image), paintable);
|
||||
g_value_unset (&value);
|
||||
gdk_clipboard_read_value_async (clipboard,
|
||||
type,
|
||||
G_PRIORITY_DEFAULT,
|
||||
NULL,
|
||||
paste_image_cb,
|
||||
g_object_ref (widget));
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
@@ -131,6 +131,19 @@ insert_tags_for_attributes (GtkTextBuffer *buffer,
|
||||
gtk_text_buffer_apply_tag (buffer, tag, start, end); \
|
||||
}
|
||||
|
||||
#define VOID_ATTR(attr_name) \
|
||||
{ \
|
||||
tag = gtk_text_tag_table_lookup (table, #attr_name); \
|
||||
if (!tag) \
|
||||
{ \
|
||||
tag = gtk_text_tag_new (#attr_name); \
|
||||
g_object_set (tag, #attr_name, TRUE, NULL); \
|
||||
gtk_text_tag_table_add (table, tag); \
|
||||
g_object_unref (tag); \
|
||||
} \
|
||||
gtk_text_buffer_apply_tag (buffer, tag, start, end); \
|
||||
}
|
||||
|
||||
fg_alpha = bg_alpha = 1.;
|
||||
|
||||
attrs = pango_attr_iterator_get_attrs (iter);
|
||||
@@ -255,6 +268,29 @@ insert_tags_for_attributes (GtkTextBuffer *buffer,
|
||||
INT_ATTR (insert_hyphens);
|
||||
break;
|
||||
|
||||
case PANGO_ATTR_LINE_HEIGHT:
|
||||
FLOAT_ATTR (line_height);
|
||||
break;
|
||||
|
||||
case PANGO_ATTR_ABSOLUTE_LINE_HEIGHT:
|
||||
break;
|
||||
|
||||
case PANGO_ATTR_WORD:
|
||||
VOID_ATTR (word);
|
||||
break;
|
||||
|
||||
case PANGO_ATTR_SENTENCE:
|
||||
VOID_ATTR (sentence);
|
||||
break;
|
||||
|
||||
case PANGO_ATTR_BASELINE_SHIFT:
|
||||
INT_ATTR (baseline_shift);
|
||||
break;
|
||||
|
||||
case PANGO_ATTR_FONT_SCALE:
|
||||
INT_ATTR (font_scale);
|
||||
break;
|
||||
|
||||
case PANGO_ATTR_SHAPE:
|
||||
case PANGO_ATTR_ABSOLUTE_SIZE:
|
||||
case PANGO_ATTR_GRAVITY:
|
||||
@@ -263,6 +299,10 @@ insert_tags_for_attributes (GtkTextBuffer *buffer,
|
||||
case PANGO_ATTR_BACKGROUND_ALPHA:
|
||||
break;
|
||||
|
||||
case PANGO_ATTR_TEXT_TRANSFORM:
|
||||
INT_ATTR (text_transform);
|
||||
break;
|
||||
|
||||
case PANGO_ATTR_INVALID:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
|
||||
+163
-13
@@ -1,6 +1,11 @@
|
||||
/* Pango/Font Rendering
|
||||
*
|
||||
* Demonstrates various aspects of font rendering.
|
||||
* Demonstrates various aspects of font rendering,
|
||||
* such as hinting, antialiasing and grid alignment.
|
||||
*
|
||||
* The demo lets you explore font rendering options
|
||||
* interactively to get a feeling for they affect the
|
||||
* shape and positioning of the glyphs.
|
||||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
@@ -10,16 +15,21 @@ static GtkWidget *font_button = NULL;
|
||||
static GtkWidget *entry = NULL;
|
||||
static GtkWidget *image = NULL;
|
||||
static GtkWidget *hinting = NULL;
|
||||
static GtkWidget *anti_alias = NULL;
|
||||
static GtkWidget *hint_metrics = NULL;
|
||||
static GtkWidget *up_button = NULL;
|
||||
static GtkWidget *down_button = NULL;
|
||||
static GtkWidget *text_radio = NULL;
|
||||
static GtkWidget *show_grid = NULL;
|
||||
static GtkWidget *show_extents = NULL;
|
||||
static GtkWidget *show_pixels = NULL;
|
||||
static GtkWidget *show_outlines = NULL;
|
||||
|
||||
static PangoContext *context;
|
||||
|
||||
static int scale = 9;
|
||||
static int scale = 7;
|
||||
static double pixel_alpha = 1.0;
|
||||
static double outline_alpha = 0.0;
|
||||
|
||||
static void
|
||||
update_image (void)
|
||||
@@ -37,6 +47,8 @@ update_image (void)
|
||||
cairo_font_options_t *fopt;
|
||||
cairo_hint_style_t hintstyle;
|
||||
cairo_hint_metrics_t hintmetrics;
|
||||
cairo_antialias_t antialias;
|
||||
cairo_path_t *path;
|
||||
|
||||
if (!context)
|
||||
context = gtk_widget_create_pango_context (image);
|
||||
@@ -65,6 +77,13 @@ update_image (void)
|
||||
hintmetrics = CAIRO_HINT_METRICS_OFF;
|
||||
cairo_font_options_set_hint_metrics (fopt, hintmetrics);
|
||||
|
||||
if (gtk_check_button_get_active (GTK_CHECK_BUTTON (anti_alias)))
|
||||
antialias = CAIRO_ANTIALIAS_GRAY;
|
||||
else
|
||||
antialias = CAIRO_ANTIALIAS_NONE;
|
||||
cairo_font_options_set_antialias (fopt, antialias);
|
||||
|
||||
pango_context_set_round_glyph_positions (context, hintmetrics == CAIRO_HINT_METRICS_ON);
|
||||
pango_cairo_context_set_font_options (context, fopt);
|
||||
cairo_font_options_destroy (fopt);
|
||||
pango_context_changed (context);
|
||||
@@ -85,10 +104,14 @@ update_image (void)
|
||||
cairo_set_source_rgb (cr, 1, 1, 1);
|
||||
cairo_paint (cr);
|
||||
|
||||
cairo_set_source_rgb (cr, 0, 0, 0);
|
||||
cairo_set_source_rgba (cr, 0, 0, 0, pixel_alpha);
|
||||
|
||||
cairo_move_to (cr, 10, 10);
|
||||
pango_cairo_show_layout (cr, layout);
|
||||
|
||||
pango_cairo_layout_path (cr, layout);
|
||||
path = cairo_copy_path (cr);
|
||||
|
||||
cairo_destroy (cr);
|
||||
g_object_unref (layout);
|
||||
|
||||
@@ -127,7 +150,7 @@ update_image (void)
|
||||
|
||||
if (gtk_check_button_get_active (GTK_CHECK_BUTTON (show_extents)))
|
||||
{
|
||||
cairo_set_source_rgba (cr, 0, 0, 1, 1);
|
||||
cairo_set_source_rgb (cr, 0, 0, 1);
|
||||
|
||||
cairo_rectangle (cr,
|
||||
scale * (10 + pango_units_to_double (logical.x)) - 0.5,
|
||||
@@ -140,7 +163,7 @@ update_image (void)
|
||||
cairo_line_to (cr, scale * (10 + pango_units_to_double (logical.x + logical.width)) + 1,
|
||||
scale * (10 + pango_units_to_double (baseline)) - 0.5);
|
||||
cairo_stroke (cr);
|
||||
cairo_set_source_rgba (cr, 1, 0, 0, 1);
|
||||
cairo_set_source_rgb (cr, 1, 0, 0);
|
||||
cairo_rectangle (cr,
|
||||
scale * (10 + pango_units_to_double (pink.x)) + 0.5,
|
||||
scale * (10 + pango_units_to_double (pink.y)) + 0.5,
|
||||
@@ -149,8 +172,36 @@ update_image (void)
|
||||
cairo_stroke (cr);
|
||||
}
|
||||
|
||||
for (int i = 0; i < path->num_data; i += path->data[i].header.length)
|
||||
{
|
||||
cairo_path_data_t *data = &path->data[i];
|
||||
switch (data->header.type)
|
||||
{
|
||||
case CAIRO_PATH_CURVE_TO:
|
||||
data[3].point.x *= scale; data[3].point.y *= scale;
|
||||
data[2].point.x *= scale; data[2].point.y *= scale;
|
||||
data[1].point.x *= scale; data[1].point.y *= scale;
|
||||
break;
|
||||
case CAIRO_PATH_LINE_TO:
|
||||
case CAIRO_PATH_MOVE_TO:
|
||||
data[1].point.x *= scale; data[1].point.y *= scale;
|
||||
break;
|
||||
case CAIRO_PATH_CLOSE_PATH:
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
cairo_set_source_rgba (cr, 0, 0, 1, outline_alpha);
|
||||
cairo_move_to (cr, scale * 20 - 0.5, scale * 20 - 0.5);
|
||||
cairo_append_path (cr, path);
|
||||
cairo_stroke (cr);
|
||||
|
||||
cairo_surface_destroy (surface);
|
||||
cairo_destroy (cr);
|
||||
|
||||
cairo_path_destroy (path);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -158,10 +209,26 @@ update_image (void)
|
||||
PangoLayoutRun *run;
|
||||
PangoGlyphInfo *g;
|
||||
int i, j;
|
||||
GString *str;
|
||||
gunichar ch;
|
||||
|
||||
if (*text == '\0')
|
||||
text = " ";
|
||||
|
||||
ch = g_utf8_get_char (text);
|
||||
|
||||
str = g_string_new ("");
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
g_string_append_unichar (str, ch);
|
||||
g_string_append_unichar (str, 0x200c);
|
||||
}
|
||||
|
||||
layout = pango_layout_new (context);
|
||||
pango_layout_set_font_description (layout, desc);
|
||||
pango_layout_set_text (layout, "aaaa", -1);
|
||||
pango_layout_set_text (layout, str->str, -1);
|
||||
g_string_free (str, TRUE);
|
||||
pango_layout_get_extents (layout, &ink, &logical);
|
||||
pango_extents_to_pixels (&logical, NULL);
|
||||
|
||||
@@ -176,7 +243,7 @@ update_image (void)
|
||||
cairo_set_source_rgb (cr, 0, 0, 0);
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
g = &(run->glyphs->glyphs[i]);
|
||||
g = &(run->glyphs->glyphs[2*i]);
|
||||
g->geometry.width = PANGO_UNITS_ROUND (g->geometry.width * 3 / 2);
|
||||
}
|
||||
|
||||
@@ -184,7 +251,7 @@ update_image (void)
|
||||
{
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
g = &(run->glyphs->glyphs[i]);
|
||||
g = &(run->glyphs->glyphs[2*i]);
|
||||
g->geometry.x_offset = i * (PANGO_SCALE / 4);
|
||||
g->geometry.y_offset = j * (PANGO_SCALE / 4);
|
||||
}
|
||||
@@ -203,7 +270,6 @@ update_image (void)
|
||||
cairo_surface_destroy (surface);
|
||||
}
|
||||
|
||||
|
||||
gtk_picture_set_pixbuf (GTK_PICTURE (image), pixbuf2);
|
||||
|
||||
g_object_unref (pixbuf2);
|
||||
@@ -211,6 +277,78 @@ update_image (void)
|
||||
pango_font_description_free (desc);
|
||||
}
|
||||
|
||||
static gboolean fading = FALSE;
|
||||
static double start_pixel_alpha;
|
||||
static double end_pixel_alpha;
|
||||
static double start_outline_alpha;
|
||||
static double end_outline_alpha;
|
||||
static gint64 start_time;
|
||||
static gint64 end_time;
|
||||
|
||||
static double
|
||||
ease_out_cubic (double t)
|
||||
{
|
||||
double p = t - 1;
|
||||
return p * p * p + 1;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
change_alpha (GtkWidget *widget,
|
||||
GdkFrameClock *clock,
|
||||
gpointer user_data)
|
||||
{
|
||||
gint64 now = g_get_monotonic_time ();
|
||||
double t;
|
||||
|
||||
t = ease_out_cubic ((now - start_time) / (double) (end_time - start_time));
|
||||
|
||||
pixel_alpha = start_pixel_alpha + (end_pixel_alpha - start_pixel_alpha) * t;
|
||||
outline_alpha = start_outline_alpha + (end_outline_alpha - start_outline_alpha) * t;
|
||||
|
||||
update_image ();
|
||||
|
||||
if (now >= end_time)
|
||||
{
|
||||
fading = FALSE;
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
static void
|
||||
start_alpha_fade (void)
|
||||
{
|
||||
gboolean pixels;
|
||||
gboolean outlines;
|
||||
|
||||
if (fading)
|
||||
return;
|
||||
|
||||
pixels = gtk_check_button_get_active (GTK_CHECK_BUTTON (show_pixels));
|
||||
outlines = gtk_check_button_get_active (GTK_CHECK_BUTTON (show_outlines));
|
||||
|
||||
start_pixel_alpha = pixel_alpha;
|
||||
if (pixels && outlines)
|
||||
end_pixel_alpha = 0.5;
|
||||
else if (pixels)
|
||||
end_pixel_alpha = 1;
|
||||
else
|
||||
end_pixel_alpha = 0;
|
||||
|
||||
start_outline_alpha = outline_alpha;
|
||||
if (outlines)
|
||||
end_outline_alpha = 1.0;
|
||||
else
|
||||
end_outline_alpha = 0.0;
|
||||
|
||||
start_time = g_get_monotonic_time ();
|
||||
end_time = start_time + G_TIME_SPAN_SECOND / 2;
|
||||
|
||||
fading = TRUE;
|
||||
gtk_widget_add_tick_callback (window, change_alpha, NULL, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
update_buttons (void)
|
||||
{
|
||||
@@ -218,20 +356,26 @@ update_buttons (void)
|
||||
gtk_widget_set_sensitive (down_button, scale > 1);
|
||||
}
|
||||
|
||||
static void
|
||||
scale_up (void)
|
||||
static gboolean
|
||||
scale_up (GtkWidget *widget,
|
||||
GVariant *args,
|
||||
gpointer user_data)
|
||||
{
|
||||
scale += 1;
|
||||
update_buttons ();
|
||||
update_image ();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
scale_down (void)
|
||||
static gboolean
|
||||
scale_down (GtkWidget *widget,
|
||||
GVariant *args,
|
||||
gpointer user_data)
|
||||
{
|
||||
scale -= 1;
|
||||
update_buttons ();
|
||||
update_image ();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
@@ -252,20 +396,26 @@ do_fontrendering (GtkWidget *do_widget)
|
||||
entry = GTK_WIDGET (gtk_builder_get_object (builder, "entry"));
|
||||
image = GTK_WIDGET (gtk_builder_get_object (builder, "image"));
|
||||
hinting = GTK_WIDGET (gtk_builder_get_object (builder, "hinting"));
|
||||
anti_alias = GTK_WIDGET (gtk_builder_get_object (builder, "antialias"));
|
||||
hint_metrics = GTK_WIDGET (gtk_builder_get_object (builder, "hint_metrics"));
|
||||
text_radio = GTK_WIDGET (gtk_builder_get_object (builder, "text_radio"));
|
||||
show_grid = GTK_WIDGET (gtk_builder_get_object (builder, "show_grid"));
|
||||
show_extents = GTK_WIDGET (gtk_builder_get_object (builder, "show_extents"));
|
||||
show_pixels = GTK_WIDGET (gtk_builder_get_object (builder, "show_pixels"));
|
||||
show_outlines = GTK_WIDGET (gtk_builder_get_object (builder, "show_outlines"));
|
||||
|
||||
g_signal_connect (up_button, "clicked", G_CALLBACK (scale_up), NULL);
|
||||
g_signal_connect (down_button, "clicked", G_CALLBACK (scale_down), NULL);
|
||||
g_signal_connect (entry, "notify::text", G_CALLBACK (update_image), NULL);
|
||||
g_signal_connect (font_button, "notify::font-desc", G_CALLBACK (update_image), NULL);
|
||||
g_signal_connect (hinting, "notify::active", G_CALLBACK (update_image), NULL);
|
||||
g_signal_connect (anti_alias, "notify::active", G_CALLBACK (update_image), NULL);
|
||||
g_signal_connect (hint_metrics, "notify::active", G_CALLBACK (update_image), NULL);
|
||||
g_signal_connect (text_radio, "notify::active", G_CALLBACK (update_image), NULL);
|
||||
g_signal_connect (show_grid, "notify::active", G_CALLBACK (update_image), NULL);
|
||||
g_signal_connect (show_extents, "notify::active", G_CALLBACK (update_image), NULL);
|
||||
g_signal_connect (show_pixels, "notify::active", G_CALLBACK (start_alpha_fade), NULL);
|
||||
g_signal_connect (show_outlines, "notify::active", G_CALLBACK (start_alpha_fade), NULL);
|
||||
|
||||
update_image ();
|
||||
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<object class="GtkAdjustment" id="scale_adj">
|
||||
<property name="upper">24</property>
|
||||
<property name="step-increment">1</property>
|
||||
<property name="page-increment">4</property>
|
||||
</object>
|
||||
<object class="GtkWindow" id="window">
|
||||
<property name="default-width">1080</property>
|
||||
<property name="default-height">430</property>
|
||||
<property name="default-width">1024</property>
|
||||
<property name="default-height">768</property>
|
||||
<child type="titlebar">
|
||||
<object class="GtkHeaderBar">
|
||||
<child type="title">
|
||||
@@ -87,35 +82,62 @@
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="label">Hinting</property>
|
||||
<property name="xalign">1</property>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
<object class="GtkCheckButton" id="show_pixels">
|
||||
<property name="label">Show _Pixels</property>
|
||||
<property name="use-underline">1</property>
|
||||
<property name="active">1</property>
|
||||
<layout>
|
||||
<property name="column">3</property>
|
||||
<property name="row">0</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBoxText" id="hinting">
|
||||
<property name="active">0</property>
|
||||
<property name="valign">center</property>
|
||||
<items>
|
||||
<item translatable="yes" id="none">None</item>
|
||||
<item translatable="yes" id="slight">Slight</item>
|
||||
<item translatable="yes" id="medium">Medium</item>
|
||||
<item translatable="yes" id="full">Full</item>
|
||||
</items>
|
||||
<object class="GtkCheckButton" id="show_outlines">
|
||||
<property name="label">Show _Outline</property>
|
||||
<property name="use-underline">1</property>
|
||||
<layout>
|
||||
<property name="column">3</property>
|
||||
<property name="row">1</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="label">_Hinting</property>
|
||||
<property name="use-underline">1</property>
|
||||
<property name="mnemonic-widget">hinting</property>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBoxText" id="hinting">
|
||||
<property name="active">0</property>
|
||||
<property name="valign">center</property>
|
||||
<items>
|
||||
<item translatable="yes" id="none">None</item>
|
||||
<item translatable="yes" id="slight">Slight</item>
|
||||
<item translatable="yes" id="medium">Medium</item>
|
||||
<item translatable="yes" id="full">Full</item>
|
||||
</items>
|
||||
</object>
|
||||
</child>
|
||||
<layout>
|
||||
<property name="column">4</property>
|
||||
<property name="column-span">2</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="hint_metrics">
|
||||
<property name="label">Hint Metrics</property>
|
||||
<object class="GtkCheckButton" id="antialias">
|
||||
<property name="label">_Antialias</property>
|
||||
<property name="use-underline">1</property>
|
||||
<property name="active">1</property>
|
||||
<layout>
|
||||
<property name="column">4</property>
|
||||
<property name="row">1</property>
|
||||
@@ -123,21 +145,31 @@
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="show_extents">
|
||||
<property name="label">Show Extents</property>
|
||||
<property name="active">1</property>
|
||||
<object class="GtkCheckButton" id="hint_metrics">
|
||||
<property name="label">Hint _Metrics</property>
|
||||
<property name="use-underline">1</property>
|
||||
<layout>
|
||||
<property name="column">5</property>
|
||||
<property name="row">1</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="show_extents">
|
||||
<property name="label">Show _Extents</property>
|
||||
<property name="use-underline">1</property>
|
||||
<layout>
|
||||
<property name="column">6</property>
|
||||
<property name="row">0</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="show_grid">
|
||||
<property name="active">1</property>
|
||||
<property name="label">Show Grid</property>
|
||||
<property name="label">Show _Grid</property>
|
||||
<property name="use-underline">1</property>
|
||||
<layout>
|
||||
<property name="column">5</property>
|
||||
<property name="column">6</property>
|
||||
<property name="row">1</property>
|
||||
</layout>
|
||||
</object>
|
||||
@@ -148,8 +180,19 @@
|
||||
<style>
|
||||
<class name="circular"/>
|
||||
</style>
|
||||
<child>
|
||||
<object class="GtkShortcutController">
|
||||
<property name="scope">managed</property>
|
||||
<child>
|
||||
<object class="GtkShortcut">
|
||||
<property name="trigger"><Control>plus</property>
|
||||
<property name="action">activate</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<layout>
|
||||
<property name="column">6</property>
|
||||
<property name="column">7</property>
|
||||
<property name="row">0</property>
|
||||
</layout>
|
||||
</object>
|
||||
@@ -160,8 +203,19 @@
|
||||
<style>
|
||||
<class name="circular"/>
|
||||
</style>
|
||||
<child>
|
||||
<object class="GtkShortcutController">
|
||||
<property name="scope">managed</property>
|
||||
<child>
|
||||
<object class="GtkShortcut">
|
||||
<property name="trigger"><Control>minus</property>
|
||||
<property name="action">activate</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<layout>
|
||||
<property name="column">6</property>
|
||||
<property name="column">7</property>
|
||||
<property name="row">1</property>
|
||||
</layout>
|
||||
</object>
|
||||
@@ -170,7 +224,7 @@
|
||||
<object class="GtkLabel">
|
||||
<property name="hexpand">1</property>
|
||||
<layout>
|
||||
<property name="column">7</property>
|
||||
<property name="column">8</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
+49
-22
@@ -61,12 +61,26 @@ show_page (GtkTextView *text_view,
|
||||
int page)
|
||||
{
|
||||
GtkTextBuffer *buffer;
|
||||
GtkTextIter iter;
|
||||
GtkTextIter iter, start;
|
||||
GtkTextMark *mark;
|
||||
GtkWidget *child;
|
||||
GtkTextChildAnchor *anchor;
|
||||
GtkEventController *controller;
|
||||
GtkTextTag *bold, *mono, *nobreaks;
|
||||
|
||||
buffer = gtk_text_view_get_buffer (text_view);
|
||||
|
||||
bold = gtk_text_buffer_create_tag (buffer, NULL,
|
||||
"weight", PANGO_WEIGHT_BOLD,
|
||||
"scale", PANGO_SCALE_X_LARGE,
|
||||
NULL);
|
||||
mono = gtk_text_buffer_create_tag (buffer, NULL,
|
||||
"family", "monospace",
|
||||
NULL);
|
||||
nobreaks = gtk_text_buffer_create_tag (buffer, NULL,
|
||||
"allow-breaks", FALSE,
|
||||
NULL);
|
||||
|
||||
gtk_text_buffer_set_text (buffer, "", 0);
|
||||
gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
|
||||
gtk_text_buffer_begin_irreversible_action (buffer);
|
||||
@@ -104,17 +118,22 @@ show_page (GtkTextView *text_view,
|
||||
}
|
||||
else if (page == 2)
|
||||
{
|
||||
GtkTextTag *tag;
|
||||
mark = gtk_text_buffer_create_mark (buffer, "mark", &iter, TRUE);
|
||||
|
||||
tag = gtk_text_buffer_create_tag (buffer, NULL,
|
||||
"weight", PANGO_WEIGHT_BOLD,
|
||||
"scale", PANGO_SCALE_X_LARGE,
|
||||
NULL);
|
||||
gtk_text_buffer_insert_with_tags (buffer, &iter, "tag", -1, tag, NULL);
|
||||
tag = gtk_text_buffer_create_tag (buffer, NULL,
|
||||
"family", "monospace",
|
||||
NULL);
|
||||
gtk_text_buffer_insert_with_tags (buffer, &iter, " / tag / ", -1, tag, NULL);
|
||||
gtk_text_buffer_insert_with_tags (buffer, &iter, "tag", -1, bold, NULL);
|
||||
gtk_text_buffer_insert (buffer, &iter, " /", -1);
|
||||
|
||||
gtk_text_buffer_get_iter_at_mark (buffer, &start, mark);
|
||||
gtk_text_buffer_apply_tag (buffer, nobreaks, &start, &iter);
|
||||
gtk_text_buffer_insert (buffer, &iter, " ", -1);
|
||||
|
||||
gtk_text_buffer_move_mark (buffer, mark, &iter);
|
||||
gtk_text_buffer_insert_with_tags (buffer, &iter, "tag", -1, mono, NULL);
|
||||
gtk_text_buffer_insert (buffer, &iter, " /", -1);
|
||||
|
||||
gtk_text_buffer_get_iter_at_mark (buffer, &start, mark);
|
||||
gtk_text_buffer_apply_tag (buffer, nobreaks, &start, &iter);
|
||||
gtk_text_buffer_insert (buffer, &iter, " ", -1);
|
||||
|
||||
anchor = gtk_text_buffer_create_child_anchor (buffer, &iter);
|
||||
child = gtk_image_new_from_icon_name ("audio-volume-high-symbolic");
|
||||
@@ -132,20 +151,26 @@ show_page (GtkTextView *text_view,
|
||||
"behavior of mouse and key presses, “lock” a range of text so the "
|
||||
"user can't edit it, or countless other things.\n", -1);
|
||||
insert_link (buffer, &iter, "Go back", 1);
|
||||
|
||||
gtk_text_buffer_delete_mark (buffer, mark);
|
||||
}
|
||||
else if (page == 3)
|
||||
{
|
||||
GtkTextTag *tag;
|
||||
mark = gtk_text_buffer_create_mark (buffer, "mark", &iter, TRUE);
|
||||
|
||||
tag = gtk_text_buffer_create_tag (buffer, NULL,
|
||||
"weight", PANGO_WEIGHT_BOLD,
|
||||
"scale", PANGO_SCALE_X_LARGE,
|
||||
NULL);
|
||||
gtk_text_buffer_insert_with_tags (buffer, &iter, "hypertext", -1, tag, NULL);
|
||||
tag = gtk_text_buffer_create_tag (buffer, NULL,
|
||||
"family", "monospace",
|
||||
NULL);
|
||||
gtk_text_buffer_insert_with_tags (buffer, &iter, " / ˈhaɪ pərˌtɛkst / ", -1, tag, NULL);
|
||||
gtk_text_buffer_insert_with_tags (buffer, &iter, "hypertext", -1, bold, NULL);
|
||||
gtk_text_buffer_insert (buffer, &iter, " /", -1);
|
||||
|
||||
gtk_text_buffer_get_iter_at_mark (buffer, &start, mark);
|
||||
gtk_text_buffer_apply_tag (buffer, nobreaks, &start, &iter);
|
||||
gtk_text_buffer_insert (buffer, &iter, " ", -1);
|
||||
|
||||
gtk_text_buffer_move_mark (buffer, mark, &iter);
|
||||
gtk_text_buffer_insert_with_tags (buffer, &iter, "ˈhaɪ pərˌtɛkst", -1, mono, NULL);
|
||||
gtk_text_buffer_insert (buffer, &iter, " /", -1);
|
||||
gtk_text_buffer_get_iter_at_mark (buffer, &start, mark);
|
||||
gtk_text_buffer_apply_tag (buffer, nobreaks, &start, &iter);
|
||||
gtk_text_buffer_insert (buffer, &iter, " ", -1);
|
||||
|
||||
anchor = gtk_text_buffer_create_child_anchor (buffer, &iter);
|
||||
child = gtk_image_new_from_icon_name ("audio-volume-high-symbolic");
|
||||
@@ -159,6 +184,8 @@ show_page (GtkTextView *text_view,
|
||||
"Machine-readable text that is not sequential but is organized "
|
||||
"so that related items of information are connected.\n", -1);
|
||||
insert_link (buffer, &iter, "Go back", 1);
|
||||
|
||||
gtk_text_buffer_delete_mark (buffer, mark);
|
||||
}
|
||||
gtk_text_buffer_end_irreversible_action (buffer);
|
||||
}
|
||||
@@ -358,7 +385,7 @@ do_hypertext (GtkWidget *do_widget)
|
||||
|
||||
sw = gtk_scrolled_window_new ();
|
||||
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
|
||||
GTK_POLICY_AUTOMATIC,
|
||||
GTK_POLICY_NEVER,
|
||||
GTK_POLICY_AUTOMATIC);
|
||||
gtk_window_set_child (GTK_WINDOW (window), sw);
|
||||
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), view);
|
||||
|
||||
@@ -58,7 +58,7 @@ do_markup (GtkWidget *do_widget)
|
||||
window = gtk_window_new ();
|
||||
gtk_window_set_display (GTK_WINDOW (window),
|
||||
gtk_widget_get_display (do_widget));
|
||||
gtk_window_set_default_size (GTK_WINDOW (window), 450, 450);
|
||||
gtk_window_set_default_size (GTK_WINDOW (window), 600, 680);
|
||||
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
|
||||
|
||||
stack = gtk_stack_new ();
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
|
||||
Text sizes: <span size="xx-small">tiny</span> <span size="x-small">very small</span> <span size="small">small</span> <span size="medium">normal</span> <span size="large">large</span> <span size="x-large">very large</span> <span size="xx-large">huge</span>
|
||||
Text sizes: <span size="xx-small">tiny </span><span size="x-small">very small </span><span size="small">small </span><span size="medium">normal </span><span size="large">large </span><span size="x-large">very large </span><span size="xx-large">huge</span>
|
||||
|
||||
Text styles: <span style="normal">Normal</span> <span style="italic">Italic</span> <span style="oblique">Olique</span>
|
||||
|
||||
Text weights: <span weight="thin">thin</span> <span weight="light">light</span> <span weight="normal">normal</span> <span weight="bold">bold</span> <span weight="ultraheavy">ultraheavy</span>
|
||||
|
||||
Text <span color="gray">c<span color="green">o</span>l<span color="tomato">o</span>rs</span> and <span background="pink">backgrounds</span>
|
||||
|
||||
@@ -15,6 +19,12 @@ OpenType font features: <span font_desc="sans regular" font_features="dlig=0">fe
|
||||
|
||||
Shortcuts: <tt>Monospace</tt> – <b>Bold</b> – <i>Italic</i> – <big>Big</big> – <small>Small</small> – <u>Underlined</u> – <s>Strikethrough</s> – Super<sup>script</sup> – Sub<sub>script</sub>
|
||||
|
||||
<span allow_breaks="false">A</span> hyphenation algorithm is a set of rules, especially one codified for implementation in a computer program, that decides at which points a word can be broken over two lines with a hyphen. For example, a hyphenation algorithm might decide that impeachment can be broken as <span allow_breaks="false">impeach‧ment</span> or <span allow_breaks="false">im‧peachment</span> but not <span allow_breaks="false">impe‧achment.</span>
|
||||
hyphenation algorithm is a <span allow_breaks="false" style="italic">set of rules</span>, especially one codified for implementation in a computer program, that decides at which points a word can be broken over two lines with a hyphen. For example, a hyphenation algorithm might decide that impeachment can be broken as impeach‧ment or im‧peachment but not impe‧achment.
|
||||
|
||||
<span insert_hyphens="false">one/two three/four five/six seven/eight nine/ten</span>
|
||||
|
||||
<span line_height='1.33'>Line height: This is an example of widely spaced text. It was achieved by setting the line-height factor to 1.33. You can set the line-height factor to any value between 0 and 10.
|
||||
Note that the line height affects the spacing between paragraphs as well as between the wrapped lines inside a paragraph.</span>
|
||||
|
||||
Transforms: <span text_transform='uppercase'>straße</span> <span text_transform='capitalize'>up, up and away</span>
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ demos = files([
|
||||
'paintable_animated.c',
|
||||
'paintable_emblem.c',
|
||||
'paintable_mediastream.c',
|
||||
'paintable_symbolic.c',
|
||||
'panes.c',
|
||||
'password_entry.c',
|
||||
'peg_solitaire.c',
|
||||
|
||||
+13
-11
@@ -45,26 +45,27 @@ struct _GtkNuclearIconClass
|
||||
* so that it can be called from all the other demos, too.
|
||||
*/
|
||||
void
|
||||
gtk_nuclear_snapshot (GtkSnapshot *snapshot,
|
||||
double width,
|
||||
double height,
|
||||
double rotation,
|
||||
gboolean draw_background)
|
||||
gtk_nuclear_snapshot (GtkSnapshot *snapshot,
|
||||
const GdkRGBA *foreground,
|
||||
const GdkRGBA *background,
|
||||
double width,
|
||||
double height,
|
||||
double rotation)
|
||||
{
|
||||
#define RADIUS 0.3
|
||||
cairo_t *cr;
|
||||
double size;
|
||||
|
||||
if (draw_background)
|
||||
gtk_snapshot_append_color (snapshot,
|
||||
&(GdkRGBA) { 0.9, 0.75, 0.15, 1.0 },
|
||||
&GRAPHENE_RECT_INIT (0, 0, width, height));
|
||||
gtk_snapshot_append_color (snapshot,
|
||||
background,
|
||||
&GRAPHENE_RECT_INIT (0, 0, width, height));
|
||||
|
||||
size = MIN (width, height);
|
||||
cr = gtk_snapshot_append_cairo (snapshot,
|
||||
&GRAPHENE_RECT_INIT ((width - size) / 2.0,
|
||||
(height - size) / 2.0,
|
||||
size, size));
|
||||
gdk_cairo_set_source_rgba (cr, foreground);
|
||||
cairo_translate (cr, width / 2.0, height / 2.0);
|
||||
cairo_scale (cr, size, size);
|
||||
cairo_rotate (cr, rotation);
|
||||
@@ -94,9 +95,10 @@ gtk_nuclear_icon_snapshot (GdkPaintable *paintable,
|
||||
*/
|
||||
|
||||
gtk_nuclear_snapshot (snapshot,
|
||||
&(GdkRGBA) { 0, 0, 0, 1 }, /* black */
|
||||
&(GdkRGBA) { 0.9, 0.75, 0.15, 1.0 }, /* yellow */
|
||||
width, height,
|
||||
nuclear->rotation,
|
||||
TRUE);
|
||||
nuclear->rotation);
|
||||
}
|
||||
|
||||
static GdkPaintableFlags
|
||||
|
||||
@@ -4,10 +4,11 @@
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
void gtk_nuclear_snapshot (GtkSnapshot *snapshot,
|
||||
const GdkRGBA *foreground,
|
||||
const GdkRGBA *background,
|
||||
double width,
|
||||
double height,
|
||||
double rotation,
|
||||
gboolean draw_background);
|
||||
double rotation);
|
||||
|
||||
GdkPaintable * gtk_nuclear_icon_new (double rotation);
|
||||
GdkPaintable * gtk_nuclear_animation_new (gboolean draw_background);
|
||||
|
||||
@@ -65,9 +65,12 @@ gtk_nuclear_animation_snapshot (GdkPaintable *paintable,
|
||||
|
||||
/* We call the function from the previous example here. */
|
||||
gtk_nuclear_snapshot (snapshot,
|
||||
&(GdkRGBA) { 0, 0, 0, 1 }, /* black */
|
||||
nuclear->draw_background
|
||||
? &(GdkRGBA) { 0.9, 0.75, 0.15, 1.0 } /* yellow */
|
||||
: &(GdkRGBA) { 0, 0, 0, 0 }, /* transparent */
|
||||
width, height,
|
||||
2 * G_PI * nuclear->progress / MAX_PROGRESS,
|
||||
nuclear->draw_background);
|
||||
2 * G_PI * nuclear->progress / MAX_PROGRESS);
|
||||
}
|
||||
|
||||
static GdkPaintable *
|
||||
|
||||
@@ -73,9 +73,10 @@ gtk_nuclear_media_stream_snapshot (GdkPaintable *paintable,
|
||||
|
||||
/* We call the function from the previous example here. */
|
||||
gtk_nuclear_snapshot (snapshot,
|
||||
&(GdkRGBA) { 0, 0, 0, 1 }, /* black */
|
||||
&(GdkRGBA) { 0.9, 0.75, 0.15, 1.0 }, /* yellow */
|
||||
width, height,
|
||||
2 * G_PI * nuclear->progress / DURATION,
|
||||
TRUE);
|
||||
2 * G_PI * nuclear->progress / DURATION);
|
||||
}
|
||||
|
||||
static GdkPaintable *
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
/* Paintable/Symbolic Paintable
|
||||
*
|
||||
* GdkPaintables can be made to follow the theme's colors. GTK calls
|
||||
* icons that do this symbolic icons, paintables that want to have
|
||||
* the same effect can implement the GtkSymbolicPaintable interface.
|
||||
*
|
||||
* We will adapt the original paintable example by adding the ability
|
||||
* to recolor the paintable based on the symbolic colors.
|
||||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "paintable.h"
|
||||
|
||||
static GtkWidget *window = NULL;
|
||||
|
||||
/* First, add the boilerplate for the object itself.
|
||||
* This part would normally go in the header.
|
||||
*/
|
||||
#define GTK_TYPE_NUCLEAR_SYMBOLIC (gtk_nuclear_symbolic_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (GtkNuclearSymbolic, gtk_nuclear_symbolic, GTK, NUCLEAR_SYMBOLIC, GObject)
|
||||
|
||||
/* Declare a few warning levels, so we can pick colors based on them */
|
||||
typedef enum
|
||||
{
|
||||
WARNING_NONE,
|
||||
WARNING_ALERT,
|
||||
WARNING_EMERGENCY
|
||||
} WarningLevel;
|
||||
|
||||
/* Declare the struct. */
|
||||
struct _GtkNuclearSymbolic
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
WarningLevel warning_level;
|
||||
};
|
||||
|
||||
struct _GtkNuclearSymbolicClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
/* Add a function to draw the nuclear icon in the given colors */
|
||||
static void
|
||||
gtk_nuclear_symbolic_snapshot_symbolic (GtkSymbolicPaintable *paintable,
|
||||
GdkSnapshot *snapshot,
|
||||
double width,
|
||||
double height,
|
||||
const GdkRGBA *colors,
|
||||
gsize n_colors)
|
||||
{
|
||||
GtkNuclearSymbolic *self = GTK_NUCLEAR_SYMBOLIC (paintable);
|
||||
static const GdkRGBA transparent = { 0, };
|
||||
const GdkRGBA *bg_color;
|
||||
|
||||
/* select the right background color from the warning level */
|
||||
switch (self->warning_level)
|
||||
{
|
||||
case WARNING_NONE:
|
||||
bg_color = &transparent;
|
||||
break;
|
||||
case WARNING_ALERT:
|
||||
bg_color = &colors[GTK_SYMBOLIC_COLOR_WARNING];
|
||||
break;
|
||||
case WARNING_EMERGENCY:
|
||||
bg_color = &colors[GTK_SYMBOLIC_COLOR_ERROR];
|
||||
break;
|
||||
default:
|
||||
/* This should never happen, but we better do defensive coding
|
||||
* with this critical icon */
|
||||
g_assert_not_reached ();
|
||||
bg_color = &transparent;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Draw the icon with the selected warning color */
|
||||
gtk_nuclear_snapshot (snapshot,
|
||||
&colors[GTK_SYMBOLIC_COLOR_FOREGROUND],
|
||||
bg_color,
|
||||
width, height,
|
||||
0);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_nuclear_symbolic_symbolic_paintable_init (GtkSymbolicPaintableInterface *iface)
|
||||
{
|
||||
iface->snapshot_symbolic = gtk_nuclear_symbolic_snapshot_symbolic;
|
||||
}
|
||||
|
||||
/* We need to implement the functionality required by the GdkPaintable interface */
|
||||
static void
|
||||
gtk_nuclear_symbolic_snapshot (GdkPaintable *paintable,
|
||||
GdkSnapshot *snapshot,
|
||||
double width,
|
||||
double height)
|
||||
{
|
||||
/* Calling this function without passing a color is a neat trick
|
||||
* to make GTK use default colors and otherwise forward the call
|
||||
* to the snapshotting function above.
|
||||
*/
|
||||
gtk_symbolic_paintable_snapshot_symbolic (GTK_SYMBOLIC_PAINTABLE (paintable),
|
||||
snapshot,
|
||||
width, height,
|
||||
NULL, 0);
|
||||
}
|
||||
|
||||
static GdkPaintableFlags
|
||||
gtk_nuclear_symbolic_get_flags (GdkPaintable *paintable)
|
||||
{
|
||||
/* This image has a static size, but the contents may change:
|
||||
* We draw different things when the warning level changes.
|
||||
*/
|
||||
return GDK_PAINTABLE_STATIC_SIZE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_nuclear_symbolic_paintable_init (GdkPaintableInterface *iface)
|
||||
{
|
||||
iface->snapshot = gtk_nuclear_symbolic_snapshot;
|
||||
iface->get_flags = gtk_nuclear_symbolic_get_flags;
|
||||
}
|
||||
|
||||
/* When defining the GType, we need to implement bot the GdkPaintable
|
||||
* and the GtkSymbolicPaintable interface */
|
||||
G_DEFINE_TYPE_WITH_CODE (GtkNuclearSymbolic, gtk_nuclear_symbolic, G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
|
||||
gtk_nuclear_symbolic_paintable_init)
|
||||
G_IMPLEMENT_INTERFACE (GTK_TYPE_SYMBOLIC_PAINTABLE,
|
||||
gtk_nuclear_symbolic_symbolic_paintable_init))
|
||||
|
||||
static void
|
||||
gtk_nuclear_symbolic_class_init (GtkNuclearSymbolicClass *klass)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_nuclear_symbolic_init (GtkNuclearSymbolic *nuclear)
|
||||
{
|
||||
}
|
||||
|
||||
/* And finally, we add the simple constructor we declared in the header. */
|
||||
GdkPaintable *
|
||||
gtk_nuclear_symbolic_new (void)
|
||||
{
|
||||
return g_object_new (GTK_TYPE_NUCLEAR_SYMBOLIC, NULL);
|
||||
}
|
||||
|
||||
/* Add some fun feature to the button */
|
||||
static void
|
||||
nuclear_button_clicked (GtkButton *button,
|
||||
GtkNuclearSymbolic *nuclear)
|
||||
{
|
||||
if (nuclear->warning_level >= WARNING_EMERGENCY)
|
||||
{
|
||||
/* On maximum warning level, reset the warning */
|
||||
nuclear->warning_level = WARNING_NONE;
|
||||
/* And sometimes (but not always to confuse people)
|
||||
* close the window.
|
||||
*/
|
||||
if (g_random_boolean ())
|
||||
gtk_window_close (GTK_WINDOW (window));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Otherwise just increase the warning level */
|
||||
nuclear->warning_level++;
|
||||
}
|
||||
|
||||
/* Don't forget to emit the signal causing the paintable to redraw.
|
||||
* Changing the warning level changes the background color after all.
|
||||
*/
|
||||
gdk_paintable_invalidate_contents (GDK_PAINTABLE (nuclear));
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
do_paintable_symbolic (GtkWidget *do_widget)
|
||||
{
|
||||
GdkPaintable *nuclear;
|
||||
GtkWidget *image, *button;
|
||||
|
||||
if (!window)
|
||||
{
|
||||
window = gtk_window_new ();
|
||||
gtk_window_set_display (GTK_WINDOW (window),
|
||||
gtk_widget_get_display (do_widget));
|
||||
gtk_window_set_title (GTK_WINDOW (window), "Don't click!");
|
||||
gtk_window_set_default_size (GTK_WINDOW (window), 200, 200);
|
||||
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
|
||||
|
||||
button = gtk_button_new ();
|
||||
gtk_window_set_child (GTK_WINDOW (window), button);
|
||||
|
||||
nuclear = gtk_nuclear_symbolic_new ();
|
||||
image = gtk_image_new_from_paintable (nuclear);
|
||||
|
||||
gtk_button_set_child (GTK_BUTTON (button), image);
|
||||
g_signal_connect (button, "clicked", G_CALLBACK (nuclear_button_clicked), nuclear);
|
||||
g_object_unref (nuclear);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_widget_show (window);
|
||||
else
|
||||
gtk_window_destroy (GTK_WINDOW (window));
|
||||
|
||||
return window;
|
||||
}
|
||||
@@ -24,7 +24,6 @@
|
||||
#include "gtkrendererpaintableprivate.h"
|
||||
|
||||
#include "gsk/gskrendernodeparserprivate.h"
|
||||
#include "gsk/gl/gskglrenderer.h"
|
||||
#include "gsk/ngl/gsknglrenderer.h"
|
||||
#ifdef GDK_WINDOWING_BROADWAY
|
||||
#include "gsk/broadway/gskbroadwayrenderer.h"
|
||||
@@ -61,7 +60,7 @@ struct _NodeEditorWindow
|
||||
|
||||
GtkWidget *renderer_listbox;
|
||||
GListStore *renderers;
|
||||
GdkPaintable *paintable;
|
||||
GskRenderNode *node;
|
||||
|
||||
GFileMonitor *file_monitor;
|
||||
|
||||
@@ -168,7 +167,6 @@ static void
|
||||
text_changed (GtkTextBuffer *buffer,
|
||||
NodeEditorWindow *self)
|
||||
{
|
||||
GskRenderNode *node;
|
||||
char *text;
|
||||
GBytes *bytes;
|
||||
GtkTextIter iter;
|
||||
@@ -179,10 +177,12 @@ text_changed (GtkTextBuffer *buffer,
|
||||
text_buffer_remove_all_tags (self->text_buffer);
|
||||
bytes = g_bytes_new_take (text, strlen (text));
|
||||
|
||||
g_clear_pointer (&self->node, gsk_render_node_unref);
|
||||
|
||||
/* If this is too slow, go fix the parser performance */
|
||||
node = gsk_render_node_deserialize (bytes, deserialize_error_func, self);
|
||||
self->node = gsk_render_node_deserialize (bytes, deserialize_error_func, self);
|
||||
g_bytes_unref (bytes);
|
||||
if (node)
|
||||
if (self->node)
|
||||
{
|
||||
/* XXX: Is this code necessary or can we have API to turn nodes into paintables? */
|
||||
GtkSnapshot *snapshot;
|
||||
@@ -191,10 +191,9 @@ text_changed (GtkTextBuffer *buffer,
|
||||
guint i;
|
||||
|
||||
snapshot = gtk_snapshot_new ();
|
||||
gsk_render_node_get_bounds (node, &bounds);
|
||||
gsk_render_node_get_bounds (self->node, &bounds);
|
||||
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (- bounds.origin.x, - bounds.origin.y));
|
||||
gtk_snapshot_append_node (snapshot, node);
|
||||
gsk_render_node_unref (node);
|
||||
gtk_snapshot_append_node (snapshot, self->node);
|
||||
paintable = gtk_snapshot_free_to_paintable (snapshot, &bounds.size);
|
||||
gtk_picture_set_paintable (GTK_PICTURE (self->picture), paintable);
|
||||
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (self->renderers)); i++)
|
||||
@@ -338,15 +337,9 @@ text_view_query_tooltip_cb (GtkWidget *widget,
|
||||
}
|
||||
|
||||
static gboolean
|
||||
load_file_contents (NodeEditorWindow *self,
|
||||
GFile *file)
|
||||
load_bytes (NodeEditorWindow *self,
|
||||
GBytes *bytes)
|
||||
{
|
||||
GBytes *bytes;
|
||||
|
||||
bytes = g_file_load_bytes (file, NULL, NULL, NULL);
|
||||
if (bytes == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (!g_utf8_validate (g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes), NULL))
|
||||
{
|
||||
g_bytes_unref (bytes);
|
||||
@@ -362,6 +355,105 @@ load_file_contents (NodeEditorWindow *self,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
load_file_contents (NodeEditorWindow *self,
|
||||
GFile *file)
|
||||
{
|
||||
GBytes *bytes;
|
||||
|
||||
bytes = g_file_load_bytes (file, NULL, NULL, NULL);
|
||||
if (bytes == NULL)
|
||||
return FALSE;
|
||||
|
||||
return load_bytes (self, bytes);
|
||||
}
|
||||
|
||||
static GdkContentProvider *
|
||||
on_picture_drag_prepare_cb (GtkDragSource *source,
|
||||
double x,
|
||||
double y,
|
||||
NodeEditorWindow *self)
|
||||
{
|
||||
if (self->node == NULL)
|
||||
return NULL;
|
||||
|
||||
return gdk_content_provider_new_typed (GSK_TYPE_RENDER_NODE, self->node);
|
||||
}
|
||||
|
||||
static void
|
||||
on_picture_drop_read_done_cb (GObject *source,
|
||||
GAsyncResult *res,
|
||||
gpointer data)
|
||||
{
|
||||
NodeEditorWindow *self = data;
|
||||
GOutputStream *stream = G_OUTPUT_STREAM (source);
|
||||
GdkDrop *drop = g_object_get_data (source, "drop");
|
||||
GdkDragAction action = 0;
|
||||
GBytes *bytes;
|
||||
|
||||
if (g_output_stream_splice_finish (stream, res, NULL) >= 0)
|
||||
{
|
||||
bytes = g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (stream));
|
||||
if (load_bytes (self, bytes))
|
||||
action = GDK_ACTION_COPY;
|
||||
}
|
||||
|
||||
g_object_unref (self);
|
||||
gdk_drop_finish (drop, action);
|
||||
g_object_unref (drop);
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
on_picture_drop_read_cb (GObject *source,
|
||||
GAsyncResult *res,
|
||||
gpointer data)
|
||||
{
|
||||
NodeEditorWindow *self = data;
|
||||
GdkDrop *drop = GDK_DROP (source);
|
||||
GInputStream *input;
|
||||
GOutputStream *output;
|
||||
|
||||
input = gdk_drop_read_finish (drop, res, NULL, NULL);
|
||||
if (input == NULL)
|
||||
{
|
||||
g_object_unref (self);
|
||||
gdk_drop_finish (drop, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
output = g_memory_output_stream_new_resizable ();
|
||||
g_object_set_data (G_OBJECT (output), "drop", drop);
|
||||
g_object_ref (drop);
|
||||
|
||||
g_output_stream_splice_async (output,
|
||||
input,
|
||||
G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
|
||||
G_PRIORITY_DEFAULT,
|
||||
NULL,
|
||||
on_picture_drop_read_done_cb,
|
||||
self);
|
||||
g_object_unref (output);
|
||||
g_object_unref (input);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
on_picture_drop_cb (GtkDropTargetAsync *dest,
|
||||
GdkDrop *drop,
|
||||
double x,
|
||||
double y,
|
||||
NodeEditorWindow *self)
|
||||
{
|
||||
gdk_drop_read_async (drop,
|
||||
(const char *[2]) { "application/x-gtk-render-node", NULL },
|
||||
G_PRIORITY_DEFAULT,
|
||||
NULL,
|
||||
on_picture_drop_read_cb,
|
||||
g_object_ref (self));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
file_changed_cb (GFileMonitor *monitor,
|
||||
GFile *file,
|
||||
@@ -736,6 +828,7 @@ node_editor_window_finalize (GObject *object)
|
||||
|
||||
g_array_free (self->errors, TRUE);
|
||||
|
||||
g_clear_pointer (&self->node, gsk_render_node_unref);
|
||||
g_clear_object (&self->renderers);
|
||||
|
||||
G_OBJECT_CLASS (node_editor_window_parent_class)->finalize (object);
|
||||
@@ -778,12 +871,9 @@ node_editor_window_realize (GtkWidget *widget)
|
||||
NULL,
|
||||
"Default");
|
||||
#endif
|
||||
node_editor_window_add_renderer (self,
|
||||
gsk_gl_renderer_new (),
|
||||
"OpenGL");
|
||||
node_editor_window_add_renderer (self,
|
||||
gsk_ngl_renderer_new (),
|
||||
"NGL");
|
||||
"OpenGL");
|
||||
#ifdef GDK_RENDERING_VULKAN
|
||||
node_editor_window_add_renderer (self,
|
||||
gsk_vulkan_renderer_new (),
|
||||
@@ -848,6 +938,8 @@ node_editor_window_class_init (NodeEditorWindowClass *class)
|
||||
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);
|
||||
gtk_widget_class_bind_template_callback (widget_class, on_picture_drag_prepare_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, on_picture_drop_cb);
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
|
||||
@@ -200,6 +200,19 @@
|
||||
<property name="can-shrink">0</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<child>
|
||||
<object class="GtkDragSource">
|
||||
<property name="actions">copy</property>
|
||||
<signal name="prepare" handler="on_picture_drag_prepare_cb" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkDropTargetAsync">
|
||||
<property name="actions">copy</property>
|
||||
<property name="formats">application/x-gtk-render-node</property>
|
||||
<signal name="drop" handler="on_picture_drop_cb" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
||||
@@ -162,6 +162,8 @@ 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-transform| [CSS Text Level 3](https://www.w3.org/TR/css-text-3/#text-transform-property) | CSS allows full-width and full-size-kana. Since 4.6 |
|
||||
|line-height| [CSS Inline Layout Level 3](https://www.w3.org/TR/css-inline-3/#line-height-property) | Since 4.6 |
|
||||
|text-decoration-line| [CSS Text Decoration Level 3](https://www.w3.org/TR/css-text-decor-3/#text-decoration-line-property) | |
|
||||
|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 |
|
||||
|
||||
@@ -137,6 +137,14 @@ capture phase, and key bindings locally, during the target phase.
|
||||
Under the hood, all shortcuts are represented as instances of `GtkShortcut`,
|
||||
and they are managed by `GtkShortcutController`.
|
||||
|
||||
## Text input
|
||||
|
||||
When actual text input is needed (i.e. not just keyboard shortcuts),
|
||||
input method support can be added to a widget by connecting an input
|
||||
method context and listening to its `::commit` signal. To create a new
|
||||
input method context, use gtk_im_multicontext_new(), to provide it with
|
||||
input, use gtk_event_controller_key_set_im_context().
|
||||
|
||||
## Event controllers and gestures
|
||||
|
||||
Event controllers are standalone objects that can perform
|
||||
|
||||
@@ -14,14 +14,19 @@ instructions, binary downloads, etc, can be found
|
||||
The Win32 GDK backend can be influenced with some additional environment
|
||||
variables.
|
||||
|
||||
### GDK_IGNORE_WINTAB
|
||||
### GDK_WIN32_TABLET_INPUT_API
|
||||
|
||||
If this variable is set, GTK doesn't use the Wintab API for tablet support.
|
||||
If this variable is set, it determines the API that GTK uses for tablet support.
|
||||
The possible values are:
|
||||
|
||||
### GDK_USE_WINTAB
|
||||
`none`
|
||||
: Disables tablet support
|
||||
|
||||
If this variable is set, GTK uses the Wintab API for tablet support.
|
||||
This is the default.
|
||||
`wintab`
|
||||
: Use the Wintab API
|
||||
|
||||
`winpointer`
|
||||
: Use the Windows Pointer Input Stack API. This is the default.
|
||||
|
||||
## Windows-specific handling of cursors
|
||||
|
||||
|
||||
@@ -5,6 +5,10 @@ gidocgen = find_program('gi-docgen', required: get_option('gtk_doc'))
|
||||
|
||||
docs_dir = gtk_datadir / 'doc'
|
||||
|
||||
if get_option('gtk_doc') and not build_gir
|
||||
error('API reference requires introspection.')
|
||||
endif
|
||||
|
||||
subdir('gdk')
|
||||
subdir('gsk')
|
||||
subdir('gtk')
|
||||
|
||||
@@ -132,7 +132,7 @@ static const GdkDebugKey gdk_debug_keys[] = {
|
||||
{ "gl-wgl", GDK_DEBUG_GL_WGL, "Use WGL on Windows" },
|
||||
{ "vulkan-disable", GDK_DEBUG_VULKAN_DISABLE, "Disable Vulkan support" },
|
||||
{ "vulkan-validate", GDK_DEBUG_VULKAN_VALIDATE, "Load the Vulkan validation layer" },
|
||||
{ "default-settings",GDK_DEBUG_DEFAULT_SETTINGS, "Force default values for xsettings" },
|
||||
{ "default-settings",GDK_DEBUG_DEFAULT_SETTINGS, "Force default values for xsettings", TRUE },
|
||||
};
|
||||
|
||||
|
||||
|
||||
+8
-2
@@ -635,7 +635,7 @@ gdk_clipboard_read_internal (GdkClipboard *clipboard,
|
||||
/**
|
||||
* gdk_clipboard_read_async:
|
||||
* @clipboard: a `GdkClipboard`
|
||||
* @mime_types: a %NULL-terminated array of mime types to choose from
|
||||
* @mime_types: (array zero-terminated=1): a %NULL-terminated array of mime types to choose from
|
||||
* @io_priority: the I/O priority of the request
|
||||
* @cancellable: (nullable): optional `GCancellable` object
|
||||
* @callback: (scope async): callback to call when the request is satisfied
|
||||
@@ -1253,9 +1253,15 @@ gdk_clipboard_set_content (GdkClipboard *clipboard,
|
||||
* @...: value contents conforming to @type
|
||||
*
|
||||
* Sets the clipboard to contain the value collected from the given varargs.
|
||||
*
|
||||
* Values should be passed the same way they are passed to other value
|
||||
* collecting APIs, such as [`method@GObject.Object.set`] or
|
||||
* [`id@g_signal_emit`].
|
||||
*
|
||||
* ```c
|
||||
* gdk_clipboard_set (clipboard, GTK_TYPE_TEXT_BUFFER, buffer);
|
||||
* gdk_clipboard_set (clipboard, GTK_TYPE_STRING, "Hello World");
|
||||
*
|
||||
* gdk_clipboard_set (clipboard, GDK_TYPE_TEXTURE, some_texture);
|
||||
* ```
|
||||
*/
|
||||
void
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
#include "filetransferportalprivate.h"
|
||||
#include "gdktexture.h"
|
||||
#include "gdkrgbaprivate.h"
|
||||
#include "gdkpng.h"
|
||||
#include "gdktiff.h"
|
||||
|
||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||
|
||||
@@ -553,6 +555,8 @@ gdk_content_deserialize_async (GInputStream *stream,
|
||||
Deserializer *deserializer;
|
||||
|
||||
g_return_if_fail (G_IS_INPUT_STREAM (stream));
|
||||
g_return_if_fail (mime_type != NULL);
|
||||
g_return_if_fail (G_TYPE_IS_VALUE_TYPE (type));
|
||||
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
|
||||
g_return_if_fail (callback != NULL);
|
||||
|
||||
@@ -591,7 +595,10 @@ gdk_content_deserialize_finish (GAsyncResult *result,
|
||||
g_return_val_if_fail (GDK_IS_CONTENT_DESERIALIZER (result), FALSE);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
||||
deserializer = GDK_CONTENT_DESERIALIZER (result);
|
||||
g_return_val_if_fail (G_VALUE_HOLDS (value, G_VALUE_TYPE (&deserializer->value)), FALSE);
|
||||
if (G_VALUE_TYPE (value) == 0)
|
||||
g_value_init (value, G_VALUE_TYPE (&deserializer->value));
|
||||
else
|
||||
g_return_val_if_fail (G_VALUE_HOLDS (value, G_VALUE_TYPE (&deserializer->value)), FALSE);
|
||||
|
||||
if (deserializer->error)
|
||||
{
|
||||
@@ -650,6 +657,66 @@ pixbuf_deserializer (GdkContentDeserializer *deserializer)
|
||||
deserializer);
|
||||
}
|
||||
|
||||
static void
|
||||
png_deserializer_finish (GObject *source,
|
||||
GAsyncResult *res,
|
||||
gpointer deserializer)
|
||||
{
|
||||
GdkTexture *texture;
|
||||
GValue *value;
|
||||
GError *error = NULL;
|
||||
|
||||
texture = gdk_load_png_finish (res, &error);
|
||||
if (texture == NULL)
|
||||
{
|
||||
gdk_content_deserializer_return_error (deserializer, error);
|
||||
return;
|
||||
}
|
||||
|
||||
value = gdk_content_deserializer_get_value (deserializer);
|
||||
g_value_take_object (value, texture);
|
||||
gdk_content_deserializer_return_success (deserializer);
|
||||
}
|
||||
|
||||
static void
|
||||
png_deserializer (GdkContentDeserializer *deserializer)
|
||||
{
|
||||
gdk_load_png_async (gdk_content_deserializer_get_input_stream (deserializer),
|
||||
gdk_content_deserializer_get_cancellable (deserializer),
|
||||
png_deserializer_finish,
|
||||
deserializer);
|
||||
}
|
||||
|
||||
static void
|
||||
tiff_deserializer_finish (GObject *source,
|
||||
GAsyncResult *res,
|
||||
gpointer deserializer)
|
||||
{
|
||||
GdkTexture *texture;
|
||||
GValue *value;
|
||||
GError *error = NULL;
|
||||
|
||||
texture = gdk_load_tiff_finish (res, &error);
|
||||
if (texture == NULL)
|
||||
{
|
||||
gdk_content_deserializer_return_error (deserializer, error);
|
||||
return;
|
||||
}
|
||||
|
||||
value = gdk_content_deserializer_get_value (deserializer);
|
||||
g_value_take_object (value, texture);
|
||||
gdk_content_deserializer_return_success (deserializer);
|
||||
}
|
||||
|
||||
static void
|
||||
tiff_deserializer (GdkContentDeserializer *deserializer)
|
||||
{
|
||||
gdk_load_tiff_async (gdk_content_deserializer_get_input_stream (deserializer),
|
||||
gdk_content_deserializer_get_cancellable (deserializer),
|
||||
tiff_deserializer_finish,
|
||||
deserializer);
|
||||
}
|
||||
|
||||
static void
|
||||
string_deserializer_finish (GObject *source,
|
||||
GAsyncResult *result,
|
||||
@@ -858,27 +925,38 @@ init (void)
|
||||
|
||||
initialized = TRUE;
|
||||
|
||||
gdk_content_register_deserializer ("image/png",
|
||||
GDK_TYPE_TEXTURE,
|
||||
png_deserializer,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
gdk_content_register_deserializer ("image/tiff",
|
||||
GDK_TYPE_TEXTURE,
|
||||
tiff_deserializer,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
formats = gdk_pixbuf_get_formats ();
|
||||
|
||||
/* Make sure png comes first */
|
||||
for (f = formats; f; f = f->next)
|
||||
{
|
||||
GdkPixbufFormat *fmt = f->data;
|
||||
char *name;
|
||||
|
||||
char *name;
|
||||
|
||||
name = gdk_pixbuf_format_get_name (fmt);
|
||||
if (g_str_equal (name, "png"))
|
||||
{
|
||||
formats = g_slist_delete_link (formats, f);
|
||||
formats = g_slist_prepend (formats, fmt);
|
||||
{
|
||||
formats = g_slist_delete_link (formats, f);
|
||||
formats = g_slist_prepend (formats, fmt);
|
||||
|
||||
g_free (name);
|
||||
|
||||
break;
|
||||
}
|
||||
g_free (name);
|
||||
break;
|
||||
}
|
||||
|
||||
g_free (name);
|
||||
}
|
||||
}
|
||||
|
||||
for (f = formats; f; f = f->next)
|
||||
{
|
||||
|
||||
@@ -80,7 +80,7 @@ gdk_content_provider_value_get_value (GdkContentProvider *provider,
|
||||
{
|
||||
GdkContentProviderValue *content = GDK_CONTENT_PROVIDER_VALUE (provider);
|
||||
|
||||
if (G_VALUE_HOLDS (value, G_VALUE_TYPE (&content->value)))
|
||||
if (G_VALUE_HOLDS (&content->value, G_VALUE_TYPE (value)))
|
||||
{
|
||||
g_value_copy (&content->value, value);
|
||||
return TRUE;
|
||||
|
||||
+149
-10
@@ -26,6 +26,9 @@
|
||||
#include "filetransferportalprivate.h"
|
||||
#include "gdktextureprivate.h"
|
||||
#include "gdkrgba.h"
|
||||
#include "gdkpng.h"
|
||||
#include "gdktiff.h"
|
||||
#include "gdkmemorytextureprivate.h"
|
||||
|
||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||
#include <string.h>
|
||||
@@ -606,6 +609,7 @@ gdk_content_serialize_finish (GAsyncResult *result,
|
||||
|
||||
/*** SERIALIZERS ***/
|
||||
|
||||
|
||||
static void
|
||||
pixbuf_serializer_finish (GObject *source,
|
||||
GAsyncResult *res,
|
||||
@@ -658,6 +662,132 @@ pixbuf_serializer (GdkContentSerializer *serializer)
|
||||
g_object_unref (pixbuf);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
GdkContentSerializer *serializer;
|
||||
GBytes *bytes;
|
||||
} PngSerializerData;
|
||||
|
||||
static void
|
||||
png_serializer_finish (GObject *source,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
PngSerializerData *data = user_data;
|
||||
GError *error = NULL;
|
||||
|
||||
if (!gdk_save_png_finish (res, &error))
|
||||
gdk_content_serializer_return_error (data->serializer, error);
|
||||
else
|
||||
gdk_content_serializer_return_success (data->serializer);
|
||||
|
||||
g_bytes_unref (data->bytes);
|
||||
g_free (data);
|
||||
}
|
||||
|
||||
static void
|
||||
png_serializer (GdkContentSerializer *serializer)
|
||||
{
|
||||
const GValue *value;
|
||||
GdkTexture *texture;
|
||||
GdkMemoryFormat format;
|
||||
GBytes *bytes = NULL;
|
||||
PngSerializerData *data;
|
||||
|
||||
value = gdk_content_serializer_get_value (serializer);
|
||||
|
||||
texture = g_value_get_object (value);
|
||||
|
||||
for (int i = 0; i < GDK_MEMORY_N_FORMATS; i++)
|
||||
{
|
||||
bytes = gdk_texture_download_format (texture, i);
|
||||
if (bytes)
|
||||
{
|
||||
format = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_assert (bytes != NULL);
|
||||
|
||||
data = g_new0 (PngSerializerData, 1);
|
||||
data->serializer = serializer;
|
||||
data->bytes = bytes;
|
||||
|
||||
gdk_save_png_async (gdk_content_serializer_get_output_stream (serializer),
|
||||
g_bytes_get_data (bytes, NULL),
|
||||
gdk_texture_get_width (texture),
|
||||
gdk_texture_get_height (texture),
|
||||
gdk_texture_get_width (texture) * gdk_memory_format_bytes_per_pixel (format),
|
||||
format,
|
||||
gdk_content_serializer_get_cancellable (serializer),
|
||||
png_serializer_finish,
|
||||
data);
|
||||
g_object_unref (texture);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
GdkContentSerializer *serializer;
|
||||
GBytes *bytes;
|
||||
} TiffSerializerData;
|
||||
|
||||
static void
|
||||
tiff_serializer_finish (GObject *source,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
TiffSerializerData *data = user_data;
|
||||
GError *error = NULL;
|
||||
|
||||
if (!gdk_save_tiff_finish (res, &error))
|
||||
gdk_content_serializer_return_error (data->serializer, error);
|
||||
else
|
||||
gdk_content_serializer_return_success (data->serializer);
|
||||
|
||||
g_bytes_unref (data->bytes);
|
||||
g_free (data);
|
||||
}
|
||||
|
||||
static void
|
||||
tiff_serializer (GdkContentSerializer *serializer)
|
||||
{
|
||||
const GValue *value;
|
||||
GdkTexture *texture;
|
||||
GdkMemoryFormat format;
|
||||
GBytes *bytes = NULL;
|
||||
TiffSerializerData *data;
|
||||
|
||||
value = gdk_content_serializer_get_value (serializer);
|
||||
|
||||
texture = g_value_get_object (value);
|
||||
|
||||
for (int i = 0; i < GDK_MEMORY_N_FORMATS; i++)
|
||||
{
|
||||
bytes = gdk_texture_download_format (texture, i);
|
||||
if (bytes)
|
||||
{
|
||||
format = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_assert (bytes != NULL);
|
||||
|
||||
data = g_new0 (TiffSerializerData, 1);
|
||||
data->serializer = serializer;
|
||||
data->bytes = bytes;
|
||||
|
||||
gdk_save_tiff_async (gdk_content_serializer_get_output_stream (serializer),
|
||||
g_bytes_get_data (bytes, NULL),
|
||||
gdk_texture_get_width (texture),
|
||||
gdk_texture_get_height (texture),
|
||||
gdk_texture_get_width (texture) * gdk_memory_format_bytes_per_pixel (format),
|
||||
format,
|
||||
gdk_content_serializer_get_cancellable (serializer),
|
||||
tiff_serializer_finish,
|
||||
data);
|
||||
g_object_unref (texture);
|
||||
}
|
||||
|
||||
static void
|
||||
string_serializer_finish (GObject *source,
|
||||
GAsyncResult *result,
|
||||
@@ -877,27 +1007,36 @@ init (void)
|
||||
|
||||
initialized = TRUE;
|
||||
|
||||
gdk_content_register_serializer (GDK_TYPE_TEXTURE,
|
||||
"image/png",
|
||||
png_serializer,
|
||||
NULL, NULL);
|
||||
|
||||
gdk_content_register_serializer (GDK_TYPE_TEXTURE,
|
||||
"image/tiff",
|
||||
tiff_serializer,
|
||||
NULL, NULL);
|
||||
|
||||
formats = gdk_pixbuf_get_formats ();
|
||||
|
||||
/* Make sure png comes first */
|
||||
for (f = formats; f; f = f->next)
|
||||
{
|
||||
GdkPixbufFormat *fmt = f->data;
|
||||
char *name;
|
||||
|
||||
char *name;
|
||||
|
||||
name = gdk_pixbuf_format_get_name (fmt);
|
||||
if (g_str_equal (name, "png"))
|
||||
{
|
||||
formats = g_slist_delete_link (formats, f);
|
||||
formats = g_slist_prepend (formats, fmt);
|
||||
{
|
||||
formats = g_slist_delete_link (formats, f);
|
||||
formats = g_slist_prepend (formats, fmt);
|
||||
|
||||
g_free (name);
|
||||
|
||||
break;
|
||||
}
|
||||
g_free (name);
|
||||
break;
|
||||
}
|
||||
|
||||
g_free (name);
|
||||
}
|
||||
}
|
||||
|
||||
for (f = formats; f; f = f->next)
|
||||
{
|
||||
|
||||
+51
-3
@@ -229,6 +229,7 @@ gdk_gl_context_upload_texture (GdkGLContext *context,
|
||||
{
|
||||
GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
|
||||
guchar *copy = NULL;
|
||||
guint gl_internalformat;
|
||||
guint gl_format;
|
||||
guint gl_type;
|
||||
guint bpp;
|
||||
@@ -250,6 +251,7 @@ gdk_gl_context_upload_texture (GdkGLContext *context,
|
||||
}
|
||||
|
||||
bpp = 4;
|
||||
gl_internalformat = GL_RGBA8;
|
||||
gl_format = GL_RGBA;
|
||||
gl_type = GL_UNSIGNED_BYTE;
|
||||
}
|
||||
@@ -257,22 +259,67 @@ gdk_gl_context_upload_texture (GdkGLContext *context,
|
||||
{
|
||||
if (data_format == GDK_MEMORY_DEFAULT) /* Cairo surface format */
|
||||
{
|
||||
gl_internalformat = GL_RGBA8;
|
||||
gl_format = GL_BGRA;
|
||||
gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
|
||||
bpp = 4;
|
||||
}
|
||||
else if (data_format == GDK_MEMORY_R8G8B8) /* Pixmap non-alpha data */
|
||||
{
|
||||
gl_internalformat = GL_RGBA8;
|
||||
gl_format = GL_RGB;
|
||||
gl_type = GL_UNSIGNED_BYTE;
|
||||
bpp = 3;
|
||||
}
|
||||
else if (data_format == GDK_MEMORY_B8G8R8)
|
||||
{
|
||||
gl_internalformat = GL_RGBA8;
|
||||
gl_format = GL_BGR;
|
||||
gl_type = GL_UNSIGNED_BYTE;
|
||||
bpp = 3;
|
||||
}
|
||||
else if (data_format == GDK_MEMORY_R16G16B16)
|
||||
{
|
||||
gl_internalformat = GL_RGBA16;
|
||||
gl_format = GL_RGB;
|
||||
gl_type = GL_UNSIGNED_SHORT;
|
||||
bpp = 6;
|
||||
}
|
||||
else if (data_format == GDK_MEMORY_R16G16B16A16_PREMULTIPLIED)
|
||||
{
|
||||
gl_internalformat = GL_RGBA16;
|
||||
gl_format = GL_RGBA;
|
||||
gl_type = GL_UNSIGNED_SHORT;
|
||||
bpp = 8;
|
||||
}
|
||||
else if (data_format == GDK_MEMORY_R16G16B16_FLOAT)
|
||||
{
|
||||
gl_internalformat = GL_RGB16F;
|
||||
gl_format = GL_RGB;
|
||||
gl_type = GL_HALF_FLOAT;
|
||||
bpp = 6;
|
||||
}
|
||||
else if (data_format == GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED)
|
||||
{
|
||||
gl_internalformat = GL_RGBA16F;
|
||||
gl_format = GL_RGBA;
|
||||
gl_type = GL_HALF_FLOAT;
|
||||
bpp = 8;
|
||||
}
|
||||
else if (data_format == GDK_MEMORY_R32G32B32_FLOAT)
|
||||
{
|
||||
gl_internalformat = GL_RGB32F;
|
||||
gl_format = GL_RGB;
|
||||
gl_type = GL_FLOAT;
|
||||
bpp = 12;
|
||||
}
|
||||
else if (data_format == GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED)
|
||||
{
|
||||
gl_internalformat = GL_RGBA32F;
|
||||
gl_format = GL_RGBA;
|
||||
gl_type = GL_FLOAT;
|
||||
bpp = 16;
|
||||
}
|
||||
else /* Fall-back, convert to cairo-surface-format */
|
||||
{
|
||||
copy = g_malloc (width * height * 4);
|
||||
@@ -283,6 +330,7 @@ gdk_gl_context_upload_texture (GdkGLContext *context,
|
||||
stride = width * 4;
|
||||
bpp = 4;
|
||||
data = copy;
|
||||
gl_internalformat = GL_RGBA8;
|
||||
gl_format = GL_BGRA;
|
||||
gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
|
||||
}
|
||||
@@ -295,7 +343,7 @@ gdk_gl_context_upload_texture (GdkGLContext *context,
|
||||
{
|
||||
glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
|
||||
|
||||
glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, gl_format, gl_type, data);
|
||||
glTexImage2D (texture_target, 0, gl_internalformat, width, height, 0, gl_format, gl_type, data);
|
||||
glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
|
||||
}
|
||||
else if ((!priv->use_es ||
|
||||
@@ -303,14 +351,14 @@ gdk_gl_context_upload_texture (GdkGLContext *context,
|
||||
{
|
||||
glPixelStorei (GL_UNPACK_ROW_LENGTH, stride / bpp);
|
||||
|
||||
glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, gl_format, gl_type, data);
|
||||
glTexImage2D (texture_target, 0, gl_internalformat, width, height, 0, gl_format, gl_type, data);
|
||||
|
||||
glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
int i;
|
||||
glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, gl_format, gl_type, NULL);
|
||||
glTexImage2D (texture_target, 0, gl_internalformat, width, height, 0, gl_format, gl_type, NULL);
|
||||
for (i = 0; i < height; i++)
|
||||
glTexSubImage2D (texture_target, 0, 0, i, width, 1, gl_format, gl_type, data + (i * stride));
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
|
||||
#include "gdkcairo.h"
|
||||
#include "gdktextureprivate.h"
|
||||
#include "gdkmemorytextureprivate.h"
|
||||
#include "gdkinternals.h"
|
||||
|
||||
#include <epoxy/gl.h>
|
||||
|
||||
@@ -110,6 +112,78 @@ gdk_gl_texture_download (GdkTexture *texture,
|
||||
cairo_surface_destroy (surface);
|
||||
}
|
||||
|
||||
static int
|
||||
type_from_internal_format (int internal_format)
|
||||
{
|
||||
switch (internal_format)
|
||||
{
|
||||
case GL_RGB16:
|
||||
case GL_RGBA16:
|
||||
return GL_UNSIGNED_SHORT;
|
||||
case GL_RGB16F:
|
||||
case GL_RGBA16F:
|
||||
return GL_HALF_FLOAT;
|
||||
case GL_RGB32F:
|
||||
case GL_RGBA32F:
|
||||
return GL_FLOAT;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
internal_format_for_format (GdkMemoryFormat format)
|
||||
{
|
||||
switch ((int)format)
|
||||
{
|
||||
case GDK_MEMORY_R16G16B16:
|
||||
return GL_RGB16;
|
||||
case GDK_MEMORY_R16G16B16A16_PREMULTIPLIED:
|
||||
return GL_RGBA16;
|
||||
case GDK_MEMORY_R16G16B16_FLOAT:
|
||||
return GL_RGB16F;
|
||||
case GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED:
|
||||
return GL_RGB16F;
|
||||
case GDK_MEMORY_R32G32B32_FLOAT:
|
||||
return GL_RGB32F;
|
||||
case GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED:
|
||||
return GL_RGB32F;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
static GBytes *
|
||||
gdk_gl_texture_download_format (GdkTexture *texture,
|
||||
GdkMemoryFormat format)
|
||||
{
|
||||
GdkGLTexture *self = GDK_GL_TEXTURE (texture);
|
||||
GdkSurface *surface;
|
||||
GdkGLContext *context;
|
||||
int internal_format = 0;
|
||||
gpointer data;
|
||||
gsize size;
|
||||
|
||||
surface = gdk_gl_context_get_surface (self->context);
|
||||
context = gdk_surface_get_paint_gl_context (surface, NULL);
|
||||
|
||||
gdk_gl_context_make_current (context);
|
||||
glBindTexture (GL_TEXTURE_2D, self->id);
|
||||
glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &internal_format);
|
||||
|
||||
if (internal_format != internal_format_for_format (format))
|
||||
return NULL;
|
||||
|
||||
size = texture->width * texture->height * gdk_memory_format_bytes_per_pixel (format);
|
||||
data = malloc (size);
|
||||
|
||||
glReadPixels (0, 0, texture->width, texture->height,
|
||||
internal_format, type_from_internal_format (internal_format),
|
||||
data);
|
||||
|
||||
return g_bytes_new_take (data, size);
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_gl_texture_class_init (GdkGLTextureClass *klass)
|
||||
{
|
||||
@@ -117,6 +191,7 @@ gdk_gl_texture_class_init (GdkGLTextureClass *klass)
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
texture_class->download = gdk_gl_texture_download;
|
||||
texture_class->download_format = gdk_gl_texture_download_format;
|
||||
gobject_class->dispose = gdk_gl_texture_dispose;
|
||||
}
|
||||
|
||||
|
||||
+234
@@ -0,0 +1,234 @@
|
||||
/* GDK - The GIMP Drawing Kit
|
||||
* Copyright (C) 2021 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gdkjpeg.h"
|
||||
|
||||
#include "gdktexture.h"
|
||||
#include "gdkmemorytextureprivate.h"
|
||||
#include <jpeglib.h>
|
||||
#include <jerror.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
/* {{{ IO handling */
|
||||
|
||||
#define BUF_SIZE 65536
|
||||
|
||||
typedef struct {
|
||||
struct jpeg_source_mgr pub;
|
||||
JOCTET buffer[BUF_SIZE];
|
||||
long skip_next;
|
||||
GInputStream *stream;
|
||||
} my_source_mgr;
|
||||
|
||||
static void
|
||||
init_source (j_decompress_ptr info)
|
||||
{
|
||||
my_source_mgr *src = (my_source_mgr *) info->src;
|
||||
src->skip_next = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
term_source (j_decompress_ptr info)
|
||||
{
|
||||
}
|
||||
|
||||
static boolean
|
||||
fill_input_buffer (j_decompress_ptr info)
|
||||
{
|
||||
my_source_mgr *src = (my_source_mgr *) info->src;
|
||||
size_t nbytes;
|
||||
|
||||
nbytes = g_input_stream_read (src->stream, src->buffer, BUF_SIZE, NULL, NULL);
|
||||
|
||||
if (nbytes <= 0)
|
||||
{
|
||||
/* Insert a fake EOI marker */
|
||||
src->buffer[0] = (JOCTET) 0xFF;
|
||||
src->buffer[1] = (JOCTET) JPEG_EOI;
|
||||
nbytes = 2;
|
||||
}
|
||||
|
||||
src->pub.next_input_byte = src->buffer;
|
||||
src->pub.bytes_in_buffer = nbytes;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
skip_input_data (j_decompress_ptr info,
|
||||
long num_bytes)
|
||||
{
|
||||
my_source_mgr *src = (my_source_mgr *) info->src;
|
||||
|
||||
if (num_bytes > 0)
|
||||
{
|
||||
while (num_bytes > src->pub.bytes_in_buffer)
|
||||
{
|
||||
num_bytes -= src->pub.bytes_in_buffer;
|
||||
fill_input_buffer (info);
|
||||
}
|
||||
|
||||
src->pub.next_input_byte += num_bytes;
|
||||
src->pub.bytes_in_buffer -= num_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
/* {{{ Error handling */
|
||||
|
||||
struct error_handler_data {
|
||||
struct jpeg_error_mgr pub;
|
||||
sigjmp_buf setjmp_buffer;
|
||||
GError **error;
|
||||
};
|
||||
|
||||
static void
|
||||
fatal_error_handler (j_common_ptr cinfo)
|
||||
{
|
||||
struct error_handler_data *errmgr;
|
||||
|
||||
errmgr = (struct error_handler_data *) cinfo->err;
|
||||
|
||||
siglongjmp (errmgr->setjmp_buffer, 1);
|
||||
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
static void
|
||||
output_message_handler (j_common_ptr cinfo)
|
||||
{
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
/* {{{ Public API */
|
||||
|
||||
GdkTexture *
|
||||
gdk_load_jpeg (GInputStream *stream,
|
||||
GError **error)
|
||||
{
|
||||
struct jpeg_decompress_struct info;
|
||||
struct error_handler_data jerr;
|
||||
struct jpeg_error_mgr err;
|
||||
my_source_mgr src;
|
||||
int width, height;
|
||||
int size;
|
||||
unsigned char *data;
|
||||
unsigned char *row[1];
|
||||
GBytes *bytes;
|
||||
GdkTexture *texture;
|
||||
|
||||
info.err = jpeg_std_error (&jerr.pub);
|
||||
jerr.pub.error_exit = fatal_error_handler;
|
||||
jerr.pub.output_message = output_message_handler;
|
||||
jerr.error = error;
|
||||
|
||||
if (sigsetjmp (jerr.setjmp_buffer, 1))
|
||||
{
|
||||
jpeg_destroy_decompress (&info);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
info.err = jpeg_std_error (&err);
|
||||
jpeg_create_decompress (&info);
|
||||
|
||||
src.pub.init_source = init_source;
|
||||
src.pub.fill_input_buffer = fill_input_buffer;
|
||||
src.pub.skip_input_data = skip_input_data;
|
||||
src.pub.resync_to_restart = jpeg_resync_to_restart;
|
||||
src.pub.term_source = term_source;
|
||||
src.pub.bytes_in_buffer = 0;
|
||||
src.pub.next_input_byte = NULL;
|
||||
src.stream = stream;
|
||||
|
||||
info.src = (struct jpeg_source_mgr *)&src;
|
||||
|
||||
jpeg_read_header (&info, TRUE);
|
||||
jpeg_start_decompress (&info);
|
||||
|
||||
width = info.output_width;
|
||||
height = info.output_height;
|
||||
|
||||
size = width * height * 3;
|
||||
data = g_malloc (size);
|
||||
|
||||
while (info.output_scanline < info.output_height)
|
||||
{
|
||||
row[0] = (unsigned char *)(&data[3 *info.output_width * info.output_scanline]);
|
||||
jpeg_read_scanlines (&info, row, 1);
|
||||
}
|
||||
|
||||
jpeg_finish_decompress (&info);
|
||||
jpeg_destroy_decompress (&info);
|
||||
|
||||
bytes = g_bytes_new_take (data, size);
|
||||
|
||||
texture = gdk_memory_texture_new (width, height,
|
||||
GDK_MEMORY_R8G8B8,
|
||||
bytes, width * 3);
|
||||
|
||||
g_bytes_unref (bytes);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
/* {{{ Async code */
|
||||
|
||||
static void
|
||||
load_jpeg_in_thread (GTask *task,
|
||||
gpointer source_object,
|
||||
gpointer task_data,
|
||||
GCancellable *cancellable)
|
||||
{
|
||||
GInputStream *stream = source_object;
|
||||
GdkTexture *texture;
|
||||
GError *error = NULL;
|
||||
|
||||
texture = gdk_load_jpeg (stream, &error);
|
||||
|
||||
if (texture)
|
||||
g_task_return_pointer (task, texture, g_object_unref);
|
||||
else
|
||||
g_task_return_error (task, error);
|
||||
}
|
||||
|
||||
void
|
||||
gdk_load_jpeg_async (GInputStream *stream,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GTask *task;
|
||||
|
||||
task = g_task_new (stream, cancellable, callback, user_data);
|
||||
g_task_run_in_thread (task, load_jpeg_in_thread);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
GdkTexture *
|
||||
gdk_load_jpeg_finish (GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
return g_task_propagate_pointer (G_TASK (result), error);
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
|
||||
/* vim:set foldmethod=marker expandtab: */
|
||||
@@ -0,0 +1,35 @@
|
||||
/* GDK - The GIMP Drawing Kit
|
||||
* Copyright (C) 2021 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __GDK_JPEG_H__
|
||||
#define __GDK_JPEG_H__
|
||||
|
||||
#include "gdkmemorytexture.h"
|
||||
#include <gio/gio.h>
|
||||
|
||||
GdkTexture *gdk_load_jpeg (GInputStream *stream,
|
||||
GError **error);
|
||||
|
||||
void gdk_load_jpeg_async (GInputStream *stream,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
GdkTexture *gdk_load_jpeg_finish (GAsyncResult *result,
|
||||
GError **error);
|
||||
|
||||
#endif
|
||||
+475
-12
@@ -20,6 +20,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "gdkmemorytextureprivate.h"
|
||||
#include "gsk/ngl/fp16private.h"
|
||||
|
||||
/**
|
||||
* GdkMemoryTexture:
|
||||
@@ -62,6 +63,22 @@ gdk_memory_format_bytes_per_pixel (GdkMemoryFormat format)
|
||||
case GDK_MEMORY_B8G8R8:
|
||||
return 3;
|
||||
|
||||
case GDK_MEMORY_R16G16B16:
|
||||
return 6;
|
||||
|
||||
case GDK_MEMORY_R16G16B16A16_PREMULTIPLIED:
|
||||
case GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED:
|
||||
return 8;
|
||||
|
||||
case GDK_MEMORY_R16G16B16_FLOAT:
|
||||
return 6;
|
||||
|
||||
case GDK_MEMORY_R32G32B32_FLOAT:
|
||||
return 12;
|
||||
|
||||
case GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED:
|
||||
return 16;
|
||||
|
||||
case GDK_MEMORY_N_FORMATS:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
@@ -97,6 +114,18 @@ gdk_memory_texture_download (GdkTexture *texture,
|
||||
area->width, area->height);
|
||||
}
|
||||
|
||||
static GBytes *
|
||||
gdk_memory_texture_download_format (GdkTexture *texture,
|
||||
GdkMemoryFormat format)
|
||||
{
|
||||
GdkMemoryTexture *self = GDK_MEMORY_TEXTURE (texture);
|
||||
|
||||
if (self->format == format)
|
||||
return g_bytes_ref (self->bytes);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_memory_texture_class_init (GdkMemoryTextureClass *klass)
|
||||
{
|
||||
@@ -104,6 +133,7 @@ gdk_memory_texture_class_init (GdkMemoryTextureClass *klass)
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
texture_class->download = gdk_memory_texture_download;
|
||||
texture_class->download_format = gdk_memory_texture_download_format;
|
||||
gobject_class->dispose = gdk_memory_texture_dispose;
|
||||
}
|
||||
|
||||
@@ -148,7 +178,7 @@ gdk_memory_texture_new (int width,
|
||||
return GDK_TEXTURE (self);
|
||||
}
|
||||
|
||||
GdkMemoryFormat
|
||||
GdkMemoryFormat
|
||||
gdk_memory_texture_get_format (GdkMemoryTexture *self)
|
||||
{
|
||||
return self->format;
|
||||
@@ -169,8 +199,10 @@ gdk_memory_texture_get_stride (GdkMemoryTexture *self)
|
||||
static void
|
||||
convert_memcpy (guchar *dest_data,
|
||||
gsize dest_stride,
|
||||
GdkMemoryFormat dest_format,
|
||||
const guchar *src_data,
|
||||
gsize src_stride,
|
||||
GdkMemoryFormat src_format,
|
||||
gsize width,
|
||||
gsize height)
|
||||
{
|
||||
@@ -184,8 +216,10 @@ convert_memcpy (guchar *dest_data,
|
||||
static void \
|
||||
convert_swizzle ## A ## R ## G ## B (guchar *dest_data, \
|
||||
gsize dest_stride, \
|
||||
GdkMemoryFormat dest_format, \
|
||||
const guchar *src_data, \
|
||||
gsize src_stride, \
|
||||
GdkMemoryFormat src_format, \
|
||||
gsize width, \
|
||||
gsize height) \
|
||||
{ \
|
||||
@@ -215,8 +249,10 @@ SWIZZLE(1,2,3,0)
|
||||
static void \
|
||||
convert_swizzle_opaque_## A ## R ## G ## B (guchar *dest_data, \
|
||||
gsize dest_stride, \
|
||||
GdkMemoryFormat dest_format, \
|
||||
const guchar *src_data, \
|
||||
gsize src_stride, \
|
||||
GdkMemoryFormat src_format, \
|
||||
gsize width, \
|
||||
gsize height) \
|
||||
{ \
|
||||
@@ -248,8 +284,10 @@ static void \
|
||||
convert_swizzle_premultiply_ ## A ## R ## G ## B ## _ ## A2 ## R2 ## G2 ## B2 \
|
||||
(guchar *dest_data, \
|
||||
gsize dest_stride, \
|
||||
GdkMemoryFormat dest_format, \
|
||||
const guchar *src_data, \
|
||||
gsize src_stride, \
|
||||
GdkMemoryFormat src_format, \
|
||||
gsize width, \
|
||||
gsize height) \
|
||||
{ \
|
||||
@@ -283,24 +321,447 @@ SWIZZLE_PREMULTIPLY (3,0,1,2, 0,1,2,3)
|
||||
SWIZZLE_PREMULTIPLY (3,0,1,2, 3,0,1,2)
|
||||
SWIZZLE_PREMULTIPLY (3,0,1,2, 0,3,2,1)
|
||||
|
||||
#define SWIZZLE_16TO8_OPAQUE(A,R,G,B) \
|
||||
static void \
|
||||
convert_16to8_swizzle_opaque_ ## A ## R ## G ## B (guchar *dest_data, \
|
||||
gsize dest_stride, \
|
||||
GdkMemoryFormat dest_format, \
|
||||
const guchar *src_data, \
|
||||
gsize src_stride, \
|
||||
GdkMemoryFormat src_format, \
|
||||
gsize width, \
|
||||
gsize height) \
|
||||
{ \
|
||||
gsize x, y; \
|
||||
\
|
||||
for (y = 0; y < height; y++) \
|
||||
{ \
|
||||
guint16 *src = (guint16 *)src_data; \
|
||||
for (x = 0; x < width; x++) \
|
||||
{ \
|
||||
dest_data[4 * x + A] = 255; \
|
||||
dest_data[4 * x + R] = (guchar)(src[4 * x + 0] >> 8); \
|
||||
dest_data[4 * x + G] = (guchar)(src[4 * x + 1] >> 8); \
|
||||
dest_data[4 * x + B] = (guchar)(src[4 * x + 2] >> 8); \
|
||||
} \
|
||||
\
|
||||
dest_data += dest_stride; \
|
||||
src_data += src_stride; \
|
||||
} \
|
||||
}
|
||||
|
||||
SWIZZLE_16TO8_OPAQUE(0,1,2,3)
|
||||
SWIZZLE_16TO8_OPAQUE(2,1,0,3)
|
||||
SWIZZLE_16TO8_OPAQUE(1,2,3,0)
|
||||
|
||||
#define SWIZZLE_16TO8(A,R,G,B) \
|
||||
static void \
|
||||
convert_16to8_swizzle_ ## A ## R ## G ## B (guchar *dest_data, \
|
||||
gsize dest_stride, \
|
||||
GdkMemoryFormat dest_format, \
|
||||
const guchar *src_data, \
|
||||
gsize src_stride, \
|
||||
GdkMemoryFormat src_format, \
|
||||
gsize width, \
|
||||
gsize height) \
|
||||
{ \
|
||||
gsize x, y; \
|
||||
\
|
||||
for (y = 0; y < height; y++) \
|
||||
{ \
|
||||
guint16 *src = (guint16 *)src_data; \
|
||||
for (x = 0; x < width; x++) \
|
||||
{ \
|
||||
dest_data[4 * x + A] = (guchar)(src[4 * x + 0] >> 8); \
|
||||
dest_data[4 * x + R] = (guchar)(src[4 * x + 1] >> 8); \
|
||||
dest_data[4 * x + G] = (guchar)(src[4 * x + 2] >> 8); \
|
||||
dest_data[4 * x + B] = (guchar)(src[4 * x + 3] >> 8); \
|
||||
} \
|
||||
\
|
||||
dest_data += dest_stride; \
|
||||
src_data += src_stride; \
|
||||
} \
|
||||
}
|
||||
|
||||
SWIZZLE_16TO8(0,1,2,3)
|
||||
SWIZZLE_16TO8(2,1,0,3)
|
||||
SWIZZLE_16TO8(1,2,3,0)
|
||||
|
||||
#define SWIZZLE_FP16_OPAQUE(A,R,G,B) \
|
||||
static void \
|
||||
convert_fp16_swizzle_opaque_ ## A ## R ## G ## B (guchar *dest_data, \
|
||||
gsize dest_stride, \
|
||||
GdkMemoryFormat dest_format, \
|
||||
const guchar *src_data, \
|
||||
gsize src_stride, \
|
||||
GdkMemoryFormat src_format, \
|
||||
gsize width, \
|
||||
gsize height) \
|
||||
{ \
|
||||
gsize x, y; \
|
||||
float *c; \
|
||||
\
|
||||
c = g_malloc (width * sizeof (float)); \
|
||||
for (y = 0; y < height; y++) \
|
||||
{ \
|
||||
guint16 *src = (guint16 *)src_data; \
|
||||
half_to_float (src, c, width); \
|
||||
for (x = 0; x < width; x++) \
|
||||
{ \
|
||||
dest_data[4 * x + A] = 255; \
|
||||
dest_data[4 * x + R] = (guchar)(255 * c[3 * x + 0]); \
|
||||
dest_data[4 * x + G] = (guchar)(255 * c[3 * x + 1]); \
|
||||
dest_data[4 * x + B] = (guchar)(255 * c[3 * x + 2]); \
|
||||
} \
|
||||
dest_data += dest_stride; \
|
||||
src_data += src_stride; \
|
||||
} \
|
||||
g_free (c); \
|
||||
}
|
||||
|
||||
SWIZZLE_FP16_OPAQUE(3,2,1,0)
|
||||
SWIZZLE_FP16_OPAQUE(0,1,2,3)
|
||||
SWIZZLE_FP16_OPAQUE(3,0,1,2)
|
||||
|
||||
#define SWIZZLE_FP16(A,R,G,B) \
|
||||
static void \
|
||||
convert_fp16_swizzle_ ## A ## R ## G ## B (guchar *dest_data, \
|
||||
gsize dest_stride, \
|
||||
GdkMemoryFormat dest_format, \
|
||||
const guchar *src_data, \
|
||||
gsize src_stride, \
|
||||
GdkMemoryFormat src_format, \
|
||||
gsize width, \
|
||||
gsize height) \
|
||||
{ \
|
||||
gsize x, y; \
|
||||
float *c; \
|
||||
\
|
||||
c = g_malloc (width * sizeof (float)); \
|
||||
for (y = 0; y < height; y++) \
|
||||
{ \
|
||||
guint16 *src = (guint16 *)src_data; \
|
||||
half_to_float (src, c, width); \
|
||||
for (x = 0; x < width; x++) \
|
||||
{ \
|
||||
dest_data[4 * x + A] = (guchar)(255 * c[4 * x + 0]); \
|
||||
dest_data[4 * x + R] = (guchar)(255 * c[4 * x + 1]); \
|
||||
dest_data[4 * x + G] = (guchar)(255 * c[4 * x + 2]); \
|
||||
dest_data[4 * x + B] = (guchar)(255 * c[4 * x + 3]); \
|
||||
} \
|
||||
dest_data += dest_stride; \
|
||||
src_data += src_stride; \
|
||||
} \
|
||||
g_free (c); \
|
||||
}
|
||||
|
||||
SWIZZLE_FP16(3,2,1,0)
|
||||
SWIZZLE_FP16(0,1,2,3)
|
||||
SWIZZLE_FP16(3,0,1,2)
|
||||
|
||||
#define SWIZZLE_FLOAT_OPAQUE(A,R,G,B) \
|
||||
static void \
|
||||
convert_float_swizzle_opaque_ ## A ## R ## G ## B (guchar *dest_data, \
|
||||
gsize dest_stride, \
|
||||
GdkMemoryFormat dest_format, \
|
||||
const guchar *src_data, \
|
||||
gsize src_stride, \
|
||||
GdkMemoryFormat src_format, \
|
||||
gsize width, \
|
||||
gsize height) \
|
||||
{ \
|
||||
gsize x, y; \
|
||||
\
|
||||
for (y = 0; y < height; y++) \
|
||||
{ \
|
||||
float *src = (float *)src_data; \
|
||||
for (x = 0; x < width; x++) \
|
||||
{ \
|
||||
dest_data[4 * x + A] = 255; \
|
||||
dest_data[4 * x + R] = (guchar)(255 * src[3 * x + 0]); \
|
||||
dest_data[4 * x + G] = (guchar)(255 * src[3 * x + 1]); \
|
||||
dest_data[4 * x + B] = (guchar)(255 * src[3 * x + 2]); \
|
||||
} \
|
||||
\
|
||||
dest_data += dest_stride; \
|
||||
src_data += src_stride; \
|
||||
} \
|
||||
}
|
||||
|
||||
SWIZZLE_FLOAT_OPAQUE(3,2,1,0)
|
||||
SWIZZLE_FLOAT_OPAQUE(0,1,2,3)
|
||||
SWIZZLE_FLOAT_OPAQUE(3,0,1,2)
|
||||
|
||||
#define SWIZZLE_FLOAT(A,R,G,B) \
|
||||
static void \
|
||||
convert_float_swizzle_ ## A ## R ## G ## B (guchar *dest_data, \
|
||||
gsize dest_stride, \
|
||||
GdkMemoryFormat dest_format, \
|
||||
const guchar *src_data, \
|
||||
gsize src_stride, \
|
||||
GdkMemoryFormat src_format, \
|
||||
gsize width, \
|
||||
gsize height) \
|
||||
{ \
|
||||
gsize x, y; \
|
||||
\
|
||||
for (y = 0; y < height; y++) \
|
||||
{ \
|
||||
float *src = (float *)src_data; \
|
||||
for (x = 0; x < width; x++) \
|
||||
{ \
|
||||
dest_data[4 * x + A] = (guchar)(255 * src[3 * x + 0]); \
|
||||
dest_data[4 * x + R] = (guchar)(255 * src[3 * x + 1]); \
|
||||
dest_data[4 * x + G] = (guchar)(255 * src[3 * x + 2]); \
|
||||
dest_data[4 * x + B] = (guchar)(255 * src[3 * x + 3]); \
|
||||
} \
|
||||
\
|
||||
dest_data += dest_stride; \
|
||||
src_data += src_stride; \
|
||||
} \
|
||||
}
|
||||
|
||||
SWIZZLE_FLOAT(3,2,1,0)
|
||||
SWIZZLE_FLOAT(0,1,2,3)
|
||||
SWIZZLE_FLOAT(3,0,1,2)
|
||||
|
||||
typedef void (* ConversionFunc) (guchar *dest_data,
|
||||
gsize dest_stride,
|
||||
GdkMemoryFormat dest_format,
|
||||
const guchar *src_data,
|
||||
gsize src_stride,
|
||||
GdkMemoryFormat src_format,
|
||||
gsize width,
|
||||
gsize height);
|
||||
|
||||
static ConversionFunc converters[GDK_MEMORY_N_FORMATS][3] =
|
||||
static ConversionFunc converters_to_bgra[GDK_MEMORY_N_FORMATS] =
|
||||
{
|
||||
{ convert_memcpy, convert_swizzle3210, convert_swizzle2103 },
|
||||
{ convert_swizzle3210, convert_memcpy, convert_swizzle3012 },
|
||||
{ convert_swizzle2103, convert_swizzle1230, convert_memcpy },
|
||||
{ convert_swizzle_premultiply_3210_3210, convert_swizzle_premultiply_0123_3210, convert_swizzle_premultiply_3012_3210, },
|
||||
{ convert_swizzle_premultiply_3210_0123, convert_swizzle_premultiply_0123_0123, convert_swizzle_premultiply_3012_0123 },
|
||||
{ convert_swizzle_premultiply_3210_3012, convert_swizzle_premultiply_0123_3012, convert_swizzle_premultiply_3012_3012 },
|
||||
{ convert_swizzle_premultiply_3210_0321, convert_swizzle_premultiply_0123_0321, convert_swizzle_premultiply_3012_0321 },
|
||||
{ convert_swizzle_opaque_3210, convert_swizzle_opaque_0123, convert_swizzle_opaque_3012 },
|
||||
{ convert_swizzle_opaque_3012, convert_swizzle_opaque_0321, convert_swizzle_opaque_3210 }
|
||||
convert_memcpy,
|
||||
convert_swizzle3210,
|
||||
convert_swizzle2103,
|
||||
convert_swizzle_premultiply_3210_3210,
|
||||
convert_swizzle_premultiply_3210_0123,
|
||||
convert_swizzle_premultiply_3210_3012,
|
||||
convert_swizzle_premultiply_3210_0321,
|
||||
convert_swizzle_opaque_3210,
|
||||
convert_swizzle_opaque_3012,
|
||||
convert_16to8_swizzle_opaque_2103,
|
||||
convert_16to8_swizzle_2103,
|
||||
convert_fp16_swizzle_opaque_3210,
|
||||
convert_fp16_swizzle_3210,
|
||||
convert_float_swizzle_opaque_3210,
|
||||
convert_float_swizzle_3210,
|
||||
};
|
||||
|
||||
static ConversionFunc converters_to_argb[GDK_MEMORY_N_FORMATS] =
|
||||
{
|
||||
convert_swizzle3210,
|
||||
convert_memcpy,
|
||||
convert_swizzle1230,
|
||||
convert_swizzle_premultiply_0123_3210,
|
||||
convert_swizzle_premultiply_0123_0123,
|
||||
convert_swizzle_premultiply_0123_3012,
|
||||
convert_swizzle_premultiply_0123_0321,
|
||||
convert_swizzle_opaque_0123,
|
||||
convert_swizzle_opaque_0321,
|
||||
convert_16to8_swizzle_opaque_1230,
|
||||
convert_16to8_swizzle_1230,
|
||||
convert_fp16_swizzle_opaque_0123,
|
||||
convert_fp16_swizzle_0123,
|
||||
convert_float_swizzle_opaque_0123,
|
||||
convert_float_swizzle_0123,
|
||||
};
|
||||
|
||||
static ConversionFunc converters_to_rgba[GDK_MEMORY_N_FORMATS] =
|
||||
{
|
||||
convert_swizzle2103,
|
||||
convert_swizzle3012,
|
||||
convert_memcpy,
|
||||
convert_swizzle_premultiply_3012_3210,
|
||||
convert_swizzle_premultiply_3012_0123,
|
||||
convert_swizzle_premultiply_3012_3012,
|
||||
convert_swizzle_premultiply_3012_0321,
|
||||
convert_swizzle_opaque_3012,
|
||||
convert_swizzle_opaque_3210,
|
||||
convert_16to8_swizzle_opaque_0123,
|
||||
convert_16to8_swizzle_0123,
|
||||
convert_fp16_swizzle_opaque_3012,
|
||||
convert_fp16_swizzle_3012,
|
||||
convert_float_swizzle_opaque_3012,
|
||||
convert_float_swizzle_3012
|
||||
};
|
||||
|
||||
static void
|
||||
convert_rgba_to_hdr (guchar *dest_data,
|
||||
gsize dest_stride,
|
||||
GdkMemoryFormat dest_format,
|
||||
const guchar *src_data,
|
||||
gsize src_stride,
|
||||
GdkMemoryFormat src_format,
|
||||
gsize width,
|
||||
gsize height)
|
||||
{
|
||||
gsize x, y;
|
||||
|
||||
for (y = 0; y < height; y++)
|
||||
{
|
||||
guint16 *src = (guint16 *)src_data;
|
||||
float *dest = (float *)dest_data;
|
||||
for (x = 0; x < width; x++)
|
||||
{
|
||||
dest[4 * (y * width + x) + 0] = src[3 * (y * width + x) + 0] / 255.f;
|
||||
dest[4 * (y * width + x) + 1] = src[3 * (y * width + x) + 1] / 255.f;
|
||||
dest[4 * (y * width + x) + 2] = src[3 * (y * width + x) + 2] / 255.f;
|
||||
dest[4 * (y * width + x) + 3] = src[3 * (y * width + x) + 3] / 255.f;
|
||||
}
|
||||
dest_data += dest_stride;
|
||||
src_data += src_stride;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
convert_8bit_to_hdr (guchar *dest_data,
|
||||
gsize dest_stride,
|
||||
GdkMemoryFormat dest_format,
|
||||
const guchar *src_data,
|
||||
gsize src_stride,
|
||||
GdkMemoryFormat src_format,
|
||||
gsize width,
|
||||
gsize height)
|
||||
{
|
||||
guchar *data;
|
||||
|
||||
/* TODO: this could perhaps be done in-place */
|
||||
data = g_malloc (width * height * 4);
|
||||
|
||||
gdk_memory_convert (data, 4 * width, GDK_MEMORY_R8G8B8A8_PREMULTIPLIED,
|
||||
src_data, src_stride, src_format,
|
||||
width, height);
|
||||
convert_rgba_to_hdr (dest_data, dest_stride, dest_format,
|
||||
data, 4 * width, GDK_MEMORY_R8G8B8A8_PREMULTIPLIED,
|
||||
width, height);
|
||||
|
||||
g_free (data);
|
||||
}
|
||||
|
||||
static void
|
||||
convert_16bit_to_hdr (guchar *dest_data,
|
||||
gsize dest_stride,
|
||||
GdkMemoryFormat dest_format,
|
||||
const guchar *src_data,
|
||||
gsize src_stride,
|
||||
GdkMemoryFormat src_format,
|
||||
gsize width,
|
||||
gsize height)
|
||||
{
|
||||
gsize x, y;
|
||||
|
||||
for (y = 0; y < height; y++)
|
||||
{
|
||||
guint16 *src = (guint16 *)src_data;
|
||||
float *dest = (float *)dest_data;
|
||||
for (x = 0; x < width; x++)
|
||||
{
|
||||
dest[4 * (y * width + x) + 0] = src[3 * (y * width + x) + 0] / 65535.f;
|
||||
dest[4 * (y * width + x) + 1] = src[3 * (y * width + x) + 1] / 65535.f;
|
||||
dest[4 * (y * width + x) + 2] = src[3 * (y * width + x) + 2] / 65535.f;
|
||||
dest[4 * (y * width + x) + 3] = src[3 * (y * width + x) + 3] / 65535.f;
|
||||
}
|
||||
dest_data += dest_stride;
|
||||
src_data += src_stride;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
convert_fp16_to_hdr (guchar *dest_data,
|
||||
gsize dest_stride,
|
||||
GdkMemoryFormat dest_format,
|
||||
const guchar *src_data,
|
||||
gsize src_stride,
|
||||
GdkMemoryFormat src_format,
|
||||
gsize width,
|
||||
gsize height)
|
||||
{
|
||||
gsize x, y;
|
||||
int src_bpp;
|
||||
|
||||
if (src_format == GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED)
|
||||
src_bpp = 4;
|
||||
else
|
||||
src_bpp = 3;
|
||||
|
||||
for (y = 0; y < height; y++)
|
||||
{
|
||||
guint16 *src = (guint16 *)src_data;
|
||||
float *dest = (float *)dest_data;
|
||||
for (x = 0; x < width; x++)
|
||||
{
|
||||
guint16 h[4];
|
||||
|
||||
h[0] = src[src_bpp * (y * width + x)];
|
||||
h[1] = src[src_bpp * (y * width + x) + 1];
|
||||
h[2] = src[src_bpp * (y * width + x) + 2];
|
||||
if (src_bpp == 4)
|
||||
h[3] = src[src_bpp * (y * width + x) + 3];
|
||||
else
|
||||
h[3] = FP16_ONE;
|
||||
half_to_float4 (h, &dest[4 * (y * width + x)]);
|
||||
}
|
||||
dest_data += dest_stride;
|
||||
src_data += src_stride;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
convert_float_to_hdr (guchar *dest_data,
|
||||
gsize dest_stride,
|
||||
GdkMemoryFormat dest_format,
|
||||
const guchar *src_data,
|
||||
gsize src_stride,
|
||||
GdkMemoryFormat src_format,
|
||||
gsize width,
|
||||
gsize height)
|
||||
{
|
||||
gsize x, y;
|
||||
|
||||
for (y = 0; y < height; y++)
|
||||
{
|
||||
float *src = (float *)src_data;
|
||||
float *dest = (float *)dest_data;
|
||||
for (x = 0; x < width; x++)
|
||||
{
|
||||
dest[4 * (y * width + x) + 0] = src[3 * (y * width + x) + 0];
|
||||
dest[4 * (y * width + x) + 1] = src[3 * (y * width + x) + 1];
|
||||
dest[4 * (y * width + x) + 2] = src[3 * (y * width + x) + 2];
|
||||
dest[4 * (y * width + x) + 3] = 1.0;
|
||||
}
|
||||
dest_data += dest_stride;
|
||||
src_data += src_stride;
|
||||
}
|
||||
}
|
||||
|
||||
static ConversionFunc converters_to_hdr[GDK_MEMORY_N_FORMATS] =
|
||||
{
|
||||
convert_8bit_to_hdr,
|
||||
convert_8bit_to_hdr,
|
||||
convert_rgba_to_hdr,
|
||||
convert_8bit_to_hdr,
|
||||
convert_8bit_to_hdr,
|
||||
convert_8bit_to_hdr,
|
||||
convert_8bit_to_hdr,
|
||||
convert_8bit_to_hdr,
|
||||
convert_8bit_to_hdr,
|
||||
convert_16bit_to_hdr,
|
||||
convert_fp16_to_hdr,
|
||||
convert_fp16_to_hdr,
|
||||
convert_float_to_hdr,
|
||||
convert_memcpy
|
||||
};
|
||||
static ConversionFunc* converters[GDK_MEMORY_N_FORMATS] = {
|
||||
converters_to_bgra, converters_to_argb, converters_to_rgba,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL,
|
||||
converters_to_hdr
|
||||
};
|
||||
|
||||
void
|
||||
@@ -316,5 +777,7 @@ gdk_memory_convert (guchar *dest_data,
|
||||
g_assert (dest_format < 3);
|
||||
g_assert (src_format < GDK_MEMORY_N_FORMATS);
|
||||
|
||||
converters[src_format][dest_format] (dest_data, dest_stride, src_data, src_stride, width, height);
|
||||
converters[dest_format][src_format] (dest_data, dest_stride, dest_format,
|
||||
src_data, src_stride, src_format,
|
||||
width, height);
|
||||
}
|
||||
|
||||
@@ -42,6 +42,20 @@ G_BEGIN_DECLS
|
||||
* @GDK_MEMORY_A8B8G8R8: 4 bytes; for alpha, blue, green, red.
|
||||
* @GDK_MEMORY_R8G8B8: 3 bytes; for red, green, blue. The data is opaque.
|
||||
* @GDK_MEMORY_B8G8R8: 3 bytes; for blue, green, red. The data is opaque.
|
||||
* @GDK_MEMORY_R16G16B16: 3 guint16 values; for red, green, blue. Since 4.6
|
||||
* @GDK_MEMORY_R16G16B16A16_PREMULTIPLIED: 4 guint16 values; for red, green,
|
||||
* blue, alpha. The color values are premultiplied with the alpha value.
|
||||
* Since 4.6
|
||||
* @GDK_MEMORY_R16G16B16_FLOAT: 3 half-float values; for red, green, blue.
|
||||
* The data is opaque. Since 4.6
|
||||
* @GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED: 4 half-float values; for
|
||||
* red, green, blue and alpha. The color values are premultiplied with
|
||||
* the alpha value. Since 4.6
|
||||
* @GDK_MEMORY_B32G32R32_FLOAT: 3 float values; for blue, green, red.
|
||||
* The data is opaque. Since 4.6
|
||||
* @GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED: 4 float values; for
|
||||
* red, green, blue and alpha. The color values are premultiplied with
|
||||
* the alpha value. Since 4.6
|
||||
* @GDK_MEMORY_N_FORMATS: The number of formats. This value will change as
|
||||
* more formats get added, so do not rely on its concrete integer.
|
||||
*
|
||||
@@ -53,6 +67,8 @@ G_BEGIN_DECLS
|
||||
* CAIRO_FORMAT_ARGB32 is represented by different `GdkMemoryFormats`
|
||||
* on architectures with different endiannesses.
|
||||
*
|
||||
* Note that color data is assumed to be linear.
|
||||
*
|
||||
* Its naming is modelled after
|
||||
* [VkFormat](https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VkFormat)
|
||||
* for details).
|
||||
@@ -67,6 +83,12 @@ typedef enum {
|
||||
GDK_MEMORY_A8B8G8R8,
|
||||
GDK_MEMORY_R8G8B8,
|
||||
GDK_MEMORY_B8G8R8,
|
||||
GDK_MEMORY_R16G16B16,
|
||||
GDK_MEMORY_R16G16B16A16_PREMULTIPLIED,
|
||||
GDK_MEMORY_R16G16B16_FLOAT,
|
||||
GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED,
|
||||
GDK_MEMORY_R32G32B32_FLOAT,
|
||||
GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED,
|
||||
|
||||
GDK_MEMORY_N_FORMATS
|
||||
} GdkMemoryFormat;
|
||||
|
||||
+5
-5
@@ -70,17 +70,17 @@ typedef enum {
|
||||
* snapshot at or 0 if none. This is purely a hint. The object must still
|
||||
* be able to render at any size.
|
||||
* @get_intrinsic_aspect_ratio: The preferred aspect ratio for this object
|
||||
* or 0 if none. If both [vfunc@Gdk.PaintableInterface.get_intrinsic_width]
|
||||
* and [vfunc@Gdk.PaintableInterface.get_intrinsic_height] return non-zero
|
||||
* or 0 if none. If both [vfunc@Gdk.Paintable.get_intrinsic_width]
|
||||
* and [vfunc@Gdk.Paintable.get_intrinsic_height] return non-zero
|
||||
* values, this function should return the aspect ratio computed from those.
|
||||
*
|
||||
* The list of functions that can be implemented for the `GdkPaintable`
|
||||
* interface.
|
||||
*
|
||||
* Note that apart from the [vfunc@Gdk.PaintableInterface.snapshot] function,
|
||||
* Note that apart from the [vfunc@Gdk.Paintable.snapshot] function,
|
||||
* no virtual function of this interface is mandatory to implement, though it
|
||||
* is a good idea to implement [vfunc@Gdk.PaintableInterface.get_current_image]
|
||||
* for non-static paintables and [vfunc@Gdk.PaintableInterface.get_flags] if the
|
||||
* is a good idea to implement [vfunc@Gdk.Paintable.get_current_image]
|
||||
* for non-static paintables and [vfunc@Gdk.Paintable.get_flags] if the
|
||||
* image is not dynamic as the default implementation returns no flags and
|
||||
* that will make the implementation likely quite slow.
|
||||
*/
|
||||
|
||||
+489
@@ -0,0 +1,489 @@
|
||||
/* GDK - The GIMP Drawing Kit
|
||||
* Copyright (C) 2021 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gdkpng.h"
|
||||
|
||||
#include "gdktexture.h"
|
||||
#include "gdkmemorytextureprivate.h"
|
||||
#include "gsk/ngl/fp16private.h"
|
||||
#include <png.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* {{{ IO handling */
|
||||
|
||||
#ifdef HAVE_FOPENCOOKIE
|
||||
|
||||
static ssize_t
|
||||
read_stream (void *cookie,
|
||||
char *buf,
|
||||
size_t size)
|
||||
{
|
||||
GInputStream *stream = cookie;
|
||||
|
||||
return g_input_stream_read (stream, buf, size, NULL, NULL);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
write_stream (void *cookie,
|
||||
const char *buf,
|
||||
size_t size)
|
||||
{
|
||||
GOutputStream *stream = cookie;
|
||||
|
||||
return g_output_stream_write (stream, buf, size, NULL, NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
seek_stream (void *cookie,
|
||||
off64_t *offset,
|
||||
int whence)
|
||||
{
|
||||
GSeekable *seekable = cookie;
|
||||
GSeekType seek_type;
|
||||
|
||||
if (whence == SEEK_SET)
|
||||
seek_type = G_SEEK_SET;
|
||||
else if (whence == SEEK_CUR)
|
||||
seek_type = G_SEEK_CUR;
|
||||
else if (whence == SEEK_END)
|
||||
seek_type = G_SEEK_END;
|
||||
else
|
||||
g_assert_not_reached ();
|
||||
|
||||
if (g_seekable_seek (seekable, *offset, seek_type, NULL, NULL))
|
||||
{
|
||||
*offset = g_seekable_tell (seekable);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
close_stream (void *cookie)
|
||||
{
|
||||
GObject *stream = cookie;
|
||||
|
||||
g_object_unref (stream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static cookie_io_functions_t cookie_funcs = {
|
||||
read_stream,
|
||||
write_stream,
|
||||
seek_stream,
|
||||
close_stream
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
static GBytes *
|
||||
read_all_data (GInputStream *source,
|
||||
GError **error)
|
||||
{
|
||||
GOutputStream *output;
|
||||
gssize size;
|
||||
GBytes *bytes;
|
||||
|
||||
output = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
|
||||
size = g_output_stream_splice (output, source, 0, NULL, error);
|
||||
if (size == -1)
|
||||
{
|
||||
g_object_unref (output);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_output_stream_close (output, NULL, NULL);
|
||||
bytes = g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (output));
|
||||
g_object_unref (output);
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* }}} */
|
||||
/* {{{ Format conversion */
|
||||
|
||||
static void
|
||||
convert_half_float (guchar *dest_data,
|
||||
gsize dest_stride,
|
||||
GdkMemoryFormat dest_format,
|
||||
const guchar *src_data,
|
||||
gsize src_stride,
|
||||
GdkMemoryFormat src_format,
|
||||
gsize width,
|
||||
gsize height)
|
||||
{
|
||||
gsize x, y;
|
||||
guint16 *dest;
|
||||
const guint16 *src;
|
||||
float *c;
|
||||
|
||||
c = g_malloc (width * 4 * sizeof (float));
|
||||
for (y = 0; y < height; y++)
|
||||
{
|
||||
dest = (guint16 *)dest_data;
|
||||
src = (const guint16 *)src_data;
|
||||
|
||||
half_to_float (src, c, width);
|
||||
for (x = 0; x < width; x++)
|
||||
{
|
||||
dest[4 * x ] = (guint16)(65535 * c[4 * x ]);
|
||||
dest[4 * x + 1] = (guint16)(65535 * c[4 * x + 1]);
|
||||
dest[4 * x + 2] = (guint16)(65535 * c[4 * x + 2]);
|
||||
if (src_format == GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED)
|
||||
dest[4 * x + 3] = (guint16)(65535 * c[4 * x + 3]);
|
||||
else
|
||||
dest[4 * x + 3] = 65535;
|
||||
}
|
||||
dest_data += dest_stride;
|
||||
src_data += src_stride;
|
||||
}
|
||||
g_free (c);
|
||||
}
|
||||
|
||||
static void
|
||||
convert_float (guchar *dest_data,
|
||||
gsize dest_stride,
|
||||
GdkMemoryFormat dest_format,
|
||||
const guchar *src_data,
|
||||
gsize src_stride,
|
||||
GdkMemoryFormat src_format,
|
||||
gsize width,
|
||||
gsize height)
|
||||
{
|
||||
gsize x, y;
|
||||
guint16 *dest;
|
||||
const float *src;
|
||||
|
||||
for (y = 0; y < height; y++)
|
||||
{
|
||||
dest = (guint16 *)dest_data;
|
||||
src = (const float *)src_data;
|
||||
for (x = 0; x < width; x++)
|
||||
{
|
||||
dest[4 * x ] = (guint16)(65535 * src[4 * x ]);
|
||||
dest[4 * x + 1] = (guint16)(65535 * src[4 * x + 1]);
|
||||
dest[4 * x + 2] = (guint16)(65535 * src[4 * x + 2]);
|
||||
if (src_format == GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED)
|
||||
dest[4 * x + 3] = (guint16)(65535 * src[4 * x + 3]);
|
||||
else
|
||||
dest[4 * x + 3] = 65535;
|
||||
}
|
||||
dest_data += dest_stride;
|
||||
src_data += src_stride;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
convert (guchar *dest_data,
|
||||
gsize dest_stride,
|
||||
GdkMemoryFormat dest_format,
|
||||
const guchar *src_data,
|
||||
gsize src_stride,
|
||||
GdkMemoryFormat src_format,
|
||||
gsize width,
|
||||
gsize height)
|
||||
{
|
||||
if (dest_format < 3)
|
||||
gdk_memory_convert (dest_data, dest_stride, dest_format,
|
||||
src_data, src_stride, src_format,
|
||||
width, height);
|
||||
else
|
||||
{
|
||||
g_assert (dest_format == GDK_MEMORY_R16G16B16A16_PREMULTIPLIED);
|
||||
|
||||
switch ((int)src_format)
|
||||
{
|
||||
case GDK_MEMORY_R16G16B16_FLOAT:
|
||||
case GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED:
|
||||
convert_half_float (dest_data, dest_stride, dest_format,
|
||||
src_data, src_stride, src_format,
|
||||
width, height);
|
||||
break;
|
||||
case GDK_MEMORY_R32G32B32_FLOAT:
|
||||
case GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED:
|
||||
convert_float (dest_data, dest_stride, dest_format,
|
||||
src_data, src_stride, src_format,
|
||||
width, height);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
/* {{{ Public API */
|
||||
|
||||
GdkTexture *
|
||||
gdk_load_png (GInputStream *stream,
|
||||
GError **error)
|
||||
{
|
||||
png_image image = { NULL, PNG_IMAGE_VERSION, 0, };
|
||||
gsize size;
|
||||
gsize stride;
|
||||
guint16 *buffer;
|
||||
GBytes *bytes;
|
||||
GdkTexture *texture;
|
||||
|
||||
#ifdef HAVE_FOPENCOOKIE
|
||||
FILE *file = fopencookie (g_object_ref (stream), "r", cookie_funcs);
|
||||
|
||||
png_image_begin_read_from_stdio (&image, file);
|
||||
#else
|
||||
GBytes *data;
|
||||
|
||||
data = read_all_data (stream, error);
|
||||
if (!data)
|
||||
return NULL;
|
||||
png_image_begin_read_from_memory (&image,
|
||||
g_bytes_get_data (data, NULL),
|
||||
g_bytes_get_size (data));
|
||||
#endif
|
||||
|
||||
image.format = PNG_FORMAT_LINEAR_RGB_ALPHA;
|
||||
|
||||
stride = PNG_IMAGE_ROW_STRIDE (image);
|
||||
size = PNG_IMAGE_BUFFER_SIZE (image, stride);
|
||||
buffer = g_malloc (size);
|
||||
|
||||
png_image_finish_read (&image, NULL, buffer, stride, NULL);
|
||||
|
||||
#ifdef HAVE_FOPENCOOKIE
|
||||
fclose (file);
|
||||
#else
|
||||
g_bytes_unref (data);
|
||||
#endif
|
||||
|
||||
bytes = g_bytes_new_take (buffer, size);
|
||||
|
||||
texture = gdk_memory_texture_new (image.width, image.height,
|
||||
GDK_MEMORY_R16G16B16A16_PREMULTIPLIED,
|
||||
bytes, 2 * stride);
|
||||
|
||||
g_bytes_unref (bytes);
|
||||
|
||||
png_image_free (&image);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gdk_save_png (GOutputStream *stream,
|
||||
const guchar *data,
|
||||
int width,
|
||||
int height,
|
||||
int stride,
|
||||
GdkMemoryFormat format,
|
||||
GError **error)
|
||||
{
|
||||
png_image image = { NULL, PNG_IMAGE_VERSION, 0, };
|
||||
gboolean result;
|
||||
guchar *new_data = NULL;
|
||||
|
||||
switch ((int)format)
|
||||
{
|
||||
case GDK_MEMORY_R8G8B8A8_PREMULTIPLIED:
|
||||
image.format = PNG_FORMAT_RGBA;
|
||||
break;
|
||||
case GDK_MEMORY_B8G8R8A8_PREMULTIPLIED:
|
||||
case GDK_MEMORY_A8R8G8B8_PREMULTIPLIED:
|
||||
case GDK_MEMORY_B8G8R8A8:
|
||||
case GDK_MEMORY_A8R8G8B8:
|
||||
case GDK_MEMORY_R8G8B8A8:
|
||||
case GDK_MEMORY_A8B8G8R8:
|
||||
case GDK_MEMORY_R8G8B8:
|
||||
case GDK_MEMORY_B8G8R8:
|
||||
stride = width * 4;
|
||||
new_data = g_malloc (stride * height);
|
||||
convert (new_data, stride, GDK_MEMORY_R8G8B8A8_PREMULTIPLIED,
|
||||
data, width * gdk_memory_format_bytes_per_pixel (format), format,
|
||||
width, height);
|
||||
data = new_data;
|
||||
image.format = PNG_FORMAT_RGBA;
|
||||
break;
|
||||
case GDK_MEMORY_R16G16B16A16_PREMULTIPLIED:
|
||||
image.format = PNG_FORMAT_LINEAR_RGB_ALPHA;
|
||||
break;
|
||||
case GDK_MEMORY_R16G16B16:
|
||||
image.format = PNG_FORMAT_LINEAR_RGB;
|
||||
break;
|
||||
case GDK_MEMORY_R16G16B16_FLOAT:
|
||||
case GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED:
|
||||
case GDK_MEMORY_R32G32B32_FLOAT:
|
||||
case GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED:
|
||||
stride = width * 8;
|
||||
new_data = g_malloc (stride * height);
|
||||
convert (new_data, stride, GDK_MEMORY_R16G16B16A16_PREMULTIPLIED,
|
||||
data, width * gdk_memory_format_bytes_per_pixel (format), format,
|
||||
width, height);
|
||||
image.format = PNG_FORMAT_LINEAR_RGB_ALPHA;
|
||||
break;
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
if (image.format & PNG_FORMAT_FLAG_LINEAR)
|
||||
stride /= 2;
|
||||
|
||||
#ifdef HAVE_FOPENCOOKIE
|
||||
FILE *file = fopencookie (g_object_ref (stream), "w", cookie_funcs);
|
||||
result = png_image_write_to_stdio (&image, file, FALSE, data, stride, NULL);
|
||||
fclose (file);
|
||||
#else
|
||||
gsize written;
|
||||
png_alloc_size_t size;
|
||||
gpointer buffer;
|
||||
png_image_write_get_memory_size (image, size, FALSE, data, stride, NULL);
|
||||
buffer = g_malloc (size);
|
||||
result = png_image_write_to_memory (&image, buffer, &size, FALSE, data, stride, NULL);
|
||||
if (result)
|
||||
result = g_output_stream_write_all (stream, buffer, (gsize)size, &written, NULL, NULL);
|
||||
g_free (buffer);
|
||||
#endif
|
||||
|
||||
if (!result)
|
||||
{
|
||||
g_set_error_literal (error,
|
||||
G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Saving png failed");
|
||||
}
|
||||
|
||||
png_image_free (&image);
|
||||
|
||||
g_free (new_data);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
/* {{{ Async code */
|
||||
|
||||
static void
|
||||
load_png_in_thread (GTask *task,
|
||||
gpointer source_object,
|
||||
gpointer task_data,
|
||||
GCancellable *cancellable)
|
||||
{
|
||||
GInputStream *stream = source_object;
|
||||
GdkTexture *texture;
|
||||
GError *error = NULL;
|
||||
|
||||
texture = gdk_load_png (stream, &error);
|
||||
|
||||
if (texture)
|
||||
g_task_return_pointer (task, texture, g_object_unref);
|
||||
else
|
||||
g_task_return_error (task, error);
|
||||
}
|
||||
|
||||
void
|
||||
gdk_load_png_async (GInputStream *stream,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GTask *task;
|
||||
|
||||
task = g_task_new (stream, cancellable, callback, user_data);
|
||||
g_task_run_in_thread (task, load_png_in_thread);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
GdkTexture *
|
||||
gdk_load_png_finish (GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
return g_task_propagate_pointer (G_TASK (result), error);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const guchar *data;
|
||||
int width;
|
||||
int height;
|
||||
int stride;
|
||||
GdkMemoryFormat format;
|
||||
} SavePngData;
|
||||
|
||||
static void
|
||||
save_png_in_thread (GTask *task,
|
||||
gpointer source_object,
|
||||
gpointer task_data,
|
||||
GCancellable *cancellable)
|
||||
{
|
||||
GOutputStream *stream = source_object;
|
||||
SavePngData *data = task_data;
|
||||
GError *error = NULL;
|
||||
gboolean result;
|
||||
|
||||
result = gdk_save_png (stream,
|
||||
data->data,
|
||||
data->width,
|
||||
data->height,
|
||||
data->stride,
|
||||
data->format,
|
||||
&error);
|
||||
|
||||
if (result)
|
||||
g_task_return_boolean (task, result);
|
||||
else
|
||||
g_task_return_error (task, error);
|
||||
}
|
||||
|
||||
void
|
||||
gdk_save_png_async (GOutputStream *stream,
|
||||
const guchar *data,
|
||||
int width,
|
||||
int height,
|
||||
int stride,
|
||||
GdkMemoryFormat format,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GTask *task;
|
||||
SavePngData *save_data;
|
||||
|
||||
save_data = g_new0 (SavePngData, 1);
|
||||
save_data->data = data;
|
||||
save_data->width = width;
|
||||
save_data->height = height;
|
||||
save_data->stride = stride;
|
||||
save_data->format = format;
|
||||
|
||||
task = g_task_new (stream, cancellable, callback, user_data);
|
||||
g_task_set_task_data (task, save_data, g_free);
|
||||
g_task_run_in_thread (task, save_png_in_thread);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gdk_save_png_finish (GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
return g_task_propagate_boolean (G_TASK (result), error);
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
|
||||
/* vim:set foldmethod=marker expandtab: */
|
||||
@@ -0,0 +1,56 @@
|
||||
/* GDK - The GIMP Drawing Kit
|
||||
* Copyright (C) 2021 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __GDK_PNG_H__
|
||||
#define __GDK_PNG_H__
|
||||
|
||||
#include "gdkmemorytexture.h"
|
||||
#include <gio/gio.h>
|
||||
|
||||
GdkTexture *gdk_load_png (GInputStream *stream,
|
||||
GError **error);
|
||||
|
||||
void gdk_load_png_async (GInputStream *stream,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
GdkTexture *gdk_load_png_finish (GAsyncResult *result,
|
||||
GError **error);
|
||||
|
||||
gboolean gdk_save_png (GOutputStream *stream,
|
||||
const guchar *data,
|
||||
int width,
|
||||
int height,
|
||||
int stride,
|
||||
GdkMemoryFormat format,
|
||||
GError **error);
|
||||
|
||||
void gdk_save_png_async (GOutputStream *stream,
|
||||
const guchar *data,
|
||||
int width,
|
||||
int height,
|
||||
int stride,
|
||||
GdkMemoryFormat format,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
gboolean gdk_save_png_finish (GAsyncResult *result,
|
||||
GError **error);
|
||||
|
||||
#endif
|
||||
+246
-16
@@ -46,6 +46,9 @@
|
||||
#include "gdksnapshot.h"
|
||||
|
||||
#include <graphene.h>
|
||||
#include "gdkpng.h"
|
||||
#include "gdktiff.h"
|
||||
#include "gdkjpeg.h"
|
||||
|
||||
/* HACK: So we don't need to include any (not-yet-created) GSK or GTK headers */
|
||||
void
|
||||
@@ -124,6 +127,13 @@ gdk_texture_real_download (GdkTexture *self,
|
||||
GDK_TEXTURE_WARN_NOT_IMPLEMENTED_METHOD (self, download);
|
||||
}
|
||||
|
||||
static GBytes *
|
||||
gdk_texture_real_download_format (GdkTexture *self,
|
||||
GdkMemoryFormat format)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_texture_set_property (GObject *gobject,
|
||||
guint prop_id,
|
||||
@@ -188,6 +198,7 @@ gdk_texture_class_init (GdkTextureClass *klass)
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
klass->download = gdk_texture_real_download;
|
||||
klass->download_format = gdk_texture_real_download_format;
|
||||
|
||||
gobject_class->set_property = gdk_texture_set_property;
|
||||
gobject_class->get_property = gdk_texture_get_property;
|
||||
@@ -351,6 +362,10 @@ gdk_texture_new_from_resource (const char *resource_path)
|
||||
* The file format is detected automatically. The supported formats
|
||||
* are PNG and JPEG, though more formats might be available.
|
||||
*
|
||||
* For PNG files, this function supports conversion from SRGB to
|
||||
* linear data (including gamma correction) and can load 16bit
|
||||
* data.
|
||||
*
|
||||
* If %NULL is returned, then @error will be set.
|
||||
*
|
||||
* Return value: A newly-created `GdkTexture`
|
||||
@@ -362,6 +377,9 @@ gdk_texture_new_from_file (GFile *file,
|
||||
GdkTexture *texture;
|
||||
GdkPixbuf *pixbuf;
|
||||
GInputStream *stream;
|
||||
GInputStream *buffered;
|
||||
const void *data;
|
||||
gsize size;
|
||||
|
||||
g_return_val_if_fail (G_IS_FILE (file), NULL);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
||||
@@ -370,6 +388,35 @@ gdk_texture_new_from_file (GFile *file,
|
||||
if (stream == NULL)
|
||||
return NULL;
|
||||
|
||||
buffered = g_buffered_input_stream_new (stream);
|
||||
g_object_unref (stream);
|
||||
stream = buffered;
|
||||
|
||||
g_buffered_input_stream_fill (G_BUFFERED_INPUT_STREAM (stream), 8, NULL, NULL);
|
||||
data = g_buffered_input_stream_peek_buffer (G_BUFFERED_INPUT_STREAM (stream), &size);
|
||||
|
||||
if (memcmp (data, "\x89PNG", 4) == 0)
|
||||
{
|
||||
texture = gdk_load_png (stream, error);
|
||||
g_object_unref (stream);
|
||||
return texture;
|
||||
}
|
||||
|
||||
if (memcmp (data, "MM\x00\x2a", 4) == 0 ||
|
||||
memcmp (data, "II\x2a\x00", 4) == 0)
|
||||
{
|
||||
texture = gdk_load_tiff (stream, error);
|
||||
g_object_unref (stream);
|
||||
return texture;
|
||||
}
|
||||
|
||||
if (memcmp (data, "\xff\xd8", 2) == 0)
|
||||
{
|
||||
texture = gdk_load_jpeg (stream, error);
|
||||
g_object_unref (stream);
|
||||
return texture;
|
||||
}
|
||||
|
||||
pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, error);
|
||||
g_object_unref (stream);
|
||||
if (pixbuf == NULL)
|
||||
@@ -491,6 +538,95 @@ gdk_texture_download (GdkTexture *texture,
|
||||
stride);
|
||||
}
|
||||
|
||||
/* Returns the texture data in the requested format if that
|
||||
* can be done without conversion. NULL, otherwise.
|
||||
*/
|
||||
GBytes *
|
||||
gdk_texture_download_format (GdkTexture *texture,
|
||||
GdkMemoryFormat format)
|
||||
{
|
||||
return GDK_TEXTURE_GET_CLASS (texture)->download_format (texture, format);
|
||||
}
|
||||
|
||||
/* Returns the texture data in the requested format, converting
|
||||
* it if necessary. This will only return NULL if we don't know
|
||||
* how to convert from the texture's format to the requested one.
|
||||
*/
|
||||
GBytes *
|
||||
gdk_texture_convert_format (GdkTexture *texture,
|
||||
GdkMemoryFormat format)
|
||||
{
|
||||
GdkMemoryFormat src_format;
|
||||
GBytes *bytes;
|
||||
int width, height, stride;
|
||||
guchar *data;
|
||||
|
||||
for (int i = 0; i < GDK_MEMORY_N_FORMATS; i++)
|
||||
{
|
||||
bytes = gdk_texture_download_format (texture, i);
|
||||
if (bytes)
|
||||
{
|
||||
src_format = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bytes || src_format == format)
|
||||
return bytes;
|
||||
|
||||
/* convert from src_format to format */
|
||||
|
||||
width = gdk_texture_get_width (texture);
|
||||
height = gdk_texture_get_height (texture);
|
||||
stride = width * gdk_memory_format_bytes_per_pixel (format);
|
||||
data = g_malloc (height * stride);
|
||||
|
||||
gdk_memory_convert (data, stride, format,
|
||||
g_bytes_get_data (bytes, NULL),
|
||||
g_bytes_get_size (bytes) / height,
|
||||
src_format,
|
||||
width, height);
|
||||
|
||||
g_bytes_unref (bytes);
|
||||
|
||||
return g_bytes_new_take (data, height * stride);
|
||||
}
|
||||
|
||||
/*
|
||||
* gdk_texture_download_float:
|
||||
* @texture: a `GdkTexture`
|
||||
* @data: (array): pointer to enough memory to be filled with the
|
||||
* downloaded data of @texture
|
||||
*
|
||||
* Downloads the @texture into local memory.
|
||||
*
|
||||
* This may be an expensive operation, as the actual texture data
|
||||
* may reside on a GPU or on a remote display server.
|
||||
*
|
||||
* The data format of the downloaded data is equivalent to
|
||||
* GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED, so every downloaded
|
||||
* pixel requires 16 bytes of memory.
|
||||
*
|
||||
* Note that the caller is responsible to provide sufficiently
|
||||
* aligned memory to access the resulting data directly as floats.
|
||||
*
|
||||
* Since: 4.6
|
||||
*/
|
||||
void
|
||||
gdk_texture_download_float (GdkTexture *texture,
|
||||
float *data)
|
||||
{
|
||||
GBytes *bytes;
|
||||
|
||||
bytes = gdk_texture_convert_format (texture, GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED);
|
||||
|
||||
g_assert (bytes);
|
||||
|
||||
memcpy (data, g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes));
|
||||
|
||||
g_bytes_unref (bytes);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gdk_texture_set_render_data (GdkTexture *self,
|
||||
gpointer key,
|
||||
@@ -537,6 +673,9 @@ gdk_texture_get_render_data (GdkTexture *self,
|
||||
*
|
||||
* Store the given @texture to the @filename as a PNG file.
|
||||
*
|
||||
* If the texture contains 16bit data, the generated PNG file
|
||||
* will have linear 16bit data, otherwise it will contain SRGB.
|
||||
*
|
||||
* This is a utility function intended for debugging and testing.
|
||||
* If you want more control over formats, proper error handling or
|
||||
* want to store to a `GFile` or other location, you might want to
|
||||
@@ -548,30 +687,121 @@ gboolean
|
||||
gdk_texture_save_to_png (GdkTexture *texture,
|
||||
const char *filename)
|
||||
{
|
||||
cairo_surface_t *surface;
|
||||
cairo_status_t status;
|
||||
int width, height, stride;
|
||||
GBytes *bytes;
|
||||
GdkMemoryFormat format;
|
||||
gboolean result;
|
||||
GFile *file;
|
||||
GOutputStream *stream;
|
||||
|
||||
g_return_val_if_fail (GDK_IS_TEXTURE (texture), FALSE);
|
||||
g_return_val_if_fail (filename != NULL, FALSE);
|
||||
|
||||
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
|
||||
gdk_texture_get_width (texture),
|
||||
gdk_texture_get_height (texture));
|
||||
gdk_texture_download (texture,
|
||||
cairo_image_surface_get_data (surface),
|
||||
cairo_image_surface_get_stride (surface));
|
||||
cairo_surface_mark_dirty (surface);
|
||||
width = gdk_texture_get_width (texture);
|
||||
height = gdk_texture_get_height (texture);
|
||||
|
||||
status = cairo_surface_write_to_png (surface, filename);
|
||||
|
||||
if (status != CAIRO_STATUS_SUCCESS ||
|
||||
cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS)
|
||||
result = FALSE;
|
||||
bytes = gdk_texture_download_format (texture, GDK_MEMORY_R16G16B16A16_PREMULTIPLIED);
|
||||
if (bytes)
|
||||
{
|
||||
format = GDK_MEMORY_R16G16B16A16_PREMULTIPLIED;
|
||||
stride = width * 8;
|
||||
}
|
||||
else
|
||||
result = TRUE;
|
||||
{
|
||||
gpointer data;
|
||||
|
||||
cairo_surface_destroy (surface);
|
||||
stride = width * 4;
|
||||
data = g_malloc (stride * height);
|
||||
|
||||
gdk_texture_download (texture, data, stride);
|
||||
|
||||
bytes = g_bytes_new_take (data, stride * height);
|
||||
|
||||
format = GDK_MEMORY_DEFAULT;
|
||||
}
|
||||
|
||||
file = g_file_new_for_path (filename);
|
||||
stream = G_OUTPUT_STREAM (g_file_replace (file, NULL, FALSE,
|
||||
G_FILE_CREATE_NONE,
|
||||
NULL, NULL));
|
||||
g_object_unref (file);
|
||||
|
||||
result = gdk_save_png (stream,
|
||||
g_bytes_get_data (bytes, NULL),
|
||||
width, height, stride,
|
||||
format,
|
||||
NULL);
|
||||
|
||||
g_output_stream_close (stream, NULL, NULL);
|
||||
g_object_unref (stream);
|
||||
|
||||
g_bytes_unref (bytes);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_texture_save_to_file:
|
||||
* @texture: a `GdkTexture`
|
||||
* @filename: (type filename): the filename to store to
|
||||
*
|
||||
* Store the given @texture to the @filename.
|
||||
*
|
||||
* GTK will choose a suitable file format to save the data in
|
||||
* depending on the format of the texture. Currently, that is
|
||||
* tiff, for all kinds of textures.
|
||||
*
|
||||
* This is a utility function intended for debugging and testing.
|
||||
* If you want more control over formats, proper error handling or
|
||||
* want to store to a `GFile` or other location, you might want to
|
||||
* look into using the gdk-pixbuf library.
|
||||
*
|
||||
* Returns: %TRUE if saving succeeded, %FALSE on failure.
|
||||
*
|
||||
* Since: 4.6
|
||||
*/
|
||||
gboolean
|
||||
gdk_texture_save_to_file (GdkTexture *texture,
|
||||
const char *filename)
|
||||
{
|
||||
GBytes *bytes = NULL;
|
||||
gboolean result;
|
||||
GdkMemoryFormat format;
|
||||
GFile *file;
|
||||
GOutputStream *stream;
|
||||
|
||||
g_return_val_if_fail (GDK_IS_TEXTURE (texture), FALSE);
|
||||
g_return_val_if_fail (filename != NULL, FALSE);
|
||||
|
||||
for (int i = 0; i < GDK_MEMORY_N_FORMATS; i++)
|
||||
{
|
||||
bytes = gdk_texture_download_format (texture, i);
|
||||
if (bytes)
|
||||
{
|
||||
format = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bytes)
|
||||
return FALSE;
|
||||
|
||||
file = g_file_new_for_path (filename);
|
||||
stream = G_OUTPUT_STREAM (g_file_replace (file, NULL, FALSE,
|
||||
G_FILE_CREATE_NONE,
|
||||
NULL, NULL));
|
||||
g_object_unref (file);
|
||||
|
||||
result = gdk_save_tiff (stream,
|
||||
g_bytes_get_data (bytes, NULL),
|
||||
gdk_texture_get_width (texture),
|
||||
gdk_texture_get_height (texture),
|
||||
gdk_texture_get_width (texture) * gdk_memory_format_bytes_per_pixel (format),
|
||||
format,
|
||||
NULL);
|
||||
|
||||
g_clear_object (&stream);
|
||||
g_clear_pointer (&bytes, g_bytes_unref);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -59,9 +59,15 @@ GDK_AVAILABLE_IN_ALL
|
||||
void gdk_texture_download (GdkTexture *texture,
|
||||
guchar *data,
|
||||
gsize stride);
|
||||
GDK_AVAILABLE_IN_4_6
|
||||
void gdk_texture_download_float (GdkTexture *texture,
|
||||
float *data);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gdk_texture_save_to_png (GdkTexture *texture,
|
||||
const char *filename);
|
||||
GDK_AVAILABLE_IN_4_6
|
||||
gboolean gdk_texture_save_to_file (GdkTexture *texture,
|
||||
const char *filename);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define __GDK_TEXTURE_PRIVATE_H__
|
||||
|
||||
#include "gdktexture.h"
|
||||
#include "gdkmemorytexture.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
@@ -28,6 +29,8 @@ struct _GdkTextureClass {
|
||||
const GdkRectangle *area,
|
||||
guchar *data,
|
||||
gsize stride);
|
||||
GBytes * (* download_format) (GdkTexture *texture,
|
||||
GdkMemoryFormat format);
|
||||
};
|
||||
|
||||
gpointer gdk_texture_new (const GdkTextureClass *klass,
|
||||
@@ -48,6 +51,11 @@ void gdk_texture_clear_render_data (GdkTexture
|
||||
gpointer gdk_texture_get_render_data (GdkTexture *self,
|
||||
gpointer key);
|
||||
|
||||
GBytes * gdk_texture_download_format (GdkTexture *texture,
|
||||
GdkMemoryFormat format);
|
||||
GBytes * gdk_texture_convert_format (GdkTexture *texture,
|
||||
GdkMemoryFormat format);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GDK_TEXTURE_PRIVATE_H__ */
|
||||
|
||||
+628
@@ -0,0 +1,628 @@
|
||||
/* GDK - The GIMP Drawing Kit
|
||||
* Copyright (C) 2021 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gdktiff.h"
|
||||
|
||||
#include "gdktexture.h"
|
||||
#include "gdkmemorytextureprivate.h"
|
||||
#include <tiffio.h>
|
||||
|
||||
/* {{{ IO handling */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GObject *stream;
|
||||
GInputStream *input;
|
||||
GOutputStream *output;
|
||||
|
||||
gchar *buffer;
|
||||
gsize allocated;
|
||||
gsize used;
|
||||
gsize position;
|
||||
} TiffIO;
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wsuggest-attribute=format"
|
||||
|
||||
static void
|
||||
tiff_io_warning (const char *module,
|
||||
const char *fmt,
|
||||
va_list ap)
|
||||
{
|
||||
g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, fmt, ap);
|
||||
}
|
||||
|
||||
static void
|
||||
tiff_io_error (const char *module,
|
||||
const char *fmt,
|
||||
va_list ap)
|
||||
{
|
||||
g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, fmt, ap);
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
static tsize_t
|
||||
tiff_io_read (thandle_t handle,
|
||||
tdata_t buffer,
|
||||
tsize_t size)
|
||||
{
|
||||
TiffIO *io = (TiffIO *) handle;
|
||||
GError *error = NULL;
|
||||
gssize read = -1;
|
||||
gsize bytes_read = 0;
|
||||
|
||||
if (! g_input_stream_read_all (io->input,
|
||||
(void *) buffer, (gsize) size,
|
||||
&bytes_read,
|
||||
NULL, &error))
|
||||
{
|
||||
g_printerr ("%s", error->message);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
|
||||
read = bytes_read;
|
||||
|
||||
return (tsize_t) read;
|
||||
}
|
||||
|
||||
static tsize_t
|
||||
tiff_io_write (thandle_t handle,
|
||||
tdata_t buffer,
|
||||
tsize_t size)
|
||||
{
|
||||
TiffIO *io = (TiffIO *) handle;
|
||||
GError *error = NULL;
|
||||
gssize written = -1;
|
||||
gsize bytes_written = 0;
|
||||
|
||||
if (! g_output_stream_write_all (io->output,
|
||||
(void *) buffer, (gsize) size,
|
||||
&bytes_written,
|
||||
NULL, &error))
|
||||
{
|
||||
g_printerr ("%s", error->message);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
|
||||
written = bytes_written;
|
||||
|
||||
return (tsize_t) written;
|
||||
}
|
||||
|
||||
static GSeekType
|
||||
lseek_to_seek_type (int whence)
|
||||
{
|
||||
switch (whence)
|
||||
{
|
||||
default:
|
||||
case SEEK_SET:
|
||||
return G_SEEK_SET;
|
||||
case SEEK_CUR:
|
||||
return G_SEEK_CUR;
|
||||
case SEEK_END:
|
||||
return G_SEEK_END;
|
||||
}
|
||||
}
|
||||
|
||||
static toff_t
|
||||
tiff_io_seek (thandle_t handle,
|
||||
toff_t offset,
|
||||
int whence)
|
||||
{
|
||||
TiffIO *io = (TiffIO *) handle;
|
||||
GError *error = NULL;
|
||||
gboolean sought = FALSE;
|
||||
goffset position = -1;
|
||||
|
||||
sought = g_seekable_seek (G_SEEKABLE (io->stream),
|
||||
(goffset) offset, lseek_to_seek_type (whence),
|
||||
NULL, &error);
|
||||
if (sought)
|
||||
{
|
||||
position = g_seekable_tell (G_SEEKABLE (io->stream));
|
||||
}
|
||||
else
|
||||
{
|
||||
g_printerr ("%s", error->message);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
|
||||
return (toff_t) position;
|
||||
}
|
||||
|
||||
static int
|
||||
tiff_io_close (thandle_t handle)
|
||||
{
|
||||
TiffIO *io = (TiffIO *) handle;
|
||||
GError *error = NULL;
|
||||
gboolean closed = FALSE;
|
||||
|
||||
if (io->input)
|
||||
closed = g_input_stream_close (io->input, NULL, &error);
|
||||
else if (io->output)
|
||||
closed = g_output_stream_close (io->output, NULL, &error);
|
||||
|
||||
if (!closed)
|
||||
{
|
||||
g_printerr ("%s", error->message);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
|
||||
g_clear_object (&io->stream);
|
||||
io->input = NULL;
|
||||
io->output = NULL;
|
||||
|
||||
g_clear_pointer (&io->buffer, g_free);
|
||||
|
||||
io->allocated = 0;
|
||||
io->used = 0;
|
||||
io->position = 0;
|
||||
|
||||
g_free (io);
|
||||
|
||||
return closed ? 0 : -1;
|
||||
}
|
||||
|
||||
static goffset
|
||||
input_stream_query_size (GInputStream *stream)
|
||||
{
|
||||
goffset size = 0;
|
||||
|
||||
while (G_IS_FILTER_INPUT_STREAM (stream))
|
||||
stream = g_filter_input_stream_get_base_stream (G_FILTER_INPUT_STREAM (stream));
|
||||
|
||||
if (G_IS_FILE_INPUT_STREAM (stream))
|
||||
{
|
||||
GFileInfo *info;
|
||||
|
||||
info = g_file_input_stream_query_info (G_FILE_INPUT_STREAM (stream),
|
||||
G_FILE_ATTRIBUTE_STANDARD_SIZE,
|
||||
NULL,
|
||||
NULL);
|
||||
if (info)
|
||||
{
|
||||
size = g_file_info_get_size (info);
|
||||
g_object_unref (info);
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static goffset
|
||||
output_stream_query_size (GOutputStream *stream)
|
||||
{
|
||||
goffset size = 0;
|
||||
|
||||
while (G_IS_FILTER_OUTPUT_STREAM (stream))
|
||||
stream = g_filter_output_stream_get_base_stream (G_FILTER_OUTPUT_STREAM (stream));
|
||||
|
||||
if (G_IS_FILE_OUTPUT_STREAM (stream))
|
||||
{
|
||||
GFileInfo *info;
|
||||
|
||||
info = g_file_output_stream_query_info (G_FILE_OUTPUT_STREAM (stream),
|
||||
G_FILE_ATTRIBUTE_STANDARD_SIZE,
|
||||
NULL,
|
||||
NULL);
|
||||
if (info)
|
||||
{
|
||||
size = g_file_info_get_size (info);
|
||||
g_object_unref (info);
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static toff_t
|
||||
tiff_io_get_file_size (thandle_t handle)
|
||||
{
|
||||
TiffIO *io = (TiffIO *) handle;
|
||||
|
||||
if (io->input)
|
||||
return (toff_t) input_stream_query_size (io->input);
|
||||
else if (io->output)
|
||||
return (toff_t) output_stream_query_size (io->output);
|
||||
|
||||
return (toff_t) 0;
|
||||
}
|
||||
|
||||
static TIFF *
|
||||
tiff_open (gpointer stream,
|
||||
const gchar *mode,
|
||||
GError **error)
|
||||
{
|
||||
TIFFSetWarningHandler ((TIFFErrorHandler) tiff_io_warning);
|
||||
TIFFSetErrorHandler ((TIFFErrorHandler) tiff_io_error);
|
||||
TiffIO *io;
|
||||
|
||||
io = g_new0 (TiffIO, 1);
|
||||
|
||||
if (strcmp (mode, "r") == 0)
|
||||
{
|
||||
io->input = G_INPUT_STREAM (stream);
|
||||
io->stream = g_object_ref (G_OBJECT (stream));
|
||||
}
|
||||
else if (strcmp (mode, "w") == 0)
|
||||
{
|
||||
io->output = G_OUTPUT_STREAM (stream);
|
||||
io->stream = g_object_ref (G_OBJECT (stream));
|
||||
}
|
||||
else
|
||||
{
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
return TIFFClientOpen ("GTK", mode,
|
||||
(thandle_t) io,
|
||||
tiff_io_read,
|
||||
tiff_io_write,
|
||||
tiff_io_seek,
|
||||
tiff_io_close,
|
||||
tiff_io_get_file_size,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
/* {{{ Public API */
|
||||
|
||||
static struct {
|
||||
GdkMemoryFormat format;
|
||||
guint16 bits_per_sample;
|
||||
guint16 samples_per_pixel;
|
||||
guint16 sample_format;
|
||||
} format_data[] = {
|
||||
{ GDK_MEMORY_DEFAULT, 8, 4, SAMPLEFORMAT_UINT },
|
||||
{ GDK_MEMORY_R8G8B8, 8, 3, SAMPLEFORMAT_UINT },
|
||||
{ GDK_MEMORY_R16G16B16A16_PREMULTIPLIED, 16, 4, SAMPLEFORMAT_UINT },
|
||||
{ GDK_MEMORY_R16G16B16_FLOAT, 16, 3, SAMPLEFORMAT_IEEEFP },
|
||||
{ GDK_MEMORY_R32G32B32_FLOAT, 32, 3, SAMPLEFORMAT_IEEEFP },
|
||||
};
|
||||
|
||||
gboolean
|
||||
gdk_save_tiff (GOutputStream *stream,
|
||||
const guchar *data,
|
||||
int width,
|
||||
int height,
|
||||
int stride,
|
||||
GdkMemoryFormat format,
|
||||
GError **error)
|
||||
{
|
||||
TIFF *tif;
|
||||
guint16 bits_per_sample = 0;
|
||||
guint16 samples_per_pixel = 0;
|
||||
guint16 sample_format = 0;
|
||||
const guchar *line;
|
||||
|
||||
tif = tiff_open (stream, "w", error);
|
||||
if (!tif)
|
||||
return FALSE;
|
||||
|
||||
for (int i = 0; i < G_N_ELEMENTS (format_data); i++)
|
||||
{
|
||||
if (format == format_data[i].format)
|
||||
{
|
||||
bits_per_sample = format_data[i].bits_per_sample;
|
||||
samples_per_pixel = format_data[i].samples_per_pixel;
|
||||
sample_format = format_data[i].sample_format;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_assert (bits_per_sample != 0);
|
||||
|
||||
TIFFSetField (tif, TIFFTAG_SOFTWARE, "GTK");
|
||||
TIFFSetField (tif, TIFFTAG_IMAGEWIDTH, width);
|
||||
TIFFSetField (tif, TIFFTAG_IMAGELENGTH, height);
|
||||
TIFFSetField (tif, TIFFTAG_BITSPERSAMPLE, bits_per_sample);
|
||||
TIFFSetField (tif, TIFFTAG_SAMPLESPERPIXEL, samples_per_pixel);
|
||||
TIFFSetField (tif, TIFFTAG_SAMPLEFORMAT, sample_format);
|
||||
TIFFSetField (tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
|
||||
TIFFSetField (tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
|
||||
// TODO: save gamma / colorspace
|
||||
|
||||
if (samples_per_pixel > 3)
|
||||
{
|
||||
guint16 extra_samples[] = { EXTRASAMPLE_ASSOCALPHA };
|
||||
TIFFSetField (tif, TIFFTAG_EXTRASAMPLES, 1, extra_samples);
|
||||
}
|
||||
|
||||
TIFFSetField (tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
|
||||
TIFFSetField (tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
|
||||
|
||||
line = (const guchar *)data;
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
if (TIFFWriteScanline (tif, (void *)line, y, 0) == -1)
|
||||
{
|
||||
g_set_error (error,
|
||||
G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Writing data failed at row %d", y);
|
||||
TIFFClose (tif);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
line += stride;
|
||||
}
|
||||
|
||||
TIFFFlushData (tif);
|
||||
TIFFClose (tif);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GdkTexture *
|
||||
load_fallback (TIFF *tif,
|
||||
GError **error)
|
||||
{
|
||||
int width, height;
|
||||
guchar *pixels;
|
||||
GBytes *bytes;
|
||||
GdkTexture *texture;
|
||||
|
||||
TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &width);
|
||||
TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &height);
|
||||
|
||||
pixels = g_malloc (width * height * 4);
|
||||
|
||||
if (!TIFFReadRGBAImageOriented (tif, width, height, (uint32 *)pixels, ORIENTATION_TOPLEFT, 1))
|
||||
{
|
||||
g_set_error_literal (error,
|
||||
G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Failed to load RGB data from TIFF file");
|
||||
g_free (pixels);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bytes = g_bytes_new_take (pixels, width * height * 4);
|
||||
|
||||
texture = gdk_memory_texture_new (width, height,
|
||||
GDK_MEMORY_R8G8B8A8_PREMULTIPLIED,
|
||||
bytes,
|
||||
width * 4);
|
||||
|
||||
g_bytes_unref (bytes);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
/* This isn't meant to be a very versatile tiff loader.
|
||||
* It just aims to load the subset that we're saving
|
||||
* ourselves, above.
|
||||
*/
|
||||
GdkTexture *
|
||||
gdk_load_tiff (GInputStream *stream,
|
||||
GError **error)
|
||||
{
|
||||
TIFF *tif;
|
||||
guint16 samples_per_pixel;
|
||||
guint16 bits_per_sample;
|
||||
guint16 photometric;
|
||||
guint16 planarconfig;
|
||||
guint16 sample_format;
|
||||
guint16 orientation;
|
||||
guint32 width, height;
|
||||
GdkMemoryFormat format;
|
||||
guchar *data, *line;
|
||||
gsize stride;
|
||||
int bpp;
|
||||
GBytes *bytes;
|
||||
GdkTexture *texture;
|
||||
|
||||
tif = tiff_open (stream, "r", error);
|
||||
if (!tif)
|
||||
return NULL;
|
||||
|
||||
TIFFSetDirectory (tif, 0);
|
||||
|
||||
TIFFGetFieldDefaulted (tif, TIFFTAG_SAMPLESPERPIXEL, &samples_per_pixel);
|
||||
TIFFGetFieldDefaulted (tif, TIFFTAG_BITSPERSAMPLE, &bits_per_sample);
|
||||
TIFFGetFieldDefaulted (tif, TIFFTAG_SAMPLEFORMAT, &sample_format);
|
||||
TIFFGetFieldDefaulted (tif, TIFFTAG_PHOTOMETRIC, &photometric);
|
||||
TIFFGetFieldDefaulted (tif, TIFFTAG_PLANARCONFIG, &planarconfig);
|
||||
TIFFGetFieldDefaulted (tif, TIFFTAG_ORIENTATION, &orientation);
|
||||
TIFFGetFieldDefaulted (tif, TIFFTAG_IMAGEWIDTH, &width);
|
||||
TIFFGetFieldDefaulted (tif, TIFFTAG_IMAGELENGTH, &height);
|
||||
|
||||
if (samples_per_pixel == 4)
|
||||
{
|
||||
guint16 extra;
|
||||
guint16 *extra_types;
|
||||
|
||||
if (!TIFFGetField (tif, TIFFTAG_EXTRASAMPLES, &extra, &extra_types))
|
||||
extra = 0;
|
||||
|
||||
if (extra == 0 || extra_types[0] != EXTRASAMPLE_ASSOCALPHA)
|
||||
{
|
||||
texture = load_fallback (tif, error);
|
||||
TIFFClose (tif);
|
||||
return texture;
|
||||
}
|
||||
}
|
||||
|
||||
format = 0;
|
||||
|
||||
for (int i = 0; i < G_N_ELEMENTS (format_data); i++)
|
||||
{
|
||||
if (format_data[i].sample_format == sample_format &&
|
||||
format_data[i].bits_per_sample == bits_per_sample &&
|
||||
format_data[i].samples_per_pixel == samples_per_pixel)
|
||||
{
|
||||
format = format_data[i].format;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (format == 0 ||
|
||||
photometric != PHOTOMETRIC_RGB ||
|
||||
planarconfig != PLANARCONFIG_CONTIG ||
|
||||
TIFFIsTiled (tif) ||
|
||||
orientation != ORIENTATION_TOPLEFT)
|
||||
{
|
||||
texture = load_fallback (tif, error);
|
||||
TIFFClose (tif);
|
||||
return texture;
|
||||
}
|
||||
|
||||
stride = width * gdk_memory_format_bytes_per_pixel (format);
|
||||
|
||||
g_assert (TIFFScanlineSize (tif) == stride);
|
||||
|
||||
data = g_new (guchar, height * stride);
|
||||
|
||||
line = data;
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
if (TIFFReadScanline (tif, line, y, 0) == -1)
|
||||
{
|
||||
g_set_error (error,
|
||||
G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Reading data failed at row %d", y);
|
||||
TIFFClose (tif);
|
||||
g_free (data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
line += stride;
|
||||
}
|
||||
|
||||
bpp = gdk_memory_format_bytes_per_pixel (format);
|
||||
bytes = g_bytes_new_take (data, width * height * bpp);
|
||||
|
||||
texture = gdk_memory_texture_new (width, height,
|
||||
format,
|
||||
bytes, width * bpp);
|
||||
g_bytes_unref (bytes);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
/* {{{ Async code */
|
||||
|
||||
static void
|
||||
load_tiff_in_thread (GTask *task,
|
||||
gpointer source_object,
|
||||
gpointer task_data,
|
||||
GCancellable *cancellable)
|
||||
{
|
||||
GInputStream *stream = source_object;
|
||||
GdkTexture *texture;
|
||||
GError *error = NULL;
|
||||
|
||||
texture = gdk_load_tiff (stream, &error);
|
||||
|
||||
if (texture)
|
||||
g_task_return_pointer (task, texture, g_object_unref);
|
||||
else
|
||||
g_task_return_error (task, error);
|
||||
}
|
||||
|
||||
void
|
||||
gdk_load_tiff_async (GInputStream *stream,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GTask *task;
|
||||
|
||||
task = g_task_new (stream, cancellable, callback, user_data);
|
||||
g_task_run_in_thread (task, load_tiff_in_thread);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
GdkTexture *
|
||||
gdk_load_tiff_finish (GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
return g_task_propagate_pointer (G_TASK (result), error);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const guchar *data;
|
||||
int width;
|
||||
int height;
|
||||
int stride;
|
||||
GdkMemoryFormat format;
|
||||
} SaveTiffData;
|
||||
|
||||
static void
|
||||
save_tiff_in_thread (GTask *task,
|
||||
gpointer source_object,
|
||||
gpointer task_data,
|
||||
GCancellable *cancellable)
|
||||
{
|
||||
GOutputStream *stream = source_object;
|
||||
SaveTiffData *data = task_data;
|
||||
GError *error = NULL;
|
||||
gboolean result;
|
||||
|
||||
result = gdk_save_tiff (stream,
|
||||
data->data,
|
||||
data->width,
|
||||
data->height,
|
||||
data->stride,
|
||||
data->format,
|
||||
&error);
|
||||
|
||||
if (result)
|
||||
g_task_return_boolean (task, result);
|
||||
else
|
||||
g_task_return_error (task, error);
|
||||
}
|
||||
|
||||
void
|
||||
gdk_save_tiff_async (GOutputStream *stream,
|
||||
const guchar *data,
|
||||
int width,
|
||||
int height,
|
||||
int stride,
|
||||
GdkMemoryFormat format,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GTask *task;
|
||||
SaveTiffData *save_data;
|
||||
|
||||
save_data = g_new0 (SaveTiffData, 1);
|
||||
save_data->data = data;
|
||||
save_data->width = width;
|
||||
save_data->height = height;
|
||||
save_data->stride = stride;
|
||||
save_data->format = format;
|
||||
|
||||
task = g_task_new (stream, cancellable, callback, user_data);
|
||||
g_task_set_task_data (task, save_data, g_free);
|
||||
g_task_run_in_thread (task, save_tiff_in_thread);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gdk_save_tiff_finish (GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
return g_task_propagate_boolean (G_TASK (result), error);
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
|
||||
/* vim:set foldmethod=marker expandtab: */
|
||||
@@ -0,0 +1,56 @@
|
||||
/* GDK - The GIMP Drawing Kit
|
||||
* Copyright (C) 2021 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __GDK_TIFF_H__
|
||||
#define __GDK_TIFF_H__
|
||||
|
||||
#include "gdkmemorytexture.h"
|
||||
#include <gio/gio.h>
|
||||
|
||||
GdkTexture *gdk_load_tiff (GInputStream *stream,
|
||||
GError **error);
|
||||
|
||||
void gdk_load_tiff_async (GInputStream *stream,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
GdkTexture *gdk_load_tiff_finish (GAsyncResult *result,
|
||||
GError **error);
|
||||
|
||||
gboolean gdk_save_tiff (GOutputStream *stream,
|
||||
const guchar *data,
|
||||
int width,
|
||||
int height,
|
||||
int stride,
|
||||
GdkMemoryFormat format,
|
||||
GError **error);
|
||||
|
||||
void gdk_save_tiff_async (GOutputStream *stream,
|
||||
const guchar *data,
|
||||
int width,
|
||||
int height,
|
||||
int stride,
|
||||
GdkMemoryFormat format,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
gboolean gdk_save_tiff_finish (GAsyncResult *result,
|
||||
GError **error);
|
||||
|
||||
#endif
|
||||
@@ -94,6 +94,14 @@
|
||||
*/
|
||||
#define GDK_VERSION_4_4 (G_ENCODE_VERSION (4, 4))
|
||||
|
||||
/**
|
||||
* GDK_VERSION_4_6:
|
||||
*
|
||||
* A macro that evaluates to the 4.6 version of GDK, in a format
|
||||
* that can be used by the C pre-processor.
|
||||
*/
|
||||
#define GDK_VERSION_4_6 (G_ENCODE_VERSION (4, 6))
|
||||
|
||||
|
||||
/* evaluates to the current stable version; for development cycles,
|
||||
* this means the next stable target, with a hard backstop to the
|
||||
@@ -221,4 +229,18 @@
|
||||
# define GDK_DEPRECATED_IN_4_4_FOR(f) _GDK_EXTERN
|
||||
#endif
|
||||
|
||||
#if GDK_VERSION_MAX_ALLOWED < GDK_VERSION_4_6
|
||||
# define GDK_AVAILABLE_IN_4_6 GDK_UNAVAILABLE(4, 6)
|
||||
#else
|
||||
# define GDK_AVAILABLE_IN_4_6 _GDK_EXTERN
|
||||
#endif
|
||||
|
||||
#if GDK_VERSION_MIN_REQUIRED >= GDK_VERSION_4_6
|
||||
# define GDK_DEPRECATED_IN_4_6 GDK_DEPRECATED
|
||||
# define GDK_DEPRECATED_IN_4_6_FOR(f) GDK_DEPRECATED_FOR(f)
|
||||
#else
|
||||
# define GDK_DEPRECATED_IN_4_6 _GDK_EXTERN
|
||||
# define GDK_DEPRECATED_IN_4_6_FOR(f) _GDK_EXTERN
|
||||
#endif
|
||||
|
||||
#endif /* __GDK_VERSION_MACROS_H__ */
|
||||
|
||||
+39
-39
@@ -117,104 +117,104 @@ gdk_vulkan_strerror (VkResult result)
|
||||
switch (result)
|
||||
{
|
||||
case VK_SUCCESS:
|
||||
return "Command successfully completed.";
|
||||
return "Command successfully completed. (VK_SUCCESS)";
|
||||
case VK_NOT_READY:
|
||||
return "A fence or query has not yet completed.";
|
||||
return "A fence or query has not yet completed. (VK_NOT_READY)";
|
||||
case VK_TIMEOUT:
|
||||
return "A wait operation has not completed in the specified time.";
|
||||
return "A wait operation has not completed in the specified time. (VK_TIMEOUT)";
|
||||
case VK_EVENT_SET:
|
||||
return "An event is signaled.";
|
||||
return "An event is signaled. (VK_EVENT_SET)";
|
||||
case VK_EVENT_RESET:
|
||||
return "An event is unsignaled.";
|
||||
return "An event is unsignaled. (VK_EVENT_RESET)";
|
||||
case VK_INCOMPLETE:
|
||||
return "A return array was too small for the result.";
|
||||
return "A return array was too small for the result. (VK_INCOMPLETE)";
|
||||
case VK_SUBOPTIMAL_KHR:
|
||||
return "A swapchain no longer matches the surface properties exactly, but can still be used to present to the surface successfully.";
|
||||
return "A swapchain no longer matches the surface properties exactly, but can still be used to present to the surface successfully. (VK_SUBOPTIMAL_KHR)";
|
||||
case VK_ERROR_OUT_OF_HOST_MEMORY:
|
||||
return "A host memory allocation has failed.";
|
||||
return "A host memory allocation has failed. (VK_ERROR_OUT_OF_HOST_MEMORY)";
|
||||
case VK_ERROR_OUT_OF_DEVICE_MEMORY:
|
||||
return "A device memory allocation has failed.";
|
||||
return "A device memory allocation has failed. (VK_ERROR_OUT_OF_DEVICE_MEMORY)";
|
||||
case VK_ERROR_INITIALIZATION_FAILED:
|
||||
return "Initialization of an object could not be completed for implementation-specific reasons.";
|
||||
return "Initialization of an object could not be completed for implementation-specific reasons. (VK_ERROR_INITIALIZATION_FAILED)";
|
||||
case VK_ERROR_DEVICE_LOST:
|
||||
return "The logical or physical device has been lost.";
|
||||
return "The logical or physical device has been lost. (VK_ERROR_DEVICE_LOST)";
|
||||
case VK_ERROR_MEMORY_MAP_FAILED:
|
||||
return "Mapping of a memory object has failed.";
|
||||
return "Mapping of a memory object has failed. (VK_ERROR_MEMORY_MAP_FAILED)";
|
||||
case VK_ERROR_LAYER_NOT_PRESENT:
|
||||
return "A requested layer is not present or could not be loaded.";
|
||||
return "A requested layer is not present or could not be loaded. (VK_ERROR_LAYER_NOT_PRESENT)";
|
||||
case VK_ERROR_EXTENSION_NOT_PRESENT:
|
||||
return "A requested extension is not supported.";
|
||||
return "A requested extension is not supported. (VK_ERROR_EXTENSION_NOT_PRESENT)";
|
||||
case VK_ERROR_FEATURE_NOT_PRESENT:
|
||||
return "A requested feature is not supported.";
|
||||
return "A requested feature is not supported. (VK_ERROR_FEATURE_NOT_PRESENT)";
|
||||
case VK_ERROR_INCOMPATIBLE_DRIVER:
|
||||
return "The requested version of Vulkan is not supported by the driver or is otherwise incompatible for implementation-specific reasons.";
|
||||
return "The requested version of Vulkan is not supported by the driver or is otherwise incompatible for implementation-specific reasons. (VK_ERROR_INCOMPATIBLE_DRIVER)";
|
||||
case VK_ERROR_TOO_MANY_OBJECTS:
|
||||
return "Too many objects of the type have already been created.";
|
||||
return "Too many objects of the type have already been created. (VK_ERROR_TOO_MANY_OBJECTS)";
|
||||
case VK_ERROR_FORMAT_NOT_SUPPORTED:
|
||||
return "A requested format is not supported on this device.";
|
||||
return "A requested format is not supported on this device. (VK_ERROR_FORMAT_NOT_SUPPORTED)";
|
||||
#if VK_HEADER_VERSION >= 24
|
||||
case VK_ERROR_FRAGMENTED_POOL:
|
||||
return "A requested pool allocation has failed due to fragmentation of the pool’s memory.";
|
||||
return "A requested pool allocation has failed due to fragmentation of the pool’s memory. (VK_ERROR_FRAGMENTED_POOL)";
|
||||
#endif
|
||||
case VK_ERROR_SURFACE_LOST_KHR:
|
||||
return "A surface is no longer available.";
|
||||
return "A surface is no longer available. (VK_ERROR_SURFACE_LOST_KHR)";
|
||||
case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR:
|
||||
return "The requested window is already in use by Vulkan or another API in a manner which prevents it from being used again.";
|
||||
return "The requested window is already in use by Vulkan or another API in a manner which prevents it from being used again. (VK_ERROR_NATIVE_WINDOW_IN_USE_KHR)";
|
||||
case VK_ERROR_OUT_OF_DATE_KHR:
|
||||
return "A surface has changed in such a way that it is no longer compatible with the swapchain.";
|
||||
return "A surface has changed in such a way that it is no longer compatible with the swapchain. (VK_ERROR_OUT_OF_DATE_KHR)";
|
||||
case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR:
|
||||
return "The display used by a swapchain does not use the same presentable image layout, or is incompatible in a way that prevents sharing an image.";
|
||||
return "The display used by a swapchain does not use the same presentable image layout, or is incompatible in a way that prevents sharing an image. (VK_ERROR_INCOMPATIBLE_DISPLAY_KHR)";
|
||||
case VK_ERROR_VALIDATION_FAILED_EXT:
|
||||
return "The application caused the validation layer to fail.";
|
||||
return "The application caused the validation layer to fail. (VK_ERROR_VALIDATION_FAILED_EXT)";
|
||||
case VK_ERROR_INVALID_SHADER_NV:
|
||||
return "One or more shaders failed to compile or link.";
|
||||
return "One or more shaders failed to compile or link. (VK_ERROR_INVALID_SHADER_NV)";
|
||||
#if VK_HEADER_VERSION >= 39
|
||||
case VK_ERROR_OUT_OF_POOL_MEMORY_KHR:
|
||||
return "A pool memory allocation has failed.";
|
||||
return "A pool memory allocation has failed. (VK_ERROR_OUT_OF_POOL_MEMORY_KHR)";
|
||||
#endif
|
||||
#if VK_HEADER_VERSION >= 54
|
||||
case VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR:
|
||||
return "An external handle is not a valid handle of the specified type.";
|
||||
return "An external handle is not a valid handle of the specified type. (VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR)";
|
||||
#endif
|
||||
#if VK_HEADER_VERSION >= 64
|
||||
case VK_ERROR_NOT_PERMITTED_EXT:
|
||||
return "The caller does not have sufficient privileges.";
|
||||
return "The caller does not have sufficient privileges. (VK_ERROR_NOT_PERMITTED_EXT)";
|
||||
#endif
|
||||
#if VK_HEADER_VERSION >= 72
|
||||
case VK_ERROR_FRAGMENTATION_EXT:
|
||||
return "A descriptor pool creation has failed due to fragmentation";
|
||||
return "A descriptor pool creation has failed due to fragmentation. (VK_ERROR_FRAGMENTATION_EXT)";
|
||||
#endif
|
||||
#if VK_HEADER_VERSION >= 89
|
||||
case VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT:
|
||||
return "Invalid DRM format modifier plane layout";
|
||||
return "Invalid DRM format modifier plane layout (VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT)";
|
||||
#endif
|
||||
#if VK_HEADER_VERSION >= 97
|
||||
case VK_ERROR_INVALID_DEVICE_ADDRESS_EXT:
|
||||
return "Invalid device address";
|
||||
return "Invalid device address (VK_ERROR_INVALID_DEVICE_ADDRESS_EXT)";
|
||||
#endif
|
||||
#if VK_HEADER_VERSION >= 105
|
||||
case VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT:
|
||||
return "An operation on a swapchain created with VK_FULL_SCREEN_EXCLUSIVE_APPLICATION_CONTROLLED_EXT failed as it did not have exclusive full-screen access.";
|
||||
return "An operation on a swapchain created with VK_FULL_SCREEN_EXCLUSIVE_APPLICATION_CONTROLLED_EXT failed as it did not have exclusive full-screen access. (VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT)";
|
||||
#endif
|
||||
#if VK_HEADER_VERSION >= 131
|
||||
case VK_ERROR_UNKNOWN:
|
||||
return "An unknown error has occurred; either the application has provided invalid input, or an implementation failure has occurred.";
|
||||
return "An unknown error has occurred; either the application has provided invalid input, or an implementation failure has occurred. (VK_ERROR_UNKNOWN)";
|
||||
#endif
|
||||
#if VK_HEADER_VERSION >= 135
|
||||
#if VK_HEADER_VERSION < 162
|
||||
case VK_ERROR_INCOMPATIBLE_VERSION_KHR:
|
||||
return "This error was removed by the Vulkan gods.";
|
||||
return "This error was removed by the Vulkan gods. (VK_ERROR_INCOMPATIBLE_VERSION_KHR)";
|
||||
#endif
|
||||
case VK_THREAD_IDLE_KHR:
|
||||
return "A deferred operation is not complete but there is currently no work for this thread to do at the time of this call.";
|
||||
return "A deferred operation is not complete but there is currently no work for this thread to do at the time of this call. (VK_THREAD_IDLE_KHR)";
|
||||
case VK_THREAD_DONE_KHR:
|
||||
return "A deferred operation is not complete but there is no work remaining to assign to additional threads.";
|
||||
return "A deferred operation is not complete but there is no work remaining to assign to additional threads. (VK_THREAD_DONE_KHR)";
|
||||
case VK_OPERATION_DEFERRED_KHR:
|
||||
return "A deferred operation was requested and at least some of the work was deferred.";
|
||||
return "A deferred operation was requested and at least some of the work was deferred. (VK_OPERATION_DEFERRED_KHR)";
|
||||
case VK_OPERATION_NOT_DEFERRED_KHR:
|
||||
return "A deferred operation was requested and no operations were deferred.";
|
||||
return "A deferred operation was requested and no operations were deferred. (VK_OPERATION_NOT_DEFERRED_KHR)";
|
||||
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.";
|
||||
return "A requested pipeline creation would have required compilation, but the application requested compilation to not be performed. (VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT)";
|
||||
#endif
|
||||
#if VK_HEADER_VERSION < 140
|
||||
case VK_RESULT_RANGE_SIZE:
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
#include "gdkmacoseventsource-private.h"
|
||||
#include "gdkmacosdisplay-private.h"
|
||||
|
||||
#include "gdk-private.h"
|
||||
|
||||
/*
|
||||
* This file implementations integration between the GLib main loop and
|
||||
* the native system of the Core Foundation run loop and Cocoa event
|
||||
|
||||
+5
-2
@@ -50,6 +50,9 @@ gdk_public_sources = files([
|
||||
'gdktoplevelsize.c',
|
||||
'gdktoplevel.c',
|
||||
'gdkdragsurface.c',
|
||||
'gdkpng.c',
|
||||
'gdktiff.c',
|
||||
'gdkjpeg.c',
|
||||
])
|
||||
|
||||
gdk_public_headers = files([
|
||||
@@ -254,8 +257,8 @@ endif
|
||||
|
||||
libgdk = static_library('gdk',
|
||||
sources: [gdk_sources, gdk_backends_gen_headers, gdkconfig],
|
||||
dependencies: gdk_deps + [libgtk_css_dep],
|
||||
link_with: [libgtk_css, ],
|
||||
dependencies: gdk_deps + [libgtk_css_dep, png_dep, tiff_dep, jpeg_dep],
|
||||
link_with: [libgtk_css],
|
||||
include_directories: [confinc, gdkx11_inc, wlinc],
|
||||
c_args: libgdk_c_args + common_cflags,
|
||||
link_whole: gdk_backends,
|
||||
|
||||
@@ -2634,6 +2634,23 @@ touch_handle_cancel (void *data,
|
||||
GDK_SEAT_NOTE (seat, EVENTS, g_message ("touch cancel"));
|
||||
}
|
||||
|
||||
static void
|
||||
touch_handle_shape (void *data,
|
||||
struct wl_touch *touch,
|
||||
int32_t id,
|
||||
wl_fixed_t major,
|
||||
wl_fixed_t minor)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
touch_handle_orientation (void *data,
|
||||
struct wl_touch *touch,
|
||||
int32_t id,
|
||||
wl_fixed_t orientation)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
emit_gesture_swipe_event (GdkWaylandSeat *seat,
|
||||
GdkTouchpadGesturePhase phase,
|
||||
@@ -3021,7 +3038,9 @@ static const struct wl_touch_listener touch_listener = {
|
||||
touch_handle_up,
|
||||
touch_handle_motion,
|
||||
touch_handle_frame,
|
||||
touch_handle_cancel
|
||||
touch_handle_cancel,
|
||||
touch_handle_shape,
|
||||
touch_handle_orientation,
|
||||
};
|
||||
|
||||
static const struct zwp_pointer_gesture_swipe_v1_listener gesture_swipe_listener = {
|
||||
|
||||
@@ -240,7 +240,7 @@ _gdk_wayland_display_add_seat (GdkWaylandDisplay *display_wayland,
|
||||
{
|
||||
struct wl_seat *seat;
|
||||
|
||||
display_wayland->seat_version = MIN (version, 5);
|
||||
display_wayland->seat_version = MIN (version, 7);
|
||||
seat = wl_registry_bind (display_wayland->wl_registry,
|
||||
id, &wl_seat_interface,
|
||||
display_wayland->seat_version);
|
||||
@@ -2117,7 +2117,7 @@ gdk_wayland_display_get_setting (GdkDisplay *display,
|
||||
{
|
||||
TranslationEntry *entry;
|
||||
|
||||
if (GDK_DISPLAY_DEBUG_CHECK (display, DEFAULT_SETTINGS))
|
||||
if (gdk_display_get_debug_flags (display) & GDK_DEBUG_DEFAULT_SETTINGS)
|
||||
return FALSE;
|
||||
|
||||
if (GDK_WAYLAND_DISPLAY (display)->settings != NULL &&
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "gdkprivate-wayland.h"
|
||||
|
||||
#include "gdkinternals.h"
|
||||
#include "gdk-private.h"
|
||||
#include "gdksurfaceprivate.h"
|
||||
#include "gdkprofilerprivate.h"
|
||||
|
||||
@@ -474,14 +475,28 @@ gdk_wayland_display_init_gl (GdkDisplay *display,
|
||||
G_GNUC_UNUSED gint64 start_time = GDK_PROFILER_CURRENT_TIME;
|
||||
G_GNUC_UNUSED gint64 start_time2;
|
||||
|
||||
if (!epoxy_has_egl ())
|
||||
{
|
||||
gboolean sandboxed = gdk_running_in_sandbox ();
|
||||
|
||||
g_set_error_literal (error, GDK_GL_ERROR,
|
||||
GDK_GL_ERROR_NOT_AVAILABLE,
|
||||
sandboxed ? _("libEGL not available in this sandbox")
|
||||
: _("libEGL not available"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
start_time2 = GDK_PROFILER_CURRENT_TIME;
|
||||
dpy = get_egl_display (display_wayland);
|
||||
gdk_profiler_end_mark (start_time, "get_egl_display", NULL);
|
||||
if (dpy == NULL)
|
||||
{
|
||||
gboolean sandboxed = gdk_running_in_sandbox ();
|
||||
|
||||
g_set_error_literal (error, GDK_GL_ERROR,
|
||||
GDK_GL_ERROR_NOT_AVAILABLE,
|
||||
_("Failed to create EGL display"));
|
||||
sandboxed ? _("Sandbox does not provide an OpenGL implementation")
|
||||
: _("No OpenGL implementation available"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -542,7 +542,7 @@ _gdk_wayland_keymap_update_from_fd (GdkKeymap *keymap,
|
||||
|
||||
context = xkb_context_new (0);
|
||||
|
||||
map_str = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0);
|
||||
map_str = mmap (NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
if (map_str == MAP_FAILED)
|
||||
{
|
||||
close(fd);
|
||||
|
||||
@@ -89,7 +89,7 @@ gdk_device_win32_query_state (GdkDevice *device,
|
||||
hwnd = NULL;
|
||||
}
|
||||
|
||||
GetCursorPos (&point);
|
||||
_gdk_win32_get_cursor_pos (&point);
|
||||
|
||||
if (hwnd)
|
||||
ScreenToClient (hwnd, &point);
|
||||
@@ -177,7 +177,7 @@ _gdk_device_win32_surface_at_position (GdkDevice *device,
|
||||
HWND hwnd;
|
||||
RECT rect;
|
||||
|
||||
if (!GetCursorPos (&screen_pt))
|
||||
if (!_gdk_win32_get_cursor_pos (&screen_pt))
|
||||
return NULL;
|
||||
|
||||
/* Use WindowFromPoint instead of ChildWindowFromPoint(Ex).
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
/* GDK - The GIMP Drawing Kit
|
||||
* Copyright (C) 2020 the GTK team
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <gdk/gdksurface.h>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "gdkwin32.h"
|
||||
#include "gdkdevice-winpointer.h"
|
||||
#include "gdkdisplay-win32.h"
|
||||
|
||||
G_DEFINE_TYPE (GdkDeviceWinpointer, gdk_device_winpointer, GDK_TYPE_DEVICE)
|
||||
|
||||
static GdkModifierType
|
||||
get_keyboard_mask (void)
|
||||
{
|
||||
GdkModifierType mask = 0;
|
||||
BYTE kbd[256];
|
||||
|
||||
GetKeyboardState (kbd);
|
||||
if (kbd[VK_SHIFT] & 0x80)
|
||||
mask |= GDK_SHIFT_MASK;
|
||||
if (kbd[VK_CAPITAL] & 0x80)
|
||||
mask |= GDK_LOCK_MASK;
|
||||
if (kbd[VK_CONTROL] & 0x80)
|
||||
mask |= GDK_CONTROL_MASK;
|
||||
if (kbd[VK_MENU] & 0x80)
|
||||
mask |= GDK_ALT_MASK;
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_device_winpointer_set_surface_cursor (GdkDevice *device,
|
||||
GdkSurface *window,
|
||||
GdkCursor *cursor)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
gdk_device_winpointer_query_state (GdkDevice *device,
|
||||
GdkSurface *window,
|
||||
GdkSurface **child_window,
|
||||
double *win_x,
|
||||
double *win_y,
|
||||
GdkModifierType *mask)
|
||||
{
|
||||
GdkDeviceWinpointer *device_winpointer;
|
||||
POINT point;
|
||||
HWND hwnd, hwndc;
|
||||
int scale;
|
||||
|
||||
device_winpointer = GDK_DEVICE_WINPOINTER (device);
|
||||
if (window)
|
||||
{
|
||||
scale = GDK_WIN32_SURFACE (window)->surface_scale;
|
||||
hwnd = GDK_SURFACE_HWND (window);
|
||||
}
|
||||
else
|
||||
{
|
||||
GdkDisplay *display = gdk_device_get_display (device);
|
||||
|
||||
scale = GDK_WIN32_DISPLAY (display)->surface_scale;
|
||||
hwnd = NULL;
|
||||
}
|
||||
|
||||
_gdk_win32_get_cursor_pos (&point);
|
||||
|
||||
if (hwnd)
|
||||
ScreenToClient (hwnd, &point);
|
||||
|
||||
if (win_x)
|
||||
*win_x = point.x / scale;
|
||||
|
||||
if (win_y)
|
||||
*win_y = point.y / scale;
|
||||
|
||||
if (!window)
|
||||
{
|
||||
if (win_x)
|
||||
*win_x += _gdk_offset_x;
|
||||
|
||||
if (win_y)
|
||||
*win_y += _gdk_offset_y;
|
||||
}
|
||||
|
||||
if (hwnd && child_window)
|
||||
{
|
||||
hwndc = ChildWindowFromPoint (hwnd, point);
|
||||
|
||||
if (hwndc && hwndc != hwnd)
|
||||
*child_window = gdk_win32_handle_table_lookup (hwndc);
|
||||
else
|
||||
*child_window = NULL; /* Direct child unknown to gdk */
|
||||
}
|
||||
|
||||
if (mask)
|
||||
{
|
||||
*mask = get_keyboard_mask ();
|
||||
*mask |= device_winpointer->last_button_mask;
|
||||
}
|
||||
}
|
||||
|
||||
static GdkGrabStatus
|
||||
gdk_device_winpointer_grab (GdkDevice *device,
|
||||
GdkSurface *window,
|
||||
gboolean owner_events,
|
||||
GdkEventMask event_mask,
|
||||
GdkSurface *confine_to,
|
||||
GdkCursor *cursor,
|
||||
guint32 time_)
|
||||
{
|
||||
return GDK_GRAB_SUCCESS;
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_device_winpointer_ungrab (GdkDevice *device,
|
||||
guint32 time_)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
screen_to_client (HWND hwnd, POINT screen_pt, POINT *client_pt)
|
||||
{
|
||||
*client_pt = screen_pt;
|
||||
ScreenToClient (hwnd, client_pt);
|
||||
}
|
||||
|
||||
static GdkSurface *
|
||||
gdk_device_winpointer_surface_at_position (GdkDevice *device,
|
||||
double *win_x,
|
||||
double *win_y,
|
||||
GdkModifierType *mask)
|
||||
{
|
||||
GdkSurface *surface = NULL;
|
||||
GdkWin32Surface *impl = NULL;
|
||||
POINT screen_pt, client_pt;
|
||||
HWND hwnd;
|
||||
RECT rect;
|
||||
|
||||
if (!_gdk_win32_get_cursor_pos (&screen_pt))
|
||||
return NULL;
|
||||
|
||||
/* Use WindowFromPoint instead of ChildWindowFromPoint(Ex).
|
||||
* Only WindowFromPoint is able to look through transparent
|
||||
* layered windows.
|
||||
*/
|
||||
hwnd = GetAncestor (WindowFromPoint (screen_pt), GA_ROOT);
|
||||
|
||||
/* Verify that we're really inside the client area of the surface */
|
||||
GetClientRect (hwnd, &rect);
|
||||
screen_to_client (hwnd, screen_pt, &client_pt);
|
||||
if (!PtInRect (&rect, client_pt))
|
||||
hwnd = NULL;
|
||||
|
||||
surface = gdk_win32_handle_table_lookup (hwnd);
|
||||
|
||||
if (surface && (win_x || win_y))
|
||||
{
|
||||
impl = GDK_WIN32_SURFACE (surface);
|
||||
|
||||
if (win_x)
|
||||
*win_x = client_pt.x / impl->surface_scale;
|
||||
if (win_y)
|
||||
*win_y = client_pt.y / impl->surface_scale;
|
||||
}
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_device_winpointer_init (GdkDeviceWinpointer *device_winpointer)
|
||||
{
|
||||
device_winpointer->device_handle = NULL;
|
||||
device_winpointer->start_cursor_id = 0;
|
||||
device_winpointer->end_cursor_id = 0;
|
||||
|
||||
device_winpointer->origin_x = 0;
|
||||
device_winpointer->origin_y = 0;
|
||||
device_winpointer->scale_x = 0.0;
|
||||
device_winpointer->scale_y = 0.0;
|
||||
|
||||
device_winpointer->last_button_mask = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_device_winpointer_finalize (GObject *object)
|
||||
{
|
||||
GdkDeviceWinpointer *device_winpointer = GDK_DEVICE_WINPOINTER (object);
|
||||
|
||||
g_clear_object (&device_winpointer->tool_pen);
|
||||
g_clear_object (&device_winpointer->tool_eraser);
|
||||
|
||||
G_OBJECT_CLASS (gdk_device_winpointer_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_device_winpointer_class_init (GdkDeviceWinpointerClass *klass)
|
||||
{
|
||||
GdkDeviceClass *device_class = GDK_DEVICE_CLASS (klass);
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->finalize = gdk_device_winpointer_finalize;
|
||||
device_class->set_surface_cursor = gdk_device_winpointer_set_surface_cursor;
|
||||
device_class->grab = gdk_device_winpointer_grab;
|
||||
device_class->ungrab = gdk_device_winpointer_ungrab;
|
||||
device_class->surface_at_position = gdk_device_winpointer_surface_at_position;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/* GDK - The GIMP Drawing Kit
|
||||
* Copyright (C) 2020 the GTK team
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __GDK_DEVICE_WINPOINTER_H__
|
||||
#define __GDK_DEVICE_WINPOINTER_H__
|
||||
|
||||
#include <gdk/gdkdeviceprivate.h>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "winpointer.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GDK_TYPE_DEVICE_WINPOINTER (gdk_device_winpointer_get_type ())
|
||||
#define GDK_DEVICE_WINPOINTER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDK_TYPE_DEVICE_WINPOINTER, GdkDeviceWinpointer))
|
||||
#define GDK_DEVICE_WINPOINTER_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), GDK_TYPE_DEVICE_WINPOINTER, GdkDeviceWinpointerClass))
|
||||
#define GDK_IS_DEVICE_WINPOINTER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDK_TYPE_DEVICE_WINPOINTER))
|
||||
#define GDK_IS_DEVICE_WINPOINTER_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), GDK_TYPE_DEVICE_WINPOINTER))
|
||||
#define GDK_DEVICE_WINPOINTER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDK_TYPE_DEVICE_WINPOINTER, GdkDeviceWinpointerClass))
|
||||
|
||||
typedef struct _GdkDeviceWinpointer GdkDeviceWinpointer;
|
||||
typedef struct _GdkDeviceWinpointerClass GdkDeviceWinpointerClass;
|
||||
|
||||
struct _GdkDeviceWinpointer
|
||||
{
|
||||
GdkDevice parent_instance;
|
||||
|
||||
HANDLE device_handle;
|
||||
UINT32 start_cursor_id;
|
||||
UINT32 end_cursor_id;
|
||||
|
||||
int origin_x;
|
||||
int origin_y;
|
||||
double scale_x;
|
||||
double scale_y;
|
||||
|
||||
GdkModifierType last_button_mask;
|
||||
|
||||
GdkDeviceTool *tool_pen;
|
||||
GdkDeviceTool *tool_eraser;
|
||||
};
|
||||
|
||||
struct _GdkDeviceWinpointerClass
|
||||
{
|
||||
GdkDeviceClass parent_class;
|
||||
};
|
||||
|
||||
GType gdk_device_winpointer_get_type (void) G_GNUC_CONST;
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GDK_DEVICE_WINPOINTER_H__ */
|
||||
@@ -88,7 +88,7 @@ gdk_device_wintab_query_state (GdkDevice *device,
|
||||
hwnd = NULL;
|
||||
}
|
||||
|
||||
GetCursorPos (&point);
|
||||
_gdk_win32_get_cursor_pos (&point);
|
||||
|
||||
if (hwnd)
|
||||
ScreenToClient (hwnd, &point);
|
||||
|
||||
@@ -29,7 +29,9 @@
|
||||
#include "gdkdevice-win32.h"
|
||||
#include "gdkdevice-virtual.h"
|
||||
#include "gdkdevice-wintab.h"
|
||||
#include "gdkinput-winpointer.h"
|
||||
#include "gdkdisplayprivate.h"
|
||||
#include "gdkdisplay-win32.h"
|
||||
#include "gdkseatdefaultprivate.h"
|
||||
|
||||
#define WINTAB32_DLL "Wintab32.dll"
|
||||
@@ -371,9 +373,6 @@ wintab_init_check (GdkDeviceManagerWin32 *device_manager)
|
||||
|
||||
wintab_contexts = NULL;
|
||||
|
||||
if (_gdk_input_ignore_wintab)
|
||||
return;
|
||||
|
||||
n = GetSystemDirectory (&dummy, 0);
|
||||
|
||||
if (n <= 0)
|
||||
@@ -684,10 +683,13 @@ wintab_default_display_notify_cb (GdkDisplayManager *display_manager)
|
||||
static void
|
||||
gdk_device_manager_win32_constructed (GObject *object)
|
||||
{
|
||||
GdkWin32Display *display_win32;
|
||||
GdkDeviceManagerWin32 *device_manager;
|
||||
GdkSeat *seat;
|
||||
GdkDisplayManager *display_manager = NULL;
|
||||
GdkDisplay *default_display = NULL;
|
||||
const char *api_preference = NULL;
|
||||
gboolean have_api_preference = TRUE;
|
||||
|
||||
display_win32 = GDK_WIN32_DISPLAY (_gdk_display);
|
||||
|
||||
device_manager = GDK_DEVICE_MANAGER_WIN32 (object);
|
||||
device_manager->core_pointer =
|
||||
@@ -728,18 +730,59 @@ gdk_device_manager_win32_constructed (GObject *object)
|
||||
gdk_seat_default_add_physical_device (GDK_SEAT_DEFAULT (seat), device_manager->system_keyboard);
|
||||
g_object_unref (seat);
|
||||
|
||||
/* Only call Wintab init stuff after the default display
|
||||
* is globally known and accessible through the display manager
|
||||
* singleton. Approach lifted from gtkmodules.c.
|
||||
*/
|
||||
display_manager = gdk_display_manager_get ();
|
||||
g_assert (display_manager != NULL);
|
||||
default_display = gdk_display_manager_get_default_display (display_manager);
|
||||
g_assert (default_display == NULL);
|
||||
_gdk_device_manager = device_manager;
|
||||
|
||||
g_signal_connect (display_manager, "notify::default-display",
|
||||
G_CALLBACK (wintab_default_display_notify_cb),
|
||||
NULL);
|
||||
api_preference = g_getenv ("GDK_WIN32_TABLET_INPUT_API");
|
||||
if (g_strcmp0 (api_preference, "none") == 0)
|
||||
{
|
||||
display_win32->tablet_input_api = GDK_WIN32_TABLET_INPUT_API_NONE;
|
||||
}
|
||||
else if (g_strcmp0 (api_preference, "wintab") == 0)
|
||||
{
|
||||
display_win32->tablet_input_api = GDK_WIN32_TABLET_INPUT_API_WINTAB;
|
||||
}
|
||||
else if (g_strcmp0 (api_preference, "winpointer") == 0)
|
||||
{
|
||||
display_win32->tablet_input_api = GDK_WIN32_TABLET_INPUT_API_WINPOINTER;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No user preference, default to WinPointer. If unsuccessful,
|
||||
* try to initialize other API's in sequence until one succeeds.
|
||||
*/
|
||||
display_win32->tablet_input_api = GDK_WIN32_TABLET_INPUT_API_WINPOINTER;
|
||||
have_api_preference = FALSE;
|
||||
}
|
||||
|
||||
if (display_win32->tablet_input_api == GDK_WIN32_TABLET_INPUT_API_WINPOINTER)
|
||||
{
|
||||
gboolean init_successful = gdk_winpointer_initialize ();
|
||||
|
||||
if (!init_successful && !have_api_preference)
|
||||
{
|
||||
/* Try Wintab */
|
||||
display_win32->tablet_input_api = GDK_WIN32_TABLET_INPUT_API_WINTAB;
|
||||
}
|
||||
}
|
||||
|
||||
if (display_win32->tablet_input_api == GDK_WIN32_TABLET_INPUT_API_WINTAB)
|
||||
{
|
||||
GdkDisplayManager *display_manager = NULL;
|
||||
GdkDisplay *default_display = NULL;
|
||||
|
||||
/* Only call Wintab init stuff after the default display
|
||||
* is globally known and accessible through the display manager
|
||||
* singleton. Approach lifted from gtkmodules.c.
|
||||
*/
|
||||
display_manager = gdk_display_manager_get ();
|
||||
g_assert (display_manager != NULL);
|
||||
default_display = gdk_display_manager_get_default_display (display_manager);
|
||||
g_assert (default_display == NULL);
|
||||
|
||||
g_signal_connect (display_manager, "notify::default-display",
|
||||
G_CALLBACK (wintab_default_display_notify_cb),
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -752,7 +795,7 @@ gdk_device_manager_win32_class_init (GdkDeviceManagerWin32Class *klass)
|
||||
}
|
||||
|
||||
void
|
||||
_gdk_input_set_tablet_active (void)
|
||||
_gdk_wintab_set_tablet_active (void)
|
||||
{
|
||||
GList *tmp_list;
|
||||
HCTX *hctx;
|
||||
@@ -763,7 +806,7 @@ _gdk_input_set_tablet_active (void)
|
||||
if (!wintab_contexts)
|
||||
return; /* No tablet devices found, or Wintab not initialized yet */
|
||||
|
||||
GDK_NOTE (INPUT, g_print ("_gdk_input_set_tablet_active: "
|
||||
GDK_NOTE (INPUT, g_print ("_gdk_wintab_set_tablet_active: "
|
||||
"Bringing Wintab contexts to the top of the overlap order\n"));
|
||||
|
||||
tmp_list = wintab_contexts;
|
||||
@@ -866,7 +909,7 @@ gdk_device_manager_find_wintab_device (GdkDeviceManagerWin32 *device_manager,
|
||||
}
|
||||
|
||||
GdkEvent *
|
||||
gdk_input_other_event (GdkDisplay *display,
|
||||
gdk_wintab_make_event (GdkDisplay *display,
|
||||
MSG *msg,
|
||||
GdkSurface *window)
|
||||
{
|
||||
@@ -894,7 +937,7 @@ gdk_input_other_event (GdkDisplay *display,
|
||||
|
||||
if (window != wintab_window)
|
||||
{
|
||||
g_warning ("gdk_input_other_event: not wintab_window?");
|
||||
g_warning ("gdk_wintab_make_event: not wintab_window?");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -905,7 +948,7 @@ gdk_input_other_event (GdkDisplay *display,
|
||||
g_object_ref (window);
|
||||
|
||||
GDK_NOTE (EVENTS_OR_INPUT,
|
||||
g_print ("gdk_input_other_event: window=%p %+g%+g\n",
|
||||
g_print ("gdk_wintab_make_event: window=%p %+g%+g\n",
|
||||
window ? GDK_SURFACE_HWND (window) : NULL, x, y));
|
||||
|
||||
if (msg->message == WT_PACKET || msg->message == WT_CSRCHANGE)
|
||||
|
||||
@@ -40,6 +40,8 @@ struct _GdkDeviceManagerWin32
|
||||
/* Fake physical devices */
|
||||
GdkDevice *system_pointer;
|
||||
GdkDevice *system_keyboard;
|
||||
|
||||
GList *winpointer_devices;
|
||||
GList *wintab_devices;
|
||||
|
||||
/* Bumped up every time a wintab device enters the proximity
|
||||
@@ -56,8 +58,8 @@ struct _GdkDeviceManagerWin32Class
|
||||
|
||||
GType gdk_device_manager_win32_get_type (void) G_GNUC_CONST;
|
||||
|
||||
void _gdk_input_set_tablet_active (void);
|
||||
GdkEvent * gdk_input_other_event (GdkDisplay *display,
|
||||
void _gdk_wintab_set_tablet_active (void);
|
||||
GdkEvent * gdk_wintab_make_event (GdkDisplay *display,
|
||||
MSG *msg,
|
||||
GdkSurface *window);
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#define _WIN32_WINNT 0x0600
|
||||
#define VK_USE_PLATFORM_WIN32_KHR
|
||||
|
||||
#include "gdk.h"
|
||||
@@ -535,7 +534,6 @@ _gdk_win32_display_open (const char *display_name)
|
||||
|
||||
_gdk_win32_lang_notification_init ();
|
||||
_gdk_drag_init ();
|
||||
_gdk_drop_init ();
|
||||
|
||||
_gdk_display->clipboard = gdk_win32_clipboard_new (_gdk_display);
|
||||
_gdk_display->primary_clipboard = gdk_clipboard_new (_gdk_display);
|
||||
|
||||
@@ -99,6 +99,12 @@ typedef struct _GdkWin32User32DPIFuncs
|
||||
funcADACE areDACEqual;
|
||||
} GdkWin32User32DPIFuncs;
|
||||
|
||||
typedef enum {
|
||||
GDK_WIN32_TABLET_INPUT_API_NONE,
|
||||
GDK_WIN32_TABLET_INPUT_API_WINTAB,
|
||||
GDK_WIN32_TABLET_INPUT_API_WINPOINTER
|
||||
} GdkWin32TabletInputAPI;
|
||||
|
||||
/* Detect running architecture */
|
||||
typedef BOOL (WINAPI *funcIsWow64Process2) (HANDLE, USHORT *, USHORT *);
|
||||
typedef struct _GdkWin32KernelCPUFuncs
|
||||
@@ -159,7 +165,9 @@ struct _GdkWin32Display
|
||||
|
||||
GdkWin32ShcoreFuncs shcore_funcs;
|
||||
GdkWin32User32DPIFuncs user32_dpi_funcs;
|
||||
|
||||
|
||||
GdkWin32TabletInputAPI tablet_input_api;
|
||||
|
||||
/* Cursor Items (GdkCursor->GdkWin32HCursor) */
|
||||
GHashTable *cursors;
|
||||
/* The cursor that is used by current grab (if any) */
|
||||
|
||||
+56
-353
@@ -158,9 +158,6 @@
|
||||
* drag window) in response to this, as all the functions
|
||||
* that GDK could perform here are already handled by the
|
||||
* OS driving the DnD process via DoDragDrop() call.
|
||||
* The LOCAL protocol, on the other hand, does a lot,
|
||||
* similar to what X11 backend does with XDND - it sends
|
||||
* GDK_DRAG_LEAVE and GDK_DRAG_ENTER, emits GDK_DRAG_MOTION.
|
||||
*
|
||||
* GDK_BUTTON_RELEASE checks the
|
||||
* released button - if it's the button that was used to
|
||||
@@ -181,11 +178,7 @@
|
||||
* the OS notifies the process about these things happening.
|
||||
* For X11 backend that is done in Xdnd event filters,
|
||||
* for W32 backend this is done in IDropSource/IDropTarget
|
||||
* object methods for the OLE2 protocol, whereas for the
|
||||
* LOCAL protocol these events are emitted only by GDK itself
|
||||
* (with the exception of WM_DROPFILES message, which causes
|
||||
* GDK to create a drop context and then immediately finish
|
||||
* the drag, providing the list of files it got from the message).
|
||||
* object methods for the OLE2 protocol.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -487,17 +480,6 @@ process_dnd_queue (gboolean timed,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
_gdk_win32_local_drag_drop_response (GdkDrag *drag,
|
||||
GdkDragAction action)
|
||||
{
|
||||
GDK_NOTE (DND, g_print ("_gdk_win32_local_drag_drop_response: 0x%p\n",
|
||||
drag));
|
||||
|
||||
g_signal_emit_by_name (drag, "dnd-finished");
|
||||
gdk_drag_drop_done (drag, action != 0);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
do_drag_drop_response (gpointer user_data)
|
||||
{
|
||||
@@ -714,16 +696,6 @@ _gdk_win32_dnd_thread_main (gpointer data)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* For the LOCAL protocol */
|
||||
typedef enum {
|
||||
GDK_DRAG_STATUS_DRAG,
|
||||
GDK_DRAG_STATUS_MOTION_WAIT,
|
||||
GDK_DRAG_STATUS_ACTION_WAIT,
|
||||
GDK_DRAG_STATUS_DROP
|
||||
} GdkDragStatus;
|
||||
|
||||
static gboolean use_ole2_dnd = TRUE;
|
||||
|
||||
static gboolean drag_context_grab (GdkDrag *drag);
|
||||
|
||||
G_DEFINE_TYPE (GdkWin32Drag, gdk_win32_drag, GDK_TYPE_DRAG)
|
||||
@@ -791,8 +763,7 @@ gdk_drag_new (GdkDisplay *display,
|
||||
GdkSurface *surface,
|
||||
GdkContentProvider *content,
|
||||
GdkDragAction actions,
|
||||
GdkDevice *device,
|
||||
GdkDragProtocol protocol)
|
||||
GdkDevice *device)
|
||||
{
|
||||
GdkWin32Drag *drag_win32;
|
||||
GdkWin32Display *display_win32 = GDK_WIN32_DISPLAY (display);
|
||||
@@ -812,8 +783,6 @@ gdk_drag_new (GdkDisplay *display,
|
||||
else
|
||||
drag_win32->scale = gdk_win32_display_get_monitor_scale_factor (display_win32, NULL, NULL);
|
||||
|
||||
drag_win32->protocol = protocol;
|
||||
|
||||
return drag;
|
||||
}
|
||||
|
||||
@@ -1081,21 +1050,6 @@ maybe_emit_action_changed (GdkWin32Drag *drag_win32,
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_gdk_win32_local_drag_give_feedback (GdkDrag *drag,
|
||||
GdkDragAction actions)
|
||||
{
|
||||
GdkWin32Drag *drag_win32 = GDK_WIN32_DRAG (drag);
|
||||
|
||||
if (drag_win32->drag_status == GDK_DRAG_STATUS_MOTION_WAIT)
|
||||
drag_win32->drag_status = GDK_DRAG_STATUS_DRAG;
|
||||
|
||||
GDK_NOTE (DND, g_print ("_gdk_win32_local_drag_give_feedback: 0x%p\n",
|
||||
drag));
|
||||
|
||||
maybe_emit_action_changed (drag_win32, actions);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
give_feedback (gpointer user_data)
|
||||
{
|
||||
@@ -1670,30 +1624,20 @@ enum_formats_new (GArray *formats)
|
||||
void
|
||||
_gdk_drag_init (void)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
CoInitializeEx (NULL, COINIT_APARTMENTTHREADED);
|
||||
|
||||
if (g_strcmp0 (getenv ("GDK_WIN32_OLE2_DND"), "0") == 0)
|
||||
use_ole2_dnd = FALSE;
|
||||
hr = OleInitialize (NULL);
|
||||
|
||||
if (use_ole2_dnd)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
hr = OleInitialize (NULL);
|
||||
|
||||
if (! SUCCEEDED (hr))
|
||||
g_error ("OleInitialize failed");
|
||||
}
|
||||
if (! SUCCEEDED (hr))
|
||||
g_error ("OleInitialize failed");
|
||||
}
|
||||
|
||||
void
|
||||
_gdk_win32_dnd_exit (void)
|
||||
{
|
||||
if (use_ole2_dnd)
|
||||
{
|
||||
OleUninitialize ();
|
||||
}
|
||||
|
||||
OleUninitialize ();
|
||||
CoUninitialize ();
|
||||
}
|
||||
|
||||
@@ -1723,6 +1667,9 @@ _gdk_win32_surface_drag_begin (GdkSurface *surface,
|
||||
GdkWin32Clipdrop *clipdrop = _gdk_win32_clipdrop_get ();
|
||||
double px, py;
|
||||
int x_root, y_root;
|
||||
GdkWin32DnDThreadDoDragDrop *ddd;
|
||||
source_drag_context *source_ctx;
|
||||
data_object *data_obj;
|
||||
|
||||
g_return_val_if_fail (surface != NULL, NULL);
|
||||
|
||||
@@ -1730,8 +1677,7 @@ _gdk_win32_surface_drag_begin (GdkSurface *surface,
|
||||
surface,
|
||||
content,
|
||||
actions,
|
||||
device,
|
||||
use_ole2_dnd ? GDK_DRAG_PROTO_OLE2 : GDK_DRAG_PROTO_LOCAL);
|
||||
device);
|
||||
drag_win32 = GDK_WIN32_DRAG (drag);
|
||||
|
||||
GDK_NOTE (DND, g_print ("_gdk_win32_surface_drag_begin\n"));
|
||||
@@ -1755,121 +1701,35 @@ _gdk_win32_surface_drag_begin (GdkSurface *surface,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (drag_win32->protocol == GDK_DRAG_PROTO_OLE2)
|
||||
{
|
||||
GdkWin32DnDThreadDoDragDrop *ddd = g_new0 (GdkWin32DnDThreadDoDragDrop, 1);
|
||||
source_drag_context *source_ctx;
|
||||
data_object *data_obj;
|
||||
ddd = g_new0 (GdkWin32DnDThreadDoDragDrop, 1);
|
||||
|
||||
source_ctx = source_context_new (drag, gdk_drag_get_formats (drag));
|
||||
data_obj = data_object_new (drag);
|
||||
source_ctx = source_context_new (drag, gdk_drag_get_formats (drag));
|
||||
data_obj = data_object_new (drag);
|
||||
|
||||
ddd->base.item_type = GDK_WIN32_DND_THREAD_QUEUE_ITEM_DO_DRAG_DROP;
|
||||
ddd->base.opaque_context = drag_win32;
|
||||
ddd->src_context = source_ctx;
|
||||
ddd->src_object = data_obj;
|
||||
ddd->allowed_drop_effects = 0;
|
||||
if (actions & GDK_ACTION_COPY)
|
||||
ddd->allowed_drop_effects |= DROPEFFECT_COPY;
|
||||
if (actions & GDK_ACTION_MOVE)
|
||||
ddd->allowed_drop_effects |= DROPEFFECT_MOVE;
|
||||
if (actions & GDK_ACTION_LINK)
|
||||
ddd->allowed_drop_effects |= DROPEFFECT_LINK;
|
||||
ddd->base.item_type = GDK_WIN32_DND_THREAD_QUEUE_ITEM_DO_DRAG_DROP;
|
||||
ddd->base.opaque_context = drag_win32;
|
||||
ddd->src_context = source_ctx;
|
||||
ddd->src_object = data_obj;
|
||||
ddd->allowed_drop_effects = 0;
|
||||
if (actions & GDK_ACTION_COPY)
|
||||
ddd->allowed_drop_effects |= DROPEFFECT_COPY;
|
||||
if (actions & GDK_ACTION_MOVE)
|
||||
ddd->allowed_drop_effects |= DROPEFFECT_MOVE;
|
||||
if (actions & GDK_ACTION_LINK)
|
||||
ddd->allowed_drop_effects |= DROPEFFECT_LINK;
|
||||
|
||||
g_hash_table_replace (clipdrop->active_source_drags, g_object_ref (drag), ddd);
|
||||
increment_dnd_queue_counter ();
|
||||
g_async_queue_push (clipdrop->dnd_queue, ddd);
|
||||
API_CALL (PostThreadMessage, (clipdrop->dnd_thread_id, thread_wakeup_message, 0, 0));
|
||||
g_hash_table_replace (clipdrop->active_source_drags, g_object_ref (drag), ddd);
|
||||
increment_dnd_queue_counter ();
|
||||
g_async_queue_push (clipdrop->dnd_queue, ddd);
|
||||
API_CALL (PostThreadMessage, (clipdrop->dnd_thread_id, thread_wakeup_message, 0, 0));
|
||||
|
||||
drag_win32->util_data.state = GDK_WIN32_DND_PENDING;
|
||||
}
|
||||
drag_win32->util_data.state = GDK_WIN32_DND_PENDING;
|
||||
|
||||
move_drag_surface (drag, x_root, y_root);
|
||||
|
||||
return drag;
|
||||
}
|
||||
|
||||
/* TODO: remove this?
|
||||
* window finder is only used by our gdk_drag_update() to
|
||||
* find the window at drag coordinates - which is
|
||||
* something IDropSourceNotify already gives us.
|
||||
* Unless, of course, we keep the LOCAL protocol around.
|
||||
*/
|
||||
typedef struct {
|
||||
int x;
|
||||
int y;
|
||||
HWND ignore;
|
||||
HWND result;
|
||||
} find_window_enum_arg;
|
||||
|
||||
static BOOL CALLBACK
|
||||
find_window_enum_proc (HWND hwnd,
|
||||
LPARAM lparam)
|
||||
{
|
||||
RECT rect;
|
||||
POINT tl, br;
|
||||
find_window_enum_arg *a = (find_window_enum_arg *) lparam;
|
||||
|
||||
if (hwnd == a->ignore)
|
||||
return TRUE;
|
||||
|
||||
if (!IsWindowVisible (hwnd))
|
||||
return TRUE;
|
||||
|
||||
tl.x = tl.y = 0;
|
||||
ClientToScreen (hwnd, &tl);
|
||||
GetClientRect (hwnd, &rect);
|
||||
br.x = rect.right;
|
||||
br.y = rect.bottom;
|
||||
ClientToScreen (hwnd, &br);
|
||||
|
||||
if (a->x >= tl.x && a->y >= tl.y && a->x < br.x && a->y < br.y)
|
||||
{
|
||||
a->result = hwnd;
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Finds the HWND under cursor. Local DnD protocol
|
||||
* uses this function, since local protocol is implemented
|
||||
* entirely in GDK and cannot rely on the OS to notify
|
||||
* drop targets about drags that move over them.
|
||||
*/
|
||||
static HWND
|
||||
gdk_win32_drag_find_window (GdkDrag *drag,
|
||||
GdkSurface *drag_surface,
|
||||
int x_root,
|
||||
int y_root)
|
||||
{
|
||||
GdkWin32Drag *drag_win32 = GDK_WIN32_DRAG (drag);
|
||||
find_window_enum_arg a;
|
||||
|
||||
g_assert (_win32_main_thread == NULL ||
|
||||
_win32_main_thread == g_thread_self ());
|
||||
|
||||
a.x = x_root * drag_win32->scale - _gdk_offset_x;
|
||||
a.y = y_root * drag_win32->scale - _gdk_offset_y;
|
||||
a.ignore = drag_surface ? GDK_SURFACE_HWND (drag_surface) : NULL;
|
||||
a.result = INVALID_HANDLE_VALUE;
|
||||
|
||||
GDK_NOTE (DND,
|
||||
g_print ("gdk_win32_drag_find_window: %p %+d%+d\n",
|
||||
(drag_surface ? GDK_SURFACE_HWND (drag_surface) : NULL),
|
||||
a.x, a.y));
|
||||
|
||||
EnumWindows (find_window_enum_proc, (LPARAM) &a);
|
||||
|
||||
GDK_NOTE (DND,
|
||||
g_print ("gdk_win32_drag_find_window: %p %+d%+d: %p\n",
|
||||
(drag_surface ? GDK_SURFACE_HWND (drag_surface) : NULL),
|
||||
x_root, y_root,
|
||||
a.result));
|
||||
|
||||
return a.result;
|
||||
}
|
||||
|
||||
static DWORD
|
||||
manufacture_keystate_from_GMT (GdkModifierType state)
|
||||
{
|
||||
@@ -1891,100 +1751,6 @@ manufacture_keystate_from_GMT (GdkModifierType state)
|
||||
return key_state;
|
||||
}
|
||||
|
||||
/* This only works if dest_window our window and the DnD operation
|
||||
* is currently local to the application.
|
||||
*/
|
||||
static GdkDrop *
|
||||
_gdk_win32_get_drop_for_dest_window (HWND dest_window)
|
||||
{
|
||||
GdkSurface *drop_surface = gdk_win32_handle_table_lookup (dest_window);
|
||||
GdkDrop *result = NULL;
|
||||
|
||||
if (drop_surface)
|
||||
result = _gdk_win32_get_drop_for_dest_surface (drop_surface);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gdk_win32_local_drag_motion (GdkDrag *drag,
|
||||
HWND dest_window,
|
||||
int x_root,
|
||||
int y_root,
|
||||
GdkDragAction possible_actions,
|
||||
DWORD key_state,
|
||||
guint32 time_)
|
||||
{
|
||||
GdkWin32Drag *drag_win32;
|
||||
GdkDrop *drop;
|
||||
GdkDragAction actions;
|
||||
|
||||
g_assert (_win32_main_thread == NULL ||
|
||||
_win32_main_thread == g_thread_self ());
|
||||
|
||||
g_return_val_if_fail (drag != NULL, FALSE);
|
||||
|
||||
drag_win32 = GDK_WIN32_DRAG (drag);
|
||||
|
||||
drop = _gdk_win32_get_drop_for_dest_window (drag_win32->dest_window);
|
||||
|
||||
actions = gdk_drag_get_actions (drag);
|
||||
|
||||
GDK_NOTE (DND, g_print ("gdk_win32_local_drag_motion: @ %+d:%+d possible=%s\n"
|
||||
" dest=%p (current %p) drop=%p drag=%p:{actions=%s,action=%s}\n",
|
||||
x_root, y_root,
|
||||
_gdk_win32_drag_action_to_string (possible_actions),
|
||||
dest_window, drag_win32->dest_window, drop, drag,
|
||||
_gdk_win32_drag_action_to_string (actions),
|
||||
_gdk_win32_drag_action_to_string (gdk_drag_get_selected_action (drag))));
|
||||
|
||||
if (drag_win32->dest_window != dest_window)
|
||||
{
|
||||
/* Send a leave to the last destination */
|
||||
if (drop)
|
||||
_gdk_win32_local_drop_target_dragleave (drop, time_);
|
||||
|
||||
drag_win32->dest_window = dest_window;
|
||||
drag_win32->drag_status = GDK_DRAG_STATUS_DRAG;
|
||||
|
||||
_gdk_win32_local_drop_target_dragenter (drag,
|
||||
gdk_win32_handle_table_lookup (dest_window),
|
||||
x_root,
|
||||
y_root,
|
||||
key_state,
|
||||
time_,
|
||||
&actions);
|
||||
|
||||
drop = _gdk_win32_get_drop_for_dest_window (drag_win32->dest_window);
|
||||
maybe_emit_action_changed (drag_win32, actions);
|
||||
}
|
||||
|
||||
/* Send a drag-motion event */
|
||||
|
||||
drag_win32->util_data.last_x = x_root;
|
||||
drag_win32->util_data.last_y = y_root;
|
||||
|
||||
if (drop != NULL &&
|
||||
drag_win32->drag_status == GDK_DRAG_STATUS_DRAG &&
|
||||
_gdk_win32_local_drop_target_will_emit_motion (drop, x_root, y_root, key_state))
|
||||
{
|
||||
actions = gdk_drag_get_actions (drag);
|
||||
drag_win32->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
|
||||
|
||||
_gdk_win32_local_drop_target_dragover (drop, drag, x_root, y_root, key_state, time_, &actions);
|
||||
|
||||
maybe_emit_action_changed (drag_win32, actions);
|
||||
}
|
||||
|
||||
GDK_NOTE (DND, g_print (" returning %s\n"
|
||||
" drag=%p:{actions=%s,action=%s}\n",
|
||||
(drop != NULL && drag_win32->drag_status == GDK_DRAG_STATUS_DRAG) ? "TRUE" : "FALSE",
|
||||
drag,
|
||||
_gdk_win32_drag_action_to_string (gdk_drag_get_actions (drag)),
|
||||
_gdk_win32_drag_action_to_string (gdk_drag_get_selected_action (drag))));
|
||||
return (drop != NULL && drag_win32->drag_status == GDK_DRAG_STATUS_DRAG);
|
||||
}
|
||||
|
||||
static void
|
||||
send_source_state_update (GdkWin32Clipdrop *clipdrop,
|
||||
GdkWin32Drag *drag_win32,
|
||||
@@ -2005,6 +1771,7 @@ gdk_win32_drag_drop (GdkDrag *drag,
|
||||
{
|
||||
GdkWin32Drag *drag_win32 = GDK_WIN32_DRAG (drag);
|
||||
GdkWin32Clipdrop *clipdrop = _gdk_win32_clipdrop_get ();
|
||||
gpointer ddd;
|
||||
|
||||
g_assert (_win32_main_thread == NULL ||
|
||||
_win32_main_thread == g_thread_self ());
|
||||
@@ -2013,29 +1780,12 @@ gdk_win32_drag_drop (GdkDrag *drag,
|
||||
|
||||
GDK_NOTE (DND, g_print ("gdk_win32_drag_drop\n"));
|
||||
|
||||
if (drag_win32->protocol == GDK_DRAG_PROTO_LOCAL)
|
||||
{
|
||||
GdkDrop *drop = _gdk_win32_get_drop_for_dest_window (drag_win32->dest_window);
|
||||
ddd = g_hash_table_lookup (clipdrop->active_source_drags, drag);
|
||||
|
||||
if (drop)
|
||||
{
|
||||
GdkDragAction actions;
|
||||
drag_win32->util_data.state = GDK_WIN32_DND_DROPPED;
|
||||
|
||||
actions = gdk_drag_get_actions (drag);
|
||||
_gdk_win32_local_drop_target_drop (drop, drag, time_, &actions);
|
||||
maybe_emit_action_changed (drag_win32, actions);
|
||||
_gdk_win32_local_drag_drop_response (drag, actions);
|
||||
}
|
||||
}
|
||||
else if (drag_win32->protocol == GDK_DRAG_PROTO_OLE2)
|
||||
{
|
||||
gpointer ddd = g_hash_table_lookup (clipdrop->active_source_drags, drag);
|
||||
|
||||
drag_win32->util_data.state = GDK_WIN32_DND_DROPPED;
|
||||
|
||||
if (ddd)
|
||||
send_source_state_update (clipdrop, drag_win32, ddd);
|
||||
}
|
||||
if (ddd)
|
||||
send_source_state_update (clipdrop, drag_win32, ddd);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -2125,6 +1875,8 @@ gdk_win32_drag_drop_done (GdkDrag *drag,
|
||||
{
|
||||
GdkWin32Drag *drag_win32 = GDK_WIN32_DRAG (drag);
|
||||
GdkDragAnim *anim;
|
||||
GdkWin32Clipdrop *clipdrop;
|
||||
gpointer ddd;
|
||||
/*
|
||||
cairo_surface_t *win_surface;
|
||||
cairo_surface_t *surface;
|
||||
@@ -2139,23 +1891,16 @@ gdk_win32_drag_drop_done (GdkDrag *drag,
|
||||
/* FIXME: This is temporary, until the code is fixed to ensure that
|
||||
* gdk_drag_finish () is called by GTK.
|
||||
*/
|
||||
if (drag_win32->protocol == GDK_DRAG_PROTO_OLE2)
|
||||
{
|
||||
GdkWin32Clipdrop *clipdrop = _gdk_win32_clipdrop_get ();
|
||||
gpointer ddd = g_hash_table_lookup (clipdrop->active_source_drags, drag);
|
||||
clipdrop = _gdk_win32_clipdrop_get ();
|
||||
ddd = g_hash_table_lookup (clipdrop->active_source_drags, drag);
|
||||
|
||||
if (success)
|
||||
drag_win32->util_data.state = GDK_WIN32_DND_DROPPED;
|
||||
else
|
||||
drag_win32->util_data.state = GDK_WIN32_DND_NONE;
|
||||
if (success)
|
||||
drag_win32->util_data.state = GDK_WIN32_DND_DROPPED;
|
||||
else
|
||||
drag_win32->util_data.state = GDK_WIN32_DND_NONE;
|
||||
|
||||
if (ddd)
|
||||
send_source_state_update (clipdrop, drag_win32, ddd);
|
||||
}
|
||||
else if (drag_win32->protocol == GDK_DRAG_PROTO_LOCAL)
|
||||
{
|
||||
|
||||
}
|
||||
if (ddd)
|
||||
send_source_state_update (clipdrop, drag_win32, ddd);
|
||||
|
||||
drag_win32->handle_events = FALSE;
|
||||
|
||||
@@ -2281,14 +2026,6 @@ gdk_win32_drag_cancel (GdkDrag *drag,
|
||||
drag,
|
||||
reason_str));
|
||||
|
||||
if (drag_win32->protocol == GDK_DRAG_PROTO_LOCAL)
|
||||
{
|
||||
GdkDrop *drop = _gdk_win32_get_drop_for_dest_window (drag_win32->dest_window);
|
||||
if (drop)
|
||||
_gdk_win32_local_drop_target_dragleave (drop, GDK_CURRENT_TIME);
|
||||
drop = NULL;
|
||||
}
|
||||
|
||||
gdk_drag_set_cursor (drag, NULL);
|
||||
drag_context_ungrab (drag);
|
||||
gdk_drag_drop_done (drag, FALSE);
|
||||
@@ -2310,28 +2047,6 @@ gdk_win32_drag_drop_performed (GdkDrag *drag,
|
||||
#define BIG_STEP 20
|
||||
#define SMALL_STEP 1
|
||||
|
||||
static void
|
||||
gdk_local_drag_update (GdkDrag *drag,
|
||||
double x_root,
|
||||
double y_root,
|
||||
DWORD grfKeyState,
|
||||
guint32 evtime)
|
||||
{
|
||||
GdkWin32Drag *drag_win32 = GDK_WIN32_DRAG (drag);
|
||||
HWND dest_window;
|
||||
|
||||
g_assert (_win32_main_thread == NULL ||
|
||||
_win32_main_thread == g_thread_self ());
|
||||
|
||||
dest_window = gdk_win32_drag_find_window (drag,
|
||||
drag_win32->drag_surface,
|
||||
x_root, y_root);
|
||||
|
||||
gdk_win32_local_drag_motion (drag, dest_window, x_root, y_root,
|
||||
gdk_drag_get_actions (drag),
|
||||
grfKeyState, evtime);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gdk_dnd_handle_motion_event (GdkDrag *drag,
|
||||
GdkEvent *event)
|
||||
@@ -2341,6 +2056,7 @@ gdk_dnd_handle_motion_event (GdkDrag *drag,
|
||||
DWORD key_state;
|
||||
double x, y;
|
||||
double x_root, y_root;
|
||||
GdkWin32Clipdrop *clipdrop;
|
||||
|
||||
GDK_NOTE (DND, g_print ("gdk_dnd_handle_motion_event: 0x%p\n", drag));
|
||||
|
||||
@@ -2355,26 +2071,18 @@ gdk_dnd_handle_motion_event (GdkDrag *drag,
|
||||
|
||||
key_state = manufacture_keystate_from_GMT (state);
|
||||
|
||||
if (drag_win32->protocol == GDK_DRAG_PROTO_LOCAL)
|
||||
{
|
||||
gdk_local_drag_update (drag, x_root, y_root, key_state,
|
||||
gdk_event_get_time (event));
|
||||
}
|
||||
else if (drag_win32->protocol == GDK_DRAG_PROTO_OLE2)
|
||||
{
|
||||
GdkWin32Clipdrop *clipdrop = _gdk_win32_clipdrop_get ();
|
||||
clipdrop = _gdk_win32_clipdrop_get ();
|
||||
|
||||
GDK_NOTE (DND, g_print ("Post WM_MOUSEMOVE keystate=%lu\n", key_state));
|
||||
GDK_NOTE (DND, g_print ("Post WM_MOUSEMOVE keystate=%lu\n", key_state));
|
||||
|
||||
drag_win32->util_data.last_x = x_root;
|
||||
drag_win32->util_data.last_y = y_root;
|
||||
drag_win32->util_data.last_x = x_root;
|
||||
drag_win32->util_data.last_y = y_root;
|
||||
|
||||
API_CALL (PostThreadMessage, (clipdrop->dnd_thread_id,
|
||||
WM_MOUSEMOVE,
|
||||
key_state,
|
||||
MAKELPARAM (x * drag_win32->scale,
|
||||
y * drag_win32->scale)));
|
||||
}
|
||||
API_CALL (PostThreadMessage, (clipdrop->dnd_thread_id,
|
||||
WM_MOUSEMOVE,
|
||||
key_state,
|
||||
MAKELPARAM (x * drag_win32->scale,
|
||||
y * drag_win32->scale)));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -2456,11 +2164,6 @@ gdk_dnd_handle_key_event (GdkDrag *drag,
|
||||
if (drag_win32->drag_surface)
|
||||
move_drag_surface (drag, drag_win32->util_data.last_x, drag_win32->util_data.last_y);
|
||||
|
||||
if (drag_win32->protocol == GDK_DRAG_PROTO_LOCAL)
|
||||
gdk_local_drag_update (drag, drag_win32->util_data.last_x, drag_win32->util_data.last_y,
|
||||
manufacture_keystate_from_GMT (state),
|
||||
gdk_event_get_time (event));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
+21
-211
@@ -140,9 +140,6 @@ struct _drop_target_context
|
||||
IDataObject *data_object;
|
||||
};
|
||||
|
||||
/* TRUE to use OLE2 protocol, FALSE to use local protocol */
|
||||
static gboolean use_ole2_dnd = TRUE;
|
||||
|
||||
static void
|
||||
gdk_win32_drop_init (GdkWin32Drop *drop)
|
||||
{
|
||||
@@ -196,28 +193,6 @@ gdk_drop_new (GdkDisplay *display,
|
||||
return GDK_DROP (drop_win32);
|
||||
}
|
||||
|
||||
/* Gets the GdkDrop that corresponds to a particular GdkSurface.
|
||||
* Will be NULL for surfaces that are not registered as drop targets,
|
||||
* or for surfaces that are currently not under the drag cursor.
|
||||
* This function is only used for local DnD, where we do have
|
||||
* a real GdkSurface that corresponds to the HWND under cursor.
|
||||
*/
|
||||
GdkDrop *
|
||||
_gdk_win32_get_drop_for_dest_surface (GdkSurface *dest)
|
||||
{
|
||||
GdkWin32Surface *impl;
|
||||
|
||||
if (dest == NULL)
|
||||
return NULL;
|
||||
|
||||
impl = GDK_WIN32_SURFACE (dest);
|
||||
|
||||
if (impl->drop_target != NULL)
|
||||
return impl->drop_target->drop;
|
||||
|
||||
return impl->drop;
|
||||
}
|
||||
|
||||
|
||||
#define PRINT_GUID(guid) \
|
||||
g_print ("%.08lx-%.04x-%.04x-%.02x%.02x-%.02x%.02x%.02x%.02x%.02x%.02x", \
|
||||
@@ -451,51 +426,6 @@ set_source_actions_helper (GdkDrop *drop,
|
||||
return actions;
|
||||
}
|
||||
|
||||
void
|
||||
_gdk_win32_local_drop_target_dragenter (GdkDrag *drag,
|
||||
GdkSurface *dest_surface,
|
||||
int x_root,
|
||||
int y_root,
|
||||
DWORD grfKeyState,
|
||||
guint32 time_,
|
||||
GdkDragAction *actions)
|
||||
{
|
||||
GdkDrop *drop;
|
||||
GdkWin32Drop *drop_win32;
|
||||
GdkDisplay *display;
|
||||
GdkDragAction source_actions;
|
||||
GdkWin32Surface *impl = GDK_WIN32_SURFACE (dest_surface);
|
||||
|
||||
GDK_NOTE (DND, g_print ("_gdk_win32_local_drop_target_dragenter %p @ %d : %d"
|
||||
" for dest window 0x%p"
|
||||
". actions = %s\n",
|
||||
drag, x_root, y_root,
|
||||
dest_surface,
|
||||
_gdk_win32_drag_action_to_string (*actions)));
|
||||
|
||||
display = gdk_surface_get_display (dest_surface);
|
||||
drop = gdk_drop_new (display,
|
||||
gdk_seat_get_pointer (gdk_display_get_default_seat (display)),
|
||||
drag,
|
||||
gdk_drag_get_formats (drag),
|
||||
dest_surface,
|
||||
GDK_DRAG_PROTO_LOCAL);
|
||||
drop_win32 = GDK_WIN32_DROP (drop);
|
||||
|
||||
impl->drop = drop;
|
||||
|
||||
source_actions = set_source_actions_helper (drop, *actions, grfKeyState);
|
||||
|
||||
gdk_drop_emit_enter_event (drop, TRUE, x_root, y_root, time_);
|
||||
drop_win32->last_key_state = grfKeyState;
|
||||
drop_win32->last_x = x_root;
|
||||
drop_win32->last_y = y_root;
|
||||
*actions = filter_actions (drop_win32->actions, source_actions);
|
||||
|
||||
GDK_NOTE (DND, g_print ("_gdk_win32_local_drop_target_dragenter returns with actions %s\n",
|
||||
_gdk_win32_drag_action_to_string (*actions)));
|
||||
}
|
||||
|
||||
/* The pdwEffect here initially points
|
||||
* to a DWORD that contains the value of dwOKEffects argument in DoDragDrop,
|
||||
* i.e. the drag action that the drag source deems acceptable.
|
||||
@@ -575,55 +505,6 @@ idroptarget_dragenter (LPDROPTARGET This,
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
gboolean
|
||||
_gdk_win32_local_drop_target_will_emit_motion (GdkDrop *drop,
|
||||
int x_root,
|
||||
int y_root,
|
||||
DWORD grfKeyState)
|
||||
{
|
||||
GdkWin32Drop *drop_win32 = GDK_WIN32_DROP (drop);
|
||||
|
||||
if (x_root != drop_win32->last_x ||
|
||||
y_root != drop_win32->last_y ||
|
||||
grfKeyState != drop_win32->last_key_state)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
_gdk_win32_local_drop_target_dragover (GdkDrop *drop,
|
||||
GdkDrag *drag,
|
||||
int x_root,
|
||||
int y_root,
|
||||
DWORD grfKeyState,
|
||||
guint32 time_,
|
||||
GdkDragAction *actions)
|
||||
{
|
||||
GdkWin32Drop *drop_win32 = GDK_WIN32_DROP (drop);
|
||||
GdkDragAction source_actions;
|
||||
|
||||
source_actions = set_source_actions_helper (drop, *actions, grfKeyState);
|
||||
|
||||
GDK_NOTE (DND, g_print ("_gdk_win32_local_drop_target_dragover %p @ %d : %d"
|
||||
", actions = %s\n",
|
||||
drop, x_root, y_root,
|
||||
_gdk_win32_drag_action_to_string (*actions)));
|
||||
|
||||
if (_gdk_win32_local_drop_target_will_emit_motion (drop, x_root, y_root, grfKeyState))
|
||||
{
|
||||
gdk_drop_emit_motion_event (drop, TRUE, x_root, y_root, time_);
|
||||
drop_win32->last_key_state = grfKeyState;
|
||||
drop_win32->last_x = x_root;
|
||||
drop_win32->last_y = y_root;
|
||||
}
|
||||
|
||||
*actions = filter_actions (drop_win32->actions, source_actions);
|
||||
|
||||
GDK_NOTE (DND, g_print ("_gdk_win32_local_drop_target_dragover returns with actions %s\n",
|
||||
_gdk_win32_drag_action_to_string (*actions)));
|
||||
}
|
||||
|
||||
/* NOTE: This method is called continuously, even if nothing is
|
||||
* happening, as long as the drag operation is in progress and
|
||||
* the cursor is above our window.
|
||||
@@ -681,18 +562,6 @@ idroptarget_dragover (LPDROPTARGET This,
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void
|
||||
_gdk_win32_local_drop_target_dragleave (GdkDrop *drop,
|
||||
guint32 time_)
|
||||
{
|
||||
GdkWin32Surface *impl = GDK_WIN32_SURFACE (gdk_drop_get_surface (drop));
|
||||
GDK_NOTE (DND, g_print ("_gdk_win32_local_drop_target_dragleave %p\n", drop));
|
||||
|
||||
gdk_drop_emit_leave_event (drop, TRUE, time_);
|
||||
|
||||
g_clear_object (&impl->drop);
|
||||
}
|
||||
|
||||
static HRESULT STDMETHODCALLTYPE
|
||||
idroptarget_dragleave (LPDROPTARGET This)
|
||||
{
|
||||
@@ -708,35 +577,6 @@ idroptarget_dragleave (LPDROPTARGET This)
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void
|
||||
_gdk_win32_local_drop_target_drop (GdkDrop *drop,
|
||||
GdkDrag *drag,
|
||||
guint32 time_,
|
||||
GdkDragAction *actions)
|
||||
{
|
||||
GdkWin32Drop *drop_win32 = GDK_WIN32_DROP (drop);
|
||||
|
||||
GDK_NOTE (DND, g_print ("_gdk_win32_local_drop_target_drop %p ", drop));
|
||||
|
||||
set_source_actions_helper (drop,
|
||||
*actions,
|
||||
drop_win32->last_key_state);
|
||||
|
||||
drop_win32->drop_finished = FALSE;
|
||||
gdk_drop_emit_drop_event (drop, TRUE, drop_win32->last_x, drop_win32->last_y, time_);
|
||||
|
||||
while (!drop_win32->drop_finished)
|
||||
g_main_context_iteration (NULL, FALSE);
|
||||
|
||||
/* Notify local source of the DnD result
|
||||
* Special case:
|
||||
* drop_win32->actions is guaranteed to contain 1 action after gdk_drop_finish ()
|
||||
*/
|
||||
*actions = drop_win32->actions;
|
||||
|
||||
GDK_NOTE (DND, g_print ("drop with action %s\n", _gdk_win32_drag_action_to_string (*actions)));
|
||||
}
|
||||
|
||||
static HRESULT STDMETHODCALLTYPE
|
||||
idroptarget_drop (LPDROPTARGET This,
|
||||
LPDATAOBJECT pDataObj,
|
||||
@@ -1078,14 +918,6 @@ gdk_win32_drop_status (GdkDrop *drop,
|
||||
_gdk_win32_drag_action_to_string (preferred)));
|
||||
|
||||
drop_win32->actions = actions;
|
||||
|
||||
if (drop_win32->protocol == GDK_DRAG_PROTO_OLE2)
|
||||
return;
|
||||
|
||||
drag = gdk_drop_get_drag (drop);
|
||||
|
||||
if (drag != NULL)
|
||||
_gdk_win32_local_drag_give_feedback (drag, actions);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1101,9 +933,6 @@ gdk_win32_drop_finish (GdkDrop *drop,
|
||||
|
||||
drop_win32->actions = action;
|
||||
drop_win32->drop_finished = TRUE;
|
||||
|
||||
if (drop_win32->protocol == GDK_DRAG_PROTO_OLE2)
|
||||
return;
|
||||
}
|
||||
|
||||
#if 0
|
||||
@@ -1136,6 +965,7 @@ _gdk_win32_surface_register_dnd (GdkSurface *window)
|
||||
{
|
||||
drop_target_context *ctx;
|
||||
HRESULT hr;
|
||||
GdkWin32Surface *impl;
|
||||
|
||||
g_return_if_fail (window != NULL);
|
||||
|
||||
@@ -1146,43 +976,30 @@ _gdk_win32_surface_register_dnd (GdkSurface *window)
|
||||
|
||||
GDK_NOTE (DND, g_print ("gdk_win32_surface_register_dnd: %p\n", GDK_SURFACE_HWND (window)));
|
||||
|
||||
if (!use_ole2_dnd)
|
||||
{
|
||||
/* We always claim to accept dropped files, but in fact we might not,
|
||||
* of course. This function is called in such a way that it cannot know
|
||||
* whether the window (widget) in question actually accepts files
|
||||
* (in gtk, data of type text/uri-list) or not.
|
||||
*/
|
||||
gdk_win32_display_add_filter (GDK_WIN32_DISPLAY (gdk_display_get_default ()), gdk_dropfiles_filter, NULL);
|
||||
DragAcceptFiles (GDK_SURFACE_HWND (window), TRUE);
|
||||
}
|
||||
impl = GDK_WIN32_SURFACE (window);
|
||||
|
||||
/* Return if window is already setup for DND. */
|
||||
if (impl->drop_target != NULL)
|
||||
return;
|
||||
|
||||
ctx = target_context_new (window);
|
||||
|
||||
hr = CoLockObjectExternal ((IUnknown *) &ctx->idt, TRUE, FALSE);
|
||||
if (!SUCCEEDED (hr))
|
||||
OTHER_API_FAILED ("CoLockObjectExternal");
|
||||
else
|
||||
{
|
||||
GdkWin32Surface *impl = GDK_WIN32_SURFACE (window);
|
||||
|
||||
/* Return if window is already setup for DND. */
|
||||
if (impl->drop_target != NULL)
|
||||
return;
|
||||
|
||||
ctx = target_context_new (window);
|
||||
|
||||
hr = CoLockObjectExternal ((IUnknown *) &ctx->idt, TRUE, FALSE);
|
||||
if (!SUCCEEDED (hr))
|
||||
OTHER_API_FAILED ("CoLockObjectExternal");
|
||||
hr = RegisterDragDrop (GDK_SURFACE_HWND (window), &ctx->idt);
|
||||
if (hr == DRAGDROP_E_ALREADYREGISTERED)
|
||||
{
|
||||
g_print ("DRAGDROP_E_ALREADYREGISTERED\n");
|
||||
CoLockObjectExternal ((IUnknown *) &ctx->idt, FALSE, FALSE);
|
||||
}
|
||||
else if (!SUCCEEDED (hr))
|
||||
OTHER_API_FAILED ("RegisterDragDrop");
|
||||
else
|
||||
{
|
||||
hr = RegisterDragDrop (GDK_SURFACE_HWND (window), &ctx->idt);
|
||||
if (hr == DRAGDROP_E_ALREADYREGISTERED)
|
||||
{
|
||||
g_print ("DRAGDROP_E_ALREADYREGISTERED\n");
|
||||
CoLockObjectExternal ((IUnknown *) &ctx->idt, FALSE, FALSE);
|
||||
}
|
||||
else if (!SUCCEEDED (hr))
|
||||
OTHER_API_FAILED ("RegisterDragDrop");
|
||||
else
|
||||
{
|
||||
impl->drop_target = ctx;
|
||||
}
|
||||
impl->drop_target = ctx;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1418,10 +1235,3 @@ gdk_win32_drop_class_init (GdkWin32DropClass *klass)
|
||||
drop_class->read_async = gdk_win32_drop_read_async;
|
||||
drop_class->read_finish = gdk_win32_drop_read_finish;
|
||||
}
|
||||
|
||||
void
|
||||
_gdk_drop_init (void)
|
||||
{
|
||||
if (g_strcmp0 (getenv ("GDK_WIN32_OLE2_DND"), "0") == 0)
|
||||
use_ole2_dnd = FALSE;
|
||||
}
|
||||
|
||||
+345
-59
@@ -55,7 +55,9 @@
|
||||
#include "gdkdevicemanager-win32.h"
|
||||
#include "gdkdisplay-win32.h"
|
||||
#include "gdkdeviceprivate.h"
|
||||
#include "gdkdevice-virtual.h"
|
||||
#include "gdkdevice-wintab.h"
|
||||
#include "gdkinput-winpointer.h"
|
||||
#include "gdkwin32dnd.h"
|
||||
#include "gdkwin32dnd-private.h"
|
||||
#include "gdkdisplay-win32.h"
|
||||
@@ -71,8 +73,9 @@
|
||||
#endif
|
||||
|
||||
#include <objbase.h>
|
||||
|
||||
#include <imm.h>
|
||||
#include <tchar.h>
|
||||
#include <tpcshrd.h>
|
||||
|
||||
#define GDK_MOD2_MASK (1 << 4)
|
||||
|
||||
@@ -163,6 +166,10 @@ static int both_shift_pressed[2]; /* to store keycodes for shift keys */
|
||||
static HHOOK keyboard_hook = NULL;
|
||||
static UINT aerosnap_message;
|
||||
|
||||
static gboolean pen_touch_input;
|
||||
static POINT pen_touch_cursor_position;
|
||||
static LONG last_digitizer_time;
|
||||
|
||||
static void
|
||||
track_mouse_event (DWORD dwFlags,
|
||||
HWND hwnd)
|
||||
@@ -197,6 +204,18 @@ _gdk_win32_get_next_tick (gulong suggested_tick)
|
||||
return cur_tick = suggested_tick;
|
||||
}
|
||||
|
||||
BOOL
|
||||
_gdk_win32_get_cursor_pos (LPPOINT lpPoint)
|
||||
{
|
||||
if (pen_touch_input)
|
||||
{
|
||||
*lpPoint = pen_touch_cursor_position;
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
return GetCursorPos (lpPoint);
|
||||
}
|
||||
|
||||
static void
|
||||
generate_focus_event (GdkDeviceManagerWin32 *device_manager,
|
||||
GdkSurface *window,
|
||||
@@ -1036,11 +1055,12 @@ do_show_window (GdkSurface *window, gboolean hide_window)
|
||||
|
||||
static void
|
||||
send_crossing_event (GdkDisplay *display,
|
||||
GdkSurface *window,
|
||||
GdkDevice *physical_device,
|
||||
GdkSurface *window,
|
||||
GdkEventType type,
|
||||
GdkCrossingMode mode,
|
||||
GdkNotifyType notify_type,
|
||||
GdkSurface *subwindow,
|
||||
GdkSurface *subwindow,
|
||||
POINT *screen_pt,
|
||||
GdkModifierType mask,
|
||||
guint32 time_)
|
||||
@@ -1067,16 +1087,18 @@ send_crossing_event (GdkDisplay *display,
|
||||
pt = *screen_pt;
|
||||
ScreenToClient (GDK_SURFACE_HWND (window), &pt);
|
||||
|
||||
_gdk_device_virtual_set_active (_gdk_device_manager->core_pointer, physical_device);
|
||||
|
||||
event = gdk_crossing_event_new (type,
|
||||
window,
|
||||
device_manager->core_pointer,
|
||||
_gdk_win32_get_next_tick (time_),
|
||||
time_,
|
||||
mask,
|
||||
pt.x / impl->surface_scale,
|
||||
pt.y / impl->surface_scale,
|
||||
mode,
|
||||
notify_type);
|
||||
|
||||
|
||||
_gdk_win32_append_event (event);
|
||||
}
|
||||
|
||||
@@ -1119,8 +1141,9 @@ find_common_ancestor (GdkSurface *win1,
|
||||
|
||||
void
|
||||
synthesize_crossing_events (GdkDisplay *display,
|
||||
GdkSurface *src,
|
||||
GdkSurface *dest,
|
||||
GdkDevice *physical_device,
|
||||
GdkSurface *src,
|
||||
GdkSurface *dest,
|
||||
GdkCrossingMode mode,
|
||||
POINT *screen_pt,
|
||||
GdkModifierType mask,
|
||||
@@ -1153,6 +1176,7 @@ synthesize_crossing_events (GdkDisplay *display,
|
||||
else
|
||||
notify_type = GDK_NOTIFY_ANCESTOR;
|
||||
send_crossing_event (display,
|
||||
physical_device,
|
||||
a, GDK_LEAVE_NOTIFY,
|
||||
mode,
|
||||
notify_type,
|
||||
@@ -1172,6 +1196,7 @@ synthesize_crossing_events (GdkDisplay *display,
|
||||
while (win != c && win != NULL)
|
||||
{
|
||||
send_crossing_event (display,
|
||||
physical_device,
|
||||
win, GDK_LEAVE_NOTIFY,
|
||||
mode,
|
||||
notify_type,
|
||||
@@ -1214,6 +1239,7 @@ synthesize_crossing_events (GdkDisplay *display,
|
||||
next = b;
|
||||
|
||||
send_crossing_event (display,
|
||||
physical_device,
|
||||
win, GDK_ENTER_NOTIFY,
|
||||
mode,
|
||||
notify_type,
|
||||
@@ -1233,6 +1259,7 @@ synthesize_crossing_events (GdkDisplay *display,
|
||||
notify_type = GDK_NOTIFY_INFERIOR;
|
||||
|
||||
send_crossing_event (display,
|
||||
physical_device,
|
||||
b, GDK_ENTER_NOTIFY,
|
||||
mode,
|
||||
notify_type,
|
||||
@@ -1242,6 +1269,26 @@ synthesize_crossing_events (GdkDisplay *display,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
make_crossing_event (GdkDevice *physical_device,
|
||||
GdkSurface *surface,
|
||||
POINT *screen_pt,
|
||||
guint32 time_)
|
||||
{
|
||||
GDK_NOTE (EVENTS, g_print (" mouse_window %p -> %p",
|
||||
mouse_window ? GDK_SURFACE_HWND (mouse_window) : NULL,
|
||||
surface ? GDK_SURFACE_HWND (surface) : NULL));
|
||||
synthesize_crossing_events (_gdk_display,
|
||||
physical_device,
|
||||
mouse_window, surface,
|
||||
GDK_CROSSING_NORMAL,
|
||||
screen_pt,
|
||||
0, /* TODO: Set right mask */
|
||||
time_,
|
||||
FALSE);
|
||||
g_set_object (&mouse_window, surface);
|
||||
}
|
||||
|
||||
/* Acquires actual client area size of the underlying native window.
|
||||
* Rectangle is in GDK screen coordinates (_gdk_offset_* is added).
|
||||
* Returns FALSE if configure events should be inhibited,
|
||||
@@ -1526,6 +1573,9 @@ generate_button_event (GdkEventType type,
|
||||
current_x = (gint16) GET_X_LPARAM (msg->lParam) / impl->surface_scale;
|
||||
current_y = (gint16) GET_Y_LPARAM (msg->lParam) / impl->surface_scale;
|
||||
|
||||
_gdk_device_virtual_set_active (_gdk_device_manager->core_pointer,
|
||||
_gdk_device_manager->system_pointer);
|
||||
|
||||
event = gdk_button_event_new (type,
|
||||
window,
|
||||
device_manager->core_pointer,
|
||||
@@ -1757,6 +1807,8 @@ gdk_event_translate (MSG *msg,
|
||||
GdkDeviceGrabInfo *pointer_grab = NULL;
|
||||
GdkSurface *grab_window = NULL;
|
||||
|
||||
crossing_cb_t crossing_cb = NULL;
|
||||
|
||||
int button;
|
||||
|
||||
char buf[256];
|
||||
@@ -2179,6 +2231,8 @@ gdk_event_translate (MSG *msg,
|
||||
g_print (" (%d,%d)",
|
||||
GET_X_LPARAM (msg->lParam), GET_Y_LPARAM (msg->lParam)));
|
||||
|
||||
pen_touch_input = FALSE;
|
||||
|
||||
g_set_object (&window, find_window_for_mouse_event (window, msg));
|
||||
/* TODO_CSW?: there used to some synthesize and propagate */
|
||||
if (GDK_SURFACE_DESTROYED (window))
|
||||
@@ -2221,6 +2275,8 @@ gdk_event_translate (MSG *msg,
|
||||
g_print (" (%d,%d)",
|
||||
GET_X_LPARAM (msg->lParam), GET_Y_LPARAM (msg->lParam)));
|
||||
|
||||
pen_touch_input = FALSE;
|
||||
|
||||
g_set_object (&window, find_window_for_mouse_event (window, msg));
|
||||
|
||||
if (pointer_grab == NULL && implicit_grab_surface != NULL)
|
||||
@@ -2245,11 +2301,12 @@ gdk_event_translate (MSG *msg,
|
||||
}
|
||||
|
||||
synthesize_crossing_events (display,
|
||||
_gdk_device_manager->system_pointer,
|
||||
implicit_grab_surface, new_window,
|
||||
GDK_CROSSING_UNGRAB,
|
||||
&msg->pt,
|
||||
0, /* TODO: Set right mask */
|
||||
msg->time,
|
||||
_gdk_win32_get_next_tick (msg->time),
|
||||
FALSE);
|
||||
g_set_object (&implicit_grab_surface, NULL);
|
||||
g_set_object (&mouse_window, new_window);
|
||||
@@ -2278,6 +2335,23 @@ gdk_event_translate (MSG *msg,
|
||||
(gpointer) msg->wParam,
|
||||
GET_X_LPARAM (msg->lParam), GET_Y_LPARAM (msg->lParam)));
|
||||
|
||||
/* Even if we handle WM_POINTER messages, synthetic WM_MOUSEMOVE messages
|
||||
* are still sent occasionally by the OS, e.g. when a surface is hidden
|
||||
* or shown. Discard spurious WM_MOUSEMOVE messages while handling pen or
|
||||
* touch input
|
||||
*
|
||||
* See the article
|
||||
* "Why do I get spurious WM_MOUSEMOVE messages?" by Raymond Chen:
|
||||
* https://devblogs.microsoft.com/oldnewthing/20031001-00/?p=42343
|
||||
*
|
||||
*/
|
||||
if (win32_display->tablet_input_api == GDK_WIN32_TABLET_INPUT_API_WINPOINTER &&
|
||||
( (msg->time - last_digitizer_time) < 200 ||
|
||||
-(msg->time - last_digitizer_time) < 200 ))
|
||||
break;
|
||||
|
||||
pen_touch_input = FALSE;
|
||||
|
||||
new_window = window;
|
||||
|
||||
if (pointer_grab != NULL)
|
||||
@@ -2305,15 +2379,16 @@ gdk_event_translate (MSG *msg,
|
||||
|
||||
if (mouse_window != new_window)
|
||||
{
|
||||
GDK_NOTE (EVENTS, g_print (" mouse_sinwod %p -> %p",
|
||||
GDK_NOTE (EVENTS, g_print (" mouse_window %p -> %p",
|
||||
mouse_window ? GDK_SURFACE_HWND (mouse_window) : NULL,
|
||||
new_window ? GDK_SURFACE_HWND (new_window) : NULL));
|
||||
synthesize_crossing_events (display,
|
||||
_gdk_device_manager->system_pointer,
|
||||
mouse_window, new_window,
|
||||
GDK_CROSSING_NORMAL,
|
||||
&msg->pt,
|
||||
0, /* TODO: Set right mask */
|
||||
msg->time,
|
||||
_gdk_win32_get_next_tick (msg->time),
|
||||
FALSE);
|
||||
g_set_object (&mouse_window, new_window);
|
||||
mouse_window_ignored_leave = NULL;
|
||||
@@ -2351,6 +2426,9 @@ gdk_event_translate (MSG *msg,
|
||||
current_x = (gint16) GET_X_LPARAM (msg->lParam) / impl->surface_scale;
|
||||
current_y = (gint16) GET_Y_LPARAM (msg->lParam) / impl->surface_scale;
|
||||
|
||||
_gdk_device_virtual_set_active (_gdk_device_manager->core_pointer,
|
||||
_gdk_device_manager->system_pointer);
|
||||
|
||||
event = gdk_motion_event_new (window,
|
||||
device_manager_win32->core_pointer,
|
||||
NULL,
|
||||
@@ -2376,6 +2454,8 @@ gdk_event_translate (MSG *msg,
|
||||
GDK_NOTE (EVENTS, g_print (" %d (%ld,%ld)",
|
||||
HIWORD (msg->wParam), msg->pt.x, msg->pt.y));
|
||||
|
||||
pen_touch_input = FALSE;
|
||||
|
||||
new_window = NULL;
|
||||
hwnd = WindowFromPoint (msg->pt);
|
||||
ignore_leave = FALSE;
|
||||
@@ -2401,11 +2481,12 @@ gdk_event_translate (MSG *msg,
|
||||
|
||||
if (!ignore_leave)
|
||||
synthesize_crossing_events (display,
|
||||
_gdk_device_manager->system_pointer,
|
||||
mouse_window, new_window,
|
||||
GDK_CROSSING_NORMAL,
|
||||
&msg->pt,
|
||||
0, /* TODO: Set right mask */
|
||||
msg->time,
|
||||
_gdk_win32_get_next_tick (msg->time),
|
||||
FALSE);
|
||||
g_set_object (&mouse_window, new_window);
|
||||
mouse_window_ignored_leave = ignore_leave ? new_window : NULL;
|
||||
@@ -2414,6 +2495,213 @@ gdk_event_translate (MSG *msg,
|
||||
return_val = TRUE;
|
||||
break;
|
||||
|
||||
case WM_POINTERDOWN:
|
||||
if (win32_display->tablet_input_api != GDK_WIN32_TABLET_INPUT_API_WINPOINTER ||
|
||||
gdk_winpointer_should_forward_message (msg))
|
||||
{
|
||||
return_val = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (IS_POINTER_PRIMARY_WPARAM (msg->wParam))
|
||||
{
|
||||
current_root_x = pen_touch_cursor_position.x = GET_X_LPARAM (msg->lParam);
|
||||
current_root_y = pen_touch_cursor_position.y = GET_Y_LPARAM (msg->lParam);
|
||||
pen_touch_input = TRUE;
|
||||
last_digitizer_time = msg->time;
|
||||
}
|
||||
|
||||
if (pointer_grab != NULL &&
|
||||
!pointer_grab->implicit &&
|
||||
!pointer_grab->owner_events)
|
||||
g_set_object (&window, pointer_grab->surface);
|
||||
|
||||
if (IS_POINTER_PRIMARY_WPARAM (msg->wParam) && mouse_window != window)
|
||||
crossing_cb = make_crossing_event;
|
||||
|
||||
gdk_winpointer_input_events (window, crossing_cb, msg);
|
||||
|
||||
*ret_valp = 0;
|
||||
return_val = TRUE;
|
||||
break;
|
||||
|
||||
case WM_POINTERUP:
|
||||
if (win32_display->tablet_input_api != GDK_WIN32_TABLET_INPUT_API_WINPOINTER ||
|
||||
gdk_winpointer_should_forward_message (msg))
|
||||
{
|
||||
return_val = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (IS_POINTER_PRIMARY_WPARAM (msg->wParam))
|
||||
{
|
||||
current_root_x = pen_touch_cursor_position.x = GET_X_LPARAM (msg->lParam);
|
||||
current_root_y = pen_touch_cursor_position.y = GET_Y_LPARAM (msg->lParam);
|
||||
pen_touch_input = TRUE;
|
||||
last_digitizer_time = msg->time;
|
||||
}
|
||||
|
||||
if (pointer_grab != NULL &&
|
||||
!pointer_grab->implicit &&
|
||||
!pointer_grab->owner_events)
|
||||
g_set_object (&window, pointer_grab->surface);
|
||||
|
||||
gdk_winpointer_input_events (window, NULL, msg);
|
||||
|
||||
impl = GDK_WIN32_SURFACE (window);
|
||||
if (impl->drag_move_resize_context.op != GDK_WIN32_DRAGOP_NONE)
|
||||
{
|
||||
gdk_win32_surface_end_move_resize_drag (window);
|
||||
}
|
||||
|
||||
*ret_valp = 0;
|
||||
return_val = TRUE;
|
||||
break;
|
||||
|
||||
case WM_POINTERUPDATE:
|
||||
if (win32_display->tablet_input_api != GDK_WIN32_TABLET_INPUT_API_WINPOINTER ||
|
||||
gdk_winpointer_should_forward_message (msg))
|
||||
{
|
||||
return_val = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (IS_POINTER_PRIMARY_WPARAM (msg->wParam))
|
||||
{
|
||||
current_root_x = pen_touch_cursor_position.x = GET_X_LPARAM (msg->lParam);
|
||||
current_root_y = pen_touch_cursor_position.y = GET_Y_LPARAM (msg->lParam);
|
||||
pen_touch_input = TRUE;
|
||||
last_digitizer_time = msg->time;
|
||||
}
|
||||
|
||||
if (pointer_grab != NULL &&
|
||||
!pointer_grab->implicit &&
|
||||
!pointer_grab->owner_events)
|
||||
g_set_object (&window, pointer_grab->surface);
|
||||
|
||||
if (IS_POINTER_PRIMARY_WPARAM (msg->wParam) && mouse_window != window)
|
||||
crossing_cb = make_crossing_event;
|
||||
|
||||
impl = GDK_WIN32_SURFACE (window);
|
||||
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
gdk_winpointer_input_events (window, crossing_cb, msg);
|
||||
}
|
||||
|
||||
*ret_valp = 0;
|
||||
return_val = TRUE;
|
||||
break;
|
||||
|
||||
case WM_NCPOINTERUPDATE:
|
||||
if (win32_display->tablet_input_api != GDK_WIN32_TABLET_INPUT_API_WINPOINTER ||
|
||||
gdk_winpointer_should_forward_message (msg))
|
||||
{
|
||||
return_val = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (IS_POINTER_PRIMARY_WPARAM (msg->wParam))
|
||||
{
|
||||
current_root_x = pen_touch_cursor_position.x = GET_X_LPARAM (msg->lParam);
|
||||
current_root_y = pen_touch_cursor_position.y = GET_Y_LPARAM (msg->lParam);
|
||||
pen_touch_input = TRUE;
|
||||
last_digitizer_time = msg->time;
|
||||
}
|
||||
|
||||
if (IS_POINTER_PRIMARY_WPARAM (msg->wParam) &&
|
||||
!IS_POINTER_INCONTACT_WPARAM (msg->wParam) &&
|
||||
mouse_window != NULL)
|
||||
{
|
||||
GdkDevice *event_device = NULL;
|
||||
guint32 event_time = 0;
|
||||
|
||||
if (gdk_winpointer_get_message_info (msg, &event_device, &event_time))
|
||||
{
|
||||
make_crossing_event(event_device,
|
||||
NULL,
|
||||
&pen_touch_cursor_position,
|
||||
event_time);
|
||||
}
|
||||
}
|
||||
|
||||
return_val = FALSE; /* forward to DefWindowProc */
|
||||
break;
|
||||
|
||||
case WM_POINTERENTER:
|
||||
if (win32_display->tablet_input_api != GDK_WIN32_TABLET_INPUT_API_WINPOINTER ||
|
||||
gdk_winpointer_should_forward_message (msg))
|
||||
{
|
||||
return_val = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (IS_POINTER_PRIMARY_WPARAM (msg->wParam))
|
||||
{
|
||||
current_root_x = pen_touch_cursor_position.x = GET_X_LPARAM (msg->lParam);
|
||||
current_root_y = pen_touch_cursor_position.y = GET_Y_LPARAM (msg->lParam);
|
||||
pen_touch_input = TRUE;
|
||||
last_digitizer_time = msg->time;
|
||||
}
|
||||
|
||||
if (pointer_grab != NULL &&
|
||||
!pointer_grab->implicit &&
|
||||
!pointer_grab->owner_events)
|
||||
g_set_object (&window, pointer_grab->surface);
|
||||
|
||||
if (IS_POINTER_NEW_WPARAM (msg->wParam))
|
||||
{
|
||||
gdk_winpointer_input_events (window, NULL, msg);
|
||||
}
|
||||
|
||||
*ret_valp = 0;
|
||||
return_val = TRUE;
|
||||
break;
|
||||
|
||||
case WM_POINTERLEAVE:
|
||||
if (win32_display->tablet_input_api != GDK_WIN32_TABLET_INPUT_API_WINPOINTER ||
|
||||
gdk_winpointer_should_forward_message (msg))
|
||||
{
|
||||
return_val = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (IS_POINTER_PRIMARY_WPARAM (msg->wParam))
|
||||
{
|
||||
current_root_x = pen_touch_cursor_position.x = GET_X_LPARAM (msg->lParam);
|
||||
current_root_y = pen_touch_cursor_position.y = GET_Y_LPARAM (msg->lParam);
|
||||
pen_touch_input = TRUE;
|
||||
last_digitizer_time = msg->time;
|
||||
}
|
||||
|
||||
if (!IS_POINTER_INRANGE_WPARAM (msg->wParam))
|
||||
{
|
||||
gdk_winpointer_input_events (window, NULL, msg);
|
||||
}
|
||||
else if (IS_POINTER_PRIMARY_WPARAM (msg->wParam) && mouse_window != NULL)
|
||||
{
|
||||
GdkDevice *event_device = NULL;
|
||||
guint32 event_time = 0;
|
||||
|
||||
if (gdk_winpointer_get_message_info (msg, &event_device, &event_time))
|
||||
{
|
||||
make_crossing_event(event_device,
|
||||
NULL,
|
||||
&pen_touch_cursor_position,
|
||||
event_time);
|
||||
}
|
||||
}
|
||||
|
||||
gdk_winpointer_interaction_ended (msg);
|
||||
|
||||
*ret_valp = 0;
|
||||
return_val = TRUE;
|
||||
break;
|
||||
|
||||
case WM_MOUSEWHEEL:
|
||||
case WM_MOUSEHWHEEL:
|
||||
GDK_NOTE (EVENTS, g_print (" %d", (short) HIWORD (msg->wParam)));
|
||||
@@ -2476,6 +2764,9 @@ gdk_event_translate (MSG *msg,
|
||||
*/
|
||||
delta_y *= -1.0;
|
||||
|
||||
_gdk_device_virtual_set_active (_gdk_device_manager->core_pointer,
|
||||
_gdk_device_manager->system_pointer);
|
||||
|
||||
event = gdk_scroll_event_new (window,
|
||||
device_manager_win32->core_pointer,
|
||||
NULL,
|
||||
@@ -2509,44 +2800,6 @@ gdk_event_translate (MSG *msg,
|
||||
return_val = TRUE;
|
||||
break;
|
||||
|
||||
case WM_HSCROLL:
|
||||
/* Just print more debugging information, don't actually handle it. */
|
||||
GDK_NOTE (EVENTS,
|
||||
(g_print (" %s",
|
||||
(LOWORD (msg->wParam) == SB_ENDSCROLL ? "ENDSCROLL" :
|
||||
(LOWORD (msg->wParam) == SB_LEFT ? "LEFT" :
|
||||
(LOWORD (msg->wParam) == SB_RIGHT ? "RIGHT" :
|
||||
(LOWORD (msg->wParam) == SB_LINELEFT ? "LINELEFT" :
|
||||
(LOWORD (msg->wParam) == SB_LINERIGHT ? "LINERIGHT" :
|
||||
(LOWORD (msg->wParam) == SB_PAGELEFT ? "PAGELEFT" :
|
||||
(LOWORD (msg->wParam) == SB_PAGERIGHT ? "PAGERIGHT" :
|
||||
(LOWORD (msg->wParam) == SB_THUMBPOSITION ? "THUMBPOSITION" :
|
||||
(LOWORD (msg->wParam) == SB_THUMBTRACK ? "THUMBTRACK" :
|
||||
"???")))))))))),
|
||||
(LOWORD (msg->wParam) == SB_THUMBPOSITION ||
|
||||
LOWORD (msg->wParam) == SB_THUMBTRACK) ?
|
||||
(g_print (" %d", HIWORD (msg->wParam)), 0) : 0));
|
||||
break;
|
||||
|
||||
case WM_VSCROLL:
|
||||
/* Just print more debugging information, don't actually handle it. */
|
||||
GDK_NOTE (EVENTS,
|
||||
(g_print (" %s",
|
||||
(LOWORD (msg->wParam) == SB_ENDSCROLL ? "ENDSCROLL" :
|
||||
(LOWORD (msg->wParam) == SB_BOTTOM ? "BOTTOM" :
|
||||
(LOWORD (msg->wParam) == SB_TOP ? "TOP" :
|
||||
(LOWORD (msg->wParam) == SB_LINEDOWN ? "LINDOWN" :
|
||||
(LOWORD (msg->wParam) == SB_LINEUP ? "LINEUP" :
|
||||
(LOWORD (msg->wParam) == SB_PAGEDOWN ? "PAGEDOWN" :
|
||||
(LOWORD (msg->wParam) == SB_PAGEUP ? "PAGEUP" :
|
||||
(LOWORD (msg->wParam) == SB_THUMBPOSITION ? "THUMBPOSITION" :
|
||||
(LOWORD (msg->wParam) == SB_THUMBTRACK ? "THUMBTRACK" :
|
||||
"???")))))))))),
|
||||
(LOWORD (msg->wParam) == SB_THUMBPOSITION ||
|
||||
LOWORD (msg->wParam) == SB_THUMBTRACK) ?
|
||||
(g_print (" %d", HIWORD (msg->wParam)), 0) : 0));
|
||||
break;
|
||||
|
||||
case WM_MOUSEACTIVATE:
|
||||
{
|
||||
if (GDK_IS_DRAG_SURFACE (window))
|
||||
@@ -2564,6 +2817,16 @@ gdk_event_translate (MSG *msg,
|
||||
|
||||
break;
|
||||
|
||||
case WM_POINTERACTIVATE:
|
||||
if (GDK_IS_DRAG_SURFACE (window) ||
|
||||
_gdk_modal_blocked (window))
|
||||
{
|
||||
*ret_valp = PA_NOACTIVATE;
|
||||
return_val = TRUE;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case WM_KILLFOCUS:
|
||||
if (keyboard_grab != NULL &&
|
||||
!GDK_SURFACE_DESTROYED (keyboard_grab->surface) &&
|
||||
@@ -2955,6 +3218,13 @@ gdk_event_translate (MSG *msg,
|
||||
*ret_valp = 0;
|
||||
break;
|
||||
|
||||
case WM_DESTROY:
|
||||
if (win32_display->tablet_input_api == GDK_WIN32_TABLET_INPUT_API_WINPOINTER)
|
||||
gdk_winpointer_finalize_surface (window);
|
||||
|
||||
return_val = FALSE;
|
||||
break;
|
||||
|
||||
case WM_NCDESTROY:
|
||||
if ((pointer_grab != NULL && pointer_grab->surface == window) ||
|
||||
(keyboard_grab && keyboard_grab->surface == window))
|
||||
@@ -3035,12 +3305,15 @@ gdk_event_translate (MSG *msg,
|
||||
{
|
||||
gdk_synthesize_surface_state (window, 0, GDK_TOPLEVEL_STATE_FOCUSED);
|
||||
|
||||
/* Bring any tablet contexts to the top of the overlap order when
|
||||
* one of our windows is activated.
|
||||
* NOTE: It doesn't seem to work well if it is done in WM_ACTIVATEAPP
|
||||
* instead
|
||||
*/
|
||||
_gdk_input_set_tablet_active ();
|
||||
if (win32_display->tablet_input_api == GDK_WIN32_TABLET_INPUT_API_WINTAB)
|
||||
{
|
||||
/* Bring any tablet contexts to the top of the overlap order when
|
||||
* one of our windows is activated.
|
||||
* NOTE: It doesn't seem to work well if it is done in WM_ACTIVATEAPP
|
||||
* instead
|
||||
*/
|
||||
_gdk_wintab_set_tablet_active ();
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -3057,6 +3330,16 @@ gdk_event_translate (MSG *msg,
|
||||
GET_Y_LPARAM (msg->lParam), ret_valp);
|
||||
break;
|
||||
|
||||
case WM_TABLET_QUERYSYSTEMGESTURESTATUS:
|
||||
*ret_valp = TABLET_DISABLE_PRESSANDHOLD |
|
||||
TABLET_DISABLE_PENTAPFEEDBACK |
|
||||
TABLET_DISABLE_PENBARRELFEEDBACK |
|
||||
TABLET_DISABLE_FLICKS |
|
||||
TABLET_DISABLE_FLICKFALLBACKKEYS;
|
||||
return_val = TRUE;
|
||||
break;
|
||||
|
||||
|
||||
/* Handle WINTAB events here, as we know that the device manager will
|
||||
* use the fixed WT_DEFBASE as lcMsgBase, and we thus can use the
|
||||
* constants as case labels.
|
||||
@@ -3079,11 +3362,14 @@ gdk_event_translate (MSG *msg,
|
||||
/* Fall through */
|
||||
wintab:
|
||||
|
||||
event = gdk_input_other_event (display, msg, window);
|
||||
if (event)
|
||||
if (win32_display->tablet_input_api == GDK_WIN32_TABLET_INPUT_API_WINTAB)
|
||||
{
|
||||
_gdk_win32_append_event (event);
|
||||
gdk_event_unref (event);
|
||||
event = gdk_wintab_make_event (display, msg, window);
|
||||
if (event)
|
||||
{
|
||||
_gdk_win32_append_event (event);
|
||||
gdk_event_unref (event);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@@ -277,7 +277,7 @@ gdk_win32_display_init_egl (GdkDisplay *display,
|
||||
display_win32->egl_disp = egl_disp;
|
||||
display_win32->egl_version = epoxy_egl_version (egl_disp);
|
||||
|
||||
eglBindAPI(EGL_OPENGL_ES_API);
|
||||
eglBindAPI (EGL_OPENGL_ES_API);
|
||||
|
||||
display_win32->hasEglSurfacelessContext =
|
||||
epoxy_has_egl_extension (egl_disp, "EGL_KHR_surfaceless_context");
|
||||
|
||||
@@ -278,6 +278,9 @@ gdk_win32_display_init_wgl (GdkDisplay *display,
|
||||
if (best_idx == 0 ||
|
||||
!wglMakeCurrent (hdc, display_win32->dummy_context_wgl.hglrc))
|
||||
{
|
||||
if (display_win32->dummy_context_wgl.hglrc != NULL)
|
||||
wglDeleteContext (display_win32->dummy_context_wgl.hglrc);
|
||||
|
||||
g_set_error_literal (error, GDK_GL_ERROR,
|
||||
GDK_GL_ERROR_NOT_AVAILABLE,
|
||||
_("No GL implementation is available"));
|
||||
@@ -288,6 +291,22 @@ gdk_win32_display_init_wgl (GdkDisplay *display,
|
||||
display_win32->wgl_pixel_format = best_idx;
|
||||
display_win32->gl_version = epoxy_gl_version ();
|
||||
|
||||
/* We must have OpenGL/WGL 2.0 or later, or have the GL_ARB_shader_objects extension */
|
||||
if (display_win32->gl_version < 20)
|
||||
{
|
||||
if (!epoxy_has_gl_extension ("GL_ARB_shader_objects"))
|
||||
{
|
||||
wglMakeCurrent (NULL, NULL);
|
||||
wglDeleteContext (display_win32->dummy_context_wgl.hglrc);
|
||||
|
||||
g_set_error_literal (error, GDK_GL_ERROR,
|
||||
GDK_GL_ERROR_NOT_AVAILABLE,
|
||||
_("No GL implementation is available"));
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
display_win32->hasWglARBCreateContext =
|
||||
epoxy_has_wgl_extension (hdc, "WGL_ARB_create_context");
|
||||
display_win32->hasWglEXTSwapControl =
|
||||
|
||||
@@ -42,9 +42,6 @@ HKL _gdk_input_locale;
|
||||
gboolean _gdk_input_locale_is_ime = FALSE;
|
||||
UINT _gdk_input_codepage;
|
||||
|
||||
int _gdk_input_ignore_wintab = FALSE;
|
||||
int _gdk_max_colors = 0;
|
||||
|
||||
GdkWin32ModalOpKind _modal_operation_in_progress = GDK_WIN32_MODAL_OP_NONE;
|
||||
HWND _modal_move_resize_window = NULL;
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,43 @@
|
||||
/* GDK - The GIMP Drawing Kit
|
||||
* Copyright (C) 2021 the GTK team
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __GDK_INPUT_WINPOINTER_H__
|
||||
#define __GDK_INPUT_WINPOINTER_H__
|
||||
|
||||
#include "winpointer.h"
|
||||
|
||||
gboolean gdk_winpointer_initialize (void);
|
||||
|
||||
void gdk_winpointer_initialize_surface (GdkSurface *surface);
|
||||
void gdk_winpointer_finalize_surface (GdkSurface *surface);
|
||||
|
||||
typedef void
|
||||
(*crossing_cb_t)(GdkDevice *physical_device,
|
||||
GdkSurface *surface,
|
||||
POINT *screen_pt,
|
||||
guint32 time_);
|
||||
|
||||
gboolean gdk_winpointer_should_forward_message (MSG *msg);
|
||||
void gdk_winpointer_input_events (GdkSurface *surface,
|
||||
crossing_cb_t crossing_cb,
|
||||
MSG *msg);
|
||||
gboolean gdk_winpointer_get_message_info (MSG *msg,
|
||||
GdkDevice **device,
|
||||
guint32 *time_);
|
||||
void gdk_winpointer_interaction_ended (MSG *msg);
|
||||
|
||||
#endif /* __GDK_INPUT_WINPOINTER_H__ */
|
||||
@@ -54,11 +54,6 @@ _gdk_win32_surfaceing_init (void)
|
||||
{
|
||||
char buf[10];
|
||||
|
||||
if (getenv ("GDK_IGNORE_WINTAB") != NULL)
|
||||
_gdk_input_ignore_wintab = TRUE;
|
||||
else if (getenv ("GDK_USE_WINTAB") != NULL)
|
||||
_gdk_input_ignore_wintab = FALSE;
|
||||
|
||||
if (gdk_synchronize)
|
||||
GdiSetBatchLimit (1);
|
||||
|
||||
@@ -253,7 +248,6 @@ _gdk_win32_drag_protocol_to_string (GdkDragProtocol protocol)
|
||||
CASE (NONE);
|
||||
CASE (WIN32_DROPFILES);
|
||||
CASE (OLE2);
|
||||
CASE (LOCAL);
|
||||
#undef CASE
|
||||
default: return static_printf ("illegal_%d", protocol);
|
||||
}
|
||||
|
||||
@@ -15,22 +15,6 @@
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined (_WIN32_WINNT) && WIN32_WINNT < 0x0601
|
||||
# undef _WIN32_WINNT
|
||||
|
||||
# define _WIN32_WINNT 0x0601
|
||||
# ifdef WINVER
|
||||
# undef WINVER
|
||||
# endif
|
||||
# define WINVER _WIN32_WINNT
|
||||
#elif !defined (_WIN32_WINNT)
|
||||
# define _WIN32_WINNT 0x0601
|
||||
# ifdef WINVER
|
||||
# undef WINVER
|
||||
# endif
|
||||
# define WINVER _WIN32_WINNT
|
||||
#endif
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gdkprivate-win32.h"
|
||||
|
||||
@@ -25,15 +25,6 @@
|
||||
#ifndef __GDK_PRIVATE_WIN32_H__
|
||||
#define __GDK_PRIVATE_WIN32_H__
|
||||
|
||||
#ifndef WINVER
|
||||
/* Vista or newer */
|
||||
#define WINVER 0x0600
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32_WINNT
|
||||
#define _WIN32_WINNT WINVER
|
||||
#endif
|
||||
|
||||
#include <gdk/gdkcursorprivate.h>
|
||||
#include <gdk/win32/gdksurface-win32.h>
|
||||
#include <gdk/win32/gdkwin32display.h>
|
||||
@@ -154,13 +145,14 @@ typedef enum
|
||||
GDK_DRAG_PROTO_NONE = 0,
|
||||
GDK_DRAG_PROTO_WIN32_DROPFILES,
|
||||
GDK_DRAG_PROTO_OLE2,
|
||||
GDK_DRAG_PROTO_LOCAL,
|
||||
} GdkDragProtocol;
|
||||
|
||||
GType _gdk_gc_win32_get_type (void);
|
||||
|
||||
gulong _gdk_win32_get_next_tick (gulong suggested_tick);
|
||||
|
||||
BOOL _gdk_win32_get_cursor_pos (LPPOINT lpPoint);
|
||||
|
||||
void _gdk_surface_init_position (GdkSurface *window);
|
||||
void _gdk_surface_move_resize_child (GdkSurface *window,
|
||||
int x,
|
||||
@@ -250,6 +242,8 @@ void _gdk_other_api_failed (const char *where,
|
||||
#define WIN32_GDI_FAILED(api) WIN32_API_FAILED (api)
|
||||
#define OTHER_API_FAILED(api) _gdk_other_api_failed (G_STRLOC, api)
|
||||
|
||||
#define WIN32_API_FAILED_LOG_ONCE(api) G_STMT_START { static gboolean logged = 0; if (!logged) { _gdk_win32_api_failed (G_STRLOC , api); logged = 1; }} G_STMT_END
|
||||
|
||||
/* These two macros call a GDI or other Win32 API and if the return
|
||||
* value is zero or NULL, print a warning message. The majority of GDI
|
||||
* calls return zero or NULL on failure. The value of the macros is nonzero
|
||||
@@ -313,11 +307,6 @@ extern HWND _modal_move_resize_window;
|
||||
void _gdk_win32_begin_modal_call (GdkWin32ModalOpKind kind);
|
||||
void _gdk_win32_end_modal_call (GdkWin32ModalOpKind kind);
|
||||
|
||||
|
||||
/* Options */
|
||||
extern gboolean _gdk_input_ignore_wintab;
|
||||
extern int _gdk_max_colors;
|
||||
|
||||
/* Convert a pixbuf to an HICON (or HCURSOR). Supports alpha under
|
||||
* Windows XP, thresholds alpha otherwise.
|
||||
*/
|
||||
@@ -442,7 +431,6 @@ BOOL WINAPI GtkShowWindow (GdkSurface *window,
|
||||
/* Initialization */
|
||||
void _gdk_win32_surfaceing_init (void);
|
||||
void _gdk_drag_init (void);
|
||||
void _gdk_drop_init (void);
|
||||
void _gdk_events_init (GdkDisplay *display);
|
||||
|
||||
#endif /* __GDK_PRIVATE_WIN32_H__ */
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#define _WIN32_WINNT 0x0600
|
||||
|
||||
#include "gdk.h"
|
||||
#include "gdkprivate-win32.h"
|
||||
#include "gdkwin32screen.h"
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#include "gdkmonitorprivate.h"
|
||||
#include "gdkwin32surface.h"
|
||||
#include "gdkwin32cursor.h"
|
||||
#include "gdkinput-winpointer.h"
|
||||
#include "gdkglcontext-win32.h"
|
||||
#include "gdkdisplay-win32.h"
|
||||
#include "gdkdevice-win32.h"
|
||||
@@ -204,7 +205,6 @@ gdk_surface_win32_finalize (GObject *object)
|
||||
}
|
||||
|
||||
_gdk_win32_surface_unregister_dnd (GDK_SURFACE (surface));
|
||||
g_clear_object (&surface->drop);
|
||||
|
||||
g_assert (surface->transient_owner == NULL);
|
||||
g_assert (surface->transient_children == NULL);
|
||||
@@ -646,6 +646,9 @@ _gdk_win32_display_create_surface (GdkDisplay *display,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (display_win32->tablet_input_api == GDK_WIN32_TABLET_INPUT_API_WINPOINTER)
|
||||
gdk_winpointer_initialize_surface (surface);
|
||||
|
||||
_gdk_win32_surface_enable_transparency (surface);
|
||||
_gdk_win32_surface_register_dnd (surface);
|
||||
|
||||
|
||||
@@ -251,10 +251,6 @@ struct _GdkWin32Surface
|
||||
* For OLE2 protocol only.
|
||||
*/
|
||||
drop_target_context *drop_target;
|
||||
/* Temporarily holds the GdkDrop currently associated with this window.
|
||||
* For LOCAL protocol only.
|
||||
*/
|
||||
GdkDrop *drop;
|
||||
|
||||
GdkSurface *transient_owner;
|
||||
GSList *transient_children;
|
||||
|
||||
@@ -93,38 +93,6 @@ struct _GdkWin32DragClass
|
||||
gpointer _gdk_win32_dnd_thread_main (gpointer data);
|
||||
|
||||
GdkDrag *_gdk_win32_find_drag_for_dest_window (HWND dest_window);
|
||||
GdkDrop *_gdk_win32_get_drop_for_dest_surface (GdkSurface *dest);
|
||||
|
||||
gboolean _gdk_win32_local_drop_target_will_emit_motion (GdkDrop *drop,
|
||||
int x_root,
|
||||
int y_root,
|
||||
DWORD grfKeyState);
|
||||
|
||||
void _gdk_win32_local_drop_target_dragenter (GdkDrag *drag,
|
||||
GdkSurface *dest_surface,
|
||||
int x_root,
|
||||
int y_root,
|
||||
DWORD grfKeyState,
|
||||
guint32 time_,
|
||||
GdkDragAction *actions);
|
||||
void _gdk_win32_local_drop_target_dragover (GdkDrop *drop,
|
||||
GdkDrag *drag,
|
||||
int x_root,
|
||||
int y_root,
|
||||
DWORD grfKeyState,
|
||||
guint32 time_,
|
||||
GdkDragAction *actions);
|
||||
void _gdk_win32_local_drop_target_dragleave (GdkDrop *drop,
|
||||
guint32 time_);
|
||||
void _gdk_win32_local_drop_target_drop (GdkDrop *drop,
|
||||
GdkDrag *drag,
|
||||
guint32 time_,
|
||||
GdkDragAction *actions);
|
||||
|
||||
void _gdk_win32_local_drag_give_feedback (GdkDrag *drag,
|
||||
GdkDragAction actions);
|
||||
void _gdk_win32_local_drag_drop_response (GdkDrag *drag,
|
||||
GdkDragAction action);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
@@ -6,6 +6,7 @@ gdk_win32_sources = files([
|
||||
'gdkdevicemanager-win32.c',
|
||||
'gdkdevice-virtual.c',
|
||||
'gdkdevice-win32.c',
|
||||
'gdkdevice-winpointer.c',
|
||||
'gdkdevice-wintab.c',
|
||||
'gdkdisplay-win32.c',
|
||||
'gdkdisplaymanager-win32.c',
|
||||
@@ -16,6 +17,7 @@ gdk_win32_sources = files([
|
||||
'gdkglcontext-win32-wgl.c',
|
||||
'gdkglobals-win32.c',
|
||||
'gdkhdataoutputstream-win32.c',
|
||||
'gdkinput-winpointer.c',
|
||||
'gdkkeys-win32.c',
|
||||
'gdkwin32langnotification.c',
|
||||
'gdkmain-win32.c',
|
||||
@@ -51,8 +53,9 @@ if win32_has_egl
|
||||
gdk_win32_sources += ['gdkglcontext-win32-egl.c']
|
||||
endif
|
||||
|
||||
gdk_win32_deps = [ # FIXME
|
||||
pangowin32_dep
|
||||
gdk_win32_deps = [
|
||||
pangowin32_dep, # FIXME
|
||||
cc.find_library('hid'),
|
||||
]
|
||||
|
||||
libgdk_win32 = static_library('gdk-win32',
|
||||
@@ -62,6 +65,8 @@ libgdk_win32 = static_library('gdk-win32',
|
||||
'-DGTK_COMPILATION',
|
||||
'-DG_LOG_DOMAIN="Gdk"',
|
||||
'-DINSIDE_GDK_WIN32',
|
||||
'-D_WIN32_WINNT=0x0601',
|
||||
'-DWINVER=0x0601',
|
||||
] + GDK_WIN32_EGL_CFLAGS,
|
||||
dependencies: [ gdk_deps, gdk_win32_deps ],
|
||||
)
|
||||
|
||||
@@ -0,0 +1,320 @@
|
||||
/* GDK - The GIMP Drawing Kit
|
||||
* Copyright (C) 2021 the GTK team
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* This code is derived from portions provided by the mingw-w64 project
|
||||
* (mingw-w64.org), originally licensed under the Zope Public License
|
||||
* (ZPL) version 2.1, with modifications made on May 12, 2021.
|
||||
* Legal notice of the Zope Public License version 2.1 follows:
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions in source code must retain the accompanying copyright
|
||||
* notice, this list of conditions, and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the accompanying
|
||||
* copyright notice, this list of conditions, and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Names of the copyright holders must not be used to endorse or promote
|
||||
* products derived from this software without prior written permission
|
||||
* from the copyright holders.
|
||||
* 4. The right to distribute this software or to use it for any purpose does
|
||||
* not give you the right to use Servicemarks (sm) or Trademarks (tm) of
|
||||
* the copyright holders. Use of them is covered by separate agreement
|
||||
* with the copyright holders.
|
||||
* 5. If any files are modified, you must cause the modified files to carry
|
||||
* prominent notices stating that you changed the files and the date of
|
||||
* any change.
|
||||
*/
|
||||
|
||||
#ifndef POINTER_FLAG_NONE
|
||||
|
||||
#include <windows.h>
|
||||
#include <tchar.h>
|
||||
|
||||
#define WM_POINTERDEVICECHANGE 0x238
|
||||
#define WM_POINTERDEVICEINRANGE 0x239
|
||||
#define WM_POINTERDEVICEOUTOFRANGE 0x23a
|
||||
|
||||
#define WM_NCPOINTERUPDATE 0x0241
|
||||
#define WM_NCPOINTERDOWN 0x0242
|
||||
#define WM_NCPOINTERUP 0x0243
|
||||
#define WM_POINTERUPDATE 0x0245
|
||||
#define WM_POINTERDOWN 0x0246
|
||||
#define WM_POINTERUP 0x0247
|
||||
#define WM_POINTERENTER 0x0249
|
||||
#define WM_POINTERLEAVE 0x024a
|
||||
#define WM_POINTERACTIVATE 0x024b
|
||||
#define WM_POINTERCAPTURECHANGED 0x024c
|
||||
#define WM_TOUCHHITTESTING 0x024d
|
||||
#define WM_POINTERWHEEL 0x024e
|
||||
#define WM_POINTERHWHEEL 0x024f
|
||||
#define DM_POINTERHITTEST 0x0250
|
||||
#define WM_POINTERROUTEDTO 0x0251
|
||||
#define WM_POINTERROUTEDAWAY 0x0252
|
||||
#define WM_POINTERROUTEDRELEASED 0x0253
|
||||
|
||||
#define POINTER_FLAG_NONE 0x00000000
|
||||
#define POINTER_FLAG_NEW 0x00000001
|
||||
#define POINTER_FLAG_INRANGE 0x00000002
|
||||
#define POINTER_FLAG_INCONTACT 0x00000004
|
||||
#define POINTER_FLAG_FIRSTBUTTON 0x00000010
|
||||
#define POINTER_FLAG_SECONDBUTTON 0x00000020
|
||||
#define POINTER_FLAG_THIRDBUTTON 0x00000040
|
||||
#define POINTER_FLAG_FOURTHBUTTON 0x00000080
|
||||
#define POINTER_FLAG_FIFTHBUTTON 0x00000100
|
||||
#define POINTER_FLAG_PRIMARY 0x00002000
|
||||
#define POINTER_FLAG_CONFIDENCE 0x00004000
|
||||
#define POINTER_FLAG_CANCELED 0x00008000
|
||||
#define POINTER_FLAG_DOWN 0x00010000
|
||||
#define POINTER_FLAG_UPDATE 0x00020000
|
||||
#define POINTER_FLAG_UP 0x00040000
|
||||
#define POINTER_FLAG_WHEEL 0x00080000
|
||||
#define POINTER_FLAG_HWHEEL 0x00100000
|
||||
#define POINTER_FLAG_CAPTURECHANGED 0x00200000
|
||||
#define POINTER_FLAG_HASTRANSFORM 0x00400000
|
||||
|
||||
#define POINTER_MOD_SHIFT (0x0004)
|
||||
#define POINTER_MOD_CTRL (0x0008)
|
||||
|
||||
#define TOUCH_FLAG_NONE 0x00000000
|
||||
|
||||
#define TOUCH_MASK_NONE 0x00000000
|
||||
#define TOUCH_MASK_CONTACTAREA 0x00000001
|
||||
#define TOUCH_MASK_ORIENTATION 0x00000002
|
||||
#define TOUCH_MASK_PRESSURE 0x00000004
|
||||
|
||||
#define PEN_FLAG_NONE 0x00000000
|
||||
#define PEN_FLAG_BARREL 0x00000001
|
||||
#define PEN_FLAG_INVERTED 0x00000002
|
||||
#define PEN_FLAG_ERASER 0x00000004
|
||||
|
||||
#define PEN_MASK_NONE 0x00000000
|
||||
#define PEN_MASK_PRESSURE 0x00000001
|
||||
#define PEN_MASK_ROTATION 0x00000002
|
||||
#define PEN_MASK_TILT_X 0x00000004
|
||||
#define PEN_MASK_TILT_Y 0x00000008
|
||||
|
||||
#define POINTER_MESSAGE_FLAG_NEW 0x00000001
|
||||
#define POINTER_MESSAGE_FLAG_INRANGE 0x00000002
|
||||
#define POINTER_MESSAGE_FLAG_INCONTACT 0x00000004
|
||||
#define POINTER_MESSAGE_FLAG_FIRSTBUTTON 0x00000010
|
||||
#define POINTER_MESSAGE_FLAG_SECONDBUTTON 0x00000020
|
||||
#define POINTER_MESSAGE_FLAG_THIRDBUTTON 0x00000040
|
||||
#define POINTER_MESSAGE_FLAG_FOURTHBUTTON 0x00000080
|
||||
#define POINTER_MESSAGE_FLAG_FIFTHBUTTON 0x00000100
|
||||
#define POINTER_MESSAGE_FLAG_PRIMARY 0x00002000
|
||||
#define POINTER_MESSAGE_FLAG_CONFIDENCE 0x00004000
|
||||
#define POINTER_MESSAGE_FLAG_CANCELED 0x00008000
|
||||
|
||||
#define GET_POINTERID_WPARAM(wParam) (LOWORD (wParam))
|
||||
#define IS_POINTER_FLAG_SET_WPARAM(wParam, flag) (((DWORD)HIWORD (wParam) &(flag)) == (flag))
|
||||
#define IS_POINTER_NEW_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM (wParam, POINTER_MESSAGE_FLAG_NEW)
|
||||
#define IS_POINTER_INRANGE_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM (wParam, POINTER_MESSAGE_FLAG_INRANGE)
|
||||
#define IS_POINTER_INCONTACT_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM (wParam, POINTER_MESSAGE_FLAG_INCONTACT)
|
||||
#define IS_POINTER_FIRSTBUTTON_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM (wParam, POINTER_MESSAGE_FLAG_FIRSTBUTTON)
|
||||
#define IS_POINTER_SECONDBUTTON_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM (wParam, POINTER_MESSAGE_FLAG_SECONDBUTTON)
|
||||
#define IS_POINTER_THIRDBUTTON_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM (wParam, POINTER_MESSAGE_FLAG_THIRDBUTTON)
|
||||
#define IS_POINTER_FOURTHBUTTON_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM (wParam, POINTER_MESSAGE_FLAG_FOURTHBUTTON)
|
||||
#define IS_POINTER_FIFTHBUTTON_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM (wParam, POINTER_MESSAGE_FLAG_FIFTHBUTTON)
|
||||
#define IS_POINTER_PRIMARY_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM (wParam, POINTER_MESSAGE_FLAG_PRIMARY)
|
||||
#define HAS_POINTER_CONFIDENCE_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM (wParam, POINTER_MESSAGE_FLAG_CONFIDENCE)
|
||||
#define IS_POINTER_CANCELED_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM (wParam, POINTER_MESSAGE_FLAG_CANCELED)
|
||||
|
||||
#define PA_ACTIVATE MA_ACTIVATE
|
||||
#define PA_NOACTIVATE MA_NOACTIVATE
|
||||
|
||||
typedef DWORD POINTER_INPUT_TYPE;
|
||||
typedef UINT32 POINTER_FLAGS;
|
||||
typedef UINT32 TOUCH_FLAGS;
|
||||
typedef UINT32 TOUCH_MASK;
|
||||
typedef UINT32 PEN_FLAGS;
|
||||
typedef UINT32 PEN_MASK;
|
||||
|
||||
enum tagPOINTER_INPUT_TYPE {
|
||||
PT_POINTER = 0x00000001,
|
||||
PT_TOUCH = 0x00000002,
|
||||
PT_PEN = 0x00000003,
|
||||
PT_MOUSE = 0x00000004,
|
||||
PT_TOUCHPAD = 0x00000005
|
||||
};
|
||||
|
||||
typedef enum tagFEEDBACK_TYPE {
|
||||
FEEDBACK_TOUCH_CONTACTVISUALIZATION = 1,
|
||||
FEEDBACK_PEN_BARRELVISUALIZATION = 2,
|
||||
FEEDBACK_PEN_TAP = 3,
|
||||
FEEDBACK_PEN_DOUBLETAP = 4,
|
||||
FEEDBACK_PEN_PRESSANDHOLD = 5,
|
||||
FEEDBACK_PEN_RIGHTTAP = 6,
|
||||
FEEDBACK_TOUCH_TAP = 7,
|
||||
FEEDBACK_TOUCH_DOUBLETAP = 8,
|
||||
FEEDBACK_TOUCH_PRESSANDHOLD = 9,
|
||||
FEEDBACK_TOUCH_RIGHTTAP = 10,
|
||||
FEEDBACK_GESTURE_PRESSANDTAP = 11,
|
||||
FEEDBACK_MAX = 0xffffffff
|
||||
} FEEDBACK_TYPE;
|
||||
|
||||
typedef enum tagPOINTER_BUTTON_CHANGE_TYPE {
|
||||
POINTER_CHANGE_NONE,
|
||||
POINTER_CHANGE_FIRSTBUTTON_DOWN,
|
||||
POINTER_CHANGE_FIRSTBUTTON_UP,
|
||||
POINTER_CHANGE_SECONDBUTTON_DOWN,
|
||||
POINTER_CHANGE_SECONDBUTTON_UP,
|
||||
POINTER_CHANGE_THIRDBUTTON_DOWN,
|
||||
POINTER_CHANGE_THIRDBUTTON_UP,
|
||||
POINTER_CHANGE_FOURTHBUTTON_DOWN,
|
||||
POINTER_CHANGE_FOURTHBUTTON_UP,
|
||||
POINTER_CHANGE_FIFTHBUTTON_DOWN,
|
||||
POINTER_CHANGE_FIFTHBUTTON_UP,
|
||||
} POINTER_BUTTON_CHANGE_TYPE;
|
||||
|
||||
typedef struct tagPOINTER_INFO {
|
||||
POINTER_INPUT_TYPE pointerType;
|
||||
UINT32 pointerId;
|
||||
UINT32 frameId;
|
||||
POINTER_FLAGS pointerFlags;
|
||||
HANDLE sourceDevice;
|
||||
HWND hwndTarget;
|
||||
POINT ptPixelLocation;
|
||||
POINT ptHimetricLocation;
|
||||
POINT ptPixelLocationRaw;
|
||||
POINT ptHimetricLocationRaw;
|
||||
DWORD dwTime;
|
||||
UINT32 historyCount;
|
||||
INT32 InputData;
|
||||
DWORD dwKeyStates;
|
||||
UINT64 PerformanceCount;
|
||||
POINTER_BUTTON_CHANGE_TYPE ButtonChangeType;
|
||||
} POINTER_INFO;
|
||||
|
||||
typedef struct tagPOINTER_TOUCH_INFO {
|
||||
POINTER_INFO pointerInfo;
|
||||
TOUCH_FLAGS touchFlags;
|
||||
TOUCH_MASK touchMask;
|
||||
RECT rcContact;
|
||||
RECT rcContactRaw;
|
||||
UINT32 orientation;
|
||||
UINT32 pressure;
|
||||
} POINTER_TOUCH_INFO;
|
||||
|
||||
typedef struct tagPOINTER_PEN_INFO {
|
||||
POINTER_INFO pointerInfo;
|
||||
PEN_FLAGS penFlags;
|
||||
PEN_MASK penMask;
|
||||
UINT32 pressure;
|
||||
UINT32 rotation;
|
||||
INT32 tiltX;
|
||||
INT32 tiltY;
|
||||
} POINTER_PEN_INFO;
|
||||
|
||||
typedef enum {
|
||||
POINTER_FEEDBACK_DEFAULT = 1,
|
||||
POINTER_FEEDBACK_INDIRECT = 2,
|
||||
POINTER_FEEDBACK_NONE = 3
|
||||
} POINTER_FEEDBACK_MODE;
|
||||
|
||||
typedef struct tagUSAGE_PROPERTIES {
|
||||
USHORT level;
|
||||
USHORT page;
|
||||
USHORT usage;
|
||||
INT32 logicalMinimum;
|
||||
INT32 logicalMaximum;
|
||||
USHORT unit;
|
||||
USHORT exponent;
|
||||
BYTE count;
|
||||
INT32 physicalMinimum;
|
||||
INT32 physicalMaximum;
|
||||
} USAGE_PROPERTIES, *PUSAGE_PROPERTIES;
|
||||
|
||||
typedef struct tagPOINTER_TYPE_INFO {
|
||||
POINTER_INPUT_TYPE type;
|
||||
union {
|
||||
POINTER_TOUCH_INFO touchInfo;
|
||||
POINTER_PEN_INFO penInfo;
|
||||
} DUMMYUNIONNAME;
|
||||
} POINTER_TYPE_INFO, *PPOINTER_TYPE_INFO;
|
||||
|
||||
#define POINTER_DEVICE_PRODUCT_STRING_MAX 520
|
||||
#define PDC_ARRIVAL 0x001
|
||||
#define PDC_REMOVAL 0x002
|
||||
#define PDC_ORIENTATION_0 0x004
|
||||
#define PDC_ORIENTATION_90 0x008
|
||||
#define PDC_ORIENTATION_180 0x010
|
||||
#define PDC_ORIENTATION_270 0x020
|
||||
#define PDC_MODE_DEFAULT 0x040
|
||||
#define PDC_MODE_CENTERED 0x080
|
||||
#define PDC_MAPPING_CHANGE 0x100
|
||||
#define PDC_RESOLUTION 0x200
|
||||
#define PDC_ORIGIN 0x400
|
||||
#define PDC_MODE_ASPECTRATIOPRESERVED 0x800
|
||||
|
||||
typedef enum tagPOINTER_DEVICE_TYPE {
|
||||
POINTER_DEVICE_TYPE_INTEGRATED_PEN = 0x00000001,
|
||||
POINTER_DEVICE_TYPE_EXTERNAL_PEN = 0x00000002,
|
||||
POINTER_DEVICE_TYPE_TOUCH = 0x00000003,
|
||||
POINTER_DEVICE_TYPE_TOUCH_PAD = 0x00000004,
|
||||
POINTER_DEVICE_TYPE_MAX = 0xffffffff
|
||||
} POINTER_DEVICE_TYPE;
|
||||
|
||||
typedef struct tagPOINTER_DEVICE_INFO {
|
||||
DWORD displayOrientation;
|
||||
HANDLE device;
|
||||
POINTER_DEVICE_TYPE pointerDeviceType;
|
||||
HMONITOR monitor;
|
||||
ULONG startingCursorId;
|
||||
USHORT maxActiveContacts;
|
||||
WCHAR productString[POINTER_DEVICE_PRODUCT_STRING_MAX];
|
||||
} POINTER_DEVICE_INFO;
|
||||
|
||||
typedef struct tagPOINTER_DEVICE_PROPERTY {
|
||||
INT32 logicalMin;
|
||||
INT32 logicalMax;
|
||||
INT32 physicalMin;
|
||||
INT32 physicalMax;
|
||||
UINT32 unit;
|
||||
UINT32 unitExponent;
|
||||
USHORT usagePageId;
|
||||
USHORT usageId;
|
||||
} POINTER_DEVICE_PROPERTY;
|
||||
|
||||
typedef enum tagPOINTER_DEVICE_CURSOR_TYPE {
|
||||
POINTER_DEVICE_CURSOR_TYPE_UNKNOWN = 0x00000000,
|
||||
POINTER_DEVICE_CURSOR_TYPE_TIP = 0x00000001,
|
||||
POINTER_DEVICE_CURSOR_TYPE_ERASER = 0x00000002,
|
||||
POINTER_DEVICE_CURSOR_TYPE_MAX = 0xffffffff
|
||||
} POINTER_DEVICE_CURSOR_TYPE;
|
||||
|
||||
typedef struct tagPOINTER_DEVICE_CURSOR_INFO {
|
||||
UINT32 cursorId;
|
||||
POINTER_DEVICE_CURSOR_TYPE cursor;
|
||||
} POINTER_DEVICE_CURSOR_INFO;
|
||||
|
||||
#endif /* POINTER_FLAG_NONE */
|
||||
|
||||
#if WINVER < 0x0601
|
||||
|
||||
typedef struct tagGESTURECONFIG {
|
||||
DWORD dwID;
|
||||
DWORD dwWant;
|
||||
DWORD dwBlock;
|
||||
} GESTURECONFIG,*PGESTURECONFIG;
|
||||
|
||||
#endif /* WINVER < 0x0601 */
|
||||
|
||||
#ifndef MICROSOFT_TABLETPENSERVICE_PROPERTY
|
||||
#define MICROSOFT_TABLETPENSERVICE_PROPERTY _T("MicrosoftTabletPenServiceProperty")
|
||||
#endif
|
||||
@@ -204,9 +204,6 @@ static void
|
||||
gdk_x11_display_init (GdkX11Display *self)
|
||||
{
|
||||
self->monitors = g_list_store_new (GDK_TYPE_MONITOR);
|
||||
self->program_class = g_strdup (g_get_prgname ());
|
||||
if (self->program_class && self->program_class[0])
|
||||
self->program_class[0] = g_ascii_toupper (self->program_class[0]);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1040,8 +1037,8 @@ gdk_x11_display_translate_event (GdkEventTranslator *translator,
|
||||
if (xevent->type - display_x11->xrandr_event_base == RRScreenChangeNotify ||
|
||||
xevent->type - display_x11->xrandr_event_base == RRNotify)
|
||||
{
|
||||
if (x11_screen)
|
||||
_gdk_x11_screen_size_changed (x11_screen, xevent);
|
||||
if (display_x11->screen)
|
||||
_gdk_x11_screen_size_changed (display_x11->screen, xevent);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
|
||||
@@ -1282,7 +1282,10 @@ _gdk_x11_display_create_surface (GdkDisplay *display,
|
||||
|
||||
class_hint = XAllocClassHint ();
|
||||
class_hint->res_name = (char *) g_get_prgname ();
|
||||
class_hint->res_class = (char *) display_x11->program_class;
|
||||
if (display_x11->program_class)
|
||||
class_hint->res_class = (char *) display_x11->program_class;
|
||||
else
|
||||
class_hint->res_class = class_hint->res_name;
|
||||
XSetClassHint (xdisplay, impl->xid, class_hint);
|
||||
XFree (class_hint);
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ get_boolean_default (GdkX11Screen *x11_screen,
|
||||
char *v;
|
||||
int i;
|
||||
|
||||
if (GDK_DISPLAY_DEBUG_CHECK (GDK_SCREEN_DISPLAY (x11_screen), DEFAULT_SETTINGS))
|
||||
if (gdk_display_get_debug_flags (GDK_SCREEN_DISPLAY (x11_screen)) & GDK_DEBUG_DEFAULT_SETTINGS)
|
||||
return FALSE;
|
||||
|
||||
v = XGetDefault (dpy, "Xft", option);
|
||||
@@ -114,7 +114,7 @@ get_double_default (GdkX11Screen *x11_screen,
|
||||
Display *dpy = GDK_SCREEN_XDISPLAY (x11_screen);
|
||||
char *v, *e;
|
||||
|
||||
if (GDK_DISPLAY_DEBUG_CHECK (GDK_SCREEN_DISPLAY (x11_screen), DEFAULT_SETTINGS))
|
||||
if (gdk_display_get_debug_flags (GDK_SCREEN_DISPLAY (x11_screen)) & GDK_DEBUG_DEFAULT_SETTINGS)
|
||||
return FALSE;
|
||||
|
||||
v = XGetDefault (dpy, "Xft", option);
|
||||
@@ -141,7 +141,7 @@ get_integer_default (GdkX11Screen *x11_screen,
|
||||
Display *dpy = GDK_SCREEN_XDISPLAY (x11_screen);
|
||||
char *v, *e;
|
||||
|
||||
if (GDK_DISPLAY_DEBUG_CHECK (GDK_SCREEN_DISPLAY (x11_screen), DEFAULT_SETTINGS))
|
||||
if (gdk_display_get_debug_flags (GDK_SCREEN_DISPLAY (x11_screen)) & GDK_DEBUG_DEFAULT_SETTINGS)
|
||||
return FALSE;
|
||||
|
||||
v = XGetDefault (dpy, "Xft", option);
|
||||
|
||||
@@ -1,317 +0,0 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define SANITY_CHECKS 0
|
||||
|
||||
|
||||
|
||||
#define rounded_rect_top_left(r) (GRAPHENE_RECT_INIT(r->bounds.origin.x, \
|
||||
r->bounds.origin.y, \
|
||||
r->corner[0].width, r->corner[0].height))
|
||||
|
||||
#define rounded_rect_top_right(r) (GRAPHENE_RECT_INIT(r->bounds.origin.x + r->bounds.size.width - r->corner[1].width, \
|
||||
r->bounds.origin.y, \
|
||||
r->corner[1].width, r->corner[1].height))
|
||||
|
||||
#define rounded_rect_bottom_right(r) (GRAPHENE_RECT_INIT(r->bounds.origin.x + r->bounds.size.width - r->corner[2].width, \
|
||||
r->bounds.origin.y + r->bounds.size.height - r->corner[2].height, \
|
||||
r->corner[2].width, r->corner[2].height))
|
||||
|
||||
#define rounded_rect_bottom_left(r) (GRAPHENE_RECT_INIT(r->bounds.origin.x, \
|
||||
r->bounds.origin.y + r->bounds.size.height - r->corner[2].height, \
|
||||
r->corner[3].width, r->corner[3].height))
|
||||
|
||||
|
||||
#define rounded_rect_corner0(r) rounded_rect_top_left(r)
|
||||
#define rounded_rect_corner1(r) rounded_rect_top_right(r)
|
||||
#define rounded_rect_corner2(r) rounded_rect_bottom_right(r)
|
||||
#define rounded_rect_corner3(r) rounded_rect_bottom_left(r)
|
||||
|
||||
#define rounded_rect_corner(r, i) (rounded_rect_corner ##i(r))
|
||||
#define graphene_size_non_zero(s) (s->width > 0 && s->height > 0)
|
||||
#define rounded_rect_has_corner(r, i) (r->corner[i].width > 0 && r->corner[i].height > 0)
|
||||
|
||||
#define rect_contains_point(r, _x, _y) (_x >= (r)->origin.x && _x <= (r)->origin.x + (r)->size.width && \
|
||||
_y >= (r)->origin.y && _y <= (r)->origin.y + (r)->size.height)
|
||||
|
||||
enum {
|
||||
NINE_SLICE_TOP_LEFT = 0,
|
||||
NINE_SLICE_TOP_CENTER = 1,
|
||||
NINE_SLICE_TOP_RIGHT = 2,
|
||||
NINE_SLICE_LEFT_CENTER = 3,
|
||||
NINE_SLICE_CENTER = 4,
|
||||
NINE_SLICE_RIGHT_CENTER = 5,
|
||||
NINE_SLICE_BOTTOM_LEFT = 6,
|
||||
NINE_SLICE_BOTTOM_CENTER = 7,
|
||||
NINE_SLICE_BOTTOM_RIGHT = 8,
|
||||
};
|
||||
#define NINE_SLICE_SIZE 9 /* Hah. */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int texture_id;
|
||||
float x;
|
||||
float y;
|
||||
float x2;
|
||||
float y2;
|
||||
} TextureRegion;
|
||||
|
||||
static inline bool G_GNUC_PURE
|
||||
slice_is_visible (const cairo_rectangle_int_t *r)
|
||||
{
|
||||
return (r->width > 0 && r->height > 0);
|
||||
}
|
||||
|
||||
static inline void
|
||||
nine_slice_rounded_rect (const GskRoundedRect *rect,
|
||||
cairo_rectangle_int_t *out_rects)
|
||||
{
|
||||
const graphene_point_t *origin = &rect->bounds.origin;
|
||||
const graphene_size_t *size = &rect->bounds.size;
|
||||
const int top_height = ceilf (MAX (rect->corner[GSK_CORNER_TOP_LEFT].height,
|
||||
rect->corner[GSK_CORNER_TOP_RIGHT].height));
|
||||
const int bottom_height = ceilf (MAX (rect->corner[GSK_CORNER_BOTTOM_LEFT].height,
|
||||
rect->corner[GSK_CORNER_BOTTOM_RIGHT].height));
|
||||
const int right_width = ceilf (MAX (rect->corner[GSK_CORNER_TOP_RIGHT].width,
|
||||
rect->corner[GSK_CORNER_BOTTOM_RIGHT].width));
|
||||
const int left_width = ceilf (MAX (rect->corner[GSK_CORNER_TOP_LEFT].width,
|
||||
rect->corner[GSK_CORNER_BOTTOM_LEFT].width));
|
||||
|
||||
/* Top left */
|
||||
out_rects[0] = (cairo_rectangle_int_t) {
|
||||
origin->x, origin->y,
|
||||
left_width, top_height,
|
||||
};
|
||||
|
||||
/* Top center */
|
||||
out_rects[1] = (cairo_rectangle_int_t) {
|
||||
origin->x + size->width / 2.0 - 0.5, origin->y,
|
||||
1, top_height,
|
||||
};
|
||||
|
||||
/* Top right */
|
||||
out_rects[2] = (cairo_rectangle_int_t) {
|
||||
origin->x + size->width - right_width, origin->y,
|
||||
right_width, top_height
|
||||
};
|
||||
|
||||
/* Left center */
|
||||
out_rects[3] = (cairo_rectangle_int_t) {
|
||||
origin->x, origin->y + size->height / 2,
|
||||
left_width, 1,
|
||||
};
|
||||
|
||||
/* center */
|
||||
out_rects[4] = (cairo_rectangle_int_t) {
|
||||
origin->x + size->width / 2.0 - 0.5,
|
||||
origin->y + size->height / 2.0 - 0.5,
|
||||
1, 1
|
||||
};
|
||||
|
||||
/* Right center */
|
||||
out_rects[5] = (cairo_rectangle_int_t) {
|
||||
origin->x + size->width - right_width,
|
||||
origin->y + (size->height / 2.0) - 0.5,
|
||||
right_width,
|
||||
1,
|
||||
};
|
||||
|
||||
/* Bottom Left */
|
||||
out_rects[6] = (cairo_rectangle_int_t) {
|
||||
origin->x, origin->y + size->height - bottom_height,
|
||||
left_width, bottom_height,
|
||||
};
|
||||
|
||||
/* Bottom center */
|
||||
out_rects[7] = (cairo_rectangle_int_t) {
|
||||
origin->x + (size->width / 2.0) - 0.5,
|
||||
origin->y + size->height - bottom_height,
|
||||
1, bottom_height,
|
||||
};
|
||||
|
||||
/* Bottom right */
|
||||
out_rects[8] = (cairo_rectangle_int_t) {
|
||||
origin->x + size->width - right_width,
|
||||
origin->y + size->height - bottom_height,
|
||||
right_width, bottom_height,
|
||||
};
|
||||
|
||||
#if SANITY_CHECKS
|
||||
g_assert_cmpfloat (size->width, >=, left_width + right_width);
|
||||
g_assert_cmpfloat (size->height, >=, top_height + bottom_height);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void
|
||||
nine_slice_grow (cairo_rectangle_int_t *slices,
|
||||
const int amount)
|
||||
{
|
||||
/* top left */
|
||||
slices[0].x -= amount;
|
||||
slices[0].y -= amount;
|
||||
if (amount > slices[0].width)
|
||||
slices[0].width += amount * 2;
|
||||
else
|
||||
slices[0].width += amount;
|
||||
|
||||
if (amount > slices[0].height)
|
||||
slices[0].height += amount * 2;
|
||||
else
|
||||
slices[0].height += amount;
|
||||
|
||||
|
||||
/* Top center */
|
||||
slices[1].y -= amount;
|
||||
if (amount > slices[1].height)
|
||||
slices[1].height += amount * 2;
|
||||
else
|
||||
slices[1].height += amount;
|
||||
|
||||
/* top right */
|
||||
slices[2].y -= amount;
|
||||
if (amount > slices[2].width)
|
||||
{
|
||||
slices[2].x -= amount;
|
||||
slices[2].width += amount * 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
slices[2].width += amount;
|
||||
}
|
||||
|
||||
if (amount > slices[2].height)
|
||||
slices[2].height += amount * 2;
|
||||
else
|
||||
slices[2].height += amount;
|
||||
|
||||
|
||||
|
||||
slices[3].x -= amount;
|
||||
if (amount > slices[3].width)
|
||||
slices[3].width += amount * 2;
|
||||
else
|
||||
slices[3].width += amount;
|
||||
|
||||
/* Leave Britney^Wcenter alone */
|
||||
|
||||
if (amount > slices[5].width)
|
||||
{
|
||||
slices[5].x -= amount;
|
||||
slices[5].width += amount * 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
slices[5].width += amount;
|
||||
}
|
||||
|
||||
|
||||
/* Bottom left */
|
||||
slices[6].x -= amount;
|
||||
if (amount > slices[6].width)
|
||||
{
|
||||
slices[6].width += amount * 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
slices[6].width += amount;
|
||||
}
|
||||
|
||||
if (amount > slices[6].height)
|
||||
{
|
||||
slices[6].y -= amount;
|
||||
slices[6].height += amount * 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
slices[6].height += amount;
|
||||
}
|
||||
|
||||
|
||||
/* Bottom center */
|
||||
if (amount > slices[7].height)
|
||||
{
|
||||
slices[7].y -= amount;
|
||||
slices[7].height += amount * 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
slices[7].height += amount;
|
||||
}
|
||||
|
||||
if (amount > slices[8].width)
|
||||
{
|
||||
slices[8].x -= amount;
|
||||
slices[8].width += amount * 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
slices[8].width += amount;
|
||||
}
|
||||
|
||||
if (amount > slices[8].height)
|
||||
{
|
||||
slices[8].y -= amount;
|
||||
slices[8].height += amount * 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
slices[8].height += amount;
|
||||
}
|
||||
|
||||
#if SANITY_CHECKS
|
||||
{
|
||||
for (int i = 0; i < 9; i ++)
|
||||
{
|
||||
g_assert_cmpint (slices[i].x, >=, 0);
|
||||
g_assert_cmpint (slices[i].y, >=, 0);
|
||||
g_assert_cmpint (slices[i].width, >=, 0);
|
||||
g_assert_cmpint (slices[i].height, >=, 0);
|
||||
}
|
||||
|
||||
/* Rows don't overlap */
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
g_assert_cmpint (slices[i * 3 + 0].x + slices[i * 3 + 0].width, <, slices[i * 3 + 1].x);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
static inline void
|
||||
nine_slice_to_texture_coords (const cairo_rectangle_int_t *slices,
|
||||
const int texture_width,
|
||||
const int texture_height,
|
||||
TextureRegion *out_regions)
|
||||
{
|
||||
const float fw = (float)texture_width;
|
||||
const float fh = (float)texture_height;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 9; i++)
|
||||
{
|
||||
out_regions[i] = (TextureRegion) {
|
||||
0, /* Texture id */
|
||||
slices[i].x / fw,
|
||||
1.0 - ((slices[i].y + slices[i].height) / fh),
|
||||
(slices[i].x + slices[i].width) / fw,
|
||||
1.0 - (slices[i].y / fh),
|
||||
};
|
||||
}
|
||||
|
||||
#if SANITY_CHECKS
|
||||
{
|
||||
for (i = 0; i < 9; i++)
|
||||
{
|
||||
const TextureRegion *r = &out_regions[i];
|
||||
g_assert_cmpfloat (r->x, >=, 0);
|
||||
g_assert_cmpfloat (r->x, <=, 1);
|
||||
g_assert_cmpfloat (r->y, >=, 0);
|
||||
g_assert_cmpfloat (r->y, <=, 1);
|
||||
|
||||
g_assert_cmpfloat (r->x, <, r->x2);
|
||||
g_assert_cmpfloat (r->y, <, r->y2);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1,849 +0,0 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "gskgldriverprivate.h"
|
||||
|
||||
#include "gskdebugprivate.h"
|
||||
#include "gskprofilerprivate.h"
|
||||
#include "gdk/gdkglcontextprivate.h"
|
||||
#include "gdk/gdktextureprivate.h"
|
||||
#include "gdk/gdkgltextureprivate.h"
|
||||
#include "gdkmemorytextureprivate.h"
|
||||
|
||||
#include <gdk/gdk.h>
|
||||
#include <epoxy/gl.h>
|
||||
|
||||
typedef struct {
|
||||
GLuint fbo_id;
|
||||
GLuint depth_stencil_id;
|
||||
} Fbo;
|
||||
|
||||
typedef struct {
|
||||
GLuint texture_id;
|
||||
int width;
|
||||
int height;
|
||||
GLuint min_filter;
|
||||
GLuint mag_filter;
|
||||
Fbo fbo;
|
||||
GdkTexture *user;
|
||||
guint in_use : 1;
|
||||
guint permanent : 1;
|
||||
|
||||
/* TODO: Make this optional and not for every texture... */
|
||||
TextureSlice *slices;
|
||||
guint n_slices;
|
||||
} Texture;
|
||||
|
||||
struct _GskGLDriver
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GdkGLContext *gl_context;
|
||||
GskProfiler *profiler;
|
||||
struct {
|
||||
GQuark created_textures;
|
||||
GQuark reused_textures;
|
||||
GQuark surface_uploads;
|
||||
} counters;
|
||||
|
||||
Fbo default_fbo;
|
||||
|
||||
GHashTable *textures; /* texture_id -> Texture */
|
||||
GHashTable *pointer_textures; /* pointer -> texture_id */
|
||||
|
||||
const Texture *bound_source_texture;
|
||||
|
||||
int max_texture_size;
|
||||
|
||||
gboolean in_frame : 1;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GskGLDriver, gsk_gl_driver, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
upload_gdk_texture (GdkTexture *source_texture,
|
||||
int target,
|
||||
int x_offset,
|
||||
int y_offset,
|
||||
int width,
|
||||
int height)
|
||||
{
|
||||
cairo_surface_t *surface = NULL;
|
||||
GdkMemoryFormat data_format;
|
||||
const guchar *data;
|
||||
gsize data_stride;
|
||||
gsize bpp;
|
||||
|
||||
g_return_if_fail (source_texture != NULL);
|
||||
g_return_if_fail (x_offset + width <= gdk_texture_get_width (source_texture));
|
||||
g_return_if_fail (y_offset + height <= gdk_texture_get_height (source_texture));
|
||||
|
||||
/* Note: GdkGLTextures are already handled before we reach this and reused as-is */
|
||||
|
||||
if (GDK_IS_MEMORY_TEXTURE (source_texture))
|
||||
{
|
||||
GdkMemoryTexture *memory_texture = GDK_MEMORY_TEXTURE (source_texture);
|
||||
data = gdk_memory_texture_get_data (memory_texture);
|
||||
data_format = gdk_memory_texture_get_format (memory_texture);
|
||||
data_stride = gdk_memory_texture_get_stride (memory_texture);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Fall back to downloading to a surface */
|
||||
surface = gdk_texture_download_surface (source_texture);
|
||||
cairo_surface_flush (surface);
|
||||
data = cairo_image_surface_get_data (surface);
|
||||
data_format = GDK_MEMORY_DEFAULT;
|
||||
data_stride = cairo_image_surface_get_stride (surface);
|
||||
}
|
||||
|
||||
bpp = gdk_memory_format_bytes_per_pixel (data_format);
|
||||
|
||||
gdk_gl_context_upload_texture (gdk_gl_context_get_current (),
|
||||
data + x_offset * bpp + y_offset * data_stride,
|
||||
width, height, data_stride,
|
||||
data_format, target);
|
||||
|
||||
if (surface)
|
||||
cairo_surface_destroy (surface);
|
||||
}
|
||||
|
||||
static Texture *
|
||||
texture_new (void)
|
||||
{
|
||||
return g_slice_new0 (Texture);
|
||||
}
|
||||
|
||||
static inline void
|
||||
fbo_clear (const Fbo *f)
|
||||
{
|
||||
if (f->depth_stencil_id != 0)
|
||||
glDeleteRenderbuffers (1, &f->depth_stencil_id);
|
||||
|
||||
glDeleteFramebuffers (1, &f->fbo_id);
|
||||
}
|
||||
|
||||
static void
|
||||
texture_free (gpointer data)
|
||||
{
|
||||
Texture *t = data;
|
||||
guint i;
|
||||
|
||||
if (t->user)
|
||||
gdk_texture_clear_render_data (t->user);
|
||||
|
||||
if (t->fbo.fbo_id != 0)
|
||||
fbo_clear (&t->fbo);
|
||||
|
||||
if (t->texture_id != 0)
|
||||
{
|
||||
glDeleteTextures (1, &t->texture_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_assert_cmpint (t->n_slices, >, 0);
|
||||
|
||||
for (i = 0; i < t->n_slices; i ++)
|
||||
glDeleteTextures (1, &t->slices[i].texture_id);
|
||||
}
|
||||
|
||||
g_slice_free (Texture, t);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_gl_driver_set_texture_parameters (GskGLDriver *self,
|
||||
int min_filter,
|
||||
int mag_filter)
|
||||
{
|
||||
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter);
|
||||
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter);
|
||||
|
||||
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_gl_driver_finalize (GObject *gobject)
|
||||
{
|
||||
GskGLDriver *self = GSK_GL_DRIVER (gobject);
|
||||
|
||||
gdk_gl_context_make_current (self->gl_context);
|
||||
|
||||
g_clear_pointer (&self->textures, g_hash_table_unref);
|
||||
g_clear_pointer (&self->pointer_textures, g_hash_table_unref);
|
||||
g_clear_object (&self->profiler);
|
||||
|
||||
if (self->gl_context == gdk_gl_context_get_current ())
|
||||
gdk_gl_context_clear_current ();
|
||||
|
||||
G_OBJECT_CLASS (gsk_gl_driver_parent_class)->finalize (gobject);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_gl_driver_class_init (GskGLDriverClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->finalize = gsk_gl_driver_finalize;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_gl_driver_init (GskGLDriver *self)
|
||||
{
|
||||
self->textures = g_hash_table_new_full (NULL, NULL, NULL, texture_free);
|
||||
|
||||
self->max_texture_size = -1;
|
||||
|
||||
#ifdef G_ENABLE_DEBUG
|
||||
self->profiler = gsk_profiler_new ();
|
||||
self->counters.created_textures = gsk_profiler_add_counter (self->profiler,
|
||||
"created_textures",
|
||||
"Textures created this frame",
|
||||
TRUE);
|
||||
self->counters.reused_textures = gsk_profiler_add_counter (self->profiler,
|
||||
"reused_textures",
|
||||
"Textures reused this frame",
|
||||
TRUE);
|
||||
self->counters.surface_uploads = gsk_profiler_add_counter (self->profiler,
|
||||
"surface_uploads",
|
||||
"Texture uploads from surfaces this frame",
|
||||
TRUE);
|
||||
#endif
|
||||
}
|
||||
|
||||
GskGLDriver *
|
||||
gsk_gl_driver_new (GdkGLContext *context)
|
||||
{
|
||||
GskGLDriver *self;
|
||||
g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
|
||||
|
||||
self = (GskGLDriver *) g_object_new (GSK_TYPE_GL_DRIVER, NULL);
|
||||
self->gl_context = context;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void
|
||||
gsk_gl_driver_begin_frame (GskGLDriver *self)
|
||||
{
|
||||
g_return_if_fail (GSK_IS_GL_DRIVER (self));
|
||||
g_return_if_fail (!self->in_frame);
|
||||
|
||||
self->in_frame = TRUE;
|
||||
|
||||
if (self->max_texture_size < 0)
|
||||
{
|
||||
glGetIntegerv (GL_MAX_TEXTURE_SIZE, (GLint *) &self->max_texture_size);
|
||||
GSK_NOTE (OPENGL, g_message ("GL max texture size: %d", self->max_texture_size));
|
||||
}
|
||||
|
||||
glBindFramebuffer (GL_FRAMEBUFFER, 0);
|
||||
|
||||
glActiveTexture (GL_TEXTURE0);
|
||||
glBindTexture (GL_TEXTURE_2D, 0);
|
||||
|
||||
glActiveTexture (GL_TEXTURE0 + 1);
|
||||
glBindTexture (GL_TEXTURE_2D, 0);
|
||||
|
||||
glBindVertexArray (0);
|
||||
glUseProgram (0);
|
||||
|
||||
glActiveTexture (GL_TEXTURE0);
|
||||
|
||||
#ifdef G_ENABLE_DEBUG
|
||||
gsk_profiler_reset (self->profiler);
|
||||
#endif
|
||||
}
|
||||
|
||||
gboolean
|
||||
gsk_gl_driver_in_frame (GskGLDriver *self)
|
||||
{
|
||||
return self->in_frame;
|
||||
}
|
||||
|
||||
void
|
||||
gsk_gl_driver_end_frame (GskGLDriver *self)
|
||||
{
|
||||
g_return_if_fail (GSK_IS_GL_DRIVER (self));
|
||||
g_return_if_fail (self->in_frame);
|
||||
|
||||
self->bound_source_texture = NULL;
|
||||
|
||||
self->default_fbo.fbo_id = 0;
|
||||
|
||||
#ifdef G_ENABLE_DEBUG
|
||||
GSK_NOTE (OPENGL,
|
||||
g_message ("Textures created: %" G_GINT64_FORMAT "\n"
|
||||
" Textures reused: %" G_GINT64_FORMAT "\n"
|
||||
" Surface uploads: %" G_GINT64_FORMAT,
|
||||
gsk_profiler_counter_get (self->profiler, self->counters.created_textures),
|
||||
gsk_profiler_counter_get (self->profiler, self->counters.reused_textures),
|
||||
gsk_profiler_counter_get (self->profiler, self->counters.surface_uploads)));
|
||||
#endif
|
||||
|
||||
GSK_NOTE (OPENGL,
|
||||
g_message ("*** Frame end: textures=%d",
|
||||
g_hash_table_size (self->textures)));
|
||||
|
||||
self->in_frame = FALSE;
|
||||
}
|
||||
|
||||
int
|
||||
gsk_gl_driver_collect_textures (GskGLDriver *self)
|
||||
{
|
||||
GHashTableIter iter;
|
||||
gpointer value_p = NULL;
|
||||
int old_size;
|
||||
|
||||
g_return_val_if_fail (GSK_IS_GL_DRIVER (self), 0);
|
||||
g_return_val_if_fail (!self->in_frame, 0);
|
||||
|
||||
old_size = g_hash_table_size (self->textures);
|
||||
|
||||
g_hash_table_iter_init (&iter, self->textures);
|
||||
while (g_hash_table_iter_next (&iter, NULL, &value_p))
|
||||
{
|
||||
Texture *t = value_p;
|
||||
|
||||
if (t->user || t->permanent)
|
||||
continue;
|
||||
|
||||
if (t->in_use)
|
||||
{
|
||||
t->in_use = FALSE;
|
||||
|
||||
if (t->fbo.fbo_id != 0)
|
||||
{
|
||||
fbo_clear (&t->fbo);
|
||||
t->fbo.fbo_id = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Remove from self->pointer_textures. */
|
||||
/* TODO: Is there a better way for this? */
|
||||
if (self->pointer_textures)
|
||||
{
|
||||
GHashTableIter pointer_iter;
|
||||
gpointer value;
|
||||
gpointer p;
|
||||
|
||||
g_hash_table_iter_init (&pointer_iter, self->pointer_textures);
|
||||
while (g_hash_table_iter_next (&pointer_iter, &p, &value))
|
||||
{
|
||||
if (GPOINTER_TO_INT (value) == t->texture_id)
|
||||
{
|
||||
g_hash_table_iter_remove (&pointer_iter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g_hash_table_iter_remove (&iter);
|
||||
}
|
||||
}
|
||||
|
||||
return old_size - g_hash_table_size (self->textures);
|
||||
}
|
||||
|
||||
|
||||
GdkGLContext *
|
||||
gsk_gl_driver_get_gl_context (GskGLDriver *self)
|
||||
{
|
||||
return self->gl_context;
|
||||
}
|
||||
|
||||
int
|
||||
gsk_gl_driver_get_max_texture_size (GskGLDriver *self)
|
||||
{
|
||||
if (self->max_texture_size < 0)
|
||||
{
|
||||
if (gdk_gl_context_get_use_es (self->gl_context))
|
||||
return 2048;
|
||||
|
||||
return 1024;
|
||||
}
|
||||
|
||||
return self->max_texture_size;
|
||||
}
|
||||
|
||||
static Texture *
|
||||
gsk_gl_driver_get_texture (GskGLDriver *self,
|
||||
int texture_id)
|
||||
{
|
||||
Texture *t;
|
||||
|
||||
if (g_hash_table_lookup_extended (self->textures, GINT_TO_POINTER (texture_id), NULL, (gpointer *) &t))
|
||||
return t;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static Texture *
|
||||
create_texture (GskGLDriver *self,
|
||||
float fwidth,
|
||||
float fheight)
|
||||
{
|
||||
guint texture_id;
|
||||
Texture *t;
|
||||
int width = ceilf (fwidth);
|
||||
int height = ceilf (fheight);
|
||||
|
||||
g_assert (width > 0);
|
||||
g_assert (height > 0);
|
||||
|
||||
if (width > self->max_texture_size ||
|
||||
height > self->max_texture_size)
|
||||
{
|
||||
g_critical ("Texture %d x %d is bigger than supported texture limit of %d; clipping...",
|
||||
width, height,
|
||||
self->max_texture_size);
|
||||
|
||||
width = MIN (width, self->max_texture_size);
|
||||
height = MIN (height, self->max_texture_size);
|
||||
}
|
||||
|
||||
glGenTextures (1, &texture_id);
|
||||
t = texture_new ();
|
||||
t->texture_id = texture_id;
|
||||
t->width = width;
|
||||
t->height = height;
|
||||
t->min_filter = GL_NEAREST;
|
||||
t->mag_filter = GL_NEAREST;
|
||||
t->in_use = TRUE;
|
||||
g_hash_table_insert (self->textures, GINT_TO_POINTER (texture_id), t);
|
||||
#ifdef G_ENABLE_DEBUG
|
||||
gsk_profiler_counter_inc (self->profiler, self->counters.created_textures);
|
||||
#endif
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_gl_driver_release_texture (gpointer data)
|
||||
{
|
||||
Texture *t = data;
|
||||
|
||||
t->user = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
gsk_gl_driver_slice_texture (GskGLDriver *self,
|
||||
GdkTexture *texture,
|
||||
TextureSlice **out_slices,
|
||||
guint *out_n_slices)
|
||||
{
|
||||
const int max_texture_size = gsk_gl_driver_get_max_texture_size (self) / 4; // XXX Too much?
|
||||
const int cols = (texture->width / max_texture_size) + 1;
|
||||
const int rows = (texture->height / max_texture_size) + 1;
|
||||
int col, row;
|
||||
int x = 0, y = 0; /* Position in the texture */
|
||||
TextureSlice *slices;
|
||||
Texture *tex;
|
||||
|
||||
g_assert (texture->width > max_texture_size || texture->height > max_texture_size);
|
||||
|
||||
|
||||
tex = gdk_texture_get_render_data (texture, self);
|
||||
|
||||
if (tex != NULL)
|
||||
{
|
||||
g_assert (tex->n_slices > 0);
|
||||
*out_slices = tex->slices;
|
||||
*out_n_slices = tex->n_slices;
|
||||
return;
|
||||
}
|
||||
|
||||
slices = g_new0 (TextureSlice, cols * rows);
|
||||
|
||||
for (col = 0; col < cols; col ++)
|
||||
{
|
||||
const int slice_width = MIN (max_texture_size, texture->width - x);
|
||||
|
||||
for (row = 0; row < rows; row ++)
|
||||
{
|
||||
const int slice_height = MIN (max_texture_size, texture->height - y);
|
||||
const int slice_index = (col * rows) + row;
|
||||
guint texture_id;
|
||||
|
||||
glGenTextures (1, &texture_id);
|
||||
|
||||
#ifdef G_ENABLE_DEBUG
|
||||
gsk_profiler_counter_inc (self->profiler, self->counters.created_textures);
|
||||
#endif
|
||||
glBindTexture (GL_TEXTURE_2D, texture_id);
|
||||
gsk_gl_driver_set_texture_parameters (self, GL_NEAREST, GL_NEAREST);
|
||||
upload_gdk_texture (texture, GL_TEXTURE_2D, x, y, slice_width, slice_height);
|
||||
|
||||
#ifdef G_ENABLE_DEBUG
|
||||
gsk_profiler_counter_inc (self->profiler, self->counters.surface_uploads);
|
||||
#endif
|
||||
|
||||
slices[slice_index].rect = (GdkRectangle){x, y, slice_width, slice_height};
|
||||
slices[slice_index].texture_id = texture_id;
|
||||
|
||||
y += slice_height;
|
||||
}
|
||||
|
||||
y = 0;
|
||||
x += slice_width;
|
||||
}
|
||||
|
||||
/* Allocate one Texture for the entire thing. */
|
||||
tex = texture_new ();
|
||||
tex->width = texture->width;
|
||||
tex->height = texture->height;
|
||||
tex->min_filter = GL_NEAREST;
|
||||
tex->mag_filter = GL_NEAREST;
|
||||
tex->in_use = TRUE;
|
||||
tex->slices = slices;
|
||||
tex->n_slices = cols * rows;
|
||||
|
||||
/* Use texture_free as destroy notify here since we are not inserting this Texture
|
||||
* into self->textures! */
|
||||
gdk_texture_set_render_data (texture, self, tex, texture_free);
|
||||
|
||||
*out_slices = slices;
|
||||
*out_n_slices = cols * rows;
|
||||
}
|
||||
|
||||
int
|
||||
gsk_gl_driver_get_texture_for_texture (GskGLDriver *self,
|
||||
GdkTexture *texture,
|
||||
int min_filter,
|
||||
int mag_filter)
|
||||
{
|
||||
Texture *t;
|
||||
GdkTexture *downloaded_texture = NULL;
|
||||
GdkTexture *source_texture;
|
||||
|
||||
if (GDK_IS_GL_TEXTURE (texture))
|
||||
{
|
||||
GdkGLTexture *gl_texture = (GdkGLTexture *) texture;
|
||||
GdkGLContext *texture_context = gdk_gl_texture_get_context (gl_texture);
|
||||
|
||||
if (gdk_gl_context_is_shared (self->gl_context, texture_context))
|
||||
{
|
||||
/* A GL texture from the same GL context is a simple task... */
|
||||
return gdk_gl_texture_get_id (gl_texture);
|
||||
}
|
||||
else
|
||||
{
|
||||
cairo_surface_t *surface;
|
||||
|
||||
/* In this case, we have to temporarily make the texture's context the current one,
|
||||
* download its data into our context and then create a texture from it. */
|
||||
if (texture_context)
|
||||
gdk_gl_context_make_current (texture_context);
|
||||
|
||||
surface = gdk_texture_download_surface (texture);
|
||||
downloaded_texture = gdk_texture_new_for_surface (surface);
|
||||
cairo_surface_destroy (surface);
|
||||
|
||||
gdk_gl_context_make_current (self->gl_context);
|
||||
|
||||
source_texture = downloaded_texture;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
t = gdk_texture_get_render_data (texture, self);
|
||||
|
||||
if (t)
|
||||
{
|
||||
if (t->min_filter == min_filter && t->mag_filter == mag_filter)
|
||||
return t->texture_id;
|
||||
}
|
||||
|
||||
source_texture = texture;
|
||||
}
|
||||
|
||||
t = create_texture (self, gdk_texture_get_width (texture), gdk_texture_get_height (texture));
|
||||
|
||||
if (gdk_texture_set_render_data (texture, self, t, gsk_gl_driver_release_texture))
|
||||
t->user = texture;
|
||||
|
||||
gsk_gl_driver_bind_source_texture (self, t->texture_id);
|
||||
gsk_gl_driver_init_texture (self,
|
||||
t->texture_id,
|
||||
source_texture,
|
||||
min_filter,
|
||||
mag_filter);
|
||||
gdk_gl_context_label_object_printf (self->gl_context, GL_TEXTURE, t->texture_id,
|
||||
"GdkTexture<%p> %d", texture, t->texture_id);
|
||||
|
||||
if (downloaded_texture)
|
||||
g_object_unref (downloaded_texture);
|
||||
|
||||
return t->texture_id;
|
||||
}
|
||||
|
||||
static guint
|
||||
texture_key_hash (gconstpointer v)
|
||||
{
|
||||
const GskTextureKey *k = (GskTextureKey *)v;
|
||||
|
||||
return GPOINTER_TO_UINT (k->pointer)
|
||||
+ (guint)(k->scale_x * 100)
|
||||
+ (guint)(k->scale_y * 100)
|
||||
+ (guint)k->filter * 2 +
|
||||
+ (guint)k->pointer_is_child;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
texture_key_equal (gconstpointer v1, gconstpointer v2)
|
||||
{
|
||||
const GskTextureKey *k1 = (GskTextureKey *)v1;
|
||||
const GskTextureKey *k2 = (GskTextureKey *)v2;
|
||||
|
||||
return k1->pointer == k2->pointer &&
|
||||
k1->scale_x == k2->scale_x &&
|
||||
k1->scale_y == k2->scale_y &&
|
||||
k1->filter == k2->filter &&
|
||||
k1->pointer_is_child == k2->pointer_is_child &&
|
||||
(!k1->pointer_is_child || graphene_rect_equal (&k1->parent_rect, &k2->parent_rect));
|
||||
}
|
||||
|
||||
int
|
||||
gsk_gl_driver_get_texture_for_key (GskGLDriver *self,
|
||||
GskTextureKey *key)
|
||||
{
|
||||
int id = 0;
|
||||
|
||||
if (G_UNLIKELY (self->pointer_textures == NULL))
|
||||
self->pointer_textures = g_hash_table_new_full (texture_key_hash, texture_key_equal, g_free, NULL);
|
||||
|
||||
id = GPOINTER_TO_INT (g_hash_table_lookup (self->pointer_textures, key));
|
||||
|
||||
if (id != 0)
|
||||
{
|
||||
Texture *t;
|
||||
|
||||
t = g_hash_table_lookup (self->textures, GINT_TO_POINTER (id));
|
||||
|
||||
if (t != NULL)
|
||||
t->in_use = TRUE;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void
|
||||
gsk_gl_driver_set_texture_for_key (GskGLDriver *self,
|
||||
GskTextureKey *key,
|
||||
int texture_id)
|
||||
{
|
||||
GskTextureKey *k;
|
||||
|
||||
if (G_UNLIKELY (self->pointer_textures == NULL))
|
||||
self->pointer_textures = g_hash_table_new_full (texture_key_hash, texture_key_equal, g_free, NULL);
|
||||
|
||||
k = g_new (GskTextureKey, 1);
|
||||
*k = *key;
|
||||
|
||||
g_hash_table_insert (self->pointer_textures, k, GINT_TO_POINTER (texture_id));
|
||||
}
|
||||
|
||||
int
|
||||
gsk_gl_driver_create_texture (GskGLDriver *self,
|
||||
float width,
|
||||
float height)
|
||||
{
|
||||
Texture *t;
|
||||
|
||||
g_return_val_if_fail (GSK_IS_GL_DRIVER (self), -1);
|
||||
|
||||
t = create_texture (self, width, height);
|
||||
|
||||
return t->texture_id;
|
||||
}
|
||||
|
||||
void
|
||||
gsk_gl_driver_create_render_target (GskGLDriver *self,
|
||||
int width,
|
||||
int height,
|
||||
int min_filter,
|
||||
int mag_filter,
|
||||
int *out_texture_id,
|
||||
int *out_render_target_id)
|
||||
{
|
||||
GLuint fbo_id;
|
||||
Texture *texture;
|
||||
|
||||
g_return_if_fail (self->in_frame);
|
||||
|
||||
texture = create_texture (self, width, height);
|
||||
gsk_gl_driver_bind_source_texture (self, texture->texture_id);
|
||||
gsk_gl_driver_init_texture_empty (self, texture->texture_id, min_filter, mag_filter);
|
||||
|
||||
glGenFramebuffers (1, &fbo_id);
|
||||
glBindFramebuffer (GL_FRAMEBUFFER, fbo_id);
|
||||
glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->texture_id, 0);
|
||||
|
||||
#if 0
|
||||
if (add_depth_buffer || add_stencil_buffer)
|
||||
{
|
||||
glGenRenderbuffersEXT (1, &depth_stencil_buffer_id);
|
||||
gdk_gl_context_label_object_printf (self->gl_context, GL_RENDERBUFFER, depth_stencil_buffer_id,
|
||||
"%s buffer for %d", add_depth_buffer ? "Depth" : "Stencil", texture_id);
|
||||
}
|
||||
else
|
||||
depth_stencil_buffer_id = 0;
|
||||
|
||||
glBindRenderbuffer (GL_RENDERBUFFER, depth_stencil_buffer_id);
|
||||
|
||||
if (add_depth_buffer || add_stencil_buffer)
|
||||
{
|
||||
if (add_stencil_buffer)
|
||||
glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, t->width, t->height);
|
||||
else
|
||||
glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, t->width, t->height);
|
||||
|
||||
if (add_depth_buffer)
|
||||
glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
|
||||
GL_RENDERBUFFER, depth_stencil_buffer_id);
|
||||
|
||||
if (add_stencil_buffer)
|
||||
glFramebufferRenderbufferEXT (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
|
||||
GL_RENDERBUFFER, depth_stencil_buffer_id);
|
||||
texture->fbo.depth_stencil_id = depth_stencil_buffer_id;
|
||||
}
|
||||
#endif
|
||||
|
||||
texture->fbo.fbo_id = fbo_id;
|
||||
|
||||
g_assert_cmphex (glCheckFramebufferStatus (GL_FRAMEBUFFER), ==, GL_FRAMEBUFFER_COMPLETE);
|
||||
|
||||
glBindFramebuffer (GL_FRAMEBUFFER, self->default_fbo.fbo_id);
|
||||
|
||||
*out_texture_id = texture->texture_id;
|
||||
*out_render_target_id = fbo_id;
|
||||
}
|
||||
|
||||
/* Mark the texture permanent, meaning it won'e be reused by the GLDriver.
|
||||
* E.g. to store it in some other cache. */
|
||||
void
|
||||
gsk_gl_driver_mark_texture_permanent (GskGLDriver *self,
|
||||
int texture_id)
|
||||
{
|
||||
Texture *t = gsk_gl_driver_get_texture (self, texture_id);
|
||||
|
||||
g_assert (t != NULL);
|
||||
|
||||
t->permanent = TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
gsk_gl_driver_bind_source_texture (GskGLDriver *self,
|
||||
int texture_id)
|
||||
{
|
||||
Texture *t;
|
||||
|
||||
g_return_if_fail (GSK_IS_GL_DRIVER (self));
|
||||
g_return_if_fail (self->in_frame);
|
||||
|
||||
t = gsk_gl_driver_get_texture (self, texture_id);
|
||||
if (t == NULL)
|
||||
{
|
||||
g_critical ("No texture %d found.", texture_id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->bound_source_texture != t)
|
||||
{
|
||||
glActiveTexture (GL_TEXTURE0);
|
||||
glBindTexture (GL_TEXTURE_2D, t->texture_id);
|
||||
|
||||
self->bound_source_texture = t;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gsk_gl_driver_destroy_texture (GskGLDriver *self,
|
||||
int texture_id)
|
||||
{
|
||||
g_return_if_fail (GSK_IS_GL_DRIVER (self));
|
||||
|
||||
g_hash_table_remove (self->textures, GINT_TO_POINTER (texture_id));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
gsk_gl_driver_init_texture_empty (GskGLDriver *self,
|
||||
int texture_id,
|
||||
int min_filter,
|
||||
int mag_filter)
|
||||
{
|
||||
Texture *t;
|
||||
|
||||
g_return_if_fail (GSK_IS_GL_DRIVER (self));
|
||||
|
||||
t = gsk_gl_driver_get_texture (self, texture_id);
|
||||
if (t == NULL)
|
||||
{
|
||||
g_critical ("No texture %d found.", texture_id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->bound_source_texture != t)
|
||||
{
|
||||
g_critical ("You must bind the texture before initializing it.");
|
||||
return;
|
||||
}
|
||||
|
||||
t->min_filter = min_filter;
|
||||
t->mag_filter = mag_filter;
|
||||
|
||||
gsk_gl_driver_set_texture_parameters (self, t->min_filter, t->mag_filter);
|
||||
|
||||
if (gdk_gl_context_get_use_es (self->gl_context))
|
||||
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, t->width, t->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
||||
else
|
||||
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, t->width, t->height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
|
||||
|
||||
glBindTexture (GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
filter_uses_mipmaps (int filter)
|
||||
{
|
||||
return filter != GL_NEAREST && filter != GL_LINEAR;
|
||||
}
|
||||
|
||||
void
|
||||
gsk_gl_driver_init_texture (GskGLDriver *self,
|
||||
int texture_id,
|
||||
GdkTexture *texture,
|
||||
int min_filter,
|
||||
int mag_filter)
|
||||
{
|
||||
Texture *t;
|
||||
|
||||
g_return_if_fail (GSK_IS_GL_DRIVER (self));
|
||||
|
||||
t = gsk_gl_driver_get_texture (self, texture_id);
|
||||
if (t == NULL)
|
||||
{
|
||||
g_critical ("No texture %d found.", texture_id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->bound_source_texture != t)
|
||||
{
|
||||
g_critical ("You must bind the texture before initializing it.");
|
||||
return;
|
||||
}
|
||||
|
||||
gsk_gl_driver_set_texture_parameters (self, min_filter, mag_filter);
|
||||
|
||||
upload_gdk_texture (texture, GL_TEXTURE_2D, 0, 0, t->width, t->height);
|
||||
|
||||
#ifdef G_ENABLE_DEBUG
|
||||
gsk_profiler_counter_inc (self->profiler, self->counters.surface_uploads);
|
||||
#endif
|
||||
|
||||
t->min_filter = min_filter;
|
||||
t->mag_filter = mag_filter;
|
||||
|
||||
if (filter_uses_mipmaps (t->min_filter))
|
||||
glGenerateMipmap (GL_TEXTURE_2D);
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
#ifndef __GSK_GL_DRIVER_PRIVATE_H__
|
||||
#define __GSK_GL_DRIVER_PRIVATE_H__
|
||||
|
||||
#include <cairo.h>
|
||||
#include <gdk/gdk.h>
|
||||
#include <graphene.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GSK_TYPE_GL_DRIVER (gsk_gl_driver_get_type ())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (GskGLDriver, gsk_gl_driver, GSK, GL_DRIVER, GObject)
|
||||
|
||||
typedef struct {
|
||||
float position[2];
|
||||
float uv[2];
|
||||
} GskQuadVertex;
|
||||
|
||||
typedef struct {
|
||||
cairo_rectangle_int_t rect;
|
||||
guint texture_id;
|
||||
} TextureSlice;
|
||||
|
||||
typedef struct {
|
||||
gpointer pointer;
|
||||
float scale_x;
|
||||
float scale_y;
|
||||
int filter;
|
||||
int pointer_is_child;
|
||||
graphene_rect_t parent_rect; /* Only set if pointer_is_child */
|
||||
} GskTextureKey;
|
||||
|
||||
GskGLDriver * gsk_gl_driver_new (GdkGLContext *context);
|
||||
GdkGLContext *gsk_gl_driver_get_gl_context (GskGLDriver *driver);
|
||||
|
||||
int gsk_gl_driver_get_max_texture_size (GskGLDriver *driver);
|
||||
|
||||
void gsk_gl_driver_begin_frame (GskGLDriver *driver);
|
||||
void gsk_gl_driver_end_frame (GskGLDriver *driver);
|
||||
gboolean gsk_gl_driver_in_frame (GskGLDriver *driver);
|
||||
int gsk_gl_driver_get_texture_for_texture (GskGLDriver *driver,
|
||||
GdkTexture *texture,
|
||||
int min_filter,
|
||||
int mag_filter);
|
||||
int gsk_gl_driver_get_texture_for_key (GskGLDriver *driver,
|
||||
GskTextureKey *key);
|
||||
void gsk_gl_driver_set_texture_for_key (GskGLDriver *driver,
|
||||
GskTextureKey *key,
|
||||
int texture_id);
|
||||
int gsk_gl_driver_create_texture (GskGLDriver *driver,
|
||||
float width,
|
||||
float height);
|
||||
void gsk_gl_driver_create_render_target (GskGLDriver *driver,
|
||||
int width,
|
||||
int height,
|
||||
int min_filter,
|
||||
int mag_filter,
|
||||
int *out_texture_id,
|
||||
int *out_render_target_id);
|
||||
void gsk_gl_driver_mark_texture_permanent (GskGLDriver *self,
|
||||
int texture_id);
|
||||
void gsk_gl_driver_bind_source_texture (GskGLDriver *driver,
|
||||
int texture_id);
|
||||
|
||||
void gsk_gl_driver_init_texture_empty (GskGLDriver *driver,
|
||||
int texture_id,
|
||||
int min_filter,
|
||||
int max_filter);
|
||||
void gsk_gl_driver_init_texture (GskGLDriver *driver,
|
||||
int texture_id,
|
||||
GdkTexture *texture,
|
||||
int min_filter,
|
||||
int mag_filter);
|
||||
|
||||
void gsk_gl_driver_destroy_texture (GskGLDriver *driver,
|
||||
int texture_id);
|
||||
|
||||
int gsk_gl_driver_collect_textures (GskGLDriver *driver);
|
||||
void gsk_gl_driver_slice_texture (GskGLDriver *self,
|
||||
GdkTexture *texture,
|
||||
TextureSlice **out_slices,
|
||||
guint *out_n_slices);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSK_GL_DRIVER_PRIVATE_H__ */
|
||||
@@ -1,397 +0,0 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "gskglglyphcacheprivate.h"
|
||||
#include "gskgldriverprivate.h"
|
||||
#include "gskdebugprivate.h"
|
||||
#include "gskprivate.h"
|
||||
#include "gskgltextureatlasprivate.h"
|
||||
|
||||
#include "gdk/gdkglcontextprivate.h"
|
||||
#include "gdk/gdkmemorytextureprivate.h"
|
||||
|
||||
#include <graphene.h>
|
||||
#include <cairo.h>
|
||||
#include <epoxy/gl.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Cache eviction strategy
|
||||
*
|
||||
* We mark glyphs as accessed every time we use them.
|
||||
* Every few frames, we mark glyphs that haven't been
|
||||
* accessed since the last check as old.
|
||||
*
|
||||
* We keep count of the pixels of each atlas that are
|
||||
* taken up by old data. When the fraction of old pixels
|
||||
* gets too high, we drop the atlas and all the items it
|
||||
* contained.
|
||||
*
|
||||
* Big glyphs are not stored in the atlas, they get their
|
||||
* own texture, but they are still cached.
|
||||
*/
|
||||
|
||||
#define MAX_FRAME_AGE (60)
|
||||
#define MAX_GLYPH_SIZE 128 /* Will get its own texture if bigger */
|
||||
|
||||
static guint glyph_cache_hash (gconstpointer v);
|
||||
static gboolean glyph_cache_equal (gconstpointer v1,
|
||||
gconstpointer v2);
|
||||
static void glyph_cache_key_free (gpointer v);
|
||||
static void glyph_cache_value_free (gpointer v);
|
||||
|
||||
GskGLGlyphCache *
|
||||
gsk_gl_glyph_cache_new (GdkDisplay *display,
|
||||
GskGLTextureAtlases *atlases)
|
||||
{
|
||||
GskGLGlyphCache *glyph_cache;
|
||||
|
||||
glyph_cache = g_new0 (GskGLGlyphCache, 1);
|
||||
|
||||
glyph_cache->display = display;
|
||||
glyph_cache->hash_table = g_hash_table_new_full (glyph_cache_hash, glyph_cache_equal,
|
||||
glyph_cache_key_free, glyph_cache_value_free);
|
||||
|
||||
glyph_cache->atlases = gsk_gl_texture_atlases_ref (atlases);
|
||||
|
||||
glyph_cache->ref_count = 1;
|
||||
|
||||
return glyph_cache;
|
||||
}
|
||||
|
||||
GskGLGlyphCache *
|
||||
gsk_gl_glyph_cache_ref (GskGLGlyphCache *self)
|
||||
{
|
||||
self->ref_count++;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void
|
||||
gsk_gl_glyph_cache_unref (GskGLGlyphCache *self)
|
||||
{
|
||||
g_assert (self->ref_count > 0);
|
||||
|
||||
if (self->ref_count == 1)
|
||||
{
|
||||
gsk_gl_texture_atlases_unref (self->atlases);
|
||||
g_hash_table_unref (self->hash_table);
|
||||
g_free (self);
|
||||
return;
|
||||
}
|
||||
|
||||
self->ref_count--;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
glyph_cache_equal (gconstpointer v1, gconstpointer v2)
|
||||
{
|
||||
return memcmp (v1, v2, sizeof (CacheKeyData)) == 0;
|
||||
}
|
||||
|
||||
static guint
|
||||
glyph_cache_hash (gconstpointer v)
|
||||
{
|
||||
const GlyphCacheKey *key = v;
|
||||
|
||||
return key->hash;
|
||||
}
|
||||
|
||||
static void
|
||||
glyph_cache_key_free (gpointer v)
|
||||
{
|
||||
GlyphCacheKey *f = v;
|
||||
|
||||
g_object_unref (f->data.font);
|
||||
g_free (f);
|
||||
}
|
||||
|
||||
static void
|
||||
glyph_cache_value_free (gpointer v)
|
||||
{
|
||||
g_free (v);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
render_glyph (GlyphCacheKey *key,
|
||||
GskGLCachedGlyph *value,
|
||||
GskImageRegion *region)
|
||||
{
|
||||
cairo_surface_t *surface;
|
||||
cairo_t *cr;
|
||||
cairo_scaled_font_t *scaled_font;
|
||||
PangoGlyphString glyph_string;
|
||||
PangoGlyphInfo glyph_info;
|
||||
int surface_width, surface_height;
|
||||
int stride;
|
||||
unsigned char *data;
|
||||
|
||||
scaled_font = pango_cairo_font_get_scaled_font ((PangoCairoFont *)key->data.font);
|
||||
if (G_UNLIKELY (!scaled_font || cairo_scaled_font_status (scaled_font) != CAIRO_STATUS_SUCCESS))
|
||||
{
|
||||
g_warning ("Failed to get a font");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
surface_width = value->draw_width * key->data.scale / 1024;
|
||||
surface_height = value->draw_height * key->data.scale / 1024;
|
||||
|
||||
stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, surface_width);
|
||||
data = g_malloc0 (stride * surface_height);
|
||||
surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32,
|
||||
surface_width, surface_height,
|
||||
stride);
|
||||
cairo_surface_set_device_scale (surface, key->data.scale / 1024.0, key->data.scale / 1024.0);
|
||||
|
||||
cr = cairo_create (surface);
|
||||
|
||||
cairo_set_scaled_font (cr, scaled_font);
|
||||
cairo_set_source_rgba (cr, 1, 1, 1, 1);
|
||||
|
||||
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 = 256 * key->data.xshift;
|
||||
else
|
||||
glyph_info.geometry.x_offset = 256 * key->data.xshift - value->draw_x * 1024;
|
||||
glyph_info.geometry.y_offset = 256 * key->data.yshift - value->draw_y * 1024;
|
||||
|
||||
glyph_string.num_glyphs = 1;
|
||||
glyph_string.glyphs = &glyph_info;
|
||||
|
||||
pango_cairo_show_glyph_string (cr, key->data.font, &glyph_string);
|
||||
cairo_destroy (cr);
|
||||
|
||||
cairo_surface_flush (surface);
|
||||
|
||||
region->width = cairo_image_surface_get_width (surface);
|
||||
region->height = cairo_image_surface_get_height (surface);
|
||||
region->stride = cairo_image_surface_get_stride (surface);
|
||||
region->data = data;
|
||||
if (value->atlas)
|
||||
{
|
||||
region->x = (gsize)(value->tx * value->atlas->width);
|
||||
region->y = (gsize)(value->ty * value->atlas->height);
|
||||
}
|
||||
else
|
||||
{
|
||||
region->x = 0;
|
||||
region->y = 0;
|
||||
}
|
||||
|
||||
cairo_surface_destroy (surface);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
upload_glyph (GlyphCacheKey *key,
|
||||
GskGLCachedGlyph *value)
|
||||
{
|
||||
GskImageRegion r;
|
||||
guchar *pixel_data;
|
||||
guchar *free_data = NULL;
|
||||
guint gl_format;
|
||||
guint gl_type;
|
||||
|
||||
gdk_gl_context_push_debug_group_printf (gdk_gl_context_get_current (),
|
||||
"Uploading glyph %d",
|
||||
key->data.glyph);
|
||||
|
||||
if (render_glyph (key, value, &r))
|
||||
{
|
||||
glPixelStorei (GL_UNPACK_ROW_LENGTH, r.stride / 4);
|
||||
glBindTexture (GL_TEXTURE_2D, value->texture_id);
|
||||
|
||||
if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ()))
|
||||
{
|
||||
pixel_data = free_data = g_malloc (r.width * r.height * 4);
|
||||
gdk_memory_convert (pixel_data, r.width * 4,
|
||||
GDK_MEMORY_R8G8B8A8_PREMULTIPLIED,
|
||||
r.data, r.width * 4,
|
||||
GDK_MEMORY_DEFAULT, r.width, r.height);
|
||||
gl_format = GL_RGBA;
|
||||
gl_type = GL_UNSIGNED_BYTE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pixel_data = r.data;
|
||||
gl_format = GL_BGRA;
|
||||
gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
|
||||
}
|
||||
|
||||
glTexSubImage2D (GL_TEXTURE_2D, 0, r.x, r.y, r.width, r.height,
|
||||
gl_format, gl_type, pixel_data);
|
||||
glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
|
||||
g_free (r.data);
|
||||
g_free (free_data);
|
||||
}
|
||||
|
||||
gdk_gl_context_pop_debug_group (gdk_gl_context_get_current ());
|
||||
}
|
||||
|
||||
static void
|
||||
add_to_cache (GskGLGlyphCache *self,
|
||||
GlyphCacheKey *key,
|
||||
GskGLDriver *driver,
|
||||
GskGLCachedGlyph *value)
|
||||
{
|
||||
const int width = value->draw_width * key->data.scale / 1024;
|
||||
const int height = value->draw_height * key->data.scale / 1024;
|
||||
|
||||
if (width < MAX_GLYPH_SIZE && height < MAX_GLYPH_SIZE)
|
||||
{
|
||||
GskGLTextureAtlas *atlas = NULL;
|
||||
int packed_x = 0;
|
||||
int packed_y = 0;
|
||||
|
||||
gsk_gl_texture_atlases_pack (self->atlases, width + 2, height + 2, &atlas, &packed_x, &packed_y);
|
||||
|
||||
value->tx = (float)(packed_x + 1) / atlas->width;
|
||||
value->ty = (float)(packed_y + 1) / atlas->height;
|
||||
value->tw = (float)width / atlas->width;
|
||||
value->th = (float)height / atlas->height;
|
||||
value->used = TRUE;
|
||||
|
||||
value->atlas = atlas;
|
||||
value->texture_id = atlas->texture_id;
|
||||
}
|
||||
else
|
||||
{
|
||||
value->atlas = NULL;
|
||||
value->texture_id = gsk_gl_driver_create_texture (driver, width, height);
|
||||
gsk_gl_driver_mark_texture_permanent (driver, value->texture_id);
|
||||
|
||||
gsk_gl_driver_bind_source_texture (driver, value->texture_id);
|
||||
gsk_gl_driver_init_texture_empty (driver, value->texture_id, GL_LINEAR, GL_LINEAR);
|
||||
|
||||
value->tx = 0.0f;
|
||||
value->ty = 0.0f;
|
||||
value->tw = 1.0f;
|
||||
value->th = 1.0f;
|
||||
}
|
||||
|
||||
upload_glyph (key, value);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_gl_glyph_cache_lookup_or_add (GskGLGlyphCache *cache,
|
||||
GlyphCacheKey *lookup,
|
||||
GskGLDriver *driver,
|
||||
const GskGLCachedGlyph **cached_glyph_out)
|
||||
{
|
||||
GskGLCachedGlyph *value;
|
||||
|
||||
value = g_hash_table_lookup (cache->hash_table, lookup);
|
||||
|
||||
if (value)
|
||||
{
|
||||
if (value->atlas && !value->used)
|
||||
{
|
||||
gsk_gl_texture_atlas_mark_used (value->atlas, value->draw_width, value->draw_height);
|
||||
value->used = TRUE;
|
||||
}
|
||||
value->accessed = TRUE;
|
||||
|
||||
*cached_glyph_out = value;
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
GlyphCacheKey *key;
|
||||
PangoRectangle ink_rect;
|
||||
|
||||
pango_font_get_glyph_extents (lookup->data.font, lookup->data.glyph, &ink_rect, NULL);
|
||||
pango_extents_to_pixels (&ink_rect, NULL);
|
||||
if (lookup->data.xshift != 0)
|
||||
ink_rect.width += 1;
|
||||
if (lookup->data.yshift != 0)
|
||||
ink_rect.height += 1;
|
||||
|
||||
value = g_new0 (GskGLCachedGlyph, 1);
|
||||
|
||||
value->draw_x = ink_rect.x;
|
||||
value->draw_y = ink_rect.y;
|
||||
value->draw_width = ink_rect.width;
|
||||
value->draw_height = ink_rect.height;
|
||||
value->accessed = TRUE;
|
||||
value->atlas = NULL; /* For now */
|
||||
|
||||
key = g_new0 (GlyphCacheKey, 1);
|
||||
|
||||
key->data.font = g_object_ref (lookup->data.font);
|
||||
key->data.glyph = lookup->data.glyph;
|
||||
key->data.xshift = lookup->data.xshift;
|
||||
key->data.yshift = lookup->data.yshift;
|
||||
key->data.scale = lookup->data.scale;
|
||||
key->hash = lookup->hash;
|
||||
|
||||
if (key->data.scale > 0 &&
|
||||
value->draw_width * key->data.scale / 1024 > 0 &&
|
||||
value->draw_height * key->data.scale / 1024 > 0)
|
||||
add_to_cache (cache, key, driver, value);
|
||||
|
||||
*cached_glyph_out = value;
|
||||
g_hash_table_insert (cache->hash_table, key, value);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gsk_gl_glyph_cache_begin_frame (GskGLGlyphCache *self,
|
||||
GskGLDriver *driver,
|
||||
GPtrArray *removed_atlases)
|
||||
{
|
||||
GHashTableIter iter;
|
||||
GlyphCacheKey *key;
|
||||
GskGLCachedGlyph *value;
|
||||
guint dropped = 0;
|
||||
|
||||
self->timestamp++;
|
||||
|
||||
if (removed_atlases->len > 0)
|
||||
{
|
||||
g_hash_table_iter_init (&iter, self->hash_table);
|
||||
while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value))
|
||||
{
|
||||
if (g_ptr_array_find (removed_atlases, value->atlas, NULL))
|
||||
{
|
||||
g_hash_table_iter_remove (&iter);
|
||||
dropped++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (self->timestamp % MAX_FRAME_AGE == 30)
|
||||
{
|
||||
g_hash_table_iter_init (&iter, self->hash_table);
|
||||
while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value))
|
||||
{
|
||||
if (!value->accessed)
|
||||
{
|
||||
if (value->atlas)
|
||||
{
|
||||
if (value->used)
|
||||
{
|
||||
gsk_gl_texture_atlas_mark_unused (value->atlas, value->draw_width, value->draw_height);
|
||||
value->used = FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gsk_gl_driver_destroy_texture (driver, value->texture_id);
|
||||
g_hash_table_iter_remove (&iter);
|
||||
|
||||
/* Sadly, if we drop an atlas-less cached glyph, we
|
||||
* have to treat it like a dropped atlas and purge
|
||||
* text node render data.
|
||||
*/
|
||||
dropped++;
|
||||
}
|
||||
}
|
||||
else
|
||||
value->accessed = FALSE;
|
||||
}
|
||||
|
||||
GSK_NOTE(GLYPH_CACHE, g_message ("%d glyphs cached", g_hash_table_size (self->hash_table)));
|
||||
}
|
||||
|
||||
GSK_NOTE(GLYPH_CACHE, if (dropped > 0) g_message ("Dropped %d glyphs", dropped));
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
#ifndef __GSK_GL_GLYPH_CACHE_PRIVATE_H__
|
||||
#define __GSK_GL_GLYPH_CACHE_PRIVATE_H__
|
||||
|
||||
#include "gskgldriverprivate.h"
|
||||
#include "gskglimageprivate.h"
|
||||
#include "gskgltextureatlasprivate.h"
|
||||
#include <pango/pango.h>
|
||||
#include <gdk/gdk.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int ref_count;
|
||||
|
||||
GdkDisplay *display;
|
||||
GHashTable *hash_table;
|
||||
GskGLTextureAtlases *atlases;
|
||||
|
||||
int timestamp;
|
||||
} GskGLGlyphCache;
|
||||
|
||||
struct _CacheKeyData
|
||||
{
|
||||
PangoFont *font;
|
||||
PangoGlyph glyph;
|
||||
guint xshift : 3;
|
||||
guint yshift : 3;
|
||||
guint scale : 26; /* times 1024 */
|
||||
};
|
||||
|
||||
typedef struct _CacheKeyData CacheKeyData;
|
||||
|
||||
struct _GlyphCacheKey
|
||||
{
|
||||
CacheKeyData data;
|
||||
guint hash;
|
||||
};
|
||||
|
||||
typedef struct _GlyphCacheKey GlyphCacheKey;
|
||||
|
||||
#define PHASE(x) ((int)(floor (4 * (x + 0.125)) - 4 * floor (x + 0.125)))
|
||||
|
||||
static inline void
|
||||
glyph_cache_key_set_glyph_and_shift (GlyphCacheKey *key,
|
||||
PangoGlyph glyph,
|
||||
float x,
|
||||
float y)
|
||||
{
|
||||
key->data.glyph = glyph;
|
||||
key->data.xshift = PHASE (x);
|
||||
key->data.yshift = PHASE (y);
|
||||
key->hash = GPOINTER_TO_UINT (key->data.font) ^
|
||||
key->data.glyph ^
|
||||
(key->data.xshift << 24) ^
|
||||
(key->data.yshift << 26) ^
|
||||
key->data.scale;
|
||||
}
|
||||
|
||||
typedef struct _GskGLCachedGlyph GskGLCachedGlyph;
|
||||
|
||||
struct _GskGLCachedGlyph
|
||||
{
|
||||
GskGLTextureAtlas *atlas;
|
||||
guint texture_id;
|
||||
|
||||
float tx;
|
||||
float ty;
|
||||
float tw;
|
||||
float th;
|
||||
|
||||
int draw_x;
|
||||
int draw_y;
|
||||
int draw_width;
|
||||
int draw_height;
|
||||
|
||||
guint accessed : 1; /* accessed since last check */
|
||||
guint used : 1; /* accounted as used in the atlas */
|
||||
};
|
||||
|
||||
|
||||
GskGLGlyphCache * gsk_gl_glyph_cache_new (GdkDisplay *display,
|
||||
GskGLTextureAtlases *atlases);
|
||||
GskGLGlyphCache * gsk_gl_glyph_cache_ref (GskGLGlyphCache *self);
|
||||
void gsk_gl_glyph_cache_unref (GskGLGlyphCache *self);
|
||||
void gsk_gl_glyph_cache_begin_frame (GskGLGlyphCache *self,
|
||||
GskGLDriver *driver,
|
||||
GPtrArray *removed_atlases);
|
||||
void gsk_gl_glyph_cache_lookup_or_add (GskGLGlyphCache *self,
|
||||
GlyphCacheKey *lookup,
|
||||
GskGLDriver *driver,
|
||||
const GskGLCachedGlyph **cached_glyph_out);
|
||||
|
||||
#endif
|
||||
@@ -1,275 +0,0 @@
|
||||
#include "gskgliconcacheprivate.h"
|
||||
#include "gskgltextureatlasprivate.h"
|
||||
#include "gdk/gdktextureprivate.h"
|
||||
#include "gdk/gdkmemorytextureprivate.h"
|
||||
#include "gdk/gdkglcontextprivate.h"
|
||||
|
||||
#include <epoxy/gl.h>
|
||||
|
||||
#define MAX_FRAME_AGE 60
|
||||
|
||||
static void
|
||||
icon_data_free (gpointer p)
|
||||
{
|
||||
g_object_unref (((IconData *)p)->source_texture);
|
||||
g_free (p);
|
||||
}
|
||||
|
||||
GskGLIconCache *
|
||||
gsk_gl_icon_cache_new (GdkDisplay *display,
|
||||
GskGLTextureAtlases *atlases)
|
||||
{
|
||||
GskGLIconCache *self;
|
||||
|
||||
self = g_new0 (GskGLIconCache, 1);
|
||||
|
||||
self->display = display;
|
||||
self->icons = g_hash_table_new_full (NULL, NULL, NULL, icon_data_free);
|
||||
self->atlases = gsk_gl_texture_atlases_ref (atlases);
|
||||
self->ref_count = 1;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
GskGLIconCache *
|
||||
gsk_gl_icon_cache_ref (GskGLIconCache *self)
|
||||
{
|
||||
self->ref_count++;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void
|
||||
gsk_gl_icon_cache_unref (GskGLIconCache *self)
|
||||
{
|
||||
g_assert (self->ref_count > 0);
|
||||
|
||||
if (self->ref_count == 1)
|
||||
{
|
||||
gsk_gl_texture_atlases_unref (self->atlases);
|
||||
g_hash_table_unref (self->icons);
|
||||
g_free (self);
|
||||
return;
|
||||
}
|
||||
|
||||
self->ref_count--;
|
||||
}
|
||||
|
||||
void
|
||||
gsk_gl_icon_cache_begin_frame (GskGLIconCache *self,
|
||||
GPtrArray *removed_atlases)
|
||||
{
|
||||
GHashTableIter iter;
|
||||
GdkTexture *texture;
|
||||
IconData *icon_data;
|
||||
|
||||
self->timestamp++;
|
||||
|
||||
/* Drop icons on removed atlases */
|
||||
if (removed_atlases->len > 0)
|
||||
{
|
||||
guint dropped = 0;
|
||||
|
||||
g_hash_table_iter_init (&iter, self->icons);
|
||||
while (g_hash_table_iter_next (&iter, (gpointer *)&texture, (gpointer *)&icon_data))
|
||||
{
|
||||
if (g_ptr_array_find (removed_atlases, icon_data->atlas, NULL))
|
||||
{
|
||||
g_hash_table_iter_remove (&iter);
|
||||
dropped++;
|
||||
}
|
||||
}
|
||||
|
||||
GSK_NOTE(GLYPH_CACHE, if (dropped > 0) g_message ("Dropped %d icons", dropped));
|
||||
}
|
||||
|
||||
if (self->timestamp % MAX_FRAME_AGE == 0)
|
||||
{
|
||||
g_hash_table_iter_init (&iter, self->icons);
|
||||
while (g_hash_table_iter_next (&iter, (gpointer *)&texture, (gpointer *)&icon_data))
|
||||
{
|
||||
if (!icon_data->accessed)
|
||||
{
|
||||
if (icon_data->used)
|
||||
{
|
||||
const int width = icon_data->source_texture->width;
|
||||
const int height = icon_data->source_texture->height;
|
||||
gsk_gl_texture_atlas_mark_unused (icon_data->atlas, width + 2, height + 2);
|
||||
icon_data->used = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
icon_data->accessed = FALSE;
|
||||
}
|
||||
|
||||
GSK_NOTE(GLYPH_CACHE, g_message ("%d icons cached", g_hash_table_size (self->icons)));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gsk_gl_icon_cache_lookup_or_add (GskGLIconCache *self,
|
||||
GdkTexture *texture,
|
||||
const IconData **out_icon_data)
|
||||
{
|
||||
IconData *icon_data = g_hash_table_lookup (self->icons, texture);
|
||||
|
||||
if (icon_data)
|
||||
{
|
||||
if (!icon_data->used)
|
||||
{
|
||||
gsk_gl_texture_atlas_mark_used (icon_data->atlas, texture->width + 2, texture->height + 2);
|
||||
icon_data->used = TRUE;
|
||||
}
|
||||
icon_data->accessed = TRUE;
|
||||
|
||||
*out_icon_data = icon_data;
|
||||
return;
|
||||
}
|
||||
|
||||
/* texture not on any atlas yet. Find a suitable one. */
|
||||
{
|
||||
const int width = texture->width;
|
||||
const int height = texture->height;
|
||||
GskGLTextureAtlas *atlas = NULL;
|
||||
int packed_x = 0;
|
||||
int packed_y = 0;
|
||||
cairo_surface_t *surface;
|
||||
unsigned char *surface_data;
|
||||
unsigned char *pixel_data;
|
||||
guchar *free_data = NULL;
|
||||
guint gl_format;
|
||||
guint gl_type;
|
||||
|
||||
gsk_gl_texture_atlases_pack (self->atlases, width + 2, height + 2, &atlas, &packed_x, &packed_y);
|
||||
|
||||
icon_data = g_new0 (IconData, 1);
|
||||
icon_data->atlas = atlas;
|
||||
icon_data->accessed = TRUE;
|
||||
icon_data->used = TRUE;
|
||||
icon_data->texture_id = atlas->texture_id;
|
||||
icon_data->source_texture = g_object_ref (texture);
|
||||
icon_data->x = (float)(packed_x + 1) / atlas->width;
|
||||
icon_data->y = (float)(packed_y + 1) / atlas->width;
|
||||
icon_data->x2 = icon_data->x + (float)width / atlas->width;
|
||||
icon_data->y2 = icon_data->y + (float)height / atlas->height;
|
||||
|
||||
g_hash_table_insert (self->icons, texture, icon_data);
|
||||
|
||||
/* actually upload the texture */
|
||||
surface = gdk_texture_download_surface (texture);
|
||||
surface_data = cairo_image_surface_get_data (surface);
|
||||
gdk_gl_context_push_debug_group_printf (gdk_gl_context_get_current (),
|
||||
"Uploading texture");
|
||||
|
||||
if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ()))
|
||||
{
|
||||
pixel_data = free_data = g_malloc (width * height * 4);
|
||||
gdk_memory_convert (pixel_data, width * 4,
|
||||
GDK_MEMORY_R8G8B8A8_PREMULTIPLIED,
|
||||
surface_data, cairo_image_surface_get_stride (surface),
|
||||
GDK_MEMORY_DEFAULT, width, height);
|
||||
gl_format = GL_RGBA;
|
||||
gl_type = GL_UNSIGNED_BYTE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pixel_data = surface_data;
|
||||
gl_format = GL_BGRA;
|
||||
gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
|
||||
}
|
||||
|
||||
glBindTexture (GL_TEXTURE_2D, atlas->texture_id);
|
||||
|
||||
glTexSubImage2D (GL_TEXTURE_2D, 0,
|
||||
packed_x + 1, packed_y + 1,
|
||||
width, height,
|
||||
gl_format, gl_type,
|
||||
pixel_data);
|
||||
/* Padding top */
|
||||
glTexSubImage2D (GL_TEXTURE_2D, 0,
|
||||
packed_x + 1, packed_y,
|
||||
width, 1,
|
||||
gl_format, gl_type,
|
||||
pixel_data);
|
||||
/* Padding left */
|
||||
glTexSubImage2D (GL_TEXTURE_2D, 0,
|
||||
packed_x, packed_y + 1,
|
||||
1, height,
|
||||
gl_format, gl_type,
|
||||
pixel_data);
|
||||
/* Padding top left */
|
||||
glTexSubImage2D (GL_TEXTURE_2D, 0,
|
||||
packed_x, packed_y,
|
||||
1, 1,
|
||||
gl_format, gl_type,
|
||||
pixel_data);
|
||||
|
||||
/* Padding right */
|
||||
glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
|
||||
glPixelStorei (GL_UNPACK_SKIP_PIXELS, width - 1);
|
||||
glTexSubImage2D (GL_TEXTURE_2D, 0,
|
||||
packed_x + width + 1, packed_y + 1,
|
||||
1, height,
|
||||
gl_format, gl_type,
|
||||
pixel_data);
|
||||
/* Padding top right */
|
||||
glTexSubImage2D (GL_TEXTURE_2D, 0,
|
||||
packed_x + width + 1, packed_y,
|
||||
1, 1,
|
||||
gl_format, gl_type,
|
||||
pixel_data);
|
||||
/* Padding bottom */
|
||||
glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0);
|
||||
glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
|
||||
glPixelStorei (GL_UNPACK_SKIP_ROWS, height - 1);
|
||||
glTexSubImage2D (GL_TEXTURE_2D, 0,
|
||||
packed_x + 1, packed_y + 1 + height,
|
||||
width, 1,
|
||||
gl_format, gl_type,
|
||||
pixel_data);
|
||||
/* Padding bottom left */
|
||||
glTexSubImage2D (GL_TEXTURE_2D, 0,
|
||||
packed_x, packed_y + 1 + height,
|
||||
1, 1,
|
||||
gl_format, gl_type,
|
||||
pixel_data);
|
||||
/* Padding bottom right */
|
||||
glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
|
||||
glPixelStorei (GL_UNPACK_SKIP_PIXELS, width - 1);
|
||||
glTexSubImage2D (GL_TEXTURE_2D, 0,
|
||||
packed_x + 1 + width, packed_y + 1 + height,
|
||||
1, 1,
|
||||
gl_format, gl_type,
|
||||
pixel_data);
|
||||
/* Reset this */
|
||||
glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0);
|
||||
glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
|
||||
glPixelStorei (GL_UNPACK_SKIP_ROWS, 0);
|
||||
|
||||
gdk_gl_context_pop_debug_group (gdk_gl_context_get_current ());
|
||||
|
||||
*out_icon_data = icon_data;
|
||||
|
||||
cairo_surface_destroy (surface);
|
||||
g_free (free_data);
|
||||
|
||||
#if 0
|
||||
{
|
||||
static int k;
|
||||
const int stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, atlas->width);
|
||||
guchar *data = g_malloc (atlas->height * stride);
|
||||
cairo_surface_t *s;
|
||||
char *filename = g_strdup_printf ("atlas_%u_%d.png", atlas->texture_id, k++);
|
||||
|
||||
glBindTexture (GL_TEXTURE_2D, atlas->texture_id);
|
||||
glGetTexImage (GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_BYTE, data);
|
||||
s = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32, atlas->width, atlas->height, stride);
|
||||
cairo_surface_write_to_png (s, filename);
|
||||
|
||||
cairo_surface_destroy (s);
|
||||
g_free (data);
|
||||
g_free (filename);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
#ifndef __GSK_GL_ICON_CACHE_PRIVATE_H__
|
||||
#define __GSK_GL_ICON_CACHE_PRIVATE_H__
|
||||
|
||||
#include "gskgldriverprivate.h"
|
||||
#include "gskglimageprivate.h"
|
||||
#include "gskrendererprivate.h"
|
||||
#include "gskgltextureatlasprivate.h"
|
||||
#include <pango/pango.h>
|
||||
#include <gdk/gdk.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int ref_count;
|
||||
|
||||
GdkDisplay *display;
|
||||
GskGLDriver *gl_driver;
|
||||
|
||||
GskGLTextureAtlases *atlases;
|
||||
GHashTable *icons; /* GdkTexture -> IconData */
|
||||
|
||||
int timestamp;
|
||||
} GskGLIconCache;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float x, y, x2, y2;
|
||||
GskGLTextureAtlas *atlas;
|
||||
guint used : 1;
|
||||
guint accessed : 1;
|
||||
int texture_id;
|
||||
GdkTexture *source_texture;
|
||||
} IconData;
|
||||
|
||||
GskGLIconCache * gsk_gl_icon_cache_new (GdkDisplay *display,
|
||||
GskGLTextureAtlases *atlases);
|
||||
GskGLIconCache * gsk_gl_icon_cache_ref (GskGLIconCache *self);
|
||||
void gsk_gl_icon_cache_unref (GskGLIconCache *self);
|
||||
void gsk_gl_icon_cache_begin_frame (GskGLIconCache *self,
|
||||
GPtrArray *removed_atlases);
|
||||
void gsk_gl_icon_cache_lookup_or_add (GskGLIconCache *self,
|
||||
GdkTexture *texture,
|
||||
const IconData **out_icon_data);
|
||||
|
||||
#endif
|
||||
@@ -1,64 +0,0 @@
|
||||
|
||||
#include "gskglimageprivate.h"
|
||||
#include <epoxy/gl.h>
|
||||
|
||||
void
|
||||
gsk_gl_image_create (GskGLImage *self,
|
||||
GskGLDriver *gl_driver,
|
||||
int width,
|
||||
int height,
|
||||
int min_filter,
|
||||
int mag_filter)
|
||||
{
|
||||
self->texture_id = gsk_gl_driver_create_texture (gl_driver, width, height);
|
||||
self->width = width;
|
||||
self->height = height;
|
||||
|
||||
gsk_gl_driver_bind_source_texture (gl_driver, self->texture_id);
|
||||
gsk_gl_driver_init_texture_empty (gl_driver, self->texture_id, min_filter, mag_filter);
|
||||
gsk_gl_driver_mark_texture_permanent (gl_driver, self->texture_id);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_gl_image_destroy (GskGLImage *self,
|
||||
GskGLDriver *gl_driver)
|
||||
{
|
||||
gsk_gl_driver_destroy_texture (gl_driver, self->texture_id);
|
||||
self->texture_id = 0;
|
||||
}
|
||||
|
||||
void
|
||||
gsk_gl_image_write_to_png (const GskGLImage *self,
|
||||
GskGLDriver *gl_driver,
|
||||
const char *filename)
|
||||
{
|
||||
int stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, self->width);
|
||||
guchar *data = g_malloc (self->height * stride);
|
||||
cairo_surface_t *s;
|
||||
|
||||
gsk_gl_driver_bind_source_texture (gl_driver, self->texture_id);
|
||||
glGetTexImage (GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, data);
|
||||
s = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32, self->width, self->height, stride);
|
||||
cairo_surface_write_to_png (s, filename);
|
||||
|
||||
cairo_surface_destroy (s);
|
||||
g_free (data);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_gl_image_upload_region (GskGLImage *self,
|
||||
GskGLDriver *gl_driver,
|
||||
const GskImageRegion *region)
|
||||
{
|
||||
gsk_gl_driver_bind_source_texture (gl_driver, self->texture_id);
|
||||
glBindTexture (GL_TEXTURE_2D, self->texture_id);
|
||||
|
||||
glTexSubImage2D (GL_TEXTURE_2D, 0, region->x, region->y, region->width, region->height,
|
||||
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, region->data);
|
||||
|
||||
#ifdef G_ENABLE_DEBUG
|
||||
/*gsk_gl_driver_bind_source_texture (gl_driver, self->texture_id);*/
|
||||
/*gsk_gl_image_dump (self, gl_driver, "/home/baedert/atlases/test_dump.png");*/
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
#ifndef __GSK_GL_IMAGE_H__
|
||||
#define __GSK_GL_IMAGE_H__
|
||||
|
||||
#include "gskgldriverprivate.h"
|
||||
#include <cairo.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
guint texture_id;
|
||||
int width;
|
||||
int height;
|
||||
} GskGLImage;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
guchar *data;
|
||||
gsize width;
|
||||
gsize height;
|
||||
gsize stride;
|
||||
gsize x;
|
||||
gsize y;
|
||||
} GskImageRegion;
|
||||
|
||||
void gsk_gl_image_create (GskGLImage *self,
|
||||
GskGLDriver *gl_driver,
|
||||
int width,
|
||||
int height,
|
||||
int min_filter,
|
||||
int mag_filter);
|
||||
void gsk_gl_image_destroy (GskGLImage *self,
|
||||
GskGLDriver *gl_driver);
|
||||
void gsk_gl_image_write_to_png (const GskGLImage *self,
|
||||
GskGLDriver *gl_driver,
|
||||
const char *filename);
|
||||
void gsk_gl_image_upload_region (GskGLImage *self,
|
||||
GskGLDriver *gl_driver,
|
||||
const GskImageRegion *region);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
|
||||
#include <glib/gprintf.h>
|
||||
#include "gskglnodesampleprivate.h"
|
||||
#include "gskrendernodeprivate.h"
|
||||
|
||||
void
|
||||
node_sample_init (NodeSample *self)
|
||||
{
|
||||
memset (self->nodes, 0, sizeof (self->nodes));
|
||||
self->count = 0;
|
||||
}
|
||||
|
||||
void
|
||||
node_sample_reset (NodeSample *self)
|
||||
{
|
||||
node_sample_init (self);
|
||||
}
|
||||
|
||||
void
|
||||
node_sample_add (NodeSample *self,
|
||||
GskRenderNode *node)
|
||||
{
|
||||
const guint node_type = gsk_render_node_get_node_type (node);
|
||||
|
||||
g_assert (node_type <= N_NODE_TYPES);
|
||||
|
||||
if (self->nodes[node_type].class_name == NULL)
|
||||
self->nodes[node_type].class_name = g_type_name_from_instance ((GTypeInstance *) node);
|
||||
|
||||
self->nodes[node_type].count ++;
|
||||
self->count ++;
|
||||
}
|
||||
|
||||
void
|
||||
node_sample_print (const NodeSample *self,
|
||||
const char *prefix)
|
||||
{
|
||||
guint i;
|
||||
|
||||
g_printf ("%s:\n", prefix);
|
||||
|
||||
for (i = 0; i < N_NODE_TYPES; i ++)
|
||||
{
|
||||
if (self->nodes[i].count > 0)
|
||||
{
|
||||
double p = (double)self->nodes[i].count / (double)self->count;
|
||||
|
||||
g_printf ("%s: %u (%.2f%%)\n", self->nodes[i].class_name, self->nodes[i].count, p * 100.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
|
||||
#ifndef __GSK_GL_NODE_SAMPLE_PRIVATE_H__
|
||||
#define __GSK_GL_NODE_SAMPLE_PRIVATE_H__
|
||||
|
||||
#include <glib.h>
|
||||
#include "gskenums.h"
|
||||
#include "gskrendernode.h"
|
||||
|
||||
/* TODO: We have no other way for this...? */
|
||||
#define N_NODE_TYPES (GSK_DEBUG_NODE + 1)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
struct {
|
||||
const char *class_name;
|
||||
guint count;
|
||||
} nodes[N_NODE_TYPES];
|
||||
guint count;
|
||||
} NodeSample;
|
||||
|
||||
void node_sample_init (NodeSample *self);
|
||||
void node_sample_reset (NodeSample *self);
|
||||
void node_sample_add (NodeSample *self,
|
||||
GskRenderNode *node);
|
||||
void node_sample_print (const NodeSample *self,
|
||||
const char *prefix);
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2016 Endless
|
||||
* 2018 Timm Bäder <mail@baedert.org>
|
||||
*
|
||||
* 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: Timm Bäder <mail@baedert.org>
|
||||
*/
|
||||
|
||||
#ifndef __GSK_GL_RENDERER_H__
|
||||
#define __GSK_GL_RENDERER_H__
|
||||
|
||||
#include <gsk/gskrenderer.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GSK_TYPE_GL_RENDERER (gsk_gl_renderer_get_type ())
|
||||
|
||||
#define GSK_GL_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSK_TYPE_GL_RENDERER, GskGLRenderer))
|
||||
#define GSK_IS_GL_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSK_TYPE_GL_RENDERER))
|
||||
#define GSK_GL_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSK_TYPE_GL_RENDERER, GskGLRendererClass))
|
||||
#define GSK_IS_GL_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSK_TYPE_GL_RENDERER))
|
||||
#define GSK_GL_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSK_TYPE_GL_RENDERER, GskGLRendererClass))
|
||||
|
||||
/**
|
||||
* GskGLRenderer:
|
||||
*
|
||||
* A GSK renderer that is using OpenGL.
|
||||
*/
|
||||
typedef struct _GskGLRenderer GskGLRenderer;
|
||||
typedef struct _GskGLRendererClass GskGLRendererClass;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gsk_gl_renderer_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GskRenderer * gsk_gl_renderer_new (void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSK_GL_RENDERER_H__ */
|
||||
@@ -1,14 +0,0 @@
|
||||
#ifndef __GSK_GL_RENDERER_PRIVATE_H__
|
||||
#define __GSK_GL_RENDERER_PRIVATE_H__
|
||||
|
||||
#include "gskglrenderer.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
gboolean gsk_gl_renderer_try_compile_gl_shader (GskGLRenderer *self,
|
||||
GskGLShader *shader,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSK_GL_RENDERER_PRIVATE_H__ */
|
||||
@@ -1,976 +0,0 @@
|
||||
#include "gskglrenderopsprivate.h"
|
||||
#include "gsktransform.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GskRoundedRect rect;
|
||||
bool is_rectilinear;
|
||||
} ClipStackEntry;
|
||||
|
||||
static inline gboolean
|
||||
rect_equal (const graphene_rect_t *a,
|
||||
const graphene_rect_t *b)
|
||||
{
|
||||
return memcmp (a, b, sizeof (graphene_rect_t)) == 0;
|
||||
}
|
||||
|
||||
static inline bool G_GNUC_PURE
|
||||
rounded_rect_equal (const GskRoundedRect *r1,
|
||||
const GskRoundedRect *r2)
|
||||
{
|
||||
if (r1 == r2)
|
||||
return true;
|
||||
|
||||
if (!r1)
|
||||
return false;
|
||||
|
||||
if (r1->bounds.origin.x != r2->bounds.origin.x ||
|
||||
r1->bounds.origin.y != r2->bounds.origin.y ||
|
||||
r1->bounds.size.width != r2->bounds.size.width ||
|
||||
r1->bounds.size.height != r2->bounds.size.height)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < 4; i ++)
|
||||
if (r1->corner[i].width != r2->corner[i].width ||
|
||||
r1->corner[i].height != r2->corner[i].height)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline gboolean G_GNUC_PURE
|
||||
rounded_rect_corners_equal (const GskRoundedRect *r1,
|
||||
const GskRoundedRect *r2)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!r1)
|
||||
return FALSE;
|
||||
|
||||
for (i = 0; i < 4; i ++)
|
||||
if (r1->corner[i].width != r2->corner[i].width ||
|
||||
r1->corner[i].height != r2->corner[i].height)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static inline ProgramState *
|
||||
get_current_program_state (RenderOpBuilder *builder)
|
||||
{
|
||||
if (!builder->current_program)
|
||||
return NULL;
|
||||
|
||||
return &builder->current_program->state;
|
||||
}
|
||||
|
||||
void
|
||||
ops_finish (RenderOpBuilder *builder)
|
||||
{
|
||||
if (builder->mv_stack)
|
||||
g_array_free (builder->mv_stack, TRUE);
|
||||
builder->mv_stack = NULL;
|
||||
|
||||
if (builder->clip_stack)
|
||||
g_array_free (builder->clip_stack, TRUE);
|
||||
builder->clip_stack = NULL;
|
||||
|
||||
builder->dx = 0;
|
||||
builder->dy = 0;
|
||||
builder->scale_x = 1;
|
||||
builder->scale_y = 1;
|
||||
builder->current_modelview = NULL;
|
||||
builder->current_clip = NULL;
|
||||
builder->clip_is_rectilinear = TRUE;
|
||||
builder->current_render_target = 0;
|
||||
builder->current_texture = 0;
|
||||
builder->current_program = NULL;
|
||||
graphene_matrix_init_identity (&builder->current_projection);
|
||||
builder->current_viewport = GRAPHENE_RECT_INIT (0, 0, 0, 0);
|
||||
}
|
||||
|
||||
/* Debugging only! */
|
||||
void
|
||||
ops_dump_framebuffer (RenderOpBuilder *builder,
|
||||
const char *filename,
|
||||
int width,
|
||||
int height)
|
||||
{
|
||||
OpDumpFrameBuffer *op;
|
||||
|
||||
op = ops_begin (builder, OP_DUMP_FRAMEBUFFER);
|
||||
op->filename = g_strdup (filename);
|
||||
op->width = width;
|
||||
op->height = height;
|
||||
}
|
||||
|
||||
void
|
||||
ops_push_debug_group (RenderOpBuilder *builder,
|
||||
const char *text)
|
||||
{
|
||||
OpDebugGroup *op;
|
||||
|
||||
op = ops_begin (builder, OP_PUSH_DEBUG_GROUP);
|
||||
strncpy (op->text, text, sizeof(op->text) - 1);
|
||||
op->text[sizeof(op->text) - 1] = 0; /* Ensure zero terminated */
|
||||
}
|
||||
|
||||
void
|
||||
ops_pop_debug_group (RenderOpBuilder *builder)
|
||||
{
|
||||
ops_begin (builder, OP_POP_DEBUG_GROUP);
|
||||
}
|
||||
|
||||
static void
|
||||
extract_matrix_metadata (GskTransform *transform,
|
||||
OpsMatrixMetadata *md)
|
||||
{
|
||||
float dummy;
|
||||
|
||||
switch (gsk_transform_get_category (transform))
|
||||
{
|
||||
case GSK_TRANSFORM_CATEGORY_IDENTITY:
|
||||
case GSK_TRANSFORM_CATEGORY_2D_TRANSLATE:
|
||||
md->scale_x = 1;
|
||||
md->scale_y = 1;
|
||||
break;
|
||||
|
||||
case GSK_TRANSFORM_CATEGORY_2D_AFFINE:
|
||||
gsk_transform_to_affine (transform,
|
||||
&md->scale_x, &md->scale_y,
|
||||
&dummy, &dummy);
|
||||
break;
|
||||
|
||||
case GSK_TRANSFORM_CATEGORY_UNKNOWN:
|
||||
case GSK_TRANSFORM_CATEGORY_ANY:
|
||||
case GSK_TRANSFORM_CATEGORY_3D:
|
||||
case GSK_TRANSFORM_CATEGORY_2D:
|
||||
{
|
||||
graphene_vec3_t col1;
|
||||
graphene_vec3_t col2;
|
||||
graphene_matrix_t m;
|
||||
|
||||
gsk_transform_to_matrix (transform, &m);
|
||||
|
||||
/* TODO: 90% sure this is incorrect. But we should never hit this code
|
||||
* path anyway. */
|
||||
graphene_vec3_init (&col1,
|
||||
graphene_matrix_get_value (&m, 0, 0),
|
||||
graphene_matrix_get_value (&m, 1, 0),
|
||||
graphene_matrix_get_value (&m, 2, 0));
|
||||
|
||||
graphene_vec3_init (&col2,
|
||||
graphene_matrix_get_value (&m, 0, 1),
|
||||
graphene_matrix_get_value (&m, 1, 1),
|
||||
graphene_matrix_get_value (&m, 2, 1));
|
||||
|
||||
md->scale_x = graphene_vec3_length (&col1);
|
||||
md->scale_y = graphene_vec3_length (&col2);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ops_transform_bounds_modelview (const RenderOpBuilder *builder,
|
||||
const graphene_rect_t *src,
|
||||
graphene_rect_t *dst)
|
||||
{
|
||||
graphene_rect_t r = *src;
|
||||
|
||||
g_assert (builder->mv_stack != NULL);
|
||||
g_assert (builder->mv_stack->len >= 1);
|
||||
|
||||
r.origin.x += builder->dx;
|
||||
r.origin.y += builder->dy;
|
||||
|
||||
gsk_transform_transform_bounds (builder->current_modelview, &r, dst);
|
||||
}
|
||||
|
||||
void
|
||||
ops_init (RenderOpBuilder *builder)
|
||||
{
|
||||
memset (builder, 0, sizeof (*builder));
|
||||
|
||||
builder->current_opacity = 1.0f;
|
||||
|
||||
op_buffer_init (&builder->render_ops);
|
||||
builder->vertices = g_array_new (FALSE, TRUE, sizeof (GskQuadVertex));
|
||||
}
|
||||
|
||||
void
|
||||
ops_free (RenderOpBuilder *builder)
|
||||
{
|
||||
g_array_unref (builder->vertices);
|
||||
op_buffer_destroy (&builder->render_ops);
|
||||
}
|
||||
|
||||
void
|
||||
ops_set_program (RenderOpBuilder *builder,
|
||||
Program *program)
|
||||
{
|
||||
OpProgram *op;
|
||||
|
||||
if (builder->current_program == program)
|
||||
return;
|
||||
|
||||
op = ops_begin (builder, OP_CHANGE_PROGRAM);
|
||||
op->program = program;
|
||||
|
||||
builder->current_program = program;
|
||||
}
|
||||
|
||||
void
|
||||
ops_push_clip (RenderOpBuilder *self,
|
||||
const GskRoundedRect *clip)
|
||||
{
|
||||
ClipStackEntry entry;
|
||||
|
||||
if (G_UNLIKELY (self->clip_stack == NULL))
|
||||
self->clip_stack = g_array_new (FALSE, TRUE, sizeof (ClipStackEntry));
|
||||
|
||||
g_assert (self->clip_stack != NULL);
|
||||
|
||||
entry.rect = *clip;
|
||||
entry.is_rectilinear = gsk_rounded_rect_is_rectilinear (clip);
|
||||
g_array_append_val (self->clip_stack, entry);
|
||||
self->current_clip = &g_array_index (self->clip_stack, ClipStackEntry, self->clip_stack->len - 1).rect;
|
||||
self->clip_is_rectilinear = entry.is_rectilinear;
|
||||
}
|
||||
|
||||
void
|
||||
ops_pop_clip (RenderOpBuilder *self)
|
||||
{
|
||||
const ClipStackEntry *head;
|
||||
|
||||
g_assert (self->clip_stack);
|
||||
g_assert (self->clip_stack->len >= 1);
|
||||
|
||||
self->clip_stack->len --;
|
||||
head = &g_array_index (self->clip_stack, ClipStackEntry, self->clip_stack->len - 1);
|
||||
|
||||
if (self->clip_stack->len >= 1)
|
||||
{
|
||||
self->current_clip = &head->rect;
|
||||
self->clip_is_rectilinear = head->is_rectilinear;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->current_clip = NULL;
|
||||
self->clip_is_rectilinear = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
ops_has_clip (RenderOpBuilder *self)
|
||||
{
|
||||
return self->clip_stack != NULL &&
|
||||
self->clip_stack->len > 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ops_set_modelview:
|
||||
* @builder
|
||||
* @transform: (transfer full): The new modelview transform
|
||||
*
|
||||
* This sets the modelview to the given one without looking at the
|
||||
* one that's currently set */
|
||||
void
|
||||
ops_set_modelview (RenderOpBuilder *builder,
|
||||
GskTransform *transform)
|
||||
{
|
||||
MatrixStackEntry *entry;
|
||||
|
||||
if (G_UNLIKELY (builder->mv_stack == NULL))
|
||||
builder->mv_stack = g_array_new (FALSE, TRUE, sizeof (MatrixStackEntry));
|
||||
|
||||
g_assert (builder->mv_stack != NULL);
|
||||
|
||||
g_array_set_size (builder->mv_stack, builder->mv_stack->len + 1);
|
||||
entry = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 1);
|
||||
|
||||
entry->transform = transform;
|
||||
|
||||
entry->metadata.dx_before = builder->dx;
|
||||
entry->metadata.dy_before = builder->dy;
|
||||
extract_matrix_metadata (entry->transform, &entry->metadata);
|
||||
|
||||
builder->dx = 0;
|
||||
builder->dy = 0;
|
||||
builder->current_modelview = entry->transform;
|
||||
builder->scale_x = entry->metadata.scale_x;
|
||||
builder->scale_y = entry->metadata.scale_y;
|
||||
}
|
||||
|
||||
/* This sets the given modelview to the one we get when multiplying
|
||||
* the given modelview with the current one. */
|
||||
void
|
||||
ops_push_modelview (RenderOpBuilder *builder,
|
||||
GskTransform *transform)
|
||||
{
|
||||
MatrixStackEntry *entry;
|
||||
|
||||
if (G_UNLIKELY (builder->mv_stack == NULL))
|
||||
builder->mv_stack = g_array_new (FALSE, TRUE, sizeof (MatrixStackEntry));
|
||||
|
||||
g_assert (builder->mv_stack != NULL);
|
||||
|
||||
g_array_set_size (builder->mv_stack, builder->mv_stack->len + 1);
|
||||
entry = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 1);
|
||||
|
||||
if (G_LIKELY (builder->mv_stack->len >= 2))
|
||||
{
|
||||
const MatrixStackEntry *cur;
|
||||
GskTransform *t = NULL;
|
||||
|
||||
cur = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 2);
|
||||
/* Multiply given matrix with current modelview */
|
||||
|
||||
t = gsk_transform_translate (gsk_transform_ref (cur->transform),
|
||||
&(graphene_point_t) { builder->dx, builder->dy});
|
||||
t = gsk_transform_transform (t, transform);
|
||||
entry->transform = t;
|
||||
}
|
||||
else
|
||||
{
|
||||
entry->transform = gsk_transform_ref (transform);
|
||||
}
|
||||
|
||||
entry->metadata.dx_before = builder->dx;
|
||||
entry->metadata.dy_before = builder->dy;
|
||||
extract_matrix_metadata (entry->transform, &entry->metadata);
|
||||
|
||||
builder->dx = 0;
|
||||
builder->dy = 0;
|
||||
builder->scale_x = entry->metadata.scale_x;
|
||||
builder->scale_y = entry->metadata.scale_y;
|
||||
builder->current_modelview = entry->transform;
|
||||
}
|
||||
|
||||
void
|
||||
ops_pop_modelview (RenderOpBuilder *builder)
|
||||
{
|
||||
const MatrixStackEntry *head;
|
||||
|
||||
g_assert (builder->mv_stack);
|
||||
g_assert (builder->mv_stack->len >= 1);
|
||||
|
||||
head = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 1);
|
||||
builder->dx = head->metadata.dx_before;
|
||||
builder->dy = head->metadata.dy_before;
|
||||
gsk_transform_unref (head->transform);
|
||||
|
||||
builder->mv_stack->len --;
|
||||
head = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 1);
|
||||
|
||||
if (builder->mv_stack->len >= 1)
|
||||
{
|
||||
builder->scale_x = head->metadata.scale_x;
|
||||
builder->scale_y = head->metadata.scale_y;
|
||||
builder->current_modelview = head->transform;
|
||||
}
|
||||
else
|
||||
{
|
||||
builder->current_modelview = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
graphene_matrix_t
|
||||
ops_set_projection (RenderOpBuilder *builder,
|
||||
const graphene_matrix_t *projection)
|
||||
{
|
||||
graphene_matrix_t prev_mv;
|
||||
|
||||
prev_mv = builder->current_projection;
|
||||
builder->current_projection = *projection;
|
||||
|
||||
return prev_mv;
|
||||
}
|
||||
|
||||
graphene_rect_t
|
||||
ops_set_viewport (RenderOpBuilder *builder,
|
||||
const graphene_rect_t *viewport)
|
||||
{
|
||||
ProgramState *current_program_state = get_current_program_state (builder);
|
||||
OpViewport *op;
|
||||
graphene_rect_t prev_viewport;
|
||||
|
||||
if (rect_equal (&builder->current_viewport, viewport))
|
||||
return *viewport;
|
||||
|
||||
op = ops_begin (builder, OP_CHANGE_VIEWPORT);
|
||||
op->viewport = *viewport;
|
||||
|
||||
if (current_program_state != NULL)
|
||||
current_program_state->viewport = *viewport;
|
||||
|
||||
prev_viewport = builder->current_viewport;
|
||||
builder->current_viewport = *viewport;
|
||||
|
||||
return prev_viewport;
|
||||
}
|
||||
|
||||
void
|
||||
ops_set_texture (RenderOpBuilder *builder,
|
||||
int texture_id)
|
||||
{
|
||||
OpTexture *op;
|
||||
|
||||
if (builder->current_texture == texture_id)
|
||||
return;
|
||||
|
||||
op = ops_begin (builder, OP_CHANGE_SOURCE_TEXTURE);
|
||||
op->texture_id = texture_id;
|
||||
builder->current_texture = texture_id;
|
||||
}
|
||||
|
||||
void
|
||||
ops_set_extra_texture (RenderOpBuilder *builder,
|
||||
int texture_id,
|
||||
int idx)
|
||||
{
|
||||
OpExtraTexture *op;
|
||||
|
||||
op = ops_begin (builder, OP_CHANGE_EXTRA_SOURCE_TEXTURE);
|
||||
op->texture_id = texture_id;
|
||||
op->idx = idx;
|
||||
}
|
||||
|
||||
int
|
||||
ops_set_render_target (RenderOpBuilder *builder,
|
||||
int render_target_id)
|
||||
{
|
||||
OpRenderTarget *op;
|
||||
int prev_render_target;
|
||||
|
||||
if (builder->current_render_target == render_target_id)
|
||||
return render_target_id;
|
||||
|
||||
prev_render_target = builder->current_render_target;
|
||||
|
||||
if (!(op = op_buffer_peek_tail_checked (&builder->render_ops, OP_CHANGE_RENDER_TARGET)))
|
||||
op = op_buffer_add (&builder->render_ops, OP_CHANGE_RENDER_TARGET);
|
||||
|
||||
op->render_target_id = render_target_id;
|
||||
|
||||
builder->current_render_target = render_target_id;
|
||||
|
||||
return prev_render_target;
|
||||
}
|
||||
|
||||
float
|
||||
ops_set_opacity (RenderOpBuilder *builder,
|
||||
float opacity)
|
||||
{
|
||||
float prev_opacity;
|
||||
|
||||
if (builder->current_opacity == opacity)
|
||||
return opacity;
|
||||
|
||||
prev_opacity = builder->current_opacity;
|
||||
builder->current_opacity = opacity;
|
||||
|
||||
return prev_opacity;
|
||||
}
|
||||
|
||||
void
|
||||
ops_set_color (RenderOpBuilder *builder,
|
||||
const GdkRGBA *color)
|
||||
{
|
||||
ProgramState *current_program_state = get_current_program_state (builder);
|
||||
OpColor *op;
|
||||
|
||||
if (gdk_rgba_equal (color, ¤t_program_state->color))
|
||||
return;
|
||||
|
||||
current_program_state->color = *color;
|
||||
|
||||
op = ops_begin (builder, OP_CHANGE_COLOR);
|
||||
op->rgba = color;
|
||||
}
|
||||
|
||||
void
|
||||
ops_set_gl_shader_args (RenderOpBuilder *builder,
|
||||
GskGLShader *shader,
|
||||
float width,
|
||||
float height,
|
||||
const guchar *uniform_data)
|
||||
{
|
||||
ProgramState *current_program_state = get_current_program_state (builder);
|
||||
OpGLShader *op;
|
||||
gsize args_size = gsk_gl_shader_get_args_size (shader);
|
||||
|
||||
if (current_program_state)
|
||||
{
|
||||
if (current_program_state->gl_shader.width == width &&
|
||||
current_program_state->gl_shader.height == height &&
|
||||
current_program_state->gl_shader.uniform_data_len == args_size &&
|
||||
memcmp (current_program_state->gl_shader.uniform_data, uniform_data, args_size) == 0)
|
||||
return;
|
||||
|
||||
current_program_state->gl_shader.width = width;
|
||||
current_program_state->gl_shader.height = height;
|
||||
if (args_size > sizeof (current_program_state->gl_shader.uniform_data))
|
||||
current_program_state->gl_shader.uniform_data_len = 0;
|
||||
else
|
||||
{
|
||||
current_program_state->gl_shader.uniform_data_len = args_size;
|
||||
memcpy (current_program_state->gl_shader.uniform_data, uniform_data, args_size);
|
||||
}
|
||||
}
|
||||
|
||||
op = ops_begin (builder, OP_CHANGE_GL_SHADER_ARGS);
|
||||
op->shader = shader;
|
||||
op->size[0] = width;
|
||||
op->size[1] = height;
|
||||
op->uniform_data = uniform_data;
|
||||
}
|
||||
|
||||
void
|
||||
ops_set_color_matrix (RenderOpBuilder *builder,
|
||||
const graphene_matrix_t *matrix,
|
||||
const graphene_vec4_t *offset)
|
||||
{
|
||||
ProgramState *current_program_state = get_current_program_state (builder);
|
||||
const bool offset_equal = graphene_vec4_equal (offset, ¤t_program_state->color_matrix.offset);
|
||||
const bool matrix_equal = graphene_matrix_equal_fast (matrix,
|
||||
¤t_program_state->color_matrix.matrix);
|
||||
OpColorMatrix *op;
|
||||
|
||||
if (offset_equal && matrix_equal)
|
||||
return;
|
||||
|
||||
op = ops_begin (builder, OP_CHANGE_COLOR_MATRIX);
|
||||
|
||||
if (!matrix_equal)
|
||||
{
|
||||
current_program_state->color_matrix.matrix = *matrix;
|
||||
op->matrix.value = matrix;
|
||||
op->matrix.send = TRUE;
|
||||
}
|
||||
else
|
||||
op->matrix.send = FALSE;
|
||||
|
||||
if (!offset_equal)
|
||||
{
|
||||
current_program_state->color_matrix.offset = *offset;
|
||||
op->offset.value = offset;
|
||||
op->offset.send = TRUE;
|
||||
}
|
||||
else
|
||||
op->offset.send = FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
ops_set_border (RenderOpBuilder *builder,
|
||||
const GskRoundedRect *outline)
|
||||
{
|
||||
ProgramState *current_program_state = get_current_program_state (builder);
|
||||
OpBorder *op;
|
||||
|
||||
if (memcmp (¤t_program_state->border.outline,
|
||||
outline, sizeof (GskRoundedRect)) == 0)
|
||||
return;
|
||||
|
||||
current_program_state->border.outline = *outline;
|
||||
|
||||
op = ops_begin (builder, OP_CHANGE_BORDER);
|
||||
op->outline = *outline;
|
||||
}
|
||||
|
||||
void
|
||||
ops_set_border_width (RenderOpBuilder *builder,
|
||||
const float *widths)
|
||||
{
|
||||
ProgramState *current_program_state = get_current_program_state (builder);
|
||||
OpBorder *op;
|
||||
|
||||
g_assert (current_program_state);
|
||||
|
||||
if (memcmp (current_program_state->border.widths,
|
||||
widths, sizeof (float) * 4) == 0)
|
||||
return;
|
||||
|
||||
memcpy (¤t_program_state->border.widths,
|
||||
widths, sizeof (float) * 4);
|
||||
|
||||
op = ops_begin (builder, OP_CHANGE_BORDER_WIDTH);
|
||||
op->widths[0] = widths[0];
|
||||
op->widths[1] = widths[1];
|
||||
op->widths[2] = widths[2];
|
||||
op->widths[3] = widths[3];
|
||||
}
|
||||
|
||||
void
|
||||
ops_set_border_color (RenderOpBuilder *builder,
|
||||
const GdkRGBA *color)
|
||||
{
|
||||
ProgramState *current_program_state = get_current_program_state (builder);
|
||||
OpBorder *op;
|
||||
|
||||
if (gdk_rgba_equal (color, ¤t_program_state->border.color))
|
||||
return;
|
||||
|
||||
op = op_buffer_add (&builder->render_ops, OP_CHANGE_BORDER_COLOR);
|
||||
op->color = color;
|
||||
|
||||
current_program_state->border.color = *color;
|
||||
}
|
||||
|
||||
GskQuadVertex *
|
||||
ops_draw (RenderOpBuilder *builder,
|
||||
const GskQuadVertex vertex_data[GL_N_VERTICES])
|
||||
{
|
||||
ProgramState *program_state = get_current_program_state (builder);
|
||||
OpDraw *op;
|
||||
|
||||
if (memcmp (&builder->current_projection, &program_state->projection, sizeof (graphene_matrix_t)) != 0)
|
||||
{
|
||||
OpMatrix *opm;
|
||||
|
||||
opm = ops_begin (builder, OP_CHANGE_PROJECTION);
|
||||
opm->matrix = builder->current_projection;
|
||||
program_state->projection = builder->current_projection;
|
||||
}
|
||||
|
||||
if (program_state->modelview == NULL ||
|
||||
!gsk_transform_equal (builder->current_modelview, program_state->modelview))
|
||||
{
|
||||
OpMatrix *opm;
|
||||
|
||||
opm = ops_begin (builder, OP_CHANGE_MODELVIEW);
|
||||
gsk_transform_to_matrix (builder->current_modelview, &opm->matrix);
|
||||
gsk_transform_unref (program_state->modelview);
|
||||
program_state->modelview = gsk_transform_ref (builder->current_modelview);
|
||||
}
|
||||
|
||||
if (!rect_equal (&builder->current_viewport, &program_state->viewport))
|
||||
{
|
||||
OpViewport *opv;
|
||||
|
||||
opv = ops_begin (builder, OP_CHANGE_VIEWPORT);
|
||||
opv->viewport = builder->current_viewport;
|
||||
program_state->viewport = builder->current_viewport;
|
||||
}
|
||||
|
||||
if (!rounded_rect_equal (builder->current_clip, &program_state->clip))
|
||||
{
|
||||
OpClip *opc;
|
||||
|
||||
opc = ops_begin (builder, OP_CHANGE_CLIP);
|
||||
opc->clip = *builder->current_clip;
|
||||
opc->send_corners = !rounded_rect_corners_equal (builder->current_clip, &program_state->clip);
|
||||
program_state->clip = *builder->current_clip;
|
||||
}
|
||||
|
||||
if (program_state->opacity != builder->current_opacity)
|
||||
{
|
||||
OpOpacity *opo;
|
||||
|
||||
opo = ops_begin (builder, OP_CHANGE_OPACITY);
|
||||
opo->opacity = builder->current_opacity;
|
||||
program_state->opacity = builder->current_opacity;
|
||||
}
|
||||
|
||||
/* TODO: Did the additions above break the following optimization? */
|
||||
if ((op = op_buffer_peek_tail_checked (&builder->render_ops, OP_DRAW)))
|
||||
{
|
||||
op->vao_size += GL_N_VERTICES;
|
||||
}
|
||||
else
|
||||
{
|
||||
op = op_buffer_add (&builder->render_ops, OP_DRAW);
|
||||
op->vao_offset = builder->vertices->len;
|
||||
op->vao_size = GL_N_VERTICES;
|
||||
}
|
||||
|
||||
if (vertex_data)
|
||||
{
|
||||
g_array_append_vals (builder->vertices, vertex_data, GL_N_VERTICES);
|
||||
return NULL; /* Better not use this on the caller side */
|
||||
}
|
||||
|
||||
g_array_set_size (builder->vertices, builder->vertices->len + GL_N_VERTICES);
|
||||
return &g_array_index (builder->vertices, GskQuadVertex, builder->vertices->len - GL_N_VERTICES);
|
||||
}
|
||||
|
||||
/* The offset is only valid for the current modelview.
|
||||
* Setting a new modelview will add the offset to that matrix
|
||||
* and reset the internal offset to 0. */
|
||||
void
|
||||
ops_offset (RenderOpBuilder *builder,
|
||||
float x,
|
||||
float y)
|
||||
{
|
||||
builder->dx += x;
|
||||
builder->dy += y;
|
||||
}
|
||||
|
||||
gpointer
|
||||
ops_begin (RenderOpBuilder *builder,
|
||||
OpKind kind)
|
||||
{
|
||||
return op_buffer_add (&builder->render_ops, kind);
|
||||
}
|
||||
|
||||
void
|
||||
ops_reset (RenderOpBuilder *builder)
|
||||
{
|
||||
op_buffer_clear (&builder->render_ops);
|
||||
g_array_set_size (builder->vertices, 0);
|
||||
}
|
||||
|
||||
OpBuffer *
|
||||
ops_get_buffer (RenderOpBuilder *builder)
|
||||
{
|
||||
return &builder->render_ops;
|
||||
}
|
||||
|
||||
void
|
||||
ops_set_inset_shadow (RenderOpBuilder *self,
|
||||
const GskRoundedRect outline,
|
||||
float spread,
|
||||
const GdkRGBA *color,
|
||||
float dx,
|
||||
float dy)
|
||||
{
|
||||
ProgramState *current_program_state = get_current_program_state (self);
|
||||
OpShadow *op;
|
||||
|
||||
g_assert (current_program_state);
|
||||
|
||||
op = ops_begin (self, OP_CHANGE_INSET_SHADOW);
|
||||
|
||||
if (!rounded_rect_equal (&outline, ¤t_program_state->inset_shadow.outline))
|
||||
{
|
||||
op->outline.value = outline;
|
||||
op->outline.send = TRUE;
|
||||
op->outline.send_corners = !rounded_rect_corners_equal (¤t_program_state->inset_shadow.outline,
|
||||
&outline);
|
||||
current_program_state->inset_shadow.outline = outline;
|
||||
}
|
||||
else
|
||||
op->outline.send = FALSE;
|
||||
|
||||
if (spread != current_program_state->inset_shadow.spread)
|
||||
{
|
||||
op->spread.value = spread;
|
||||
op->spread.send = TRUE;
|
||||
|
||||
current_program_state->inset_shadow.spread = spread;
|
||||
}
|
||||
else
|
||||
op->spread.send = FALSE;
|
||||
|
||||
if (!gdk_rgba_equal (color, ¤t_program_state->inset_shadow.color))
|
||||
{
|
||||
op->color.value = color;
|
||||
op->color.send = TRUE;
|
||||
|
||||
current_program_state->inset_shadow.color = *color;
|
||||
}
|
||||
else
|
||||
op->color.send = FALSE;
|
||||
|
||||
if (dx != current_program_state->inset_shadow.dx ||
|
||||
dy != current_program_state->inset_shadow.dy)
|
||||
{
|
||||
op->offset.value[0] = dx;
|
||||
op->offset.value[1] = dy;
|
||||
op->offset.send = TRUE;
|
||||
|
||||
current_program_state->inset_shadow.dx = dx;
|
||||
current_program_state->inset_shadow.dy = dy;
|
||||
}
|
||||
else
|
||||
op->offset.send = FALSE;
|
||||
|
||||
if (!op->outline.send &&
|
||||
!op->spread.send &&
|
||||
!op->offset.send &&
|
||||
!op->color.send)
|
||||
{
|
||||
op_buffer_pop_tail (&self->render_ops);
|
||||
}
|
||||
}
|
||||
void
|
||||
ops_set_unblurred_outset_shadow (RenderOpBuilder *self,
|
||||
const GskRoundedRect outline,
|
||||
float spread,
|
||||
const GdkRGBA *color,
|
||||
float dx,
|
||||
float dy)
|
||||
{
|
||||
ProgramState *current_program_state = get_current_program_state (self);
|
||||
OpShadow *op;
|
||||
|
||||
g_assert (current_program_state);
|
||||
|
||||
op = ops_begin (self, OP_CHANGE_UNBLURRED_OUTSET_SHADOW);
|
||||
|
||||
if (!rounded_rect_equal (&outline, ¤t_program_state->unblurred_outset_shadow.outline))
|
||||
{
|
||||
op->outline.value = outline;
|
||||
op->outline.send = TRUE;
|
||||
op->outline.send_corners = !rounded_rect_corners_equal (¤t_program_state->unblurred_outset_shadow.outline,
|
||||
&outline);
|
||||
current_program_state->unblurred_outset_shadow.outline = outline;
|
||||
}
|
||||
else
|
||||
op->outline.send = FALSE;
|
||||
|
||||
if (spread != current_program_state->unblurred_outset_shadow.spread)
|
||||
{
|
||||
op->spread.value = spread;
|
||||
op->spread.send = TRUE;
|
||||
|
||||
current_program_state->unblurred_outset_shadow.spread = spread;
|
||||
}
|
||||
else
|
||||
op->spread.send = FALSE;
|
||||
|
||||
if (!gdk_rgba_equal (color, ¤t_program_state->unblurred_outset_shadow.color))
|
||||
{
|
||||
op->color.value = color;
|
||||
op->color.send = TRUE;
|
||||
|
||||
current_program_state->unblurred_outset_shadow.color = *color;
|
||||
}
|
||||
else
|
||||
op->color.send = FALSE;
|
||||
|
||||
if (dx != current_program_state->unblurred_outset_shadow.dx ||
|
||||
dy != current_program_state->unblurred_outset_shadow.dy)
|
||||
{
|
||||
op->offset.value[0] = dx;
|
||||
op->offset.value[1] = dy;
|
||||
op->offset.send = TRUE;
|
||||
|
||||
current_program_state->unblurred_outset_shadow.dx = dx;
|
||||
current_program_state->unblurred_outset_shadow.dy = dy;
|
||||
}
|
||||
else
|
||||
op->offset.send = FALSE;
|
||||
}
|
||||
|
||||
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,
|
||||
float end_y)
|
||||
{
|
||||
ProgramState *current_program_state = get_current_program_state (self);
|
||||
OpLinearGradient *op;
|
||||
const guint real_n_color_stops = MIN (GL_MAX_GRADIENT_STOPS, n_color_stops);
|
||||
|
||||
g_assert (current_program_state);
|
||||
|
||||
op = ops_begin (self, OP_CHANGE_LINEAR_GRADIENT);
|
||||
|
||||
/* We always save the n_color_stops value in the op so the renderer can use it in
|
||||
* cases where we send the color stops, but not n_color_stops */
|
||||
op->n_color_stops.value = real_n_color_stops;
|
||||
if (current_program_state->linear_gradient.n_color_stops != real_n_color_stops)
|
||||
{
|
||||
op->n_color_stops.send = TRUE;
|
||||
current_program_state->linear_gradient.n_color_stops = real_n_color_stops;
|
||||
}
|
||||
else
|
||||
op->n_color_stops.send = FALSE;
|
||||
|
||||
op->color_stops.send = FALSE;
|
||||
if (!op->n_color_stops.send)
|
||||
{
|
||||
g_assert (current_program_state->linear_gradient.n_color_stops == real_n_color_stops);
|
||||
|
||||
for (guint i = 0; i < real_n_color_stops; i ++)
|
||||
{
|
||||
const GskColorStop *s1 = &color_stops[i];
|
||||
const GskColorStop *s2 = ¤t_program_state->linear_gradient.color_stops[i];
|
||||
|
||||
if (s1->offset != s2->offset ||
|
||||
!gdk_rgba_equal (&s1->color, &s2->color))
|
||||
{
|
||||
op->color_stops.send = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
op->color_stops.send = TRUE;
|
||||
|
||||
if (op->color_stops.send)
|
||||
{
|
||||
op->color_stops.value = color_stops;
|
||||
memcpy (¤t_program_state->linear_gradient.color_stops,
|
||||
color_stops,
|
||||
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;
|
||||
op->end_point[1] = end_y;
|
||||
}
|
||||
|
||||
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,
|
||||
float end,
|
||||
float hradius,
|
||||
float vradius)
|
||||
{
|
||||
const guint real_n_color_stops = MIN (GL_MAX_GRADIENT_STOPS, n_color_stops);
|
||||
OpRadialGradient *op;
|
||||
|
||||
/* TODO: State tracking? */
|
||||
|
||||
op = ops_begin (self, OP_CHANGE_RADIAL_GRADIENT);
|
||||
op->n_color_stops.value = real_n_color_stops;
|
||||
op->n_color_stops.send = true;
|
||||
op->color_stops.value = color_stops;
|
||||
op->color_stops.send = true;
|
||||
op->center[0] = center_x;
|
||||
op->center[1] = center_y;
|
||||
op->radius[0] = hradius;
|
||||
op->radius[1] = vradius;
|
||||
op->start = start;
|
||||
op->end = end;
|
||||
op->repeat = repeat;
|
||||
}
|
||||
|
||||
void
|
||||
ops_set_conic_gradient (RenderOpBuilder *self,
|
||||
guint n_color_stops,
|
||||
const GskColorStop *color_stops,
|
||||
float center_x,
|
||||
float center_y,
|
||||
float angle)
|
||||
{
|
||||
const guint real_n_color_stops = MIN (GL_MAX_GRADIENT_STOPS, n_color_stops);
|
||||
OpConicGradient *op;
|
||||
|
||||
/* TODO: State tracking? */
|
||||
|
||||
op = ops_begin (self, OP_CHANGE_CONIC_GRADIENT);
|
||||
op->n_color_stops.value = real_n_color_stops;
|
||||
op->n_color_stops.send = true;
|
||||
op->color_stops.value = color_stops;
|
||||
op->color_stops.send = true;
|
||||
op->center[0] = center_x;
|
||||
op->center[1] = center_y;
|
||||
op->angle = angle;
|
||||
}
|
||||
|
||||
@@ -1,353 +0,0 @@
|
||||
#ifndef __GSK_GL_RENDER_OPS_H__
|
||||
#define __GSK_GL_RENDER_OPS_H__
|
||||
|
||||
#include <glib.h>
|
||||
#include <graphene.h>
|
||||
#include <gdk/gdk.h>
|
||||
|
||||
#include "gskgldriverprivate.h"
|
||||
#include "gskroundedrectprivate.h"
|
||||
#include "gskglrenderer.h"
|
||||
#include "gskrendernodeprivate.h"
|
||||
|
||||
#include "opbuffer.h"
|
||||
|
||||
#define GL_N_VERTICES 6
|
||||
#define GL_N_PROGRAMS 15
|
||||
#define GL_MAX_GRADIENT_STOPS 6
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float scale_x;
|
||||
float scale_y;
|
||||
|
||||
float dx_before;
|
||||
float dy_before;
|
||||
} OpsMatrixMetadata;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GskTransform *transform;
|
||||
OpsMatrixMetadata metadata;
|
||||
} MatrixStackEntry;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GskTransform *modelview;
|
||||
GskRoundedRect clip;
|
||||
graphene_matrix_t projection;
|
||||
int source_texture;
|
||||
graphene_rect_t viewport;
|
||||
float opacity;
|
||||
/* Per-program state */
|
||||
union {
|
||||
GdkRGBA color;
|
||||
struct {
|
||||
graphene_matrix_t matrix;
|
||||
graphene_vec4_t offset;
|
||||
} color_matrix;
|
||||
struct {
|
||||
float widths[4];
|
||||
GdkRGBA color;
|
||||
GskRoundedRect outline;
|
||||
} border;
|
||||
struct {
|
||||
GskRoundedRect outline;
|
||||
float dx;
|
||||
float dy;
|
||||
float spread;
|
||||
GdkRGBA color;
|
||||
} inset_shadow;
|
||||
struct {
|
||||
GskRoundedRect outline;
|
||||
float dx;
|
||||
float dy;
|
||||
float spread;
|
||||
GdkRGBA color;
|
||||
} unblurred_outset_shadow;
|
||||
struct {
|
||||
int n_color_stops;
|
||||
GskColorStop color_stops[GL_MAX_GRADIENT_STOPS];
|
||||
float start_point[2];
|
||||
float end_point[2];
|
||||
} linear_gradient;
|
||||
struct {
|
||||
int n_color_stops;
|
||||
GskColorStop color_stops[GL_MAX_GRADIENT_STOPS];
|
||||
float center[2];
|
||||
float start;
|
||||
float end;
|
||||
float radius[2]; /* h/v */
|
||||
} radial_gradient;
|
||||
struct {
|
||||
float width;
|
||||
float height;
|
||||
int uniform_data_len;
|
||||
guchar uniform_data[32];
|
||||
} gl_shader;
|
||||
};
|
||||
} ProgramState;
|
||||
|
||||
struct _Program
|
||||
{
|
||||
const char *name;
|
||||
|
||||
int index; /* Into the renderer's program array -1 for custom */
|
||||
|
||||
int id;
|
||||
/* Common locations (gl_common)*/
|
||||
int source_location;
|
||||
int position_location;
|
||||
int uv_location;
|
||||
int alpha_location;
|
||||
int viewport_location;
|
||||
int projection_location;
|
||||
int modelview_location;
|
||||
int clip_rect_location;
|
||||
union {
|
||||
struct {
|
||||
int color_location;
|
||||
} color;
|
||||
struct {
|
||||
int color_location;
|
||||
} coloring;
|
||||
struct {
|
||||
int color_matrix_location;
|
||||
int color_offset_location;
|
||||
} color_matrix;
|
||||
struct {
|
||||
int num_color_stops_location;
|
||||
int color_stops_location;
|
||||
int points_location;
|
||||
int repeat_location;
|
||||
} linear_gradient;
|
||||
struct {
|
||||
int num_color_stops_location;
|
||||
int color_stops_location;
|
||||
int geometry_location;
|
||||
int range_location;
|
||||
int repeat_location;
|
||||
} radial_gradient;
|
||||
struct {
|
||||
int num_color_stops_location;
|
||||
int color_stops_location;
|
||||
int geometry_location;
|
||||
} conic_gradient;
|
||||
struct {
|
||||
int blur_radius_location;
|
||||
int blur_size_location;
|
||||
int blur_dir_location;
|
||||
} blur;
|
||||
struct {
|
||||
int color_location;
|
||||
int spread_location;
|
||||
int offset_location;
|
||||
int outline_rect_location;
|
||||
} inset_shadow;
|
||||
struct {
|
||||
int color_location;
|
||||
int outline_rect_location;
|
||||
} outset_shadow;
|
||||
struct {
|
||||
int outline_rect_location;
|
||||
int color_location;
|
||||
int spread_location;
|
||||
int offset_location;
|
||||
} unblurred_outset_shadow;
|
||||
struct {
|
||||
int color_location;
|
||||
int widths_location;
|
||||
int outline_rect_location;
|
||||
} border;
|
||||
struct {
|
||||
int source2_location;
|
||||
int progress_location;
|
||||
} cross_fade;
|
||||
struct {
|
||||
int source2_location;
|
||||
int mode_location;
|
||||
} blend;
|
||||
struct {
|
||||
int child_bounds_location;
|
||||
int texture_rect_location;
|
||||
} repeat;
|
||||
struct {
|
||||
int size_location;
|
||||
int args_locations[8];
|
||||
int texture_locations[4];
|
||||
GError *compile_error;
|
||||
} glshader;
|
||||
};
|
||||
ProgramState state;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
int ref_count;
|
||||
union {
|
||||
Program programs[GL_N_PROGRAMS];
|
||||
struct {
|
||||
Program blend_program;
|
||||
Program blit_program;
|
||||
Program blur_program;
|
||||
Program border_program;
|
||||
Program color_matrix_program;
|
||||
Program color_program;
|
||||
Program coloring_program;
|
||||
Program cross_fade_program;
|
||||
Program inset_shadow_program;
|
||||
Program linear_gradient_program;
|
||||
Program radial_gradient_program;
|
||||
Program conic_gradient_program;
|
||||
Program outset_shadow_program;
|
||||
Program repeat_program;
|
||||
Program unblurred_outset_shadow_program;
|
||||
};
|
||||
};
|
||||
GHashTable *custom_programs; /* GskGLShader -> Program* */
|
||||
} GskGLRendererPrograms;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GskGLRendererPrograms *programs;
|
||||
Program *current_program;
|
||||
int current_render_target;
|
||||
int current_texture;
|
||||
|
||||
graphene_matrix_t current_projection;
|
||||
graphene_rect_t current_viewport;
|
||||
float current_opacity;
|
||||
float dx, dy;
|
||||
float scale_x, scale_y;
|
||||
|
||||
OpBuffer render_ops;
|
||||
GArray *vertices;
|
||||
|
||||
GskGLRenderer *renderer;
|
||||
|
||||
/* Stack of modelview matrices */
|
||||
GArray *mv_stack;
|
||||
GskTransform *current_modelview;
|
||||
|
||||
/* Same thing */
|
||||
GArray *clip_stack;
|
||||
/* Pointer into clip_stack */
|
||||
const GskRoundedRect *current_clip;
|
||||
bool clip_is_rectilinear;
|
||||
} RenderOpBuilder;
|
||||
|
||||
|
||||
void ops_dump_framebuffer (RenderOpBuilder *builder,
|
||||
const char *filename,
|
||||
int width,
|
||||
int height);
|
||||
void ops_init (RenderOpBuilder *builder);
|
||||
void ops_free (RenderOpBuilder *builder);
|
||||
void ops_reset (RenderOpBuilder *builder);
|
||||
void ops_push_debug_group (RenderOpBuilder *builder,
|
||||
const char *text);
|
||||
void ops_pop_debug_group (RenderOpBuilder *builder);
|
||||
|
||||
void ops_finish (RenderOpBuilder *builder);
|
||||
void ops_push_modelview (RenderOpBuilder *builder,
|
||||
GskTransform *transform);
|
||||
void ops_set_modelview (RenderOpBuilder *builder,
|
||||
GskTransform *transform);
|
||||
void ops_pop_modelview (RenderOpBuilder *builder);
|
||||
void ops_set_program (RenderOpBuilder *builder,
|
||||
Program *program);
|
||||
|
||||
void ops_push_clip (RenderOpBuilder *builder,
|
||||
const GskRoundedRect *clip);
|
||||
void ops_pop_clip (RenderOpBuilder *builder);
|
||||
gboolean ops_has_clip (RenderOpBuilder *builder);
|
||||
|
||||
void ops_transform_bounds_modelview (const RenderOpBuilder *builder,
|
||||
const graphene_rect_t *src,
|
||||
graphene_rect_t *dst);
|
||||
|
||||
graphene_matrix_t ops_set_projection (RenderOpBuilder *builder,
|
||||
const graphene_matrix_t *projection);
|
||||
|
||||
graphene_rect_t ops_set_viewport (RenderOpBuilder *builder,
|
||||
const graphene_rect_t *viewport);
|
||||
|
||||
void ops_set_texture (RenderOpBuilder *builder,
|
||||
int texture_id);
|
||||
void ops_set_extra_texture (RenderOpBuilder *builder,
|
||||
int texture_id,
|
||||
int idx);
|
||||
|
||||
int ops_set_render_target (RenderOpBuilder *builder,
|
||||
int render_target_id);
|
||||
|
||||
float ops_set_opacity (RenderOpBuilder *builder,
|
||||
float opacity);
|
||||
void ops_set_color (RenderOpBuilder *builder,
|
||||
const GdkRGBA *color);
|
||||
|
||||
void ops_set_color_matrix (RenderOpBuilder *builder,
|
||||
const graphene_matrix_t *matrix,
|
||||
const graphene_vec4_t *offset);
|
||||
|
||||
void ops_set_border (RenderOpBuilder *builder,
|
||||
const GskRoundedRect *outline);
|
||||
void ops_set_border_width (RenderOpBuilder *builder,
|
||||
const float *widths);
|
||||
|
||||
void ops_set_border_color (RenderOpBuilder *builder,
|
||||
const GdkRGBA *color);
|
||||
void ops_set_inset_shadow (RenderOpBuilder *self,
|
||||
const GskRoundedRect outline,
|
||||
float spread,
|
||||
const GdkRGBA *color,
|
||||
float dx,
|
||||
float dy);
|
||||
void ops_set_gl_shader_args (RenderOpBuilder *builder,
|
||||
GskGLShader *shader,
|
||||
float width,
|
||||
float height,
|
||||
const guchar *uniform_data);
|
||||
void ops_set_unblurred_outset_shadow (RenderOpBuilder *self,
|
||||
const GskRoundedRect outline,
|
||||
float spread,
|
||||
const GdkRGBA *color,
|
||||
float dx,
|
||||
float dy);
|
||||
|
||||
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,
|
||||
float end_y);
|
||||
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,
|
||||
float end,
|
||||
float hradius,
|
||||
float vradius);
|
||||
void ops_set_conic_gradient (RenderOpBuilder *self,
|
||||
guint n_color_stops,
|
||||
const GskColorStop *color_stops,
|
||||
float center_x,
|
||||
float center_y,
|
||||
float angle);
|
||||
|
||||
GskQuadVertex * ops_draw (RenderOpBuilder *builder,
|
||||
const GskQuadVertex vertex_data[GL_N_VERTICES]);
|
||||
|
||||
void ops_offset (RenderOpBuilder *builder,
|
||||
float x,
|
||||
float y);
|
||||
|
||||
gpointer ops_begin (RenderOpBuilder *builder,
|
||||
OpKind kind);
|
||||
OpBuffer *ops_get_buffer (RenderOpBuilder *builder);
|
||||
|
||||
#endif
|
||||
@@ -1,271 +0,0 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "gskglshaderbuilderprivate.h"
|
||||
|
||||
#include "gskdebugprivate.h"
|
||||
|
||||
#include <gdk/gdk.h>
|
||||
#include <epoxy/gl.h>
|
||||
|
||||
void
|
||||
gsk_gl_shader_builder_init (GskGLShaderBuilder *self,
|
||||
const char *common_preamble_resource_path,
|
||||
const char *vs_preamble_resource_path,
|
||||
const char *fs_preamble_resource_path)
|
||||
{
|
||||
memset (self, 0, sizeof (*self));
|
||||
|
||||
self->preamble = g_resources_lookup_data (common_preamble_resource_path, 0, NULL);
|
||||
self->vs_preamble = g_resources_lookup_data (vs_preamble_resource_path, 0, NULL);
|
||||
self->fs_preamble = g_resources_lookup_data (fs_preamble_resource_path, 0, NULL);
|
||||
|
||||
g_assert (self->preamble);
|
||||
g_assert (self->vs_preamble);
|
||||
g_assert (self->fs_preamble);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_gl_shader_builder_finish (GskGLShaderBuilder *self)
|
||||
{
|
||||
g_bytes_unref (self->preamble);
|
||||
g_bytes_unref (self->vs_preamble);
|
||||
g_bytes_unref (self->fs_preamble);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_gl_shader_builder_set_glsl_version (GskGLShaderBuilder *self,
|
||||
int version)
|
||||
{
|
||||
self->version = version;
|
||||
}
|
||||
|
||||
static void
|
||||
prepend_line_numbers (char *code,
|
||||
GString *s)
|
||||
{
|
||||
char *p;
|
||||
int line;
|
||||
|
||||
p = code;
|
||||
line = 1;
|
||||
while (*p)
|
||||
{
|
||||
char *end = strchr (p, '\n');
|
||||
if (end)
|
||||
end = end + 1; /* Include newline */
|
||||
else
|
||||
end = p + strlen (p);
|
||||
|
||||
g_string_append_printf (s, "%3d| ", line++);
|
||||
g_string_append_len (s, p, end - p);
|
||||
|
||||
p = end;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
check_shader_error (int shader_id,
|
||||
int shader_type,
|
||||
const char *resource_path,
|
||||
GError **error)
|
||||
{
|
||||
int status;
|
||||
int log_len;
|
||||
char *buffer;
|
||||
int code_len;
|
||||
char *code;
|
||||
GString *s;
|
||||
|
||||
glGetShaderiv (shader_id, GL_COMPILE_STATUS, &status);
|
||||
|
||||
if (G_LIKELY (status == GL_TRUE))
|
||||
return TRUE;
|
||||
|
||||
glGetShaderiv (shader_id, GL_INFO_LOG_LENGTH, &log_len);
|
||||
buffer = g_malloc0 (log_len + 1);
|
||||
glGetShaderInfoLog (shader_id, log_len, NULL, buffer);
|
||||
|
||||
glGetShaderiv (shader_id, GL_SHADER_SOURCE_LENGTH, &code_len);
|
||||
code = g_malloc0 (code_len + 1);
|
||||
glGetShaderSource (shader_id, code_len, NULL, code);
|
||||
|
||||
s = g_string_new ("");
|
||||
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,
|
||||
s->str,
|
||||
buffer);
|
||||
|
||||
g_string_free (s, TRUE);
|
||||
g_free (buffer);
|
||||
g_free (code);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
print_shader_info (const char *prefix,
|
||||
int shader_id,
|
||||
const char *resource_path)
|
||||
{
|
||||
if (GSK_DEBUG_CHECK(SHADERS))
|
||||
{
|
||||
int code_len;
|
||||
char *code;
|
||||
GString *s;
|
||||
|
||||
glGetShaderiv (shader_id, GL_SHADER_SOURCE_LENGTH, &code_len);
|
||||
code = g_malloc0 (code_len + 1);
|
||||
glGetShaderSource (shader_id, code_len, NULL, code);
|
||||
|
||||
s = g_string_new ("");
|
||||
prepend_line_numbers (code, s);
|
||||
|
||||
g_message ("%s %d, %s:\n%s", prefix, shader_id, resource_path, s->str);
|
||||
g_string_free (s, TRUE);
|
||||
g_free (code);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
gsk_gl_shader_builder_create_program (GskGLShaderBuilder *self,
|
||||
const char *resource_path,
|
||||
const char *extra_fragment_snippet,
|
||||
gsize extra_fragment_length,
|
||||
GError **error)
|
||||
{
|
||||
|
||||
GBytes *source_bytes = g_resources_lookup_data (resource_path, 0, NULL);
|
||||
char version_buffer[64];
|
||||
const char *source;
|
||||
const char *vertex_shader_start;
|
||||
const char *fragment_shader_start;
|
||||
int vertex_id;
|
||||
int fragment_id;
|
||||
int program_id = -1;
|
||||
int status;
|
||||
|
||||
g_assert (source_bytes);
|
||||
|
||||
source = g_bytes_get_data (source_bytes, NULL);
|
||||
vertex_shader_start = strstr (source, "VERTEX_SHADER");
|
||||
fragment_shader_start = strstr (source, "FRAGMENT_SHADER");
|
||||
|
||||
g_assert (vertex_shader_start);
|
||||
g_assert (fragment_shader_start);
|
||||
|
||||
/* They both start at the next newline */
|
||||
vertex_shader_start = strstr (vertex_shader_start, "\n");
|
||||
fragment_shader_start = strstr (fragment_shader_start, "\n");
|
||||
|
||||
g_snprintf (version_buffer, sizeof (version_buffer),
|
||||
"#version %d\n", self->version);
|
||||
|
||||
vertex_id = glCreateShader (GL_VERTEX_SHADER);
|
||||
glShaderSource (vertex_id, 8,
|
||||
(const char *[]) {
|
||||
version_buffer,
|
||||
self->debugging ? "#define GSK_DEBUG 1\n" : "",
|
||||
self->legacy ? "#define GSK_LEGACY 1\n" : "",
|
||||
self->gl3 ? "#define GSK_GL3 1\n" : "",
|
||||
self->gles ? "#define GSK_GLES 1\n" : "",
|
||||
g_bytes_get_data (self->preamble, NULL),
|
||||
g_bytes_get_data (self->vs_preamble, NULL),
|
||||
vertex_shader_start
|
||||
},
|
||||
(int[]) {
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
fragment_shader_start - vertex_shader_start
|
||||
});
|
||||
glCompileShader (vertex_id);
|
||||
|
||||
if (!check_shader_error (vertex_id, GL_VERTEX_SHADER, resource_path, error))
|
||||
{
|
||||
glDeleteShader (vertex_id);
|
||||
goto out;
|
||||
}
|
||||
|
||||
print_shader_info ("Vertex shader", vertex_id, resource_path);
|
||||
|
||||
fragment_id = glCreateShader (GL_FRAGMENT_SHADER);
|
||||
glShaderSource (fragment_id, 9,
|
||||
(const char *[]) {
|
||||
version_buffer,
|
||||
self->debugging ? "#define GSK_DEBUG 1\n" : "",
|
||||
self->legacy ? "#define GSK_LEGACY 1\n" : "",
|
||||
self->gl3 ? "#define GSK_GL3 1\n" : "",
|
||||
self->gles ? "#define GSK_GLES 1\n" : "",
|
||||
g_bytes_get_data (self->preamble, NULL),
|
||||
g_bytes_get_data (self->fs_preamble, NULL),
|
||||
fragment_shader_start,
|
||||
extra_fragment_snippet ? extra_fragment_snippet : ""
|
||||
},
|
||||
(int[]) {
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
extra_fragment_length,
|
||||
});
|
||||
glCompileShader (fragment_id);
|
||||
|
||||
if (!check_shader_error (fragment_id, GL_FRAGMENT_SHADER, resource_path, error))
|
||||
{
|
||||
glDeleteShader (fragment_id);
|
||||
goto out;
|
||||
}
|
||||
|
||||
print_shader_info ("Fragment shader", fragment_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");
|
||||
glLinkProgram (program_id);
|
||||
glDetachShader (program_id, vertex_id);
|
||||
glDetachShader (program_id, fragment_id);
|
||||
|
||||
glGetProgramiv (program_id, GL_LINK_STATUS, &status);
|
||||
if (status == GL_FALSE)
|
||||
{
|
||||
char *buffer = NULL;
|
||||
int log_len = 0;
|
||||
|
||||
glGetProgramiv (program_id, GL_INFO_LOG_LENGTH, &log_len);
|
||||
|
||||
buffer = g_malloc0 (log_len + 1);
|
||||
glGetProgramInfoLog (program_id, log_len, NULL, buffer);
|
||||
|
||||
g_warning ("Linking failure in shader:\n%s", buffer);
|
||||
g_set_error (error, GDK_GL_ERROR, GDK_GL_ERROR_LINK_FAILED,
|
||||
"Linking failure in shader: %s", buffer);
|
||||
|
||||
g_free (buffer);
|
||||
|
||||
glDeleteProgram (program_id);
|
||||
program_id = -1;
|
||||
}
|
||||
|
||||
glDeleteShader (vertex_id);
|
||||
glDeleteShader (fragment_id);
|
||||
|
||||
out:
|
||||
g_bytes_unref (source_bytes);
|
||||
|
||||
return program_id;
|
||||
}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
#ifndef __GSK_SHADER_BUILDER_PRIVATE_H__
|
||||
#define __GSK_SHADER_BUILDER_PRIVATE_H__
|
||||
|
||||
#include <gdk/gdk.h>
|
||||
#include <graphene.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GBytes *preamble;
|
||||
GBytes *vs_preamble;
|
||||
GBytes *fs_preamble;
|
||||
|
||||
int version;
|
||||
|
||||
guint debugging: 1;
|
||||
guint gles: 1;
|
||||
guint gl3: 1;
|
||||
guint legacy: 1;
|
||||
|
||||
} GskGLShaderBuilder;
|
||||
|
||||
|
||||
void gsk_gl_shader_builder_init (GskGLShaderBuilder *self,
|
||||
const char *common_preamble_resource_path,
|
||||
const char *vs_preamble_resource_path,
|
||||
const char *fs_preamble_resource_path);
|
||||
void gsk_gl_shader_builder_finish (GskGLShaderBuilder *self);
|
||||
|
||||
void gsk_gl_shader_builder_set_glsl_version (GskGLShaderBuilder *self,
|
||||
int version);
|
||||
|
||||
int gsk_gl_shader_builder_create_program (GskGLShaderBuilder *self,
|
||||
const char *resource_path,
|
||||
const char *extra_fragment_snippet,
|
||||
gsize extra_fragment_length,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSK_SHADER_BUILDER_PRIVATE_H__ */
|
||||
@@ -1,141 +0,0 @@
|
||||
|
||||
#include "gskglshadowcacheprivate.h"
|
||||
|
||||
#define MAX_UNUSED_FRAMES (16 * 5)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GskRoundedRect outline;
|
||||
float blur_radius;
|
||||
} CacheKey;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GskRoundedRect outline;
|
||||
float blur_radius;
|
||||
|
||||
int texture_id;
|
||||
int unused_frames;
|
||||
} CacheItem;
|
||||
|
||||
static gboolean
|
||||
key_equal (const void *x,
|
||||
const void *y)
|
||||
{
|
||||
const CacheKey *a = x;
|
||||
const CacheKey *b = y;
|
||||
|
||||
return a->blur_radius == b->blur_radius &&
|
||||
graphene_size_equal (&a->outline.corner[0], &b->outline.corner[0]) &&
|
||||
graphene_size_equal (&a->outline.corner[1], &b->outline.corner[1]) &&
|
||||
graphene_size_equal (&a->outline.corner[2], &b->outline.corner[2]) &&
|
||||
graphene_size_equal (&a->outline.corner[3], &b->outline.corner[3]) &&
|
||||
graphene_rect_equal (&a->outline.bounds, &b->outline.bounds);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_gl_shadow_cache_init (GskGLShadowCache *self)
|
||||
{
|
||||
self->textures = g_array_new (FALSE, TRUE, sizeof (CacheItem));
|
||||
}
|
||||
|
||||
void
|
||||
gsk_gl_shadow_cache_free (GskGLShadowCache *self,
|
||||
GskGLDriver *gl_driver)
|
||||
{
|
||||
guint i, p;
|
||||
|
||||
for (i = 0, p = self->textures->len; i < p; i ++)
|
||||
{
|
||||
const CacheItem *item = &g_array_index (self->textures, CacheItem, i);
|
||||
|
||||
gsk_gl_driver_destroy_texture (gl_driver, item->texture_id);
|
||||
}
|
||||
|
||||
g_array_free (self->textures, TRUE);
|
||||
self->textures = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
gsk_gl_shadow_cache_begin_frame (GskGLShadowCache *self,
|
||||
GskGLDriver *gl_driver)
|
||||
{
|
||||
guint i, p;
|
||||
|
||||
for (i = 0, p = self->textures->len; i < p; i ++)
|
||||
{
|
||||
CacheItem *item = &g_array_index (self->textures, CacheItem, i);
|
||||
|
||||
if (item->unused_frames > MAX_UNUSED_FRAMES)
|
||||
{
|
||||
gsk_gl_driver_destroy_texture (gl_driver, item->texture_id);
|
||||
g_array_remove_index_fast (self->textures, i);
|
||||
p --;
|
||||
i --;
|
||||
}
|
||||
else
|
||||
{
|
||||
item->unused_frames ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* XXX
|
||||
* The offset origin should always be at 0/0, or the blur radius should just go
|
||||
* away since it defines the origin position anyway?
|
||||
*/
|
||||
int
|
||||
gsk_gl_shadow_cache_get_texture_id (GskGLShadowCache *self,
|
||||
GskGLDriver *gl_driver,
|
||||
const GskRoundedRect *shadow_rect,
|
||||
float blur_radius)
|
||||
{
|
||||
CacheItem *item= NULL;
|
||||
guint i;
|
||||
|
||||
g_assert (self != NULL);
|
||||
g_assert (gl_driver != NULL);
|
||||
g_assert (shadow_rect != NULL);
|
||||
|
||||
for (i = 0; i < self->textures->len; i ++)
|
||||
{
|
||||
CacheItem *k = &g_array_index (self->textures, CacheItem, i);
|
||||
|
||||
if (key_equal (&(CacheKey){*shadow_rect, blur_radius},
|
||||
&(CacheKey){k->outline, k->blur_radius}))
|
||||
{
|
||||
item = k;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (item == NULL)
|
||||
return 0;
|
||||
|
||||
item->unused_frames = 0;
|
||||
|
||||
g_assert (item->texture_id != 0);
|
||||
|
||||
return item->texture_id;
|
||||
}
|
||||
|
||||
void
|
||||
gsk_gl_shadow_cache_commit (GskGLShadowCache *self,
|
||||
const GskRoundedRect *shadow_rect,
|
||||
float blur_radius,
|
||||
int texture_id)
|
||||
{
|
||||
CacheItem *item;
|
||||
|
||||
g_assert (self != NULL);
|
||||
g_assert (shadow_rect != NULL);
|
||||
g_assert (texture_id > 0);
|
||||
|
||||
g_array_set_size (self->textures, self->textures->len + 1);
|
||||
item = &g_array_index (self->textures, CacheItem, self->textures->len - 1);
|
||||
|
||||
item->outline = *shadow_rect;
|
||||
item->blur_radius = blur_radius;
|
||||
item->unused_frames = 0;
|
||||
item->texture_id = texture_id;
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
|
||||
|
||||
#ifndef __GSK_GL_SHADOW_CACHE_H__
|
||||
#define __GSK_GL_SHADOW_CACHE_H__
|
||||
|
||||
#include <glib.h>
|
||||
#include "gskgldriverprivate.h"
|
||||
#include "gskroundedrect.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GArray *textures;
|
||||
} GskGLShadowCache;
|
||||
|
||||
|
||||
void gsk_gl_shadow_cache_init (GskGLShadowCache *self);
|
||||
void gsk_gl_shadow_cache_free (GskGLShadowCache *self,
|
||||
GskGLDriver *gl_driver);
|
||||
void gsk_gl_shadow_cache_begin_frame (GskGLShadowCache *self,
|
||||
GskGLDriver *gl_driver);
|
||||
int gsk_gl_shadow_cache_get_texture_id (GskGLShadowCache *self,
|
||||
GskGLDriver *gl_driver,
|
||||
const GskRoundedRect *shadow_rect,
|
||||
float blur_radius);
|
||||
void gsk_gl_shadow_cache_commit (GskGLShadowCache *self,
|
||||
const GskRoundedRect *shadow_rect,
|
||||
float blur_radius,
|
||||
int texture_id);
|
||||
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user