diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:30:19 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:30:19 +0000 |
commit | 5c1676dfe6d2f3c837a5e074117b45613fd29a72 (patch) | |
tree | cbffb45144febf451e54061db2b21395faf94bfe /modules | |
parent | Initial commit. (diff) | |
download | gimp-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.am | 102 | ||||
-rw-r--r-- | modules/Makefile.in | 1247 | ||||
-rw-r--r-- | modules/color-selector-cmyk.c | 421 | ||||
-rw-r--r-- | modules/color-selector-water.c | 545 | ||||
-rw-r--r-- | modules/color-selector-wheel.c | 169 | ||||
-rw-r--r-- | modules/controller-dx-dinput.c | 1122 | ||||
-rw-r--r-- | modules/controller-linux-input.c | 711 | ||||
-rw-r--r-- | modules/controller-midi.c | 893 | ||||
-rw-r--r-- | modules/display-filter-clip-warning.c | 494 | ||||
-rw-r--r-- | modules/display-filter-color-blind.c | 495 | ||||
-rw-r--r-- | modules/display-filter-gamma.c | 228 | ||||
-rw-r--r-- | modules/display-filter-high-contrast.c | 228 | ||||
-rw-r--r-- | modules/gimpcolorwheel.c | 1623 | ||||
-rw-r--r-- | modules/gimpcolorwheel.h | 98 | ||||
-rw-r--r-- | modules/gimpinputdevicestore-dx.c | 482 | ||||
-rw-r--r-- | modules/gimpinputdevicestore-gudev.c | 441 | ||||
-rw-r--r-- | modules/gimpinputdevicestore.h | 42 |
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__ */ |