FLTK

Abbreviation for Fast Light Tool Kit. Extremely lightweight and fast cross-platform GUI library. Achieves small binary size with minimal dependencies. Optimal for embedded systems and resource-constrained environments.

desktopC++lightweightcross-platformGUIOpenGL

GitHub Overview

fltk/fltk

FLTK - Fast Light Tool Kit - https://github.com/fltk/fltk - cross platform GUI development

Stars1,935
Watchers52
Forks310
Created:September 18, 2018
Language:C++
License:Other

Topics

None

Star History

fltk/fltk Star History
Data as of: 7/15/2025, 10:55 PM

Desktop Framework

FLTK (Fast Light Toolkit)

Overview

FLTK is a lightweight and fast cross-platform C++ GUI toolkit. It runs on Unix/Linux (X11 and Wayland), Microsoft Windows, and macOS, with a very small memory footprint (Hello World program is around 100KB) and supports 3D graphics via OpenGL.

Details

FLTK uses a "minimalist" design philosophy and focuses on GUI functionality as a lightweight library. In contrast to other UI libraries like GTK, Qt, and wxWidgets, FLTK specializes only in user interface functionality, resulting in very small file sizes.

Features

  • Lightweight design: Minimal memory footprint (Hello World around 100KB)
  • Cross-platform: Unix/Linux (X11, Wayland), Windows, macOS support
  • OpenGL integration: 3D graphics via built-in GLUT emulation
  • FLUID: Graphical GUI designer tool included
  • Static linking: Usually generates standalone executables with static linking
  • Language bindings: Support for Lua, Perl, Python, Ruby, Rust, Tcl

Architecture

  • Widget hierarchy: All UI elements derive from Fl class
  • Event-driven: Callback functionality and event processing
  • Drawing system: Fast 2D drawing and OpenGL integration
  • Layout management: Automatic layout with Group, Pack, Tile, etc.
  • Modern support: Wayland backend (since 1.4.0)

Latest Version

  • Stable: 1.4.3 (maintenance mode)
  • Development: 1.5.x-20250620-acd77fa8dc41 (weekly snapshots)
  • Wayland support: Implemented since 1.4.0

Pros and Cons

Pros

  • Extremely lightweight and fast (minimal memory usage)
  • Simple and easy-to-learn API
  • Easy distribution with static linking
  • Integrated OpenGL/3D graphics support
  • Visual GUI design with FLUID
  • Long-term stability and compatibility
  • Rich language bindings

Cons

  • Limited native OS look and feel
  • Not suitable for modern design or material design
  • Fewer widget types than other toolkits
  • Relatively small community size
  • Limited documentation compared to major frameworks
  • Slow response to modern UI/UX trends

Reference Pages

Code Examples

Hello World

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Box.H>

int main(int argc, char **argv) {
    Fl_Window *window = new Fl_Window(340, 180);
    Fl_Box *box = new Fl_Box(20, 40, 300, 100, "Hello, World!");
    box->box(FL_UP_BOX);
    box->labelfont(FL_BOLD + FL_ITALIC);
    box->labelsize(36);
    box->labeltype(FL_SHADOW_LABEL);
    window->end();
    window->show(argc, argv);
    return Fl::run();
}

Basic Window and Controls

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Input.H>
#include <FL/Fl_Output.H>
#include <FL/fl_ask.H>

void button_callback(Fl_Widget* widget, void* data) {
    Fl_Input* input = (Fl_Input*)data;
    Fl_Output* output = (Fl_Output*)widget->parent()->child(2);
    
    const char* text = input->value();
    if (strlen(text) > 0) {
        char message[256];
        snprintf(message, sizeof(message), "Hello, %s!", text);
        output->value(message);
        fl_message("Message updated");
    } else {
        fl_alert("Please enter a name");
    }
}

int main(int argc, char **argv) {
    Fl_Window* window = new Fl_Window(400, 200, "FLTK Basic Example");
    
    Fl_Input* input = new Fl_Input(100, 30, 200, 30, "Name:");
    input->value("World");
    
    Fl_Button* button = new Fl_Button(150, 80, 100, 40, "Greet");
    button->callback(button_callback, input);
    
    Fl_Output* output = new Fl_Output(100, 140, 200, 30, "Result:");
    
    window->end();
    window->show(argc, argv);
    return Fl::run();
}

Menu and Window Management

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Menu_Bar.H>
#include <FL/Fl_Text_Display.H>
#include <FL/Fl_Text_Buffer.H>
#include <FL/fl_ask.H>

Fl_Text_Buffer* textbuf = nullptr;

void menu_callback(Fl_Widget* widget, void* data) {
    Fl_Menu_Bar* menubar = (Fl_Menu_Bar*)widget;
    const Fl_Menu_Item* item = menubar->mvalue();
    
    if (strcmp(item->label(), "New") == 0) {
        textbuf->text("");
    } else if (strcmp(item->label(), "Open") == 0) {
        const char* filename = fl_file_chooser("Open File", "*.txt", "");
        if (filename) {
            textbuf->loadfile(filename);
        }
    } else if (strcmp(item->label(), "Save") == 0) {
        const char* filename = fl_file_chooser("Save", "*.txt", "");
        if (filename) {
            textbuf->savefile(filename);
        }
    } else if (strcmp(item->label(), "Exit") == 0) {
        exit(0);
    } else if (strcmp(item->label(), "About") == 0) {
        fl_message("FLTK Sample Application\nVersion 1.0");
    }
}

int main(int argc, char **argv) {
    Fl_Window* window = new Fl_Window(600, 400, "FLTK Text Editor");
    
    // Menu bar
    Fl_Menu_Bar* menubar = new Fl_Menu_Bar(0, 0, 600, 25);
    menubar->add("File/New", FL_CTRL + 'n', menu_callback);
    menubar->add("File/Open", FL_CTRL + 'o', menu_callback);
    menubar->add("File/Save", FL_CTRL + 's', menu_callback);
    menubar->add("File/Exit", FL_CTRL + 'q', menu_callback);
    menubar->add("Help/About", 0, menu_callback);
    
    // Text editor
    textbuf = new Fl_Text_Buffer();
    Fl_Text_Display* textdisp = new Fl_Text_Display(0, 25, 600, 375);
    textdisp->buffer(textbuf);
    textbuf->text("Enter your text here...");
    
    window->resizable(textdisp);
    window->end();
    window->show(argc, argv);
    return Fl::run();
}

Multiple Windows and Dialogs

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Input.H>
#include <FL/Fl_Check_Button.H>
#include <FL/Fl_Choice.H>
#include <FL/fl_ask.H>

class ConfigDialog {
public:
    Fl_Window* window;
    Fl_Input* name_input;
    Fl_Choice* color_choice;
    Fl_Check_Button* notify_check;
    bool result;
    
    ConfigDialog() {
        window = new Fl_Window(300, 200, "Settings");
        
        name_input = new Fl_Input(80, 30, 150, 30, "Name:");
        
        color_choice = new Fl_Choice(80, 70, 150, 30, "Color:");
        color_choice->add("Red");
        color_choice->add("Green");
        color_choice->add("Blue");
        color_choice->value(0);
        
        notify_check = new Fl_Check_Button(80, 110, 150, 30, "Enable notifications");
        
        Fl_Button* ok_btn = new Fl_Button(80, 150, 60, 30, "OK");
        ok_btn->callback(ok_callback, this);
        
        Fl_Button* cancel_btn = new Fl_Button(150, 150, 60, 30, "Cancel");
        cancel_btn->callback(cancel_callback, this);
        
        window->end();
        window->set_modal();
        result = false;
    }
    
    static void ok_callback(Fl_Widget*, void* data) {
        ConfigDialog* dialog = (ConfigDialog*)data;
        dialog->result = true;
        dialog->window->hide();
    }
    
    static void cancel_callback(Fl_Widget*, void* data) {
        ConfigDialog* dialog = (ConfigDialog*)data;
        dialog->result = false;
        dialog->window->hide();
    }
    
    bool show() {
        window->show();
        while (window->shown()) {
            Fl::wait();
        }
        return result;
    }
    
    ~ConfigDialog() {
        delete window;
    }
};

void config_callback(Fl_Widget*, void*) {
    ConfigDialog dialog;
    if (dialog.show()) {
        char message[256];
        snprintf(message, sizeof(message), 
                "Settings saved:\nName: %s\nColor: %s\nNotifications: %s",
                dialog.name_input->value(),
                dialog.color_choice->text(),
                dialog.notify_check->value() ? "Enabled" : "Disabled");
        fl_message("%s", message);
    }
}

int main(int argc, char **argv) {
    Fl_Window* window = new Fl_Window(300, 150, "FLTK Main Window");
    
    Fl_Button* config_btn = new Fl_Button(100, 50, 100, 40, "Settings");
    config_btn->callback(config_callback);
    
    window->end();
    window->show(argc, argv);
    return Fl::run();
}

OpenGL Integration

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Gl_Window.H>
#include <FL/gl.h>
#include <FL/glu.h>
#include <cmath>

class OpenGLWindow : public Fl_Gl_Window {
    float rotation_angle;
    
public:
    OpenGLWindow(int x, int y, int w, int h, const char* title) 
        : Fl_Gl_Window(x, y, w, h, title), rotation_angle(0) {}
    
    void draw() override {
        if (!valid()) {
            valid(1);
            glViewport(0, 0, w(), h());
            glMatrixMode(GL_PROJECTION);
            glLoadIdentity();
            gluPerspective(45.0, (double)w()/(double)h(), 1.0, 10.0);
            glMatrixMode(GL_MODELVIEW);
            glEnable(GL_DEPTH_TEST);
        }
        
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glLoadIdentity();
        glTranslatef(0, 0, -5);
        glRotatef(rotation_angle, 1, 1, 1);
        
        // Draw colorful cube
        glBegin(GL_QUADS);
        
        // Front face (red)
        glColor3f(1.0f, 0.0f, 0.0f);
        glVertex3f(-1.0f, -1.0f,  1.0f);
        glVertex3f( 1.0f, -1.0f,  1.0f);
        glVertex3f( 1.0f,  1.0f,  1.0f);
        glVertex3f(-1.0f,  1.0f,  1.0f);
        
        // Back face (green)
        glColor3f(0.0f, 1.0f, 0.0f);
        glVertex3f(-1.0f, -1.0f, -1.0f);
        glVertex3f(-1.0f,  1.0f, -1.0f);
        glVertex3f( 1.0f,  1.0f, -1.0f);
        glVertex3f( 1.0f, -1.0f, -1.0f);
        
        // Top face (blue)
        glColor3f(0.0f, 0.0f, 1.0f);
        glVertex3f(-1.0f,  1.0f, -1.0f);
        glVertex3f(-1.0f,  1.0f,  1.0f);
        glVertex3f( 1.0f,  1.0f,  1.0f);
        glVertex3f( 1.0f,  1.0f, -1.0f);
        
        // Bottom face (yellow)
        glColor3f(1.0f, 1.0f, 0.0f);
        glVertex3f(-1.0f, -1.0f, -1.0f);
        glVertex3f( 1.0f, -1.0f, -1.0f);
        glVertex3f( 1.0f, -1.0f,  1.0f);
        glVertex3f(-1.0f, -1.0f,  1.0f);
        
        // Right face (magenta)
        glColor3f(1.0f, 0.0f, 1.0f);
        glVertex3f( 1.0f, -1.0f, -1.0f);
        glVertex3f( 1.0f,  1.0f, -1.0f);
        glVertex3f( 1.0f,  1.0f,  1.0f);
        glVertex3f( 1.0f, -1.0f,  1.0f);
        
        // Left face (cyan)
        glColor3f(0.0f, 1.0f, 1.0f);
        glVertex3f(-1.0f, -1.0f, -1.0f);
        glVertex3f(-1.0f, -1.0f,  1.0f);
        glVertex3f(-1.0f,  1.0f,  1.0f);
        glVertex3f(-1.0f,  1.0f, -1.0f);
        
        glEnd();
        
        rotation_angle += 1.0f;
        if (rotation_angle >= 360.0f) rotation_angle = 0.0f;
    }
    
    int handle(int event) override {
        if (event == FL_PUSH) {
            rotation_angle += 15.0f;
            redraw();
            return 1;
        }
        return Fl_Gl_Window::handle(event);
    }
};

void timer_callback(void* data) {
    OpenGLWindow* gl_win = (OpenGLWindow*)data;
    gl_win->redraw();
    Fl::repeat_timeout(1.0/60.0, timer_callback, data); // 60 FPS
}

int main(int argc, char **argv) {
    Fl_Window* window = new Fl_Window(500, 400, "FLTK OpenGL Example");
    
    OpenGLWindow* gl_window = new OpenGLWindow(10, 10, 480, 380, "");
    
    window->end();
    window->resizable(gl_window);
    window->show(argc, argv);
    
    // Start animation timer
    Fl::add_timeout(1.0/60.0, timer_callback, gl_window);
    
    return Fl::run();
}

User Interface Interaction and Events

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Slider.H>
#include <FL/Fl_Value_Output.H>
#include <FL/Fl_Progress.H>
#include <FL/Fl_Group.H>

class InteractiveDemo {
public:
    Fl_Window* window;
    Fl_Slider* red_slider;
    Fl_Slider* green_slider;
    Fl_Slider* blue_slider;
    Fl_Button* color_display;
    Fl_Progress* progress_bar;
    Fl_Value_Output* value_display;
    
    InteractiveDemo() {
        window = new Fl_Window(400, 350, "FLTK Interactive Demo");
        
        // Color mixer
        Fl_Group* color_group = new Fl_Group(10, 10, 380, 150, "Color Mixer");
        color_group->box(FL_ENGRAVED_FRAME);
        
        red_slider = new Fl_Slider(60, 40, 200, 20, "Red:");
        red_slider->type(FL_HOR_SLIDER);
        red_slider->range(0, 255);
        red_slider->value(128);
        red_slider->callback(color_callback, this);
        
        green_slider = new Fl_Slider(60, 70, 200, 20, "Green:");
        green_slider->type(FL_HOR_SLIDER);
        green_slider->range(0, 255);
        green_slider->value(128);
        green_slider->callback(color_callback, this);
        
        blue_slider = new Fl_Slider(60, 100, 200, 20, "Blue:");
        blue_slider->type(FL_HOR_SLIDER);
        blue_slider->range(0, 255);
        blue_slider->value(128);
        blue_slider->callback(color_callback, this);
        
        color_display = new Fl_Button(280, 40, 80, 80, "");
        color_display->box(FL_FLAT_BOX);
        
        color_group->end();
        
        // Progress bar control
        Fl_Group* progress_group = new Fl_Group(10, 170, 380, 100, "Progress");
        progress_group->box(FL_ENGRAVED_FRAME);
        
        Fl_Button* start_btn = new Fl_Button(20, 200, 60, 30, "Start");
        start_btn->callback(start_progress, this);
        
        Fl_Button* reset_btn = new Fl_Button(90, 200, 60, 30, "Reset");
        reset_btn->callback(reset_progress, this);
        
        progress_bar = new Fl_Progress(160, 200, 200, 30);
        progress_bar->minimum(0);
        progress_bar->maximum(100);
        progress_bar->value(0);
        
        value_display = new Fl_Value_Output(20, 240, 100, 20, "Value:");
        
        progress_group->end();
        
        // Action buttons
        Fl_Button* demo_btn = new Fl_Button(150, 300, 100, 30, "Run Demo");
        demo_btn->callback(demo_callback, this);
        
        window->end();
        update_color();
    }
    
    static void color_callback(Fl_Widget*, void* data) {
        InteractiveDemo* demo = (InteractiveDemo*)data;
        demo->update_color();
    }
    
    static void start_progress(Fl_Widget*, void* data) {
        InteractiveDemo* demo = (InteractiveDemo*)data;
        Fl::add_timeout(0.1, progress_timer, data);
    }
    
    static void reset_progress(Fl_Widget*, void* data) {
        InteractiveDemo* demo = (InteractiveDemo*)data;
        demo->progress_bar->value(0);
        demo->value_display->value(0);
        Fl::remove_timeout(progress_timer, data);
    }
    
    static void progress_timer(void* data) {
        InteractiveDemo* demo = (InteractiveDemo*)data;
        double current = demo->progress_bar->value();
        if (current < 100) {
            current += 2;
            demo->progress_bar->value(current);
            demo->value_display->value(current);
            Fl::repeat_timeout(0.1, progress_timer, data);
        }
    }
    
    static void demo_callback(Fl_Widget*, void* data) {
        InteractiveDemo* demo = (InteractiveDemo*)data;
        // Generate random color
        demo->red_slider->value(rand() % 256);
        demo->green_slider->value(rand() % 256);
        demo->blue_slider->value(rand() % 256);
        demo->update_color();
    }
    
    void update_color() {
        int r = (int)red_slider->value();
        int g = (int)green_slider->value();
        int b = (int)blue_slider->value();
        
        Fl_Color color = fl_rgb_color(r, g, b);
        color_display->color(color);
        color_display->redraw();
        
        char label[64];
        snprintf(label, sizeof(label), "RGB\n(%d,%d,%d)", r, g, b);
        color_display->copy_label(label);
    }
    
    void show() {
        window->show();
    }
    
    ~InteractiveDemo() {
        delete window;
    }
};

int main(int argc, char **argv) {
    InteractiveDemo demo;
    demo.show();
    return Fl::run();
}