CrystalCatalystLibrary: A Cross-Platform Surface for Windows, Pixels, and Graphics

View CrystalCatalystLibrary on GitHub

Back to overview

A portable application needs more than code that compiles on multiple operating systems. It needs a dependable place where the application can meet the operating system itself.

Windows must be created and destroyed. Input events must cross platform boundaries. Pixel buffers must reach the screen. OpenGL contexts must be configured and activated. Icons and cursors must be translated into native resources. Clipboard and drag-and-drop operations must cooperate with operating-system protocols. Managed code must be able to use these facilities without losing control of native memory or object lifetime.

CrystalCatalystLibrary provides that meeting place.

It is a cross-platform native and managed windowing and rendering substrate for the Crystal and NewAge ecosystem. Its native C++ layer owns the platform-facing implementation, while a stable exported interface makes that functionality available to native callers and to a managed .NET wrapper.

The central architectural idea can be expressed as a simple path:

Application or rendering engine
        ↓
Managed CrystalWindow API
        ↓
Exported native ABI
        ↓
Platform-specific window implementation
        ↓
Windows or Linux/X11

Pixel data, callbacks, graphics contexts, and data-interchange operations move through this seam without requiring the higher-level application to implement each operating system separately.

A Surface Rather Than a Full Application Framework

CrystalCatalystLibrary is best understood as a substrate.

It does not attempt to prescribe an entire user-interface model, widget hierarchy, document system, or application architecture. Instead, it supplies the native window and rendering surface upon which those higher-level systems can be built.

Its current capabilities include:

  • native window creation and lifecycle management;

  • window display, activation, closing, and redraw scheduling;

  • title, size, and location management;

  • keyboard and mouse event delivery;

  • mouse capture and release;

  • focus, resize, idle, and close callbacks;

  • direct pixel presentation;

  • configurable OpenGL contexts;

  • custom and standard cursors;

  • application and window icons;

  • clipboard infrastructure;

  • drag-and-drop infrastructure;

  • managed .NET bindings;

  • SkiaSharp and SVG rendering support;

  • pixel interchange between native and managed systems.

This makes CrystalCatalystLibrary useful at several levels. A small program can create a simple window and present an image. A graphics application can establish an OpenGL context and manage rendering itself. A managed application can render through SkiaSharp and hand the resulting pixels to the native window. A more ambitious framework can use CrystalCatalyst as its platform layer while defining an entirely different scene graph or interaction model above it.

The library supplies the surface without demanding ownership of everything drawn upon it.

Native Ownership of Platform Behavior

The native C++ layer is responsible for the realities of operating-system integration.

CrystalCatalystLibrary currently separates common interfaces from Linux- and Windows-specific implementations. Shared headers define the behavior expected of a Crystal window, while platform directories provide the implementation appropriate to each operating system.

This is an important architectural choice. Cross-platform development is often weakened by one of two extremes:

  1. platform behavior is scattered throughout otherwise portable code; or

  2. platform differences are hidden behind an abstraction so restrictive that important native capabilities become inaccessible.

CrystalCatalystLibrary takes a middle path. The common interface provides a stable vocabulary, but the implementation remains explicitly platform-aware. The system does not pretend that X11 and the Windows API are identical. It instead defines what a Crystal window must be able to do and gives each platform room to do it correctly.

The native build currently includes common subsystems for:

  • application lifecycle;

  • window creation and operation;

  • pixel structures and conversion;

  • clipboard behavior;

  • drag-and-drop;

  • generalized data interchange;

  • synchronization;

  • font-related infrastructure;

  • platform selection.

CMake chooses the active platform implementation and builds it into the shared CrystalCatalystLibrary native library. The project then links against JWCEssentials, inheriting the lower-level workspace, export, UTF-8 string, and native utility conventions established by that repository.

The Exported ABI as a Stable Seam

CrystalCatalystLibrary exposes its native functionality through a set of exported procedural functions.

This exported ABI is one of the most important parts of the design. It separates the public interoperability surface from the internal C++ implementation.

Representative functions include:

CrystalWindow_Create
CrystalWindow_CreateSimple
CrystalWindow_Show
CrystalWindow_Close
CrystalWindow_PostClose

CrystalWindow_SetTitle
CrystalWindow_SetSize
CrystalWindow_SetLocation
CrystalWindow_Activate

CrystalWindow_PresentPix
CrystalWindow_PresentImage
CrystalWindow_QueueRedraw

CrystalWindow_SetCursor
CrystalWindow_SetStandardCursor
CrystalWindow_SetIcon

CrystalWindow_GLInit
CrystalWindow_GLInitVersioned
CrystalWindow_GLInitAdvanced
CrystalWindow_GLMakeCurrent
CrystalWindow_GLPresent
CrystalWindow_GLGetProcAddress

The C++ side remains free to use virtual classes, platform-specific subclasses, native handles, and internal state. Consumers interact through exported functions and opaque window handles.

That creates a durable boundary.

A native C or C++ application can call the exported API directly. A .NET application can reach the same functions through platform invocation. Other languages with foreign-function support could also bind to this seam without needing to understand the library’s internal class hierarchy.

This approach also makes the native library easier to evolve. Internal implementation details can change while the exported contract remains recognizable.

A Rich Window Event Model

CrystalCatalystLibrary’s window abstraction is not limited to creating a rectangle and placing pixels inside it. It defines a broad callback surface through which native events can be delivered to consuming code.

The callback model includes:

  • draw events;

  • key-down and key-up events;

  • mouse movement;

  • mouse-button press and release;

  • mouse enter and leave;

  • resize;

  • close;

  • focus gain and loss;

  • idle processing;

  • clipboard events;

  • drag-receive events;

  • drag-provide events;

  • data-interchange errors.

Mouse buttons are represented through a cross-platform enumeration that includes the primary buttons, vertical and horizontal wheel movement, and additional mouse buttons. Standard cursor choices are similarly represented through a shared enumeration covering arrows, text selection, waiting, crosshairs, movement, resize directions, links, and prohibited actions.

The event surface gives managed and native applications access to meaningful interaction without forcing them to consume raw platform messages directly.

At the same time, CrystalCatalystLibrary retains an extension seam through message-handler registration. Callers can associate named handlers with native function pointers, enabling the generated managed wrapper to connect .NET delegates to native callbacks.

This mechanism is especially important for generated interoperability code. The managed class can expose familiar properties such as draw or keyboard callbacks while internally translating those delegates into function pointers recognized by the native library.

Managed Objects Backed by Native Windows

The .NET layer presents the native system through a managed CrystalWindow class.

Applications can create windows with familiar managed calls:

Application.Init(args);

var window = CrystalWindow.Create(
    800,
    600,
    "CrystalCatalyst"
);

window.OnDraw = wnd =>
{
    // Render or present pixels.
};

window.OnClose = wnd =>
{
    wnd.ApplicationRelease();
};

window.ApplicationRetain();
window.Show(true);

Application.Run();

Behind this approachable surface is an explicit native handle.

The managed wrapper maintains a thread-safe cache that maps native pointers to managed CrystalWindow objects. Weak references allow callbacks that receive a native window pointer to recover the appropriate existing managed object without forcing that wrapper to remain alive forever.

This solves an important interoperability problem. The native system and the managed runtime have different object and memory models. A window created from .NET may later appear inside a native callback. Without a mapping layer, that pointer could produce duplicate managed wrappers or make identity difficult to preserve.

CrystalCatalystLibrary treats object identity as part of the interop contract.

The managed wrapper also implements disposal behavior. Closing a managed window removes it from the pointer cache, invokes the native close path, and clears the handle to reduce the risk of repeated cleanup.

This does not eliminate the need for careful native-resource management, but it gives managed consumers a recognizable ownership model.

Application Retention and Event-Loop Lifetime

CrystalCatalystLibrary distinguishes between the existence of an individual window object and the lifetime of the application event loop.

A window can retain the application when it becomes active and release the application when it closes. This allows the native application layer to determine whether the event loop still has meaningful work to perform.

The pattern is valuable for applications that may create more than one window or whose lifecycle does not map cleanly to a single global form.

Rather than assuming that closing any window terminates the process, the application can remain alive while retained windows or services still exist. Conversely, the event loop can exit when all relevant participants have released it.

Crystal windows also provide an uptime timer based on a monotonic clock. A caller can query elapsed time or reset the timer, which is useful for animation, diagnostics, demos, and frame-relative behavior without relying on a wall clock that may change.

PixData as the Common Image Currency

One of CrystalCatalystLibrary’s strongest architectural choices is its use of PixData as a shared image-transfer structure.

A rendering engine may have its own internal representation. OpenGL uses textures and framebuffers. SkiaSharp uses Skia surfaces and pixel formats. The operating system may expect a bitmap, icon, cursor resource, or platform-specific image buffer.

CrystalCatalystLibrary does not require all of those systems to become the same thing. Instead, it provides a common exchange point.

PixData carries pixel memory, dimensions, format information, and cleanup behavior across native and managed boundaries. It can be used for:

  • presenting a rendered image in a window;

  • supplying a window icon;

  • creating a custom cursor;

  • transferring stock icon data;

  • receiving output from Skia;

  • receiving captured or rendered OpenGL output;

  • moving image data between managed and native components.

The common Skia presentation path currently uses a BGRA 8-bit format:

bgra:int8

This explicit format naming is preferable to passing an unqualified byte pointer. Width and height alone are not enough to interpret an image safely. Channel order, component type, and memory ownership are also part of the image contract.

The PixData design includes a cleanup callback so that memory allocated on one side of the interop boundary can be released correctly. Managed consumers must dispose of pixel data when ownership requires it, allowing the appropriate native cleanup function to run.

This is a practical example of interoperability that respects memory ownership instead of hiding it.

Presenting Pixels Directly

The simplest rendering path is direct pixel presentation.

A caller can prepare a PixData buffer and pass it to PresentPix, or call the lower-level PresentImage interface with an explicit pixel format, data pointer, length, width, and height.

This makes CrystalCatalystLibrary rendering-engine agnostic.

The pixels might come from:

  • a software renderer;

  • an image decoder;

  • procedural drawing code;

  • SkiaSharp;

  • an OpenGL readback;

  • a simulation;

  • a generated visualization;

  • a remote image source;

  • a custom retained-mode scene system.

CrystalCatalystLibrary does not need to know how the image was produced. Its responsibility is to transfer that image into the native presentation surface.

On Windows, operating-system-level double buffering is supported through GDI. On Linux, the current core window implementation targets X11.

This direct presentation model is particularly useful for cross-platform applications whose rendering logic already produces complete frames. It also offers a straightforward route for testing the window layer independently of a more complex GPU pipeline.

OpenGL Without Losing Platform Control

CrystalCatalystLibrary also supports native OpenGL contexts.

Applications may request a default context, specify a major and minor version, or provide a more detailed GLOptions structure. Advanced options include:

  • OpenGL major and minor version;

  • core, compatibility, or unspecified profile;

  • debug-context request;

  • forward compatibility;

  • depth-buffer size;

  • stencil-buffer size;

  • alpha-buffer size;

  • double buffering;

  • stereo buffering;

  • strict version behavior.

Once initialized, callers can:

  • make the window’s OpenGL context current;

  • query the resulting OpenGL version;

  • resolve OpenGL procedure addresses;

  • present or swap the rendered frame.

This is an intentionally low-level but valuable surface. CrystalCatalystLibrary does not force applications into one managed OpenGL wrapper. By exposing GLGetProcAddress, it allows a binding such as Silk.NET—or another loader—to resolve functions against the actual context owned by the native window.

The managed repository includes a CrystalOpenGL subsystem built around Silk.NET. Its helpers cover texture pixel transfer, framebuffer-backed rendering, conversion to PixData, and capability checks such as direct-state-access availability.

This means the OpenGL path participates in the same broader image architecture:

OpenGL rendering
      ↓
Texture or framebuffer
      ↓
PixData when readback is needed
      ↓
CrystalCatalyst presentation or interchange

Applications can also render directly to the native window’s active context and call GLPresent, avoiding an unnecessary CPU pixel handoff when direct GPU presentation is appropriate.

State Politeness in a Shared Graphics Context

Graphics helper code can easily become disruptive when it assumes exclusive ownership of the OpenGL state machine.

CrystalCatalystLibrary’s managed rendering helpers are designed with the need to preserve surrounding state in mind. The OpenGL subsystem tracks concerns such as framebuffer bindings, renderbuffer bindings, and viewport restoration when performing offscreen operations.

This is an important quality for reusable graphics infrastructure.

A rendering helper should not quietly leave the caller’s context in an unexpected configuration. CrystalCatalyst’s OpenGL layer is evolving around a principle of state politeness: use the state required for an operation, then restore what the caller reasonably expects to remain intact.

That allows its rendering utilities to coexist with larger OpenGL applications rather than functioning only in isolated demos.

SkiaSharp and SVG as a Managed Rendering Path

The CrystalSkia.net project adds a high-level managed rendering path based on SkiaSharp and Svg.Skia.

Its SvgSkiaRenderer can turn SVG content into PixData. Once rendered, the same asset can serve several roles:

  • a window image;

  • a window icon;

  • a custom cursor;

  • a frame of an animated icon;

  • a frame of an animated cursor.

The renderer supports transformation matrices, fixed output sizes, cropping, transparent borders, and hotspot translation.

A simplified path looks like this:

SVG source
    ↓
Svg.Skia representation
    ↓
SkiaSharp rendering
    ↓
PixData
    ↓
PresentPix, IconPix, or CursorPix
    ↓
Native window resource

This demonstrates the value of a shared pixel currency. The SVG renderer does not need platform-specific icon or cursor code. It produces PixData, and the native Crystal window converts that data into the correct operating-system resource.

Custom cursors reveal why additional rendering controls matter. Cropping an SVG to its visible bounds can reduce unused space, but some platform cursor implementations need transparent border space to prevent clipping or corruption. The renderer therefore allows an explicit border to be added after cropping.

Because that border changes the final pixel coordinates, the renderer also translates the cursor hotspot into the new image space.

This is a small but representative example of CrystalCatalystLibrary’s role: connect high-level rendering intent to the exact practical requirements of the platform boundary.

Icons and Cursors as First-Class Pixel Consumers

Window icons and cursors are treated as real consumers of the rendering pipeline rather than as isolated file-loading features.

A caller can provide either a PixData structure or raw image details. Standard cursors are available through a cross-platform enumeration, while custom cursors accept pixel data and an explicit hotspot.

This enables applications to use dynamically generated graphics instead of being limited to static operating-system resources.

An SVG can be rotated, recolored, transformed, or animated in managed code, rendered through Skia, and passed to the native window as an icon or cursor. Procedural pixel data can follow the same route.

That opens creative possibilities uncommon in minimal window wrappers:

  • icons that reflect live application state;

  • animated progress or activity icons;

  • application-specific cursor tools;

  • cursor previews generated from current brush settings;

  • theme-responsive vector assets;

  • high-resolution assets rendered at the exact required size.

The library does not prescribe those uses, but its architecture makes them natural.

Clipboard and Drag-and-Drop as Data Interchange

Clipboard and drag-and-drop behavior are built around a shared data-interchange concept.

This is more powerful than treating either system as a single string transfer.

A data-interchange operation may advertise or receive multiple formats. The receiving side can inspect what is available and select the format it understands. The providing side can respond when a particular representation is chosen.

The callback surface includes events for:

  • starting a drag receive operation;

  • entering or leaving a target;

  • pointer motion during dragging;

  • completing a drop;

  • choosing a receive format;

  • reporting drag-provider status;

  • notifying the provider of the selected format;

  • reporting completion;

  • selecting clipboard representations;

  • receiving clipboard data;

  • reporting interchange errors.

This structure reflects the actual nature of operating-system data exchange. A single dragged item may be representable as plain text, HTML, a URI list, or an application-specific format. The receiver should not have to commit prematurely to one universal payload type.

On Linux, the implementation has included X11 selection and drag-and-drop protocol work. On Windows, native system integration provides the corresponding platform path.

By placing clipboard and drag-and-drop beneath one conceptual interchange layer, CrystalCatalystLibrary creates room for richer transfers while keeping platform protocol details out of the consuming application.

Generated Bindings as an Architectural Boundary

A substantial portion of the managed interoperability surface is generated.

Generated C# files contain P/Invoke declarations, native type mappings, window methods, callback translation, and related wrapper code. Handwritten managed logic can then live outside that generated region.

This separation has several benefits:

  • exported native declarations remain the source-facing contract;

  • repetitive wrapper code can be reproduced consistently;

  • callbacks follow common translation patterns;

  • native additions can propagate into managed bindings;

  • maintainers can distinguish generated code from intentional high-level logic;

  • interop conventions remain grep-friendly and machine-readable.

The repository includes a generator signature file that describes native types and mappings. Native exports are deliberately kept on individual lines so they can be discovered reliably by tooling.

This is a practical expression of a larger NewAge theme: source structure should be understandable not only to compilers and human maintainers, but also to the development tools and agents working with the code.

Experiments as Proof of Generality

CrystalCatalystLibrary includes tests and experiments that exercise its different rendering paths.

These include native test clients, simple managed windows, OpenGL examples, texture experiments, SVG icon and cursor animation, and experimental visual demonstrations.

Such projects serve a purpose beyond showcasing the library. They test whether the abstraction is genuinely general.

A window substrate may look portable until it is asked to support:

  • continuous animation;

  • OpenGL version negotiation;

  • texture presentation;

  • cursor hotspots;

  • custom icons;

  • framebuffer readback;

  • event-driven input;

  • dynamic SVG rendering;

  • data exchange;

  • native and managed ownership at the same time.

The experiments pressure these seams and reveal where the abstraction needs refinement.

CrystalCatalystLibrary has grown through this kind of practical use. Features are not merely theoretical API entries; they have been exercised as parts of actual rendering and interaction paths.

A Platform Layer That Does Not Hide the Platform

CrystalCatalystLibrary’s portability philosophy is closely aligned with JWCEssentials.

The goal is not to erase operating-system differences. It is to place them where they can be handled intentionally.

The common Crystal window interface defines the capabilities expected by the rest of the ecosystem. Linux and Windows implementations remain responsible for the details of event loops, native handles, cursor creation, graphics contexts, clipboard ownership, window messages, and presentation mechanisms.

Higher-level code gains a shared interface, but the project retains explicit extension points for:

  • new platform implementations;

  • new native exports;

  • new managed bindings;

  • new pixel formats;

  • new rendering helpers;

  • new OpenGL capabilities;

  • new Skia-based rendering patterns;

  • richer data-interchange behavior.

This makes the abstraction permeable rather than sealed. When a real platform distinction matters, the architecture has a place for it.

Its Relationship to JWCEssentials

CrystalCatalystLibrary builds directly upon the foundation established by JWCEssentials.

JWCEssentials supplies lower-level conventions and facilities such as:

  • native import and export handling;

  • UTF-8 string structures;

  • transferable native structures;

  • shared CMake functions;

  • NewAge workspace discovery;

  • build-lane configuration;

  • artifact staging.

CrystalCatalystLibrary uses that foundation to address a higher layer of responsibility.

The relationship can be summarized as:

JWCEssentials
    Portable native foundations
    Shared workspace conventions
    Common structures and tooling
             ↓
CrystalCatalystLibrary
    Windows and event loops
    Pixels and presentation
    OpenGL contexts
    Icons and cursors
    Clipboard and drag-and-drop
    Managed application access

This is the first clear architectural progression within the public repository family. JWCEssentials makes modular native development predictable. CrystalCatalystLibrary uses that predictability to provide portable application surfaces.

A Foundation for Future Interfaces

CrystalCatalystLibrary already supports direct pixels, OpenGL, SkiaSharp, SVG rendering, input events, and native data exchange. Those capabilities also make it a useful foundation for future interface systems.

A retained-mode scene graph could render through Skia into PixData. A lightweight game or visualization engine could render directly through OpenGL. A symbolic application could generate SVG assets and use them as live interface elements. A document system could use clipboard and drag-and-drop interchange without implementing X11 and Windows protocols itself.

Because CrystalCatalystLibrary does not impose a fixed widget toolkit, these systems can develop according to their own needs.

The library’s role is to provide the stable native surface beneath them:

  • somewhere to draw;

  • somewhere to receive input;

  • somewhere to exchange data;

  • somewhere to establish a graphics context;

  • somewhere for managed and native code to meet.

Its Place in the Public Ecosystem

Within the public JWC software ecosystem, CrystalCatalystLibrary is the application and graphics platform layer.

JWCEssentials provides foundational utilities and workspace conventions. CrystalCatalystLibrary builds upon those foundations to create native windows and connect them to managed rendering systems. Higher-level repositories such as NewLib can then use CrystalCatalyst as a reusable bridge to operating-system windows, graphics, input, and data exchange.

Its purpose can be summarized as:

CrystalCatalystLibrary provides a portable but platform-conscious surface through which native and managed applications can create windows, receive events, present pixels, use OpenGL and Skia, and participate in operating-system data exchange.

View CrystalCatalystLibrary on GitHub

Back to overview