summaryrefslogtreecommitdiffstats
path: root/modules
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:30:19 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:30:19 +0000
commit5c1676dfe6d2f3c837a5e074117b45613fd29a72 (patch)
treecbffb45144febf451e54061db2b21395faf94bfe /modules
parentInitial commit. (diff)
downloadgimp-63d1391ab989f6cb1b9abeaca4ec268574f16491.tar.xz
gimp-63d1391ab989f6cb1b9abeaca4ec268574f16491.zip
Adding upstream version 2.10.34.upstream/2.10.34upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'modules')
-rw-r--r--modules/Makefile.am102
-rw-r--r--modules/Makefile.in1247
-rw-r--r--modules/color-selector-cmyk.c421
-rw-r--r--modules/color-selector-water.c545
-rw-r--r--modules/color-selector-wheel.c169
-rw-r--r--modules/controller-dx-dinput.c1122
-rw-r--r--modules/controller-linux-input.c711
-rw-r--r--modules/controller-midi.c893
-rw-r--r--modules/display-filter-clip-warning.c494
-rw-r--r--modules/display-filter-color-blind.c495
-rw-r--r--modules/display-filter-gamma.c228
-rw-r--r--modules/display-filter-high-contrast.c228
-rw-r--r--modules/gimpcolorwheel.c1623
-rw-r--r--modules/gimpcolorwheel.h98
-rw-r--r--modules/gimpinputdevicestore-dx.c482
-rw-r--r--modules/gimpinputdevicestore-gudev.c441
-rw-r--r--modules/gimpinputdevicestore.h42
17 files changed, 9341 insertions, 0 deletions
diff --git a/modules/Makefile.am b/modules/Makefile.am
new file mode 100644
index 0000000..867fb6d
--- /dev/null
+++ b/modules/Makefile.am
@@ -0,0 +1,102 @@
+## Process this file with automake to produce Makefile.in
+
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpmodule = $(top_builddir)/libgimpmodule/libgimpmodule-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+
+if PLATFORM_WIN32
+no_undefined = -no-undefined
+endif
+
+libdir = $(gimpplugindir)/modules
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GEGL_CFLAGS) \
+ $(GTK_CFLAGS) \
+ -I$(includedir)
+
+if PLATFORM_WIN32
+else
+controller_midi_module = libcontroller-midi.la
+endif
+
+if HAVE_LINUX_INPUT
+controller_linux_input_module = libcontroller-linux-input.la
+endif
+
+if HAVE_DX_DINPUT
+controller_dx_dinput_module = libcontroller-dx-dinput.la
+endif
+
+lib_LTLIBRARIES = \
+ libcolor-selector-cmyk.la \
+ libcolor-selector-water.la \
+ libcolor-selector-wheel.la \
+ libdisplay-filter-clip-warning.la \
+ libdisplay-filter-color-blind.la \
+ libdisplay-filter-gamma.la \
+ libdisplay-filter-high-contrast.la \
+ $(controller_midi_module) \
+ $(controller_linux_input_module) \
+ $(controller_dx_dinput_module)
+
+
+modules_libadd = $(libgimpmodule) $(libgimpwidgets) $(GTK_LIBS)
+
+color_selector_libadd = $(libgimpconfig) $(libgimpcolor) $(modules_libadd) $(BABL_LIBS)
+display_filter_libadd = $(libgimpbase) $(libgimpconfig) $(libgimpcolor) $(modules_libadd) $(GEGL_LIBS)
+controller_libadd = $(modules_libadd)
+
+libcolor_selector_cmyk_la_SOURCES = color-selector-cmyk.c
+libcolor_selector_cmyk_la_LDFLAGS = -avoid-version -module $(no_undefined)
+libcolor_selector_cmyk_la_LIBADD = $(libgimpconfig) $(color_selector_libadd)
+
+libcolor_selector_water_la_SOURCES = color-selector-water.c
+libcolor_selector_water_la_LDFLAGS = -avoid-version -module $(no_undefined)
+libcolor_selector_water_la_LIBADD = $(color_selector_libadd)
+
+libcolor_selector_wheel_la_SOURCES = color-selector-wheel.c gimpcolorwheel.c gimpcolorwheel.h
+libcolor_selector_wheel_la_LDFLAGS = -avoid-version -module $(no_undefined)
+libcolor_selector_wheel_la_LIBADD = $(color_selector_libadd)
+
+libdisplay_filter_clip_warning_la_SOURCES = display-filter-clip-warning.c
+libdisplay_filter_clip_warning_la_LDFLAGS = -avoid-version -module $(no_undefined)
+libdisplay_filter_clip_warning_la_LIBADD = $(display_filter_libadd)
+
+libdisplay_filter_color_blind_la_SOURCES = display-filter-color-blind.c
+libdisplay_filter_color_blind_la_LDFLAGS = -avoid-version -module $(no_undefined)
+libdisplay_filter_color_blind_la_LIBADD = $(display_filter_libadd)
+
+libdisplay_filter_gamma_la_SOURCES = display-filter-gamma.c
+libdisplay_filter_gamma_la_LDFLAGS = -avoid-version -module $(no_undefined)
+libdisplay_filter_gamma_la_LIBADD = $(display_filter_libadd)
+
+libdisplay_filter_high_contrast_la_SOURCES = display-filter-high-contrast.c
+libdisplay_filter_high_contrast_la_LDFLAGS = -avoid-version -module $(no_undefined)
+libdisplay_filter_high_contrast_la_LIBADD = $(display_filter_libadd)
+
+libcontroller_linux_input_la_SOURCES = \
+ gimpinputdevicestore-gudev.c \
+ gimpinputdevicestore.h \
+ controller-linux-input.c
+libcontroller_linux_input_la_CFLAGS = $(GUDEV_CFLAGS)
+libcontroller_linux_input_la_LDFLAGS = -avoid-version -module $(no_undefined)
+libcontroller_linux_input_la_LIBADD = \
+ $(controller_libadd) $(GUDEV_LIBS)
+
+libcontroller_dx_dinput_la_SOURCES = \
+ gimpinputdevicestore-dx.c \
+ gimpinputdevicestore.h \
+ controller-dx-dinput.c
+# Use -Wl to avoid libtool lossage
+libcontroller_dx_dinput_la_LDFLAGS = -avoid-version -module $(no_undefined) -Wl,-ldinput8 -Wl,-ldxguid
+libcontroller_dx_dinput_la_LIBADD = \
+ $(controller_libadd) -lrpcrt4
+
+libcontroller_midi_la_SOURCES = controller-midi.c
+libcontroller_midi_la_CFLAGS = $(ALSA_CFLAGS)
+libcontroller_midi_la_LDFLAGS = -avoid-version -module $(no_undefined)
+libcontroller_midi_la_LIBADD = $(controller_libadd) $(ALSA_LIBS)
diff --git a/modules/Makefile.in b/modules/Makefile.in
new file mode 100644
index 0000000..93ff541
--- /dev/null
+++ b/modules/Makefile.in
@@ -0,0 +1,1247 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = modules
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(libdir)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+am__DEPENDENCIES_2 = $(libgimpmodule) $(libgimpwidgets) \
+ $(am__DEPENDENCIES_1)
+am__DEPENDENCIES_3 = $(libgimpconfig) $(libgimpcolor) \
+ $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1)
+libcolor_selector_cmyk_la_DEPENDENCIES = $(libgimpconfig) \
+ $(am__DEPENDENCIES_3)
+am_libcolor_selector_cmyk_la_OBJECTS = color-selector-cmyk.lo
+libcolor_selector_cmyk_la_OBJECTS = \
+ $(am_libcolor_selector_cmyk_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libcolor_selector_cmyk_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libcolor_selector_cmyk_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libcolor_selector_water_la_DEPENDENCIES = $(am__DEPENDENCIES_3)
+am_libcolor_selector_water_la_OBJECTS = color-selector-water.lo
+libcolor_selector_water_la_OBJECTS = \
+ $(am_libcolor_selector_water_la_OBJECTS)
+libcolor_selector_water_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libcolor_selector_water_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libcolor_selector_wheel_la_DEPENDENCIES = $(am__DEPENDENCIES_3)
+am_libcolor_selector_wheel_la_OBJECTS = color-selector-wheel.lo \
+ gimpcolorwheel.lo
+libcolor_selector_wheel_la_OBJECTS = \
+ $(am_libcolor_selector_wheel_la_OBJECTS)
+libcolor_selector_wheel_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libcolor_selector_wheel_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+am__DEPENDENCIES_4 = $(am__DEPENDENCIES_2)
+libcontroller_dx_dinput_la_DEPENDENCIES = $(am__DEPENDENCIES_4)
+am_libcontroller_dx_dinput_la_OBJECTS = gimpinputdevicestore-dx.lo \
+ controller-dx-dinput.lo
+libcontroller_dx_dinput_la_OBJECTS = \
+ $(am_libcontroller_dx_dinput_la_OBJECTS)
+libcontroller_dx_dinput_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libcontroller_dx_dinput_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+@HAVE_DX_DINPUT_TRUE@am_libcontroller_dx_dinput_la_rpath = -rpath \
+@HAVE_DX_DINPUT_TRUE@ $(libdir)
+libcontroller_linux_input_la_DEPENDENCIES = $(am__DEPENDENCIES_4) \
+ $(am__DEPENDENCIES_1)
+am_libcontroller_linux_input_la_OBJECTS = \
+ libcontroller_linux_input_la-gimpinputdevicestore-gudev.lo \
+ libcontroller_linux_input_la-controller-linux-input.lo
+libcontroller_linux_input_la_OBJECTS = \
+ $(am_libcontroller_linux_input_la_OBJECTS)
+libcontroller_linux_input_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(libcontroller_linux_input_la_CFLAGS) $(CFLAGS) \
+ $(libcontroller_linux_input_la_LDFLAGS) $(LDFLAGS) -o $@
+@HAVE_LINUX_INPUT_TRUE@am_libcontroller_linux_input_la_rpath = -rpath \
+@HAVE_LINUX_INPUT_TRUE@ $(libdir)
+libcontroller_midi_la_DEPENDENCIES = $(am__DEPENDENCIES_4) \
+ $(am__DEPENDENCIES_1)
+am_libcontroller_midi_la_OBJECTS = \
+ libcontroller_midi_la-controller-midi.lo
+libcontroller_midi_la_OBJECTS = $(am_libcontroller_midi_la_OBJECTS)
+libcontroller_midi_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(libcontroller_midi_la_CFLAGS) $(CFLAGS) \
+ $(libcontroller_midi_la_LDFLAGS) $(LDFLAGS) -o $@
+@PLATFORM_WIN32_FALSE@am_libcontroller_midi_la_rpath = -rpath \
+@PLATFORM_WIN32_FALSE@ $(libdir)
+am__DEPENDENCIES_5 = $(libgimpbase) $(libgimpconfig) $(libgimpcolor) \
+ $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1)
+libdisplay_filter_clip_warning_la_DEPENDENCIES = \
+ $(am__DEPENDENCIES_5)
+am_libdisplay_filter_clip_warning_la_OBJECTS = \
+ display-filter-clip-warning.lo
+libdisplay_filter_clip_warning_la_OBJECTS = \
+ $(am_libdisplay_filter_clip_warning_la_OBJECTS)
+libdisplay_filter_clip_warning_la_LINK = $(LIBTOOL) $(AM_V_lt) \
+ --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+ $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libdisplay_filter_clip_warning_la_LDFLAGS) $(LDFLAGS) -o $@
+libdisplay_filter_color_blind_la_DEPENDENCIES = $(am__DEPENDENCIES_5)
+am_libdisplay_filter_color_blind_la_OBJECTS = \
+ display-filter-color-blind.lo
+libdisplay_filter_color_blind_la_OBJECTS = \
+ $(am_libdisplay_filter_color_blind_la_OBJECTS)
+libdisplay_filter_color_blind_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) \
+ $(libdisplay_filter_color_blind_la_LDFLAGS) $(LDFLAGS) -o $@
+libdisplay_filter_gamma_la_DEPENDENCIES = $(am__DEPENDENCIES_5)
+am_libdisplay_filter_gamma_la_OBJECTS = display-filter-gamma.lo
+libdisplay_filter_gamma_la_OBJECTS = \
+ $(am_libdisplay_filter_gamma_la_OBJECTS)
+libdisplay_filter_gamma_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libdisplay_filter_gamma_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+libdisplay_filter_high_contrast_la_DEPENDENCIES = \
+ $(am__DEPENDENCIES_5)
+am_libdisplay_filter_high_contrast_la_OBJECTS = \
+ display-filter-high-contrast.lo
+libdisplay_filter_high_contrast_la_OBJECTS = \
+ $(am_libdisplay_filter_high_contrast_la_OBJECTS)
+libdisplay_filter_high_contrast_la_LINK = $(LIBTOOL) $(AM_V_lt) \
+ --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+ $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libdisplay_filter_high_contrast_la_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/color-selector-cmyk.Plo \
+ ./$(DEPDIR)/color-selector-water.Plo \
+ ./$(DEPDIR)/color-selector-wheel.Plo \
+ ./$(DEPDIR)/controller-dx-dinput.Plo \
+ ./$(DEPDIR)/display-filter-clip-warning.Plo \
+ ./$(DEPDIR)/display-filter-color-blind.Plo \
+ ./$(DEPDIR)/display-filter-gamma.Plo \
+ ./$(DEPDIR)/display-filter-high-contrast.Plo \
+ ./$(DEPDIR)/gimpcolorwheel.Plo \
+ ./$(DEPDIR)/gimpinputdevicestore-dx.Plo \
+ ./$(DEPDIR)/libcontroller_linux_input_la-controller-linux-input.Plo \
+ ./$(DEPDIR)/libcontroller_linux_input_la-gimpinputdevicestore-gudev.Plo \
+ ./$(DEPDIR)/libcontroller_midi_la-controller-midi.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libcolor_selector_cmyk_la_SOURCES) \
+ $(libcolor_selector_water_la_SOURCES) \
+ $(libcolor_selector_wheel_la_SOURCES) \
+ $(libcontroller_dx_dinput_la_SOURCES) \
+ $(libcontroller_linux_input_la_SOURCES) \
+ $(libcontroller_midi_la_SOURCES) \
+ $(libdisplay_filter_clip_warning_la_SOURCES) \
+ $(libdisplay_filter_color_blind_la_SOURCES) \
+ $(libdisplay_filter_gamma_la_SOURCES) \
+ $(libdisplay_filter_high_contrast_la_SOURCES)
+DIST_SOURCES = $(libcolor_selector_cmyk_la_SOURCES) \
+ $(libcolor_selector_water_la_SOURCES) \
+ $(libcolor_selector_wheel_la_SOURCES) \
+ $(libcontroller_dx_dinput_la_SOURCES) \
+ $(libcontroller_linux_input_la_SOURCES) \
+ $(libcontroller_midi_la_SOURCES) \
+ $(libdisplay_filter_clip_warning_la_SOURCES) \
+ $(libdisplay_filter_color_blind_la_SOURCES) \
+ $(libdisplay_filter_gamma_la_SOURCES) \
+ $(libdisplay_filter_high_contrast_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = $(gimpplugindir)/modules
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpmodule = $(top_builddir)/libgimpmodule/libgimpmodule-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+@PLATFORM_WIN32_TRUE@no_undefined = -no-undefined
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GEGL_CFLAGS) \
+ $(GTK_CFLAGS) \
+ -I$(includedir)
+
+@PLATFORM_WIN32_FALSE@controller_midi_module = libcontroller-midi.la
+@HAVE_LINUX_INPUT_TRUE@controller_linux_input_module = libcontroller-linux-input.la
+@HAVE_DX_DINPUT_TRUE@controller_dx_dinput_module = libcontroller-dx-dinput.la
+lib_LTLIBRARIES = \
+ libcolor-selector-cmyk.la \
+ libcolor-selector-water.la \
+ libcolor-selector-wheel.la \
+ libdisplay-filter-clip-warning.la \
+ libdisplay-filter-color-blind.la \
+ libdisplay-filter-gamma.la \
+ libdisplay-filter-high-contrast.la \
+ $(controller_midi_module) \
+ $(controller_linux_input_module) \
+ $(controller_dx_dinput_module)
+
+modules_libadd = $(libgimpmodule) $(libgimpwidgets) $(GTK_LIBS)
+color_selector_libadd = $(libgimpconfig) $(libgimpcolor) $(modules_libadd) $(BABL_LIBS)
+display_filter_libadd = $(libgimpbase) $(libgimpconfig) $(libgimpcolor) $(modules_libadd) $(GEGL_LIBS)
+controller_libadd = $(modules_libadd)
+libcolor_selector_cmyk_la_SOURCES = color-selector-cmyk.c
+libcolor_selector_cmyk_la_LDFLAGS = -avoid-version -module $(no_undefined)
+libcolor_selector_cmyk_la_LIBADD = $(libgimpconfig) $(color_selector_libadd)
+libcolor_selector_water_la_SOURCES = color-selector-water.c
+libcolor_selector_water_la_LDFLAGS = -avoid-version -module $(no_undefined)
+libcolor_selector_water_la_LIBADD = $(color_selector_libadd)
+libcolor_selector_wheel_la_SOURCES = color-selector-wheel.c gimpcolorwheel.c gimpcolorwheel.h
+libcolor_selector_wheel_la_LDFLAGS = -avoid-version -module $(no_undefined)
+libcolor_selector_wheel_la_LIBADD = $(color_selector_libadd)
+libdisplay_filter_clip_warning_la_SOURCES = display-filter-clip-warning.c
+libdisplay_filter_clip_warning_la_LDFLAGS = -avoid-version -module $(no_undefined)
+libdisplay_filter_clip_warning_la_LIBADD = $(display_filter_libadd)
+libdisplay_filter_color_blind_la_SOURCES = display-filter-color-blind.c
+libdisplay_filter_color_blind_la_LDFLAGS = -avoid-version -module $(no_undefined)
+libdisplay_filter_color_blind_la_LIBADD = $(display_filter_libadd)
+libdisplay_filter_gamma_la_SOURCES = display-filter-gamma.c
+libdisplay_filter_gamma_la_LDFLAGS = -avoid-version -module $(no_undefined)
+libdisplay_filter_gamma_la_LIBADD = $(display_filter_libadd)
+libdisplay_filter_high_contrast_la_SOURCES = display-filter-high-contrast.c
+libdisplay_filter_high_contrast_la_LDFLAGS = -avoid-version -module $(no_undefined)
+libdisplay_filter_high_contrast_la_LIBADD = $(display_filter_libadd)
+libcontroller_linux_input_la_SOURCES = \
+ gimpinputdevicestore-gudev.c \
+ gimpinputdevicestore.h \
+ controller-linux-input.c
+
+libcontroller_linux_input_la_CFLAGS = $(GUDEV_CFLAGS)
+libcontroller_linux_input_la_LDFLAGS = -avoid-version -module $(no_undefined)
+libcontroller_linux_input_la_LIBADD = \
+ $(controller_libadd) $(GUDEV_LIBS)
+
+libcontroller_dx_dinput_la_SOURCES = \
+ gimpinputdevicestore-dx.c \
+ gimpinputdevicestore.h \
+ controller-dx-dinput.c
+
+# Use -Wl to avoid libtool lossage
+libcontroller_dx_dinput_la_LDFLAGS = -avoid-version -module $(no_undefined) -Wl,-ldinput8 -Wl,-ldxguid
+libcontroller_dx_dinput_la_LIBADD = \
+ $(controller_libadd) -lrpcrt4
+
+libcontroller_midi_la_SOURCES = controller-midi.c
+libcontroller_midi_la_CFLAGS = $(ALSA_CFLAGS)
+libcontroller_midi_la_LDFLAGS = -avoid-version -module $(no_undefined)
+libcontroller_midi_la_LIBADD = $(controller_libadd) $(ALSA_LIBS)
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu modules/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu modules/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libcolor-selector-cmyk.la: $(libcolor_selector_cmyk_la_OBJECTS) $(libcolor_selector_cmyk_la_DEPENDENCIES) $(EXTRA_libcolor_selector_cmyk_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libcolor_selector_cmyk_la_LINK) -rpath $(libdir) $(libcolor_selector_cmyk_la_OBJECTS) $(libcolor_selector_cmyk_la_LIBADD) $(LIBS)
+
+libcolor-selector-water.la: $(libcolor_selector_water_la_OBJECTS) $(libcolor_selector_water_la_DEPENDENCIES) $(EXTRA_libcolor_selector_water_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libcolor_selector_water_la_LINK) -rpath $(libdir) $(libcolor_selector_water_la_OBJECTS) $(libcolor_selector_water_la_LIBADD) $(LIBS)
+
+libcolor-selector-wheel.la: $(libcolor_selector_wheel_la_OBJECTS) $(libcolor_selector_wheel_la_DEPENDENCIES) $(EXTRA_libcolor_selector_wheel_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libcolor_selector_wheel_la_LINK) -rpath $(libdir) $(libcolor_selector_wheel_la_OBJECTS) $(libcolor_selector_wheel_la_LIBADD) $(LIBS)
+
+libcontroller-dx-dinput.la: $(libcontroller_dx_dinput_la_OBJECTS) $(libcontroller_dx_dinput_la_DEPENDENCIES) $(EXTRA_libcontroller_dx_dinput_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libcontroller_dx_dinput_la_LINK) $(am_libcontroller_dx_dinput_la_rpath) $(libcontroller_dx_dinput_la_OBJECTS) $(libcontroller_dx_dinput_la_LIBADD) $(LIBS)
+
+libcontroller-linux-input.la: $(libcontroller_linux_input_la_OBJECTS) $(libcontroller_linux_input_la_DEPENDENCIES) $(EXTRA_libcontroller_linux_input_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libcontroller_linux_input_la_LINK) $(am_libcontroller_linux_input_la_rpath) $(libcontroller_linux_input_la_OBJECTS) $(libcontroller_linux_input_la_LIBADD) $(LIBS)
+
+libcontroller-midi.la: $(libcontroller_midi_la_OBJECTS) $(libcontroller_midi_la_DEPENDENCIES) $(EXTRA_libcontroller_midi_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libcontroller_midi_la_LINK) $(am_libcontroller_midi_la_rpath) $(libcontroller_midi_la_OBJECTS) $(libcontroller_midi_la_LIBADD) $(LIBS)
+
+libdisplay-filter-clip-warning.la: $(libdisplay_filter_clip_warning_la_OBJECTS) $(libdisplay_filter_clip_warning_la_DEPENDENCIES) $(EXTRA_libdisplay_filter_clip_warning_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libdisplay_filter_clip_warning_la_LINK) -rpath $(libdir) $(libdisplay_filter_clip_warning_la_OBJECTS) $(libdisplay_filter_clip_warning_la_LIBADD) $(LIBS)
+
+libdisplay-filter-color-blind.la: $(libdisplay_filter_color_blind_la_OBJECTS) $(libdisplay_filter_color_blind_la_DEPENDENCIES) $(EXTRA_libdisplay_filter_color_blind_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libdisplay_filter_color_blind_la_LINK) -rpath $(libdir) $(libdisplay_filter_color_blind_la_OBJECTS) $(libdisplay_filter_color_blind_la_LIBADD) $(LIBS)
+
+libdisplay-filter-gamma.la: $(libdisplay_filter_gamma_la_OBJECTS) $(libdisplay_filter_gamma_la_DEPENDENCIES) $(EXTRA_libdisplay_filter_gamma_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libdisplay_filter_gamma_la_LINK) -rpath $(libdir) $(libdisplay_filter_gamma_la_OBJECTS) $(libdisplay_filter_gamma_la_LIBADD) $(LIBS)
+
+libdisplay-filter-high-contrast.la: $(libdisplay_filter_high_contrast_la_OBJECTS) $(libdisplay_filter_high_contrast_la_DEPENDENCIES) $(EXTRA_libdisplay_filter_high_contrast_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libdisplay_filter_high_contrast_la_LINK) -rpath $(libdir) $(libdisplay_filter_high_contrast_la_OBJECTS) $(libdisplay_filter_high_contrast_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/color-selector-cmyk.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/color-selector-water.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/color-selector-wheel.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/controller-dx-dinput.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/display-filter-clip-warning.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/display-filter-color-blind.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/display-filter-gamma.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/display-filter-high-contrast.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcolorwheel.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpinputdevicestore-dx.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcontroller_linux_input_la-controller-linux-input.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcontroller_linux_input_la-gimpinputdevicestore-gudev.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcontroller_midi_la-controller-midi.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+libcontroller_linux_input_la-gimpinputdevicestore-gudev.lo: gimpinputdevicestore-gudev.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcontroller_linux_input_la_CFLAGS) $(CFLAGS) -MT libcontroller_linux_input_la-gimpinputdevicestore-gudev.lo -MD -MP -MF $(DEPDIR)/libcontroller_linux_input_la-gimpinputdevicestore-gudev.Tpo -c -o libcontroller_linux_input_la-gimpinputdevicestore-gudev.lo `test -f 'gimpinputdevicestore-gudev.c' || echo '$(srcdir)/'`gimpinputdevicestore-gudev.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcontroller_linux_input_la-gimpinputdevicestore-gudev.Tpo $(DEPDIR)/libcontroller_linux_input_la-gimpinputdevicestore-gudev.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gimpinputdevicestore-gudev.c' object='libcontroller_linux_input_la-gimpinputdevicestore-gudev.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcontroller_linux_input_la_CFLAGS) $(CFLAGS) -c -o libcontroller_linux_input_la-gimpinputdevicestore-gudev.lo `test -f 'gimpinputdevicestore-gudev.c' || echo '$(srcdir)/'`gimpinputdevicestore-gudev.c
+
+libcontroller_linux_input_la-controller-linux-input.lo: controller-linux-input.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcontroller_linux_input_la_CFLAGS) $(CFLAGS) -MT libcontroller_linux_input_la-controller-linux-input.lo -MD -MP -MF $(DEPDIR)/libcontroller_linux_input_la-controller-linux-input.Tpo -c -o libcontroller_linux_input_la-controller-linux-input.lo `test -f 'controller-linux-input.c' || echo '$(srcdir)/'`controller-linux-input.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcontroller_linux_input_la-controller-linux-input.Tpo $(DEPDIR)/libcontroller_linux_input_la-controller-linux-input.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='controller-linux-input.c' object='libcontroller_linux_input_la-controller-linux-input.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcontroller_linux_input_la_CFLAGS) $(CFLAGS) -c -o libcontroller_linux_input_la-controller-linux-input.lo `test -f 'controller-linux-input.c' || echo '$(srcdir)/'`controller-linux-input.c
+
+libcontroller_midi_la-controller-midi.lo: controller-midi.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcontroller_midi_la_CFLAGS) $(CFLAGS) -MT libcontroller_midi_la-controller-midi.lo -MD -MP -MF $(DEPDIR)/libcontroller_midi_la-controller-midi.Tpo -c -o libcontroller_midi_la-controller-midi.lo `test -f 'controller-midi.c' || echo '$(srcdir)/'`controller-midi.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcontroller_midi_la-controller-midi.Tpo $(DEPDIR)/libcontroller_midi_la-controller-midi.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='controller-midi.c' object='libcontroller_midi_la-controller-midi.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcontroller_midi_la_CFLAGS) $(CFLAGS) -c -o libcontroller_midi_la-controller-midi.lo `test -f 'controller-midi.c' || echo '$(srcdir)/'`controller-midi.c
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/color-selector-cmyk.Plo
+ -rm -f ./$(DEPDIR)/color-selector-water.Plo
+ -rm -f ./$(DEPDIR)/color-selector-wheel.Plo
+ -rm -f ./$(DEPDIR)/controller-dx-dinput.Plo
+ -rm -f ./$(DEPDIR)/display-filter-clip-warning.Plo
+ -rm -f ./$(DEPDIR)/display-filter-color-blind.Plo
+ -rm -f ./$(DEPDIR)/display-filter-gamma.Plo
+ -rm -f ./$(DEPDIR)/display-filter-high-contrast.Plo
+ -rm -f ./$(DEPDIR)/gimpcolorwheel.Plo
+ -rm -f ./$(DEPDIR)/gimpinputdevicestore-dx.Plo
+ -rm -f ./$(DEPDIR)/libcontroller_linux_input_la-controller-linux-input.Plo
+ -rm -f ./$(DEPDIR)/libcontroller_linux_input_la-gimpinputdevicestore-gudev.Plo
+ -rm -f ./$(DEPDIR)/libcontroller_midi_la-controller-midi.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/color-selector-cmyk.Plo
+ -rm -f ./$(DEPDIR)/color-selector-water.Plo
+ -rm -f ./$(DEPDIR)/color-selector-wheel.Plo
+ -rm -f ./$(DEPDIR)/controller-dx-dinput.Plo
+ -rm -f ./$(DEPDIR)/display-filter-clip-warning.Plo
+ -rm -f ./$(DEPDIR)/display-filter-color-blind.Plo
+ -rm -f ./$(DEPDIR)/display-filter-gamma.Plo
+ -rm -f ./$(DEPDIR)/display-filter-high-contrast.Plo
+ -rm -f ./$(DEPDIR)/gimpcolorwheel.Plo
+ -rm -f ./$(DEPDIR)/gimpinputdevicestore-dx.Plo
+ -rm -f ./$(DEPDIR)/libcontroller_linux_input_la-controller-linux-input.Plo
+ -rm -f ./$(DEPDIR)/libcontroller_linux_input_la-gimpinputdevicestore-gudev.Plo
+ -rm -f ./$(DEPDIR)/libcontroller_midi_la-controller-midi.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libLTLIBRARIES
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libLTLIBRARIES clean-libtool cscopelist-am \
+ ctags ctags-am distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-libLTLIBRARIES install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am uninstall-libLTLIBRARIES
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/modules/color-selector-cmyk.c b/modules/color-selector-cmyk.c
new file mode 100644
index 0000000..7fba514
--- /dev/null
+++ b/modules/color-selector-cmyk.c
@@ -0,0 +1,421 @@
+/* GIMP CMYK ColorSelector using littleCMS
+ * Copyright (C) 2006 Sven Neumann <sven@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpconfig/gimpconfig.h"
+#include "libgimpmodule/gimpmodule.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/* definitions and variables */
+
+#define COLORSEL_TYPE_CMYK (colorsel_cmyk_get_type ())
+#define COLORSEL_CMYK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), COLORSEL_TYPE_CMYK, ColorselCmyk))
+#define COLORSEL_CMYK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), COLORSEL_TYPE_CMYK, ColorselCmykClass))
+#define COLORSEL_IS_CMYK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), COLORSEL_TYPE_CMYK))
+#define COLORSEL_IS_CMYK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), COLORSEL_TYPE_CMYK))
+
+
+typedef struct _ColorselCmyk ColorselCmyk;
+typedef struct _ColorselCmykClass ColorselCmykClass;
+
+struct _ColorselCmyk
+{
+ GimpColorSelector parent_instance;
+
+ GimpColorConfig *config;
+ GimpColorTransform *rgb2cmyk;
+ GimpColorTransform *cmyk2rgb;
+
+ GimpCMYK cmyk;
+ GtkAdjustment *adj[4];
+ GtkWidget *name_label;
+
+ gboolean in_destruction;
+};
+
+struct _ColorselCmykClass
+{
+ GimpColorSelectorClass parent_class;
+};
+
+
+static GType colorsel_cmyk_get_type (void);
+
+static void colorsel_cmyk_dispose (GObject *object);
+
+static void colorsel_cmyk_set_color (GimpColorSelector *selector,
+ const GimpRGB *rgb,
+ const GimpHSV *hsv);
+static void colorsel_cmyk_set_config (GimpColorSelector *selector,
+ GimpColorConfig *config);
+
+static void colorsel_cmyk_adj_update (GtkAdjustment *adj,
+ ColorselCmyk *module);
+static void colorsel_cmyk_config_changed (ColorselCmyk *module);
+
+
+static const GimpModuleInfo colorsel_cmyk_info =
+{
+ GIMP_MODULE_ABI_VERSION,
+ N_("CMYK color selector (using color profile)"),
+ "Sven Neumann <sven@gimp.org>",
+ "v0.1",
+ "(c) 2006, released under the GPL",
+ "September 2006"
+};
+
+
+G_DEFINE_DYNAMIC_TYPE (ColorselCmyk, colorsel_cmyk,
+ GIMP_TYPE_COLOR_SELECTOR)
+
+
+G_MODULE_EXPORT const GimpModuleInfo *
+gimp_module_query (GTypeModule *module)
+{
+ return &colorsel_cmyk_info;
+}
+
+G_MODULE_EXPORT gboolean
+gimp_module_register (GTypeModule *module)
+{
+ colorsel_cmyk_register_type (module);
+
+ return TRUE;
+}
+
+static void
+colorsel_cmyk_class_init (ColorselCmykClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpColorSelectorClass *selector_class = GIMP_COLOR_SELECTOR_CLASS (klass);
+
+ object_class->dispose = colorsel_cmyk_dispose;
+
+ selector_class->name = _("CMYK");
+ selector_class->help_id = "gimp-colorselector-cmyk";
+ selector_class->icon_name = GIMP_ICON_COLOR_SELECTOR_CMYK;
+ selector_class->set_color = colorsel_cmyk_set_color;
+ selector_class->set_config = colorsel_cmyk_set_config;
+}
+
+static void
+colorsel_cmyk_class_finalize (ColorselCmykClass *klass)
+{
+}
+
+static void
+colorsel_cmyk_init (ColorselCmyk *module)
+{
+ GtkWidget *table;
+ GtkObject *adj;
+ gint i;
+
+ static const gchar * const cmyk_labels[] =
+ {
+ /* Cyan */
+ N_("_C"),
+ /* Magenta */
+ N_("_M"),
+ /* Yellow */
+ N_("_Y"),
+ /* Key (Black) */
+ N_("_K")
+ };
+ static const gchar * const cmyk_tips[] =
+ {
+ N_("Cyan"),
+ N_("Magenta"),
+ N_("Yellow"),
+ N_("Black")
+ };
+
+ module->config = NULL;
+ module->rgb2cmyk = NULL;
+ module->cmyk2rgb = NULL;
+
+ gtk_box_set_spacing (GTK_BOX (module), 6);
+
+ table = gtk_table_new (4, 4, FALSE);
+
+ gtk_table_set_row_spacings (GTK_TABLE (table), 1);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 2);
+ gtk_table_set_col_spacing (GTK_TABLE (table), 0, 0);
+
+ gtk_box_pack_start (GTK_BOX (module), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ for (i = 0; i < 4; i++)
+ {
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 1, i,
+ gettext (cmyk_labels[i]),
+ -1, -1,
+ 0.0,
+ 0.0, 100.0,
+ 1.0, 10.0,
+ 0,
+ TRUE, 0.0, 0.0,
+ gettext (cmyk_tips[i]),
+ NULL);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (colorsel_cmyk_adj_update),
+ module);
+
+ module->adj[i] = GTK_ADJUSTMENT (adj);
+ }
+
+ module->name_label = gtk_label_new (NULL);
+ gtk_label_set_xalign (GTK_LABEL (module->name_label), 0.0);
+ gtk_label_set_ellipsize (GTK_LABEL (module->name_label), PANGO_ELLIPSIZE_END);
+ gimp_label_set_attributes (GTK_LABEL (module->name_label),
+ PANGO_ATTR_SCALE, PANGO_SCALE_SMALL,
+ -1);
+ gtk_box_pack_start (GTK_BOX (module), module->name_label, FALSE, FALSE, 0);
+ gtk_widget_show (module->name_label);
+}
+
+static void
+colorsel_cmyk_dispose (GObject *object)
+{
+ ColorselCmyk *module = COLORSEL_CMYK (object);
+
+ module->in_destruction = TRUE;
+
+ colorsel_cmyk_set_config (GIMP_COLOR_SELECTOR (object), NULL);
+
+ G_OBJECT_CLASS (colorsel_cmyk_parent_class)->dispose (object);
+}
+
+static void
+colorsel_cmyk_set_color (GimpColorSelector *selector,
+ const GimpRGB *rgb,
+ const GimpHSV *hsv)
+{
+ ColorselCmyk *module = COLORSEL_CMYK (selector);
+ gdouble values[4];
+ gint i;
+
+ if (module->rgb2cmyk)
+ {
+ gdouble rgb_values[3];
+ gdouble cmyk_values[4];
+
+ rgb_values[0] = rgb->r;
+ rgb_values[1] = rgb->g;
+ rgb_values[2] = rgb->b;
+
+ gimp_color_transform_process_pixels (module->rgb2cmyk,
+ babl_format ("R'G'B' double"),
+ rgb_values,
+ babl_format ("CMYK double"),
+ cmyk_values,
+ 1);
+
+ module->cmyk.c = cmyk_values[0] / 100.0;
+ module->cmyk.m = cmyk_values[1] / 100.0;
+ module->cmyk.y = cmyk_values[2] / 100.0;
+ module->cmyk.k = cmyk_values[3] / 100.0;
+ }
+ else
+ {
+ gimp_rgb_to_cmyk (rgb, 1.0, &module->cmyk);
+ }
+
+ values[0] = module->cmyk.c * 100.0;
+ values[1] = module->cmyk.m * 100.0;
+ values[2] = module->cmyk.y * 100.0;
+ values[3] = module->cmyk.k * 100.0;
+
+ for (i = 0; i < 4; i++)
+ {
+ g_signal_handlers_block_by_func (module->adj[i],
+ colorsel_cmyk_adj_update,
+ module);
+
+ gtk_adjustment_set_value (module->adj[i], values[i]);
+
+ g_signal_handlers_unblock_by_func (module->adj[i],
+ colorsel_cmyk_adj_update,
+ module);
+ }
+}
+
+static void
+colorsel_cmyk_set_config (GimpColorSelector *selector,
+ GimpColorConfig *config)
+{
+ ColorselCmyk *module = COLORSEL_CMYK (selector);
+
+ if (config != module->config)
+ {
+ if (module->config)
+ g_signal_handlers_disconnect_by_func (module->config,
+ colorsel_cmyk_config_changed,
+ module);
+
+ g_set_object (&module->config, config);
+
+ if (module->config)
+ g_signal_connect_swapped (module->config, "notify",
+ G_CALLBACK (colorsel_cmyk_config_changed),
+ module);
+
+ colorsel_cmyk_config_changed (module);
+ }
+}
+
+static void
+colorsel_cmyk_adj_update (GtkAdjustment *adj,
+ ColorselCmyk *module)
+{
+ GimpColorSelector *selector = GIMP_COLOR_SELECTOR (module);
+ gint i;
+ gdouble value;
+
+ for (i = 0; i < 4; i++)
+ if (module->adj[i] == adj)
+ break;
+
+ value = gtk_adjustment_get_value (adj) / 100.0;
+
+ switch (i)
+ {
+ case 0:
+ module->cmyk.c = value;
+ break;
+ case 1:
+ module->cmyk.m = value;
+ break;
+ case 2:
+ module->cmyk.y = value;
+ break;
+ case 3:
+ module->cmyk.k = value;
+ break;
+ default:
+ return;
+ }
+
+ if (module->cmyk2rgb)
+ {
+ gdouble cmyk_values[4];
+ gdouble rgb_values[3];
+
+ cmyk_values[0] = module->cmyk.c * 100.0;
+ cmyk_values[1] = module->cmyk.m * 100.0;
+ cmyk_values[2] = module->cmyk.y * 100.0;
+ cmyk_values[3] = module->cmyk.k * 100.0;
+
+ gimp_color_transform_process_pixels (module->cmyk2rgb,
+ babl_format ("CMYK double"),
+ cmyk_values,
+ babl_format ("R'G'B' double"),
+ rgb_values,
+ 1);
+
+ selector->rgb.r = rgb_values[0];
+ selector->rgb.g = rgb_values[1];
+ selector->rgb.b = rgb_values[2];
+ }
+ else
+ {
+ gimp_cmyk_to_rgb (&module->cmyk, &selector->rgb);
+ }
+
+ gimp_rgb_to_hsv (&selector->rgb, &selector->hsv);
+
+ gimp_color_selector_color_changed (selector);
+}
+
+static void
+colorsel_cmyk_config_changed (ColorselCmyk *module)
+{
+ GimpColorSelector *selector = GIMP_COLOR_SELECTOR (module);
+ GimpColorConfig *config = module->config;
+ GimpColorTransformFlags flags = 0;
+ GimpColorProfile *rgb_profile = NULL;
+ GimpColorProfile *cmyk_profile = NULL;
+ gchar *text;
+
+ if (module->rgb2cmyk)
+ {
+ g_object_unref (module->rgb2cmyk);
+ module->rgb2cmyk = NULL;
+ }
+
+ if (module->cmyk2rgb)
+ {
+ g_object_unref (module->cmyk2rgb);
+ module->cmyk2rgb = NULL;
+ }
+
+ gtk_label_set_text (GTK_LABEL (module->name_label), _("Profile: (none)"));
+ gimp_help_set_help_data (module->name_label, NULL, NULL);
+
+ if (! config)
+ goto out;
+
+ cmyk_profile = gimp_color_config_get_cmyk_color_profile (config, NULL);
+ if (! cmyk_profile)
+ goto out;
+
+ rgb_profile = gimp_color_profile_new_rgb_srgb ();
+
+ text = g_strdup_printf (_("Profile: %s"),
+ gimp_color_profile_get_label (cmyk_profile));
+ gtk_label_set_text (GTK_LABEL (module->name_label), text);
+ g_free (text);
+
+ gimp_help_set_help_data (module->name_label,
+ gimp_color_profile_get_summary (cmyk_profile),
+ NULL);
+
+ flags |= GIMP_COLOR_TRANSFORM_FLAGS_NOOPTIMIZE;
+ flags |= GIMP_COLOR_TRANSFORM_FLAGS_BLACK_POINT_COMPENSATION;
+
+ module->rgb2cmyk = gimp_color_transform_new (rgb_profile,
+ babl_format ("R'G'B' double"),
+ cmyk_profile,
+ babl_format ("CMYK double"),
+ GIMP_COLOR_RENDERING_INTENT_PERCEPTUAL,
+ flags);
+ module->cmyk2rgb = gimp_color_transform_new (cmyk_profile,
+ babl_format ("CMYK double"),
+ rgb_profile,
+ babl_format ("R'G'B' double"),
+ GIMP_COLOR_RENDERING_INTENT_PERCEPTUAL,
+ flags);
+
+ out:
+
+ if (rgb_profile)
+ g_object_unref (rgb_profile);
+
+ if (cmyk_profile)
+ g_object_unref (cmyk_profile);
+
+ if (! module->in_destruction)
+ colorsel_cmyk_set_color (selector, &selector->rgb, &selector->hsv);
+}
diff --git a/modules/color-selector-water.c b/modules/color-selector-water.c
new file mode 100644
index 0000000..faec37d
--- /dev/null
+++ b/modules/color-selector-water.c
@@ -0,0 +1,545 @@
+/* Watercolor color_select_module, Raph Levien <raph@acm.org>, February 1998
+ *
+ * Ported to loadable color-selector, Sven Neumann <sven@gimp.org>, May 1999
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpmath/gimpmath.h"
+#include "libgimpmodule/gimpmodule.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+#define COLORSEL_TYPE_WATER (colorsel_water_get_type ())
+#define COLORSEL_WATER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), COLORSEL_TYPE_WATER, ColorselWater))
+#define COLORSEL_WATER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), COLORSEL_TYPE_WATER, ColorselWaterClass))
+#define COLORSEL_IS_WATER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), COLORSEL_TYPE_WATER))
+#define COLORSEL_IS_WATER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), COLORSEL_TYPE_WATER))
+
+
+typedef struct _ColorselWater ColorselWater;
+typedef struct _ColorselWaterClass ColorselWaterClass;
+
+struct _ColorselWater
+{
+ GimpColorSelector parent_instance;
+
+ GtkWidget *area;
+
+ gdouble last_x;
+ gdouble last_y;
+
+ gfloat pressure_adjust;
+ guint32 motion_time;
+
+ GimpColorConfig *config;
+ GimpColorTransform *transform;
+};
+
+struct _ColorselWaterClass
+{
+ GimpColorSelectorClass parent_class;
+};
+
+
+static GType colorsel_water_get_type (void);
+
+static void colorsel_water_dispose (GObject *object);
+
+static void colorsel_water_set_config (GimpColorSelector *selector,
+ GimpColorConfig *config);
+
+static void colorsel_water_create_transform (ColorselWater *water);
+static void colorsel_water_destroy_transform (ColorselWater *water);
+
+static gboolean select_area_expose (GtkWidget *widget,
+ GdkEventExpose *event,
+ ColorselWater *water);
+static gboolean button_press_event (GtkWidget *widget,
+ GdkEventButton *event,
+ ColorselWater *water);
+static gboolean motion_notify_event (GtkWidget *widget,
+ GdkEventMotion *event,
+ ColorselWater *water);
+static gboolean proximity_out_event (GtkWidget *widget,
+ GdkEventProximity *event,
+ ColorselWater *water);
+static void pressure_adjust_update (GtkAdjustment *adj,
+ ColorselWater *water);
+
+
+static const GimpModuleInfo colorsel_water_info =
+{
+ GIMP_MODULE_ABI_VERSION,
+ N_("Watercolor style color selector"),
+ "Raph Levien <raph@acm.org>, Sven Neumann <sven@gimp.org>",
+ "v0.4",
+ "released under the GPL",
+ "1998-2006"
+};
+
+
+G_DEFINE_DYNAMIC_TYPE (ColorselWater, colorsel_water,
+ GIMP_TYPE_COLOR_SELECTOR)
+
+
+G_MODULE_EXPORT const GimpModuleInfo *
+gimp_module_query (GTypeModule *module)
+{
+ return &colorsel_water_info;
+}
+
+G_MODULE_EXPORT gboolean
+gimp_module_register (GTypeModule *module)
+{
+ colorsel_water_register_type (module);
+
+ return TRUE;
+}
+
+static void
+colorsel_water_class_init (ColorselWaterClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpColorSelectorClass *selector_class = GIMP_COLOR_SELECTOR_CLASS (klass);
+
+ object_class->dispose = colorsel_water_dispose;
+
+ selector_class->name = _("Watercolor");
+ selector_class->help_id = "gimp-colorselector-watercolor";
+ selector_class->icon_name = GIMP_ICON_COLOR_SELECTOR_WATER;
+ selector_class->set_config = colorsel_water_set_config;
+}
+
+static void
+colorsel_water_class_finalize (ColorselWaterClass *klass)
+{
+}
+
+static void
+colorsel_water_init (ColorselWater *water)
+{
+ GtkWidget *hbox;
+ GtkWidget *frame;
+ GtkAdjustment *adj;
+ GtkWidget *scale;
+
+ colorsel_water_get_type (); /* useless function call to silence compiler */
+
+ water->pressure_adjust = 1.0;
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
+ gtk_box_pack_start (GTK_BOX (water), hbox, TRUE, TRUE, 0);
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
+
+ water->area = gtk_drawing_area_new ();
+ gtk_container_add (GTK_CONTAINER (frame), water->area);
+ g_signal_connect (water->area, "expose-event",
+ G_CALLBACK (select_area_expose),
+ water);
+
+ /* Event signals */
+ g_signal_connect (water->area, "motion-notify-event",
+ G_CALLBACK (motion_notify_event),
+ water);
+ g_signal_connect (water->area, "button-press-event",
+ G_CALLBACK (button_press_event),
+ water);
+ g_signal_connect (water->area, "proximity-out-event",
+ G_CALLBACK (proximity_out_event),
+ water);
+
+ gtk_widget_add_events (water->area,
+ GDK_LEAVE_NOTIFY_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_KEY_PRESS_MASK |
+ GDK_POINTER_MOTION_MASK |
+ GDK_POINTER_MOTION_HINT_MASK |
+ GDK_PROXIMITY_OUT_MASK);
+
+ /* The following call enables tracking and processing of extension
+ * events for the drawing area
+ */
+ gtk_widget_set_extension_events (water->area, GDK_EXTENSION_EVENTS_ALL);
+ gtk_widget_grab_focus (water->area);
+
+ adj = GTK_ADJUSTMENT (gtk_adjustment_new (200.0 - water->pressure_adjust * 100.0,
+ 0.0, 200.0, 1.0, 1.0, 0.0));
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (pressure_adjust_update),
+ water);
+
+ scale = gtk_scale_new (GTK_ORIENTATION_VERTICAL, adj);
+ gtk_scale_set_digits (GTK_SCALE (scale), 0);
+ gtk_scale_set_draw_value (GTK_SCALE (scale), FALSE);
+ gimp_help_set_help_data (scale, _("Pressure"), NULL);
+ gtk_box_pack_start (GTK_BOX (hbox), scale, FALSE, FALSE, 0);
+
+ gtk_widget_show_all (hbox);
+
+ gimp_widget_track_monitor (GTK_WIDGET (water),
+ G_CALLBACK (colorsel_water_destroy_transform),
+ NULL);
+}
+
+static gdouble
+calc (gdouble x,
+ gdouble y,
+ gdouble angle)
+{
+ gdouble s = 2.0 * sin (angle * G_PI / 180.0) * 256.0;
+ gdouble c = 2.0 * cos (angle * G_PI / 180.0) * 256.0;
+
+ return 128 + (x - 0.5) * c - (y - 0.5) * s;
+}
+
+static void
+colorsel_water_dispose (GObject *object)
+{
+ colorsel_water_set_config (GIMP_COLOR_SELECTOR (object), NULL);
+
+ G_OBJECT_CLASS (colorsel_water_parent_class)->dispose (object);
+}
+
+static void
+colorsel_water_set_config (GimpColorSelector *selector,
+ GimpColorConfig *config)
+{
+ ColorselWater *water = COLORSEL_WATER (selector);
+
+ if (config != water->config)
+ {
+ if (water->config)
+ {
+ g_signal_handlers_disconnect_by_func (water->config,
+ colorsel_water_destroy_transform,
+ water);
+
+ colorsel_water_destroy_transform (water);
+ }
+
+ g_set_object (&water->config, config);
+
+ if (water->config)
+ {
+ g_signal_connect_swapped (water->config, "notify",
+ G_CALLBACK (colorsel_water_destroy_transform),
+ water);
+ }
+ }
+}
+
+static void
+colorsel_water_create_transform (ColorselWater *water)
+{
+ if (water->config)
+ {
+ static GimpColorProfile *profile = NULL;
+
+ const Babl *format = babl_format ("cairo-RGB24");
+
+ if (G_UNLIKELY (! profile))
+ profile = gimp_color_profile_new_rgb_srgb ();
+
+ water->transform = gimp_widget_get_color_transform (water->area,
+ water->config,
+ profile,
+ format,
+ format);
+ }
+}
+
+static void
+colorsel_water_destroy_transform (ColorselWater *water)
+{
+ if (water->transform)
+ {
+ g_object_unref (water->transform);
+ water->transform = NULL;
+ }
+
+ gtk_widget_queue_draw (GTK_WIDGET (water->area));
+}
+
+static gboolean
+select_area_expose (GtkWidget *widget,
+ GdkEventExpose *event,
+ ColorselWater *water)
+{
+ cairo_t *cr;
+ GtkAllocation allocation;
+ gdouble dx;
+ gdouble dy;
+ cairo_surface_t *surface;
+ guchar *dest;
+ gdouble y;
+ gint j;
+
+ cr = gdk_cairo_create (event->window);
+
+ gdk_cairo_region (cr, event->region);
+ cairo_clip (cr);
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ dx = 1.0 / allocation.width;
+ dy = 1.0 / allocation.height;
+
+ surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
+ event->area.width,
+ event->area.height);
+
+ dest = cairo_image_surface_get_data (surface);
+
+ if (! water->transform)
+ colorsel_water_create_transform (water);
+
+ for (j = 0, y = event->area.y / allocation.height;
+ j < event->area.height;
+ j++, y += dy)
+ {
+ guchar *d = dest;
+
+ gdouble r = calc (0, y, 0);
+ gdouble g = calc (0, y, 120);
+ gdouble b = calc (0, y, 240);
+
+ gdouble dr = calc (dx, y, 0) - r;
+ gdouble dg = calc (dx, y, 120) - g;
+ gdouble db = calc (dx, y, 240) - b;
+
+ gint i;
+
+ r += event->area.x * dr;
+ g += event->area.x * dg;
+ b += event->area.x * db;
+
+ for (i = 0; i < event->area.width ; i++)
+ {
+ GIMP_CAIRO_RGB24_SET_PIXEL (d,
+ CLAMP ((gint) r, 0, 255),
+ CLAMP ((gint) g, 0, 255),
+ CLAMP ((gint) b, 0, 255));
+
+ r += dr;
+ g += dg;
+ b += db;
+
+ d += 4;
+ }
+
+ if (water->transform)
+ gimp_color_transform_process_pixels (water->transform,
+ babl_format ("cairo-RGB24"),
+ dest,
+ babl_format ("cairo-RGB24"),
+ dest,
+ event->area.width);
+
+ dest += cairo_image_surface_get_stride (surface);
+ }
+
+ cairo_surface_mark_dirty (surface);
+ cairo_set_source_surface (cr, surface,
+ event->area.x, event->area.y);
+ cairo_surface_destroy (surface);
+
+ cairo_paint (cr);
+
+ cairo_destroy (cr);
+
+ return FALSE;
+}
+
+static void
+add_pigment (ColorselWater *water,
+ gboolean erase,
+ gdouble x,
+ gdouble y,
+ gdouble much)
+{
+ GimpColorSelector *selector = GIMP_COLOR_SELECTOR (water);
+
+ much *= (gdouble) water->pressure_adjust;
+
+ if (erase)
+ {
+ selector->rgb.r = 1.0 - (1.0 - selector->rgb.r) * (1.0 - much);
+ selector->rgb.g = 1.0 - (1.0 - selector->rgb.g) * (1.0 - much);
+ selector->rgb.b = 1.0 - (1.0 - selector->rgb.b) * (1.0 - much);
+ }
+ else
+ {
+ gdouble r = calc (x, y, 0) / 256.0;
+ gdouble g = calc (x, y, 120) / 256.0;
+ gdouble b = calc (x, y, 240) / 256.0;
+
+ selector->rgb.r *= (1.0 - (1.0 - r) * much);
+ selector->rgb.g *= (1.0 - (1.0 - g) * much);
+ selector->rgb.b *= (1.0 - (1.0 - b) * much);
+ }
+
+ gimp_rgb_clamp (&selector->rgb);
+
+ gimp_rgb_to_hsv (&selector->rgb, &selector->hsv);
+
+ gimp_color_selector_color_changed (selector);
+}
+
+static void
+draw_brush (ColorselWater *water,
+ GtkWidget *widget,
+ gboolean erase,
+ gdouble x,
+ gdouble y,
+ gdouble pressure)
+{
+ gdouble much = sqrt (SQR (x - water->last_x) + SQR (y - water->last_y));
+
+ add_pigment (water, erase, x, y, much * pressure);
+
+ water->last_x = x;
+ water->last_y = y;
+}
+
+static gboolean
+button_press_event (GtkWidget *widget,
+ GdkEventButton *event,
+ ColorselWater *water)
+{
+ GtkAllocation allocation;
+ gboolean erase;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ water->last_x = event->x / allocation.width;
+ water->last_y = event->y / allocation.height;
+
+ erase = (event->button != 1);
+ /* FIXME: (event->source == GDK_SOURCE_ERASER) */
+
+ if (event->state & GDK_SHIFT_MASK)
+ erase = !erase;
+
+ add_pigment (water, erase, water->last_x, water->last_y, 0.05);
+
+ water->motion_time = event->time;
+
+ return FALSE;
+}
+
+static gboolean
+motion_notify_event (GtkWidget *widget,
+ GdkEventMotion *event,
+ ColorselWater *water)
+{
+ GtkAllocation allocation;
+ GdkTimeCoord **coords;
+ gint nevents;
+ gint i;
+ gboolean erase;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ if (event->state & (GDK_BUTTON1_MASK |
+ GDK_BUTTON2_MASK |
+ GDK_BUTTON3_MASK |
+ GDK_BUTTON4_MASK))
+ {
+ guint32 last_motion_time = event->time;
+
+ erase = ((event->state &
+ (GDK_BUTTON2_MASK | GDK_BUTTON3_MASK | GDK_BUTTON4_MASK)) ||
+ FALSE);
+ /* FIXME: (event->source == GDK_SOURCE_ERASER) */
+
+ if (event->state & GDK_SHIFT_MASK)
+ erase = !erase;
+
+ water->motion_time = event->time;
+
+ if (gdk_device_get_history (event->device,
+ event->window,
+ last_motion_time,
+ event->time,
+ &coords,
+ &nevents))
+ {
+ for (i = 0; i < nevents; i++)
+ {
+ gdouble x = 0.0;
+ gdouble y = 0.0;
+ gdouble pressure = 0.5;
+
+ gdk_device_get_axis (event->device, coords[i]->axes,
+ GDK_AXIS_X, &x);
+ gdk_device_get_axis (event->device, coords[i]->axes,
+ GDK_AXIS_Y, &y);
+ gdk_device_get_axis (event->device, coords[i]->axes,
+ GDK_AXIS_PRESSURE, &pressure);
+
+ draw_brush (water, widget, erase,
+ x / allocation.width,
+ y / allocation.height, pressure);
+ }
+
+ gdk_device_free_history (coords, nevents);
+ }
+ else
+ {
+ gdouble pressure = 0.5;
+
+ gdk_event_get_axis ((GdkEvent *) event, GDK_AXIS_PRESSURE, &pressure);
+
+ draw_brush (water, widget, erase,
+ event->x / allocation.width,
+ event->y / allocation.height, pressure);
+ }
+ }
+
+ /* Ask for more motion events in case the event was a hint */
+ gdk_event_request_motions (event);
+
+ return TRUE;
+}
+
+static gboolean
+proximity_out_event (GtkWidget *widget,
+ GdkEventProximity *event,
+ ColorselWater *water)
+{
+ return TRUE;
+}
+
+static void
+pressure_adjust_update (GtkAdjustment *adj,
+ ColorselWater *water)
+{
+ water->pressure_adjust = (gtk_adjustment_get_upper (adj) -
+ gtk_adjustment_get_value (adj)) / 100.0;
+}
diff --git a/modules/color-selector-wheel.c b/modules/color-selector-wheel.c
new file mode 100644
index 0000000..47adb75
--- /dev/null
+++ b/modules/color-selector-wheel.c
@@ -0,0 +1,169 @@
+/* GIMP Wheel ColorSelector
+ * Copyright (C) 2008 Michael Natterer <mitch@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpmath/gimpmath.h"
+#include "libgimpmodule/gimpmodule.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "gimpcolorwheel.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+#define COLORSEL_TYPE_WHEEL (colorsel_wheel_get_type ())
+#define COLORSEL_WHEEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), COLORSEL_TYPE_WHEEL, ColorselWheel))
+#define COLORSEL_WHEEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), COLORSEL_TYPE_WHEEL, ColorselWheelClass))
+#define COLORSEL_IS_WHEEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), COLORSEL_TYPE_WHEEL))
+#define COLORSEL_IS_WHEEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), COLORSEL_TYPE_WHEEL))
+
+
+typedef struct _ColorselWheel ColorselWheel;
+typedef struct _ColorselWheelClass ColorselWheelClass;
+
+struct _ColorselWheel
+{
+ GimpColorSelector parent_instance;
+
+ GtkWidget *hsv;
+};
+
+struct _ColorselWheelClass
+{
+ GimpColorSelectorClass parent_class;
+};
+
+
+static GType colorsel_wheel_get_type (void);
+
+static void colorsel_wheel_set_color (GimpColorSelector *selector,
+ const GimpRGB *rgb,
+ const GimpHSV *hsv);
+static void colorsel_wheel_set_config (GimpColorSelector *selector,
+ GimpColorConfig *config);
+static void colorsel_wheel_changed (GimpColorWheel *hsv,
+ GimpColorSelector *selector);
+
+static const GimpModuleInfo colorsel_wheel_info =
+{
+ GIMP_MODULE_ABI_VERSION,
+ N_("HSV color wheel"),
+ "Michael Natterer <mitch@gimp.org>",
+ "v1.0",
+ "(c) 2008, released under the GPL",
+ "08 Aug 2008"
+};
+
+
+G_DEFINE_DYNAMIC_TYPE (ColorselWheel, colorsel_wheel,
+ GIMP_TYPE_COLOR_SELECTOR)
+
+
+G_MODULE_EXPORT const GimpModuleInfo *
+gimp_module_query (GTypeModule *module)
+{
+ return &colorsel_wheel_info;
+}
+
+G_MODULE_EXPORT gboolean
+gimp_module_register (GTypeModule *module)
+{
+ color_wheel_register_type (module);
+ colorsel_wheel_register_type (module);
+
+ return TRUE;
+}
+
+static void
+colorsel_wheel_class_init (ColorselWheelClass *klass)
+{
+ GimpColorSelectorClass *selector_class = GIMP_COLOR_SELECTOR_CLASS (klass);
+
+ selector_class->name = _("Wheel");
+ selector_class->help_id = "gimp-colorselector-triangle";
+ selector_class->icon_name = GIMP_ICON_COLOR_SELECTOR_TRIANGLE;
+ selector_class->set_color = colorsel_wheel_set_color;
+ selector_class->set_config = colorsel_wheel_set_config;
+}
+
+static void
+colorsel_wheel_class_finalize (ColorselWheelClass *klass)
+{
+}
+
+static void
+colorsel_wheel_init (ColorselWheel *wheel)
+{
+ GtkWidget *frame;
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (wheel), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ wheel->hsv = gimp_color_wheel_new ();
+ g_object_add_weak_pointer (G_OBJECT (wheel->hsv),
+ (gpointer) &wheel->hsv);
+ gtk_container_add (GTK_CONTAINER (frame), wheel->hsv);
+ gtk_widget_show (wheel->hsv);
+
+ g_signal_connect (wheel->hsv, "changed",
+ G_CALLBACK (colorsel_wheel_changed),
+ wheel);
+}
+
+static void
+colorsel_wheel_set_color (GimpColorSelector *selector,
+ const GimpRGB *rgb,
+ const GimpHSV *hsv)
+{
+ ColorselWheel *wheel = COLORSEL_WHEEL (selector);
+
+ gimp_color_wheel_set_color (GIMP_COLOR_WHEEL (wheel->hsv),
+ hsv->h, hsv->s, hsv->v);
+}
+
+static void
+colorsel_wheel_set_config (GimpColorSelector *selector,
+ GimpColorConfig *config)
+{
+ ColorselWheel *wheel = COLORSEL_WHEEL (selector);
+
+ if (wheel->hsv)
+ gimp_color_wheel_set_color_config (GIMP_COLOR_WHEEL (wheel->hsv), config);
+}
+
+static void
+colorsel_wheel_changed (GimpColorWheel *hsv,
+ GimpColorSelector *selector)
+{
+ gimp_color_wheel_get_color (hsv,
+ &selector->hsv.h,
+ &selector->hsv.s,
+ &selector->hsv.v);
+ gimp_hsv_to_rgb (&selector->hsv, &selector->rgb);
+
+ gimp_color_selector_color_changed (selector);
+}
diff --git a/modules/controller-dx-dinput.c b/modules/controller-dx-dinput.c
new file mode 100644
index 0000000..87c0eab
--- /dev/null
+++ b/modules/controller-dx-dinput.c
@@ -0,0 +1,1122 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * controller_dx_dinput.c
+ * Copyright (C) 2004-2007 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ * Tor Lillqvist <tml@iki.fi>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/* When using gcc with the February 2007 version of the DirectX SDK,
+ * at least, you need to fix a couple of duplicate typedefs in the
+ * DirectX <dinput.h>. Unfortunately I can't include the diff here,
+ * both for copyright reasons, and because that would confuse the C
+ * lexer even if inside #if 0.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+#include <math.h>
+
+#define _WIN32_WINNT 0x0501
+#include <windows.h>
+#define DIRECTINPUT_VERSION 0x0800
+#include <dinput.h>
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpconfig/gimpconfig.h"
+#include "libgimpmodule/gimpmodule.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#define GIMP_ENABLE_CONTROLLER_UNDER_CONSTRUCTION
+#include "libgimpwidgets/gimpcontroller.h"
+
+#include "gimpinputdevicestore.h"
+
+#include "libgimp/libgimp-intl.h"
+
+enum
+{
+ PROP_0,
+ PROP_DEVICE,
+ PROP_DEVICE_STORE
+};
+
+#define NUM_EVENTS_PER_BUTTON 3 /* Button click, press and release */
+#define NUM_EVENTS_PER_AXIS 2 /* Axis decrease and increase */
+#define NUM_EVENTS_PER_SLIDER 2 /* Slider decrease and increase */
+#define NUM_EVENTS_PER_POV 3 /* POV view vector X and Y view and return */
+
+#define CONTROLLER_TYPE_DX_DINPUT (controller_dx_dinput_get_type ())
+#define CONTROLLER_DX_DINPUT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CONTROLLER_TYPE_DX_DINPUT, ControllerDXDInput))
+#define CONTROLLER_DX_DINPUT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CONTROLLER_TYPE_DX_DINPUT, ControllerDXDInputClass))
+#define CONTROLLER_IS_DX_DINPUT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CONTROLLER_TYPE_DX_DINPUT))
+#define CONTROLLER_IS_DX_DINPUT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CONTROLLER_TYPE_DX_DINPUT))
+
+typedef struct _DXDInputSource DXDInputSource;
+typedef struct _ControllerDXDInput ControllerDXDInput;
+typedef struct _ControllerDXDInputClass ControllerDXDInputClass;
+
+struct _DXDInputSource
+{
+ GSource source;
+ ControllerDXDInput *controller;
+};
+
+struct _ControllerDXDInput
+{
+ GimpController parent_instance;
+
+ GimpInputDeviceStore *store;
+
+ LPDIRECTINPUTDEVICE8W didevice8;
+
+ HANDLE event;
+
+ GPollFD *pollfd;
+
+ gint num_buttons;
+ gint num_button_events;
+
+ gint num_axes;
+ gint num_axis_events;
+
+ gint num_sliders;
+ gint num_slider_events;
+
+ gint num_povs;
+ gint num_pov_events;
+
+ gint num_events;
+ gchar **event_names;
+ gchar **event_blurbs;
+
+ DIDATAFORMAT *format;
+
+ guchar *prevdata;
+
+ gchar *guid;
+ GSource *source;
+
+ /* Variables used by enumeration callbacks only*/
+ gint bei, bi, aei, ai, sei, si, pei, pi;
+};
+
+struct _ControllerDXDInputClass
+{
+ GimpControllerClass parent_class;
+};
+
+
+static GType controller_dx_dinput_get_type (void);
+
+static void dx_dinput_dispose (GObject *object);
+static void dx_dinput_finalize (GObject *object);
+static void dx_dinput_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void dx_dinput_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static gint dx_dinput_get_n_events (GimpController *controller);
+static const gchar * dx_dinput_get_event_name (GimpController *controller,
+ gint event_id);
+static const gchar * dx_dinput_get_event_blurb (GimpController *controller,
+ gint event_id);
+
+static void dx_dinput_device_changed (ControllerDXDInput *controller,
+ const gchar *guid);
+static gboolean dx_dinput_set_device (ControllerDXDInput *controller,
+ const gchar *guid);
+
+
+static const GimpModuleInfo dx_dinput_info =
+{
+ GIMP_MODULE_ABI_VERSION,
+ N_("DirectX DirectInput event controller"),
+ "Tor Lillqvist <tml@iki.fi>",
+ "v0.1",
+ "(c) 2004-2007, released under the GPL",
+ "2004-2007"
+};
+
+
+G_DEFINE_DYNAMIC_TYPE (ControllerDXDInput, controller_dx_dinput,
+ GIMP_TYPE_CONTROLLER)
+
+
+G_MODULE_EXPORT const GimpModuleInfo *
+gimp_module_query (GTypeModule *module)
+{
+ return &dx_dinput_info;
+}
+
+G_MODULE_EXPORT gboolean
+gimp_module_register (GTypeModule *module)
+{
+ gimp_input_device_store_register_types (module);
+ controller_dx_dinput_register_type (module);
+
+ return TRUE;
+}
+
+static void
+controller_dx_dinput_class_init (ControllerDXDInputClass *klass)
+{
+ GimpControllerClass *controller_class = GIMP_CONTROLLER_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = dx_dinput_dispose;
+ object_class->finalize = dx_dinput_finalize;
+ object_class->get_property = dx_dinput_get_property;
+ object_class->set_property = dx_dinput_set_property;
+
+ g_object_class_install_property (object_class, PROP_DEVICE,
+ g_param_spec_string ("device",
+ _("Device:"),
+ _("The device to read DirectInput events from."),
+ NULL,
+ GIMP_CONFIG_PARAM_FLAGS));
+ g_object_class_install_property (object_class, PROP_DEVICE_STORE,
+ g_param_spec_object ("device-values",
+ NULL, NULL,
+ GIMP_TYPE_INPUT_DEVICE_STORE,
+ G_PARAM_READABLE));
+
+ controller_class->name = _("DirectX DirectInput");
+ controller_class->help_id = "gimp-controller-directx-directinput";
+ controller_class->icon_name = GIMP_ICON_CONTROLLER_LINUX_INPUT;
+
+ controller_class->get_n_events = dx_dinput_get_n_events;
+ controller_class->get_event_name = dx_dinput_get_event_name;
+ controller_class->get_event_blurb = dx_dinput_get_event_blurb;
+}
+
+static void
+controller_dx_dinput_class_finalize (ControllerDXDInputClass *klass)
+{
+}
+
+static void
+controller_dx_dinput_init (ControllerDXDInput *controller)
+{
+ controller->store = gimp_input_device_store_new ();
+
+ if (controller->store)
+ {
+ g_signal_connect_swapped (controller->store, "device-added",
+ G_CALLBACK (dx_dinput_device_changed),
+ controller);
+ g_signal_connect_swapped (controller->store, "device-removed",
+ G_CALLBACK (dx_dinput_device_changed),
+ controller);
+ }
+}
+
+static void
+dx_dinput_dispose (GObject *object)
+{
+ ControllerDXDInput *controller = CONTROLLER_DX_DINPUT (object);
+
+ dx_dinput_set_device (controller, NULL);
+
+ G_OBJECT_CLASS (controller_dx_dinput_parent_class)->dispose (object);
+}
+
+static void
+dx_dinput_finalize (GObject *object)
+{
+ ControllerDXDInput *controller = CONTROLLER_DX_DINPUT (object);
+
+ if (controller->source != NULL)
+ {
+ g_source_remove_poll (controller->source, controller->pollfd);
+ g_source_remove (g_source_get_id (controller->source));
+ g_source_unref (controller->source);
+ }
+
+ if (controller->didevice8)
+ IDirectInputDevice8_SetEventNotification (controller->didevice8, NULL);
+
+ if (controller->format != NULL)
+ {
+ g_free (controller->format->rgodf);
+ g_free (controller->format);
+ controller->format = NULL;
+ }
+
+ g_free (controller->prevdata);
+ controller->prevdata = NULL;
+
+ if (controller->event != NULL)
+ {
+ CloseHandle (controller->event);
+ controller->event = 0;
+ }
+
+ if (controller->store)
+ {
+ g_object_unref (controller->store);
+ controller->store = NULL;
+ }
+
+ G_OBJECT_CLASS (controller_dx_dinput_parent_class)->finalize (object);
+}
+
+static void
+dx_dinput_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ControllerDXDInput *controller = CONTROLLER_DX_DINPUT (object);
+
+ switch (property_id)
+ {
+ case PROP_DEVICE:
+ dx_dinput_set_device (controller, g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+dx_dinput_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ControllerDXDInput *controller = CONTROLLER_DX_DINPUT (object);
+
+ switch (property_id)
+ {
+ case PROP_DEVICE:
+ g_value_set_string (value, controller->guid);
+ break;
+ case PROP_DEVICE_STORE:
+ g_value_set_object (value, controller->store);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static gint
+dx_dinput_get_n_events (GimpController *controller)
+{
+ ControllerDXDInput *cdxdi = (ControllerDXDInput *) controller;
+
+ return cdxdi->num_events;
+}
+
+static const gchar *
+dx_dinput_get_event_name (GimpController *controller,
+ gint event_id)
+{
+ ControllerDXDInput *cdxdi = (ControllerDXDInput *) controller;
+
+ if (event_id < 0 || event_id >= cdxdi->num_events)
+ return NULL;
+
+ return cdxdi->event_names[event_id];
+}
+
+static const gchar *
+dx_dinput_get_event_blurb (GimpController *controller,
+ gint event_id)
+{
+ ControllerDXDInput *cdxdi = (ControllerDXDInput *) controller;
+
+ if (event_id < 0 || event_id >= cdxdi->num_events)
+ return NULL;
+
+ return cdxdi->event_blurbs[event_id];
+}
+
+static BOOL CALLBACK
+count_objects (const DIDEVICEOBJECTINSTANCEW *doi,
+ void *user_data)
+{
+ ControllerDXDInput *controller = (ControllerDXDInput *) user_data;
+ HRESULT hresult;
+ DIPROPRANGE range;
+
+ if (doi->dwType & DIDFT_AXIS)
+ {
+ /* Set reported range to physical range, not to give upper
+ * level software any false impression of higher accuracy.
+ */
+
+ range.diph.dwSize = sizeof (DIPROPRANGE);
+ range.diph.dwHeaderSize = sizeof (DIPROPHEADER);
+ range.diph.dwObj = doi->dwType;
+ range.diph.dwHow = DIPH_BYID;
+
+ if (FAILED ((hresult = IDirectInputDevice8_GetProperty (controller->didevice8,
+ DIPROP_PHYSICALRANGE,
+ &range.diph))))
+ g_warning ("IDirectInputDevice8::GetProperty failed: %s",
+ g_win32_error_message (hresult));
+ else
+ {
+ if (FAILED ((hresult = IDirectInputDevice8_SetProperty (controller->didevice8,
+ DIPROP_RANGE,
+ &range.diph))))
+ g_warning ("IDirectInputDevice8::SetProperty failed: %s",
+ g_win32_error_message (hresult));
+ }
+ }
+
+ if (IsEqualGUID (&doi->guidType, &GUID_Button))
+ controller->num_buttons++;
+ else if (IsEqualGUID (&doi->guidType, &GUID_XAxis) ||
+ IsEqualGUID (&doi->guidType, &GUID_YAxis) ||
+ IsEqualGUID (&doi->guidType, &GUID_ZAxis) ||
+ IsEqualGUID (&doi->guidType, &GUID_RxAxis) ||
+ IsEqualGUID (&doi->guidType, &GUID_RyAxis) ||
+ IsEqualGUID (&doi->guidType, &GUID_RzAxis))
+ controller->num_axes++;
+ else if (IsEqualGUID (&doi->guidType, &GUID_Slider))
+ controller->num_sliders++;
+ else if (IsEqualGUID (&doi->guidType, &GUID_POV))
+ controller->num_povs++;
+
+ return DIENUM_CONTINUE;
+}
+
+static BOOL CALLBACK
+name_objects (const DIDEVICEOBJECTINSTANCEW *doi,
+ void *user_data)
+{
+ ControllerDXDInput *controller = (ControllerDXDInput *) user_data;
+
+ if (IsEqualGUID (&doi->guidType, &GUID_Button))
+ {
+ controller->event_names[controller->bei] = g_strdup_printf ("button-%d", controller->bi);
+ controller->event_blurbs[controller->bei] = g_strdup_printf (_("Button %d"), controller->bi);
+ controller->bei++;
+ controller->event_names[controller->bei] = g_strdup_printf ("button-%d-press", controller->bi);
+ controller->event_blurbs[controller->bei] = g_strdup_printf (_("Button %d Press"), controller->bi);
+ controller->bei++;
+ controller->event_names[controller->bei] = g_strdup_printf ("button-%d-release", controller->bi);
+ controller->event_blurbs[controller->bei] = g_strdup_printf (_("Button %d Release"), controller->bi);
+ controller->bei++;
+
+ g_assert (controller->bi < controller->num_buttons);
+
+ controller->bi++;
+ }
+ else if (IsEqualGUID (&doi->guidType, &GUID_XAxis) ||
+ IsEqualGUID (&doi->guidType, &GUID_YAxis) ||
+ IsEqualGUID (&doi->guidType, &GUID_ZAxis) ||
+ IsEqualGUID (&doi->guidType, &GUID_RxAxis) ||
+ IsEqualGUID (&doi->guidType, &GUID_RyAxis) ||
+ IsEqualGUID (&doi->guidType, &GUID_RzAxis))
+ {
+ if (IsEqualGUID (&doi->guidType, &GUID_XAxis))
+ {
+ controller->event_names[controller->aei] = g_strdup ("x-move-left");
+ controller->event_blurbs[controller->aei] = g_strdup (_("X Move Left"));
+ controller->aei++;
+ controller->event_names[controller->aei] = g_strdup ("x-move-right");
+ controller->event_blurbs[controller->aei] = g_strdup (_("X Move Right"));
+ controller->aei++;
+ }
+ else if (IsEqualGUID (&doi->guidType, &GUID_YAxis))
+ {
+ controller->event_names[controller->aei] = g_strdup ("y-move-away");
+ controller->event_blurbs[controller->aei] = g_strdup (_("Y Move Away"));
+ controller->aei++;
+ controller->event_names[controller->aei] = g_strdup ("y-move-near");
+ controller->event_blurbs[controller->aei] = g_strdup (_("Y Move Near"));
+ controller->aei++;
+ }
+ else if (IsEqualGUID (&doi->guidType, &GUID_ZAxis))
+ {
+ controller->event_names[controller->aei] = g_strdup ("z-move-up");
+ controller->event_blurbs[controller->aei] = g_strdup (_("Z Move Up"));
+ controller->aei++;
+ controller->event_names[controller->aei] = g_strdup ("z-move-down");
+ controller->event_blurbs[controller->aei] = g_strdup (_("Z Move Down"));
+ controller->aei++;
+ }
+ else if (IsEqualGUID (&doi->guidType, &GUID_RxAxis))
+ {
+ controller->event_names[controller->aei] = g_strdup ("x-axis-tilt-away");
+ controller->event_blurbs[controller->aei] = g_strdup (_("X Axis Tilt Away"));
+ controller->aei++;
+ controller->event_names[controller->aei] = g_strdup ("x-axis-tilt-near");
+ controller->event_blurbs[controller->aei] = g_strdup (_("X Axis Tilt Near"));
+ controller->aei++;
+ }
+ else if (IsEqualGUID (&doi->guidType, &GUID_RyAxis))
+ {
+ controller->event_names[controller->aei] = g_strdup ("y-axis-tilt-right");
+ controller->event_blurbs[controller->aei] = g_strdup (_("Y Axis Tilt Right"));
+ controller->aei++;
+ controller->event_names[controller->aei] = g_strdup ("y-axis-tilt-left");
+ controller->event_blurbs[controller->aei] = g_strdup (_("Y Axis Tilt Left"));
+ controller->aei++;
+ }
+ else if (IsEqualGUID (&doi->guidType, &GUID_RzAxis))
+ {
+ controller->event_names[controller->aei] = g_strdup ("z-axis-turn-left");
+ controller->event_blurbs[controller->aei] = g_strdup (_("Z Axis Turn Left"));
+ controller->aei++;
+ controller->event_names[controller->aei] = g_strdup ("z-axis-turn-right");
+ controller->event_blurbs[controller->aei] = g_strdup (_("Z Axis Turn Right"));
+ controller->aei++;
+ }
+
+ g_assert (controller->ai < controller->num_axes);
+
+ controller->ai++;
+ }
+ else if (IsEqualGUID (&doi->guidType, &GUID_Slider))
+ {
+ controller->event_names[controller->sei] = g_strdup_printf ("slider-%d-increase", controller->si);
+ controller->event_blurbs[controller->sei] = g_strdup_printf (_("Slider %d Increase"), controller->si);
+ controller->sei++;
+ controller->event_names[controller->sei] = g_strdup_printf ("slider-%d-decrease", controller->si);
+ controller->event_blurbs[controller->sei] = g_strdup_printf (_("Slider %d Decrease"), controller->si);
+ controller->sei++;
+
+ g_assert (controller->si < controller->num_sliders);
+
+ controller->si++;
+ }
+ else if (IsEqualGUID (&doi->guidType, &GUID_POV))
+ {
+ controller->event_names[controller->pei] = g_strdup_printf ("pov-%d-x-view", controller->pi);
+ controller->event_blurbs[controller->pei] = g_strdup_printf (_("POV %d X View"), controller->pi);
+ controller->pei++;
+ controller->event_names[controller->pei] = g_strdup_printf ("pov-%d-y-view", controller->pi);
+ controller->event_blurbs[controller->pei] = g_strdup_printf (_("POV %d Y View"), controller->pi);
+ controller->pei++;
+ controller->event_names[controller->pei] = g_strdup_printf ("pov-%d-x-return", controller->pi);
+ controller->event_blurbs[controller->pei] = g_strdup_printf (_("POV %d Return"), controller->pi);
+ controller->pei++;
+
+ g_assert (controller->pi < controller->num_povs);
+
+ controller->pi++;
+ }
+
+ return DIENUM_CONTINUE;
+}
+
+static gboolean
+dx_dinput_get_device_info (ControllerDXDInput *controller,
+ GError **error)
+{
+ HRESULT hresult;
+
+ controller->num_buttons =
+ controller->num_axes =
+ controller->num_sliders =
+ controller->num_povs = 0;
+
+ if (FAILED ((hresult = IDirectInputDevice8_EnumObjects (controller->didevice8,
+ count_objects,
+ controller,
+ DIDFT_AXIS|
+ DIDFT_BUTTON|
+ DIDFT_POV|
+ DIDFT_PSHBUTTON|
+ DIDFT_RELAXIS|
+ DIDFT_TGLBUTTON))))
+ {
+ g_set_error (error, GIMP_MODULE_ERROR, GIMP_MODULE_FAILED,
+ "IDirectInputDevice8::EnumObjects failed: %s",
+ g_win32_error_message (hresult));
+ return FALSE;
+ }
+
+ controller->num_button_events = controller->num_buttons*NUM_EVENTS_PER_BUTTON;
+ controller->num_axis_events = controller->num_axes*NUM_EVENTS_PER_AXIS;
+ controller->num_slider_events = controller->num_sliders*NUM_EVENTS_PER_SLIDER;
+ controller->num_pov_events = controller->num_povs*NUM_EVENTS_PER_POV;
+
+ controller->num_events =
+ controller->num_button_events +
+ controller->num_axis_events +
+ controller->num_slider_events +
+ controller->num_pov_events;
+
+ controller->event_names = g_new (gchar *, controller->num_events);
+ controller->event_blurbs = g_new (gchar *, controller->num_events);
+
+ controller->bi = controller->ai = controller->si = controller->pi = 0;
+
+ controller->bei = 0;
+ controller->aei = controller->bei + controller->num_button_events;
+ controller->sei = controller->aei + controller->num_axis_events;
+ controller->pei = controller->sei + controller->num_slider_events;
+
+ if (FAILED ((hresult = IDirectInputDevice8_EnumObjects (controller->didevice8,
+ name_objects,
+ controller,
+ DIDFT_AXIS|
+ DIDFT_BUTTON|
+ DIDFT_POV|
+ DIDFT_PSHBUTTON|
+ DIDFT_RELAXIS|
+ DIDFT_TGLBUTTON))))
+ {
+ g_free (controller->event_names);
+ g_free (controller->event_blurbs);
+
+ g_set_error (error, GIMP_MODULE_ERROR, GIMP_MODULE_FAILED,
+ "IDirectInputDevice8::EnumObjects failed: %s",
+ g_win32_error_message (hresult));
+ return FALSE;
+ }
+
+
+ g_assert (controller->bei == controller->num_button_events);
+ g_assert (controller->aei == controller->bei + controller->num_axis_events);
+ g_assert (controller->sei == controller->aei + controller->num_slider_events);
+ g_assert (controller->pei == controller->sei + controller->num_pov_events);
+
+ return TRUE;
+}
+
+
+static void
+dx_dinput_device_changed (ControllerDXDInput *controller,
+ const gchar *guid)
+{
+ if (controller->guid && strcmp (guid, controller->guid) == 0)
+ {
+ dx_dinput_set_device (controller, guid);
+ g_object_notify (G_OBJECT (controller), "device");
+ }
+}
+
+static gboolean
+dx_dinput_event_prepare (GSource *source,
+ gint *timeout)
+{
+ *timeout = -1;
+
+ return FALSE;
+}
+
+static gboolean
+dx_dinput_event_check (GSource *source)
+{
+ DXDInputSource *input = (DXDInputSource *) source;
+
+ if (WaitForSingleObject (input->controller->event, 0) == WAIT_OBJECT_0)
+ {
+ ResetEvent (input->controller->event);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+dx_dinput_event_dispatch (GSource *source,
+ GSourceFunc callback,
+ gpointer user_data)
+{
+ ControllerDXDInput * const input = ((DXDInputSource *) source)->controller;
+ GimpController *controller = &input->parent_instance;
+ const DIDATAFORMAT * const format = input->format;
+ const DIOBJECTDATAFORMAT *rgodf = format->rgodf;
+ guchar *data;
+ gint i;
+ GimpControllerEvent cevent = { 0, };
+
+ data = g_alloca (format->dwDataSize);
+
+ if (FAILED (IDirectInputDevice8_GetDeviceState (input->didevice8,
+ format->dwDataSize,
+ data)))
+ {
+ return TRUE;
+ }
+
+ g_object_ref (controller);
+
+ for (i = 0; i < input->num_buttons; i++)
+ {
+ if (input->prevdata[rgodf->dwOfs] != data[rgodf->dwOfs])
+ {
+ if (data[rgodf->dwOfs] & 0x80)
+ {
+ /* Click event, compatibility with Linux Input */
+ cevent.any.type = GIMP_CONTROLLER_EVENT_TRIGGER;
+ cevent.any.source = controller;
+ cevent.any.event_id = i*NUM_EVENTS_PER_BUTTON;
+ gimp_controller_event (controller, &cevent);
+
+ /* Press event */
+ cevent.any.event_id = i*NUM_EVENTS_PER_BUTTON + 1;
+ gimp_controller_event (controller, &cevent);
+ }
+ else
+ {
+ /* Release event */
+ cevent.any.type = GIMP_CONTROLLER_EVENT_TRIGGER;
+ cevent.any.source = controller;
+ cevent.any.event_id = i*NUM_EVENTS_PER_BUTTON + 2;
+ gimp_controller_event (controller, &cevent);
+ }
+ }
+ rgodf++;
+ }
+
+ for (i = 0; i < input->num_axes; i++)
+ {
+ LONG *prev = (LONG *)(input->prevdata+rgodf->dwOfs);
+ LONG *curr = (LONG *)(data+rgodf->dwOfs);
+
+ if (ABS (*prev - *curr) > 1)
+ {
+ cevent.any.type = GIMP_CONTROLLER_EVENT_VALUE;
+ cevent.any.source = controller;
+ cevent.any.event_id =
+ input->num_button_events +
+ i*NUM_EVENTS_PER_AXIS;
+ g_value_init (&cevent.value.value, G_TYPE_DOUBLE);
+ if (*curr - *prev < 0)
+ {
+ g_value_set_double (&cevent.value.value, *prev - *curr);
+ }
+ else
+ {
+ cevent.any.event_id++;
+ g_value_set_double (&cevent.value.value, *curr - *prev);
+ }
+ gimp_controller_event (controller, &cevent);
+ g_value_unset (&cevent.value.value);
+ }
+ else
+ *curr = *prev;
+ rgodf++;
+ }
+
+ for (i = 0; i < input->num_sliders; i++)
+ {
+ LONG *prev = (LONG *)(input->prevdata+rgodf->dwOfs);
+ LONG *curr = (LONG *)(data+rgodf->dwOfs);
+
+ if (ABS (*prev - *curr) > 1)
+ {
+ cevent.any.type = GIMP_CONTROLLER_EVENT_VALUE;
+ cevent.any.source = controller;
+ cevent.any.event_id =
+ input->num_button_events +
+ input->num_axis_events +
+ i*NUM_EVENTS_PER_SLIDER;
+ g_value_init (&cevent.value.value, G_TYPE_DOUBLE);
+ if (*curr - *prev < 0)
+ {
+ g_value_set_double (&cevent.value.value, *prev - *curr);
+ }
+ else
+ {
+ cevent.any.event_id++;
+ g_value_set_double (&cevent.value.value, *curr - *prev);
+ }
+ gimp_controller_event (controller, &cevent);
+ g_value_unset (&cevent.value.value);
+ }
+ else
+ *curr = *prev;
+ rgodf++;
+ }
+
+ for (i = 0; i < input->num_povs; i++)
+ {
+ LONG prev = *((LONG *)(input->prevdata+rgodf->dwOfs));
+ LONG curr = *((LONG *)(data+rgodf->dwOfs));
+ double prevx, prevy;
+ double currx, curry;
+
+ if (prev != curr)
+ {
+ if (prev == -1)
+ {
+ prevx = 0.;
+ prevy = 0.;
+ }
+ else
+ {
+ prevx = sin (prev/36000.*2.*G_PI);
+ prevy = cos (prev/36000.*2.*G_PI);
+ }
+ if (curr == -1)
+ {
+ currx = 0.;
+ curry = 0.;
+ }
+ else
+ {
+ currx = sin (curr/36000.*2.*G_PI);
+ curry = cos (curr/36000.*2.*G_PI);
+ }
+
+ cevent.any.type = GIMP_CONTROLLER_EVENT_VALUE;
+ cevent.any.source = controller;
+ cevent.any.event_id =
+ input->num_button_events +
+ input->num_axis_events +
+ input->num_slider_events +
+ i*NUM_EVENTS_PER_POV;
+ g_value_init (&cevent.value.value, G_TYPE_DOUBLE);
+ g_value_set_double (&cevent.value.value, currx - prevx);
+ gimp_controller_event (controller, &cevent);
+ cevent.any.event_id++;
+ g_value_set_double (&cevent.value.value, curry - prevy);
+ gimp_controller_event (controller, &cevent);
+ g_value_unset (&cevent.value.value);
+ if (curr == -1)
+ {
+ cevent.any.type = GIMP_CONTROLLER_EVENT_TRIGGER;
+ cevent.any.event_id++;
+ gimp_controller_event (controller, &cevent);
+ }
+ }
+ rgodf++;
+ }
+
+ g_assert (rgodf == format->rgodf + format->dwNumObjs);
+
+ memmove (input->prevdata, data, format->dwDataSize);
+
+ g_object_unref (controller);
+
+ return TRUE;
+}
+
+static GSourceFuncs dx_dinput_event_funcs = {
+ dx_dinput_event_prepare,
+ dx_dinput_event_check,
+ dx_dinput_event_dispatch,
+ NULL
+};
+
+
+static void
+dump_data_format (const DIDATAFORMAT *format)
+{
+#ifdef GIMP_UNSTABLE
+ gint i;
+
+ g_print ("dwSize: %ld\n", format->dwSize);
+ g_print ("dwObjSize: %ld\n", format->dwObjSize);
+ g_print ("dwFlags: ");
+ if (format->dwFlags == DIDF_ABSAXIS)
+ g_print ("DIDF_ABSAXIS");
+ else if (format->dwFlags == DIDF_RELAXIS)
+ g_print ("DIDF_RELAXIS");
+ else
+ g_print ("%#lx", format->dwFlags);
+ g_print ("\n");
+ g_print ("dwDataSize: %ld\n", format->dwDataSize);
+ g_print ("dwNumObjs: %ld\n", format->dwNumObjs);
+
+ for (i = 0; i < format->dwNumObjs; i++)
+ {
+ const DIOBJECTDATAFORMAT *oformat = (DIOBJECTDATAFORMAT *) (((char *) format->rgodf) + i*format->dwObjSize);
+ unsigned char *guid;
+
+ g_print ("Object %d:\n", i);
+ if (oformat->pguid == NULL)
+ g_print (" pguid: NULL\n");
+ else
+ {
+ UuidToString (oformat->pguid, &guid);
+ g_print (" pguid: %s\n", guid);
+ RpcStringFree (&guid);
+ }
+
+ g_print (" dwOfs: %ld\n", oformat->dwOfs);
+ g_print (" dwType: ");
+ switch (DIDFT_GETTYPE (oformat->dwType))
+ {
+#define CASE(x) case DIDFT_##x: g_print ("DIDFT_"#x); break
+ CASE (RELAXIS);
+ CASE (ABSAXIS);
+ CASE (AXIS);
+ CASE (PSHBUTTON);
+ CASE (TGLBUTTON);
+ CASE (BUTTON);
+ CASE (POV);
+ CASE (COLLECTION);
+ CASE (NODATA);
+#undef CASE
+ default: g_print ("%#x", DIDFT_GETTYPE (oformat->dwType));
+ }
+ g_print (" ");
+ if (DIDFT_GETINSTANCE (oformat->dwType) == DIDFT_GETINSTANCE (DIDFT_ANYINSTANCE))
+ g_print ("ANYINSTANCE");
+ else
+ g_print ("#%d", DIDFT_GETINSTANCE (oformat->dwType));
+#define BIT(x) if (oformat->dwType & DIDFT_##x) g_print (" "#x)
+ BIT (FFACTUATOR);
+ BIT (FFEFFECTTRIGGER);
+ BIT (OUTPUT);
+ BIT (VENDORDEFINED);
+ BIT (ALIAS);
+ BIT (OPTIONAL);
+#undef BIT
+ g_print ("\n");
+ g_print (" dwFlags:");
+ switch (oformat->dwFlags & DIDOI_ASPECTACCEL)
+ {
+ case DIDOI_ASPECTPOSITION: g_print (" DIDOI_ASPECTPOSITION"); break;
+ case DIDOI_ASPECTVELOCITY: g_print (" DIDOI_ASPECTVELOCITY"); break;
+ case DIDOI_ASPECTACCEL: g_print (" DIDOI_ASPECTACCEL"); break;
+ }
+ if (oformat->dwFlags & DIDOI_ASPECTFORCE)
+ g_print (" DIDOI_ASPECTFORCE");
+ g_print ("\n");
+ }
+#endif
+}
+
+static gboolean
+dx_dinput_setup_events (ControllerDXDInput *controller,
+ GError **error)
+{
+ HRESULT hresult;
+ DIPROPDWORD dword;
+ gint i, k;
+ DXDInputSource *source;
+
+ if ((controller->event = CreateEvent (NULL, TRUE, FALSE, NULL)) == NULL)
+ {
+ g_set_error (error, GIMP_MODULE_ERROR, GIMP_MODULE_FAILED,
+ "CreateEvent failed: %s",
+ g_win32_error_message (GetLastError ()));
+ return FALSE;
+ }
+
+ controller->format = g_new (DIDATAFORMAT, 1);
+ controller->format->dwSize = sizeof (DIDATAFORMAT);
+ controller->format->dwObjSize = sizeof (DIOBJECTDATAFORMAT);
+
+ dword.diph.dwSize = sizeof (DIPROPDWORD);
+ dword.diph.dwHeaderSize = sizeof (DIPROPHEADER);
+ dword.diph.dwObj = 0;
+ dword.diph.dwHow = DIPH_DEVICE;
+
+ /* Get the axis mode so we can use the same in the format */
+ if (FAILED ((hresult = IDirectInputDevice8_GetProperty (controller->didevice8,
+ DIPROP_AXISMODE,
+ &dword.diph))))
+ {
+ g_set_error (error, GIMP_MODULE_ERROR, GIMP_MODULE_FAILED,
+ "IDirectInputDevice8::GetParameters failed: %s",
+ g_win32_error_message (hresult));
+ goto fail0;
+ }
+
+ controller->format->dwFlags = dword.dwData + 1;
+
+ controller->format->dwNumObjs =
+ controller->num_buttons +
+ controller->num_axes +
+ controller->num_sliders +
+ controller->num_povs;
+
+ controller->format->rgodf = g_new (DIOBJECTDATAFORMAT, controller->format->dwNumObjs);
+
+ k = 0;
+ controller->format->dwDataSize = 0;
+
+ for (i = 0; i < controller->num_buttons; i++)
+ {
+ controller->format->rgodf[k].pguid = NULL;
+ controller->format->rgodf[k].dwOfs = controller->format->dwDataSize;
+ controller->format->rgodf[k].dwType = DIDFT_BUTTON | DIDFT_MAKEINSTANCE (i);
+ controller->format->rgodf[k].dwFlags = 0;
+ controller->format->dwDataSize += 1;
+ k++;
+ }
+
+ controller->format->dwDataSize = 4*((controller->format->dwDataSize + 3)/4);
+
+ for (i = 0; i < controller->num_axes; i++)
+ {
+ controller->format->rgodf[k].pguid = NULL;
+ controller->format->rgodf[k].dwOfs = controller->format->dwDataSize;
+ controller->format->rgodf[k].dwType = DIDFT_AXIS | DIDFT_MAKEINSTANCE (i);
+ controller->format->rgodf[k].dwFlags = DIDOI_ASPECTPOSITION;
+ controller->format->dwDataSize += 4;
+ k++;
+ }
+
+ for (i = 0; i < controller->num_sliders; i++)
+ {
+ controller->format->rgodf[k].pguid = NULL;
+ controller->format->rgodf[k].dwOfs = controller->format->dwDataSize;
+ controller->format->rgodf[k].dwType = DIDFT_AXIS | DIDFT_MAKEINSTANCE (i);
+ controller->format->rgodf[k].dwFlags = DIDOI_ASPECTPOSITION;
+ controller->format->dwDataSize += 4;
+ k++;
+ }
+
+ for (i = 0; i < controller->num_povs; i++)
+ {
+ controller->format->rgodf[k].pguid = NULL;
+ controller->format->rgodf[k].dwOfs = controller->format->dwDataSize;
+ controller->format->rgodf[k].dwType = DIDFT_POV | DIDFT_MAKEINSTANCE (i);
+ controller->format->rgodf[k].dwFlags = 0;
+ controller->format->dwDataSize += 4;
+ k++;
+ }
+
+ g_assert (k == controller->format->dwNumObjs);
+
+ controller->format->dwDataSize = 4*((controller->format->dwDataSize + 3)/4);
+ controller->prevdata = g_malloc (controller->format->dwDataSize);
+
+ dump_data_format (controller->format);
+
+ if (FAILED ((hresult = IDirectInputDevice8_SetDataFormat (controller->didevice8,
+ controller->format))))
+ {
+ g_set_error (error, GIMP_MODULE_ERROR, GIMP_MODULE_FAILED,
+ "IDirectInputDevice8::SetDataFormat failed: %s",
+ g_win32_error_message (hresult));
+ goto fail1;
+ }
+
+ if (FAILED ((hresult = IDirectInputDevice8_SetEventNotification (controller->didevice8,
+ controller->event))))
+ {
+ g_set_error (error, GIMP_MODULE_ERROR, GIMP_MODULE_FAILED,
+ "IDirectInputDevice8::SetEventNotification failed: %s",
+ g_win32_error_message (hresult));
+ goto fail2;
+ }
+
+ if (FAILED ((hresult = IDirectInputDevice8_Acquire (controller->didevice8))))
+ {
+ g_set_error (error, GIMP_MODULE_ERROR, GIMP_MODULE_FAILED,
+ "IDirectInputDevice8::Acquire failed: %s",
+ g_win32_error_message (hresult));
+ goto fail2;
+ }
+
+ if (FAILED ((hresult = IDirectInputDevice8_GetDeviceState (controller->didevice8,
+ controller->format->dwDataSize,
+ controller->prevdata))))
+ {
+ g_set_error (error, GIMP_MODULE_ERROR, GIMP_MODULE_FAILED,
+ "IDirectInputDevice8::GetDeviceState failed: %s",
+ g_win32_error_message (hresult));
+ goto fail2;
+ }
+
+ source = (DXDInputSource *) g_source_new (&dx_dinput_event_funcs,
+ sizeof (DXDInputSource));
+ source->controller = controller;
+ controller->source = (GSource *) source;
+
+ controller->pollfd = g_new (GPollFD, 1);
+
+ controller->pollfd->fd = (int) controller->event;
+ controller->pollfd->events = G_IO_IN;
+
+ g_source_add_poll (&source->source, controller->pollfd);
+ g_source_attach (&source->source, NULL);
+
+ return TRUE;
+
+ fail2:
+ IDirectInputDevice8_SetEventNotification (controller->didevice8, NULL);
+ fail1:
+ g_free (controller->format->rgodf);
+ g_free (controller->format);
+ controller->format = NULL;
+ g_free (controller->prevdata);
+ controller->prevdata = NULL;
+ fail0:
+ CloseHandle (controller->event);
+ controller->event = 0;
+
+ return FALSE;
+}
+
+static gboolean
+dx_dinput_set_device (ControllerDXDInput *controller,
+ const gchar *guid)
+{
+ GError *error = NULL;
+
+ if (controller->guid)
+ g_free (controller->guid);
+
+ controller->guid = g_strdup (guid);
+
+ g_object_set (controller, "name", _("DirectInput Events"), NULL);
+
+ if (controller->guid && strlen (controller->guid))
+ {
+ if (controller->store)
+ controller->didevice8 =
+ (LPDIRECTINPUTDEVICE8W) gimp_input_device_store_get_device_file (controller->store,
+ controller->guid);
+ }
+ else
+ {
+ g_object_set (controller, "state", _("No device configured"), NULL);
+
+ return FALSE;
+ }
+
+ if (controller->didevice8)
+ {
+ if (!dx_dinput_get_device_info (controller, &error) ||
+ !dx_dinput_setup_events (controller, &error))
+ {
+ g_object_set (controller, "state", error->message, NULL);
+ g_error_free (error);
+ }
+ }
+ else if (controller->store)
+ {
+ error = gimp_input_device_store_get_error (controller->store);
+
+ if (error)
+ {
+ g_object_set (controller, "state", error->message, NULL);
+ g_error_free (error);
+ }
+ else
+ {
+ g_object_set (controller, "state", _("Device not available"), NULL);
+ }
+ }
+
+ return FALSE;
+}
diff --git a/modules/controller-linux-input.c b/modules/controller-linux-input.c
new file mode 100644
index 0000000..760ccd8
--- /dev/null
+++ b/modules/controller-linux-input.c
@@ -0,0 +1,711 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * controller_linux_input.c
+ * Copyright (C) 2004-2007 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <linux/input.h>
+
+#include <glib/gstdio.h>
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpconfig/gimpconfig.h"
+#include "libgimpmodule/gimpmodule.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#define GIMP_ENABLE_CONTROLLER_UNDER_CONSTRUCTION
+#include "libgimpwidgets/gimpcontroller.h"
+
+#include "gimpinputdevicestore.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+typedef struct
+{
+ guint16 code;
+ const gchar *name;
+ const gchar *blurb;
+} LinuxInputEvent;
+
+static const LinuxInputEvent key_events[] =
+{
+ { BTN_0, "button-0", N_("Button 0") },
+ { BTN_1, "button-1", N_("Button 1") },
+ { BTN_2, "button-2", N_("Button 2") },
+ { BTN_3, "button-3", N_("Button 3") },
+ { BTN_4, "button-4", N_("Button 4") },
+ { BTN_5, "button-5", N_("Button 5") },
+ { BTN_6, "button-6", N_("Button 6") },
+ { BTN_7, "button-7", N_("Button 7") },
+ { BTN_8, "button-8", N_("Button 8") },
+ { BTN_9, "button-9", N_("Button 9") },
+ { BTN_MOUSE, "button-mouse", N_("Button Mouse") },
+ { BTN_LEFT, "button-left", N_("Button Left") },
+ { BTN_RIGHT, "button-right", N_("Button Right") },
+ { BTN_MIDDLE, "button-middle", N_("Button Middle") },
+ { BTN_SIDE, "button-side", N_("Button Side") },
+ { BTN_EXTRA, "button-extra", N_("Button Extra") },
+ { BTN_FORWARD, "button-forward", N_("Button Forward") },
+ { BTN_BACK, "button-back", N_("Button Back") },
+ { BTN_TASK, "button-task", N_("Button Task") },
+#ifdef BTN_WHEEL
+ { BTN_WHEEL, "button-wheel", N_("Button Wheel") },
+#endif
+#ifdef BTN_GEAR_DOWN
+ { BTN_GEAR_DOWN, "button-gear-down", N_("Button Gear Down") },
+#endif
+#ifdef BTN_GEAR_UP
+ { BTN_GEAR_UP, "button-gear-up", N_("Button Gear Up") }
+#endif
+};
+
+static const LinuxInputEvent rel_events[] =
+{
+ { REL_X, "x-move-left", N_("X Move Left") },
+ { REL_X, "x-move-right", N_("X Move Right") },
+ { REL_Y, "y-move-forward", N_("Y Move Forward") },
+ { REL_Y, "y-move-back", N_("Y Move Back") },
+ { REL_Z, "z-move-up", N_("Z Move Up") },
+ { REL_Z, "z-move-down", N_("Z Move Down") },
+#ifdef REL_RX
+ { REL_RX, "x-axis-tilt-forward", N_("X Axis Tilt Forward") },
+ { REL_RX, "x-axis-tilt-back", N_("X Axis Tilt Back") },
+ { REL_RY, "y-axis-tilt-right", N_("Y Axis Tilt Right") },
+ { REL_RY, "y-axis-tilt-left", N_("Y Axis Tilt Left") },
+ { REL_RZ, "z-axis-turn-left", N_("Z Axis Turn Left") },
+ { REL_RZ, "z-axis-turn-right", N_("Z Axis Turn Right") },
+#endif /* REL_RX */
+ { REL_HWHEEL, "horizontal-wheel-turn-back", N_("Horiz. Wheel Turn Back") },
+ { REL_HWHEEL, "horizontal-wheel-turn-forward", N_("Horiz. Wheel Turn Forward") },
+ { REL_DIAL, "dial-turn-left", N_("Dial Turn Left") },
+ { REL_DIAL, "dial-turn-right", N_("Dial Turn Right") },
+ { REL_WHEEL, "wheel-turn-left", N_("Wheel Turn Left") },
+ { REL_WHEEL, "wheel-turn-right", N_("Wheel Turn Right") },
+};
+
+
+enum
+{
+ PROP_0,
+ PROP_DEVICE,
+ PROP_DEVICE_STORE
+};
+
+
+#define CONTROLLER_TYPE_LINUX_INPUT (controller_linux_input_get_type ())
+#define CONTROLLER_LINUX_INPUT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CONTROLLER_TYPE_LINUX_INPUT, ControllerLinuxInput))
+#define CONTROLLER_LINUX_INPUT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CONTROLLER_TYPE_LINUX_INPUT, ControllerLinuxInputClass))
+#define CONTROLLER_IS_LINUX_INPUT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CONTROLLER_TYPE_LINUX_INPUT))
+#define CONTROLLER_IS_LINUX_INPUT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CONTROLLER_TYPE_LINUX_INPUT))
+
+
+typedef struct _ControllerLinuxInput ControllerLinuxInput;
+typedef struct _ControllerLinuxInputClass ControllerLinuxInputClass;
+
+struct _ControllerLinuxInput
+{
+ GimpController parent_instance;
+
+ GimpInputDeviceStore *store;
+ gchar *device;
+ GIOChannel *io;
+ guint io_id;
+};
+
+struct _ControllerLinuxInputClass
+{
+ GimpControllerClass parent_class;
+};
+
+
+static GType controller_linux_input_get_type (void);
+
+static void linux_input_dispose (GObject *object);
+static void linux_input_finalize (GObject *object);
+static void linux_input_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void linux_input_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static gint linux_input_get_n_events (GimpController *controller);
+static const gchar * linux_input_get_event_name (GimpController *controller,
+ gint event_id);
+static const gchar * linux_input_get_event_blurb (GimpController *controller,
+ gint event_id);
+
+static void linux_input_device_changed (ControllerLinuxInput *controller,
+ const gchar *udi);
+static gboolean linux_input_set_device (ControllerLinuxInput *controller,
+ const gchar *device);
+static gboolean linux_input_read_event (GIOChannel *io,
+ GIOCondition cond,
+ gpointer data);
+
+
+static const GimpModuleInfo linux_input_info =
+{
+ GIMP_MODULE_ABI_VERSION,
+ N_("Linux input event controller"),
+ "Sven Neumann <sven@gimp.org>, Michael Natterer <mitch@gimp.org>",
+ "v0.2",
+ "(c) 2004-2007, released under the GPL",
+ "2004-2007"
+};
+
+
+G_DEFINE_DYNAMIC_TYPE (ControllerLinuxInput, controller_linux_input,
+ GIMP_TYPE_CONTROLLER)
+
+
+G_MODULE_EXPORT const GimpModuleInfo *
+gimp_module_query (GTypeModule *module)
+{
+ return &linux_input_info;
+}
+
+G_MODULE_EXPORT gboolean
+gimp_module_register (GTypeModule *module)
+{
+ gimp_input_device_store_register_types (module);
+ controller_linux_input_register_type (module);
+
+ return TRUE;
+}
+
+static void
+controller_linux_input_class_init (ControllerLinuxInputClass *klass)
+{
+ GimpControllerClass *controller_class = GIMP_CONTROLLER_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = linux_input_dispose;
+ object_class->finalize = linux_input_finalize;
+ object_class->get_property = linux_input_get_property;
+ object_class->set_property = linux_input_set_property;
+
+ g_object_class_install_property (object_class, PROP_DEVICE,
+ g_param_spec_string ("device",
+ _("Device:"),
+ _("The name of the device to read Linux Input events from."),
+ NULL,
+ GIMP_CONFIG_PARAM_FLAGS));
+#ifdef HAVE_LIBGUDEV
+ g_object_class_install_property (object_class, PROP_DEVICE_STORE,
+ g_param_spec_object ("device-values",
+ NULL, NULL,
+ GIMP_TYPE_INPUT_DEVICE_STORE,
+ G_PARAM_READABLE));
+#endif
+
+ controller_class->name = _("Linux Input");
+ controller_class->help_id = "gimp-controller-linux-input";
+ controller_class->icon_name = GIMP_ICON_CONTROLLER_LINUX_INPUT;
+
+ controller_class->get_n_events = linux_input_get_n_events;
+ controller_class->get_event_name = linux_input_get_event_name;
+ controller_class->get_event_blurb = linux_input_get_event_blurb;
+}
+
+static void
+controller_linux_input_class_finalize (ControllerLinuxInputClass *klass)
+{
+}
+
+static void
+controller_linux_input_init (ControllerLinuxInput *controller)
+{
+ controller->store = gimp_input_device_store_new ();
+
+ if (controller->store)
+ {
+ g_signal_connect_swapped (controller->store, "device-added",
+ G_CALLBACK (linux_input_device_changed),
+ controller);
+ g_signal_connect_swapped (controller->store, "device-removed",
+ G_CALLBACK (linux_input_device_changed),
+ controller);
+ }
+}
+
+static void
+linux_input_dispose (GObject *object)
+{
+ ControllerLinuxInput *controller = CONTROLLER_LINUX_INPUT (object);
+
+ linux_input_set_device (controller, NULL);
+
+ G_OBJECT_CLASS (controller_linux_input_parent_class)->dispose (object);
+}
+
+static void
+linux_input_finalize (GObject *object)
+{
+ ControllerLinuxInput *controller = CONTROLLER_LINUX_INPUT (object);
+
+ if (controller->store)
+ {
+ g_object_unref (controller->store);
+ controller->store = NULL;
+ }
+
+ G_OBJECT_CLASS (controller_linux_input_parent_class)->finalize (object);
+}
+
+static void
+linux_input_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ControllerLinuxInput *controller = CONTROLLER_LINUX_INPUT (object);
+
+ switch (property_id)
+ {
+ case PROP_DEVICE:
+ linux_input_set_device (controller, g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+linux_input_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ControllerLinuxInput *controller = CONTROLLER_LINUX_INPUT (object);
+
+ switch (property_id)
+ {
+ case PROP_DEVICE:
+ g_value_set_string (value, controller->device);
+ break;
+ case PROP_DEVICE_STORE:
+ g_value_set_object (value, controller->store);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static gint
+linux_input_get_n_events (GimpController *controller)
+{
+ return G_N_ELEMENTS (key_events) + G_N_ELEMENTS (rel_events);
+}
+
+static const gchar *
+linux_input_get_event_name (GimpController *controller,
+ gint event_id)
+{
+ if (event_id < 0)
+ {
+ return NULL;
+ }
+ else if (event_id < G_N_ELEMENTS (key_events))
+ {
+ return key_events[event_id].name;
+ }
+ else if (event_id < linux_input_get_n_events (controller))
+ {
+ return rel_events[event_id - G_N_ELEMENTS (key_events)].name;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+static const gchar *
+linux_input_get_event_blurb (GimpController *controller,
+ gint event_id)
+{
+ if (event_id < 0)
+ {
+ return NULL;
+ }
+ else if (event_id < G_N_ELEMENTS (key_events))
+ {
+ return gettext (key_events[event_id].blurb);
+ }
+ else if (event_id < linux_input_get_n_events (controller))
+ {
+ return gettext (rel_events[event_id - G_N_ELEMENTS (key_events)].blurb);
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+#define BITS_PER_LONG (sizeof(long) * 8)
+#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
+#define OFF(x) ((x)%BITS_PER_LONG)
+#define BIT(x) (1UL<<OFF(x))
+#define LONG(x) ((x)/BITS_PER_LONG)
+#define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1)
+
+static void
+linux_input_get_device_info (ControllerLinuxInput *controller,
+ int fd)
+{
+ unsigned long evbit[NBITS (EV_MAX)];
+ unsigned long keybit[NBITS (KEY_MAX)];
+ unsigned long relbit[NBITS (REL_MAX)];
+ unsigned long absbit[NBITS (ABS_MAX)];
+
+ gint num_keys = 0;
+ gint num_ext_keys = 0;
+ gint num_buttons = 0;
+ gint num_rels = 0;
+ gint num_abs = 0;
+
+ /* get event type bits */
+ ioctl (fd, EVIOCGBIT (0, EV_MAX), evbit);
+
+ if (test_bit (EV_KEY, evbit))
+ {
+ gint i;
+
+ /* get keyboard bits */
+ ioctl (fd, EVIOCGBIT (EV_KEY, KEY_MAX), keybit);
+
+ /** count typical keyboard keys only */
+ for (i = KEY_Q; i < KEY_M; i++)
+ if (test_bit (i, keybit))
+ {
+ num_keys++;
+
+ g_print ("%s: key 0x%02x present\n", G_STRFUNC, i);
+ }
+
+ g_print ("%s: #keys = %d\n", G_STRFUNC, num_keys);
+
+ for (i = KEY_OK; i < KEY_MAX; i++)
+ if (test_bit (i, keybit))
+ {
+ num_ext_keys++;
+
+ g_print ("%s: ext key 0x%02x present\n", G_STRFUNC, i);
+ }
+
+ g_print ("%s: #ext_keys = %d\n", G_STRFUNC, num_ext_keys);
+
+ for (i = BTN_MISC; i < KEY_OK; i++)
+ if (test_bit (i, keybit))
+ {
+ num_buttons++;
+
+ g_print ("%s: button 0x%02x present\n", G_STRFUNC, i);
+ }
+
+ g_print ("%s: #buttons = %d\n", G_STRFUNC, num_buttons);
+ }
+
+ if (test_bit (EV_REL, evbit))
+ {
+ gint i;
+
+ /* get bits for relative axes */
+ ioctl (fd, EVIOCGBIT (EV_REL, REL_MAX), relbit);
+
+ for (i = 0; i < REL_MAX; i++)
+ if (test_bit (i, relbit))
+ {
+ num_rels++;
+
+ g_print ("%s: rel 0x%02x present\n", G_STRFUNC, i);
+ }
+
+ g_print ("%s: #rels = %d\n", G_STRFUNC, num_rels);
+ }
+
+ if (test_bit (EV_ABS, evbit))
+ {
+ gint i;
+
+ /* get bits for absolute axes */
+ ioctl (fd, EVIOCGBIT (EV_ABS, ABS_MAX), absbit);
+
+ for (i = 0; i < ABS_MAX; i++)
+ if (test_bit (i, absbit))
+ {
+ struct input_absinfo absinfo;
+
+ num_abs++;
+
+ /* get info for the absolute axis */
+ ioctl (fd, EVIOCGABS (i), &absinfo);
+
+ g_print ("%s: abs 0x%02x present [%d..%d]\n", G_STRFUNC, i,
+ absinfo.minimum, absinfo.maximum);
+ }
+
+ g_print ("%s: #abs = %d\n", G_STRFUNC, num_abs);
+ }
+}
+
+static void
+linux_input_device_changed (ControllerLinuxInput *controller,
+ const gchar *identifier)
+{
+ if (controller->device && strcmp (identifier, controller->device) == 0)
+ {
+ linux_input_set_device (controller, identifier);
+ g_object_notify (G_OBJECT (controller), "device");
+ }
+}
+
+static gboolean
+linux_input_set_device (ControllerLinuxInput *controller,
+ const gchar *device)
+{
+ gchar *filename;
+
+ if (controller->io)
+ {
+ g_source_remove (controller->io_id);
+ controller->io_id = 0;
+
+ g_io_channel_unref (controller->io);
+ controller->io = NULL;
+ }
+
+ if (controller->device)
+ g_free (controller->device);
+
+ controller->device = g_strdup (device);
+
+ g_object_set (controller, "name", _("Linux Input Events"), NULL);
+
+ if (controller->device && strlen (controller->device))
+ {
+ if (controller->store)
+ filename = gimp_input_device_store_get_device_file (controller->store,
+ controller->device);
+ else
+ filename = g_strdup (controller->device);
+ }
+ else
+ {
+ g_object_set (controller, "state", _("No device configured"), NULL);
+
+ return FALSE;
+ }
+
+ if (filename)
+ {
+ gchar *state;
+ gint fd;
+
+ fd = g_open (filename, O_RDONLY, 0);
+
+ if (fd >= 0)
+ {
+ gchar name[256];
+
+ name[0] = '\0';
+ if (ioctl (fd, EVIOCGNAME (sizeof (name)), name) >= 0 &&
+ strlen (name) > 0 &&
+ g_utf8_validate (name, -1, NULL))
+ {
+ g_object_set (controller, "name", name, NULL);
+ }
+
+ linux_input_get_device_info (controller, fd);
+
+ state = g_strdup_printf (_("Reading from %s"), filename);
+ g_object_set (controller, "state", state, NULL);
+ g_free (state);
+
+ g_free (filename);
+
+ controller->io = g_io_channel_unix_new (fd);
+ g_io_channel_set_close_on_unref (controller->io, TRUE);
+ g_io_channel_set_encoding (controller->io, NULL, NULL);
+
+ controller->io_id = g_io_add_watch (controller->io,
+ G_IO_IN,
+ linux_input_read_event,
+ controller);
+ return TRUE;
+ }
+ else
+ {
+ state = g_strdup_printf (_("Device not available: %s"),
+ g_strerror (errno));
+ g_object_set (controller, "state", state, NULL);
+ g_free (state);
+ }
+
+ g_free (filename);
+ }
+ else if (controller->store)
+ {
+ GError *error = gimp_input_device_store_get_error (controller->store);
+
+ if (error)
+ {
+ g_object_set (controller, "state", error->message, NULL);
+ g_error_free (error);
+ }
+ else
+ {
+ g_object_set (controller, "state", _("Device not available"), NULL);
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean
+linux_input_read_event (GIOChannel *io,
+ GIOCondition cond,
+ gpointer data)
+{
+ ControllerLinuxInput *input = CONTROLLER_LINUX_INPUT (data);
+ GIOStatus status;
+ GError *error = NULL;
+ struct input_event ev;
+ gsize n_bytes;
+
+ status = g_io_channel_read_chars (io,
+ (gchar *) &ev,
+ sizeof (struct input_event), &n_bytes,
+ &error);
+
+ switch (status)
+ {
+ case G_IO_STATUS_ERROR:
+ case G_IO_STATUS_EOF:
+ g_source_remove (input->io_id);
+ input->io_id = 0;
+
+ g_io_channel_unref (input->io);
+ input->io = NULL;
+
+ if (error)
+ {
+ gchar *state = g_strdup_printf (_("Device not available: %s"),
+ error->message);
+ g_object_set (input, "state", state, NULL);
+ g_free (state);
+
+ g_clear_error (&error);
+ }
+ else
+ {
+ g_object_set (input, "state", _("End of file"), NULL);
+ }
+ return FALSE;
+ break;
+
+ case G_IO_STATUS_AGAIN:
+ return TRUE;
+
+ default:
+ break;
+ }
+
+ if (n_bytes == sizeof (struct input_event))
+ {
+ GimpController *controller = GIMP_CONTROLLER (data);
+ GimpControllerEvent cevent = { 0, };
+ gint i;
+
+ switch (ev.type)
+ {
+ case EV_KEY:
+ g_print ("%s: EV_KEY code = 0x%02x\n", G_STRFUNC, ev.code);
+
+ for (i = 0; i < G_N_ELEMENTS (key_events); i++)
+ if (ev.code == key_events[i].code)
+ {
+ cevent.any.type = GIMP_CONTROLLER_EVENT_TRIGGER;
+ cevent.any.source = controller;
+ cevent.any.event_id = i;
+
+ gimp_controller_event (controller, &cevent);
+
+ break;
+ }
+ break;
+
+ case EV_REL:
+ g_print ("%s: EV_REL code = 0x%02x (value = %d)\n", G_STRFUNC,
+ ev.code, ev.value);
+
+ for (i = 0; i < G_N_ELEMENTS (rel_events); i++)
+ if (ev.code == rel_events[i].code)
+ {
+ cevent.any.type = GIMP_CONTROLLER_EVENT_VALUE;
+ cevent.any.source = controller;
+ cevent.any.event_id = G_N_ELEMENTS (key_events) + i;
+
+ g_value_init (&cevent.value.value, G_TYPE_DOUBLE);
+
+ if (ev.value < 0)
+ {
+ g_value_set_double (&cevent.value.value, -ev.value);
+ }
+ else
+ {
+ cevent.any.event_id++;
+
+ g_value_set_double (&cevent.value.value, ev.value);
+ }
+
+ gimp_controller_event (controller, &cevent);
+
+ g_value_unset (&cevent.value.value);
+
+ break;
+ }
+ break;
+
+ case EV_ABS:
+ g_print ("%s: EV_ABS code = 0x%02x (value = %d)\n", G_STRFUNC,
+ ev.code, ev.value);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return TRUE;
+}
diff --git a/modules/controller-midi.c b/modules/controller-midi.c
new file mode 100644
index 0000000..284f02d
--- /dev/null
+++ b/modules/controller-midi.c
@@ -0,0 +1,893 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * controller_midi.c
+ * Copyright (C) 2004-2007 Michael Natterer <mitch@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#define _GNU_SOURCE /* the ALSA headers need this */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include <glib/gstdio.h>
+
+#ifdef HAVE_ALSA
+#include <alsa/asoundlib.h>
+#endif
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpconfig/gimpconfig.h"
+#include "libgimpmodule/gimpmodule.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#define GIMP_ENABLE_CONTROLLER_UNDER_CONSTRUCTION
+#include "libgimpwidgets/gimpcontroller.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+typedef struct
+{
+ gchar *name;
+ gchar *blurb;
+} MidiEvent;
+
+
+enum
+{
+ PROP_0,
+ PROP_DEVICE,
+ PROP_CHANNEL
+};
+
+
+#define CONTROLLER_TYPE_MIDI (controller_midi_get_type ())
+#define CONTROLLER_MIDI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CONTROLLER_TYPE_MIDI, ControllerMidi))
+#define CONTROLLER_MIDI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CONTROLLER_TYPE_MIDI, ControllerMidiClass))
+#define CONTROLLER_IS_MIDI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CONTROLLER_TYPE_MIDI))
+#define CONTROLLER_IS_MIDI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CONTROLLER_TYPE_MIDI))
+
+
+typedef struct _ControllerMidi ControllerMidi;
+typedef struct _ControllerMidiClass ControllerMidiClass;
+
+struct _ControllerMidi
+{
+ GimpController parent_instance;
+
+ gchar *device;
+ gint midi_channel;
+
+ GIOChannel *io;
+ guint io_id;
+
+#ifdef HAVE_ALSA
+ snd_seq_t *sequencer;
+ guint seq_id;
+#endif
+
+ /* midi status */
+ gboolean swallow;
+ gint command;
+ gint channel;
+ gint key;
+ gint velocity;
+ gint msb;
+ gint lsb;
+};
+
+struct _ControllerMidiClass
+{
+ GimpControllerClass parent_class;
+};
+
+
+static GType controller_midi_get_type (void);
+
+static void midi_dispose (GObject *object);
+static void midi_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void midi_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static gint midi_get_n_events (GimpController *controller);
+static const gchar * midi_get_event_name (GimpController *controller,
+ gint event_id);
+static const gchar * midi_get_event_blurb (GimpController *controller,
+ gint event_id);
+
+static gboolean midi_set_device (ControllerMidi *controller,
+ const gchar *device);
+static void midi_event (ControllerMidi *midi,
+ gint channel,
+ gint event_id,
+ gdouble value);
+
+static gboolean midi_read_event (GIOChannel *io,
+ GIOCondition cond,
+ gpointer data);
+
+#ifdef HAVE_ALSA
+static gboolean midi_alsa_prepare (GSource *source,
+ gint *timeout);
+static gboolean midi_alsa_check (GSource *source);
+static gboolean midi_alsa_dispatch (GSource *source,
+ GSourceFunc callback,
+ gpointer user_data);
+
+static GSourceFuncs alsa_source_funcs =
+{
+ midi_alsa_prepare,
+ midi_alsa_check,
+ midi_alsa_dispatch,
+ NULL
+};
+
+typedef struct _GAlsaSource GAlsaSource;
+
+struct _GAlsaSource
+{
+ GSource source;
+ ControllerMidi *controller;
+};
+#endif /* HAVE_ALSA */
+
+static const GimpModuleInfo midi_info =
+{
+ GIMP_MODULE_ABI_VERSION,
+ N_("MIDI event controller"),
+ "Michael Natterer <mitch@gimp.org>",
+ "v0.2",
+ "(c) 2004-2007, released under the GPL",
+ "2004-2007"
+};
+
+
+G_DEFINE_DYNAMIC_TYPE (ControllerMidi, controller_midi,
+ GIMP_TYPE_CONTROLLER)
+
+static MidiEvent midi_events[128 + 128 + 128];
+
+
+G_MODULE_EXPORT const GimpModuleInfo *
+gimp_module_query (GTypeModule *module)
+{
+ return &midi_info;
+}
+
+G_MODULE_EXPORT gboolean
+gimp_module_register (GTypeModule *module)
+{
+ controller_midi_register_type (module);
+
+ return TRUE;
+}
+
+static void
+controller_midi_class_init (ControllerMidiClass *klass)
+{
+ GimpControllerClass *controller_class = GIMP_CONTROLLER_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ gchar *blurb;
+
+ object_class->dispose = midi_dispose;
+ object_class->get_property = midi_get_property;
+ object_class->set_property = midi_set_property;
+
+ blurb = g_strconcat (_("The name of the device to read MIDI events from."),
+#ifdef HAVE_ALSA
+ "\n",
+ _("Enter 'alsa' to use the ALSA sequencer."),
+#endif
+ NULL);
+
+ g_object_class_install_property (object_class, PROP_DEVICE,
+ g_param_spec_string ("device",
+ _("Device:"),
+ blurb,
+ NULL,
+ GIMP_CONFIG_PARAM_FLAGS));
+
+ g_free (blurb);
+
+ g_object_class_install_property (object_class, PROP_CHANNEL,
+ g_param_spec_int ("channel",
+ _("Channel:"),
+ _("The MIDI channel to read events from. Set to -1 for reading from all MIDI channels."),
+ -1, 15, -1,
+ GIMP_CONFIG_PARAM_FLAGS));
+
+ controller_class->name = _("MIDI");
+ controller_class->help_id = "gimp-controller-midi";
+ controller_class->icon_name = GIMP_ICON_CONTROLLER_MIDI;
+
+ controller_class->get_n_events = midi_get_n_events;
+ controller_class->get_event_name = midi_get_event_name;
+ controller_class->get_event_blurb = midi_get_event_blurb;
+}
+
+static void
+controller_midi_class_finalize (ControllerMidiClass *klass)
+{
+}
+
+static void
+controller_midi_init (ControllerMidi *midi)
+{
+ midi->device = NULL;
+ midi->midi_channel = -1;
+ midi->io = NULL;
+ midi->io_id = 0;
+#ifdef HAVE_ALSA
+ midi->sequencer = NULL;
+ midi->seq_id = 0;
+#endif
+
+ midi->swallow = TRUE; /* get rid of data bytes at start of stream */
+ midi->command = 0x0;
+ midi->channel = 0x0;
+ midi->key = -1;
+ midi->velocity = -1;
+ midi->msb = -1;
+ midi->lsb = -1;
+}
+
+static void
+midi_dispose (GObject *object)
+{
+ ControllerMidi *midi = CONTROLLER_MIDI (object);
+
+ midi_set_device (midi, NULL);
+
+ G_OBJECT_CLASS (controller_midi_parent_class)->dispose (object);
+}
+
+static void
+midi_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ControllerMidi *midi = CONTROLLER_MIDI (object);
+
+ switch (property_id)
+ {
+ case PROP_DEVICE:
+ midi_set_device (midi, g_value_get_string (value));
+ break;
+ case PROP_CHANNEL:
+ midi->midi_channel = g_value_get_int (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+midi_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ControllerMidi *midi = CONTROLLER_MIDI (object);
+
+ switch (property_id)
+ {
+ case PROP_DEVICE:
+ g_value_set_string (value, midi->device);
+ break;
+ case PROP_CHANNEL:
+ g_value_set_int (value, midi->midi_channel);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static gint
+midi_get_n_events (GimpController *controller)
+{
+ return 128 + 128 + 128;
+}
+
+static const gchar *
+midi_get_event_name (GimpController *controller,
+ gint event_id)
+{
+ if (event_id < (128 + 128 + 128))
+ {
+ if (! midi_events[event_id].name)
+ {
+ if (event_id < 128)
+ midi_events[event_id].name = g_strdup_printf ("note-on-%02x",
+ event_id);
+ else if (event_id < (128 + 128))
+ midi_events[event_id].name = g_strdup_printf ("note-off-%02x",
+ event_id - 128);
+ else if (event_id < (128 + 128 + 128))
+ midi_events[event_id].name = g_strdup_printf ("controller-%03d",
+ event_id - 256);
+ }
+
+ return midi_events[event_id].name;
+ }
+
+ return NULL;
+}
+
+static const gchar *
+midi_get_event_blurb (GimpController *controller,
+ gint event_id)
+{
+ if (event_id <= 383)
+ {
+ if (! midi_events[event_id].blurb)
+ {
+ if (event_id <= 127)
+ midi_events[event_id].blurb = g_strdup_printf (_("Note %02x on"),
+ event_id);
+ else if (event_id <= 255)
+ midi_events[event_id].blurb = g_strdup_printf (_("Note %02x off"),
+ event_id - 128);
+ else if (event_id <= 383)
+ midi_events[event_id].blurb = g_strdup_printf (_("Controller %03d"),
+ event_id - 256);
+ }
+
+ return midi_events[event_id].blurb;
+ }
+
+ return NULL;
+}
+
+static gboolean
+midi_set_device (ControllerMidi *midi,
+ const gchar *device)
+{
+ midi->swallow = TRUE;
+ midi->command = 0x0;
+ midi->channel = 0x0;
+ midi->key = -1;
+ midi->velocity = -1;
+ midi->msb = -1;
+ midi->lsb = -1;
+
+ if (midi->io)
+ {
+ g_source_remove (midi->io_id);
+ midi->io_id = 0;
+
+ g_io_channel_unref (midi->io);
+ midi->io = NULL;
+ }
+
+#ifdef HAVE_ALSA
+ if (midi->seq_id)
+ {
+ g_source_remove (midi->seq_id);
+ midi->seq_id = 0;
+
+ snd_seq_close (midi->sequencer);
+ midi->sequencer = NULL;
+ }
+#endif /* HAVE_ALSA */
+
+ if (midi->device)
+ g_free (midi->device);
+
+ midi->device = g_strdup (device);
+
+ g_object_set (midi, "name", _("MIDI Events"), NULL);
+
+ if (midi->device && strlen (midi->device))
+ {
+ gint fd;
+
+#ifdef HAVE_ALSA
+ if (! g_ascii_strcasecmp (midi->device, "alsa"))
+ {
+ GSource *event_source;
+ gchar *alsa;
+ gchar *state;
+ gint ret;
+
+ ret = snd_seq_open (&midi->sequencer, "default",
+ SND_SEQ_OPEN_INPUT, 0);
+ if (ret >= 0)
+ {
+ snd_seq_set_client_name (midi->sequencer, _("GIMP"));
+ ret = snd_seq_create_simple_port (midi->sequencer,
+ _("GIMP MIDI Input Controller"),
+ SND_SEQ_PORT_CAP_WRITE |
+ SND_SEQ_PORT_CAP_SUBS_WRITE,
+ SND_SEQ_PORT_TYPE_APPLICATION);
+ }
+
+ if (ret < 0)
+ {
+ state = g_strdup_printf (_("Device not available: %s"),
+ snd_strerror (ret));
+ g_object_set (midi, "state", state, NULL);
+ g_free (state);
+
+ if (midi->sequencer)
+ {
+ snd_seq_close (midi->sequencer);
+ midi->sequencer = NULL;
+ }
+
+ return FALSE;
+ }
+
+ /* hack to avoid new message to translate */
+ alsa = g_strdup_printf ("ALSA (%d:%d)",
+ snd_seq_client_id (midi->sequencer),
+ ret);
+ state = g_strdup_printf (_("Reading from %s"), alsa);
+ g_free (alsa);
+
+ g_object_set (midi, "state", state, NULL);
+ g_free (state);
+
+ event_source = g_source_new (&alsa_source_funcs,
+ sizeof (GAlsaSource));
+
+ ((GAlsaSource *) event_source)->controller = midi;
+
+ midi->seq_id = g_source_attach (event_source, NULL);
+ g_source_unref (event_source);
+
+ return TRUE;
+ }
+#endif /* HAVE_ALSA */
+
+#ifdef G_OS_WIN32
+ fd = g_open (midi->device, O_RDONLY, 0);
+#else
+ fd = g_open (midi->device, O_RDONLY | O_NONBLOCK, 0);
+#endif
+
+ if (fd >= 0)
+ {
+ gchar *state = g_strdup_printf (_("Reading from %s"), midi->device);
+ g_object_set (midi, "state", state, NULL);
+ g_free (state);
+
+ midi->io = g_io_channel_unix_new (fd);
+ g_io_channel_set_close_on_unref (midi->io, TRUE);
+ g_io_channel_set_encoding (midi->io, NULL, NULL);
+
+ midi->io_id = g_io_add_watch (midi->io,
+ G_IO_IN | G_IO_ERR |
+ G_IO_HUP | G_IO_NVAL,
+ midi_read_event,
+ midi);
+ return TRUE;
+ }
+ else
+ {
+ gchar *state = g_strdup_printf (_("Device not available: %s"),
+ g_strerror (errno));
+ g_object_set (midi, "state", state, NULL);
+ g_free (state);
+ }
+ }
+ else
+ {
+ g_object_set (midi, "state", _("No device configured"), NULL);
+ }
+
+ return FALSE;
+}
+
+static void
+midi_event (ControllerMidi *midi,
+ gint channel,
+ gint event_id,
+ gdouble value)
+{
+ if (channel == -1 ||
+ midi->midi_channel == -1 ||
+ channel == midi->midi_channel)
+ {
+ GimpControllerEvent event = { 0, };
+
+ event.any.type = GIMP_CONTROLLER_EVENT_VALUE;
+ event.any.source = GIMP_CONTROLLER (midi);
+ event.any.event_id = event_id;
+
+ g_value_init (&event.value.value, G_TYPE_DOUBLE);
+ g_value_set_double (&event.value.value, value);
+
+ gimp_controller_event (GIMP_CONTROLLER (midi), &event);
+
+ g_value_unset (&event.value.value);
+ }
+}
+
+
+#define D(stmnt) stmnt;
+
+gboolean
+midi_read_event (GIOChannel *io,
+ GIOCondition cond,
+ gpointer data)
+{
+ ControllerMidi *midi = CONTROLLER_MIDI (data);
+ GIOStatus status;
+ GError *error = NULL;
+ guchar buf[0xf];
+ gsize size;
+ gint pos = 0;
+
+ status = g_io_channel_read_chars (io,
+ (gchar *) buf,
+ sizeof (buf), &size,
+ &error);
+
+ switch (status)
+ {
+ case G_IO_STATUS_ERROR:
+ case G_IO_STATUS_EOF:
+ g_source_remove (midi->io_id);
+ midi->io_id = 0;
+
+ g_io_channel_unref (midi->io);
+ midi->io = NULL;
+
+ if (error)
+ {
+ gchar *state = g_strdup_printf (_("Device not available: %s"),
+ error->message);
+ g_object_set (midi, "state", state, NULL);
+ g_free (state);
+
+ g_clear_error (&error);
+ }
+ else
+ {
+ g_object_set (midi, "state", _("End of file"), NULL);
+ }
+ return FALSE;
+ break;
+
+ case G_IO_STATUS_AGAIN:
+ return TRUE;
+
+ default:
+ break;
+ }
+
+ while (pos < size)
+ {
+#if 0
+ gint i;
+
+ g_print ("MIDI IN (%d bytes), command 0x%02x: ", size, midi->command);
+
+ for (i = 0; i < size; i++)
+ g_print ("%02x ", buf[i]);
+ g_print ("\n");
+#endif
+
+ if (buf[pos] & 0x80) /* status byte */
+ {
+ if (buf[pos] >= 0xf8) /* realtime messages */
+ {
+ switch (buf[pos])
+ {
+ case 0xf8: /* timing clock */
+ case 0xf9: /* (undefined) */
+ case 0xfa: /* start */
+ case 0xfb: /* continue */
+ case 0xfc: /* stop */
+ case 0xfd: /* (undefined) */
+ case 0xfe: /* active sensing */
+ case 0xff: /* system reset */
+ /* nop */
+#if 0
+ g_print ("MIDI: realtime message (%02x)\n", buf[pos]);
+#endif
+ break;
+ }
+ }
+ else
+ {
+ midi->swallow = FALSE; /* any status bytes ends swallowing */
+
+ if (buf[pos] >= 0xf0) /* system messages */
+ {
+ switch (buf[pos])
+ {
+ case 0xf0: /* sysex start */
+ midi->swallow = TRUE;
+
+ D (g_print ("MIDI: sysex start\n"));
+ break;
+
+ case 0xf1: /* time code */
+ midi->swallow = TRUE; /* type + data */
+
+ D (g_print ("MIDI: time code\n"));
+ break;
+
+ case 0xf2: /* song position */
+ midi->swallow = TRUE; /* lsb + msb */
+
+ D (g_print ("MIDI: song position\n"));
+ break;
+
+ case 0xf3: /* song select */
+ midi->swallow = TRUE; /* song number */
+
+ D (g_print ("MIDI: song select\n"));
+ break;
+
+ case 0xf4: /* (undefined) */
+ case 0xf5: /* (undefined) */
+ D (g_print ("MIDI: undefined system message\n"));
+ break;
+
+ case 0xf6: /* tune request */
+ D (g_print ("MIDI: tune request\n"));
+ break;
+
+ case 0xf7: /* sysex end */
+ D (g_print ("MIDI: sysex end\n"));
+ break;
+ }
+ }
+ else /* channel messages */
+ {
+ midi->command = buf[pos] >> 4;
+ midi->channel = buf[pos] & 0xf;
+
+ /* reset running status */
+ midi->key = -1;
+ midi->velocity = -1;
+ midi->msb = -1;
+ midi->lsb = -1;
+ }
+ }
+
+ pos++; /* status byte consumed */
+ continue;
+ }
+
+ if (midi->swallow)
+ {
+ pos++; /* consume any data byte */
+ continue;
+ }
+
+ switch (midi->command)
+ {
+ case 0x8: /* note off */
+ case 0x9: /* note on */
+ case 0xa: /* aftertouch */
+
+ if (midi->key == -1)
+ {
+ midi->key = buf[pos++]; /* key byte consumed */
+ continue;
+ }
+
+ if (midi->velocity == -1)
+ midi->velocity = buf[pos++]; /* velocity byte consumed */
+
+ /* note on with velocity = 0 means note off */
+ if (midi->command == 0x9 && midi->velocity == 0x0)
+ midi->command = 0x8;
+
+ if (midi->command == 0x9)
+ {
+ D (g_print ("MIDI (ch %02d): note on (%02x vel %02x)\n",
+ midi->channel,
+ midi->key, midi->velocity));
+
+ midi_event (midi, midi->channel, midi->key,
+ (gdouble) midi->velocity / 127.0);
+ }
+ else if (midi->command == 0x8)
+ {
+ D (g_print ("MIDI (ch %02d): note off (%02x vel %02x)\n",
+ midi->channel, midi->key, midi->velocity));
+
+ midi_event (midi, midi->channel, midi->key + 128,
+ (gdouble) midi->velocity / 127.0);
+ }
+ else
+ {
+ D (g_print ("MIDI (ch %02d): polyphonic aftertouch (%02x pressure %02x)\n",
+ midi->channel, midi->key, midi->velocity));
+ }
+
+ midi->key = -1;
+ midi->velocity = -1;
+ break;
+
+ case 0xb: /* controllers, sustain */
+
+ if (midi->key == -1)
+ {
+ midi->key = buf[pos++];
+ continue;
+ }
+
+ if (midi->velocity == -1)
+ midi->velocity = buf[pos++];
+
+ D (g_print ("MIDI (ch %02d): controller %d (value %d)\n",
+ midi->channel, midi->key, midi->velocity));
+
+ midi_event (midi, midi->channel, midi->key + 128 + 128,
+ (gdouble) midi->velocity / 127.0);
+
+ midi->key = -1;
+ midi->velocity = -1;
+ break;
+
+ case 0xc: /* program change */
+ midi->key = buf[pos++];
+
+ D (g_print ("MIDI (ch %02d): program change (%d)\n",
+ midi->channel, midi->key));
+
+ midi->key = -1;
+ break;
+
+ case 0xd: /* channel key pressure */
+ midi->velocity = buf[pos++];
+
+ D (g_print ("MIDI (ch %02d): channel aftertouch (%d)\n",
+ midi->channel, midi->velocity));
+
+ midi->velocity = -1;
+ break;
+
+ case 0xe: /* pitch bend */
+ if (midi->lsb == -1)
+ {
+ midi->lsb = buf[pos++];
+ continue;
+ }
+
+ if (midi->msb == -1)
+ midi->msb = buf[pos++];
+
+ midi->velocity = midi->lsb | (midi->msb << 7);
+
+ D (g_print ("MIDI (ch %02d): pitch (%d)\n",
+ midi->channel, midi->velocity));
+
+ midi->msb = -1;
+ midi->lsb = -1;
+ midi->velocity = -1;
+ break;
+ }
+ }
+
+ return TRUE;
+}
+
+#ifdef HAVE_ALSA
+static gboolean
+midi_alsa_prepare (GSource *source,
+ gint *timeout)
+{
+ ControllerMidi *midi = CONTROLLER_MIDI (((GAlsaSource *) source)->controller);
+ gboolean ready;
+
+ ready = snd_seq_event_input_pending (midi->sequencer, 1) > 0;
+ *timeout = ready ? 1 : 10;
+
+ return ready;
+}
+
+static gboolean
+midi_alsa_check (GSource *source)
+{
+ ControllerMidi *midi = CONTROLLER_MIDI (((GAlsaSource *) source)->controller);
+
+ return snd_seq_event_input_pending (midi->sequencer, 1) > 0;
+}
+
+static gboolean
+midi_alsa_dispatch (GSource *source,
+ GSourceFunc callback,
+ gpointer user_data)
+{
+ ControllerMidi *midi = CONTROLLER_MIDI (((GAlsaSource *) source)->controller);
+
+ snd_seq_event_t *event;
+ snd_seq_client_info_t *client_info;
+ snd_seq_port_info_t *port_info;
+
+ if (snd_seq_event_input_pending (midi->sequencer, 1) > 0)
+ {
+ snd_seq_event_input (midi->sequencer, &event);
+
+ if (event->type == SND_SEQ_EVENT_NOTEON &&
+ event->data.note.velocity == 0)
+ event->type = SND_SEQ_EVENT_NOTEOFF;
+
+ switch (event->type)
+ {
+ case SND_SEQ_EVENT_NOTEON:
+ midi_event (midi, event->data.note.channel,
+ event->data.note.note,
+ (gdouble) event->data.note.velocity / 127.0);
+ break;
+
+ case SND_SEQ_EVENT_NOTEOFF:
+ midi_event (midi, event->data.note.channel,
+ event->data.note.note + 128,
+ (gdouble) event->data.note.velocity / 127.0);
+ break;
+
+ case SND_SEQ_EVENT_CONTROLLER:
+ midi_event (midi, event->data.control.channel,
+ event->data.control.param + 256,
+ (gdouble) event->data.control.value / 127.0);
+ break;
+
+ case SND_SEQ_EVENT_PORT_SUBSCRIBED:
+ snd_seq_client_info_alloca (&client_info);
+ snd_seq_port_info_alloca (&port_info);
+ snd_seq_get_any_client_info (midi->sequencer,
+ event->data.connect.sender.client,
+ client_info);
+ snd_seq_get_any_port_info (midi->sequencer,
+ event->data.connect.sender.client,
+ event->data.connect.sender.port,
+ port_info);
+ /*
+ * g_printerr ("subscribed to \"%s:%s\"\n",
+ * snd_seq_client_info_get_name (client_info),
+ * snd_seq_port_info_get_name (port_info));
+ */
+ break;
+
+ case SND_SEQ_EVENT_PORT_UNSUBSCRIBED:
+ /* g_printerr ("unsubscribed\n");
+ */
+ break;
+
+ default:
+ break;
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+#endif /* HAVE_ALSA */
diff --git a/modules/display-filter-clip-warning.c b/modules/display-filter-clip-warning.c
new file mode 100644
index 0000000..89d290d
--- /dev/null
+++ b/modules/display-filter-clip-warning.c
@@ -0,0 +1,494 @@
+/* GIMP - The GNU Image Manipulation Program
+ *
+ * Copyright (C) 2017 Ell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpconfig/gimpconfig.h"
+#include "libgimpmath/gimpmath.h"
+#include "libgimpmodule/gimpmodule.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+#define DEFAULT_SHADOWS_COLOR (&(GimpRGB) {0.25, 0.25, 1.00, 1.00})
+#define DEFAULT_HIGHLIGHTS_COLOR (&(GimpRGB) {1.00, 0.25, 0.25, 1.00})
+#define DEFAULT_BOGUS_COLOR (&(GimpRGB) {1.00, 1.00, 0.25, 1.00})
+
+
+#define CDISPLAY_TYPE_CLIP_WARNING (cdisplay_clip_warning_get_type ())
+#define CDISPLAY_CLIP_WARNING(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CDISPLAY_TYPE_CLIP_WARNING, CdisplayClipWarning))
+#define CDISPLAY_CLIP_WARNING_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CDISPLAY_TYPE_CLIP_WARNING, CdisplayClipWarningClass))
+#define CDISPLAY_IS_CLIP_WARNING(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CDISPLAY_TYPE_CLIP_WARNING))
+#define CDISPLAY_IS_CLIP_WARNING_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CDISPLAY_TYPE_CLIP_WARNING))
+
+
+typedef enum
+{
+ WARNING_SHADOW = 1 << 0,
+ WARNING_HIGHLIGHT = 1 << 1,
+ WARNING_BOGUS = 1 << 2
+} Warning;
+
+
+typedef struct _CdisplayClipWarning CdisplayClipWarning;
+typedef struct _CdisplayClipWarningClass CdisplayClipWarningClass;
+
+struct _CdisplayClipWarning
+{
+ GimpColorDisplay parent_instance;
+
+ gboolean show_shadows;
+ GimpRGB shadows_color;
+ gboolean show_highlights;
+ GimpRGB highlights_color;
+ gboolean show_bogus;
+ GimpRGB bogus_color;
+ gboolean include_alpha;
+ gboolean include_transparent;
+
+ gfloat colors[8][2][4];
+};
+
+struct _CdisplayClipWarningClass
+{
+ GimpColorDisplayClass parent_instance;
+};
+
+
+enum
+{
+ PROP_0,
+ PROP_SHOW_SHADOWS,
+ PROP_SHADOWS_COLOR,
+ PROP_SHOW_HIGHLIGHTS,
+ PROP_HIGHLIGHTS_COLOR,
+ PROP_SHOW_BOGUS,
+ PROP_BOGUS_COLOR,
+ PROP_INCLUDE_ALPHA,
+ PROP_INCLUDE_TRANSPARENT
+};
+
+
+static GType cdisplay_clip_warning_get_type (void);
+
+static void cdisplay_clip_warning_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void cdisplay_clip_warning_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void cdisplay_clip_warning_convert_buffer (GimpColorDisplay *display,
+ GeglBuffer *buffer,
+ GeglRectangle *area);
+
+static void cdisplay_clip_warning_set_member (CdisplayClipWarning *clip_warning,
+ const gchar *property_name,
+ gpointer member,
+ gconstpointer value,
+ gsize size);
+static void cdisplay_clip_warning_update_colors (CdisplayClipWarning *clip_warning);
+
+
+static const GimpModuleInfo cdisplay_clip_warning_info =
+{
+ GIMP_MODULE_ABI_VERSION,
+ N_("Clip warning color display filter"),
+ "Ell",
+ "v1.0",
+ "(c) 2017, released under the GPL",
+ "2017"
+};
+
+
+G_DEFINE_DYNAMIC_TYPE (CdisplayClipWarning, cdisplay_clip_warning,
+ GIMP_TYPE_COLOR_DISPLAY)
+
+
+G_MODULE_EXPORT const GimpModuleInfo *
+gimp_module_query (GTypeModule *module)
+{
+ return &cdisplay_clip_warning_info;
+}
+
+G_MODULE_EXPORT gboolean
+gimp_module_register (GTypeModule *module)
+{
+ cdisplay_clip_warning_register_type (module);
+
+ return TRUE;
+}
+
+static void
+cdisplay_clip_warning_class_init (CdisplayClipWarningClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpColorDisplayClass *display_class = GIMP_COLOR_DISPLAY_CLASS (klass);
+
+ object_class->get_property = cdisplay_clip_warning_get_property;
+ object_class->set_property = cdisplay_clip_warning_set_property;
+
+ GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_SHADOWS,
+ "show-shadows",
+ _("Show shadows"),
+ _("Show warning for pixels with a negative component"),
+ TRUE,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_RGB (object_class, PROP_SHADOWS_COLOR,
+ "shadows-color",
+ _("Shadows color"),
+ _("Shadows warning color"),
+ FALSE,
+ DEFAULT_SHADOWS_COLOR,
+ GIMP_PARAM_STATIC_STRINGS |
+ GIMP_CONFIG_PARAM_DEFAULTS);
+
+ gegl_param_spec_set_property_key (
+ g_object_class_find_property (G_OBJECT_CLASS (klass), "shadows-color"),
+ "sensitive", "show-shadows");
+
+ GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_HIGHLIGHTS,
+ "show-highlights",
+ _("Show highlights"),
+ _("Show warning for pixels with a component greater than one"),
+ TRUE,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_RGB (object_class, PROP_HIGHLIGHTS_COLOR,
+ "highlights-color",
+ _("Highlights color"),
+ _("Highlights warning color"),
+ FALSE,
+ DEFAULT_HIGHLIGHTS_COLOR,
+ GIMP_PARAM_STATIC_STRINGS |
+ GIMP_CONFIG_PARAM_DEFAULTS);
+
+ gegl_param_spec_set_property_key (
+ g_object_class_find_property (G_OBJECT_CLASS (klass), "highlights-color"),
+ "sensitive", "show-highlights");
+
+ GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_BOGUS,
+ "show-bogus",
+ _("Show bogus"),
+ _("Show warning for pixels with an infinite or NaN component"),
+ TRUE,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_RGB (object_class, PROP_BOGUS_COLOR,
+ "bogus-color",
+ _("Bogus color"),
+ _("Bogus warning color"),
+ FALSE,
+ DEFAULT_BOGUS_COLOR,
+ GIMP_PARAM_STATIC_STRINGS |
+ GIMP_CONFIG_PARAM_DEFAULTS);
+
+ gegl_param_spec_set_property_key (
+ g_object_class_find_property (G_OBJECT_CLASS (klass), "bogus-color"),
+ "sensitive", "show-bogus");
+
+ GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_INCLUDE_ALPHA,
+ "include-alpha",
+ _("Include alpha component"),
+ _("Include alpha component in the warning"),
+ TRUE,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_INCLUDE_TRANSPARENT,
+ "include-transparent",
+ _("Include transparent pixels"),
+ _("Include fully transparent pixels in the warning"),
+ TRUE,
+ GIMP_PARAM_STATIC_STRINGS);
+
+ display_class->name = _("Clip Warning");
+ display_class->help_id = "gimp-colordisplay-clip-warning";
+ display_class->icon_name = GIMP_ICON_DISPLAY_FILTER_CLIP_WARNING;
+
+ display_class->convert_buffer = cdisplay_clip_warning_convert_buffer;
+}
+
+static void
+cdisplay_clip_warning_class_finalize (CdisplayClipWarningClass *klass)
+{
+}
+
+static void
+cdisplay_clip_warning_init (CdisplayClipWarning *clip_warning)
+{
+}
+
+static void
+cdisplay_clip_warning_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ CdisplayClipWarning *clip_warning = CDISPLAY_CLIP_WARNING (object);
+
+ switch (property_id)
+ {
+ case PROP_SHOW_SHADOWS:
+ g_value_set_boolean (value, clip_warning->show_shadows);
+ break;
+ case PROP_SHADOWS_COLOR:
+ g_value_set_boxed (value, &clip_warning->shadows_color);
+ break;
+
+ case PROP_SHOW_HIGHLIGHTS:
+ g_value_set_boolean (value, clip_warning->show_highlights);
+ break;
+ case PROP_HIGHLIGHTS_COLOR:
+ g_value_set_boxed (value, &clip_warning->highlights_color);
+ break;
+
+ case PROP_SHOW_BOGUS:
+ g_value_set_boolean (value, clip_warning->show_bogus);
+ break;
+ case PROP_BOGUS_COLOR:
+ g_value_set_boxed (value, &clip_warning->bogus_color);
+ break;
+
+ case PROP_INCLUDE_ALPHA:
+ g_value_set_boolean (value, clip_warning->include_alpha);
+ break;
+ case PROP_INCLUDE_TRANSPARENT:
+ g_value_set_boolean (value, clip_warning->include_transparent);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+cdisplay_clip_warning_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ CdisplayClipWarning *clip_warning = CDISPLAY_CLIP_WARNING (object);
+
+#define SET_MEMBER_PTR(member, value) \
+ cdisplay_clip_warning_set_member (clip_warning, \
+ pspec->name, \
+ &clip_warning->member, \
+ (value), \
+ sizeof (clip_warning->member))
+#define SET_MEMBER_VAL(member, type, value) \
+ SET_MEMBER_PTR (member, &(type) {value})
+
+ switch (property_id)
+ {
+ case PROP_SHOW_SHADOWS:
+ SET_MEMBER_VAL (show_shadows, gboolean, g_value_get_boolean (value));
+ break;
+ case PROP_SHADOWS_COLOR:
+ SET_MEMBER_PTR (shadows_color, g_value_get_boxed (value));
+ break;
+
+ case PROP_SHOW_HIGHLIGHTS:
+ SET_MEMBER_VAL (show_highlights, gboolean, g_value_get_boolean (value));
+ break;
+ case PROP_HIGHLIGHTS_COLOR:
+ SET_MEMBER_PTR (highlights_color, g_value_get_boxed (value));
+ break;
+
+ case PROP_SHOW_BOGUS:
+ SET_MEMBER_VAL (show_bogus, gboolean, g_value_get_boolean (value));
+ break;
+ case PROP_BOGUS_COLOR:
+ SET_MEMBER_PTR (bogus_color, g_value_get_boxed (value));
+ break;
+
+ case PROP_INCLUDE_ALPHA:
+ SET_MEMBER_VAL (include_alpha, gboolean, g_value_get_boolean (value));
+ break;
+ case PROP_INCLUDE_TRANSPARENT:
+ SET_MEMBER_VAL (include_transparent, gboolean, g_value_get_boolean (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+
+#undef SET_MEMBER_PTR
+#undef SET_MEMBER_VAL
+}
+
+static void
+cdisplay_clip_warning_convert_buffer (GimpColorDisplay *display,
+ GeglBuffer *buffer,
+ GeglRectangle *area)
+{
+ CdisplayClipWarning *clip_warning = CDISPLAY_CLIP_WARNING (display);
+ GeglBufferIterator *iter;
+
+ iter = gegl_buffer_iterator_new (buffer, area, 0,
+ babl_format ("R'G'B'A float"),
+ GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE, 1);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ gfloat *data = iter->items[0].data;
+ gint count = iter->length;
+ gint x = iter->items[0].roi.x;
+ gint y = iter->items[0].roi.y;
+
+ while (count--)
+ {
+ gint warning = 0;
+
+ if (clip_warning->include_transparent ||
+ ! (data[3] <= 0.0f) /* include nan */)
+ {
+ if (clip_warning->show_bogus &&
+ (! isfinite (data[0]) || ! isfinite (data[1]) || ! isfinite (data[2]) ||
+ (clip_warning->include_alpha && ! isfinite (data[3]))))
+ {
+ /* don't combine warning color of pixels with a bogus
+ * component with other warnings
+ */
+ warning = WARNING_BOGUS;
+ }
+ else
+ {
+ if (clip_warning->show_shadows &&
+ (data[0] < 0.0f || data[1] < 0.0f || data[2] < 0.0f ||
+ (clip_warning->include_alpha && data[3] < 0.0f)))
+ {
+ warning |= WARNING_SHADOW;
+ }
+
+ if (clip_warning->show_highlights &&
+ (data[0] > 1.0f || data[1] > 1.0f || data[2] > 1.0f ||
+ (clip_warning->include_alpha && data[3] > 1.0f)))
+ {
+ warning |= WARNING_HIGHLIGHT;
+ }
+ }
+ }
+
+ if (warning)
+ {
+ gboolean alt = ((x + y) >> 3) & 1;
+
+ memcpy (data, clip_warning->colors[warning][alt],
+ 4 * sizeof (gfloat));
+ }
+
+ data += 4;
+
+ if (++x == iter->items[0].roi.x + iter->items[0].roi.width)
+ {
+ x = iter->items[0].roi.x;
+ y++;
+ }
+ }
+ }
+}
+
+static void
+cdisplay_clip_warning_set_member (CdisplayClipWarning *clip_warning,
+ const gchar *property_name,
+ gpointer member,
+ gconstpointer value,
+ gsize size)
+{
+ if (memcmp (member, value, size))
+ {
+ memcpy (member, value, size);
+
+ cdisplay_clip_warning_update_colors (clip_warning);
+
+ g_object_notify (G_OBJECT (clip_warning), property_name);
+ gimp_color_display_changed (GIMP_COLOR_DISPLAY (clip_warning));
+ }
+}
+
+static void
+cdisplay_clip_warning_update_colors (CdisplayClipWarning *clip_warning)
+{
+ gint i;
+ gint j;
+
+ for (i = 0; i < 8; i++)
+ {
+ gfloat *color = clip_warning->colors[i][0];
+ gfloat *alt_color = clip_warning->colors[i][1];
+ gfloat alt_value;
+ gint n = 0;
+
+ memset (color, 0, 3 * sizeof (gfloat));
+
+ if (i & WARNING_SHADOW)
+ {
+ color[0] += clip_warning->shadows_color.r;
+ color[1] += clip_warning->shadows_color.g;
+ color[2] += clip_warning->shadows_color.b;
+
+ n++;
+ }
+
+ if (i & WARNING_HIGHLIGHT)
+ {
+ color[0] += clip_warning->highlights_color.r;
+ color[1] += clip_warning->highlights_color.g;
+ color[2] += clip_warning->highlights_color.b;
+
+ n++;
+ }
+
+ if (i & WARNING_BOGUS)
+ {
+ color[0] += clip_warning->bogus_color.r;
+ color[1] += clip_warning->bogus_color.g;
+ color[2] += clip_warning->bogus_color.b;
+
+ n++;
+ }
+
+ if (n)
+ {
+ for (j = 0; j < 3; j++)
+ color[j] /= n;
+ }
+ color[3] = 1.0;
+
+ if (MAX (color[0], MAX (color[1], color[2])) <= 0.5)
+ alt_value = 1.0;
+ else
+ alt_value = 0.0;
+
+ for (j = 0; j < 3; j++)
+ alt_color[j] = 0.75 * color[j] + 0.25 * alt_value;
+ alt_color[3] = 1.0;
+ }
+}
diff --git a/modules/display-filter-color-blind.c b/modules/display-filter-color-blind.c
new file mode 100644
index 0000000..2d5785c
--- /dev/null
+++ b/modules/display-filter-color-blind.c
@@ -0,0 +1,495 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * cdisplay_colorblind.c
+ * Copyright (C) 2002-2003 Michael Natterer <mitch@gimp.org>,
+ * Sven Neumann <sven@gimp.org>,
+ * Robert Dougherty <bob@vischeck.com> and
+ * Alex Wade <alex@vischeck.com>
+ *
+ * This code is an implementation of an algorithm described by Hans Brettel,
+ * Francoise Vienot and John Mollon in the Journal of the Optical Society of
+ * America V14(10), pg 2647. (See http://vischeck.com/ for more info.)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpconfig/gimpconfig.h"
+#include "libgimpmath/gimpmath.h"
+#include "libgimpmodule/gimpmodule.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+typedef enum
+{
+ COLORBLIND_DEFICIENCY_PROTANOPIA,
+ COLORBLIND_DEFICIENCY_DEUTERANOPIA,
+ COLORBLIND_DEFICIENCY_TRITANOPIA
+} ColorblindDeficiencyType;
+
+#define CDISPLAY_TYPE_COLORBLIND_DEFICIENCY_TYPE (cdisplay_colorblind_deficiency_type_type)
+static GType cdisplay_colorblind_deficiency_type_register_type (GTypeModule *module);
+
+static const GEnumValue enum_values[] =
+{
+ { COLORBLIND_DEFICIENCY_PROTANOPIA,
+ "COLORBLIND_DEFICIENCY_PROTANOPIA", "protanopia" },
+ { COLORBLIND_DEFICIENCY_DEUTERANOPIA,
+ "COLORBLIND_DEFICIENCY_DEUTERANOPIA", "deuteranopia" },
+ { COLORBLIND_DEFICIENCY_TRITANOPIA,
+ "COLORBLIND_DEFICIENCY_TRITANOPIA", "tritanopia" },
+ { 0, NULL, NULL }
+};
+
+static const GimpEnumDesc enum_descs[] =
+ {
+ { COLORBLIND_DEFICIENCY_PROTANOPIA,
+ N_("Protanopia (insensitivity to red)"), NULL },
+ { COLORBLIND_DEFICIENCY_DEUTERANOPIA,
+ N_("Deuteranopia (insensitivity to green)"), NULL },
+ { COLORBLIND_DEFICIENCY_TRITANOPIA,
+ N_("Tritanopia (insensitivity to blue)"), NULL },
+ { 0, NULL, NULL }
+ };
+
+#define DEFAULT_DEFICIENCY_TYPE COLORBLIND_DEFICIENCY_DEUTERANOPIA
+#define COLOR_CACHE_SIZE 1021
+
+
+#define CDISPLAY_TYPE_COLORBLIND (cdisplay_colorblind_get_type ())
+#define CDISPLAY_COLORBLIND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CDISPLAY_TYPE_COLORBLIND, CdisplayColorblind))
+#define CDISPLAY_COLORBLIND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CDISPLAY_TYPE_COLORBLIND, CdisplayColorblindClass))
+#define CDISPLAY_IS_COLORBLIND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CDISPLAY_TYPE_COLORBLIND))
+#define CDISPLAY_IS_COLORBLIND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CDISPLAY_TYPE_COLORBLIND))
+
+
+typedef struct _CdisplayColorblind CdisplayColorblind;
+typedef struct _CdisplayColorblindClass CdisplayColorblindClass;
+
+struct _CdisplayColorblind
+{
+ GimpColorDisplay parent_instance;
+
+ ColorblindDeficiencyType type;
+
+ gfloat a1, b1, c1;
+ gfloat a2, b2, c2;
+ gfloat inflection;
+};
+
+struct _CdisplayColorblindClass
+{
+ GimpColorDisplayClass parent_instance;
+};
+
+
+enum
+{
+ PROP_0,
+ PROP_TYPE
+};
+
+
+static GType cdisplay_colorblind_get_type (void);
+
+static void cdisplay_colorblind_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void cdisplay_colorblind_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void cdisplay_colorblind_convert_buffer (GimpColorDisplay *display,
+ GeglBuffer *buffer,
+ GeglRectangle *area);
+static void cdisplay_colorblind_changed (GimpColorDisplay *display);
+
+static void cdisplay_colorblind_set_type (CdisplayColorblind *colorblind,
+ ColorblindDeficiencyType value);
+
+
+/* The RGB<->LMS transforms above are computed from the human cone
+ * photo-pigment absorption spectra and the monitor phosphor
+ * emission spectra. These parameters are fairly constant for most
+ * humans and most monitors (at least for modern CRTs). However,
+ * gamma will vary quite a bit, as it is a property of the monitor
+ * (eg. amplifier gain), the video card, and even the
+ * software. Further, users can adjust their gammas (either via
+ * adjusting the monitor amp gains or in software). That said, the
+ * following are the gamma estimates that we have used in the
+ * Vischeck code. Many colorblind users have viewed our simulations
+ * and told us that they "work" (simulated and original images are
+ * indistinguishable).
+ */
+
+#if 0
+/* Gamma conversion is now handled by simply asking for a linear buffer */
+static const gfloat gammaRGB = 2.1;
+#endif
+
+/* For most modern Cathode-Ray Tube monitors (CRTs), the following
+* are good estimates of the RGB->LMS and LMS->RGB transform
+* matrices. They are based on spectra measured on a typical CRT
+* with a PhotoResearch PR650 spectral photometer and the Stockman
+* human cone fundamentals. NOTE: these estimates will NOT work well
+* for LCDs!
+*/
+static const gfloat rgb2lms[9] =
+{
+ 0.05059983,
+ 0.08585369,
+ 0.00952420,
+
+ 0.01893033,
+ 0.08925308,
+ 0.01370054,
+
+ 0.00292202,
+ 0.00975732,
+ 0.07145979
+};
+
+static const gfloat lms2rgb[9] =
+{
+ 30.830854,
+ -29.832659,
+ 1.610474,
+
+ -6.481468,
+ 17.715578,
+ -2.532642,
+
+ -0.375690,
+ -1.199062,
+ 14.273846
+};
+
+
+static const GimpModuleInfo cdisplay_colorblind_info =
+{
+ GIMP_MODULE_ABI_VERSION,
+ N_("Color deficit simulation filter (Brettel-Vienot-Mollon algorithm)"),
+ "Michael Natterer <mitch@gimp.org>, Bob Dougherty <bob@vischeck.com>, "
+ "Alex Wade <alex@vischeck.com>",
+ "v0.2",
+ "(c) 2002-2004, released under the GPL",
+ "January 22, 2003"
+};
+
+
+G_DEFINE_DYNAMIC_TYPE (CdisplayColorblind, cdisplay_colorblind,
+ GIMP_TYPE_COLOR_DISPLAY)
+
+static GType cdisplay_colorblind_deficiency_type_type = 0;
+
+
+G_MODULE_EXPORT const GimpModuleInfo *
+gimp_module_query (GTypeModule *module)
+{
+ return &cdisplay_colorblind_info;
+}
+
+G_MODULE_EXPORT gboolean
+gimp_module_register (GTypeModule *module)
+{
+ cdisplay_colorblind_register_type (module);
+ cdisplay_colorblind_deficiency_type_register_type (module);
+
+ return TRUE;
+}
+
+static GType
+cdisplay_colorblind_deficiency_type_register_type (GTypeModule *module)
+{
+ if (! cdisplay_colorblind_deficiency_type_type)
+ {
+ cdisplay_colorblind_deficiency_type_type =
+ g_type_module_register_enum (module, "CDisplayColorblindDeficiencyType",
+ enum_values);
+
+ gimp_type_set_translation_domain (cdisplay_colorblind_deficiency_type_type,
+ GETTEXT_PACKAGE "-libgimp");
+ gimp_enum_set_value_descriptions (cdisplay_colorblind_deficiency_type_type,
+ enum_descs);
+ }
+
+ return cdisplay_colorblind_deficiency_type_type;
+}
+
+static void
+cdisplay_colorblind_class_init (CdisplayColorblindClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpColorDisplayClass *display_class = GIMP_COLOR_DISPLAY_CLASS (klass);
+
+ object_class->get_property = cdisplay_colorblind_get_property;
+ object_class->set_property = cdisplay_colorblind_set_property;
+
+ GIMP_CONFIG_PROP_ENUM (object_class, PROP_TYPE,
+ "type",
+ _("Type"),
+ _("Color vision deficiency type"),
+ CDISPLAY_TYPE_COLORBLIND_DEFICIENCY_TYPE,
+ DEFAULT_DEFICIENCY_TYPE,
+ 0);
+
+ display_class->name = _("Color Deficient Vision");
+ display_class->help_id = "gimp-colordisplay-colorblind";
+ display_class->icon_name = GIMP_ICON_DISPLAY_FILTER_COLORBLIND;
+
+ display_class->convert_buffer = cdisplay_colorblind_convert_buffer;
+ display_class->changed = cdisplay_colorblind_changed;
+}
+
+static void
+cdisplay_colorblind_class_finalize (CdisplayColorblindClass *klass)
+{
+}
+
+static void
+cdisplay_colorblind_init (CdisplayColorblind *colorblind)
+{
+}
+
+static void
+cdisplay_colorblind_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ CdisplayColorblind *colorblind = CDISPLAY_COLORBLIND (object);
+
+ switch (property_id)
+ {
+ case PROP_TYPE:
+ g_value_set_enum (value, colorblind->type);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+cdisplay_colorblind_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ CdisplayColorblind *colorblind = CDISPLAY_COLORBLIND (object);
+
+ switch (property_id)
+ {
+ case PROP_TYPE:
+ cdisplay_colorblind_set_type (colorblind, g_value_get_enum (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+cdisplay_colorblind_convert_buffer (GimpColorDisplay *display,
+ GeglBuffer *buffer,
+ GeglRectangle *area)
+{
+ CdisplayColorblind *colorblind = CDISPLAY_COLORBLIND (display);
+ GeglBufferIterator *iter;
+ const gfloat a1 = colorblind->a1;
+ const gfloat b1 = colorblind->b1;
+ const gfloat c1 = colorblind->c1;
+ const gfloat a2 = colorblind->a2;
+ const gfloat b2 = colorblind->b2;
+ const gfloat c2 = colorblind->c2;
+
+ iter = gegl_buffer_iterator_new (buffer, area, 0,
+ babl_format ("RGBA float") /* linear! */,
+ GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE, 1);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ gfloat *data = iter->items[0].data;
+ gint count = iter->length;
+
+ while (count--)
+ {
+ gfloat tmp;
+ gfloat red, green, blue;
+ gfloat redOld, greenOld;
+
+ red = data[0];
+ green = data[1];
+ blue = data[2];
+
+ /* Convert to LMS (dot product with transform matrix) */
+ redOld = red;
+ greenOld = green;
+
+ red = redOld * rgb2lms[0] + greenOld * rgb2lms[1] + blue * rgb2lms[2];
+ green = redOld * rgb2lms[3] + greenOld * rgb2lms[4] + blue * rgb2lms[5];
+ blue = redOld * rgb2lms[6] + greenOld * rgb2lms[7] + blue * rgb2lms[8];
+
+ switch (colorblind->type)
+ {
+ case COLORBLIND_DEFICIENCY_DEUTERANOPIA:
+ tmp = blue / red;
+ /* See which side of the inflection line we fall... */
+ if (tmp < colorblind->inflection)
+ green = -(a1 * red + c1 * blue) / b1;
+ else
+ green = -(a2 * red + c2 * blue) / b2;
+ break;
+
+ case COLORBLIND_DEFICIENCY_PROTANOPIA:
+ tmp = blue / green;
+ /* See which side of the inflection line we fall... */
+ if (tmp < colorblind->inflection)
+ red = -(b1 * green + c1 * blue) / a1;
+ else
+ red = -(b2 * green + c2 * blue) / a2;
+ break;
+
+ case COLORBLIND_DEFICIENCY_TRITANOPIA:
+ tmp = green / red;
+ /* See which side of the inflection line we fall... */
+ if (tmp < colorblind->inflection)
+ blue = -(a1 * red + b1 * green) / c1;
+ else
+ blue = -(a2 * red + b2 * green) / c2;
+ break;
+
+ default:
+ break;
+ }
+
+ /* Convert back to RGB (cross product with transform matrix) */
+ redOld = red;
+ greenOld = green;
+
+ red = redOld * lms2rgb[0] + greenOld * lms2rgb[1] + blue * lms2rgb[2];
+ green = redOld * lms2rgb[3] + greenOld * lms2rgb[4] + blue * lms2rgb[5];
+ blue = redOld * lms2rgb[6] + greenOld * lms2rgb[7] + blue * lms2rgb[8];
+
+ data[0] = red;
+ data[1] = green;
+ data[2] = blue;
+
+ data += 4;
+ }
+ }
+}
+
+static void
+cdisplay_colorblind_changed (GimpColorDisplay *display)
+{
+ CdisplayColorblind *colorblind = CDISPLAY_COLORBLIND (display);
+ gfloat anchor_e[3];
+ gfloat anchor[12];
+
+ /* This function performs initialisations that are dependent
+ * on the type of color deficiency.
+ */
+
+ /* Performs protan, deutan or tritan color image simulation based on
+ * Brettel, Vienot and Mollon JOSA 14/10 1997
+ * L,M,S for lambda=475,485,575,660
+ *
+ * Load the LMS anchor-point values for lambda = 475 & 485 nm (for
+ * protans & deutans) and the LMS values for lambda = 575 & 660 nm
+ * (for tritans)
+ */
+ anchor[0] = 0.08008; anchor[1] = 0.1579; anchor[2] = 0.5897;
+ anchor[3] = 0.1284; anchor[4] = 0.2237; anchor[5] = 0.3636;
+ anchor[6] = 0.9856; anchor[7] = 0.7325; anchor[8] = 0.001079;
+ anchor[9] = 0.0914; anchor[10] = 0.007009; anchor[11] = 0.0;
+
+ /* We also need LMS for RGB=(1,1,1)- the equal-energy point (one of
+ * our anchors) (we can just peel this out of the rgb2lms transform
+ * matrix)
+ */
+ anchor_e[0] = rgb2lms[0] + rgb2lms[1] + rgb2lms[2];
+ anchor_e[1] = rgb2lms[3] + rgb2lms[4] + rgb2lms[5];
+ anchor_e[2] = rgb2lms[6] + rgb2lms[7] + rgb2lms[8];
+
+ switch (colorblind->type)
+ {
+ case COLORBLIND_DEFICIENCY_DEUTERANOPIA:
+ /* find a,b,c for lam=575nm and lam=475 */
+ colorblind->a1 = anchor_e[1] * anchor[8] - anchor_e[2] * anchor[7];
+ colorblind->b1 = anchor_e[2] * anchor[6] - anchor_e[0] * anchor[8];
+ colorblind->c1 = anchor_e[0] * anchor[7] - anchor_e[1] * anchor[6];
+ colorblind->a2 = anchor_e[1] * anchor[2] - anchor_e[2] * anchor[1];
+ colorblind->b2 = anchor_e[2] * anchor[0] - anchor_e[0] * anchor[2];
+ colorblind->c2 = anchor_e[0] * anchor[1] - anchor_e[1] * anchor[0];
+ colorblind->inflection = (anchor_e[2] / anchor_e[0]);
+ break;
+
+ case COLORBLIND_DEFICIENCY_PROTANOPIA:
+ /* find a,b,c for lam=575nm and lam=475 */
+ colorblind->a1 = anchor_e[1] * anchor[8] - anchor_e[2] * anchor[7];
+ colorblind->b1 = anchor_e[2] * anchor[6] - anchor_e[0] * anchor[8];
+ colorblind->c1 = anchor_e[0] * anchor[7] - anchor_e[1] * anchor[6];
+ colorblind->a2 = anchor_e[1] * anchor[2] - anchor_e[2] * anchor[1];
+ colorblind->b2 = anchor_e[2] * anchor[0] - anchor_e[0] * anchor[2];
+ colorblind->c2 = anchor_e[0] * anchor[1] - anchor_e[1] * anchor[0];
+ colorblind->inflection = (anchor_e[2] / anchor_e[1]);
+ break;
+
+ case COLORBLIND_DEFICIENCY_TRITANOPIA:
+ /* Set 1: regions where lambda_a=575, set 2: lambda_a=475 */
+ colorblind->a1 = anchor_e[1] * anchor[11] - anchor_e[2] * anchor[10];
+ colorblind->b1 = anchor_e[2] * anchor[9] - anchor_e[0] * anchor[11];
+ colorblind->c1 = anchor_e[0] * anchor[10] - anchor_e[1] * anchor[9];
+ colorblind->a2 = anchor_e[1] * anchor[5] - anchor_e[2] * anchor[4];
+ colorblind->b2 = anchor_e[2] * anchor[3] - anchor_e[0] * anchor[5];
+ colorblind->c2 = anchor_e[0] * anchor[4] - anchor_e[1] * anchor[3];
+ colorblind->inflection = (anchor_e[1] / anchor_e[0]);
+ break;
+ }
+}
+
+static void
+cdisplay_colorblind_set_type (CdisplayColorblind *colorblind,
+ ColorblindDeficiencyType value)
+{
+ if (value != colorblind->type)
+ {
+ GEnumClass *enum_class;
+
+ enum_class = g_type_class_peek (CDISPLAY_TYPE_COLORBLIND_DEFICIENCY_TYPE);
+
+ if (! g_enum_get_value (enum_class, value))
+ return;
+
+ colorblind->type = value;
+
+ g_object_notify (G_OBJECT (colorblind), "type");
+ gimp_color_display_changed (GIMP_COLOR_DISPLAY (colorblind));
+ }
+}
diff --git a/modules/display-filter-gamma.c b/modules/display-filter-gamma.c
new file mode 100644
index 0000000..c579742
--- /dev/null
+++ b/modules/display-filter-gamma.c
@@ -0,0 +1,228 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1999 Manish Singh <yosh@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpconfig/gimpconfig.h"
+#include "libgimpmath/gimpmath.h"
+#include "libgimpmodule/gimpmodule.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+#define DEFAULT_GAMMA 1.0
+
+
+#define CDISPLAY_TYPE_GAMMA (cdisplay_gamma_get_type ())
+#define CDISPLAY_GAMMA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CDISPLAY_TYPE_GAMMA, CdisplayGamma))
+#define CDISPLAY_GAMMA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CDISPLAY_TYPE_GAMMA, CdisplayGammaClass))
+#define CDISPLAY_IS_GAMMA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CDISPLAY_TYPE_GAMMA))
+#define CDISPLAY_IS_GAMMA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CDISPLAY_TYPE_GAMMA))
+
+
+typedef struct _CdisplayGamma CdisplayGamma;
+typedef struct _CdisplayGammaClass CdisplayGammaClass;
+
+struct _CdisplayGamma
+{
+ GimpColorDisplay parent_instance;
+
+ gdouble gamma;
+};
+
+struct _CdisplayGammaClass
+{
+ GimpColorDisplayClass parent_instance;
+};
+
+
+enum
+{
+ PROP_0,
+ PROP_GAMMA
+};
+
+
+static GType cdisplay_gamma_get_type (void);
+
+static void cdisplay_gamma_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void cdisplay_gamma_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void cdisplay_gamma_convert_buffer (GimpColorDisplay *display,
+ GeglBuffer *buffer,
+ GeglRectangle *area);
+static void cdisplay_gamma_set_gamma (CdisplayGamma *gamma,
+ gdouble value);
+
+
+static const GimpModuleInfo cdisplay_gamma_info =
+{
+ GIMP_MODULE_ABI_VERSION,
+ N_("Gamma color display filter"),
+ "Manish Singh <yosh@gimp.org>",
+ "v0.2",
+ "(c) 1999, released under the GPL",
+ "October 14, 2000"
+};
+
+
+G_DEFINE_DYNAMIC_TYPE (CdisplayGamma, cdisplay_gamma,
+ GIMP_TYPE_COLOR_DISPLAY)
+
+
+G_MODULE_EXPORT const GimpModuleInfo *
+gimp_module_query (GTypeModule *module)
+{
+ return &cdisplay_gamma_info;
+}
+
+G_MODULE_EXPORT gboolean
+gimp_module_register (GTypeModule *module)
+{
+ cdisplay_gamma_register_type (module);
+
+ return TRUE;
+}
+
+static void
+cdisplay_gamma_class_init (CdisplayGammaClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpColorDisplayClass *display_class = GIMP_COLOR_DISPLAY_CLASS (klass);
+
+ object_class->get_property = cdisplay_gamma_get_property;
+ object_class->set_property = cdisplay_gamma_set_property;
+
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_GAMMA,
+ "gamma",
+ _("Gamma"),
+ NULL,
+ 0.01, 10.0, DEFAULT_GAMMA,
+ 0);
+
+ display_class->name = _("Gamma");
+ display_class->help_id = "gimp-colordisplay-gamma";
+ display_class->icon_name = GIMP_ICON_DISPLAY_FILTER_GAMMA;
+
+ display_class->convert_buffer = cdisplay_gamma_convert_buffer;
+}
+
+static void
+cdisplay_gamma_class_finalize (CdisplayGammaClass *klass)
+{
+}
+
+static void
+cdisplay_gamma_init (CdisplayGamma *gamma)
+{
+}
+
+static void
+cdisplay_gamma_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ CdisplayGamma *gamma = CDISPLAY_GAMMA (object);
+
+ switch (property_id)
+ {
+ case PROP_GAMMA:
+ g_value_set_double (value, gamma->gamma);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+cdisplay_gamma_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ CdisplayGamma *gamma = CDISPLAY_GAMMA (object);
+
+ switch (property_id)
+ {
+ case PROP_GAMMA:
+ cdisplay_gamma_set_gamma (gamma, g_value_get_double (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+cdisplay_gamma_convert_buffer (GimpColorDisplay *display,
+ GeglBuffer *buffer,
+ GeglRectangle *area)
+{
+ CdisplayGamma *gamma = CDISPLAY_GAMMA (display);
+ GeglBufferIterator *iter;
+ gdouble one_over_gamma;
+
+ one_over_gamma = 1.0 / gamma->gamma;
+
+ iter = gegl_buffer_iterator_new (buffer, area, 0,
+ babl_format ("R'G'B'A float"),
+ GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE, 1);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ gfloat *data = iter->items[0].data;
+ gint count = iter->length;
+
+ while (count--)
+ {
+ *data = pow (*data, one_over_gamma); data++;
+ *data = pow (*data, one_over_gamma); data++;
+ *data = pow (*data, one_over_gamma); data++;
+
+ data++;
+ }
+ }
+}
+
+static void
+cdisplay_gamma_set_gamma (CdisplayGamma *gamma,
+ gdouble value)
+{
+ if (value <= 0.0)
+ value = 1.0;
+
+ if (value != gamma->gamma)
+ {
+ gamma->gamma = value;
+
+ g_object_notify (G_OBJECT (gamma), "gamma");
+ gimp_color_display_changed (GIMP_COLOR_DISPLAY (gamma));
+ }
+}
diff --git a/modules/display-filter-high-contrast.c b/modules/display-filter-high-contrast.c
new file mode 100644
index 0000000..7cf1ac0
--- /dev/null
+++ b/modules/display-filter-high-contrast.c
@@ -0,0 +1,228 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1999 Manish Singh <yosh@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpconfig/gimpconfig.h"
+#include "libgimpmath/gimpmath.h"
+#include "libgimpmodule/gimpmodule.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+#define DEFAULT_CONTRAST 1.0
+
+
+#define CDISPLAY_TYPE_CONTRAST (cdisplay_contrast_get_type ())
+#define CDISPLAY_CONTRAST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CDISPLAY_TYPE_CONTRAST, CdisplayContrast))
+#define CDISPLAY_CONTRAST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CDISPLAY_TYPE_CONTRAST, CdisplayContrastClass))
+#define CDISPLAY_IS_CONTRAST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CDISPLAY_TYPE_CONTRAST))
+#define CDISPLAY_IS_CONTRAST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CDISPLAY_TYPE_CONTRAST))
+
+
+typedef struct _CdisplayContrast CdisplayContrast;
+typedef struct _CdisplayContrastClass CdisplayContrastClass;
+
+struct _CdisplayContrast
+{
+ GimpColorDisplay parent_instance;
+
+ gdouble contrast;
+};
+
+struct _CdisplayContrastClass
+{
+ GimpColorDisplayClass parent_instance;
+};
+
+
+enum
+{
+ PROP_0,
+ PROP_CONTRAST
+};
+
+
+static GType cdisplay_contrast_get_type (void);
+
+static void cdisplay_contrast_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void cdisplay_contrast_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void cdisplay_contrast_convert_buffer (GimpColorDisplay *display,
+ GeglBuffer *buffer,
+ GeglRectangle *area);
+static void cdisplay_contrast_set_contrast (CdisplayContrast *contrast,
+ gdouble value);
+
+
+static const GimpModuleInfo cdisplay_contrast_info =
+{
+ GIMP_MODULE_ABI_VERSION,
+ N_("High Contrast color display filter"),
+ "Jay Cox <jaycox@gimp.org>",
+ "v0.2",
+ "(c) 2000, released under the GPL",
+ "October 14, 2000"
+};
+
+
+G_DEFINE_DYNAMIC_TYPE (CdisplayContrast, cdisplay_contrast,
+ GIMP_TYPE_COLOR_DISPLAY)
+
+
+G_MODULE_EXPORT const GimpModuleInfo *
+gimp_module_query (GTypeModule *module)
+{
+ return &cdisplay_contrast_info;
+}
+
+G_MODULE_EXPORT gboolean
+gimp_module_register (GTypeModule *module)
+{
+ cdisplay_contrast_register_type (module);
+
+ return TRUE;
+}
+
+static void
+cdisplay_contrast_class_init (CdisplayContrastClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpColorDisplayClass *display_class = GIMP_COLOR_DISPLAY_CLASS (klass);
+
+ object_class->get_property = cdisplay_contrast_get_property;
+ object_class->set_property = cdisplay_contrast_set_property;
+
+ GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_CONTRAST,
+ "contrast",
+ _("Contrast cycles"),
+ NULL,
+ 0.01, 10.0, DEFAULT_CONTRAST,
+ 0);
+
+ display_class->name = _("Contrast");
+ display_class->help_id = "gimp-colordisplay-contrast";
+ display_class->icon_name = GIMP_ICON_DISPLAY_FILTER_CONTRAST;
+
+ display_class->convert_buffer = cdisplay_contrast_convert_buffer;
+}
+
+static void
+cdisplay_contrast_class_finalize (CdisplayContrastClass *klass)
+{
+}
+
+static void
+cdisplay_contrast_init (CdisplayContrast *contrast)
+{
+}
+
+static void
+cdisplay_contrast_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ CdisplayContrast *contrast = CDISPLAY_CONTRAST (object);
+
+ switch (property_id)
+ {
+ case PROP_CONTRAST:
+ g_value_set_double (value, contrast->contrast);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+cdisplay_contrast_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ CdisplayContrast *contrast = CDISPLAY_CONTRAST (object);
+
+ switch (property_id)
+ {
+ case PROP_CONTRAST:
+ cdisplay_contrast_set_contrast (contrast, g_value_get_double (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+cdisplay_contrast_convert_buffer (GimpColorDisplay *display,
+ GeglBuffer *buffer,
+ GeglRectangle *area)
+{
+ CdisplayContrast *contrast = CDISPLAY_CONTRAST (display);
+ GeglBufferIterator *iter;
+ gfloat c;
+
+ c = contrast->contrast * 2 * G_PI;
+
+ iter = gegl_buffer_iterator_new (buffer, area, 0,
+ babl_format ("R'G'B'A float"),
+ GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE, 1);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ gfloat *data = iter->items[0].data;
+ gint count = iter->length;
+
+ while (count--)
+ {
+ *data = 0.5 * (1.0 + sin (c * *data)); data++;
+ *data = 0.5 * (1.0 + sin (c * *data)); data++;
+ *data = 0.5 * (1.0 + sin (c * *data)); data++;
+
+ data++;
+ }
+ }
+}
+
+static void
+cdisplay_contrast_set_contrast (CdisplayContrast *contrast,
+ gdouble value)
+{
+ if (value <= 0.0)
+ value = 1.0;
+
+ if (value != contrast->contrast)
+ {
+ contrast->contrast = value;
+
+ g_object_notify (G_OBJECT (contrast), "contrast");
+ gimp_color_display_changed (GIMP_COLOR_DISPLAY (contrast));
+ }
+}
diff --git a/modules/gimpcolorwheel.c b/modules/gimpcolorwheel.c
new file mode 100644
index 0000000..293d051
--- /dev/null
+++ b/modules/gimpcolorwheel.c
@@ -0,0 +1,1623 @@
+/* HSV color selector for GTK+
+ *
+ * Copyright (C) 1999 The Free Software Foundation
+ *
+ * Authors: Simon Budig <Simon.Budig@unix-ag.org> (original code)
+ * Federico Mena-Quintero <federico@gimp.org> (cleanup for GTK+)
+ * Jonathan Blandford <jrb@redhat.com> (cleanup for GTK+)
+ * Michael Natterer <mitch@gimp.org> (ported back to GIMP)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GTK+ Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+#include <libgimpconfig/gimpconfig.h>
+#include <libgimpcolor/gimpcolor.h>
+#include <libgimpmath/gimpmath.h>
+#include <libgimpwidgets/gimpwidgets.h>
+
+#include "gimpcolorwheel.h"
+
+
+/* Default ring fraction */
+#define DEFAULT_FRACTION 0.1
+
+/* Default width/height */
+#define DEFAULT_SIZE 100
+
+/* Default ring width */
+#define DEFAULT_RING_WIDTH 10
+
+
+/* Dragging modes */
+typedef enum
+{
+ DRAG_NONE,
+ DRAG_H,
+ DRAG_SV
+} DragMode;
+
+/* Private part of the GimpColorWheel structure */
+typedef struct
+{
+ /* Color value */
+ gdouble h;
+ gdouble s;
+ gdouble v;
+
+ /* ring_width is this fraction of size */
+ gdouble ring_fraction;
+
+ /* Size and ring width */
+ gint size;
+ gint ring_width;
+
+ /* Window for capturing events */
+ GdkWindow *window;
+
+ /* Dragging mode */
+ DragMode mode;
+
+ guint focus_on_ring : 1;
+
+ GimpColorConfig *config;
+ GimpColorTransform *transform;
+} GimpColorWheelPrivate;
+
+enum
+{
+ CHANGED,
+ MOVE,
+ LAST_SIGNAL
+};
+
+static void gimp_color_wheel_dispose (GObject *object);
+
+static void gimp_color_wheel_map (GtkWidget *widget);
+static void gimp_color_wheel_unmap (GtkWidget *widget);
+static void gimp_color_wheel_realize (GtkWidget *widget);
+static void gimp_color_wheel_unrealize (GtkWidget *widget);
+static void gimp_color_wheel_size_request (GtkWidget *widget,
+ GtkRequisition *requisition);
+static void gimp_color_wheel_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
+static gboolean gimp_color_wheel_button_press (GtkWidget *widget,
+ GdkEventButton *event);
+static gboolean gimp_color_wheel_button_release (GtkWidget *widget,
+ GdkEventButton *event);
+static gboolean gimp_color_wheel_motion (GtkWidget *widget,
+ GdkEventMotion *event);
+static gboolean gimp_color_wheel_expose (GtkWidget *widget,
+ GdkEventExpose *event);
+static gboolean gimp_color_wheel_grab_broken (GtkWidget *widget,
+ GdkEventGrabBroken *event);
+static gboolean gimp_color_wheel_focus (GtkWidget *widget,
+ GtkDirectionType direction);
+static void gimp_color_wheel_move (GimpColorWheel *wheel,
+ GtkDirectionType dir);
+
+static void gimp_color_wheel_create_transform (GimpColorWheel *wheel);
+static void gimp_color_wheel_destroy_transform (GimpColorWheel *wheel);
+
+
+static guint wheel_signals[LAST_SIGNAL];
+
+G_DEFINE_DYNAMIC_TYPE_EXTENDED (GimpColorWheel, gimp_color_wheel,
+ GTK_TYPE_WIDGET, 0,
+ G_ADD_PRIVATE_DYNAMIC (GimpColorWheel))
+
+#define parent_class gimp_color_wheel_parent_class
+
+
+void
+color_wheel_register_type (GTypeModule *module)
+{
+ gimp_color_wheel_register_type (module);
+}
+
+static void
+gimp_color_wheel_class_init (GimpColorWheelClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+ GimpColorWheelClass *wheel_class = GIMP_COLOR_WHEEL_CLASS (class);
+ GtkBindingSet *binding_set;
+
+ object_class->dispose = gimp_color_wheel_dispose;
+
+ widget_class->map = gimp_color_wheel_map;
+ widget_class->unmap = gimp_color_wheel_unmap;
+ widget_class->realize = gimp_color_wheel_realize;
+ widget_class->unrealize = gimp_color_wheel_unrealize;
+ widget_class->size_request = gimp_color_wheel_size_request;
+ widget_class->size_allocate = gimp_color_wheel_size_allocate;
+ widget_class->button_press_event = gimp_color_wheel_button_press;
+ widget_class->button_release_event = gimp_color_wheel_button_release;
+ widget_class->motion_notify_event = gimp_color_wheel_motion;
+ widget_class->expose_event = gimp_color_wheel_expose;
+ widget_class->focus = gimp_color_wheel_focus;
+ widget_class->grab_broken_event = gimp_color_wheel_grab_broken;
+
+ wheel_class->move = gimp_color_wheel_move;
+
+ wheel_signals[CHANGED] =
+ g_signal_new ("changed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpColorWheelClass, changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ wheel_signals[MOVE] =
+ g_signal_new ("move",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (GimpColorWheelClass, move),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__ENUM,
+ G_TYPE_NONE, 1,
+ GTK_TYPE_DIRECTION_TYPE);
+
+ binding_set = gtk_binding_set_by_class (class);
+
+ gtk_binding_entry_add_signal (binding_set, GDK_Up, 0,
+ "move", 1,
+ G_TYPE_ENUM, GTK_DIR_UP);
+ gtk_binding_entry_add_signal (binding_set, GDK_KP_Up, 0,
+ "move", 1,
+ G_TYPE_ENUM, GTK_DIR_UP);
+
+ gtk_binding_entry_add_signal (binding_set, GDK_Down, 0,
+ "move", 1,
+ G_TYPE_ENUM, GTK_DIR_DOWN);
+ gtk_binding_entry_add_signal (binding_set, GDK_KP_Down, 0,
+ "move", 1,
+ G_TYPE_ENUM, GTK_DIR_DOWN);
+
+
+ gtk_binding_entry_add_signal (binding_set, GDK_Right, 0,
+ "move", 1,
+ G_TYPE_ENUM, GTK_DIR_RIGHT);
+ gtk_binding_entry_add_signal (binding_set, GDK_KP_Right, 0,
+ "move", 1,
+ G_TYPE_ENUM, GTK_DIR_RIGHT);
+
+ gtk_binding_entry_add_signal (binding_set, GDK_Left, 0,
+ "move", 1,
+ G_TYPE_ENUM, GTK_DIR_LEFT);
+ gtk_binding_entry_add_signal (binding_set, GDK_KP_Left, 0,
+ "move", 1,
+ G_TYPE_ENUM, GTK_DIR_LEFT);
+}
+
+static void
+gimp_color_wheel_class_finalize (GimpColorWheelClass *klass)
+{
+}
+
+static void
+gimp_color_wheel_init (GimpColorWheel *wheel)
+{
+ GimpColorWheelPrivate *priv = gimp_color_wheel_get_instance_private (wheel);
+
+ wheel->priv = priv;
+
+ gtk_widget_set_has_window (GTK_WIDGET (wheel), FALSE);
+ gtk_widget_set_can_focus (GTK_WIDGET (wheel), TRUE);
+
+ priv->ring_fraction = DEFAULT_FRACTION;
+ priv->size = DEFAULT_SIZE;
+ priv->ring_width = DEFAULT_RING_WIDTH;
+
+ gimp_widget_track_monitor (GTK_WIDGET (wheel),
+ G_CALLBACK (gimp_color_wheel_destroy_transform),
+ NULL);
+}
+
+static void
+gimp_color_wheel_dispose (GObject *object)
+{
+ GimpColorWheel *wheel = GIMP_COLOR_WHEEL (object);
+
+ gimp_color_wheel_set_color_config (wheel, NULL);
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gimp_color_wheel_map (GtkWidget *widget)
+{
+ GimpColorWheel *wheel = GIMP_COLOR_WHEEL (widget);
+ GimpColorWheelPrivate *priv = wheel->priv;
+
+ GTK_WIDGET_CLASS (parent_class)->map (widget);
+
+ gdk_window_show (priv->window);
+}
+
+static void
+gimp_color_wheel_unmap (GtkWidget *widget)
+{
+ GimpColorWheel *wheel = GIMP_COLOR_WHEEL (widget);
+ GimpColorWheelPrivate *priv = wheel->priv;
+
+ gdk_window_hide (priv->window);
+
+ GTK_WIDGET_CLASS (parent_class)->unmap (widget);
+}
+
+static void
+gimp_color_wheel_realize (GtkWidget *widget)
+{
+ GimpColorWheel *wheel = GIMP_COLOR_WHEEL (widget);
+ GimpColorWheelPrivate *priv = wheel->priv;
+ GtkAllocation allocation;
+ GdkWindowAttr attr;
+ gint attr_mask;
+ GdkWindow *parent_window;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ gtk_widget_set_realized (widget, TRUE);
+
+ attr.window_type = GDK_WINDOW_CHILD;
+ attr.x = allocation.x;
+ attr.y = allocation.y;
+ attr.width = allocation.width;
+ attr.height = allocation.height;
+ attr.wclass = GDK_INPUT_ONLY;
+ attr.event_mask = (gtk_widget_get_events (widget) |
+ GDK_KEY_PRESS_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_POINTER_MOTION_MASK |
+ GDK_ENTER_NOTIFY_MASK |
+ GDK_LEAVE_NOTIFY_MASK);
+
+ attr_mask = GDK_WA_X | GDK_WA_Y;
+
+ parent_window = gtk_widget_get_parent_window (widget);
+
+ gtk_widget_set_window (widget, parent_window);
+ g_object_ref (parent_window);
+
+ priv->window = gdk_window_new (parent_window, &attr, attr_mask);
+ gdk_window_set_user_data (priv->window, wheel);
+
+ gtk_widget_style_attach (widget);
+}
+
+static void
+gimp_color_wheel_unrealize (GtkWidget *widget)
+{
+ GimpColorWheel *wheel = GIMP_COLOR_WHEEL (widget);
+ GimpColorWheelPrivate *priv = wheel->priv;
+
+ gdk_window_set_user_data (priv->window, NULL);
+ gdk_window_destroy (priv->window);
+ priv->window = NULL;
+
+ GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
+}
+
+static void
+gimp_color_wheel_size_request (GtkWidget *widget,
+ GtkRequisition *requisition)
+{
+ gint focus_width;
+ gint focus_pad;
+
+ gtk_widget_style_get (widget,
+ "focus-line-width", &focus_width,
+ "focus-padding", &focus_pad,
+ NULL);
+
+ requisition->width = DEFAULT_SIZE + 2 * (focus_width + focus_pad);
+ requisition->height = DEFAULT_SIZE + 2 * (focus_width + focus_pad);
+}
+
+static void
+gimp_color_wheel_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ GimpColorWheel *wheel = GIMP_COLOR_WHEEL (widget);
+ GimpColorWheelPrivate *priv = wheel->priv;
+ gint focus_width;
+ gint focus_pad;
+
+ GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
+
+ gtk_widget_style_get (widget,
+ "focus-line-width", &focus_width,
+ "focus-padding", &focus_pad,
+ NULL);
+
+ priv->size = MIN (allocation->width - 2 * (focus_width + focus_pad),
+ allocation->height - 2 * (focus_width + focus_pad));
+
+ priv->ring_width = priv->size * priv->ring_fraction;
+
+ if (gtk_widget_get_realized (widget))
+ gdk_window_move_resize (priv->window,
+ allocation->x,
+ allocation->y,
+ allocation->width,
+ allocation->height);
+}
+
+
+/* Utility functions */
+
+/* Converts from HSV to RGB */
+static void
+hsv_to_rgb (gdouble *h,
+ gdouble *s,
+ gdouble *v)
+{
+ gdouble hue, saturation, value;
+ gdouble f, p, q, t;
+
+ if (*s == 0.0)
+ {
+ *h = *v;
+ *s = *v;
+ /* *v = *v; -- heh */
+ }
+ else
+ {
+ hue = *h * 6.0;
+ saturation = *s;
+ value = *v;
+
+ if (hue == 6.0)
+ hue = 0.0;
+
+ f = hue - (int) hue;
+ p = value * (1.0 - saturation);
+ q = value * (1.0 - saturation * f);
+ t = value * (1.0 - saturation * (1.0 - f));
+
+ switch ((int) hue)
+ {
+ case 0:
+ *h = value;
+ *s = t;
+ *v = p;
+ break;
+
+ case 1:
+ *h = q;
+ *s = value;
+ *v = p;
+ break;
+
+ case 2:
+ *h = p;
+ *s = value;
+ *v = t;
+ break;
+
+ case 3:
+ *h = p;
+ *s = q;
+ *v = value;
+ break;
+
+ case 4:
+ *h = t;
+ *s = p;
+ *v = value;
+ break;
+
+ case 5:
+ *h = value;
+ *s = p;
+ *v = q;
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+ }
+}
+
+/* Computes the vertices of the saturation/value triangle */
+static void
+compute_triangle (GimpColorWheel *wheel,
+ gint *hx,
+ gint *hy,
+ gint *sx,
+ gint *sy,
+ gint *vx,
+ gint *vy)
+{
+ GimpColorWheelPrivate *priv = wheel->priv;
+ GtkAllocation allocation;
+ gdouble center_x;
+ gdouble center_y;
+ gdouble inner, outer;
+ gdouble angle;
+
+ gtk_widget_get_allocation (GTK_WIDGET (wheel), &allocation);
+
+ center_x = allocation.width / 2.0;
+ center_y = allocation.height / 2.0;
+
+ outer = priv->size / 2.0;
+ inner = outer - priv->ring_width;
+ angle = priv->h * 2.0 * G_PI;
+
+ *hx = floor (center_x + cos (angle) * inner + 0.5);
+ *hy = floor (center_y - sin (angle) * inner + 0.5);
+ *sx = floor (center_x + cos (angle + 2.0 * G_PI / 3.0) * inner + 0.5);
+ *sy = floor (center_y - sin (angle + 2.0 * G_PI / 3.0) * inner + 0.5);
+ *vx = floor (center_x + cos (angle + 4.0 * G_PI / 3.0) * inner + 0.5);
+ *vy = floor (center_y - sin (angle + 4.0 * G_PI / 3.0) * inner + 0.5);
+}
+
+/* Computes whether a point is inside the hue ring */
+static gboolean
+is_in_ring (GimpColorWheel *wheel,
+ gdouble x,
+ gdouble y)
+{
+ GimpColorWheelPrivate *priv = wheel->priv;
+ GtkAllocation allocation;
+ gdouble dx, dy, dist;
+ gdouble center_x;
+ gdouble center_y;
+ gdouble inner, outer;
+
+ gtk_widget_get_allocation (GTK_WIDGET (wheel), &allocation);
+
+ center_x = allocation.width / 2.0;
+ center_y = allocation.height / 2.0;
+
+ outer = priv->size / 2.0;
+ inner = outer - priv->ring_width;
+
+ dx = x - center_x;
+ dy = center_y - y;
+ dist = dx * dx + dy * dy;
+
+ return (dist >= inner * inner && dist <= outer * outer);
+}
+
+/* Computes a saturation/value pair based on the mouse coordinates */
+static void
+compute_sv (GimpColorWheel *wheel,
+ gdouble x,
+ gdouble y,
+ gdouble *s,
+ gdouble *v)
+{
+ GtkAllocation allocation;
+ gint ihx, ihy, isx, isy, ivx, ivy;
+ gdouble hx, hy, sx, sy, vx, vy;
+ gdouble center_x;
+ gdouble center_y;
+
+ gtk_widget_get_allocation (GTK_WIDGET (wheel), &allocation);
+
+ compute_triangle (wheel, &ihx, &ihy, &isx, &isy, &ivx, &ivy);
+
+ center_x = allocation.width / 2.0;
+ center_y = allocation.height / 2.0;
+
+ hx = ihx - center_x;
+ hy = center_y - ihy;
+ sx = isx - center_x;
+ sy = center_y - isy;
+ vx = ivx - center_x;
+ vy = center_y - ivy;
+ x -= center_x;
+ y = center_y - y;
+
+ if (vx * (x - sx) + vy * (y - sy) < 0.0)
+ {
+ *s = 1.0;
+ *v = (((x - sx) * (hx - sx) + (y - sy) * (hy-sy))
+ / ((hx - sx) * (hx - sx) + (hy - sy) * (hy - sy)));
+
+ if (*v < 0.0)
+ *v = 0.0;
+ else if (*v > 1.0)
+ *v = 1.0;
+ }
+ else if (hx * (x - sx) + hy * (y - sy) < 0.0)
+ {
+ *s = 0.0;
+ *v = (((x - sx) * (vx - sx) + (y - sy) * (vy - sy))
+ / ((vx - sx) * (vx - sx) + (vy - sy) * (vy - sy)));
+
+ if (*v < 0.0)
+ *v = 0.0;
+ else if (*v > 1.0)
+ *v = 1.0;
+ }
+ else if (sx * (x - hx) + sy * (y - hy) < 0.0)
+ {
+ *v = 1.0;
+ *s = (((x - vx) * (hx - vx) + (y - vy) * (hy - vy)) /
+ ((hx - vx) * (hx - vx) + (hy - vy) * (hy - vy)));
+
+ if (*s < 0.0)
+ *s = 0.0;
+ else if (*s > 1.0)
+ *s = 1.0;
+ }
+ else
+ {
+ *v = (((x - sx) * (hy - vy) - (y - sy) * (hx - vx))
+ / ((vx - sx) * (hy - vy) - (vy - sy) * (hx - vx)));
+
+ if (*v<= 0.0)
+ {
+ *v = 0.0;
+ *s = 0.0;
+ }
+ else
+ {
+ if (*v > 1.0)
+ *v = 1.0;
+
+ if (fabs (hy - vy) < fabs (hx - vx))
+ *s = (x - sx - *v * (vx - sx)) / (*v * (hx - vx));
+ else
+ *s = (y - sy - *v * (vy - sy)) / (*v * (hy - vy));
+
+ if (*s < 0.0)
+ *s = 0.0;
+ else if (*s > 1.0)
+ *s = 1.0;
+ }
+ }
+}
+
+/* Computes whether a point is inside the saturation/value triangle */
+static gboolean
+is_in_triangle (GimpColorWheel *wheel,
+ gdouble x,
+ gdouble y)
+{
+ gint hx, hy, sx, sy, vx, vy;
+ gdouble det, s, v;
+
+ compute_triangle (wheel, &hx, &hy, &sx, &sy, &vx, &vy);
+
+ det = (vx - sx) * (hy - sy) - (vy - sy) * (hx - sx);
+
+ s = ((x - sx) * (hy - sy) - (y - sy) * (hx - sx)) / det;
+ v = ((vx - sx) * (y - sy) - (vy - sy) * (x - sx)) / det;
+
+ return (s >= 0.0 && v >= 0.0 && s + v <= 1.0);
+}
+
+/* Computes a value based on the mouse coordinates */
+static double
+compute_v (GimpColorWheel *wheel,
+ gdouble x,
+ gdouble y)
+{
+ GtkAllocation allocation;
+ gdouble center_x;
+ gdouble center_y;
+ gdouble dx, dy;
+ gdouble angle;
+
+ gtk_widget_get_allocation (GTK_WIDGET (wheel), &allocation);
+
+ center_x = allocation.width / 2.0;
+ center_y = allocation.height / 2.0;
+
+ dx = x - center_x;
+ dy = center_y - y;
+
+ angle = atan2 (dy, dx);
+ if (angle < 0.0)
+ angle += 2.0 * G_PI;
+
+ return angle / (2.0 * G_PI);
+}
+
+static void
+set_cross_grab (GimpColorWheel *wheel,
+ guint32 time)
+{
+ GimpColorWheelPrivate *priv = wheel->priv;
+ GdkCursor *cursor;
+
+ cursor =
+ gdk_cursor_new_for_display (gtk_widget_get_display (GTK_WIDGET (wheel)),
+ GDK_CROSSHAIR);
+
+ gdk_pointer_grab (priv->window, FALSE,
+ GDK_POINTER_MOTION_MASK |
+ GDK_POINTER_MOTION_HINT_MASK |
+ GDK_BUTTON_RELEASE_MASK,
+ NULL, cursor, time);
+ gdk_cursor_unref (cursor);
+}
+
+static gboolean
+gimp_color_wheel_grab_broken (GtkWidget *widget,
+ GdkEventGrabBroken *event)
+{
+ GimpColorWheel *wheel = GIMP_COLOR_WHEEL (widget);
+ GimpColorWheelPrivate *priv = wheel->priv;
+
+ priv->mode = DRAG_NONE;
+
+ return TRUE;
+}
+
+static gboolean
+gimp_color_wheel_button_press (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ GimpColorWheel *wheel = GIMP_COLOR_WHEEL (widget);
+ GimpColorWheelPrivate *priv = wheel->priv;
+ gdouble x, y;
+
+ if (priv->mode != DRAG_NONE || event->button != 1)
+ return FALSE;
+
+ x = event->x;
+ y = event->y;
+
+ if (is_in_ring (wheel, x, y))
+ {
+ priv->mode = DRAG_H;
+ set_cross_grab (wheel, event->time);
+
+ gimp_color_wheel_set_color (wheel,
+ compute_v (wheel, x, y),
+ priv->s,
+ priv->v);
+
+ gtk_widget_grab_focus (widget);
+ priv->focus_on_ring = TRUE;
+
+ return TRUE;
+ }
+
+ if (is_in_triangle (wheel, x, y))
+ {
+ gdouble s, v;
+
+ priv->mode = DRAG_SV;
+ set_cross_grab (wheel, event->time);
+
+ compute_sv (wheel, x, y, &s, &v);
+ gimp_color_wheel_set_color (wheel, priv->h, s, v);
+
+ gtk_widget_grab_focus (widget);
+ priv->focus_on_ring = FALSE;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gimp_color_wheel_button_release (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ GimpColorWheel *wheel = GIMP_COLOR_WHEEL (widget);
+ GimpColorWheelPrivate *priv = wheel->priv;
+ DragMode mode;
+ gdouble x, y;
+
+ if (priv->mode == DRAG_NONE || event->button != 1)
+ return FALSE;
+
+ /* Set the drag mode to DRAG_NONE so that signal handlers for "catched"
+ * can see that this is the final color state.
+ */
+
+ mode = priv->mode;
+ priv->mode = DRAG_NONE;
+
+ x = event->x;
+ y = event->y;
+
+ if (mode == DRAG_H)
+ {
+ gimp_color_wheel_set_color (wheel,
+ compute_v (wheel, x, y), priv->s, priv->v);
+ }
+ else if (mode == DRAG_SV)
+ {
+ gdouble s, v;
+
+ compute_sv (wheel, x, y, &s, &v);
+ gimp_color_wheel_set_color (wheel, priv->h, s, v);
+ }
+ else
+ g_assert_not_reached ();
+
+ gdk_display_pointer_ungrab (gdk_window_get_display (event->window),
+ event->time);
+
+ return TRUE;
+}
+
+static gboolean
+gimp_color_wheel_motion (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ GimpColorWheel *wheel = GIMP_COLOR_WHEEL (widget);
+ GimpColorWheelPrivate *priv = wheel->priv;
+ gdouble x, y;
+
+ if (priv->mode == DRAG_NONE)
+ return FALSE;
+
+ gdk_event_request_motions (event);
+ x = event->x;
+ y = event->y;
+
+ if (priv->mode == DRAG_H)
+ {
+ gimp_color_wheel_set_color (wheel,
+ compute_v (wheel, x, y), priv->s, priv->v);
+ return TRUE;
+ }
+ else if (priv->mode == DRAG_SV)
+ {
+ gdouble s, v;
+
+ compute_sv (wheel, x, y, &s, &v);
+ gimp_color_wheel_set_color (wheel, priv->h, s, v);
+ return TRUE;
+ }
+
+ g_assert_not_reached ();
+
+ return FALSE;
+}
+
+
+/* Redrawing */
+
+/* Paints the hue ring */
+static void
+paint_ring (GimpColorWheel *wheel,
+ cairo_t *cr,
+ gint x,
+ gint y,
+ gint width,
+ gint height)
+{
+ GtkWidget *widget = GTK_WIDGET (wheel);
+ GimpColorWheelPrivate *priv = wheel->priv;
+ GtkAllocation allocation;
+ gint xx, yy;
+ gdouble dx, dy, dist;
+ gdouble center_x;
+ gdouble center_y;
+ gdouble inner, outer;
+ guint32 *buf, *p;
+ gdouble angle;
+ gdouble hue;
+ gdouble r, g, b;
+ cairo_surface_t *source;
+ cairo_t *source_cr;
+ gint stride;
+ gint focus_width;
+ gint focus_pad;
+
+ gtk_widget_get_allocation (GTK_WIDGET (wheel), &allocation);
+
+ gtk_widget_style_get (widget,
+ "focus-line-width", &focus_width,
+ "focus-padding", &focus_pad,
+ NULL);
+
+ center_x = allocation.width / 2.0;
+ center_y = allocation.height / 2.0;
+
+ outer = priv->size / 2.0;
+ inner = outer - priv->ring_width;
+
+ /* Create an image initialized with the ring colors */
+
+ stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, width);
+ buf = g_new (guint32, height * stride / 4);
+
+ for (yy = 0; yy < height; yy++)
+ {
+ p = buf + yy * width;
+
+ dy = -(yy + y - center_y);
+
+ for (xx = 0; xx < width; xx++)
+ {
+ dx = xx + x - center_x;
+
+ dist = dx * dx + dy * dy;
+ if (dist < ((inner-1) * (inner-1)) || dist > ((outer+1) * (outer+1)))
+ {
+ *p++ = 0;
+ continue;
+ }
+
+ angle = atan2 (dy, dx);
+ if (angle < 0.0)
+ angle += 2.0 * G_PI;
+
+ hue = angle / (2.0 * G_PI);
+
+ r = hue;
+ g = 1.0;
+ b = 1.0;
+ hsv_to_rgb (&r, &g, &b);
+
+ *p++ = (((int)floor (r * 255 + 0.5) << 16) |
+ ((int)floor (g * 255 + 0.5) << 8) |
+ (int)floor (b * 255 + 0.5));
+ }
+ }
+
+ if (priv->transform)
+ {
+ const Babl *format = babl_format ("cairo-RGB24");
+ guchar *b = (guchar *) buf;
+ gint i;
+
+ for (i = 0; i < height; i++)
+ {
+ gimp_color_transform_process_pixels (priv->transform,
+ format, b,
+ format, b,
+ width);
+
+ b += stride;
+ }
+ }
+
+ source = cairo_image_surface_create_for_data ((guchar *) buf,
+ CAIRO_FORMAT_RGB24,
+ width, height, stride);
+
+ /* Now draw the value marker onto the source image, so that it
+ * will get properly clipped at the edges of the ring
+ */
+ source_cr = cairo_create (source);
+
+ r = priv->h;
+ g = 1.0;
+ b = 1.0;
+ hsv_to_rgb (&r, &g, &b);
+
+ if (GIMP_RGB_LUMINANCE (r, g, b) > 0.5)
+ cairo_set_source_rgb (source_cr, 0., 0., 0.);
+ else
+ cairo_set_source_rgb (source_cr, 1., 1., 1.);
+
+ cairo_move_to (source_cr, -x + center_x, - y + center_y);
+ cairo_line_to (source_cr,
+ -x + center_x + cos (priv->h * 2.0 * G_PI) * priv->size / 2,
+ -y + center_y - sin (priv->h * 2.0 * G_PI) * priv->size / 2);
+ cairo_stroke (source_cr);
+ cairo_destroy (source_cr);
+
+ /* Draw the ring using the source image */
+
+ cairo_save (cr);
+
+ cairo_set_source_surface (cr, source, x, y);
+ cairo_surface_destroy (source);
+
+ cairo_set_line_width (cr, priv->ring_width);
+ cairo_new_path (cr);
+ cairo_arc (cr,
+ center_x, center_y,
+ priv->size / 2. - priv->ring_width / 2.,
+ 0, 2 * G_PI);
+ cairo_stroke (cr);
+
+ cairo_restore (cr);
+
+ g_free (buf);
+}
+
+/* Converts an HSV triplet to an integer RGB triplet */
+static void
+get_color (gdouble h,
+ gdouble s,
+ gdouble v,
+ gint *r,
+ gint *g,
+ gint *b)
+{
+ hsv_to_rgb (&h, &s, &v);
+
+ *r = floor (h * 255 + 0.5);
+ *g = floor (s * 255 + 0.5);
+ *b = floor (v * 255 + 0.5);
+}
+
+#define SWAP(a, b, t) ((t) = (a), (a) = (b), (b) = (t))
+
+#define LERP(a, b, v1, v2, i) (((v2) - (v1) != 0) \
+ ? ((a) + ((b) - (a)) * ((i) - (v1)) / ((v2) - (v1))) \
+ : (a))
+
+/* Number of pixels we extend out from the edges when creating
+ * color source to avoid artifacts
+ */
+#define PAD 3
+
+/* Paints the HSV triangle */
+static void
+paint_triangle (GimpColorWheel *wheel,
+ cairo_t *cr,
+ gint x,
+ gint y,
+ gint width,
+ gint height)
+{
+ GtkWidget *widget = GTK_WIDGET (wheel);
+ GimpColorWheelPrivate *priv = wheel->priv;
+ gint hx, hy, sx, sy, vx, vy; /* HSV vertices */
+ gint x1, y1, r1, g1, b1; /* First vertex in scanline order */
+ gint x2, y2, r2, g2, b2; /* Second vertex */
+ gint x3, y3, r3, g3, b3; /* Third vertex */
+ gint t;
+ guint32 *buf, *p, c;
+ gint xl, xr, rl, rr, gl, gr, bl, br; /* Scanline data */
+ gint xx, yy;
+ gint x_interp, y_interp;
+ gint x_start, x_end;
+ cairo_surface_t *source;
+ gdouble r, g, b;
+ gchar *detail;
+ gint stride;
+
+ /* Compute triangle's vertices */
+
+ compute_triangle (wheel, &hx, &hy, &sx, &sy, &vx, &vy);
+
+ x1 = hx;
+ y1 = hy;
+ get_color (priv->h, 1.0, 1.0, &r1, &g1, &b1);
+
+ x2 = sx;
+ y2 = sy;
+ get_color (priv->h, 1.0, 0.0, &r2, &g2, &b2);
+
+ x3 = vx;
+ y3 = vy;
+ get_color (priv->h, 0.0, 1.0, &r3, &g3, &b3);
+
+ if (y2 > y3)
+ {
+ SWAP (x2, x3, t);
+ SWAP (y2, y3, t);
+ SWAP (r2, r3, t);
+ SWAP (g2, g3, t);
+ SWAP (b2, b3, t);
+ }
+
+ if (y1 > y3)
+ {
+ SWAP (x1, x3, t);
+ SWAP (y1, y3, t);
+ SWAP (r1, r3, t);
+ SWAP (g1, g3, t);
+ SWAP (b1, b3, t);
+ }
+
+ if (y1 > y2)
+ {
+ SWAP (x1, x2, t);
+ SWAP (y1, y2, t);
+ SWAP (r1, r2, t);
+ SWAP (g1, g2, t);
+ SWAP (b1, b2, t);
+ }
+
+ /* Shade the triangle */
+
+ stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, width);
+ buf = g_new (guint32, height * stride / 4);
+
+ for (yy = 0; yy < height; yy++)
+ {
+ p = buf + yy * width;
+
+ if (yy + y >= y1 - PAD && yy + y < y3 + PAD)
+ {
+ y_interp = CLAMP (yy + y, y1, y3);
+
+ if (y_interp < y2)
+ {
+ xl = LERP (x1, x2, y1, y2, y_interp);
+
+ rl = LERP (r1, r2, y1, y2, y_interp);
+ gl = LERP (g1, g2, y1, y2, y_interp);
+ bl = LERP (b1, b2, y1, y2, y_interp);
+ }
+ else
+ {
+ xl = LERP (x2, x3, y2, y3, y_interp);
+
+ rl = LERP (r2, r3, y2, y3, y_interp);
+ gl = LERP (g2, g3, y2, y3, y_interp);
+ bl = LERP (b2, b3, y2, y3, y_interp);
+ }
+
+ xr = LERP (x1, x3, y1, y3, y_interp);
+
+ rr = LERP (r1, r3, y1, y3, y_interp);
+ gr = LERP (g1, g3, y1, y3, y_interp);
+ br = LERP (b1, b3, y1, y3, y_interp);
+
+ if (xl > xr)
+ {
+ SWAP (xl, xr, t);
+ SWAP (rl, rr, t);
+ SWAP (gl, gr, t);
+ SWAP (bl, br, t);
+ }
+
+ x_start = MAX (xl - PAD, x);
+ x_end = MIN (xr + PAD, x + width);
+ x_start = MIN (x_start, x_end);
+
+ c = (rl << 16) | (gl << 8) | bl;
+
+ for (xx = x; xx < x_start; xx++)
+ *p++ = c;
+
+ for (; xx < x_end; xx++)
+ {
+ x_interp = CLAMP (xx, xl, xr);
+
+ *p++ = ((LERP (rl, rr, xl, xr, x_interp) << 16) |
+ (LERP (gl, gr, xl, xr, x_interp) << 8) |
+ LERP (bl, br, xl, xr, x_interp));
+ }
+
+ c = (rr << 16) | (gr << 8) | br;
+
+ for (; xx < x + width; xx++)
+ *p++ = c;
+ }
+ }
+
+ if (priv->transform)
+ {
+ const Babl *format = babl_format ("cairo-RGB24");
+ guchar *b = (guchar *) buf;
+ gint i;
+
+ for (i = 0; i < height; i++)
+ {
+ gimp_color_transform_process_pixels (priv->transform,
+ format, b,
+ format, b,
+ width);
+
+ b += stride;
+ }
+ }
+
+ source = cairo_image_surface_create_for_data ((guchar *) buf,
+ CAIRO_FORMAT_RGB24,
+ width, height, stride);
+
+ /* Draw a triangle with the image as a source */
+
+ cairo_set_source_surface (cr, source, x, y);
+ cairo_surface_destroy (source);
+
+ cairo_move_to (cr, x1, y1);
+ cairo_line_to (cr, x2, y2);
+ cairo_line_to (cr, x3, y3);
+ cairo_close_path (cr);
+ cairo_fill (cr);
+
+ g_free (buf);
+
+ /* Draw value marker */
+
+ xx = floor (sx + (vx - sx) * priv->v + (hx - vx) * priv->s * priv->v + 0.5);
+ yy = floor (sy + (vy - sy) * priv->v + (hy - vy) * priv->s * priv->v + 0.5);
+
+ r = priv->h;
+ g = priv->s;
+ b = priv->v;
+ hsv_to_rgb (&r, &g, &b);
+
+ if (GIMP_RGB_LUMINANCE (r, g, b) > 0.5)
+ {
+ detail = "colorwheel_light";
+ cairo_set_source_rgb (cr, 0., 0., 0.);
+ }
+ else
+ {
+ detail = "colorwheel_dark";
+ cairo_set_source_rgb (cr, 1., 1., 1.);
+ }
+
+#define RADIUS 4
+#define FOCUS_RADIUS 6
+
+ cairo_new_path (cr);
+ cairo_arc (cr, xx, yy, RADIUS, 0, 2 * G_PI);
+ cairo_stroke (cr);
+
+ /* Draw focus outline */
+
+ if (gtk_widget_has_focus (widget) &&
+ ! priv->focus_on_ring)
+ {
+ GtkAllocation allocation;
+ gint focus_width;
+ gint focus_pad;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ gtk_widget_style_get (widget,
+ "focus-line-width", &focus_width,
+ "focus-padding", &focus_pad,
+ NULL);
+
+ gtk_paint_focus (gtk_widget_get_style (widget),
+ gtk_widget_get_window (widget),
+ gtk_widget_get_state (widget),
+ NULL, widget, detail,
+ allocation.x + xx - FOCUS_RADIUS - focus_width - focus_pad,
+ allocation.y + yy - FOCUS_RADIUS - focus_width - focus_pad,
+ 2 * (FOCUS_RADIUS + focus_width + focus_pad),
+ 2 * (FOCUS_RADIUS + focus_width + focus_pad));
+ }
+}
+
+/* Paints the contents of the HSV color selector */
+static void
+paint (GimpColorWheel *hsv,
+ cairo_t *cr,
+ gint x,
+ gint y,
+ gint width,
+ gint height)
+{
+ GimpColorWheelPrivate *priv = hsv->priv;
+
+ if (! priv->transform)
+ gimp_color_wheel_create_transform (hsv);
+
+ paint_ring (hsv, cr, x, y, width, height);
+ paint_triangle (hsv, cr, x, y, width, height);
+}
+
+static gint
+gimp_color_wheel_expose (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ GimpColorWheel *wheel = GIMP_COLOR_WHEEL (widget);
+ GimpColorWheelPrivate *priv = wheel->priv;
+ GtkAllocation allocation;
+ GdkRectangle dest;
+ cairo_t *cr;
+
+ if (! (event->window == gtk_widget_get_window (widget) &&
+ gtk_widget_is_drawable (widget)))
+ return FALSE;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ if (!gdk_rectangle_intersect (&event->area, &allocation, &dest))
+ return FALSE;
+
+ cr = gdk_cairo_create (gtk_widget_get_window (widget));
+
+ cairo_translate (cr, allocation.x, allocation.y);
+ paint (wheel, cr,
+ dest.x - allocation.x,
+ dest.y - allocation.y,
+ dest.width, dest.height);
+ cairo_destroy (cr);
+
+ if (gtk_widget_has_focus (widget) && priv->focus_on_ring)
+ gtk_paint_focus (gtk_widget_get_style (widget),
+ gtk_widget_get_window (widget),
+ gtk_widget_get_state (widget),
+ &event->area, widget, NULL,
+ allocation.x,
+ allocation.y,
+ allocation.width,
+ allocation.height);
+
+ return FALSE;
+}
+
+static gboolean
+gimp_color_wheel_focus (GtkWidget *widget,
+ GtkDirectionType dir)
+{
+ GimpColorWheel *wheel = GIMP_COLOR_WHEEL (widget);
+ GimpColorWheelPrivate *priv = wheel->priv;
+
+ if (!gtk_widget_has_focus (widget))
+ {
+ if (dir == GTK_DIR_TAB_BACKWARD)
+ priv->focus_on_ring = FALSE;
+ else
+ priv->focus_on_ring = TRUE;
+
+ gtk_widget_grab_focus (widget);
+ return TRUE;
+ }
+
+ switch (dir)
+ {
+ case GTK_DIR_UP:
+ if (priv->focus_on_ring)
+ return FALSE;
+ else
+ priv->focus_on_ring = TRUE;
+ break;
+
+ case GTK_DIR_DOWN:
+ if (priv->focus_on_ring)
+ priv->focus_on_ring = FALSE;
+ else
+ return FALSE;
+ break;
+
+ case GTK_DIR_LEFT:
+ case GTK_DIR_TAB_BACKWARD:
+ if (priv->focus_on_ring)
+ return FALSE;
+ else
+ priv->focus_on_ring = TRUE;
+ break;
+
+ case GTK_DIR_RIGHT:
+ case GTK_DIR_TAB_FORWARD:
+ if (priv->focus_on_ring)
+ priv->focus_on_ring = FALSE;
+ else
+ return FALSE;
+ break;
+ }
+
+ gtk_widget_queue_draw (widget);
+
+ return TRUE;
+}
+
+/**
+ * gimp_color_wheel_new:
+ *
+ * Creates a new HSV color selector.
+ *
+ * Return value: A newly-created HSV color selector.
+ *
+ * Since: 2.10
+ */
+GtkWidget*
+gimp_color_wheel_new (void)
+{
+ return g_object_new (GIMP_TYPE_COLOR_WHEEL, NULL);
+}
+
+/**
+ * gimp_color_wheel_set_color:
+ * @hsv: An HSV color selector
+ * @h: Hue
+ * @s: Saturation
+ * @v: Value
+ *
+ * Sets the current color in an HSV color selector.
+ * Color component values must be in the [0.0, 1.0] range.
+ *
+ * Since: 2.10
+ */
+void
+gimp_color_wheel_set_color (GimpColorWheel *wheel,
+ gdouble h,
+ gdouble s,
+ gdouble v)
+{
+ GimpColorWheelPrivate *priv;
+
+ g_return_if_fail (GIMP_IS_COLOR_WHEEL (wheel));
+ g_return_if_fail (h >= 0.0 && h <= 1.0);
+ g_return_if_fail (s >= 0.0 && s <= 1.0);
+ g_return_if_fail (v >= 0.0 && v <= 1.0);
+
+ priv = wheel->priv;
+
+ priv->h = h;
+ priv->s = s;
+ priv->v = v;
+
+ g_signal_emit (wheel, wheel_signals[CHANGED], 0);
+
+ gtk_widget_queue_draw (GTK_WIDGET (wheel));
+}
+
+/**
+ * gimp_color_wheel_get_color:
+ * @hsv: An HSV color selector
+ * @h: (out): Return value for the hue
+ * @s: (out): Return value for the saturation
+ * @v: (out): Return value for the value
+ *
+ * Queries the current color in an HSV color selector.
+ * Returned values will be in the [0.0, 1.0] range.
+ *
+ * Since: 2.10
+ */
+void
+gimp_color_wheel_get_color (GimpColorWheel *wheel,
+ gdouble *h,
+ gdouble *s,
+ gdouble *v)
+{
+ GimpColorWheelPrivate *priv;
+
+ g_return_if_fail (GIMP_IS_COLOR_WHEEL (wheel));
+
+ priv = wheel->priv;
+
+ if (h) *h = priv->h;
+ if (s) *s = priv->s;
+ if (v) *v = priv->v;
+}
+
+/**
+ * gimp_color_wheel_set_ring_fraction:
+ * @ring: A wheel color selector
+ * @fraction: Ring fraction
+ *
+ * Sets the ring fraction of a wheel color selector.
+ *
+ * Since: 2.10
+ */
+void
+gimp_color_wheel_set_ring_fraction (GimpColorWheel *hsv,
+ gdouble fraction)
+{
+ GimpColorWheelPrivate *priv;
+
+ g_return_if_fail (GIMP_IS_COLOR_WHEEL (hsv));
+
+ priv = hsv->priv;
+
+ priv->ring_fraction = CLAMP (fraction, 0.01, 0.99);
+
+ gtk_widget_queue_draw (GTK_WIDGET (hsv));
+}
+
+/**
+ * gimp_color_wheel_get_ring_fraction:
+ * @ring: A wheel color selector
+ *
+ * Returns value: The ring fraction of the wheel color selector.
+ *
+ * Since: 2.10
+ */
+gdouble
+gimp_color_wheel_get_ring_fraction (GimpColorWheel *wheel)
+{
+ GimpColorWheelPrivate *priv;
+
+ g_return_val_if_fail (GIMP_IS_COLOR_WHEEL (wheel), DEFAULT_FRACTION);
+
+ priv = wheel->priv;
+
+ return priv->ring_fraction;
+}
+
+/**
+ * gimp_color_wheel_set_color_config:
+ * @wheel: a #GimpColorWheel widget.
+ * @config: a #GimpColorConfig object.
+ *
+ * Sets the color management configuration to use with this color wheel.
+ *
+ * Since: 2.10
+ */
+void
+gimp_color_wheel_set_color_config (GimpColorWheel *wheel,
+ GimpColorConfig *config)
+{
+ GimpColorWheelPrivate *priv;
+
+ g_return_if_fail (GIMP_IS_COLOR_WHEEL (wheel));
+ g_return_if_fail (config == NULL || GIMP_IS_COLOR_CONFIG (config));
+
+ priv = wheel->priv;
+
+ if (config != priv->config)
+ {
+ if (priv->config)
+ {
+ g_signal_handlers_disconnect_by_func (priv->config,
+ gimp_color_wheel_destroy_transform,
+ wheel);
+
+ gimp_color_wheel_destroy_transform (wheel);
+ }
+
+ g_set_object (&priv->config, config);
+
+ if (priv->config)
+ {
+ g_signal_connect_swapped (priv->config, "notify",
+ G_CALLBACK (gimp_color_wheel_destroy_transform),
+ wheel);
+ }
+ }
+}
+
+/**
+ * gimp_color_wheel_is_adjusting:
+ * @hsv: A #GimpColorWheel
+ *
+ * An HSV color selector can be said to be adjusting if multiple rapid
+ * changes are being made to its value, for example, when the user is
+ * adjusting the value with the mouse. This function queries whether
+ * the HSV color selector is being adjusted or not.
+ *
+ * Return value: %TRUE if clients can ignore changes to the color value,
+ * since they may be transitory, or %FALSE if they should consider
+ * the color value status to be final.
+ *
+ * Since: 2.10
+ */
+gboolean
+gimp_color_wheel_is_adjusting (GimpColorWheel *wheel)
+{
+ GimpColorWheelPrivate *priv;
+
+ g_return_val_if_fail (GIMP_IS_COLOR_WHEEL (wheel), FALSE);
+
+ priv = wheel->priv;
+
+ return priv->mode != DRAG_NONE;
+}
+
+static void
+gimp_color_wheel_move (GimpColorWheel *wheel,
+ GtkDirectionType dir)
+{
+ GimpColorWheelPrivate *priv = wheel->priv;
+ gdouble hue, sat, val;
+ gint hx, hy, sx, sy, vx, vy; /* HSV vertices */
+ gint x, y; /* position in triangle */
+
+ hue = priv->h;
+ sat = priv->s;
+ val = priv->v;
+
+ compute_triangle (wheel, &hx, &hy, &sx, &sy, &vx, &vy);
+
+ x = floor (sx + (vx - sx) * priv->v + (hx - vx) * priv->s * priv->v + 0.5);
+ y = floor (sy + (vy - sy) * priv->v + (hy - vy) * priv->s * priv->v + 0.5);
+
+#define HUE_DELTA 0.002
+ switch (dir)
+ {
+ case GTK_DIR_UP:
+ if (priv->focus_on_ring)
+ hue += HUE_DELTA;
+ else
+ {
+ y -= 1;
+ compute_sv (wheel, x, y, &sat, &val);
+ }
+ break;
+
+ case GTK_DIR_DOWN:
+ if (priv->focus_on_ring)
+ hue -= HUE_DELTA;
+ else
+ {
+ y += 1;
+ compute_sv (wheel, x, y, &sat, &val);
+ }
+ break;
+
+ case GTK_DIR_LEFT:
+ if (priv->focus_on_ring)
+ hue += HUE_DELTA;
+ else
+ {
+ x -= 1;
+ compute_sv (wheel, x, y, &sat, &val);
+ }
+ break;
+
+ case GTK_DIR_RIGHT:
+ if (priv->focus_on_ring)
+ hue -= HUE_DELTA
+ ;
+ else
+ {
+ x += 1;
+ compute_sv (wheel, x, y, &sat, &val);
+ }
+ break;
+
+ default:
+ /* we don't care about the tab directions */
+ break;
+ }
+
+ /* Wrap */
+ if (hue < 0.0)
+ hue = 1.0;
+ else if (hue > 1.0)
+ hue = 0.0;
+
+ gimp_color_wheel_set_color (wheel, hue, sat, val);
+}
+
+static void
+gimp_color_wheel_create_transform (GimpColorWheel *wheel)
+{
+ GimpColorWheelPrivate *priv = wheel->priv;
+
+ if (priv->config)
+ {
+ static GimpColorProfile *profile = NULL;
+
+ const Babl *format = babl_format ("cairo-RGB24");
+
+ if (G_UNLIKELY (! profile))
+ profile = gimp_color_profile_new_rgb_srgb ();
+
+ priv->transform = gimp_widget_get_color_transform (GTK_WIDGET (wheel),
+ priv->config,
+ profile,
+ format,
+ format);
+ }
+}
+
+static void
+gimp_color_wheel_destroy_transform (GimpColorWheel *wheel)
+{
+ GimpColorWheelPrivate *priv = wheel->priv;
+
+ if (priv->transform)
+ {
+ g_object_unref (priv->transform);
+ priv->transform = NULL;
+ }
+
+ gtk_widget_queue_draw (GTK_WIDGET (wheel));
+}
diff --git a/modules/gimpcolorwheel.h b/modules/gimpcolorwheel.h
new file mode 100644
index 0000000..bc4bcc7
--- /dev/null
+++ b/modules/gimpcolorwheel.h
@@ -0,0 +1,98 @@
+/* HSV color selector for GTK+
+ *
+ * Copyright (C) 1999 The Free Software Foundation
+ *
+ * Authors: Simon Budig <Simon.Budig@unix-ag.org> (original code)
+ * Federico Mena-Quintero <federico@gimp.org> (cleanup for GTK+)
+ * Jonathan Blandford <jrb@redhat.com> (cleanup for GTK+)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GTK+ Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#ifndef __GIMP_COLOR_WHEEL_H__
+#define __GIMP_COLOR_WHEEL_H__
+
+G_BEGIN_DECLS
+
+#define GIMP_TYPE_COLOR_WHEEL (gimp_color_wheel_get_type ())
+#define GIMP_COLOR_WHEEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_COLOR_WHEEL, GimpColorWheel))
+#define GIMP_COLOR_WHEEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_COLOR_WHEEL, GimpColorWheelClass))
+#define GIMP_IS_COLOR_WHEEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_COLOR_WHEEL))
+#define GIMP_IS_COLOR_WHEEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_COLOR_WHEEL))
+#define GIMP_COLOR_WHEEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_COLOR_WHEEL, GimpColorWheelClass))
+
+
+typedef struct _GimpColorWheel GimpColorWheel;
+typedef struct _GimpColorWheelClass GimpColorWheelClass;
+
+struct _GimpColorWheel
+{
+ GtkWidget parent_instance;
+
+ /* Private data */
+ gpointer priv;
+};
+
+struct _GimpColorWheelClass
+{
+ GtkWidgetClass parent_class;
+
+ /* Notification signals */
+ void (* changed) (GimpColorWheel *wheel);
+
+ /* Keybindings */
+ void (* move) (GimpColorWheel *wheel,
+ GtkDirectionType type);
+
+ /* Padding for future expansion */
+ void (*_gimp_reserved1) (void);
+ void (*_gimp_reserved2) (void);
+ void (*_gimp_reserved3) (void);
+ void (*_gimp_reserved4) (void);
+};
+
+
+void color_wheel_register_type (GTypeModule *module);
+
+GType gimp_color_wheel_get_type (void) G_GNUC_CONST;
+GtkWidget * gimp_color_wheel_new (void);
+
+void gimp_color_wheel_set_color (GimpColorWheel *wheel,
+ double h,
+ double s,
+ double v);
+void gimp_color_wheel_get_color (GimpColorWheel *wheel,
+ gdouble *h,
+ gdouble *s,
+ gdouble *v);
+
+void gimp_color_wheel_set_ring_fraction (GimpColorWheel *wheel,
+ gdouble fraction);
+gdouble gimp_color_wheel_get_ring_fraction (GimpColorWheel *wheel);
+
+void gimp_color_wheel_set_color_config (GimpColorWheel *wheel,
+ GimpColorConfig *config);
+
+gboolean gimp_color_wheel_is_adjusting (GimpColorWheel *wheel);
+
+G_END_DECLS
+
+#endif /* __GIMP_COLOR_WHEEL_H__ */
diff --git a/modules/gimpinputdevicestore-dx.c b/modules/gimpinputdevicestore-dx.c
new file mode 100644
index 0000000..66ca6f2
--- /dev/null
+++ b/modules/gimpinputdevicestore-dx.c
@@ -0,0 +1,482 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpinputdevicestore-dx.c
+ * Input device store based on DirectX.
+ * Copyright (C) 2007 Sven Neumann <sven@gimp.org>
+ * Copyright (C) 2007 Tor Lillqvist <tml@novell.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#ifdef HAVE_DX_DINPUT
+#define _WIN32_WINNT 0x0501
+#include <windows.h>
+#define DIRECTINPUT_VERSION 0x0800
+#include <dinput.h>
+#include <rpc.h>
+
+#ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
+#define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 0x00000004
+#endif
+
+#include <gdk/gdkwin32.h>
+
+#include "libgimpmodule/gimpmodule.h"
+
+#include "gimpinputdevicestore.h"
+
+
+enum
+{
+ COLUMN_GUID,
+ COLUMN_LABEL,
+ COLUMN_IDEVICE,
+ NUM_COLUMNS
+};
+
+enum
+{
+ DEVICE_ADDED,
+ DEVICE_REMOVED,
+ LAST_SIGNAL
+};
+
+typedef struct _GimpInputDeviceStoreClass GimpInputDeviceStoreClass;
+
+struct _GimpInputDeviceStore
+{
+ GtkListStore parent_instance;
+
+ GdkWindow *window;
+
+ LPDIRECTINPUT8W directinput8;
+
+ GError *error;
+};
+
+
+struct _GimpInputDeviceStoreClass
+{
+ GtkListStoreClass parent_class;
+
+ void (* device_added) (GimpInputDeviceStore *store,
+ const gchar *udi);
+ void (* device_removed) (GimpInputDeviceStore *store,
+ const gchar *udi);
+};
+
+
+static void gimp_input_device_store_finalize (GObject *object);
+
+static gboolean gimp_input_device_store_add (GimpInputDeviceStore *store,
+ const GUID *guid);
+static gboolean gimp_input_device_store_remove (GimpInputDeviceStore *store,
+ const gchar *udi);
+
+
+G_DEFINE_DYNAMIC_TYPE (GimpInputDeviceStore, gimp_input_device_store,
+ GTK_TYPE_LIST_STORE)
+
+static guint store_signals[LAST_SIGNAL] = { 0 };
+
+
+void
+gimp_input_device_store_register_types (GTypeModule *module)
+{
+ gimp_input_device_store_register_type (module);
+}
+
+static void
+gimp_input_device_store_class_init (GimpInputDeviceStoreClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ store_signals[DEVICE_ADDED] =
+ g_signal_new ("device-added",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpInputDeviceStoreClass, device_added),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1, G_TYPE_STRING);
+
+ store_signals[DEVICE_REMOVED] =
+ g_signal_new ("device-removed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpInputDeviceStoreClass, device_removed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1, G_TYPE_STRING);
+
+ object_class->finalize = gimp_input_device_store_finalize;
+
+ klass->device_added = NULL;
+ klass->device_removed = NULL;
+}
+
+static void
+gimp_input_device_store_class_finalize (GimpInputDeviceStoreClass *klass)
+{
+}
+
+static GdkFilterReturn
+aux_window_filter (GdkXEvent *xevent,
+ GdkEvent *event,
+ gpointer data)
+{
+#if 0
+ GimpInputDeviceStore *store = (GimpInputDeviceStore *) data;
+ const MSG *msg = (MSG *) xevent;
+
+ /* Look for deviced being added or removed */
+ switch (msg->message)
+ {
+ }
+#endif
+
+ return GDK_FILTER_REMOVE;
+}
+
+static GdkWindow *
+create_aux_window (GimpInputDeviceStore *store)
+{
+ GdkWindowAttr wa;
+ GdkWindow *retval;
+
+ /* Create a dummy window to be associated with DirectInput devices */
+ wa.wclass = GDK_INPUT_OUTPUT;
+ wa.event_mask = GDK_ALL_EVENTS_MASK;
+ wa.width = 2;
+ wa.height = 2;
+ wa.x = -100;
+ wa.y = -100;
+ wa.window_type = GDK_WINDOW_TOPLEVEL;
+ if ((retval = gdk_window_new (NULL, &wa, GDK_WA_X|GDK_WA_Y)) == NULL)
+ return NULL;
+ g_object_ref (retval);
+
+ gdk_window_add_filter (retval, aux_window_filter, store);
+
+ return retval;
+}
+
+static BOOL CALLBACK
+enum_devices (const DIDEVICEINSTANCEW *di,
+ void *user_data)
+{
+ GimpInputDeviceStore *store = (GimpInputDeviceStore *) user_data;
+
+ gimp_input_device_store_add (store, &di->guidInstance);
+
+ return DIENUM_CONTINUE;
+}
+
+static void
+gimp_input_device_store_init (GimpInputDeviceStore *store)
+{
+ GType types[] = { G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER };
+ HRESULT hresult;
+ HMODULE thismodule;
+ HMODULE dinput8;
+
+ typedef HRESULT (WINAPI *t_DirectInput8Create) (HINSTANCE, DWORD, REFIID, LPVOID *, LPUNKNOWN);
+ t_DirectInput8Create p_DirectInput8Create;
+
+ g_assert (G_N_ELEMENTS (types) == NUM_COLUMNS);
+
+ gtk_list_store_set_column_types (GTK_LIST_STORE (store),
+ G_N_ELEMENTS (types), types);
+
+ if (!GetModuleHandleEx (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
+ (LPCTSTR) &gimp_input_device_store_init,
+ &thismodule))
+ return;
+
+ if ((store->window = create_aux_window (store)) == NULL)
+ {
+ g_set_error_literal (&store->error, GIMP_MODULE_ERROR, GIMP_MODULE_FAILED,
+ "Could not create aux window");
+ return;
+ }
+
+ if ((dinput8 = LoadLibrary ("dinput8.dll")) == NULL)
+ {
+ g_set_error_literal (&store->error, GIMP_MODULE_ERROR, GIMP_MODULE_FAILED,
+ "Could not load dinput8.dll");
+ return;
+ }
+
+ if ((p_DirectInput8Create = (t_DirectInput8Create) GetProcAddress (dinput8, "DirectInput8Create")) == NULL)
+ {
+ g_set_error_literal (&store->error, GIMP_MODULE_ERROR, GIMP_MODULE_FAILED,
+ "Could not find DirectInput8Create in dinput8.dll");
+ return;
+ }
+
+ if (FAILED ((hresult = (*p_DirectInput8Create) (thismodule,
+ DIRECTINPUT_VERSION,
+ &IID_IDirectInput8W,
+ (LPVOID *) &store->directinput8,
+ NULL))))
+ {
+ g_set_error (&store->error, GIMP_MODULE_ERROR, GIMP_MODULE_FAILED,
+ "DirectInput8Create failed: %s",
+ g_win32_error_message (hresult));
+ return;
+ }
+
+ if (FAILED ((hresult = IDirectInput8_EnumDevices (store->directinput8,
+ DI8DEVCLASS_GAMECTRL,
+ enum_devices,
+ store,
+ DIEDFL_ATTACHEDONLY))))
+ {
+ g_set_error (&store->error, GIMP_MODULE_ERROR, GIMP_MODULE_FAILED,
+ "IDirectInput8::EnumDevices failed: %s",
+ g_win32_error_message (hresult));
+ return;
+ }
+}
+
+static void
+gimp_input_device_store_finalize (GObject *object)
+{
+ GimpInputDeviceStore *store = GIMP_INPUT_DEVICE_STORE (object);
+
+ if (store->directinput8)
+ {
+ IDirectInput8_Release (store->directinput8);
+ store->directinput8 = NULL;
+ }
+
+ if (store->error)
+ {
+ g_error_free (store->error);
+ store->error = NULL;
+ }
+
+ G_OBJECT_CLASS (gimp_input_device_store_parent_class)->finalize (object);
+}
+
+static gboolean
+gimp_input_device_store_lookup (GimpInputDeviceStore *store,
+ const gchar *guid,
+ GtkTreeIter *iter)
+{
+ GtkTreeModel *model = GTK_TREE_MODEL (store);
+ GValue value = G_VALUE_INIT;
+ gboolean iter_valid;
+
+ for (iter_valid = gtk_tree_model_get_iter_first (model, iter);
+ iter_valid;
+ iter_valid = gtk_tree_model_iter_next (model, iter))
+ {
+ const gchar *str;
+
+ gtk_tree_model_get_value (model, iter, COLUMN_GUID, &value);
+
+ str = g_value_get_string (&value);
+
+ if (strcmp (str, guid) == 0)
+ {
+ g_value_unset (&value);
+ break;
+ }
+
+ g_value_unset (&value);
+ }
+
+ return iter_valid;
+}
+
+/* insert in alphabetic order */
+static void
+gimp_input_device_store_insert (GimpInputDeviceStore *store,
+ const gchar *guid,
+ const gchar *label,
+ LPDIRECTINPUTDEVICE8W didevice8)
+{
+ GtkTreeModel *model = GTK_TREE_MODEL (store);
+ GtkTreeIter iter;
+ GValue value = G_VALUE_INIT;
+ gint pos = 0;
+ gboolean iter_valid;
+
+ for (iter_valid = gtk_tree_model_get_iter_first (model, &iter);
+ iter_valid;
+ iter_valid = gtk_tree_model_iter_next (model, &iter), pos++)
+ {
+ const gchar *str;
+
+ gtk_tree_model_get_value (model, &iter, COLUMN_LABEL, &value);
+
+ str = g_value_get_string (&value);
+
+ if (g_utf8_collate (label, str) < 0)
+ {
+ g_value_unset (&value);
+ break;
+ }
+
+ g_value_unset (&value);
+ }
+
+ gtk_list_store_insert_with_values (GTK_LIST_STORE (store), &iter, pos,
+ COLUMN_GUID, guid,
+ COLUMN_LABEL, label,
+ COLUMN_IDEVICE, didevice8,
+ -1);
+}
+
+static gboolean
+gimp_input_device_store_add (GimpInputDeviceStore *store,
+ const GUID *guid)
+{
+ HRESULT hresult;
+ LPDIRECTINPUTDEVICE8W didevice8;
+ DIDEVICEINSTANCEW di;
+ gboolean added = FALSE;
+ unsigned char *s;
+ gchar *guidstring;
+ gchar *name;
+
+ if (UuidToString (guid, &s) != S_OK)
+ return FALSE;
+ guidstring = g_strdup (s);
+ RpcStringFree (&s);
+
+ if (FAILED ((hresult = IDirectInput8_CreateDevice (store->directinput8,
+ guid,
+ &didevice8,
+ NULL))))
+ {
+ g_free (guidstring);
+ return FALSE;
+ }
+
+ if (FAILED ((hresult = IDirectInputDevice8_SetCooperativeLevel (didevice8,
+ (HWND) gdk_win32_drawable_get_handle (store->window),
+ DISCL_NONEXCLUSIVE | DISCL_BACKGROUND))))
+ {
+ g_warning ("IDirectInputDevice8::SetCooperativeLevel failed: %s",
+ g_win32_error_message (hresult));
+ g_free (guidstring);
+ return FALSE;
+ }
+
+ di.dwSize = sizeof (DIDEVICEINSTANCEW);
+ if (FAILED ((hresult = IDirectInputDevice8_GetDeviceInfo (didevice8,
+ &di))))
+ {
+ g_warning ("IDirectInputDevice8::GetDeviceInfo failed: %s",
+ g_win32_error_message (hresult));
+ g_free (guidstring);
+ return FALSE;
+ }
+
+ name = g_utf16_to_utf8 (di.tszInstanceName, -1, NULL, NULL, NULL);
+ gimp_input_device_store_insert (store, guidstring, name, didevice8);
+
+ return added;
+}
+
+static gboolean
+gimp_input_device_store_remove (GimpInputDeviceStore *store,
+ const gchar *guid)
+{
+ GtkTreeIter iter;
+
+ if (gimp_input_device_store_lookup (store, guid, &iter))
+ {
+ gtk_list_store_remove (GTK_LIST_STORE (store), &iter);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+#if 0
+
+static void
+gimp_input_device_store_device_added (LibHalContext *ctx,
+ const char *guid)
+{
+ GimpInputDeviceStore *store = libhal_ctx_get_user_data (ctx);
+
+ if (gimp_input_device_store_add (store, udi))
+ {
+ g_signal_emit (store, store_signals[DEVICE_ADDED], 0, udi);
+ }
+}
+
+static void
+gimp_input_device_store_device_removed (LibHalContext *ctx,
+ const char *udi)
+{
+ GimpInputDeviceStore *store = libhal_ctx_get_user_data (ctx);
+
+ if (gimp_input_device_store_remove (store, udi))
+ {
+ g_signal_emit (store, store_signals[DEVICE_REMOVED], 0, udi);
+ }
+}
+
+#endif
+
+GimpInputDeviceStore *
+gimp_input_device_store_new (void)
+{
+ return g_object_new (GIMP_TYPE_INPUT_DEVICE_STORE, NULL);
+}
+
+gchar *
+gimp_input_device_store_get_device_file (GimpInputDeviceStore *store,
+ const gchar *udi)
+{
+ GtkTreeIter iter;
+ GValue value = G_VALUE_INIT;
+
+ g_return_val_if_fail (GIMP_IS_INPUT_DEVICE_STORE (store), NULL);
+ g_return_val_if_fail (udi != NULL, NULL);
+
+ if (! store->directinput8)
+ return NULL;
+
+ if (gimp_input_device_store_lookup (store, udi, &iter))
+ {
+ gtk_tree_model_get_value (GTK_TREE_MODEL (store),
+ &iter, COLUMN_IDEVICE, &value);
+ return g_value_get_pointer (&value);
+ }
+
+ return NULL;
+}
+
+GError *
+gimp_input_device_store_get_error (GimpInputDeviceStore *store)
+{
+ g_return_val_if_fail (GIMP_IS_INPUT_DEVICE_STORE (store), NULL);
+
+ return store->error ? g_error_copy (store->error) : NULL;
+}
+
+#endif /* HAVE_DX_DINPUT */
diff --git a/modules/gimpinputdevicestore-gudev.c b/modules/gimpinputdevicestore-gudev.c
new file mode 100644
index 0000000..4c54474
--- /dev/null
+++ b/modules/gimpinputdevicestore-gudev.c
@@ -0,0 +1,441 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpinputdevicestore-gudev.c
+ * Input device store based on GUdev, the hardware abstraction layer.
+ * Copyright (C) 2007 Sven Neumann <sven@gimp.org>
+ * 2011 Michael Natterer <mitch@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "gimpinputdevicestore.h"
+
+#include "libgimpmodule/gimpmodule.h"
+
+
+#ifdef HAVE_LIBGUDEV
+
+#include <gudev/gudev.h>
+
+enum
+{
+ COLUMN_IDENTIFIER,
+ COLUMN_LABEL,
+ COLUMN_DEVICE_FILE,
+ NUM_COLUMNS
+};
+
+enum
+{
+ PROP_0,
+ PROP_CONSTRUCT_ERROR
+};
+
+enum
+{
+ DEVICE_ADDED,
+ DEVICE_REMOVED,
+ LAST_SIGNAL
+};
+
+typedef struct _GimpInputDeviceStoreClass GimpInputDeviceStoreClass;
+
+struct _GimpInputDeviceStore
+{
+ GtkListStore parent_instance;
+
+ GUdevClient *client;
+ GError *error;
+};
+
+
+struct _GimpInputDeviceStoreClass
+{
+ GtkListStoreClass parent_class;
+
+ void (* device_added) (GimpInputDeviceStore *store,
+ const gchar *identifier);
+ void (* device_removed) (GimpInputDeviceStore *store,
+ const gchar *identifier);
+};
+
+
+static void gimp_input_device_store_finalize (GObject *object);
+
+static gboolean gimp_input_device_store_add (GimpInputDeviceStore *store,
+ GUdevDevice *device);
+static gboolean gimp_input_device_store_remove (GimpInputDeviceStore *store,
+ GUdevDevice *device);
+
+static void gimp_input_device_store_uevent (GUdevClient *client,
+ const gchar *action,
+ GUdevDevice *device,
+ GimpInputDeviceStore *store);
+
+
+G_DEFINE_DYNAMIC_TYPE (GimpInputDeviceStore, gimp_input_device_store,
+ GTK_TYPE_LIST_STORE)
+
+static guint store_signals[LAST_SIGNAL] = { 0 };
+
+
+void
+gimp_input_device_store_register_types (GTypeModule *module)
+{
+ gimp_input_device_store_register_type (module);
+}
+
+static void
+gimp_input_device_store_class_init (GimpInputDeviceStoreClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ store_signals[DEVICE_ADDED] =
+ g_signal_new ("device-added",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpInputDeviceStoreClass, device_added),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1, G_TYPE_STRING);
+
+ store_signals[DEVICE_REMOVED] =
+ g_signal_new ("device-removed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpInputDeviceStoreClass, device_removed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1, G_TYPE_STRING);
+
+ object_class->finalize = gimp_input_device_store_finalize;
+
+ klass->device_added = NULL;
+ klass->device_removed = NULL;
+}
+
+static void
+gimp_input_device_store_class_finalize (GimpInputDeviceStoreClass *klass)
+{
+}
+
+static void
+gimp_input_device_store_init (GimpInputDeviceStore *store)
+{
+ GType types[] = { G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING };
+ const gchar *subsystems[] = { "input", NULL };
+ GList *devices;
+ GList *list;
+
+ gtk_list_store_set_column_types (GTK_LIST_STORE (store),
+ G_N_ELEMENTS (types), types);
+
+ store->client = g_udev_client_new (subsystems);
+
+ devices = g_udev_client_query_by_subsystem (store->client, "input");
+
+ for (list = devices; list; list = g_list_next (list))
+ {
+ GUdevDevice *device = list->data;
+
+ gimp_input_device_store_add (store, device);
+ g_object_unref (device);
+ }
+
+ g_list_free (devices);
+
+ g_signal_connect (store->client, "uevent",
+ G_CALLBACK (gimp_input_device_store_uevent),
+ store);
+}
+
+static void
+gimp_input_device_store_finalize (GObject *object)
+{
+ GimpInputDeviceStore *store = GIMP_INPUT_DEVICE_STORE (object);
+
+ if (store->client)
+ {
+ g_object_unref (store->client);
+ store->client = NULL;
+ }
+
+ if (store->error)
+ {
+ g_error_free (store->error);
+ store->error = NULL;
+ }
+
+ G_OBJECT_CLASS (gimp_input_device_store_parent_class)->finalize (object);
+}
+
+static gboolean
+gimp_input_device_store_lookup (GimpInputDeviceStore *store,
+ const gchar *identifier,
+ GtkTreeIter *iter)
+{
+ GtkTreeModel *model = GTK_TREE_MODEL (store);
+ GValue value = G_VALUE_INIT;
+ gboolean iter_valid;
+
+ for (iter_valid = gtk_tree_model_get_iter_first (model, iter);
+ iter_valid;
+ iter_valid = gtk_tree_model_iter_next (model, iter))
+ {
+ const gchar *str;
+
+ gtk_tree_model_get_value (model, iter, COLUMN_IDENTIFIER, &value);
+
+ str = g_value_get_string (&value);
+
+ if (strcmp (str, identifier) == 0)
+ {
+ g_value_unset (&value);
+ break;
+ }
+
+ g_value_unset (&value);
+ }
+
+ return iter_valid;
+}
+
+/* insert in alphabetic order */
+static void
+gimp_input_device_store_insert (GimpInputDeviceStore *store,
+ const gchar *identifier,
+ const gchar *label,
+ const gchar *device_file)
+{
+ GtkTreeModel *model = GTK_TREE_MODEL (store);
+ GtkTreeIter iter;
+ GValue value = G_VALUE_INIT;
+ gint pos = 0;
+ gboolean iter_valid;
+
+ for (iter_valid = gtk_tree_model_get_iter_first (model, &iter);
+ iter_valid;
+ iter_valid = gtk_tree_model_iter_next (model, &iter), pos++)
+ {
+ const gchar *str;
+
+ gtk_tree_model_get_value (model, &iter, COLUMN_LABEL, &value);
+
+ str = g_value_get_string (&value);
+
+ if (g_utf8_collate (label, str) < 0)
+ {
+ g_value_unset (&value);
+ break;
+ }
+
+ g_value_unset (&value);
+ }
+
+ gtk_list_store_insert_with_values (GTK_LIST_STORE (store), &iter, pos,
+ COLUMN_IDENTIFIER, identifier,
+ COLUMN_LABEL, label,
+ COLUMN_DEVICE_FILE, device_file,
+ -1);
+}
+
+static gboolean
+gimp_input_device_store_add (GimpInputDeviceStore *store,
+ GUdevDevice *device)
+{
+ const gchar *device_file = g_udev_device_get_device_file (device);
+#if 0
+ const gchar *path = g_udev_device_get_sysfs_path (device);
+#endif
+ const gchar *name = g_udev_device_get_sysfs_attr (device, "name");
+
+#if 0
+ g_printerr ("\ndevice added: %s, %s, %s\n",
+ name ? name : "NULL",
+ device_file ? device_file : "NULL",
+ path);
+#endif
+
+ if (device_file)
+ {
+ if (name)
+ {
+ GtkTreeIter unused;
+
+ if (! gimp_input_device_store_lookup (store, name, &unused))
+ {
+ gimp_input_device_store_insert (store, name, name, device_file);
+
+ g_signal_emit (store, store_signals[DEVICE_ADDED], 0,
+ name);
+
+ return TRUE;
+ }
+ }
+ else
+ {
+ GUdevDevice *parent = g_udev_device_get_parent (device);
+
+ if (parent)
+ {
+ const gchar *parent_name;
+
+ parent_name = g_udev_device_get_sysfs_attr (parent, "name");
+
+ if (parent_name)
+ {
+ GtkTreeIter unused;
+
+ if (! gimp_input_device_store_lookup (store, parent_name,
+ &unused))
+ {
+ gimp_input_device_store_insert (store,
+ parent_name, parent_name,
+ device_file);
+
+ g_signal_emit (store, store_signals[DEVICE_ADDED], 0,
+ parent_name);
+
+ g_object_unref (parent);
+ return TRUE;
+ }
+ }
+
+ g_object_unref (parent);
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gimp_input_device_store_remove (GimpInputDeviceStore *store,
+ GUdevDevice *device)
+{
+ const gchar *name = g_udev_device_get_sysfs_attr (device, "name");
+ GtkTreeIter iter;
+
+ if (name)
+ {
+ if (gimp_input_device_store_lookup (store, name, &iter))
+ {
+ gtk_list_store_remove (GTK_LIST_STORE (store), &iter);
+
+ g_signal_emit (store, store_signals[DEVICE_REMOVED], 0, name);
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+gimp_input_device_store_uevent (GUdevClient *client,
+ const gchar *action,
+ GUdevDevice *device,
+ GimpInputDeviceStore *store)
+{
+ if (! strcmp (action, "add"))
+ {
+ gimp_input_device_store_add (store, device);
+ }
+ else if (! strcmp (action, "remove"))
+ {
+ gimp_input_device_store_remove (store, device);
+ }
+}
+
+GimpInputDeviceStore *
+gimp_input_device_store_new (void)
+{
+ return g_object_new (GIMP_TYPE_INPUT_DEVICE_STORE, NULL);
+}
+
+gchar *
+gimp_input_device_store_get_device_file (GimpInputDeviceStore *store,
+ const gchar *identifier)
+{
+ GtkTreeIter iter;
+
+ g_return_val_if_fail (GIMP_IS_INPUT_DEVICE_STORE (store), NULL);
+ g_return_val_if_fail (identifier != NULL, NULL);
+
+ if (! store->client)
+ return NULL;
+
+ if (gimp_input_device_store_lookup (store, identifier, &iter))
+ {
+ GtkTreeModel *model = GTK_TREE_MODEL (store);
+ gchar *device_file;
+
+ gtk_tree_model_get (model, &iter,
+ COLUMN_DEVICE_FILE, &device_file,
+ -1);
+
+ return device_file;
+ }
+
+ return NULL;
+}
+
+GError *
+gimp_input_device_store_get_error (GimpInputDeviceStore *store)
+{
+ g_return_val_if_fail (GIMP_IS_INPUT_DEVICE_STORE (store), NULL);
+
+ return store->error ? g_error_copy (store->error) : NULL;
+}
+
+#else /* HAVE_LIBGUDEV */
+
+void
+gimp_input_device_store_register_types (GTypeModule *module)
+{
+}
+
+GType
+gimp_input_device_store_get_type (void)
+{
+ return G_TYPE_NONE;
+}
+
+GimpInputDeviceStore *
+gimp_input_device_store_new (void)
+{
+ return NULL;
+}
+
+gchar *
+gimp_input_device_store_get_device_file (GimpInputDeviceStore *store,
+ const gchar *identifier)
+{
+ return NULL;
+}
+
+GError *
+gimp_input_device_store_get_error (GimpInputDeviceStore *store)
+{
+ return NULL;
+}
+
+#endif /* HAVE_LIBGUDEV */
diff --git a/modules/gimpinputdevicestore.h b/modules/gimpinputdevicestore.h
new file mode 100644
index 0000000..a02d453
--- /dev/null
+++ b/modules/gimpinputdevicestore.h
@@ -0,0 +1,42 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpinputdevicestore.h
+ * Copyright (C) 2007 Sven Neumann <sven@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_INPUT_DEVICE_STORE_H__
+#define __GIMP_INPUT_DEVICE_STORE_H__
+
+
+#define GIMP_TYPE_INPUT_DEVICE_STORE (gimp_input_device_store_get_type ())
+#define GIMP_INPUT_DEVICE_STORE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_INPUT_DEVICE_STORE, GimpInputDeviceStore))
+#define GIMP_IS_INPUT_DEVICE_STORE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_INPUT_DEVICE_STORE))
+
+typedef struct _GimpInputDeviceStore GimpInputDeviceStore;
+
+
+void gimp_input_device_store_register_types (GTypeModule *module);
+
+GType gimp_input_device_store_get_type (void);
+
+GimpInputDeviceStore * gimp_input_device_store_new (void);
+gchar * gimp_input_device_store_get_device_file (GimpInputDeviceStore *store,
+ const gchar *udi);
+GError * gimp_input_device_store_get_error (GimpInputDeviceStore *store);
+
+
+#endif /* __GIMP_INPUT_DEVICE_STORE_H__ */