Initial checkin of Pika from heckimp

This commit is contained in:
2023-09-25 15:35:21 -07:00
commit 891e999216
6761 changed files with 5240685 additions and 0 deletions

View 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);
}

View 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;
}

View 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);
}

File diff suppressed because it is too large Load Diff

View 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
View 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 */

View 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));
}
}

View 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;
}
}

View 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));
}
}

View 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));
}
}

View 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
View 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

File diff suppressed because it is too large Load Diff

95
modules/pikacolorwheel.h Normal file
View 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__ */

View 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 */

View 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 */

View 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__ */