Initial checkin of Pika from heckimp
This commit is contained in:
419
modules/color-selector-cmyk.c
Normal file
419
modules/color-selector-cmyk.c
Normal file
@ -0,0 +1,419 @@
|
||||
/* PIKA 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 "libpikacolor/pikacolor.h"
|
||||
#include "libpikaconfig/pikaconfig.h"
|
||||
#include "libpikamodule/pikamodule.h"
|
||||
#include "libpikawidgets/pikawidgets.h"
|
||||
|
||||
#include "libpika/libpika-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
|
||||
{
|
||||
PikaColorSelector parent_instance;
|
||||
|
||||
PikaColorConfig *config;
|
||||
PikaColorProfile *simulation_profile;
|
||||
PikaColorRenderingIntent simulation_intent;
|
||||
gboolean simulation_bpc;
|
||||
|
||||
PikaCMYK cmyk;
|
||||
GtkWidget *scales[4];
|
||||
GtkWidget *name_label;
|
||||
|
||||
gboolean in_destruction;
|
||||
};
|
||||
|
||||
struct _ColorselCmykClass
|
||||
{
|
||||
PikaColorSelectorClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
GType colorsel_cmyk_get_type (void);
|
||||
|
||||
static void colorsel_cmyk_dispose (GObject *object);
|
||||
|
||||
static void colorsel_cmyk_set_color (PikaColorSelector *selector,
|
||||
const PikaRGB *rgb,
|
||||
const PikaHSV *hsv);
|
||||
static void colorsel_cmyk_set_config (PikaColorSelector *selector,
|
||||
PikaColorConfig *config);
|
||||
static void colorsel_cmyk_set_simulation (PikaColorSelector *selector,
|
||||
PikaColorProfile *profile,
|
||||
PikaColorRenderingIntent intent,
|
||||
gboolean bpc);
|
||||
|
||||
static void colorsel_cmyk_scale_update (PikaLabelSpin *scale,
|
||||
ColorselCmyk *module);
|
||||
static void colorsel_cmyk_config_changed (ColorselCmyk *module);
|
||||
|
||||
|
||||
static const PikaModuleInfo colorsel_cmyk_info =
|
||||
{
|
||||
PIKA_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,
|
||||
PIKA_TYPE_COLOR_SELECTOR)
|
||||
|
||||
|
||||
G_MODULE_EXPORT const PikaModuleInfo *
|
||||
pika_module_query (GTypeModule *module)
|
||||
{
|
||||
return &colorsel_cmyk_info;
|
||||
}
|
||||
|
||||
G_MODULE_EXPORT gboolean
|
||||
pika_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);
|
||||
PikaColorSelectorClass *selector_class = PIKA_COLOR_SELECTOR_CLASS (klass);
|
||||
|
||||
object_class->dispose = colorsel_cmyk_dispose;
|
||||
|
||||
selector_class->name = _("CMYK");
|
||||
selector_class->help_id = "pika-colorselector-cmyk";
|
||||
selector_class->icon_name = PIKA_ICON_COLOR_SELECTOR_CMYK;
|
||||
selector_class->set_color = colorsel_cmyk_set_color;
|
||||
selector_class->set_config = colorsel_cmyk_set_config;
|
||||
selector_class->set_simulation = colorsel_cmyk_set_simulation;
|
||||
|
||||
gtk_widget_class_set_css_name (GTK_WIDGET_CLASS (klass), "ColorselCmyk");
|
||||
}
|
||||
|
||||
static void
|
||||
colorsel_cmyk_class_finalize (ColorselCmykClass *klass)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
colorsel_cmyk_init (ColorselCmyk *module)
|
||||
{
|
||||
GtkWidget *grid;
|
||||
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;
|
||||
|
||||
gtk_box_set_spacing (GTK_BOX (module), 6);
|
||||
|
||||
grid = gtk_grid_new ();
|
||||
|
||||
gtk_grid_set_row_spacing (GTK_GRID (grid), 1);
|
||||
gtk_grid_set_column_spacing (GTK_GRID (grid), 2);
|
||||
|
||||
gtk_box_pack_start (GTK_BOX (module), grid, FALSE, FALSE, 0);
|
||||
gtk_widget_show (grid);
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
module->scales[i] = pika_scale_entry_new (gettext (cmyk_labels[i]),
|
||||
0.0, 0.0, 100.0, 0);
|
||||
pika_help_set_help_data (module->scales[i], gettext (cmyk_tips[i]), NULL);
|
||||
|
||||
g_signal_connect (module->scales[i], "value-changed",
|
||||
G_CALLBACK (colorsel_cmyk_scale_update),
|
||||
module);
|
||||
|
||||
gtk_grid_attach (GTK_GRID (grid), module->scales[i], 1, i, 3, 1);
|
||||
gtk_widget_show (module->scales[i]);
|
||||
}
|
||||
|
||||
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);
|
||||
pika_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 (PIKA_COLOR_SELECTOR (object), NULL);
|
||||
g_clear_object (&module->simulation_profile);
|
||||
|
||||
G_OBJECT_CLASS (colorsel_cmyk_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
colorsel_cmyk_set_color (PikaColorSelector *selector,
|
||||
const PikaRGB *rgb,
|
||||
const PikaHSV *hsv)
|
||||
{
|
||||
PikaColorProfile *cmyk_profile = NULL;
|
||||
PikaColorRenderingIntent intent = PIKA_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC;
|
||||
const Babl *fish = NULL;
|
||||
const Babl *space = NULL;
|
||||
ColorselCmyk *module = COLORSEL_CMYK (selector);
|
||||
gfloat values[4];
|
||||
gint i;
|
||||
|
||||
/* Try Image Soft-proofing profile first, then default CMYK profile */
|
||||
if (module->simulation_profile)
|
||||
cmyk_profile = module->simulation_profile;
|
||||
|
||||
if (! cmyk_profile && PIKA_IS_COLOR_CONFIG (module->config))
|
||||
cmyk_profile = pika_color_config_get_cmyk_color_profile (PIKA_COLOR_CONFIG (module->config),
|
||||
NULL);
|
||||
|
||||
if (cmyk_profile && pika_color_profile_is_cmyk (cmyk_profile))
|
||||
{
|
||||
intent = module->simulation_intent;
|
||||
|
||||
space = pika_color_profile_get_space (cmyk_profile, intent,
|
||||
NULL);
|
||||
}
|
||||
|
||||
fish = babl_fish (babl_format ("R'G'B'A double"),
|
||||
babl_format_with_space ("CMYK float", space));
|
||||
|
||||
babl_process (fish, rgb, values, 1);
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
g_signal_handlers_block_by_func (module->scales[i],
|
||||
colorsel_cmyk_scale_update,
|
||||
module);
|
||||
|
||||
values[i] *= 100.0;
|
||||
pika_label_spin_set_value (PIKA_LABEL_SPIN (module->scales[i]), values[i]);
|
||||
|
||||
g_signal_handlers_unblock_by_func (module->scales[i],
|
||||
colorsel_cmyk_scale_update,
|
||||
module);
|
||||
}
|
||||
|
||||
if (cmyk_profile && ! module->simulation_profile)
|
||||
g_object_unref (cmyk_profile);
|
||||
}
|
||||
|
||||
static void
|
||||
colorsel_cmyk_set_config (PikaColorSelector *selector,
|
||||
PikaColorConfig *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_set_simulation (PikaColorSelector *selector,
|
||||
PikaColorProfile *profile,
|
||||
PikaColorRenderingIntent intent,
|
||||
gboolean bpc)
|
||||
{
|
||||
ColorselCmyk *module = COLORSEL_CMYK (selector);
|
||||
PikaColorProfile *cmyk_profile = NULL;
|
||||
gchar *text;
|
||||
|
||||
gtk_label_set_text (GTK_LABEL (module->name_label), _("Profile: (none)"));
|
||||
pika_help_set_help_data (module->name_label, NULL, NULL);
|
||||
|
||||
g_set_object (&module->simulation_profile, profile);
|
||||
|
||||
cmyk_profile = module->simulation_profile;
|
||||
|
||||
if (! cmyk_profile && PIKA_IS_COLOR_CONFIG (module->config))
|
||||
cmyk_profile = pika_color_config_get_cmyk_color_profile (PIKA_COLOR_CONFIG (module->config),
|
||||
NULL);
|
||||
|
||||
if (cmyk_profile && pika_color_profile_is_cmyk (cmyk_profile))
|
||||
{
|
||||
text = g_strdup_printf (_("Profile: %s"),
|
||||
pika_color_profile_get_label (cmyk_profile));
|
||||
gtk_label_set_text (GTK_LABEL (module->name_label), text);
|
||||
g_free (text);
|
||||
|
||||
pika_help_set_help_data (module->name_label,
|
||||
pika_color_profile_get_summary (cmyk_profile),
|
||||
NULL);
|
||||
}
|
||||
|
||||
module->simulation_intent = intent;
|
||||
module->simulation_bpc = bpc;
|
||||
|
||||
if (! module->in_destruction)
|
||||
colorsel_cmyk_set_color (selector, &selector->rgb, &selector->hsv);
|
||||
}
|
||||
|
||||
static void
|
||||
colorsel_cmyk_scale_update (PikaLabelSpin *scale,
|
||||
ColorselCmyk *module)
|
||||
{
|
||||
PikaColorProfile *cmyk_profile = NULL;
|
||||
PikaColorSelector *selector = PIKA_COLOR_SELECTOR (module);
|
||||
PikaColorRenderingIntent intent = PIKA_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC;
|
||||
const Babl *fish = NULL;
|
||||
const Babl *space = NULL;
|
||||
gfloat cmyk_values[4];
|
||||
gfloat rgb_values[3];
|
||||
gint i;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
cmyk_values[i] = pika_label_spin_get_value (PIKA_LABEL_SPIN (module->scales[i])) / 100.0;
|
||||
|
||||
if (module->simulation_profile)
|
||||
cmyk_profile = module->simulation_profile;
|
||||
|
||||
if (! cmyk_profile && PIKA_IS_COLOR_CONFIG (module->config))
|
||||
cmyk_profile = pika_color_config_get_cmyk_color_profile (PIKA_COLOR_CONFIG (module->config),
|
||||
NULL);
|
||||
if (cmyk_profile)
|
||||
{
|
||||
intent = module->simulation_intent;
|
||||
|
||||
space = pika_color_profile_get_space (cmyk_profile, intent,
|
||||
NULL);
|
||||
}
|
||||
|
||||
fish = babl_fish (babl_format_with_space ("CMYK float", space),
|
||||
babl_format ("R'G'B'A float"));
|
||||
|
||||
babl_process (fish, cmyk_values, rgb_values, 1);
|
||||
|
||||
selector->rgb.r = rgb_values[0];
|
||||
selector->rgb.g = rgb_values[1];
|
||||
selector->rgb.b = rgb_values[2];
|
||||
|
||||
pika_rgb_to_hsv (&selector->rgb, &selector->hsv);
|
||||
|
||||
pika_color_selector_emit_color_changed (selector);
|
||||
|
||||
if (cmyk_profile && ! module->simulation_profile)
|
||||
g_object_unref (cmyk_profile);
|
||||
}
|
||||
|
||||
static void
|
||||
colorsel_cmyk_config_changed (ColorselCmyk *module)
|
||||
{
|
||||
PikaColorSelector *selector = PIKA_COLOR_SELECTOR (module);
|
||||
PikaColorConfig *config = module->config;
|
||||
PikaColorProfile *rgb_profile = NULL;
|
||||
PikaColorProfile *cmyk_profile = NULL;
|
||||
gchar *text;
|
||||
|
||||
gtk_label_set_text (GTK_LABEL (module->name_label), _("Profile: (none)"));
|
||||
pika_help_set_help_data (module->name_label, NULL, NULL);
|
||||
|
||||
if (! config)
|
||||
goto out;
|
||||
|
||||
if (module->simulation_profile)
|
||||
cmyk_profile = module->simulation_profile;
|
||||
|
||||
if (! cmyk_profile && PIKA_IS_COLOR_CONFIG (module->config))
|
||||
cmyk_profile = pika_color_config_get_cmyk_color_profile (PIKA_COLOR_CONFIG (module->config),
|
||||
NULL);
|
||||
|
||||
if (! cmyk_profile)
|
||||
goto out;
|
||||
|
||||
rgb_profile = pika_color_profile_new_rgb_srgb ();
|
||||
|
||||
text = g_strdup_printf (_("Profile: %s"),
|
||||
pika_color_profile_get_label (cmyk_profile));
|
||||
gtk_label_set_text (GTK_LABEL (module->name_label), text);
|
||||
g_free (text);
|
||||
|
||||
pika_help_set_help_data (module->name_label,
|
||||
pika_color_profile_get_summary (cmyk_profile),
|
||||
NULL);
|
||||
|
||||
out:
|
||||
|
||||
if (rgb_profile)
|
||||
g_object_unref (rgb_profile);
|
||||
|
||||
if (cmyk_profile && ! module->simulation_profile)
|
||||
g_object_unref (cmyk_profile);
|
||||
|
||||
if (! module->in_destruction)
|
||||
colorsel_cmyk_set_color (selector, &selector->rgb, &selector->hsv);
|
||||
}
|
544
modules/color-selector-water.c
Normal file
544
modules/color-selector-water.c
Normal file
@ -0,0 +1,544 @@
|
||||
/* 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 "libpikacolor/pikacolor.h"
|
||||
#include "libpikamath/pikamath.h"
|
||||
#include "libpikamodule/pikamodule.h"
|
||||
#include "libpikawidgets/pikawidgets.h"
|
||||
|
||||
#include "libpika/libpika-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
|
||||
{
|
||||
PikaColorSelector parent_instance;
|
||||
|
||||
GtkWidget *area;
|
||||
|
||||
gdouble last_x;
|
||||
gdouble last_y;
|
||||
|
||||
gfloat pressure_adjust;
|
||||
guint32 motion_time;
|
||||
|
||||
PikaColorConfig *config;
|
||||
PikaColorTransform *transform;
|
||||
};
|
||||
|
||||
struct _ColorselWaterClass
|
||||
{
|
||||
PikaColorSelectorClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
GType colorsel_water_get_type (void);
|
||||
|
||||
static void colorsel_water_dispose (GObject *object);
|
||||
|
||||
static void colorsel_water_set_config (PikaColorSelector *selector,
|
||||
PikaColorConfig *config);
|
||||
|
||||
static void colorsel_water_create_transform (ColorselWater *water);
|
||||
static void colorsel_water_destroy_transform (ColorselWater *water);
|
||||
|
||||
static gboolean select_area_draw (GtkWidget *widget,
|
||||
cairo_t *cr,
|
||||
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 PikaModuleInfo colorsel_water_info =
|
||||
{
|
||||
PIKA_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,
|
||||
PIKA_TYPE_COLOR_SELECTOR)
|
||||
|
||||
|
||||
G_MODULE_EXPORT const PikaModuleInfo *
|
||||
pika_module_query (GTypeModule *module)
|
||||
{
|
||||
return &colorsel_water_info;
|
||||
}
|
||||
|
||||
G_MODULE_EXPORT gboolean
|
||||
pika_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);
|
||||
PikaColorSelectorClass *selector_class = PIKA_COLOR_SELECTOR_CLASS (klass);
|
||||
|
||||
object_class->dispose = colorsel_water_dispose;
|
||||
|
||||
selector_class->name = _("Watercolor");
|
||||
selector_class->help_id = "pika-colorselector-watercolor";
|
||||
selector_class->icon_name = PIKA_ICON_COLOR_SELECTOR_WATER;
|
||||
selector_class->set_config = colorsel_water_set_config;
|
||||
|
||||
gtk_widget_class_set_css_name (GTK_WIDGET_CLASS (klass), "ColorselWater");
|
||||
}
|
||||
|
||||
static void
|
||||
colorsel_water_class_finalize (ColorselWaterClass *klass)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
colorsel_water_init (ColorselWater *water)
|
||||
{
|
||||
GtkWidget *hbox;
|
||||
GtkWidget *frame;
|
||||
GtkAdjustment *adj;
|
||||
GtkWidget *scale;
|
||||
|
||||
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, "draw",
|
||||
G_CALLBACK (select_area_draw),
|
||||
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);
|
||||
|
||||
gtk_widget_grab_focus (water->area);
|
||||
|
||||
adj = 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);
|
||||
pika_help_set_help_data (scale, _("Pressure"), NULL);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), scale, FALSE, FALSE, 0);
|
||||
|
||||
gtk_widget_show_all (hbox);
|
||||
|
||||
pika_widget_track_monitor (GTK_WIDGET (water),
|
||||
G_CALLBACK (colorsel_water_destroy_transform),
|
||||
NULL, 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 (PIKA_COLOR_SELECTOR (object), NULL);
|
||||
|
||||
G_OBJECT_CLASS (colorsel_water_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
colorsel_water_set_config (PikaColorSelector *selector,
|
||||
PikaColorConfig *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 PikaColorProfile *profile = NULL;
|
||||
|
||||
const Babl *format = babl_format ("cairo-RGB24");
|
||||
|
||||
if (G_UNLIKELY (! profile))
|
||||
profile = pika_color_profile_new_rgb_srgb ();
|
||||
|
||||
water->transform = pika_widget_get_color_transform (water->area,
|
||||
water->config,
|
||||
profile,
|
||||
format,
|
||||
format,
|
||||
NULL,
|
||||
PIKA_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC,
|
||||
FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
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_draw (GtkWidget *widget,
|
||||
cairo_t *cr,
|
||||
ColorselWater *water)
|
||||
{
|
||||
GdkRectangle area;
|
||||
GtkAllocation allocation;
|
||||
gdouble x1, y1, x2, y2;
|
||||
gdouble dx;
|
||||
gdouble dy;
|
||||
cairo_surface_t *surface;
|
||||
guchar *dest;
|
||||
gdouble y;
|
||||
gint j;
|
||||
|
||||
cairo_clip_extents (cr, &x1, &y1, &x2, &y2);
|
||||
|
||||
area.x = floor (x1);
|
||||
area.y = floor (y1);
|
||||
area.width = ceil (x2) - area.x;
|
||||
area.height = ceil (y2) - area.y;
|
||||
|
||||
gtk_widget_get_allocation (widget, &allocation);
|
||||
|
||||
dx = 1.0 / allocation.width;
|
||||
dy = 1.0 / allocation.height;
|
||||
|
||||
surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
|
||||
area.width,
|
||||
area.height);
|
||||
|
||||
dest = cairo_image_surface_get_data (surface);
|
||||
|
||||
if (! water->transform)
|
||||
colorsel_water_create_transform (water);
|
||||
|
||||
for (j = 0, y = area.y / allocation.height;
|
||||
j < 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 += area.x * dr;
|
||||
g += area.x * dg;
|
||||
b += area.x * db;
|
||||
|
||||
for (i = 0; i < area.width; i++)
|
||||
{
|
||||
PIKA_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)
|
||||
pika_color_transform_process_pixels (water->transform,
|
||||
babl_format ("cairo-RGB24"),
|
||||
dest,
|
||||
babl_format ("cairo-RGB24"),
|
||||
dest,
|
||||
area.width);
|
||||
|
||||
dest += cairo_image_surface_get_stride (surface);
|
||||
}
|
||||
|
||||
cairo_surface_mark_dirty (surface);
|
||||
cairo_set_source_surface (cr, surface, area.x, area.y);
|
||||
cairo_surface_destroy (surface);
|
||||
|
||||
cairo_paint (cr);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
add_pigment (ColorselWater *water,
|
||||
gboolean erase,
|
||||
gdouble x,
|
||||
gdouble y,
|
||||
gdouble much)
|
||||
{
|
||||
PikaColorSelector *selector = PIKA_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);
|
||||
}
|
||||
|
||||
pika_rgb_clamp (&selector->rgb);
|
||||
|
||||
pika_rgb_to_hsv (&selector->rgb, &selector->hsv);
|
||||
|
||||
pika_color_selector_emit_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;
|
||||
}
|
164
modules/color-selector-wheel.c
Normal file
164
modules/color-selector-wheel.c
Normal file
@ -0,0 +1,164 @@
|
||||
/* PIKA 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 "libpikacolor/pikacolor.h"
|
||||
#include "libpikamath/pikamath.h"
|
||||
#include "libpikamodule/pikamodule.h"
|
||||
#include "libpikawidgets/pikawidgets.h"
|
||||
|
||||
#include "pikacolorwheel.h"
|
||||
|
||||
#include "libpika/libpika-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
|
||||
{
|
||||
PikaColorSelector parent_instance;
|
||||
|
||||
GtkWidget *hsv;
|
||||
};
|
||||
|
||||
struct _ColorselWheelClass
|
||||
{
|
||||
PikaColorSelectorClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
GType colorsel_wheel_get_type (void);
|
||||
|
||||
static void colorsel_wheel_set_color (PikaColorSelector *selector,
|
||||
const PikaRGB *rgb,
|
||||
const PikaHSV *hsv);
|
||||
static void colorsel_wheel_set_config (PikaColorSelector *selector,
|
||||
PikaColorConfig *config);
|
||||
static void colorsel_wheel_changed (PikaColorWheel *hsv,
|
||||
PikaColorSelector *selector);
|
||||
|
||||
static const PikaModuleInfo colorsel_wheel_info =
|
||||
{
|
||||
PIKA_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,
|
||||
PIKA_TYPE_COLOR_SELECTOR)
|
||||
|
||||
|
||||
G_MODULE_EXPORT const PikaModuleInfo *
|
||||
pika_module_query (GTypeModule *module)
|
||||
{
|
||||
return &colorsel_wheel_info;
|
||||
}
|
||||
|
||||
G_MODULE_EXPORT gboolean
|
||||
pika_module_register (GTypeModule *module)
|
||||
{
|
||||
color_wheel_register_type (module);
|
||||
colorsel_wheel_register_type (module);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
colorsel_wheel_class_init (ColorselWheelClass *klass)
|
||||
{
|
||||
PikaColorSelectorClass *selector_class = PIKA_COLOR_SELECTOR_CLASS (klass);
|
||||
|
||||
selector_class->name = _("Wheel");
|
||||
selector_class->help_id = "pika-colorselector-triangle";
|
||||
selector_class->icon_name = PIKA_ICON_COLOR_SELECTOR_TRIANGLE;
|
||||
selector_class->set_color = colorsel_wheel_set_color;
|
||||
selector_class->set_config = colorsel_wheel_set_config;
|
||||
|
||||
gtk_widget_class_set_css_name (GTK_WIDGET_CLASS (klass), "ColorselWheel");
|
||||
}
|
||||
|
||||
static void
|
||||
colorsel_wheel_class_finalize (ColorselWheelClass *klass)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
colorsel_wheel_init (ColorselWheel *wheel)
|
||||
{
|
||||
wheel->hsv = pika_color_wheel_new ();
|
||||
g_object_add_weak_pointer (G_OBJECT (wheel->hsv),
|
||||
(gpointer) &wheel->hsv);
|
||||
gtk_box_pack_start (GTK_BOX (wheel), wheel->hsv, TRUE, TRUE, 0);
|
||||
gtk_widget_show (wheel->hsv);
|
||||
|
||||
g_signal_connect (wheel->hsv, "changed",
|
||||
G_CALLBACK (colorsel_wheel_changed),
|
||||
wheel);
|
||||
}
|
||||
|
||||
static void
|
||||
colorsel_wheel_set_color (PikaColorSelector *selector,
|
||||
const PikaRGB *rgb,
|
||||
const PikaHSV *hsv)
|
||||
{
|
||||
ColorselWheel *wheel = COLORSEL_WHEEL (selector);
|
||||
|
||||
pika_color_wheel_set_color (PIKA_COLOR_WHEEL (wheel->hsv),
|
||||
hsv->h, hsv->s, hsv->v);
|
||||
}
|
||||
|
||||
static void
|
||||
colorsel_wheel_set_config (PikaColorSelector *selector,
|
||||
PikaColorConfig *config)
|
||||
{
|
||||
ColorselWheel *wheel = COLORSEL_WHEEL (selector);
|
||||
|
||||
if (wheel->hsv)
|
||||
pika_color_wheel_set_color_config (PIKA_COLOR_WHEEL (wheel->hsv), config);
|
||||
}
|
||||
|
||||
static void
|
||||
colorsel_wheel_changed (PikaColorWheel *hsv,
|
||||
PikaColorSelector *selector)
|
||||
{
|
||||
pika_color_wheel_get_color (hsv,
|
||||
&selector->hsv.h,
|
||||
&selector->hsv.s,
|
||||
&selector->hsv.v);
|
||||
pika_hsv_to_rgb (&selector->hsv, &selector->rgb);
|
||||
|
||||
pika_color_selector_emit_color_changed (selector);
|
||||
}
|
1126
modules/controller-dx-dinput.c
Normal file
1126
modules/controller-dx-dinput.c
Normal file
File diff suppressed because it is too large
Load Diff
715
modules/controller-linux-input.c
Normal file
715
modules/controller-linux-input.c
Normal file
@ -0,0 +1,715 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* 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 "libpikaconfig/pikaconfig.h"
|
||||
#include "libpikamodule/pikamodule.h"
|
||||
#include "libpikawidgets/pikawidgets.h"
|
||||
|
||||
#define PIKA_ENABLE_CONTROLLER_UNDER_CONSTRUCTION
|
||||
#include "libpikawidgets/pikacontroller.h"
|
||||
|
||||
#include "pikainputdevicestore.h"
|
||||
|
||||
#include "libpika/libpika-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
|
||||
{
|
||||
PikaController parent_instance;
|
||||
|
||||
PikaInputDeviceStore *store;
|
||||
gchar *device;
|
||||
GIOChannel *io;
|
||||
guint io_id;
|
||||
};
|
||||
|
||||
struct _ControllerLinuxInputClass
|
||||
{
|
||||
PikaControllerClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
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 (PikaController *controller);
|
||||
static const gchar * linux_input_get_event_name (PikaController *controller,
|
||||
gint event_id);
|
||||
static const gchar * linux_input_get_event_blurb (PikaController *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 PikaModuleInfo linux_input_info =
|
||||
{
|
||||
PIKA_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,
|
||||
PIKA_TYPE_CONTROLLER)
|
||||
|
||||
|
||||
G_MODULE_EXPORT const PikaModuleInfo *
|
||||
pika_module_query (GTypeModule *module)
|
||||
{
|
||||
return &linux_input_info;
|
||||
}
|
||||
|
||||
G_MODULE_EXPORT gboolean
|
||||
pika_module_register (GTypeModule *module)
|
||||
{
|
||||
pika_input_device_store_register_types (module);
|
||||
controller_linux_input_register_type (module);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
controller_linux_input_class_init (ControllerLinuxInputClass *klass)
|
||||
{
|
||||
PikaControllerClass *controller_class = PIKA_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,
|
||||
PIKA_CONFIG_PARAM_FLAGS));
|
||||
#ifdef HAVE_LIBGUDEV
|
||||
g_object_class_install_property (object_class, PROP_DEVICE_STORE,
|
||||
g_param_spec_object ("device-values",
|
||||
NULL, NULL,
|
||||
PIKA_TYPE_INPUT_DEVICE_STORE,
|
||||
G_PARAM_READABLE));
|
||||
#endif
|
||||
|
||||
controller_class->name = _("Linux Input");
|
||||
controller_class->help_id = "pika-controller-linux-input";
|
||||
controller_class->icon_name = PIKA_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 = pika_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 (PikaController *controller)
|
||||
{
|
||||
return G_N_ELEMENTS (key_events) + G_N_ELEMENTS (rel_events);
|
||||
}
|
||||
|
||||
static const gchar *
|
||||
linux_input_get_event_name (PikaController *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 (PikaController *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 = pika_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 = pika_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))
|
||||
{
|
||||
PikaController *controller = PIKA_CONTROLLER (data);
|
||||
PikaControllerEvent 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 = PIKA_CONTROLLER_EVENT_TRIGGER;
|
||||
cevent.any.source = controller;
|
||||
cevent.any.event_id = i;
|
||||
|
||||
pika_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 = PIKA_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);
|
||||
}
|
||||
|
||||
pika_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;
|
||||
}
|
897
modules/controller-midi.c
Normal file
897
modules/controller-midi.c
Normal file
@ -0,0 +1,897 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* 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 "libpikaconfig/pikaconfig.h"
|
||||
#include "libpikamodule/pikamodule.h"
|
||||
#include "libpikawidgets/pikawidgets.h"
|
||||
|
||||
#define PIKA_ENABLE_CONTROLLER_UNDER_CONSTRUCTION
|
||||
#include "libpikawidgets/pikacontroller.h"
|
||||
|
||||
#include "libpika/libpika-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
|
||||
{
|
||||
PikaController 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
|
||||
{
|
||||
PikaControllerClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
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 (PikaController *controller);
|
||||
static const gchar * midi_get_event_name (PikaController *controller,
|
||||
gint event_id);
|
||||
static const gchar * midi_get_event_blurb (PikaController *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 PikaModuleInfo midi_info =
|
||||
{
|
||||
PIKA_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,
|
||||
PIKA_TYPE_CONTROLLER)
|
||||
|
||||
static MidiEvent midi_events[128 + 128 + 128];
|
||||
|
||||
|
||||
G_MODULE_EXPORT const PikaModuleInfo *
|
||||
pika_module_query (GTypeModule *module)
|
||||
{
|
||||
return &midi_info;
|
||||
}
|
||||
|
||||
G_MODULE_EXPORT gboolean
|
||||
pika_module_register (GTypeModule *module)
|
||||
{
|
||||
controller_midi_register_type (module);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
controller_midi_class_init (ControllerMidiClass *klass)
|
||||
{
|
||||
PikaControllerClass *controller_class = PIKA_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,
|
||||
PIKA_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,
|
||||
PIKA_CONFIG_PARAM_FLAGS));
|
||||
|
||||
controller_class->name = _("MIDI");
|
||||
controller_class->help_id = "pika-controller-midi";
|
||||
controller_class->icon_name = PIKA_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 (PikaController *controller)
|
||||
{
|
||||
return 128 + 128 + 128;
|
||||
}
|
||||
|
||||
static const gchar *
|
||||
midi_get_event_name (PikaController *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 (PikaController *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, _("PIKA"));
|
||||
ret = snd_seq_create_simple_port (midi->sequencer,
|
||||
_("PIKA 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)
|
||||
{
|
||||
PikaControllerEvent event = { 0, };
|
||||
|
||||
event.any.type = PIKA_CONTROLLER_EVENT_VALUE;
|
||||
event.any.source = PIKA_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);
|
||||
|
||||
pika_controller_event (PIKA_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 */
|
235
modules/display-filter-aces-rrt.c
Normal file
235
modules/display-filter-aces-rrt.c
Normal file
@ -0,0 +1,235 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 2018 Øyvind Kolås <pippin@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 "libpikacolor/pikacolor.h"
|
||||
#include "libpikaconfig/pikaconfig.h"
|
||||
#include "libpikamath/pikamath.h"
|
||||
#include "libpikamodule/pikamodule.h"
|
||||
#include "libpikawidgets/pikawidgets.h"
|
||||
|
||||
#include "libpika/libpika-intl.h"
|
||||
|
||||
#define DEFAULT_EXPOSURE 0.0
|
||||
|
||||
#define CDISPLAY_TYPE_ACES_RRT (cdisplay_aces_rrt_get_type ())
|
||||
#define CDISPLAY_ACES_RRT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CDISPLAY_TYPE_ACES_RRT, CdisplayAcesRRT))
|
||||
#define CDISPLAY_ACES_RRT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CDISPLAY_TYPE_ACES_RRT, CdisplayAcesRRTClass))
|
||||
#define CDISPLAY_IS_ACES_RRT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CDISPLAY_TYPE_ACES_RRT))
|
||||
#define CDISPLAY_IS_ACES_RRT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CDISPLAY_TYPE_ACES_RRT))
|
||||
|
||||
|
||||
typedef struct _CdisplayAcesRRT CdisplayAcesRRT;
|
||||
typedef struct _CdisplayAcesRRTClass CdisplayAcesRRTClass;
|
||||
|
||||
struct _CdisplayAcesRRT
|
||||
{
|
||||
PikaColorDisplay parent_instance;
|
||||
|
||||
gdouble exposure;
|
||||
};
|
||||
|
||||
struct _CdisplayAcesRRTClass
|
||||
{
|
||||
PikaColorDisplayClass parent_instance;
|
||||
};
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_EXPOSURE
|
||||
};
|
||||
|
||||
|
||||
GType cdisplay_aces_rrt_get_type (void);
|
||||
|
||||
static void cdisplay_aces_rrt_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec);
|
||||
static void cdisplay_aces_rrt_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec);
|
||||
|
||||
static void cdisplay_aces_rrt_convert_buffer (PikaColorDisplay *display,
|
||||
GeglBuffer *buffer,
|
||||
GeglRectangle *area);
|
||||
static void cdisplay_aces_rrt_set_exposure (CdisplayAcesRRT *aces_rrt,
|
||||
gdouble value);
|
||||
|
||||
|
||||
static const PikaModuleInfo cdisplay_aces_rrt_info =
|
||||
{
|
||||
PIKA_MODULE_ABI_VERSION,
|
||||
N_("ACES RRT (RRT = Reference Rendering Transform). An HDR to SDR proof color display filter, using a luminance-only approximation of the ACES RRT, a pre-defined filmic look to be used before ODT (display or output space ICC profile)"),
|
||||
"Øyvind Kolås <pippin@gimp.org>",
|
||||
"v0.1",
|
||||
"(c) 2018, released under the LGPLv2+",
|
||||
"July 17, 2018"
|
||||
};
|
||||
|
||||
|
||||
G_DEFINE_DYNAMIC_TYPE (CdisplayAcesRRT, cdisplay_aces_rrt,
|
||||
PIKA_TYPE_COLOR_DISPLAY)
|
||||
|
||||
|
||||
G_MODULE_EXPORT const PikaModuleInfo *
|
||||
pika_module_query (GTypeModule *module)
|
||||
{
|
||||
return &cdisplay_aces_rrt_info;
|
||||
}
|
||||
|
||||
G_MODULE_EXPORT gboolean
|
||||
pika_module_register (GTypeModule *module)
|
||||
{
|
||||
cdisplay_aces_rrt_register_type (module);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
cdisplay_aces_rrt_class_init (CdisplayAcesRRTClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
PikaColorDisplayClass *display_class = PIKA_COLOR_DISPLAY_CLASS (klass);
|
||||
|
||||
object_class->get_property = cdisplay_aces_rrt_get_property;
|
||||
object_class->set_property = cdisplay_aces_rrt_set_property;
|
||||
|
||||
PIKA_CONFIG_PROP_DOUBLE (object_class, PROP_EXPOSURE,
|
||||
"exposure",
|
||||
_("Pre-transform change in stops"),
|
||||
NULL,
|
||||
-10.0, 10.0, DEFAULT_EXPOSURE,
|
||||
1);
|
||||
|
||||
display_class->name = _("Aces RRT");
|
||||
display_class->help_id = "pika-colordisplay-aces-rrt";
|
||||
display_class->icon_name = PIKA_ICON_DISPLAY_FILTER_GAMMA;
|
||||
|
||||
display_class->convert_buffer = cdisplay_aces_rrt_convert_buffer;
|
||||
}
|
||||
|
||||
static void
|
||||
cdisplay_aces_rrt_class_finalize (CdisplayAcesRRTClass *klass)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
cdisplay_aces_rrt_init (CdisplayAcesRRT *aces_rrt)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
cdisplay_aces_rrt_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
CdisplayAcesRRT *aces_rrt = CDISPLAY_ACES_RRT (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_EXPOSURE:
|
||||
g_value_set_double (value, aces_rrt->exposure);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cdisplay_aces_rrt_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
CdisplayAcesRRT *aces_rrt = CDISPLAY_ACES_RRT (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_EXPOSURE:
|
||||
cdisplay_aces_rrt_set_exposure (aces_rrt, g_value_get_double (value));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline float aces_aces_rrt (float x)
|
||||
{
|
||||
/*
|
||||
Approximation of the ACES aces_rrt HDR to SDR mapping from:
|
||||
https://github.com/TheRealMJP/BakingLab/blob/master/BakingLab/ACES.hlsl
|
||||
*/
|
||||
float a = x * (x + 0.0245786f) - 0.000090537f;
|
||||
float b = x * (0.983729f * x + 0.4329510f) + 0.238081f;
|
||||
return a / b;
|
||||
}
|
||||
|
||||
static void
|
||||
cdisplay_aces_rrt_convert_buffer (PikaColorDisplay *display,
|
||||
GeglBuffer *buffer,
|
||||
GeglRectangle *area)
|
||||
{
|
||||
CdisplayAcesRRT *filter = CDISPLAY_ACES_RRT (display);
|
||||
GeglBufferIterator *iter;
|
||||
float gain = 1.0f / exp2f(-filter->exposure);
|
||||
|
||||
iter = gegl_buffer_iterator_new (buffer, area, 0,
|
||||
babl_format ("RGBA 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 = aces_aces_rrt (*data * gain); data++;
|
||||
*data = aces_aces_rrt (*data * gain); data++;
|
||||
*data = aces_aces_rrt (*data * gain); data++;
|
||||
data++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cdisplay_aces_rrt_set_exposure (CdisplayAcesRRT *aces_rrt,
|
||||
gdouble value)
|
||||
{
|
||||
if (value != aces_rrt->exposure)
|
||||
{
|
||||
aces_rrt->exposure = value;
|
||||
|
||||
g_object_notify (G_OBJECT (aces_rrt), "exposure");
|
||||
pika_color_display_changed (PIKA_COLOR_DISPLAY (aces_rrt));
|
||||
}
|
||||
}
|
502
modules/display-filter-clip-warning.c
Normal file
502
modules/display-filter-clip-warning.c
Normal file
@ -0,0 +1,502 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
*
|
||||
* 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 "libpikabase/pikabase.h"
|
||||
#include "libpikacolor/pikacolor.h"
|
||||
#include "libpikaconfig/pikaconfig.h"
|
||||
#include "libpikamath/pikamath.h"
|
||||
#include "libpikamodule/pikamodule.h"
|
||||
#include "libpikawidgets/pikawidgets.h"
|
||||
|
||||
#include "libpika/libpika-intl.h"
|
||||
|
||||
|
||||
#define DEFAULT_SHADOWS_COLOR (&(PikaRGB) {0.25, 0.25, 1.00, 1.00})
|
||||
#define DEFAULT_HIGHLIGHTS_COLOR (&(PikaRGB) {1.00, 0.25, 0.25, 1.00})
|
||||
#define DEFAULT_BOGUS_COLOR (&(PikaRGB) {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
|
||||
{
|
||||
PikaColorDisplay parent_instance;
|
||||
|
||||
gboolean show_shadows;
|
||||
PikaRGB shadows_color;
|
||||
gboolean show_highlights;
|
||||
PikaRGB highlights_color;
|
||||
gboolean show_bogus;
|
||||
PikaRGB bogus_color;
|
||||
gboolean include_alpha;
|
||||
gboolean include_transparent;
|
||||
|
||||
gfloat colors[8][2][4];
|
||||
};
|
||||
|
||||
struct _CdisplayClipWarningClass
|
||||
{
|
||||
PikaColorDisplayClass 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
|
||||
};
|
||||
|
||||
|
||||
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 (PikaColorDisplay *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 PikaModuleInfo cdisplay_clip_warning_info =
|
||||
{
|
||||
PIKA_MODULE_ABI_VERSION,
|
||||
/* Translators: "Clip Warning" is the name of a (color) display filter
|
||||
* that highlights pixels outside of the color space range.
|
||||
* Shown as a label description. */
|
||||
N_("Clip warning color display filter"),
|
||||
"Ell",
|
||||
"v1.0",
|
||||
"(c) 2017, released under the GPL",
|
||||
"2017"
|
||||
};
|
||||
|
||||
|
||||
G_DEFINE_DYNAMIC_TYPE (CdisplayClipWarning, cdisplay_clip_warning,
|
||||
PIKA_TYPE_COLOR_DISPLAY)
|
||||
|
||||
|
||||
G_MODULE_EXPORT const PikaModuleInfo *
|
||||
pika_module_query (GTypeModule *module)
|
||||
{
|
||||
return &cdisplay_clip_warning_info;
|
||||
}
|
||||
|
||||
G_MODULE_EXPORT gboolean
|
||||
pika_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);
|
||||
PikaColorDisplayClass *display_class = PIKA_COLOR_DISPLAY_CLASS (klass);
|
||||
|
||||
object_class->get_property = cdisplay_clip_warning_get_property;
|
||||
object_class->set_property = cdisplay_clip_warning_set_property;
|
||||
|
||||
PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_SHADOWS,
|
||||
"show-shadows",
|
||||
_("Show shadows"),
|
||||
_("Show warning for pixels with a negative component"),
|
||||
TRUE,
|
||||
PIKA_PARAM_STATIC_STRINGS);
|
||||
|
||||
PIKA_CONFIG_PROP_RGB (object_class, PROP_SHADOWS_COLOR,
|
||||
"shadows-color",
|
||||
_("Shadows color"),
|
||||
_("Shadows warning color"),
|
||||
FALSE,
|
||||
DEFAULT_SHADOWS_COLOR,
|
||||
PIKA_PARAM_STATIC_STRINGS |
|
||||
PIKA_CONFIG_PARAM_DEFAULTS);
|
||||
|
||||
gegl_param_spec_set_property_key (
|
||||
g_object_class_find_property (G_OBJECT_CLASS (klass), "shadows-color"),
|
||||
"sensitive", "show-shadows");
|
||||
|
||||
PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_HIGHLIGHTS,
|
||||
"show-highlights",
|
||||
_("Show highlights"),
|
||||
_("Show warning for pixels with a component greater than one"),
|
||||
TRUE,
|
||||
PIKA_PARAM_STATIC_STRINGS);
|
||||
|
||||
PIKA_CONFIG_PROP_RGB (object_class, PROP_HIGHLIGHTS_COLOR,
|
||||
"highlights-color",
|
||||
_("Highlights color"),
|
||||
_("Highlights warning color"),
|
||||
FALSE,
|
||||
DEFAULT_HIGHLIGHTS_COLOR,
|
||||
PIKA_PARAM_STATIC_STRINGS |
|
||||
PIKA_CONFIG_PARAM_DEFAULTS);
|
||||
|
||||
gegl_param_spec_set_property_key (
|
||||
g_object_class_find_property (G_OBJECT_CLASS (klass), "highlights-color"),
|
||||
"sensitive", "show-highlights");
|
||||
|
||||
PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_BOGUS,
|
||||
"show-bogus",
|
||||
_("Show bogus"),
|
||||
_("Show warning for pixels with an infinite or NaN component"),
|
||||
TRUE,
|
||||
PIKA_PARAM_STATIC_STRINGS);
|
||||
|
||||
PIKA_CONFIG_PROP_RGB (object_class, PROP_BOGUS_COLOR,
|
||||
"bogus-color",
|
||||
_("Bogus color"),
|
||||
_("Bogus warning color"),
|
||||
FALSE,
|
||||
DEFAULT_BOGUS_COLOR,
|
||||
PIKA_PARAM_STATIC_STRINGS |
|
||||
PIKA_CONFIG_PARAM_DEFAULTS);
|
||||
|
||||
gegl_param_spec_set_property_key (
|
||||
g_object_class_find_property (G_OBJECT_CLASS (klass), "bogus-color"),
|
||||
"sensitive", "show-bogus");
|
||||
|
||||
PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_INCLUDE_ALPHA,
|
||||
"include-alpha",
|
||||
_("Include alpha component"),
|
||||
_("Include alpha component in the warning"),
|
||||
TRUE,
|
||||
PIKA_PARAM_STATIC_STRINGS);
|
||||
|
||||
PIKA_CONFIG_PROP_BOOLEAN (object_class, PROP_INCLUDE_TRANSPARENT,
|
||||
"include-transparent",
|
||||
_("Include transparent pixels"),
|
||||
_("Include fully transparent pixels in the warning"),
|
||||
TRUE,
|
||||
PIKA_PARAM_STATIC_STRINGS);
|
||||
|
||||
display_class->name = _("Clip Warning");
|
||||
display_class->help_id = "pika-colordisplay-clip-warning";
|
||||
display_class->icon_name = PIKA_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 (PikaColorDisplay *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);
|
||||
pika_color_display_changed (PIKA_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;
|
||||
}
|
||||
}
|
499
modules/display-filter-color-blind.c
Normal file
499
modules/display-filter-color-blind.c
Normal file
@ -0,0 +1,499 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* 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 "libpikacolor/pikacolor.h"
|
||||
#include "libpikaconfig/pikaconfig.h"
|
||||
#include "libpikamath/pikamath.h"
|
||||
#include "libpikamodule/pikamodule.h"
|
||||
#include "libpikawidgets/pikawidgets.h"
|
||||
|
||||
#include "libpika/libpika-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 PikaEnumDesc 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
|
||||
{
|
||||
PikaColorDisplay parent_instance;
|
||||
|
||||
ColorblindDeficiencyType type;
|
||||
|
||||
gfloat a1, b1, c1;
|
||||
gfloat a2, b2, c2;
|
||||
gfloat inflection;
|
||||
};
|
||||
|
||||
struct _CdisplayColorblindClass
|
||||
{
|
||||
PikaColorDisplayClass parent_instance;
|
||||
};
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_TYPE
|
||||
};
|
||||
|
||||
|
||||
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 (PikaColorDisplay *display,
|
||||
GeglBuffer *buffer,
|
||||
GeglRectangle *area);
|
||||
static void cdisplay_colorblind_changed (PikaColorDisplay *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 PikaModuleInfo cdisplay_colorblind_info =
|
||||
{
|
||||
PIKA_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,
|
||||
PIKA_TYPE_COLOR_DISPLAY)
|
||||
|
||||
static GType cdisplay_colorblind_deficiency_type_type = 0;
|
||||
|
||||
|
||||
G_MODULE_EXPORT const PikaModuleInfo *
|
||||
pika_module_query (GTypeModule *module)
|
||||
{
|
||||
return &cdisplay_colorblind_info;
|
||||
}
|
||||
|
||||
G_MODULE_EXPORT gboolean
|
||||
pika_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);
|
||||
|
||||
pika_type_set_translation_domain (cdisplay_colorblind_deficiency_type_type,
|
||||
GETTEXT_PACKAGE "-libpika");
|
||||
pika_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);
|
||||
PikaColorDisplayClass *display_class = PIKA_COLOR_DISPLAY_CLASS (klass);
|
||||
|
||||
object_class->get_property = cdisplay_colorblind_get_property;
|
||||
object_class->set_property = cdisplay_colorblind_set_property;
|
||||
|
||||
PIKA_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 = "pika-colordisplay-colorblind";
|
||||
display_class->icon_name = PIKA_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 (PikaColorDisplay *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 (PikaColorDisplay *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");
|
||||
pika_color_display_changed (PIKA_COLOR_DISPLAY (colorblind));
|
||||
}
|
||||
}
|
232
modules/display-filter-gamma.c
Normal file
232
modules/display-filter-gamma.c
Normal file
@ -0,0 +1,232 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* 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 "libpikacolor/pikacolor.h"
|
||||
#include "libpikaconfig/pikaconfig.h"
|
||||
#include "libpikamath/pikamath.h"
|
||||
#include "libpikamodule/pikamodule.h"
|
||||
#include "libpikawidgets/pikawidgets.h"
|
||||
|
||||
#include "libpika/libpika-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
|
||||
{
|
||||
PikaColorDisplay parent_instance;
|
||||
|
||||
gdouble gamma;
|
||||
};
|
||||
|
||||
struct _CdisplayGammaClass
|
||||
{
|
||||
PikaColorDisplayClass parent_instance;
|
||||
};
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_GAMMA
|
||||
};
|
||||
|
||||
|
||||
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 (PikaColorDisplay *display,
|
||||
GeglBuffer *buffer,
|
||||
GeglRectangle *area);
|
||||
static void cdisplay_gamma_set_gamma (CdisplayGamma *gamma,
|
||||
gdouble value);
|
||||
|
||||
|
||||
static const PikaModuleInfo cdisplay_gamma_info =
|
||||
{
|
||||
PIKA_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,
|
||||
PIKA_TYPE_COLOR_DISPLAY)
|
||||
|
||||
|
||||
G_MODULE_EXPORT const PikaModuleInfo *
|
||||
pika_module_query (GTypeModule *module)
|
||||
{
|
||||
return &cdisplay_gamma_info;
|
||||
}
|
||||
|
||||
G_MODULE_EXPORT gboolean
|
||||
pika_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);
|
||||
PikaColorDisplayClass *display_class = PIKA_COLOR_DISPLAY_CLASS (klass);
|
||||
|
||||
object_class->get_property = cdisplay_gamma_get_property;
|
||||
object_class->set_property = cdisplay_gamma_set_property;
|
||||
|
||||
PIKA_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 = "pika-colordisplay-gamma";
|
||||
display_class->icon_name = PIKA_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 (PikaColorDisplay *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");
|
||||
pika_color_display_changed (PIKA_COLOR_DISPLAY (gamma));
|
||||
}
|
||||
}
|
232
modules/display-filter-high-contrast.c
Normal file
232
modules/display-filter-high-contrast.c
Normal file
@ -0,0 +1,232 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* 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 "libpikacolor/pikacolor.h"
|
||||
#include "libpikaconfig/pikaconfig.h"
|
||||
#include "libpikamath/pikamath.h"
|
||||
#include "libpikamodule/pikamodule.h"
|
||||
#include "libpikawidgets/pikawidgets.h"
|
||||
|
||||
#include "libpika/libpika-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
|
||||
{
|
||||
PikaColorDisplay parent_instance;
|
||||
|
||||
gdouble contrast;
|
||||
};
|
||||
|
||||
struct _CdisplayContrastClass
|
||||
{
|
||||
PikaColorDisplayClass parent_instance;
|
||||
};
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_CONTRAST
|
||||
};
|
||||
|
||||
|
||||
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 (PikaColorDisplay *display,
|
||||
GeglBuffer *buffer,
|
||||
GeglRectangle *area);
|
||||
static void cdisplay_contrast_set_contrast (CdisplayContrast *contrast,
|
||||
gdouble value);
|
||||
|
||||
|
||||
static const PikaModuleInfo cdisplay_contrast_info =
|
||||
{
|
||||
PIKA_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,
|
||||
PIKA_TYPE_COLOR_DISPLAY)
|
||||
|
||||
|
||||
G_MODULE_EXPORT const PikaModuleInfo *
|
||||
pika_module_query (GTypeModule *module)
|
||||
{
|
||||
return &cdisplay_contrast_info;
|
||||
}
|
||||
|
||||
G_MODULE_EXPORT gboolean
|
||||
pika_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);
|
||||
PikaColorDisplayClass *display_class = PIKA_COLOR_DISPLAY_CLASS (klass);
|
||||
|
||||
object_class->get_property = cdisplay_contrast_get_property;
|
||||
object_class->set_property = cdisplay_contrast_set_property;
|
||||
|
||||
PIKA_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 = "pika-colordisplay-contrast";
|
||||
display_class->icon_name = PIKA_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 (PikaColorDisplay *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");
|
||||
pika_color_display_changed (PIKA_COLOR_DISPLAY (contrast));
|
||||
}
|
||||
}
|
98
modules/meson.build
Normal file
98
modules/meson.build
Normal file
@ -0,0 +1,98 @@
|
||||
|
||||
modules_deps = [
|
||||
gtk3, babl, gegl, math,
|
||||
]
|
||||
|
||||
color_selector_libs = [
|
||||
libpikacolor,
|
||||
libpikaconfig,
|
||||
libpikamodule,
|
||||
libpikawidgets,
|
||||
]
|
||||
controller_libs = [
|
||||
libpikamodule,
|
||||
libpikawidgets,
|
||||
]
|
||||
display_filter_libs = [
|
||||
libpikabase,
|
||||
libpikacolor,
|
||||
libpikaconfig,
|
||||
libpikamodule,
|
||||
libpikawidgets,
|
||||
]
|
||||
|
||||
# Name, Sources, deps, link.
|
||||
modules = [
|
||||
{
|
||||
'name': 'color-selector-cmyk',
|
||||
'link': color_selector_libs,
|
||||
}, {
|
||||
'name': 'color-selector-water',
|
||||
'link': color_selector_libs,
|
||||
}, {
|
||||
'name': 'color-selector-wheel',
|
||||
'srcs': [ 'color-selector-wheel.c', 'pikacolorwheel.c', ],
|
||||
'link': color_selector_libs,
|
||||
}, {
|
||||
'name': 'display-filter-clip-warning',
|
||||
'link': display_filter_libs,
|
||||
}, {
|
||||
'name': 'display-filter-color-blind',
|
||||
'link': display_filter_libs,
|
||||
}, {
|
||||
'name': 'display-filter-aces-rrt',
|
||||
'link': display_filter_libs,
|
||||
}, {
|
||||
'name': 'display-filter-gamma',
|
||||
'link': display_filter_libs,
|
||||
}, {
|
||||
'name': 'display-filter-high-contrast',
|
||||
'link': display_filter_libs,
|
||||
},
|
||||
]
|
||||
|
||||
if have_linuxinput
|
||||
modules += {
|
||||
'name': 'controller-linux-input',
|
||||
'srcs': [ 'controller-linux-input.c', 'pikainputdevicestore-gudev.c', ],
|
||||
'deps': gudev,
|
||||
'link': controller_libs,
|
||||
}
|
||||
endif
|
||||
|
||||
if directx.found()
|
||||
modules += {
|
||||
'name': 'controller-dx-dinput',
|
||||
'srcs': [ 'controller-dx-dinput.c', 'pikainputdevicestore-dx.c', ],
|
||||
'deps': directx,
|
||||
'link': [ controller_libs, ],
|
||||
'link-args': [ '-lrpcrt4', ],
|
||||
}
|
||||
endif
|
||||
|
||||
if not platform_windows
|
||||
modules += {
|
||||
'name': 'controller-midi',
|
||||
'deps': alsa,
|
||||
'link': controller_libs,
|
||||
}
|
||||
endif
|
||||
|
||||
|
||||
foreach module : modules
|
||||
name = module.get('name')
|
||||
srcs = module.get('srcs', name + '.c')
|
||||
deps = module.get('deps', [])
|
||||
link = module.get('link', [])
|
||||
link_args = module.get('link-args', [])
|
||||
|
||||
library(name,
|
||||
srcs,
|
||||
include_directories: rootInclude,
|
||||
dependencies: modules_deps + [ deps ],
|
||||
link_with: link,
|
||||
link_args: link_args,
|
||||
install: true,
|
||||
install_dir: pikaplugindir / 'modules',
|
||||
)
|
||||
endforeach
|
1573
modules/pikacolorwheel.c
Normal file
1573
modules/pikacolorwheel.c
Normal file
File diff suppressed because it is too large
Load Diff
95
modules/pikacolorwheel.h
Normal file
95
modules/pikacolorwheel.h
Normal file
@ -0,0 +1,95 @@
|
||||
/* 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 __PIKA_COLOR_WHEEL_H__
|
||||
#define __PIKA_COLOR_WHEEL_H__
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define PIKA_TYPE_COLOR_WHEEL (pika_color_wheel_get_type ())
|
||||
#define PIKA_COLOR_WHEEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_COLOR_WHEEL, PikaColorWheel))
|
||||
#define PIKA_COLOR_WHEEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIKA_TYPE_COLOR_WHEEL, PikaColorWheelClass))
|
||||
#define PIKA_IS_COLOR_WHEEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_COLOR_WHEEL))
|
||||
#define PIKA_IS_COLOR_WHEEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIKA_TYPE_COLOR_WHEEL))
|
||||
#define PIKA_COLOR_WHEEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIKA_TYPE_COLOR_WHEEL, PikaColorWheelClass))
|
||||
|
||||
|
||||
typedef struct _PikaColorWheel PikaColorWheel;
|
||||
typedef struct _PikaColorWheelClass PikaColorWheelClass;
|
||||
|
||||
struct _PikaColorWheel
|
||||
{
|
||||
GtkWidget parent_instance;
|
||||
};
|
||||
|
||||
struct _PikaColorWheelClass
|
||||
{
|
||||
GtkWidgetClass parent_class;
|
||||
|
||||
/* Notification signals */
|
||||
void (* changed) (PikaColorWheel *wheel);
|
||||
|
||||
/* Keybindings */
|
||||
void (* move) (PikaColorWheel *wheel,
|
||||
GtkDirectionType type);
|
||||
|
||||
/* Padding for future expansion */
|
||||
void (*_pika_reserved1) (void);
|
||||
void (*_pika_reserved2) (void);
|
||||
void (*_pika_reserved3) (void);
|
||||
void (*_pika_reserved4) (void);
|
||||
};
|
||||
|
||||
|
||||
void color_wheel_register_type (GTypeModule *module);
|
||||
|
||||
GType pika_color_wheel_get_type (void) G_GNUC_CONST;
|
||||
GtkWidget * pika_color_wheel_new (void);
|
||||
|
||||
void pika_color_wheel_set_color (PikaColorWheel *wheel,
|
||||
double h,
|
||||
double s,
|
||||
double v);
|
||||
void pika_color_wheel_get_color (PikaColorWheel *wheel,
|
||||
gdouble *h,
|
||||
gdouble *s,
|
||||
gdouble *v);
|
||||
|
||||
void pika_color_wheel_set_ring_fraction (PikaColorWheel *wheel,
|
||||
gdouble fraction);
|
||||
gdouble pika_color_wheel_get_ring_fraction (PikaColorWheel *wheel);
|
||||
|
||||
void pika_color_wheel_set_color_config (PikaColorWheel *wheel,
|
||||
PikaColorConfig *config);
|
||||
|
||||
gboolean pika_color_wheel_is_adjusting (PikaColorWheel *wheel);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __PIKA_COLOR_WHEEL_H__ */
|
485
modules/pikainputdevicestore-dx.c
Normal file
485
modules/pikainputdevicestore-dx.c
Normal file
@ -0,0 +1,485 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* pikainputdevicestore-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 "libpikamodule/pikamodule.h"
|
||||
|
||||
#include "pikainputdevicestore.h"
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
COLUMN_GUID,
|
||||
COLUMN_LABEL,
|
||||
COLUMN_IDEVICE,
|
||||
NUM_COLUMNS
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
DEVICE_ADDED,
|
||||
DEVICE_REMOVED,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
typedef struct _PikaInputDeviceStoreClass PikaInputDeviceStoreClass;
|
||||
|
||||
struct _PikaInputDeviceStore
|
||||
{
|
||||
GtkListStore parent_instance;
|
||||
|
||||
GdkWindow *window;
|
||||
|
||||
LPDIRECTINPUT8W directinput8;
|
||||
|
||||
GError *error;
|
||||
};
|
||||
|
||||
|
||||
struct _PikaInputDeviceStoreClass
|
||||
{
|
||||
GtkListStoreClass parent_class;
|
||||
|
||||
void (* device_added) (PikaInputDeviceStore *store,
|
||||
const gchar *udi);
|
||||
void (* device_removed) (PikaInputDeviceStore *store,
|
||||
const gchar *udi);
|
||||
};
|
||||
|
||||
|
||||
static void pika_input_device_store_finalize (GObject *object);
|
||||
|
||||
static gboolean pika_input_device_store_add (PikaInputDeviceStore *store,
|
||||
const GUID *guid);
|
||||
static gboolean pika_input_device_store_remove (PikaInputDeviceStore *store,
|
||||
const gchar *udi);
|
||||
|
||||
|
||||
G_DEFINE_DYNAMIC_TYPE (PikaInputDeviceStore, pika_input_device_store,
|
||||
GTK_TYPE_LIST_STORE)
|
||||
|
||||
static guint store_signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
|
||||
void
|
||||
pika_input_device_store_register_types (GTypeModule *module)
|
||||
{
|
||||
pika_input_device_store_register_type (module);
|
||||
}
|
||||
|
||||
static void
|
||||
pika_input_device_store_class_init (PikaInputDeviceStoreClass *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 (PikaInputDeviceStoreClass, device_added),
|
||||
NULL, NULL, NULL,
|
||||
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 (PikaInputDeviceStoreClass, device_removed),
|
||||
NULL, NULL, NULL,
|
||||
G_TYPE_NONE, 1, G_TYPE_STRING);
|
||||
|
||||
object_class->finalize = pika_input_device_store_finalize;
|
||||
|
||||
klass->device_added = NULL;
|
||||
klass->device_removed = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
pika_input_device_store_class_finalize (PikaInputDeviceStoreClass *klass)
|
||||
{
|
||||
}
|
||||
|
||||
static GdkFilterReturn
|
||||
aux_window_filter (GdkXEvent *xevent,
|
||||
GdkEvent *event,
|
||||
gpointer data)
|
||||
{
|
||||
#if 0
|
||||
PikaInputDeviceStore *store = (PikaInputDeviceStore *) 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 (PikaInputDeviceStore *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)
|
||||
{
|
||||
PikaInputDeviceStore *store = (PikaInputDeviceStore *) user_data;
|
||||
|
||||
pika_input_device_store_add (store, &di->guidInstance);
|
||||
|
||||
return DIENUM_CONTINUE;
|
||||
}
|
||||
|
||||
static void
|
||||
pika_input_device_store_init (PikaInputDeviceStore *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 (!GetModuleHandleExW (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
|
||||
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
|
||||
(LPCTSTR) &pika_input_device_store_init,
|
||||
&thismodule))
|
||||
return;
|
||||
|
||||
if ((store->window = create_aux_window (store)) == NULL)
|
||||
{
|
||||
g_set_error_literal (&store->error, PIKA_MODULE_ERROR, PIKA_MODULE_FAILED,
|
||||
"Could not create aux window");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((dinput8 = LoadLibraryW (L"dinput8.dll")) == NULL)
|
||||
{
|
||||
g_set_error_literal (&store->error, PIKA_MODULE_ERROR, PIKA_MODULE_FAILED,
|
||||
"Could not load dinput8.dll");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((p_DirectInput8Create = (t_DirectInput8Create) GetProcAddress (dinput8, "DirectInput8Create")) == NULL)
|
||||
{
|
||||
g_set_error_literal (&store->error, PIKA_MODULE_ERROR, PIKA_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, PIKA_MODULE_ERROR, PIKA_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, PIKA_MODULE_ERROR, PIKA_MODULE_FAILED,
|
||||
"IDirectInput8::EnumDevices failed: %s",
|
||||
g_win32_error_message (hresult));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pika_input_device_store_finalize (GObject *object)
|
||||
{
|
||||
PikaInputDeviceStore *store = PIKA_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 (pika_input_device_store_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
pika_input_device_store_lookup (PikaInputDeviceStore *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
|
||||
pika_input_device_store_insert (PikaInputDeviceStore *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
|
||||
pika_input_device_store_add (PikaInputDeviceStore *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_window_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);
|
||||
pika_input_device_store_insert (store, guidstring, name, didevice8);
|
||||
|
||||
return added;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
pika_input_device_store_remove (PikaInputDeviceStore *store,
|
||||
const gchar *guid)
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
|
||||
if (pika_input_device_store_lookup (store, guid, &iter))
|
||||
{
|
||||
gtk_list_store_remove (GTK_LIST_STORE (store), &iter);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
static void
|
||||
pika_input_device_store_device_added (LibHalContext *ctx,
|
||||
const char *guid)
|
||||
{
|
||||
PikaInputDeviceStore *store = libhal_ctx_get_user_data (ctx);
|
||||
|
||||
if (pika_input_device_store_add (store, udi))
|
||||
{
|
||||
g_signal_emit (store, store_signals[DEVICE_ADDED], 0, udi);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pika_input_device_store_device_removed (LibHalContext *ctx,
|
||||
const char *udi)
|
||||
{
|
||||
PikaInputDeviceStore *store = libhal_ctx_get_user_data (ctx);
|
||||
|
||||
if (pika_input_device_store_remove (store, udi))
|
||||
{
|
||||
g_signal_emit (store, store_signals[DEVICE_REMOVED], 0, udi);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
PikaInputDeviceStore *
|
||||
pika_input_device_store_new (void)
|
||||
{
|
||||
return g_object_new (PIKA_TYPE_INPUT_DEVICE_STORE, NULL);
|
||||
}
|
||||
|
||||
gchar *
|
||||
pika_input_device_store_get_device_file (PikaInputDeviceStore *store,
|
||||
const gchar *udi)
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
GValue value = G_VALUE_INIT;
|
||||
|
||||
g_return_val_if_fail (PIKA_IS_INPUT_DEVICE_STORE (store), NULL);
|
||||
g_return_val_if_fail (udi != NULL, NULL);
|
||||
|
||||
if (! store->directinput8)
|
||||
return NULL;
|
||||
|
||||
if (pika_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 *
|
||||
pika_input_device_store_get_error (PikaInputDeviceStore *store)
|
||||
{
|
||||
g_return_val_if_fail (PIKA_IS_INPUT_DEVICE_STORE (store), NULL);
|
||||
|
||||
return store->error ? g_error_copy (store->error) : NULL;
|
||||
}
|
||||
|
||||
#endif /* HAVE_DX_DINPUT */
|
443
modules/pikainputdevicestore-gudev.c
Normal file
443
modules/pikainputdevicestore-gudev.c
Normal file
@ -0,0 +1,443 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* pikainputdevicestore-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 "pikainputdevicestore.h"
|
||||
|
||||
#include "libpikamodule/pikamodule.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 _PikaInputDeviceStoreClass PikaInputDeviceStoreClass;
|
||||
|
||||
struct _PikaInputDeviceStore
|
||||
{
|
||||
GtkListStore parent_instance;
|
||||
|
||||
GUdevClient *client;
|
||||
GError *error;
|
||||
};
|
||||
|
||||
|
||||
struct _PikaInputDeviceStoreClass
|
||||
{
|
||||
GtkListStoreClass parent_class;
|
||||
|
||||
void (* device_added) (PikaInputDeviceStore *store,
|
||||
const gchar *identifier);
|
||||
void (* device_removed) (PikaInputDeviceStore *store,
|
||||
const gchar *identifier);
|
||||
};
|
||||
|
||||
|
||||
static void pika_input_device_store_finalize (GObject *object);
|
||||
|
||||
static gboolean pika_input_device_store_add (PikaInputDeviceStore *store,
|
||||
GUdevDevice *device);
|
||||
static gboolean pika_input_device_store_remove (PikaInputDeviceStore *store,
|
||||
GUdevDevice *device);
|
||||
|
||||
static void pika_input_device_store_uevent (GUdevClient *client,
|
||||
const gchar *action,
|
||||
GUdevDevice *device,
|
||||
PikaInputDeviceStore *store);
|
||||
|
||||
|
||||
G_DEFINE_DYNAMIC_TYPE (PikaInputDeviceStore, pika_input_device_store,
|
||||
GTK_TYPE_LIST_STORE)
|
||||
|
||||
static guint store_signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
|
||||
void
|
||||
pika_input_device_store_register_types (GTypeModule *module)
|
||||
{
|
||||
pika_input_device_store_register_type (module);
|
||||
}
|
||||
|
||||
static void
|
||||
pika_input_device_store_class_init (PikaInputDeviceStoreClass *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 (PikaInputDeviceStoreClass, device_added),
|
||||
NULL, NULL, NULL,
|
||||
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 (PikaInputDeviceStoreClass, device_removed),
|
||||
NULL, NULL, NULL,
|
||||
G_TYPE_NONE, 1, G_TYPE_STRING);
|
||||
|
||||
object_class->finalize = pika_input_device_store_finalize;
|
||||
|
||||
klass->device_added = NULL;
|
||||
klass->device_removed = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
pika_input_device_store_class_finalize (PikaInputDeviceStoreClass *klass)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
pika_input_device_store_init (PikaInputDeviceStore *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;
|
||||
|
||||
pika_input_device_store_add (store, device);
|
||||
g_object_unref (device);
|
||||
}
|
||||
|
||||
g_list_free (devices);
|
||||
|
||||
g_signal_connect (store->client, "uevent",
|
||||
G_CALLBACK (pika_input_device_store_uevent),
|
||||
store);
|
||||
}
|
||||
|
||||
static void
|
||||
pika_input_device_store_finalize (GObject *object)
|
||||
{
|
||||
PikaInputDeviceStore *store = PIKA_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 (pika_input_device_store_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
pika_input_device_store_lookup (PikaInputDeviceStore *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
|
||||
pika_input_device_store_insert (PikaInputDeviceStore *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
|
||||
pika_input_device_store_add (PikaInputDeviceStore *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 (! pika_input_device_store_lookup (store, name, &unused))
|
||||
{
|
||||
pika_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 (! pika_input_device_store_lookup (store, parent_name,
|
||||
&unused))
|
||||
{
|
||||
pika_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
|
||||
pika_input_device_store_remove (PikaInputDeviceStore *store,
|
||||
GUdevDevice *device)
|
||||
{
|
||||
const gchar *name = g_udev_device_get_sysfs_attr (device, "name");
|
||||
GtkTreeIter iter;
|
||||
|
||||
if (name)
|
||||
{
|
||||
if (pika_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
|
||||
pika_input_device_store_uevent (GUdevClient *client,
|
||||
const gchar *action,
|
||||
GUdevDevice *device,
|
||||
PikaInputDeviceStore *store)
|
||||
{
|
||||
if (! strcmp (action, "add"))
|
||||
{
|
||||
pika_input_device_store_add (store, device);
|
||||
}
|
||||
else if (! strcmp (action, "remove"))
|
||||
{
|
||||
pika_input_device_store_remove (store, device);
|
||||
}
|
||||
}
|
||||
|
||||
PikaInputDeviceStore *
|
||||
pika_input_device_store_new (void)
|
||||
{
|
||||
return g_object_new (PIKA_TYPE_INPUT_DEVICE_STORE, NULL);
|
||||
}
|
||||
|
||||
gchar *
|
||||
pika_input_device_store_get_device_file (PikaInputDeviceStore *store,
|
||||
const gchar *identifier)
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
|
||||
g_return_val_if_fail (PIKA_IS_INPUT_DEVICE_STORE (store), NULL);
|
||||
g_return_val_if_fail (identifier != NULL, NULL);
|
||||
|
||||
if (! store->client)
|
||||
return NULL;
|
||||
|
||||
if (pika_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 *
|
||||
pika_input_device_store_get_error (PikaInputDeviceStore *store)
|
||||
{
|
||||
g_return_val_if_fail (PIKA_IS_INPUT_DEVICE_STORE (store), NULL);
|
||||
|
||||
return store->error ? g_error_copy (store->error) : NULL;
|
||||
}
|
||||
|
||||
#else /* HAVE_LIBGUDEV */
|
||||
|
||||
void
|
||||
pika_input_device_store_register_types (GTypeModule *module)
|
||||
{
|
||||
}
|
||||
|
||||
GType
|
||||
pika_input_device_store_get_type (void)
|
||||
{
|
||||
return G_TYPE_NONE;
|
||||
}
|
||||
|
||||
PikaInputDeviceStore *
|
||||
pika_input_device_store_new (void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gchar *
|
||||
pika_input_device_store_get_device_file (PikaInputDeviceStore *store,
|
||||
const gchar *identifier)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GError *
|
||||
pika_input_device_store_get_error (PikaInputDeviceStore *store)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* HAVE_LIBGUDEV */
|
46
modules/pikainputdevicestore.h
Normal file
46
modules/pikainputdevicestore.h
Normal file
@ -0,0 +1,46 @@
|
||||
/* PIKA - Photo and Image Kooker Application
|
||||
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
||||
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
||||
*
|
||||
* Original copyright, applying to most contents (license remains unchanged):
|
||||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||||
*
|
||||
* pikainputdevicestore.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 __PIKA_INPUT_DEVICE_STORE_H__
|
||||
#define __PIKA_INPUT_DEVICE_STORE_H__
|
||||
|
||||
|
||||
#define PIKA_TYPE_INPUT_DEVICE_STORE (pika_input_device_store_get_type ())
|
||||
#define PIKA_INPUT_DEVICE_STORE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIKA_TYPE_INPUT_DEVICE_STORE, PikaInputDeviceStore))
|
||||
#define PIKA_IS_INPUT_DEVICE_STORE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIKA_TYPE_INPUT_DEVICE_STORE))
|
||||
|
||||
typedef struct _PikaInputDeviceStore PikaInputDeviceStore;
|
||||
|
||||
|
||||
void pika_input_device_store_register_types (GTypeModule *module);
|
||||
|
||||
GType pika_input_device_store_get_type (void);
|
||||
|
||||
PikaInputDeviceStore * pika_input_device_store_new (void);
|
||||
gchar * pika_input_device_store_get_device_file (PikaInputDeviceStore *store,
|
||||
const gchar *udi);
|
||||
GError * pika_input_device_store_get_error (PikaInputDeviceStore *store);
|
||||
|
||||
|
||||
#endif /* __PIKA_INPUT_DEVICE_STORE_H__ */
|
Reference in New Issue
Block a user