FLTK Editor
FLTK 是一个GUI库,它提供了跨平台支持。下载源代码在 deepin 系统中,make 即可生成。生成时,根据提示需先安装依赖库。
编译 FLTK 需要的库:
sudo apt-get install libx11-dev
sudo apt-get install libxrender-dev
sudo apt-get install libxft-dev
sudo apt-get install libgl1-mesa-dev
sudo apt-get install libglu1-mesa-dev
sudo apt install mesa-utils
sudo apt install libglew-dev
FLTK Editor 效果图:

编译运行:
- 在fltk目录中, 新建 Editor.cxx 文件,终端编译:
./fltk-config --compile Editor.cxx`
- 终端运行:
./Editor
Editor.cxx 源码:
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Menu_Bar.H>
#include <FL/fl_ask.H>
#include <FL/filename.H>
#include <FL/fl_string_functions.h>
#include <FL/Fl_Text_Buffer.H>
#include <FL/Fl_Text_Editor.H>
#include <FL/Fl_Native_File_Chooser.H>
#include <FL/platform.H>
#include <FL/Fl_Flex.H>
#include <FL/Fl_Tile.H>
#include <errno.h>
Fl_Double_Window *window = NULL;
Fl_Menu_Bar *menubar = NULL;
bool text_changed = false;
char app_filename[FL_PATH_MAX + 1] = "";
char last_find_text[1024] = "";
char last_replace_text[1024] = "";
Fl_Text_Editor *text_editor = NULL;
Fl_Text_Editor *split_editor = NULL;
Fl_Text_Buffer *text_buffer = NULL;
Fl_Tile *app_tile = NULL;
void SetChanged(bool);
void TextChanged(int, int inserted, int deleted, int, const char *, void *) {
SetChanged(true);
}
void MenuNewCallback(Fl_Widget *, void *) {
text_buffer->text("");
SetChanged(false);
}
void Build_Editor() {
window->begin();
text_buffer = new Fl_Text_Buffer();
text_buffer->add_modify_callback(TextChanged, NULL);
text_editor = new Fl_Text_Editor(0, 25, window->w(), window->h() - 25);
text_editor->buffer(text_buffer);
text_editor->textfont(FL_COURIER);
window->resizable(text_editor);
window->end();
}
void Build_Window() {
window = new Fl_Double_Window(900, 680, "FLTK Editor");
}
void UpdateTitle() {
const char *filename = NULL;
if (app_filename[0])
filename = fl_filename_name(app_filename);
if (filename) {
char buf[FL_PATH_MAX + 3];
if (text_changed) {
snprintf(buf, FL_PATH_MAX + 2, "%s *", filename);
} else {
snprintf(buf, FL_PATH_MAX + 2, "%s", filename);
}
window->copy_label(buf);
} else {
window->label("FLTK Editor");
}
}
void SetChanged(bool v) {
if (v != text_changed) {
text_changed = v;
UpdateTitle();
}
}
void SetFilename(const char *filename) {
if (filename) {
fl_strlcpy(app_filename, filename, FL_PATH_MAX + 1);
} else {
app_filename[0] = 0;
}
UpdateTitle();
}
void MenuQuitCallback(Fl_Widget *w, void *v) {
if (text_changed) {
int c = fl_choice("Save changes?", "Quit", "Cancel", NULL);
if (c == 1) {
return;
}
}
Fl::hide_all_windows();
}
void Build_Menu() {
window->begin();
menubar = new Fl_Menu_Bar(0, 0, window->w(), 25);
menubar->add("File/Quit", FL_COMMAND + 'q', MenuQuitCallback);
int index = menubar->find_index(MenuQuitCallback);
menubar->insert(index, "New", FL_COMMAND + 'n', MenuNewCallback);
window->callback(MenuQuitCallback);
window->end();
}
void MenuSaveAsCallback(Fl_Widget *, void *) {
Fl_Native_File_Chooser chooser;
chooser.title("Save As");
chooser.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE);
if (app_filename[0]) {
char temp[FL_PATH_MAX + 1];
fl_strlcpy(temp, app_filename, FL_PATH_MAX + 1);
const char *name = fl_filename_name(temp);
if (name) {
chooser.preset_file(name);
temp[name - temp] = 0;
chooser.directory(temp);
}
}
if (chooser.show() == 0) {
text_buffer->savefile(chooser.filename());
SetFilename(chooser.filename());
SetChanged(false);
}
}
void MenuSaveCallback(Fl_Widget *, void *) {
if (app_filename[0]) {
text_buffer->savefile(app_filename);
SetChanged(false);
} else {
MenuSaveAsCallback(NULL, NULL);
}
}
void LoadFlie(const char *filename) {
if (filename && filename[0]) {
text_buffer->loadfile(filename);
SetFilename(filename);
SetChanged(false);
}
}
// TODO: Add Open File (Fl_Native_File_Chooser
void MenuOpenCallback(Fl_Widget *, void *) {
if (text_changed) {
int c = fl_choice("Save changes?", "Cancel", "Save", "Don't Save");
if (c == 2)
return;
if (c == 1)
MenuSaveCallback(NULL, NULL);
}
Fl_Native_File_Chooser chooser;
chooser.title("Open File");
chooser.type(Fl_Native_File_Chooser::BROWSE_FILE);
if (!app_filename[0]) {
char temp[FL_PATH_MAX + 1];
fl_strlcpy(temp, app_filename, FL_PATH_MAX + 1);
const char *name = fl_filename_name(temp);
if (name) {
chooser.preset_file(name);
temp[name - temp] = 0;
chooser.directory(temp);
}
}
if (chooser.show() == 0) {
LoadFlie(chooser.filename());
}
}
void AddFileMenu() {
int i = menubar->find_index(MenuQuitCallback);
menubar->insert(i, "Open", FL_COMMAND + 'o', MenuOpenCallback, NULL, FL_MENU_DIVIDER);
menubar->insert(i + 1, "Save", FL_COMMAND + 's', MenuSaveCallback);
menubar->insert(i + 2, "Save As", FL_COMMAND + 'a', MenuSaveAsCallback, NULL, FL_MENU_DIVIDER);
}
int ArgsHandler(int argc, char **argv, int &i) {
if (argv && argv[i] && argv[i][0] != '-') {
LoadFlie(argv[i]);
i++;
return 1;
}
return 0;
}
int HandleCommandLine(int argc, char **argv) {
for (int i = 1; i < argc; ++i) {
if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
printf("Usage: %s [filename]\n", argv[0]);
}
}
int i = 0;
Fl::args_to_utf8(argc, argv);
Fl::args(argc, argv, i, ArgsHandler);
fl_open_callback(LoadFlie);
window->show(argc, argv);
return Fl::run();
}
void MenuCutCallback(Fl_Widget *, void *) {
Fl_Widget *w = Fl::focus();
if (w && (w == text_editor || w == split_editor)) {
Fl_Text_Editor::kf_cut(0, (Fl_Text_Editor *)w);
}
}
void MenuCopyCallback(Fl_Widget *, void *) {
Fl_Widget *w = Fl::focus();
if (w && (w == text_editor || w == split_editor)) {
Fl_Text_Editor::kf_copy(0, (Fl_Text_Editor *)w);
}
}
void MenuPasteCallback(Fl_Widget *, void *) {
Fl_Widget *w = Fl::focus();
if (w && (w == text_editor || w == split_editor)) {
Fl_Text_Editor::kf_paste(0, (Fl_Text_Editor *)w);
}
}
void FindNext(const char *needle) {
Fl_Text_Editor *editor = text_editor;
Fl_Widget *w = Fl::focus();
if (w && w == split_editor) {
editor = split_editor;
}
int pos = editor->insert_position();
int found = text_buffer->search_forward(pos, needle, &pos);
if (found) {
text_buffer->select(pos, pos + (int)strlen(needle));
editor->insert_position(pos + (int)strlen(needle));
editor->show_insert_position();
} else {
fl_alert("Find: '%s' not found", needle);
}
}
void MenuFindCallback(Fl_Widget *, void *) {
const char *find_text = fl_input("Find in text: ", last_find_text);
if (find_text && find_text[0]) {
fl_strlcpy(last_find_text, find_text, sizeof(last_find_text));
FindNext(find_text);
}
}
void MenuF1indNextCallback(Fl_Widget *, void *) {
if (last_find_text[0]) {
FindNext(last_find_text);
} else {
MenuFindCallback(NULL, NULL);
}
}
void ReplaceSelection(const char *new_text) {
Fl_Text_Editor *editor = text_editor;
Fl_Widget *w = Fl::focus();
if (w && w == split_editor) {
editor = split_editor;
}
int start, end;
if (text_buffer->selection_position(&start, &end)) {
text_buffer->remove_selection();
text_buffer->insert(start, new_text);
text_buffer->select(start, start + (int)strlen(new_text));
editor->insert_position(start + (int)strlen(new_text));
editor->show_insert_position();
}
}
class ReplaceDialog : public Fl_Double_Window {
Fl_Input *find_text_input, *replace_text_input;
Fl_Button *find_next_button, *replace_and_find_button, *close_button;
public:
ReplaceDialog(const char *label);
void show() FL_OVERRIDE;
private:
static void find_next_callback(Fl_Widget *, void *);
static void replace_and_find_callback(Fl_Widget *, void *);
static void close_callback(Fl_Widget *, void *);
};
ReplaceDialog *replace_dialog = NULL;
ReplaceDialog::ReplaceDialog(const char *label)
: Fl_Double_Window(430, 100, label) {
find_text_input = new Fl_Input(100, 10, 320, 25, "Find: ");
replace_text_input = new Fl_Input(100, 50, 320, 25, "Replace: ");
Fl_Flex *button_field = new Fl_Flex(100, 70, w() - 100, 40);
button_field->type(Fl_Flex::HORIZONTAL);
button_field->margin(0, 5, 10, 10);
button_field->gap(10);
find_next_button = new Fl_Button(0, 0, 0, 0, "Next");
find_next_button->callback(replace_and_find_callback, this);
close_button = new Fl_Button(0, 0, 0, 0, "Close");
close_button->callback(close_callback, this);
button_field->end();
set_non_modal();
}
void ReplaceDialog::show() {
find_text_input->value(last_find_text);
replace_text_input->value(last_replace_text);
Fl_Double_Window::show();
}
void ReplaceDialog::find_next_callback(Fl_Widget *, void *data) {
FindNext(static_cast<ReplaceDialog *>(data)->find_text_input->value());
}
void ReplaceDialog::replace_and_find_callback(Fl_Widget *, void *data) {
ReplaceDialog *dlg = static_cast<ReplaceDialog *>(data);
ReplaceSelection(dlg->replace_text_input->value());
find_next_callback(NULL, data);
}
void ReplaceDialog::close_callback(Fl_Widget *, void *data) {
ReplaceDialog *dlg = static_cast<ReplaceDialog *>(data);
dlg->hide();
}
void MenuReplaceCallback(Fl_Widget *, void *) {
if (!replace_dialog) {
replace_dialog = new ReplaceDialog("Find and Replace");
}
replace_dialog->show();
}
void AddEditMenu() {
menubar->add("Edit/Cut", FL_COMMAND + 'x', MenuCutCallback);
menubar->add("Edit/Copy", FL_COMMAND + 'c', MenuCopyCallback);
menubar->add("Edit/Paste", FL_COMMAND + 'v', MenuPasteCallback, NULL, FL_MENU_DIVIDER);
menubar->add("Edit/Find", FL_COMMAND + 'f', MenuFindCallback);
menubar->add("Edit/Find Next", FL_COMMAND + 'n', MenuF1indNextCallback);
menubar->add("Edit/Replace", FL_COMMAND + 'r', MenuReplaceCallback);
}
// Window menu
void MenuLineNumberCallback(Fl_Widget *w, void *) {
Fl_Menu_Bar *menu = static_cast<Fl_Menu_Bar *>(w);
const Fl_Menu_Item *item = menu->mvalue();
if (item->value()) {
text_editor->linenumber_width(40);
if (split_editor)
split_editor->linenumber_width(40);
} else {
text_editor->linenumber_width(0);
if (split_editor)
split_editor->linenumber_width(0);
}
text_editor->redraw();
if (split_editor)
split_editor->redraw();
}
void MenuWordWrapCallback(Fl_Widget *w, void *) {
Fl_Menu_Bar *menu = static_cast<Fl_Menu_Bar *>(w);
const Fl_Menu_Item *item = menu->mvalue();
if (item->value()) {
text_editor->wrap_mode(Fl_Text_Display::WRAP_AT_BOUNDS, 0);
if (split_editor)
split_editor->wrap_mode(Fl_Text_Display::WRAP_AT_BOUNDS, 0);
} else {
text_editor->wrap_mode(Fl_Text_Display::WRAP_NONE, 0);
if (split_editor)
split_editor->wrap_mode(Fl_Text_Display::WRAP_NONE, 0);
}
text_editor->redraw();
if (split_editor)
split_editor->redraw();
}
void BuildSplitEditor() {
window->begin();
app_tile = new Fl_Tile(text_editor->x(), text_editor->y(), text_editor->w(), text_editor->h());
window->remove(text_editor);
app_tile->add(text_editor);
split_editor = new Fl_Text_Editor(app_tile->x(), app_tile->y() + app_tile->h(), app_tile->w(), 0);
split_editor->buffer(text_buffer);
split_editor->textfont(FL_COURIER);
split_editor->hide();
app_tile->end();
app_tile->size_range(0, 25, 25);
app_tile->size_range(1, 25, 25);
window->end();
window->resizable(app_tile);
app_tile->resizable(text_editor);
}
void MenuSplitCallback(Fl_Widget *w, void *) {
Fl_Menu_Bar *menu = static_cast<Fl_Menu_Bar *>(w);
const Fl_Menu_Item *item = menu->mvalue();
if (item->value()) {
int h_split = app_tile->h() / 2;
text_editor->size(app_tile->w(), h_split);
split_editor->resize(app_tile->x(), app_tile->y() + h_split, app_tile->w(),
app_tile->h() - h_split);
split_editor->show();
} else {
text_editor->size(app_tile->w(), app_tile->h());
split_editor->resize(app_tile->x(), app_tile->y() + app_tile->h(), app_tile->w(), 0);
split_editor->hide();
}
app_tile->resizable(text_editor);
app_tile->init_sizes();
app_tile->redraw();
}
// Syntax highlighting
#include <ctype.h>
#include <stdlib.h>
Fl_Text_Buffer *app_style_buffer = NULL;
#define TS 14
Fl_Text_Display::Style_Table_Entry styletable[] = {
{FL_BLACK, FL_COURIER, TS}, // A - Plain
{FL_DARK_GREEN, FL_HELVETICA_ITALIC, TS}, // B - Line comments
{FL_DARK_GREEN, FL_HELVETICA_ITALIC, TS}, // C - Block comments
{FL_BLUE, FL_COURIER, TS}, // D - Strings
{FL_DARK_RED, FL_COURIER, TS}, // E - Directives
{FL_DARK_RED, FL_COURIER_BOLD, TS}, // F - Types
{FL_BLUE, FL_COURIER_BOLD, TS}, // G - Keywords
};
const char *code_keywords[] = {
"and", "and_eq", "asm", "bitand", "bitor", "break", "case", "catch", "compl",
"continue", "default", "delete", "do", "else", "false", "for", "goto", "if",
"new", "not", "not_eq", "operator", "or", "or_eq", "return", "switch", "template",
"this", "throw", "true", "try", "while", "xor", "xor_eq"};
const char *code_types[] = {
"auto", "bool", "char", "class", "const", "const_cast",
"double", "dynamic_cast", "enum", "explicit", "extern", "float",
"friend", "inline", "int", "long", "mutable", "namespace",
"private", "protected", "public", "register", "short", "signed",
"sizeof", "static", "static_cast", "struct", "template", "typedef",
"typename", "union", "unsigned", "virtual", "void", "volatile"};
extern "C" {
int compare_keywords(const void *a, const void *b) {
return strcmp(*(const char **)a, *(const char **)b);
}
}
void style_parse(const char *text, char *style, int length) {
char current;
int col;
int last;
char buf[255], *bufptr;
const char *temp;
// Style letters:
//
// A - Plain
// B - Line comments
// C - Block comments
// D - Strings
// E - Directives
// F - Types
// G - Keywords
for (current = *style, col = 0, last = 0; length > 0; length--, text++) {
if (current == 'B' || current == 'F' || current == 'G')
current = 'A';
if (current == 'A') {
// Check for directives, comments, strings, and keywords...
if (col == 0 && *text == '#') {
// Set style to directive
current = 'E';
} else if (strncmp(text, "//", 2) == 0) {
current = 'B';
for (; length > 0 && *text != '\n'; length--, text++)
*style++ = 'B';
if (length == 0)
break;
} else if (strncmp(text, "/*", 2) == 0) {
current = 'C';
} else if (strncmp(text, "\\\"", 2) == 0) {
// Quoted quote...
*style++ = current;
*style++ = current;
text++;
length--;
col += 2;
continue;
} else if (*text == '\"') {
current = 'D';
} else if (!last && (islower((*text) & 255) || *text == '_')) {
// Might be a keyword...
for (temp = text, bufptr = buf;
(islower((*temp) & 255) || *temp == '_') && bufptr < (buf + sizeof(buf) - 1);
*bufptr++ = *temp++) {
// nothing
}
if (!islower((*temp) & 255) && *temp != '_') {
*bufptr = '\0';
bufptr = buf;
if (bsearch(&bufptr, code_types, sizeof(code_types) / sizeof(code_types[0]),
sizeof(code_types[0]), compare_keywords)) {
while (text < temp) {
*style++ = 'F';
text++;
length--;
col++;
}
text--;
length++;
last = 1;
continue;
} else if (bsearch(&bufptr, code_keywords,
sizeof(code_keywords) / sizeof(code_keywords[0]),
sizeof(code_keywords[0]), compare_keywords)) {
while (text < temp) {
*style++ = 'G';
text++;
length--;
col++;
}
text--;
length++;
last = 1;
continue;
}
}
}
} else if (current == 'C' && strncmp(text, "*/", 2) == 0) {
// Close a C comment...
*style++ = current;
*style++ = current;
text++;
length--;
current = 'A';
col += 2;
continue;
} else if (current == 'D') {
// Continuing in string...
if (strncmp(text, "\\\"", 2) == 0) {
// Quoted end quote...
*style++ = current;
*style++ = current;
text++;
length--;
col += 2;
continue;
} else if (*text == '\"') {
// End quote...
*style++ = current;
col++;
current = 'A';
continue;
}
}
// Copy style info...
if (current == 'A' && (*text == '{' || *text == '}'))
*style++ = 'G';
else
*style++ = current;
col++;
last = isalnum((*text) & 255) || *text == '_' || *text == '.';
if (*text == '\n') {
// Reset column and possibly reset the style
col = 0;
if (current == 'B' || current == 'E')
current = 'A';
}
}
}
void StyleInit() {
char *style = new char[text_buffer->length() + 1];
char *text = text_buffer->text();
memset(style, 'A', text_buffer->length());
style[text_buffer->length()] = '\0';
if (!app_style_buffer)
app_style_buffer = new Fl_Text_Buffer(text_buffer->length() + 1);
style_parse(text, style, text_buffer->length());
app_style_buffer->text(style);
delete[] style;
free(text);
}
void StyleUnfinishedCallback(int reason, void *editor) {}
void StyleUpdate(int pos, int nInserted,
int nDeleted, int nRestyled, const char *dletedText, void *cbArg)
{
int start, end;
char last, *style, *text;
if (nInserted == 0 && nDeleted == 0) {
app_style_buffer->unselect();
return;
}
if (nInserted > 0) {
style = new char[nInserted + 1];
memset(style, 'A', nInserted);
style[nInserted] = '\0';
app_style_buffer->replace(pos, pos + nDeleted, style);
delete[] style;
} else {
app_style_buffer->remove(pos, pos + nDeleted);
}
app_style_buffer->select(pos, pos + nInserted - nDeleted);
start = text_buffer->line_start(pos);
end = text_buffer->line_end(pos + nInserted);
text = text_buffer->text_range(start, end);
style = app_style_buffer->text_range(start, end);
if (start == end)
last = 0;
else
last = style[end - start - 1];
style_parse(text, style, end - start);
app_style_buffer->replace(start, end, style);
((Fl_Text_Editor *)cbArg)->redisplay_range(start, end);
if (start == end || last != style[end - start - 1]) {
free(text);
free(style);
end = text_buffer->length();
text = text_buffer->text_range(start, end);
style = app_style_buffer->text_range(start, end);
style_parse(text, style, end - start);
app_style_buffer->replace(start, end, style);
((Fl_Text_Editor *)cbArg)->redisplay_range(start, end);
}
free(text);
free(style);
}
void MenuSyntaxHighlightCallback(Fl_Widget *w, void *) {
Fl_Menu_Bar *menu = static_cast<Fl_Menu_Bar *>(w);
const Fl_Menu_Item *item = menu->mvalue();
if (item->value()) {
StyleInit();
text_editor->highlight_data(app_style_buffer, styletable,
sizeof(styletable) / sizeof(styletable[0]), 'A',
StyleUnfinishedCallback, 0);
text_buffer->add_modify_callback(StyleUpdate, text_editor);
} else {
text_buffer->remove_modify_callback(StyleUpdate, text_editor);
text_editor->highlight_data(NULL, NULL, 0, 'A', NULL, 0);
}
text_editor->redraw();
if (split_editor) {
if (item->value()) {
split_editor->highlight_data(app_style_buffer, styletable,
sizeof(styletable) / sizeof(styletable[0]), 'A',
StyleUnfinishedCallback, 0);
} else {
split_editor->highlight_data(NULL, NULL, 0, 'A', NULL, 0);
}
split_editor->redraw();
}
}
void AddWindowMenu() {
menubar->add("Window/Line Numbers", FL_COMMAND + 'l', MenuLineNumberCallback, NULL,
FL_MENU_TOGGLE);
menubar->add("Window/Word Wrap", FL_COMMAND + 'w', MenuWordWrapCallback, NULL, FL_MENU_TOGGLE);
menubar->add("Window/Split", FL_COMMAND + 's', MenuSplitCallback, NULL, FL_MENU_TOGGLE);
menubar->add("Window/Syntax Highlighting", FL_COMMAND + 'h', MenuSyntaxHighlightCallback, NULL,
FL_MENU_TOGGLE);
}
int main(int argc, char **argv) {
Build_Window();
Build_Menu();
Build_Editor();
BuildSplitEditor();
AddFileMenu();
AddEditMenu();
AddWindowMenu();
return HandleCommandLine(argc, argv);
}
fltk 封装为 pyfltk 包
- fltk1.4 的 CMakeLists.txt 文件中,在 project 下面添加 -fPIC 编译选项如下:
project(FLTK VERSION 1.4.0)
# 为所有目标添加 -fPIC 编译选项,可解决重定向动态库问题
add_compile_options(-fPIC)
- 在 fltk1.4 目录下分别运行以下命令:
mkdir build
cd build
cmake ..
make
sudo make install
- 下载 pyFltk1.4 ,在 pyFltk1.4 目录下运行以下命令:
python setup.py swig
python setup.py build
python setup.py install
- 在生成的 [Python lib path]/site-packages/easy-install.pth 文件中的
./pyFltk1.4-1.4.0rc1-py3.12-linux-x86_64.egg后面添加 fltk14 如下:
./pyFltk1.4-1.4.0rc1-py3.12-linux-x86_64.egg/fltk14
- 运行 fltk14/test/hello.py 测试, OK!

Build PySide6 from source
官方链接: build PySide6
# set Qt6/bin to PATH
sudo apt install clang-18 libclang-18-dev llvm-18
sudo ln /usr/bin/clang-18 /usr/bin/clang
sudo ln /usr/bin/llvm-confg-18 /usr/bin/llvm-config
pip install -r requirements.txt
#Check your Qt installation path
python setup.py build --qtpaths=/home/x01/Qt/6.8.1/gcc_64/bin/qtpaths --build-tests --ignore-git --parallel=8
# Install on the current directory
python setup.py install --qtpaths=/home/x01/Qt/6.8.1/gcc_64/bin/qtpaths --build-tests --ignore-git --parallel=8
绘制 Button
- 新建文件 drawing.h drawing.c main.c 如下:
#ifndef DRAWING_H
#define DRAWING_H
#include <X11/Xlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
typedef struct Widget {
int x, y, width, height;
void (*draw)(struct Widget*, Display*, Window, GC);
void (*handle_event)(struct Widget*, XEvent*);
} Widget;
typedef struct {
Widget base;
char* label;
void (*on_click)();
} Button;
typedef struct Layout {
void (*apply)(struct Layout*, Widget**, int count);
} Layout;
// 示例:垂直布局
typedef struct {
Layout base;
int spacing;
} VBoxLayout;
void vbox_apply(Layout* layout, Widget** widgets, int count) ;
void button_draw(Widget* widget, Display* d, Window w, GC gc);
void draw_test(Display *display, Window window, XEvent event, int screen) ;
#endif
#include "drawing.h"
void draw_test(Display *display, Window window, XEvent event, int screen) {
// 在事件循环前添加图形上下文
GC gc = XCreateGC(display, window, 0, NULL);
XSetForeground(display, gc, BlackPixel(display, screen));
// 在Expose事件中绘制
if (event.type == Expose) {
// 绘制矩形
XFillRectangle(display, window, gc, 50, 50, 100, 80);
// 绘制文本
XDrawString(display, window, gc, 150, 150, "Hello GUI", 9);
}
}
void button_draw(Widget* widget, Display* d, Window w, GC gc) {
Button* btn = (Button*)widget;
XDrawRectangle(d, w, gc, btn->base.x, btn->base.y, btn->base.width, btn->base.height);
XDrawString(d, w, gc, btn->base.x+10, btn->base.y+20, btn->label, strlen(btn->label));
}
void vbox_apply(Layout* layout, Widget** widgets, int count) {
VBoxLayout* vbox = (VBoxLayout*)layout;
int y = widgets[0]->y;
for (int i = 0; i < count; i++) {
widgets[i]->y = y;
y += widgets[i]->height + vbox->spacing;
}
}
// void draw_button(Display *display, Window window, XEvent event, int screen, Button btn) {
// // Button btn = {120, 200, 80, 30, "Click Me", NULL};
// // 在事件循环前添加图形上下文
// GC gc = XCreateGC(display, window, 0, NULL);
// XSetForeground(display, gc, BlackPixel(display, screen));
// // 在事件循环中检测点击
// if (event.type == ButtonPress) {
// int x = event.xbutton.x;
// int y = event.xbutton.y;
// if (x >= btn.x && x <= btn.x + btn.width &&
// y >= btn.y && y <= btn.y + btn.height) {
// printf("Button Clicked!\n");
// }
// }
// // 绘制按钮
// XDrawRectangle(display, window, gc, btn.x, btn.y, btn.width, btn.height);
// XDrawString(display, window, gc, btn.x+10, btn.y+20, btn.label, strlen(btn.label));
// }
#include <X11/Xlib.h>
#include <unistd.h>
#include "drawing.h"
int main()
{
Display *display = XOpenDisplay(NULL);
int screen = DefaultScreen(display);
Window root = RootWindow(display, screen);
// 创建窗口
Window window = XCreateSimpleWindow(
display, root,
100, 100, 400, 300, // x,y,width,height
1, BlackPixel(display, screen), // border
WhitePixel(display, screen) // background
);
GC gc = XCreateGC(display, window, 0, NULL);
// 设置窗口属性
XSelectInput(display, window, ExposureMask | KeyPressMask);
XMapWindow(display, window);
XFlush(display);
// 事件循环
XEvent event;
while (1)
{
XNextEvent(display, &event);
if (event.type == KeyPress)
break;
if (event.type == Expose)
{
// 处理窗口暴露事件,例如绘制内容
// draw_test(display, window, event,screen);
Widget w1 = {100, 40, 100, 50, NULL, NULL};
Button btn1 = {w1, "Click Me 1", NULL};
Widget w2 = {100, 0, 200, 50, NULL, NULL};
Button btn2 = {w2, "Click Me 2", NULL};
Layout layout = {NULL};
VBoxLayout vbox = {layout, 8};
Widget* widgets[2] = {&btn1.base, &btn2.base};
vbox_apply(&vbox.base, widgets, 2);
button_draw(&btn1.base, display, window, gc);
button_draw(&btn2.base, display, window, gc);
}
}
XCloseDisplay(display);
return 0;
}
- 编译运行
gcc drawing.c main.c -lX11 -o hello
./hello
Linux 3d graphics programming
- 进入 md/res/02/l3d 目录,
mkdir build && cd build && cmake .. && cmake --build . - 进入 md/res/02/c3 目录,
make即可, 运行:./demo - 下载链接
camspikes 效果图

浙公网安备 33010602011771号