/* This file is part of darktable, Copyright (C) 2010-2026 darktable developers. darktable 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. darktable 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 darktable. If not, see . */ // our includes go first: #include "bauhaus/bauhaus.h" #include "develop/imageop.h" #include "develop/imageop_gui.h" #include "gui/color_picker_proxy.h" #include "gui/gtk.h" #include "iop/iop_api.h" #include #include // Silence the compiler during dev of new module as we often // need to have temporary unfinished code that will hurt the // compiler. // THIS MUST be REMOVED before submitting a PR. #pragma GCC diagnostic ignored "-Wunused-variable" #pragma GCC diagnostic ignored "-Wunused-but-set-variable" #pragma GCC diagnostic ignored "-Wunused-function" // This is an example implementation of an image operation module that // does nothing useful. It demonstrates how the different functions // work together. To build your own module, take all of the functions // that are mandatory, stripping them of comments. Then add only the // optional functions that are required to implement the functionality // you need. Don't copy default implementations (hint: if you don't // need to change or add anything, you probably don't need the // copy). Make sure you choose descriptive names for your fields and // variables. The ones given here are just examples; rename them. // // To have your module compile and appear in darkroom, add it to // CMakeLists.txt, with add_iop(useless "useless.c") and to // iop_order.c, in the initialisation of legacy_order & v30_order // with: { {XX.0f }, "useless", 0}, // This is the version of the module's parameters, // and includes version information about compile-time dt. // The first released version should be 1. DT_MODULE_INTROSPECTION(3, dt_iop_useless_params_t) // TODO: some build system to support dt-less compilation and translation! // Enums used in params_t can have $DESCRIPTIONs that will be used to // automatically populate a combobox with dt_bauhaus_combobox_from_params. // They are also used in the history changes tooltip. // Combobox options will be presented in the same order as defined here. // These numbers must not be changed when a new version is introduced. typedef enum dt_iop_useless_type_t { DT_USELESS_NONE = 0, // $DESCRIPTION: "no" DT_USELESS_FIRST = 1, // $DESCRIPTION: "first option" DT_USELESS_SECOND = 2, // $DESCRIPTION: "second one" } dt_iop_useless_type_t; typedef struct dt_iop_useless_params_t { // The parameters defined here fully record the state of the module // and are stored (as a serialized binary blob) into the db. Make // sure everything is in here does not depend on temporary memory // (pointers etc). This struct defines the layout of self->params // and self->default_params. You should keep changes to this struct // to a minimum. If you have to change this struct, it will break // user data bases, and you have to increment the version of // DT_MODULE_INTROSPECTION(VERSION) above and provide a // legacy_params upgrade path! // // Tags in the comments get picked up by the introspection framework // and are used in gui_init to set range and labels (for widgets and // history) and value checks before commit_params. If no explicit // init() is specified, the default implementation uses $DEFAULT // tags to initialise self->default_params, which is then used in // gui_init to set widget defaults. // // These field names are just examples; chose meaningful ones! For // performance reasons, align to 4 byte boundaries (use gboolean, // not bool). int checker_scale; // $MIN: 0 $MAX: 10 $DEFAULT: 1 $DESCRIPTION: "size" float factor; // $MIN: -5.0 $MAX: 5.0 $DEFAULT: 0 gboolean check; // $DESCRIPTION: "checkbox option" dt_iop_useless_type_t method; // $DEFAULT: DT_USELESS_SECOND $DESCRIPTION: "parameter choices" } dt_iop_useless_params_t; typedef struct dt_iop_useless_gui_data_t { // Whatever you need to make your gui happy and provide access to // widgets between gui_init, gui_update etc. Stored in // self->gui_data while in darkroom. To permanently store per-user // gui configuration settings, you could use dt_conf_set/_get. GtkWidget *scale, *factor, *check, *method, *extra; // this is needed by gui_update } dt_iop_useless_gui_data_t; typedef struct dt_iop_useless_global_data_t { // This is optionally stored in self->global_data // and can be used to alloc globally needed stuff // which is needed in gui mode and during processing. // We don't need it for this example (as for most dt plugins). } dt_iop_useless_global_data_t; // this returns a translatable name const char *name() { // make sure you put all your translatable strings into _() ! return _("silly example"); } // a routine returning the description of the module. this is // displayed when the mouse is over the module's header. const char **description(dt_iop_module_t *self) { return dt_iop_set_description (self, // first line is the general description for the module _("adjustment of the RGB color primaries for color grading"), // the goal: _("corrective or creative"), // the input: _("linear, RGB, scene-referred"), // the internal working: _("linear, RGB"), // the output: _("linear, RGB, scene-referred")); } // some additional flags (self explanatory i think): int flags() { return IOP_FLAGS_INCLUDE_IN_STYLES | IOP_FLAGS_SUPPORTS_BLENDING; // optionally add IOP_FLAGS_ALLOW_TILING and implement tiling_callback } // where does it appear in the gui? int default_group() { return IOP_GROUP_BASIC | IOP_GROUP_TECHNICAL; } dt_iop_colorspace_type_t default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece) { return IOP_CS_RGB; } // Whenever new fields are added to (or removed from) // dt_iop_..._params_t or when their meaning changes, a translation // from the old to the new version must be added here. A verbatim // copy of the old struct definition should be copied into the routine // with a _v?_t ending. Since this will get very little future // testing (because few developers still have very old versions lying // around) existing code should be changed as little as possible, if // at all. // // Upgrading from an older version than the previous one should always // go through all in between versions (unless there was a bug) so that // the end result will always be the same. // // Be careful with changes to structs that are included in _params_t // // Individually copy each existing field that is still in the new // version. This is robust even if reordered. If only new fields were // added at the end, one call can be used: memcpy(n, o, sizeof *o); // // Hardcode the default values for new fields that were added, rather // than referring to default_params; in future, the field may not // exist anymore or the default may change. The best default for a new // version to replicate a previous version might not be the optimal // default for a fresh image. int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void **new_params, int32_t *new_params_size, int *new_version) { typedef struct dt_iop_useless_params_v3_t { int checker_scale; float factor; int strength; } dt_iop_useless_params_v3_t; // do migration from 2 to 3 (one step at a time, this legacy_params // update is incremental and will be done as many time as needed to // bring the parameters to the latest version of the module. if(old_version == 2) { typedef struct dt_iop_useless_params_v2_t { int checker_scale; float factor; } dt_iop_useless_params_v2_t; dt_iop_useless_params_v2_t *o = (dt_iop_useless_params_v2_t *)old_params; dt_iop_useless_params_v3_t *n = malloc(sizeof(dt_iop_useless_params_v3_t)); memcpy(n, o, sizeof(dt_iop_useless_params_v2_t)); n->strength = 1; *new_params = n; *new_params_size = sizeof(dt_iop_useless_params_v3_t); *new_version = 3; return 0; } return 1; } static const dt_mask_id_t mask_id = NO_MASKID + 1; // key "0" is reserved for the pipe static const char *mask_name = "useless checkerboard"; void commit_params(dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece) { memcpy(piece->data, p1, self->params_size); // there is no real need for this, but if the number of masks can be // changed by the user this is the way to go. otherwise we can have // old stale masks floating around g_hash_table_remove_all(self->raster_mask.source.masks); g_hash_table_insert(self->raster_mask.source.masks, GINT_TO_POINTER(mask_id), g_strdup(mask_name)); } #if 0 /** optional, always needed if tiling is permitted by setting IOP_FLAGS_ALLOW_TILING Also define this if the module uses more memory on the OpenCl device than the in& output buffers. */ void tiling_callback(dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const dt_iop_roi_t *roi_in, const dt_iop_roi_t *roi_out, dt_develop_tiling_t *tiling) { tiling->factor = 2.0f; // input buffer + output buffer; increase // if additional memory allocated tiling->factor_cl = 2.0f; // same, but for OpenCL code path running on GPU tiling->maxbuf = 1.0f; // largest buffer needed regardless of how // tiling splits up the processing tiling->maxbuf_cl = 1.0f; // same, but for running on GPU tiling->overhead = 0; // number of bytes of fixed overhead tiling->overlap = 0; // how many pixels do we need to access // from the neighboring tile? tiling->align = 1; } #endif /** modify regions of interest (optional, per pixel ops don't need this) */ // void modify_roi_out(dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, dt_iop_roi_t // *roi_out, const dt_iop_roi_t *roi_in); // void modify_roi_in(dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const dt_iop_roi_t // *roi_out, dt_iop_roi_t *roi_in); #if 0 /** modify pixel coordinates according to the pixel shifts the module * applies (optional, per-pixel ops don't need) */ gboolean distort_transform(dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, float *points, const size_t points_count) { const dt_iop_useless_params_t *d = piece->data; const float adjx = 0.0 * d->factor; const float adjy = 0.0; // nothing to be done if parameters are set to neutral values (no pixel shifts) if(adjx == 0.0 && adjy == 0.0) return TRUE; // apply the coordinate adjustment to each provided point for(size_t i = 0; i < points_count * 2; i += 2) { points[i] -= adjx; points[i + 1] -= adjy; } return TRUE; // return TRUE on success, FALSE if one or more points could not be transformed } #endif #if 0 /** undo pixel shifts the module applies (optional, per-pixel ops don't need this) */ gboolean distort_backtransform(dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, float *points, const size_t points_count) { const dt_iop_useless_params_t *d = piece->data; const float adjx = 0.0 * d->factor; const float adjy = 0.0; // nothing to be done if parameters are set to neutral values (no pixel shifts) if(adjx == 0.0 && adjy == 0.0) return TRUE; // apply the inverse coordinate adjustment to each provided point for(size_t i = 0; i < points_count * 2; i += 2) { points[i] += adjx; points[i + 1] += adjy; } return TRUE; // return TRUE on success, FALSE if one or more points could not be back-transformed } #endif /** modify a mask according to the pixel shifts the module applies (optional, per-pixel ops don't need this) */ // void distort_mask(dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const float *const in, // float *const out, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out); /** process, all real work is done here. NOTE: process() must never use the Gtk+ API. All GUI modifications must be done in the Gtk+ thread. This is to be conducted in gui_update or gui_changed. If process detect a state and something it to be change on the UI a signal should be used (raise a signal here) and a corresponding callback must be connected to this signal. */ void process(dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out) { // this is called for preview and full pipe separately, each with // its own pixelpipe piece. get our data struct: dt_iop_useless_params_t *d = piece->data; // the total scale is composed of scale before input to the pipeline (iscale), // and the scale of the roi. const float scale = piece->iscale / roi_in->scale; // how many colors in our buffer? const size_t ch = piece->colors; // most modules only support a single type of input data, so we can // check whether that format has been supplied and simply pass along // the data if not (setting a trouble flag to inform the user) dt_iop_useless_gui_data_t *g = self->gui_data; if(!dt_iop_have_required_input_format(4 /*we need full-color pixels*/, self, piece->colors, ivoid, ovoid, roi_in, roi_out)) return; // we create a raster mask as an example float *mask = NULL; if(dt_iop_is_raster_mask_used(self, mask_id)) { // Attempt to allocate all of the buffers we need. For this // example, we need one buffer that is equal in dimensions to the // output buffer, has one color channel, and has been zero'd. // (See common/imagebuf.h for more details on all of the options.) if(!dt_iop_alloc_image_buffers (self, roi_in, roi_out, 1/*ch per pixel*/ | DT_IMGSZ_OUTPUT | DT_IMGSZ_FULL | DT_IMGSZ_CLEARBUF, &mask, 0 /* end of list of buffers to allocate */)) { // Uh oh, we didn't have enough memory! If multiple buffers // were requested, any that had already been allocated have been // freed, and the module's trouble flag has been set. We can // simply pass through the input image and return now, since // there isn't anything else we need to clean up at this point. dt_iop_copy_image_roi(ovoid, ivoid, ch, roi_in, roi_out); return; } } else g_hash_table_remove(piece->raster_masks, GINT_TO_POINTER(mask_id)); // iterate over all output pixels (same coordinates as input) DT_OMP_FOR() for(int j = 0; j < roi_out->height; j++) { float *in = ((float *)ivoid) + (size_t)ch * roi_in->width * j; // make sure to address input, output and temp // buffers with size_t as we want to also float *out = ((float *)ovoid) + (size_t)ch * roi_out->width * j; // correctly // handle // huge // images float *out_mask = mask ? &(mask[(size_t)roi_out->width * j]) : NULL; for(int i = 0; i < roi_out->width; i++) { // calculate world space coordinates: int wi = (roi_in->x + i) * scale, wj = (roi_in->y + j) * scale; if((wi / d->checker_scale + wj / d->checker_scale) & 1) { for_each_channel(c, aligned(in,out)) // vectorize if possible out[c] = in[c] * (1.0 - d->factor); // does this for c=0..2 // or c=0..3, whichever // is faster if(out_mask) out_mask[i] = 1.0; } else { copy_pixel(out, in); } in += ch; out += ch; } } // now that the mask is generated we can publish it if(mask) g_hash_table_replace(piece->raster_masks, GINT_TO_POINTER(mask_id), mask); } /** Optional init and cleanup */ void init(dt_iop_module_t *self) { // Allocates memory for a module instance and fills default_params. // If this callback is not provided, the standard implementation in // dt_iop_default_init is used, which looks at the $DEFAULT // introspection tags in the comments of the params_t struct // definition. An explicit implementation of init is only required // if not all fields are fully supported by dt_iop_default_init, for // example arrays with non-identical values. In that case, // dt_iop_default_init can be called first followed by additional // initialisation. The values in params will not be used and // default_params can be overwritten by reload_params on a per-image // basis. dt_iop_default_init(self); // Any non-default settings; for example disabling the on/off switch: self->hide_enable_button = TRUE; // To make this work correctly, you also need to hide the widgets, // otherwise moving one would enable the module anyway. The standard // way is to set up a gtk_stack and show the page that only has a // label with an explanatory text when the module can't be used. } void init_global(dt_iop_module_so_t *self) { self->data = malloc(sizeof(dt_iop_useless_global_data_t)); } void cleanup(dt_iop_module_t *self) { dt_iop_default_cleanup(self); // Releases any memory allocated in init(module) Implement this // function explicitly if the module allocates additional memory // besides (default_)params. this is rare. } void cleanup_global(dt_iop_module_so_t *self) { free(self->data); self->data = NULL; } /** Put your local callbacks here, be sure to make them static so they * won't be visible outside this file! */ static void extra_callback(GtkWidget *w, dt_iop_module_t *self) { // this is important to avoid cycles! DT_GUARD_GUI_UPDATE(); dt_iop_useless_params_t *p = self->params; dt_iop_useless_gui_data_t *g = self->gui_data; float extra = dt_bauhaus_slider_get(w); // Setting a widget value will trigger a callback that will update params. // If this is not desirable (because it might result in a cycle) then use // DT_ENTER_GUI_UPDATE(); dt_bauhaus_slider_set(g->factor, p->factor + extra); // and reverse with DT_LEAVE_GUI_UPDATE(); // If any params updated directly, not via a callback, then // let core know of the changes dt_dev_add_history_item(darktable.develop, self, TRUE); } /** optional gui callbacks. */ void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous) { // If defined, this gets called when any of the introspection based widgets // (created with dt_bauhaus_..._from_params) are changed. // The updated value from the widget is already set in params. // any additional side-effects can be achieved here. dt_iop_useless_params_t *p = self->params; dt_iop_useless_gui_data_t *g = self->gui_data; // Test which widget was changed. // If allowing w == NULL, this can be called from gui_update, so that // gui configuration adjustments only need to be dealt with once, here. if(!w || w == g->method) { gtk_widget_set_visible(g->check, p->method == DT_USELESS_SECOND); } // Widget configurations that don't depend any any current params values should // go in reload_defaults (if they depend on the image) or gui_init. } void color_picker_apply(dt_iop_module_t *self, GtkWidget *picker, dt_dev_pixelpipe_t *pipe) { dt_iop_useless_params_t *p = self->params; dt_iop_useless_gui_data_t *g = self->gui_data; // This automatically gets called when any of the color pickers set up with // dt_color_picker_new in gui_init is used. If there is more than one, // check which one is active first. if(picker == g->factor) { p->factor = self->picked_color[1]; } dt_dev_add_history_item(darktable.develop, self, TRUE); dt_control_queue_redraw_widget(self->widget); } /** gui setup and update, these are needed. */ void gui_update(dt_iop_module_t *self) { // This gets called when switching to darkroom, with each image change or when // a different history item is selected. // Here, all the widgets need to be set to the current values in param. // // Note, this moves data from params -> gui. All fields at same // time. The opposite direction, gui -> params happens one field at // a time, for example when the user manipulates a slider. It is // handled by gui_changed (and the automatic callback) for // introspection based widgets or by the explicit callback set up // manually (see example of extra_callback above). dt_iop_useless_gui_data_t *g = self->gui_data; dt_iop_useless_params_t *p = self->params; // introspection based bauhaus widgets, created with // dt_bauhaus_slider_from_params, dt_bauhaus_combobox_from_params or // dt_bauhaus_toggle_from_params, get updated automatically. // they cannot use any transformations here (for example *100 for // percentages) because that will break enforcement of $MIN/$MAX. // Use dt_bauhaus_slider_set_factor/offset in gui_init instead. dt_bauhaus_slider_set(g->extra, 0.0f); // Any configuration changes to the gui that depend on field values // should be done here, or can be done in gui_changed which can then // be called from here with widget == NULL. gui_changed(self, NULL, NULL); } /** optional: if this exists, it will be called to init new defaults if a new image is * loaded from film strip mode. */ void reload_defaults(dt_iop_module_t *self) { // This only has to be provided if module settings or default_params // need to depend on image type (raw?) or exif data. Make sure to // always reset to the default for non-special cases, otherwise the // override will stick when switching to another image. dt_iop_useless_params_t *d = self->default_params; // As an example, switch off for non-raw images. The enable button // was already hidden in init(). if(!dt_image_is_raw(&self->dev->image_storage)) { self->default_enabled = FALSE; } else { self->default_enabled = TRUE; d->checker_scale = 3; // something dependent on exif, for example. } // If we are in darkroom, gui_init will already have been called and // has initialised module->gui_data and widgets. So if default // values have been changed, it may then be necessary to also change // the default values in widgets. Resetting the individual widgets // will then have the same effect as resetting the whole module at // once. dt_iop_useless_gui_data_t *g = self->gui_data; if(g) { dt_bauhaus_slider_set_default(g->scale, d->checker_scale); } } void gui_init(dt_iop_module_t *self) { // Allocates memory for the module's user interface in the darkroom // and sets up the widgets in it. // // self->widget needs to be set to the top level widget. This can // be a (vertical) box, a grid or even a notebook. Modules that are // disabled for certain types of images (for example non-raw) may // use a stack where one of the pages contains just a label // explaining why it is disabled. // // Widgets that are directly linked to a field in params_t may be // set up using the dt_bauhaus_..._from_params family. They take a // string with the field name in the params_t struct definition. The // $MIN, $MAX and $DEFAULT tags will be used to set up the widget // (slider) ranges and default values and the $DESCRIPTION is used // as the widget label. // // The _from_params calls also set up an automatic callback that // updates the field in params whenever the widget is changed. In // addition, gui_changed is called, if it exists, so that any other // required changes, to dependent fields or to gui widgets, can be // made. // // Whenever self->params changes (switching images or history) the // widget values have to be updated in gui_update. // // Do not set the value of widgets or configure them depending on // field values here; this should be done in gui_update (or // gui_changed or individual widget callbacks) // // If any default values for(slider) widgets or options (in // comboboxes) depend on the type of image, then the widgets have to // be updated in reload_params. dt_iop_useless_gui_data_t *g = IOP_GUI_ALLOC(useless); // If the first widget is created using a _from_params call, // self->widget does not have to be explicitly initialised, as a new // vertical box will be created automatically. self->widget = dt_gui_vbox(); // Linking a slider to an integer will make it take only whole // numbers (step=1). The new slider is added to self->widget g->scale = dt_bauhaus_slider_from_params(self, "checker_scale"); // If the field name should be used as label too, it does not need a // $DESCRIPTION; mark it for translation here using N_() // // A colorpicker can be attached to a slider, as here, or put // standalone in a box. When a color is picked, color_picker_apply // is called with either the slider or the button that triggered it. g->factor = dt_color_picker_new(self, DT_COLOR_PICKER_AREA, dt_bauhaus_slider_from_params(self, N_("factor"))); // The initial slider range can be reduced from the introspection $MIN - $MAX dt_bauhaus_slider_set_soft_range(g->factor, 0.5f, 1.5f); // The default step is range/100, but can be changed here dt_bauhaus_slider_set_step(g->factor, .1); dt_bauhaus_slider_set_digits(g->factor, 2); // Additional parameters determine how the value will be shown. dt_bauhaus_slider_set_format(g->factor, "%"); // For a percentage, use factor 100. dt_bauhaus_slider_set_factor(g->factor, -100.0f); dt_bauhaus_slider_set_offset(g->factor, 100.0f); // Tooltips explain the otherwise compact interface gtk_widget_set_tooltip_text(g->factor, _("adjust factor")); // A combobox linked to struct field will be filled with the values // and $DESCRIPTIONs in the struct definition, in the same // order. The automatic callback will put the enum value, not the // position within the combobox list, in the field. g->method = dt_bauhaus_combobox_from_params(self, "method"); g->check = dt_bauhaus_toggle_from_params(self, "check"); // Any widgets that are _not_ directly linked to a field need to // have a custom callback function set up to respond to the // "value-changed" signal. g->extra = dt_bauhaus_slider_new_with_range(self, -0.5, 0.5, 0, 0, 2); dt_bauhaus_widget_set_label(g->extra, NULL, N_("extra")); dt_gui_box_add(self->widget, g->extra); g_signal_connect(G_OBJECT(g->extra), "value-changed", G_CALLBACK(extra_callback), self); } void gui_cleanup(dt_iop_module_t *self) { // This only needs to be provided if gui_init allocates any memory // or resources besides self->widget and gui_data_t. } /** additional, optional callbacks to capture darkroom center events. */ // void gui_post_expose(dt_iop_module_t *self, cairo_t *cr, int32_t width, int32_t height, int32_t pointerx, // int32_t pointery); // int mouse_moved(dt_iop_module_t *self, double x, double y, double pressure, int which); // int button_pressed(dt_iop_module_t *self, double x, double y, double pressure, int which, int type, // uint32_t state); // int button_released(dt_iop_module_t *self, double x, double y, int which, uint32_t state); // int scrolled(dt_iop_module_t *self, double x, double y, int up, uint32_t state); // optional: if mouse events are handled by the iop, we can add text to the help screen by declaring // the mouse actions and their descriptions #if 0 GSList *mouse_actions(dt_iop_module_t *self) { GSList *lm = NULL; // add the first action lm = dt_mouse_action_create_format(lm, DT_MOUSE_ACTION_SCROLL, GDK_SHIFT_MASK, _("[%s] some action"), self->name()); // append a second action to the list we will return lm = dt_mouse_action_create_format(lm, DT_MOUSE_ACTION_LEFT_DRAG, GDK_CONTROL_MASK | GDK_SHIFT_MASK, _("[%s] other action"), self->name()); return lm; } #endif // clang-format off // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py // vim: shiftwidth=2 expandtab tabstop=2 cindent // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified; // clang-format on