# GILDAS graphic interface

## Structure

```sh  
ggtk
├── gtk-dialog.c        # Gathers methods used to create, manipulate and destroy windows and dialogs. (nothing related to the drawing part, only windows, popups, buttons, expanders...)
├── gtk-dialog.h        # Exposes the run_dialog method launched by gtk-main
├── gtk-graph.c         # List of methods manipulating the _ggtk_env structure. Exposed APIs are listed in the s_ggtk_graph_api object.
├── gtk-graph.h         # List of methods shared with other files of the ggtk module.
├── gtk-local.h         # Contains the definition of the _ggtk_env and of the _ggtk_toolbar_args structures.
├── gtk-main.c          # Entry point file starting the gtk code
├── gtk-menu.c          # Contains all the code responsible of the small window gathering additional commands (called "greg GUI" for greg tool for example)
├── gtk-menu.h          # Exposes the run_gtk_menu and kill_gtk_menu method used by gtk-main
├── gtk-toolbar.c       # Contains the run_gtk_main_loop launching the gtk main loop and additional methods used to create the main graphical window with all available commands (Pencil, Marker etc...)
├── gtk-toolbar.h       # Exposes methods used by other ggtk module files
├── magnify-tool.c      # Contains methods used to create the immersive magnify popup 
├── magnify-tool.h      # Exposes a method used to ceate the magnifying tool
├── message-c.c         # /
├── message-c.h         # /
├── zoom-tool.c         # Contains methods used to draw the zoom cursor and to create a sub window with a zoomed portion of the main window
├── zoom-tool.h         # Exposes the ggtk_activate_zoom called after launching the zoom tool
├── Makefile            # /
└── win-ggtk.def        # /
```

## Main concepts

The graphic interface is handled by C code files located under ggtk2 or ggtk3 directories.

The ggtk library exposes a list of methods which can be called by the main code principally written in fortran.  

The fortran code, run in the main thread, calls methods exposed by the ggtk library if this library is built.

The ggtk code is run in a dedicated thread.

When graphic elements are created (polylines, surfaces etc...) the main thread calls specific methods implemented by the ggtk library to draw those elements in a gtk drawing area (if any)

## Legacy graphic interface using GTK2

The ggtk library based on GTK2 is located in ggtk2 folder (next to the ggtk folder) and works as follow:
- Creation of the new thread
- Initialization of the GTK windows to build
- Call of the gkt main loop function which displays the different windows
- When a graphical component (like a polyline) is created by the main thread, a specific API is called on ggtk side to draw this element inside the active drawing area (the environment in which the graphical element needs to be displayed is passed as an argument to the draw method)

Note here that the rendering operations are synchronously executed when the main thread code calls the dedicated ggtk draw APIs.

GTK2 library is deprecated and should not be used anymore.
To build the GILDAS graphical interface using GTK2, source your environment as follow:

```sh
. admin/gildas-env.sh -o gtk2
```

## Current graphic interface based on GTK3

The ggtk library based on GTK3 works almost similarly as GTK2 for the panel creation and for the main loop process. The way elements are rendered is changed and works now with Cairo:
- Creation of the new thread
- Initialization of the GTK windows to build
- Call of the gkt main loop function which displays the different windows
- When a graphical component (like a polyline) is created by the main thread, a specific API is called on ggtk side to store this element in a buffer attached to the current graphical environment (main drawing area or the magnifying tool basically). GTK uses a draw event which basically asks to render the entire drawing area in one shot. This process, based on events, is asynchronous. When the draw callback is executed, the internal graphical element stores are used to render the view.

Note here that the rendering operations are executed asynchronously. GTK manages his own way to redraw the drawing area.
In the code we can ask for a redraw but this operation is not synchronous. It just warns GTK that a refresh of the drawing area is required.

To build the GILDAS graphical interface using GTK3, source your environment as follow:

```sh
. admin/gildas-env.sh
```

The GTK3 version of ggtk works natively on Linux (X11 / Wayland) and MacOs (X11 / XQuartz) for Intel chips only.
To make this work on MacOS computers with Arm chips (M1, M2 etc...) you also need to export the following variable before launching the application:

```sh
export GDK_RENDERING=image
```

The XQuartz + Arm chip + MacOS issue is tracked by the following ticket: https://trac.macports.org/ticket/65892?cversion=1&cnum_hist=17

### Drawing areas

GTK3 uses Cairo to draw graphical primitives such as polylines or surfaces.
All primitives are stored inside the _ggtk_env structure defined in the gtk-local.h file.
Multiple methods are declared and used to manipulate the content of those graphical primitive stores in "gtk-graph" files (polyline_init, polystore_free, etc...)

Some specific attributes of the _ggtk_env such (line_width, line_is_dashed, red, blue, green, invert, cap_style, join_style and antialiasing) are used to change the graphical properties of the next graphical elements to store in the different sub stores.

The flow is as follow:
- the ggtk_pen_rgb method is called by the main thread to set color1 (changes the red, blue and green attributes of the _ggtk_env)
- the ggtk_flush_points method is called by the main thread and adds a new polyline in the internal store of the _ggtk_env. This new polyline is saved with color1 in the _ggtk_env.
- the ggtk_pen_rgb method is called by the main thread with another: color2
- the ggtk_flush_points method is called by the main thread and adds a new polyline in the internal store of the _ggtk_env. This new polyline is saved with color2 in the _ggtk_env.

When a draw event is catched (event managed by GTK) the **draw_scene_from_genv** of the gtk-graph file is called and checks all internal graphical element stores to draw the scene.

Note that the antialiasing can be changed from the code and depends on the _ggtk_env.
For the moment no antialiasing is applied on the main window and the 'CAIRO_ANTIALIAS_BEST' antialiasing is applied for magnifying windows.

### Rendering performance model

The two latest performance commits on master changed the rendering model:
- commit `2367605c34`: offscreen scene cache + incremental chunked draw
- commit `b00edc792c`: Cairo style state cache for polylines

#### Offscreen scene cache and chunked refresh

`draw_scene_from_genv` does not redraw the full scene on each draw callback
anymore. The scene is now rendered into an offscreen Cairo surface stored in
`_ggtk_env`, then painted on screen.

Important fields in `gtk-local.h`:
- `scene_surface`, `scene_surface_width`, `scene_surface_height`
- `scene_background`
- `rendered_primitive_count`, `rendered_poly_count`, `rendered_image_count`
- `draw_pending`

Important flow in `gtk-graph.c`:
- `_ggtk_prepare_scene_surface` ensures the cache size matches the widget.
- `draw_scene_from_genv` renders only a chunk of pending primitives on each
  callback and re-queues a draw if needed.
- `_ggtk_queue_draw` prevents multiple redundant draw requests for the same
  frame.

The chunk size is controlled by `GGTK_DRAW_CHUNK` in `gtk-graph.c`.

#### Cairo style-state caching for polylines

Polyline rendering now avoids repeated Cairo state changes when style is
unchanged across consecutive polylines.

Key helpers in `gtk-graph.c`:
- `PolylineStyleState`
- `_polyline_style_matches`
- `_apply_polyline_style`

`internal_draw_polyline_or_polygon` now calls `_apply_polyline_style` before
drawing the path.

If you add a new polyline style attribute, update:
- the `Polyline` structure (if needed),
- `PolylineStyleState`,
- `_polyline_style_matches`,
- `_apply_polyline_style`.

## Next steps: GTK4

GTK4 is the latest 'LTS' version of GTK.
This last version is deeply different compared to the previous one. 
One of the key change is the fact that this library is no longer 'thread safe'.
It means that the GTK application cannot be launched in a separated thread.

The entire application management needs to be changed in order to create the main application in the main thread and handle the keyboard and the calculations in a separated thread.
