Scintilla GTK 4 port

168 views
Skip to first unread message

Sergey Bugaev

unread,
Nov 6, 2025, 4:19:08 PMNov 6
to scintilla...@googlegroups.com
Hello,

I'm writing to announce that I have developed a mostly working port of
Scintilla to the GTK 4 toolkit.

## Why?

GTK 3 is in maintenance mode: there are no new developments, it's API
and feature frozen. It only receives small fixes, sometimes. GTK 4 is
what is being actively developed and used by most applications. It's
modern and cool and featureful.

Geany maintainers have expressed interest in a GTK 4 port of Geany,
but Scintilla is the biggest missing piece [0]. There was also a post
by Neil on this mailing list suggesting that this is desirable [1].

[0]: https://github.com/geany/geany/discussions/3675
[1]: https://groups.google.com/g/scintilla-interest/c/RX4kQkWJBk8

I am involved in a project to develop a large commercial application,
using GTK 4 as the toolkit. The application uses Scintilla; so I took
up the challenge of making the port.

## What It Is and Does

As suggested by Neil in [1], this is not an adaptation of the GTK 2/3
port (in gtk/ of the source tree), but rather a whole new independent
port, living in gtk4/. It doesn't directly use code form the GTK 2/3
port, although I did consult that port for ideas & inspiration when
working on the GTK 4 one.

Another bit of motivation for structuring this as a separate port is
that I wanted to take the opportunity to do the GTK 4 version quite
differently from the old one, in various aspects. That is, given my
background in GTK and GObject (as opposed to background in Win32 and
Scintilla internals, which I imagine other Scintilla developers might
have), I wanted to do things in a way that is much more *proper* and
*native* for GTK and GObject. I wanted Scintilla to be a "good
citizen" of the GTK ecosystem, to behave a lot like any other GTK
("self-respecting", if you will) widget and library would.

I tried to keep the port self-contained, i.e. it only adds a gtk4/
directory, without altering the existing code in src/ and elsewhere.
That being said, this required some compromises (read: hacks), and
some things would be cleaner if we could tune some Scintilla internals
to better suit the needs of the GTK 4 port.

## The Widget

There is a single widget, ScintillaView. It implements the
GtkScrollable interface [2]. It doesn't contain the scrollbars or
handle the scrolling events/gestures, instead you're expected to place
a ScintillaView as a child into GtkScrolledWindow [3], which is what
manages scrollbars and events; this is how scrollable widgets are done
in GTK.
[2]: https://docs.gtk.org/gtk4/iface.Scrollable.html
[3]: https://docs.gtk.org/gtk4/class.ScrolledWindow.html

The ScintillaView class has a bunch of GObject *properties*, as well
as widget *actions*, this is again similar to what all the other GTK
widgets are doing.

## Rendering Concerns

GTK 4 has a whole new rendering model, which is based on a scene
graph, a tree of render nodes, and GPU-based rendering (potentially,
with the actual rendering happening on a separate thread concurrently
to the application logic, but this is not implemented in GTK yet).
This is in contrast to GTK 3, which used a more classic CPU/RAM-based
rendering model utilizing Cairo, a 2D graphics library with a
plotter/PostScript-like API.

It is technically possible to still use Cairo in GTK 4 through
GskCairoNode / gtk_snapshot_append_cairo, which will cause GTK to
render the 2D graphics on the CPU / in the RAM, then upload the
resulting texture to the GPU. But this has all the downsides of the
CPU-based/2D rendering (performance on HiDPI). So the recommendation
is to use GTK4-native rendering as much as possible, and only to fall
back onto Cairo when that is not possible.

Fortunately, in most cases, Scintilla's drawing operations (the
Surface class) map fairly well to that of GTK 4! So most of the
drawing Scintilla does ends up as fairly idiomatic render nodes in the
scene graph. There are some unfortunate mismatches though, of which I
could talk more about.

GTK supports HiDPI displays, with both integer and fractional (e.g.
2.5x) scaling. This is, for the most part, transparent to
applications; all the coordinates and sizes on the GTK level are
expressed in logical units, which are converted to and from *device
pixels* at a lower level. The render tree (that is produced by the
widget tree of a window) is essentially a piece of vector graphics,
which is then rendered for the desired physical resolution, at an
appropriate scale. But there are some considerations for applications
& widgets as well: in particular Scintilla tries to round extents to
physical pixels via a PixelDivisions value, which is, for one thing,
an integer, with no way to represent a fractional scale like 2.5 or
1.75. Even if it was made a float, there is still no clear way how
this could work, because the "subpixel positions" of device pixels are
*not* uniform among the "logical pixels".

Another issue here is that Scintilla paints the background behind each
piece of text individually as its own little rectangle; this for one
thing has the downside of producing a lot of render nodes (a lot of
small white rectangles, instead of one large white rectangle covering
the whole widget area), and therefore a bunch of extra work for the
GPU to push through. But also visually, while with integer scaling the
sides of the rectangles align perfectly and the whole background ends
up painted white, with fractional scaling the sides do not always
align, and there are visible "seams". This is a general issue with
fractional scaling, but it's aggravated by what Scintilla is doing
(drawing these little rectangles and expecting them to align), and
typically avoided by just not doing that. I tried to make it less bad
by setting the default background color to transparent, and instead
painting the white background the typical GTK way (adding the CSS
.view class); this helps a bunch, but seams still show up as soon as
you e.g. select some text.

There is some work on the GTK side [4] to enable opt-in *snapping* of
render nodes to physical pixels, whether by growing or shrinking or
rounding rectangles a tiny bit to land precisely on physical pixel
boundaries, thus ensuring there are no seams between two neighboring
snapped rectangles. Once this work lands in a future version of GTK,
we should be able to make use of it in Scintilla.

[4]: https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/8494

Another topic here is what we even want to draw. Generally, GTK
doesn't define a global set of things like "the platform highlight
color" or "the default font". Instead, widgets are assigned *style*
via *stylesheets* (written in CSS). This style is dynamic, so it
varies with time (the colors change when the system-wide dark mode is
toggled, for example), and between widgets -- so different instances
of ScintillaView could have different styles assigned to them,
depending on the context where they appear. So it makes no sense to
talk about which font should be used, for example, without mentioning
which specific widget that question is about.

I had used various tricks to somewhat reconcile Scintilla code's idea
of each platform having a single global style with this GTK's idea of
style inherently coming from a style sheet and differing per-widget
and with time. It works somewhat, but it's not as perfect as it could
be.

Yet another topic is phases. In GTK, "every frame is perfect". We
don't draw bad frames, because... why would we do such a thing? But
Scintilla sometimes decides to just *adandon painting* if it discovers
it needs to re-style or update something in the middle of painting. I
must admit, I don't quite understand what is supposed to happen in
this case on other platforms (a brief flash of black? a bunch of stale
pixels from a previous frame?), but this is not an option for GTK; we
must do things properly. One option here would be immediately starting
a new paint cycle if Scintilla core decides to abandon painting,
hoping that this second one succeeds. This would require creating an
auxiliary GtkSnapshot object, and still wouldn't solve the issue
entirely, since the changes that Scintilla core may do, such as
updating positions of scroll bars, really need to happen before the
*drawing phase*, namely in the *layout phase* of the GDK frame clock
[5]; various things in GTK rely on various other things being done in
the correct phase, and this whole mechanism is intentionally designed
in such a way that we can do things in the right order and display
every frame perfectly. I ended up pushing a bunch of work that *would*
cause painting to be abandoned into the size-allocate virtual method;
this appears to work great this far.

[5]: https://docs.gtk.org/gtk4/drawing-model.html#the-frame-clock

## GObject Introspection

GObject introspection [6] is a cool feature of the GObject type system, where:
* a library's APIs are annotated in a special way in the source code;
* a tool called g-i-scanner scans the source code, the annotations,
and the compiled library, and produces a structured, machine- and
human-readable description of the API (in XML and binary formats);
* based on that API description, pretty documentation can be generated
using gi-docgen [7];
* bindings for using the library from many other languages (including
Python, JavaScript, C++, Rust, Go, C#/.NET, Java, and others) can be
generated automatically and transparently, which enables using the
library almost seamlessly from pretty much any language.

[6]: https://gi.readthedocs.io/
[7]: https://gnome.pages.gitlab.gnome.org/gi-docgen/

Of course, all of these goodies are only available if the library
exposes a C API in the GObject style, and is properly annotated.

There are some bits of this in the GTK 2/3 port already, but for the
GTK 4 port, I went all in. Various things that, on Win32, are
available through the messaging model, are exposed as GObject
properties, various other things as GObject methods, with annotations
matching the Scintilla semantics.

For example, SCI_APPENDTEXT is exposed as scintilla_view_append_text()
in C, which turns into a Scintilla.View.append_text() method in the
object model, which can be then called from JavaScript just like so:

```
import Scintilla from 'gi://Scintilla';
import Gtk from 'gi://Gtk?version=4.0';

Gtk.init();
const sc = new Scintilla.View();

sc.append_text("Hello");
```

In some cases, this work has uncovered issues in the GObject
introspection itself [8].

[8]: https://gitlab.gnome.org/GNOME/gobject-introspection/-/issues/536#note_2398113

Scintilla has accumulated a lot of API surface; I haven't completed
exposing all of it as GObject methods & properties -- yet. But this
looks very nice & very promising in places where I have done the job.

One little thing I'm unreasonably proud of is that
SCI_GETDOCPOINTER/SCI_SETDOCPOINTER are exposed as a property (and
accessor methods) of type ScintillaDocument. When compiling in C++
mode, ScintillaDocument is just a type alias for
Scintilla::IDocumentEditable, but in C it's an opaque type. This type
is then registered as a "boxed type" in the GObject type system, and
appears as just a normal *boxed record* type when seen through
introspection (and so from language bindings), but internally it
really is an IDocumentEditable.

The classic "send message" funnel is also exposed as
scintilla_view_send_message, but it is not at all
introspection-friendly, so it's cumbersome to use from anything but
C/C++/Objective-C.

## Shared Library

Scintilla is built as a shared dynamic library. All of these C
functions are exposed from it; it is expected that users will link it
just like they link any other library (including GTK itself), and call
the provided APIs, either directly via linker-assisted symbol
references (from static languages), or by dynamically looking up
symbols (which is what happens when you invoke an API from a dynamic
language like Python or JavaScript).

It is also possible to use Scintilla in the classic style, by *not*
linking to the library, dlopen'ing it at runtime, and then finding a
few functions (you can get by with as little as two, namely
scintilla_view_new and scintilla_view_send_message). But this is again
only really remotely nice to do from C.

I have spent some effort on ensuring proper symbol visibility, so only
the symbols that are intended to be exposed from the shared library,
are, and the rest are internal to the library and are indeed
referenced as such (by a PC-relative offset and not via GOT or PLT). I
use both symbol visibility annotations and a linker script to achieve
this.

It would also be great if distributions (like Debian etc) packaged
Scintilla as a normal library, much like GTK or any other, with the
compiled library appearing in /lib/libscintilla.so (or such), the
headers at /usr/include/ScintillaView.h, and so on. To that end, we
might want to write and install a pkg-config file for Scintilla; this
would enable others to link to the system build of Scintilla easily,
e.g. with just `dependency("scintilla")` in Meson.

## Status

Works:
* Basic functionality: you can view and edit text documents
* Scrolling, including kinetic scrolling on touchpads and touchscreens
(thanks to GtkScrolledWindow)
* GObject introspection, using the library from various languages
* Lexilla, syntax highlighting, folding
* Clipboard: you can cut/copy/paste pieces of text. When you copy text
from one instance of Scintilla to another (or the same one) inside one
application, the selection is not serialized to a string, but is
represented by the SelectionText type, and keeps the rectangular shape
etc. Support for the primary clipboard (middle-click/triple-tap paste)
is halfway-implemented: you can paste text copied from elsewhere, but
Scintilla itself doesn't yet claim the primary selection.

There is some support for:
* IME (e.g. on-screen keyboard)
* Accessibility, using GtkAccessible and GtkAccessibleText (when
building with GTK 4.14 or later) interfaces. If you activate Orca the
screen reader, it reads the text around cursor, the current selection
etc
* The context menu is there, but it's the native GTK menu, populated
via a menu model (GMenuModel), and not Scintilla core's idea of a menu
(AddToPopUp).

But, it is very likely that there are issues concerning encodings,
bytes vs characters vs something else. There is work to be done in
straightening this out.

Not implemented / does not work yet:
* Drag-n-drop of text
* Call tips
* Autocompletion popovers
* Some more arcane drawing operations
* Proper accent color / dark theme / high contrast support (this
requires Scintilla styles to better follow the GTK stylesheet)
* Non-UTF-8 support? (Is that a thing?)
* Various bidirectional text things are very likely broken

Touchscreen experience is not very perfect: scrolling with your finger
also causes cursor/selection jumps, there are no text handles
(analogous to GtkTextHandle), and as said IME is not quite working
perfectly either.

I have built & tested Scintilla on GNU/Linux (Wayland and X11) and
Windows, but not on other targets supported by GTK (macOS, Android,
Broadway).

Another TODO is commenting/documenting all of this code. This means
both documenting the public APIs (so the gi-docgen documentation is
complete and can be read instead of the Win32-message-oriented one
that's currently visible at [9]), and commenting things internally, so
it's very clear to any person looking to read or contribute to the GTK
4 port how things are done, what caveats and important points there
are.

[9]: https://scintilla.org/ScintillaDoc.html

## The Demo

Included with the port is a little application called Scintilla Demo,
which is a very basic text editor, with tabs and menus and a
ScintillaView in each tab. It's using cool technologies like Vala and
Blueprint and Gio (but not libadwaita, as to not tie it to GNOME). It
is *very* basic and incomplete; for instance I implemented opening
files, but not saving them. I don't think it should turn into a
feature-complete competitor to GNOME Text Editor or Geany or SciTE;
it's fine if it stays very simple, though of course basic
functionality should be completed.

Speaking of SciTE, is there interest in porting that to GTK 4 (while
simultaneously making it a lot more idiomatic)? Last time I looked at
SciTE, I could not exactly figure out how to run it "uninstalled", how
all these "properties" are intended to work; but perhaps I should take
another look?

## Where Do We Go From Here?

I'm looking for people who are interested in Scintilla and GTK 4; if
you are, please let me know! This port could use some more testing,
and real-world usage beyond my own use cases. There is also still a
lot to work on (accessibility, IME support, call tips,
autocomplete...), let's hack on this together!

I'm looking for feedback from Scintilla core developers. Do you want
this to go upstream?

If so, how do we organize this process? I see you're using Git and
GitHub for Lexilla, but SourceForge and Mercurial for Scintilla. I'm
not familiar with those; would it work for you if I send you a patch
(in diff(1) / patch(1) / git format-patch -compatible format) or
upload a code archive or a Git repository somewhere? How do we do code
review?

Could we host the HTML documentation autogenerated using gi-docgen
somewhere on scintilla.org? Is there a CI process?

Would you be open to changing those little things about Scintilla core
that would make the GTK 4 so much less hacky? (One example of this
might be, not querying a "platform color" globally, but in relation to
a specific instance of Scintilla.)

If you *don't* want this upstream, for one reason or another, are
people nevertheless interested in this work, should I maintain a
friendly downstream fork?

Cheers,
Sergey

P.S. This email was typed and edited using Scintilla on GTK 4, in the
Scintilla Demo application.

Mitchell

unread,
Nov 6, 2025, 4:37:01 PMNov 6
to scintilla...@googlegroups.com
Hi,

> On Nov 6, 2025, at 4:31 AM, Sergey Bugaev <bug...@gmail.com> wrote:
>
> Hello,
>
> I'm writing to announce that I have developed a mostly working port of
> Scintilla to the GTK 4 toolkit.

<snip>

I maintain the terminal/curses platform of Scintilla as a separate project: https://github.com/orbitalquark/scinterm.

All I wanted to say is that, speaking as a platform maintainer, this is a very impressive effort you have begun! Your write-up was very informative and a pleasure to read. I wish you the best of luck.

Cheers,
Mitchell

Colomban Wendling

unread,
Nov 13, 2025, 5:24:57 AMNov 13
to scintilla...@googlegroups.com
Hello Sergey,

Thanks a lot for this effort, it sounds very promising! It also sounds
like a lot of work, and trying to make it fit the GTK drawing model is a
great news.

Kudos also for also trying to make it a more GTK-like widget, which can
make Scintilla more welcoming for GTK developers. Just in case it might
be of interest, to you know of GtkSicntilla[1]? It's quite old now, but
it was an attempt at "GObjectizing" the API.

[1] https://github.com/codebrainz/GtkScintilla

Le 06/11/2025 à 10:31, Sergey Bugaev a écrit :
> […]
>
> Another issue here is that Scintilla paints the background behind each
> piece of text individually as its own little rectangle; […]

Could these rander nodes be "compressed" on the widget's side? It might
be some work, but usually background is fairly uniform (but for a few
specific pieces, like selection or specific ranges), so it might be
worth trying to background nodes that share the same properties as a
large drawing (or just paint the whole background with the most
prominent value), and only keep specific nodes when that doesn't match.
I don't know how hard/CPU intensive that would be, but it might help a
bit, wouldn't it?
Of course that wouldn't necessarily solve everything, but it might be a
first step. And if then nodes could be snapped to pixels, that could
fix the rendering issues of those specific areas -- without requiring
more complex merge logic in the widget's side.

> […] I don't quite understand what is supposed to happen in
> this case on other platforms (a brief flash of black? a bunch of stale
> pixels from a previous frame?),

IIUC, while it depends on the platform, the idea is not to flip the
double-buffer, and so leave the previous frame untouched. Of course,
depending on the implementation or the use of a double-buffer at all,
this could indeed lead to a partially refreshed frame with stale
portions, or an entirely blank frame -- the last being most problematic.

I don't know if there are case where the paint is abandoned but would
result in *incorrect* drawing were it to continue, or if it's only an
optimization not to waste time finishing a frame that will need updating
anyway, though. If it's just an optimization, possibly the abandonment
could be inhibited in the GTK4 platform.

> […]
> * Accessibility, using GtkAccessible and GtkAccessibleText (when
> building with GTK 4.14 or later) interfaces. If you activate Orca the
> screen reader, it reads the text around cursor, the current selection
> etc

Thanks a lot for considering implementing a11y in this port!

> […]
>
> ## Where Do We Go From Here?
>
> I'm looking for people who are interested in Scintilla and GTK 4; if
> you are, please let me know! This port could use some more testing,
> and real-world usage beyond my own use cases. There is also still a
> lot to work on (accessibility, IME support, call tips,
> autocomplete...), let's hack on this together!

I am, and I guess the Geany community at large is :) I however probably
won't have time to properly dive in this just yet, but I'd be very
curious to look at it whenever I get some time. I might also be able to
help more quickly on e.g. a11y -- not making promises.

Is there some code we can look at somewhere?

Also porting Geany to GTK4 won't be trivial even when Scintilla is
entirely working, so it likely won't happen quickly, but giving it a try
might be a interesting and educational, given it's a fairly
comprehensive user (well, we don't use each and every feature, but still).

> […]
>
> If you *don't* want this upstream, for one reason or another, are
> people nevertheless interested in this work, should I maintain a
> friendly downstream fork?

While I hope support would be upstreamed, yes, I would be interested
either way. Hopefully, and especially if you don't touch anything
outside the platform layer itself, it would be easy to combine latest
Scintilla with your platform layer even if they live separately.

Regards,
Colomban

Sergey Bugaev

unread,
Nov 13, 2025, 6:51:44 AMNov 13
to scintilla...@googlegroups.com
On Thu, Nov 13, 2025 at 1:24 PM Colomban Wendling
<list...@herbesfolles.org> wrote:
> Hello Sergey,

Hello,

> Thanks a lot for this effort, it sounds very promising! It also sounds
> like a lot of work, and trying to make it fit the GTK drawing model is a
> great news.

yay! Thanks for taking interest :)

> Kudos also for also trying to make it a more GTK-like widget, which can
> make Scintilla more welcoming for GTK developers. Just in case it might
> be of interest, to you know of GtkSicntilla[1]? It's quite old now, but
> it was an attempt at "GObjectizing" the API.
>
> [1] https://github.com/codebrainz/GtkScintilla

I have not seen it, thanks. It does seem interesting, but indeed old,
before many of the modern GTK/GObject development best practices were
established. I'll be certainly keeping an eye on how they did things
for future work.

> Le 06/11/2025 à 10:31, Sergey Bugaev a écrit :
> > […]
> >
> > Another issue here is that Scintilla paints the background behind each
> > piece of text individually as its own little rectangle; […]
>
> Could these rander nodes be "compressed" on the widget's side? It might
> be some work, but usually background is fairly uniform (but for a few
> specific pieces, like selection or specific ranges), so it might be
> worth trying to background nodes that share the same properties as a
> large drawing (or just paint the whole background with the most
> prominent value), and only keep specific nodes when that doesn't match.
> I don't know how hard/CPU intensive that would be, but it might help a
> bit, wouldn't it?

I don't think the CPU complexity is going to be an issue with this;
but it would require a bunch of effort to track & match these
rectangles. Part of the complication would be that if we were to delay
rendering the background, we would have to have a robust way of
painting things out of order. We don't get information from the
Scintilla core whether the rectangle being painted is a part of the
background or not. We get requests like "paint this rectangle this
color"; it could be background (so it has to be painted below
everything else, and at the very end, so we could possibly collapse it
with neighbouring nodes) or foreground or something else, and we're
not told which one.

> Of course that wouldn't necessarily solve everything, but it might be a
> first step. And if then nodes could be snapped to pixels, that could
> fix the rendering issues of those specific areas -- without requiring
> more complex merge logic in the widget's side.
>
> > […] I don't quite understand what is supposed to happen in
> > this case on other platforms (a brief flash of black? a bunch of stale
> > pixels from a previous frame?),
>
> IIUC, while it depends on the platform, the idea is not to flip the
> double-buffer, and so leave the previous frame untouched. Of course,
> depending on the implementation or the use of a double-buffer at all,
> this could indeed lead to a partially refreshed frame with stale
> portions, or an entirely blank frame -- the last being most problematic.

It would be problematic even if it does retain stale pixels,
especially if the widget is being resized (so its size doesn't match
what it was when it was painted), or if it's the very first frame and
there are no previous contents etc.

This is how the infamous flickering-when-resizing happens.

> Thanks a lot for considering implementing a11y in this port!

Of course! Accessibility is very important :)

> > ## Where Do We Go From Here?
> >
> > I'm looking for people who are interested in Scintilla and GTK 4; if
> > you are, please let me know! This port could use some more testing,
> > and real-world usage beyond my own use cases. There is also still a
> > lot to work on (accessibility, IME support, call tips,
> > autocomplete...), let's hack on this together!
>
> I am, and I guess the Geany community at large is :) I however probably
> won't have time to properly dive in this just yet, but I'd be very
> curious to look at it whenever I get some time. I might also be able to
> help more quickly on e.g. a11y -- not making promises.
>
> Is there some code we can look at somewhere?

Great! I've uploaded a snapshot of the port, rebased on top of the
latest changes from Mercurial (so, the 5.5.8 release), to
https://github.com/bugaevc/scintilla on GitHub. Please take a look,
try building it, let me know how it goes :)

> Also porting Geany to GTK4 won't be trivial even when Scintilla is
> entirely working, so it likely won't happen quickly, but giving it a try
> might be a interesting and educational, given it's a fairly
> comprehensive user (well, we don't use each and every feature, but still).

I imagine it will be a separate bunch of work for Geany itself, yeah.

> While I hope support would be upstreamed, yes, I would be interested
> either way. Hopefully, and especially if you don't touch anything
> outside the platform layer itself, it would be easy to combine latest
> Scintilla with your platform layer even if they live separately.

I intentionally did not, and it is very easy, yes.

> Regards,
> Colomban

Sergey

Neil Hodgson

unread,
Nov 13, 2025, 10:27:32 PMNov 13
to Scintilla mailing list
Sergey Bugaev:

> I'm writing to announce that I have developed a mostly working port of
> Scintilla to the GTK 4 toolkit.

> snapshot of the port, rebased on top of the
> latest changes from Mercurial (so, the 5.5.8 release), to
> https://github.com/bugaevc/scintilla on GitHub


I'm a bit confused here. Are you also using the 'xuges' identity responsible for https://github.com/xuges/scintilla or are these separate or related projects?

> … But there are some considerations for applications
> & widgets as well: in particular Scintilla tries to round extents to
> physical pixels via a PixelDivisions value, which is, for one thing,
> an integer, with no way to represent a fractional scale like 2.5 or
> 1.75.

The PixelDivisions value is mostly for macOS or similar systems where windowing-layer coordinates may not match physical pixels but are always an integral number of physical pixels.

Win32 provides fractional scaling by allowing the application/thread/window to enrol as High DPI aware with windowing-layer coordinates then matching physical pixels. The drawing-layer calls may then use scaling if they want. Other behaviour like invalidation is also directed to the windowing-layer and do not use scaled coordinates.

> Another issue here is that Scintilla paints the background behind each
> piece of text individually as its own little rectangle; this for one
> thing has the downside of producing a lot of render nodes (a lot of
> small white rectangles, instead of one large white rectangle covering
> the whole widget area), and therefore a bunch of extra work for the
> GPU to push through.

Each text span (commonly associated with a lexeme) may have a different background colour to other spans. Other features (like highlighting breakpoint lines) may also impose a background colour. One common technique is to colour the background of major zones of the file differently: for HTML, text and tags may have one background but client-side and server-side scripts use different background colours.

Commonly, a view may have 80 lines and less than 10 spans per line, resulting in 800 pieces of text and 800 simple coloured rectangles. GPUs can normally draw many million rectangles per second so it's unlikely that 800 rectangles will be a major problem. Looking at profiles shows that its drawing text that is slow, not backgrounds.

A few years back, I thought this may be a worthwhile area to optimize so wrote some code that coalesced adjacent background rectangles and only drew when the background changed. While this would often reduce the number of background drawing calls to one per line, it had no effect on speed for Direct2D (Win32) or macOS. It's likely the system is batching rectangle drawing or even coalescing itself. If this produces a noticeable benefit on some platform, then it can be added, probably as an option.

> But also visually, while with integer scaling the
> sides of the rectangles align perfectly and the whole background ends
> up painted white, with fractional scaling the sides do not always
> align, and there are visible "seams". This is a general issue with
> fractional scaling, but it's aggravated by what Scintilla is doing
> (drawing these little rectangles and expecting them to align),

Drawing lines separately has been a performance and stability win in the past. Unfortunately, seams appear even when the entire background is painted a base background colour since file zones that use a different background colour will then part draw from both the current and previous line onto that background. There could be coalescing between lines but that won't work as well where 'eolfilled' is off. The selection also commonly covers multiple lines and is the focus of attention, so the appearance of seams is noticeable. An expensive option would be to produce a list of regions from the set of backgrounds possible but that may not be as stable when interactions move a feature such as a debugger current instruction line.

A potential addition here is for an optional base fill colour to be used as the background. Following drawing of text backgrounds can then assume that has already been drawn and refrain from redrawing with the same colour. This may reduce seaming, particularly for applications that have a consistent or mostly consistent background colour.

> There is some work on the GTK side [4] to enable opt-in *snapping* of
> render nodes to physical pixels, ...

Strict geometric scaling that does not respect the pixel grain is often disliked, sometimes intensely. Thin lines and thin-lined boxes are particularly noticeable when they are smeared over multiple pixels.

> I had used various tricks to somewhat reconcile Scintilla code's idea
> of each platform having a single global style with this GTK's idea of
> style inherently coming from a style sheet and differing per-widget
> and with time.

Applications and individual users often want detailed control over styling issues and it's unlikely they will write GTK style sheets when they want documentation comments to be a slightly bluer tone.

> Yet another topic is phases. In GTK, "every frame is perfect". We
> don't draw bad frames, because... why would we do such a thing? But
> Scintilla sometimes decides to just *adandon painting* if it discovers
> it needs to re-style or update something in the middle of painting.

Some Scintilla features, like syntax styling and word-wrapping are quite slow so are performed as background activities and may slip against input to avoid blocking.

Requiring that the entire document be fully prepared before painting will not be viable for huge documents. As well as syntax styling and word-wrapping, applications may also choose various features like spelling and linting that will require time.

> GObject introspection [6] is a cool feature of the GObject type system, where:

In the past I have had great hopes for language interoperability systems and they never achieved all that much. Microsoft's COM is the biggest success in this space but has still underachieved with users. I was 'the COM guy' at one company and have been through all the stages of belief and disappointment here.

> There are some bits of this in the GTK 2/3 port already, but for the
> GTK 4 port, I went all in.

This isn't a bad feature, especially if you can use it well yourself but don't expect it to be embraced by many.

A scripting language tightly integrated into an application with an object model customized to the language is much more likely to attract users than a generic, automatically translated, API.

> It would also be great if distributions (like Debian etc) packaged
> Scintilla as a normal library, much like GTK or any other, with the
> compiled library appearing in /lib/libscintilla.so (or such), the
> headers at /usr/include/ScintillaView.h, and so on. To that end, we
> might want to write and install a pkg-config file for Scintilla; this
> would enable others to link to the system build of Scintilla easily,
> e.g. with just `dependency("scintilla")` in Meson.

One hassle here is that Scintilla can be built with different GUI toolkits like Qt, Tk, and wxWidgets. If it's going to be installed into a global shared object directory then it may need a toolkit dongle on the name.

> Included with the port is a little application called Scintilla Demo,
> which is a very basic text editor, with tabs and menus and a
> ScintillaView in each tab.

Unfortunately, I couldn't make this build with a bunch of 'incompatible pointer type' errors. This is on Ubuntu 25.10.

For anyone else wanting to experiment, the prerequisites I needed (over standard Scintilla) to 'apt install' were libgtk-4-dev, gobject-introspection, valac, and blueprint-compiler.

> Speaking of SciTE, is there interest in porting that to GTK 4 (while
> simultaneously making it a lot more idiomatic)?

I'd like to retain GTK3 and even GTK2 support so this would probably be a fork, which is fine.

> I'm looking for feedback from Scintilla core developers. Do you want
> this to go upstream?

Eventually. It will depend on the degree of compatibility and whether it will be a support burden.

> Could we host the HTML documentation autogenerated using gi-docgen
> somewhere on scintilla.org? Is there a CI process?

There is no CI easily available on SourceForge. If the documentation is easy to generate then it can go on scintilla.org <http://scintilla.org/>.

> Would you be open to changing those little things about Scintilla core
> that would make the GTK 4 so much less hacky? (One example of this
> might be, not querying a "platform color" globally, but in relation to
> a specific instance of Scintilla.)

Scintilla has little global state. The Platform:: calls for colour and font are last-resort and effectively never used since these are determined by the application.

Neil

Sergey Bugaev

unread,
Nov 14, 2025, 8:57:54 AMNov 14
to scintilla...@googlegroups.com
Hello,

On Fri, Nov 14, 2025 at 6:27 AM 'Neil Hodgson' via scintilla-interest
<scintilla...@googlegroups.com> wrote:
>
> Sergey Bugaev:
>
> > I'm writing to announce that I have developed a mostly working port of
> > Scintilla to the GTK 4 toolkit.
> …
> > snapshot of the port, rebased on top of the
> > latest changes from Mercurial (so, the 5.5.8 release), to
> > https://github.com/bugaevc/scintilla on GitHub
>
> I'm a bit confused here. Are you also using the 'xuges' identity responsible for https://github.com/xuges/scintilla or are these separate or related projects?

uhh, no, that is someone else, and seemingly another GTK 4 port?

From a brief look at it, it looks to be based on the existing GTK 2/3
port. In particular it has the code to render itself using Cairo and
GtkDrawingArea (not even gtk_snapshot_append_cairo). Mine is a whole
new independent port, which also tries to do things better / more
conventionally than the GTK 2/3 port on various fronts, as described.

> > … But there are some considerations for applications
> > & widgets as well: in particular Scintilla tries to round extents to
> > physical pixels via a PixelDivisions value, which is, for one thing,
> > an integer, with no way to represent a fractional scale like 2.5 or
> > 1.75.
>
> The PixelDivisions value is mostly for macOS or similar systems where windowing-layer coordinates may not match physical pixels but are always an integral number of physical pixels.
>
> Win32 provides fractional scaling by allowing the application/thread/window to enrol as High DPI aware with windowing-layer coordinates then matching physical pixels. The drawing-layer calls may then use scaling if they want. Other behaviour like invalidation is also directed to the windowing-layer and do not use scaled coordinates.

Yes, I understand that.

Between these two, GTK behaves closer to Cocoa, in that
application/widget level coordinates are in logical pixels/units,
which are then scaled inside the toolkit to get to physical/device
pixels. This way, most of the application code never has to care about
the details of scaling (other than shipping icons with a larger
resolution etc).

Up until GTK 4.11, this only supported integer scaling, so as you're
saying about macOS, a single logical pixel would always correspond to
an integral number of physical pixels. The reality is that there are
displays out there for which e.g. 100% is too small and 200% is too
large (or 200% is too small and 300% is too large, as is the case with
this laptop I'm typing this on), hence, fractional scaling is needed.
The traditional way to implement this has been telling applications to
render at 300% and then downscaling the produced textures by 2x
compositor-side — this way, you get 150%, but the application/toolkit
only has to deal with integer scaling. I believe this is also what
macOS / Cocoa does as well. The downside with that approach of course
is that the toolkit does antialiasing etc for a pixel layout that
doesn't actually match the display.

Newer versions of GTK support full (or "native") fractional scaling
toolkit-side. This is generally much better for sharpness & clarity,
but indeed introduces the issues with seams and other kinds of
misalignment. It's a trade-off.

> > Another issue here is that Scintilla paints the background behind each
> > piece of text individually as its own little rectangle; this for one
> > thing has the downside of producing a lot of render nodes (a lot of
> > small white rectangles, instead of one large white rectangle covering
> > the whole widget area), and therefore a bunch of extra work for the
> > GPU to push through.
>
> Each text span (commonly associated with a lexeme) may have a different background colour to other spans. Other features (like highlighting breakpoint lines) may also impose a background colour.

Yes, sure. Seams are a lot less visible where the colors are different
along the sides of the seam. It's the case where the colors are the
same that is most visible.

> One common technique is to colour the background of major zones of the file differently: for HTML, text and tags may have one background but client-side and server-side scripts use different background colours.
>
> Commonly, a view may have 80 lines and less than 10 spans per line, resulting in 800 pieces of text and 800 simple coloured rectangles. GPUs can normally draw many million rectangles per second so it's unlikely that 800 rectangles will be a major problem. Looking at profiles shows that its drawing text that is slow, not backgrounds.
>
> A few years back, I thought this may be a worthwhile area to optimize so wrote some code that coalesced adjacent background rectangles and only drew when the background changed. While this would often reduce the number of background drawing calls to one per line, it had no effect on speed for Direct2D (Win32) or macOS. It's likely the system is batching rectangle drawing or even coalescing itself. If this produces a noticeable benefit on some platform, then it can be added, probably as an option.

Yes, so that sounds like it would help. It would likely make little
difference performance-wise, but the render tree would look tidier,
and there would be less seams visible.

> A potential addition here is for an optional base fill colour to be used as the background. Following drawing of text backgrounds can then assume that has already been drawn and refrain from redrawing with the same colour. This may reduce seaming, particularly for applications that have a consistent or mostly consistent background colour.

Yes, exactly, that's what I tried to implement on my side in
gtk4/ScintillaGTK.cxx:RemoveWhiteBackground. But this is a hack, and
only really helps when the background is the default white. It can be
easily removed if a more proper solution would be implemented in
Scintilla core.

> > I had used various tricks to somewhat reconcile Scintilla code's idea
> > of each platform having a single global style with this GTK's idea of
> > style inherently coming from a style sheet and differing per-widget
> > and with time.
>
> Applications and individual users often want detailed control over styling issues and it's unlikely they will write GTK style sheets when they want documentation comments to be a slightly bluer tone.

Yes, so we of course have to keep compatibility with applications
forcing specific colors and fonts, if they do.

The issue with this (as it's always the case when a specific color is
provided instead of going through the style sheet mechanism) is that
it won't (without additional work on each application's side) follow
system settings like dark mode, accent color, high contrast.

GtkSourceView has pairs of light/dark styles for this, and uses the
accent color for selection.

> > Yet another topic is phases. In GTK, "every frame is perfect". We
> > don't draw bad frames, because... why would we do such a thing? But
> > Scintilla sometimes decides to just *adandon painting* if it discovers
> > it needs to re-style or update something in the middle of painting.
>
> Some Scintilla features, like syntax styling and word-wrapping are quite slow so are performed as background activities and may slip against input to avoid blocking.
>
> Requiring that the entire document be fully prepared before painting will not be viable for huge documents. As well as syntax styling and word-wrapping, applications may also choose various features like spelling and linting that will require time.

Certainly, so, we don't have to highlight the entire document to be
able to display one screenful of text, only the visible part. (This is
too of course non-trivial — how would you know if there's a /* ten
thousand lines above, and everything after that should have been
highlighted as a comment?) If that's still too slow, the work could be
split in chunks, so we do one chunk of work per frame.

It's fine if we initially show the document unhighlighted, and
highlighting catches up later. It's not fine to produce broken/junk
frames, where we just decide to avoid painting altogether.

> > GObject introspection [6] is a cool feature of the GObject type system, where:
>
> In the past I have had great hopes for language interoperability systems and they never achieved all that much. Microsoft's COM is the biggest success in this space but has still underachieved with users. I was 'the COM guy' at one company and have been through all the stages of belief and disappointment here.

I'm not really familiar with COM (besides some superficial knowledge
about IUnknown/QueryInterface), so I cannot compare how successful it
has been compared to GObject.

But, GObject introspection (& numerous language bindings) certainly
exists and works and is widely adopted; it's not a pipe dream. It's
been a success, technologically and socially; I don't remember seeing
any language interoperability solution succeed to this extent on other
platforms (though again, I just might be uninformed on what COM has
achieved).

The proof is in the pudding. If you take, for example, the *core*
GNOME apps listed on the "Apps for GNOME" page [0], you'll find that:

[0]: https://apps.gnome.org/

* Audio Player (Decibels) is in TypeScript
* Calculator is in Vala
* Calendar is in C
* Camera (Snapshot) is in Rust
* Characters is in JavaScript (with parts in C)
* Clocks is in Vala
* Connections is in Vala
* Console (kgx) is in C
* Contacts is in Vala
* Disk Usage Analyzer (Baobab) is in Vala
* Disks (disk utility) is in C, with newer parts in Rust
* Document Scanner (SimpleScan) is in Vala
* Document Viewer (Papers) is mixed C and (newer parts) in Rust
* Files (Nautilus) is in C
* Fonts is in C
* Help (Yelp) is in C
* Image Viewer (Loupe) is in Rust
* Logs is in C
* Maps is in JavaScript
* Music is in Python
* Settings (Control Center) is in C
* Software is in C
* System Monitor is in C++
* Text Editor is in C
* Tour is in Rust
* Video Player (Showtime) is in Python
* Weather is in TypeScript
* Web (Epiphany) is in C

So, while there is a lot of C (mostly in older apps), and it's not
going anywhere, other languages — mostly Python, Rust,
JavaScript/TypeScript (GJS), Vala — are also being used, a lot. That's
GNOME core apps; there's even more of non-C language usage in the
GNOME Circle apps, and the wider ecosystem (just to name a few:
Foliate is in JavaScript, rnote is in Rust, Apostrophe is in Python).
The very UI of GNOME Shell is (in)famously written in JavaScript.
elementary OS people are writing all of their code, apps and
otherwise, in Vala [1]. I've recently seen this visualization of
language usage stats [2] (made by Sophie).

[1]: https://blog.elementary.io/why-we-write-elementary-apps-in-vala/
[2]: https://sophie-h.pages.gitlab.gnome.org/app-overview/charts.html

There are libraries in the platform whose API was not designed with
introspection and language bindings in mind, and this is seen as a
deficiency. Pango is an example here, although it is still possible to
use Pango through introspection to a large extent.

So: it's a vibrant and language-diverse ecosystem, and GObject
introspection, which enables language interoperability, is central to
it. I don't think I'm being delusional or overly hopeful here :)

> > There are some bits of this in the GTK 2/3 port already, but for the
> > GTK 4 port, I went all in.
>
> This isn't a bad feature, especially if you can use it well yourself but don't expect it to be embraced by many.
>
> A scripting language tightly integrated into an application with an object model customized to the language is much more likely to attract users than a generic, automatically translated, API.

I suppose you're thinking of SciTE and Lua?

It is of course possible for an application which uses Scintilla to
bring its own scripting language integration which is based on some
other technology (not GObject introspection). That being said, GObject
introspection makes it pretty easy to do so (bring in a scripting
language), so that opens a bunch of interesting possibilities.

> > It would also be great if distributions (like Debian etc) packaged
> > Scintilla as a normal library, much like GTK or any other, with the
> > compiled library appearing in /lib/libscintilla.so (or such), the
> > headers at /usr/include/ScintillaView.h, and so on. To that end, we
> > might want to write and install a pkg-config file for Scintilla; this
> > would enable others to link to the system build of Scintilla easily,
> > e.g. with just `dependency("scintilla")` in Meson.
>
> One hassle here is that Scintilla can be built with different GUI toolkits like Qt, Tk, and wxWidgets. If it's going to be installed into a global shared object directory then it may need a toolkit dongle on the name.

Sure, so that'd be libscintilla-gtk4.so and dependency('scintilla-gtk4') then.

> > Included with the port is a little application called Scintilla Demo,
> > which is a very basic text editor, with tabs and menus and a
> > ScintillaView in each tab.
>
> Unfortunately, I couldn't make this build with a bunch of 'incompatible pointer type' errors. This is on Ubuntu 25.10.
>
> For anyone else wanting to experiment, the prerequisites I needed (over standard Scintilla) to 'apt install' were libgtk-4-dev, gobject-introspection, valac, and blueprint-compiler.

FWIW, only libgtk-4-dev from this list is required to build
libscintilla.so (i.e. 'make shared'), the rest are for the
introspection, Vala, and the demo.

I just tried building this on Ubuntu 25.10, using their provided
toolchain, and the packages you listed, and I only get some
"-Wincompatible-pointer-types" warnings (when building the demo, not
Scintilla itself). These warnings are unfortunately an issue with the
C code generated by Vala (it messes up things like 'const char * const
*' vs 'char **', which the compilers then warn about); I actually had
some work that attempted to fix that on the Vala compiler side. While
it's unfortunate, those are warnings, not erros, and can be ignored,
the build should still succeed, leaving you with an executable at
gtk4/ScintillaDemo/demo, does it not?

> > Could we host the HTML documentation autogenerated using gi-docgen
> > somewhere on scintilla.org? Is there a CI process?
>
> There is no CI easily available on SourceForge. If the documentation is easy to generate then it can go on scintilla.org <http://scintilla.org/>.

Please try 'make doc' (after installing gi-docgen), then see the
gtk4/doc/Scintilla-5/ directory (start at index.html).

In addition to actually writing more of the documentation, there are
some configuration improvements to be made there as well. We should
fill out more metadata for the project (see gi-docgen doc page [3])
such as authors and license.

Assuming Scintilla GTK 4 will eventually be upstream, for the
'source-location' feature, what's the appropriate way to link to a
specific line of code in a file on SourceForge? On GitHub and GitLab
(and many others), this is done with #L<line-number> suffix, e.g.
clicking on [4] should bring you directly to the definition of
gtk_accessible_set_accessible_parent in the GTK code base. How would I
do the same on SourceForge?

[3]: https://gnome.pages.gitlab.gnome.org/gi-docgen/project-configuration.html
[4]: https://gitlab.gnome.org/GNOME/gtk/-/blob/main/gtk/gtkaccessible.c#L163

Sergey

Neil Hodgson

unread,
Nov 20, 2025, 6:22:46 AM (8 days ago) Nov 20
to scintilla...@googlegroups.com
Sergey Bugaev:

> Between these two, GTK behaves closer to Cocoa, in that
> application/widget level coordinates are in logical pixels/units,
> which are then scaled inside the toolkit to get to physical/device
> pixels. This way, most of the application code never has to care about
> the details of scaling (other than shipping icons with a larger
> resolution etc).

Since Windows allows fractional scaling, I'd say that GTK4 is more
similar to Windows than Cocoa.

To minimize difficulties now and with applying future changes, it
could help to make this platform layer behave as much as possible like
Windows. Windows is the most popular platform for Scintilla,
accounting for most of the users and developers with Linux and macOS
minorities. Linux is further split into GTK and Qt, wxWidgets, Tk, and
other toolkits.

To avoid seams, the scale of the drawing context could be changed to
ensure that Scintilla is again addressing whole pixels. That would
then need to be balanced by changing font creation to invert that
scale so text appears the expected size.

> Yes, so we of course have to keep compatibility with applications
> forcing specific colors and fonts, if they do.
>
> The issue with this (as it's always the case when a specific color is
> provided instead of going through the style sheet mechanism) is that
> it won't (without additional work on each application's side) follow
> system settings like dark mode, accent color, high contrast.

Applications and users really do implement detailed control over
styling with themes and complex style configuration dialogs. This is
one of the main reasons that applications choose Scintilla. There are
often between 20 and 100 individual styles set by a language mode with
many distinct colours.

> GtkSourceView has pairs of light/dark styles for this, and uses the
> accent color for selection.

Using the accent colour for selection is currently possible on Cocoa.
It is a bit more complex there as Cocoa allows the user to set
different accent and selection colours.

Selection background colors on Cocoa go through the elements system
which also allows the application to override the colours if desired.
See SC_ELEMENT_SELECTION_BACK and ScintillaCocoa::UpdateBaseElements
which is responsible for reflecting user preferences and changes to
these.

There are 5 selection background colours currently defined by
Scintilla to allow differentiating additional (and secondary)
selections and to choose a different appearance when the window does
not have focus.

> There are libraries in the platform whose API was not designed with
> introspection and language bindings in mind, and this is seen as a
> deficiency.

There is a lot of code in the gtk4 directory to support introspection
and wrapping the API: 130K for enumerations in ScintillaEnums.[ch] and
around 22K for a partial API wrapper.

For Qt there are separate directories for ScintillaEditBase and
ScintillaEdit (which includes an API wrapper) so that it was easier to
understand and modify the base functionality.

> I just tried building this on Ubuntu 25.10, using their provided
> toolchain, and the packages you listed, and I only get some
> "-Wincompatible-pointer-types" warnings (when building the demo, not

The actual failure was not finding the scintilla library and that was
due to building inside the ScintillaDemo directory instead of gtk4. It
is working now.

> fill out more metadata for the project (see gi-docgen doc page [3])
> such as authors and license.

There should also be a mention of the license in the source code
files. All source code distributed with Scintilla uses
https://www.scintilla.org/License.txt . It is OK to add additional
license possibilities (as Sun did for files in the cocoa directory)
but everything (distributed from scintilla.org) must also allow use
with the main license.

Neil
Reply all
Reply to author
Forward
0 new messages