├── tools ├── bin2cstr │ ├── src │ │ ├── bin2cstr.mk │ │ └── bin2cstr.c │ └── GNUmakefile ├── xtc │ ├── GNUmakefile │ └── src │ │ ├── updater.h │ │ ├── compiler.h │ │ ├── sourcegen.h │ │ ├── xtc.mk │ │ ├── util.h │ │ ├── xtc.c │ │ ├── deffile.h │ │ ├── util.c │ │ ├── compiler.c │ │ ├── updater.c │ │ ├── sourcegen.c │ │ └── deffile.c └── emojigen │ ├── GNUmakefile │ └── src │ ├── sourcegen.h │ ├── translate.h │ ├── emojinames.h │ ├── groupnames.h │ ├── util.h │ ├── char32.h │ ├── emojigen.mk │ ├── emojigen.c │ ├── util.c │ ├── emojireader.h │ ├── emojinames.c │ ├── groupnames.c │ ├── sourcegen.c │ ├── emojireader.c │ └── translate.c ├── .github ├── screenshots │ └── xmoji.png └── workflows │ └── ci.yml ├── src └── bin │ └── xmoji │ ├── icons │ ├── 16x16 │ │ └── xmoji.png │ ├── 32x32 │ │ └── xmoji.png │ ├── 48x48 │ │ └── xmoji.png │ └── 256x256 │ │ └── xmoji.png │ ├── svghooks.h │ ├── contrib │ ├── nanosvg.txt │ └── nanosvg │ │ └── LICENSE.txt │ ├── nanosvg.h │ ├── char32.h │ ├── emojifont.h │ ├── macros.h │ ├── nanosvg.c │ ├── xmoji.desktop.in │ ├── icons.h │ ├── pen.h │ ├── icon.h │ ├── tablerow.h │ ├── table.h │ ├── tooltip.h │ ├── singleinstance.h │ ├── menu.h │ ├── scrollbox.h │ ├── tablerow.c │ ├── x11app-int.h │ ├── pixmap.h │ ├── shape.h │ ├── surface.h │ ├── xdgopen.h │ ├── hyperlink.h │ ├── filewatcher.h │ ├── flyout.h │ ├── tabbox.h │ ├── imagelabel.h │ ├── keyinjector.h │ ├── flowgrid.h │ ├── spinbox.h │ ├── translator.h │ ├── hbox.h │ ├── configfile.h │ ├── dropdown.h │ ├── emojihistory.h │ ├── vbox.h │ ├── suppress.h │ ├── textlabel.h │ ├── emojibutton.h │ ├── icons.c │ ├── xselection.h │ ├── textbox.h │ ├── button.h │ ├── unistrbuilder.h │ ├── colorset.h │ ├── command.h │ ├── xrdb.h │ ├── x11app.h │ ├── emoji.h │ ├── textrenderer.h │ ├── tooltip.c │ ├── config.h │ ├── shape.c │ ├── imagelabel.c │ ├── font.h │ ├── hyperlink.c │ ├── icon.c │ ├── table.c │ ├── window.h │ ├── object.h │ ├── pen.c │ ├── emoji.c │ ├── object.c │ ├── valuetypes.h │ ├── singleinstance.c │ ├── emojihistory.c │ ├── command.c │ ├── unistr.h │ ├── unistrbuilder.c │ ├── translator.c │ ├── xdgopen.c │ ├── translations │ └── xmoji-ui.def.in │ ├── xmoji.mk │ ├── flyout.c │ ├── textlabel.c │ ├── svghooks.c │ ├── pixmap.c │ ├── surface.c │ ├── keyinjector.c │ ├── button.c │ └── dropdown.c ├── .gitmodules ├── .gitignore ├── LICENSE.txt ├── GNUmakefile ├── ftbundle └── GNUmakefile └── TRANSLATE.md /tools/bin2cstr/src/bin2cstr.mk: -------------------------------------------------------------------------------- 1 | bin2cstr_MODULES= bin2cstr 2 | $(call binrules,bin2cstr) 3 | -------------------------------------------------------------------------------- /tools/xtc/GNUmakefile: -------------------------------------------------------------------------------- 1 | zimkdir=../../zimk 2 | include $(zimkdir)/zimk.mk 3 | $(call zinc,src/xtc.mk) 4 | -------------------------------------------------------------------------------- /.github/screenshots/xmoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zirias/xmoji/HEAD/.github/screenshots/xmoji.png -------------------------------------------------------------------------------- /tools/bin2cstr/GNUmakefile: -------------------------------------------------------------------------------- 1 | zimkdir=../../zimk 2 | include $(zimkdir)/zimk.mk 3 | $(call zinc,src/bin2cstr.mk) 4 | -------------------------------------------------------------------------------- /tools/emojigen/GNUmakefile: -------------------------------------------------------------------------------- 1 | zimkdir=../../zimk 2 | include $(zimkdir)/zimk.mk 3 | $(call zinc,src/emojigen.mk) 4 | -------------------------------------------------------------------------------- /src/bin/xmoji/icons/16x16/xmoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zirias/xmoji/HEAD/src/bin/xmoji/icons/16x16/xmoji.png -------------------------------------------------------------------------------- /src/bin/xmoji/icons/32x32/xmoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zirias/xmoji/HEAD/src/bin/xmoji/icons/32x32/xmoji.png -------------------------------------------------------------------------------- /src/bin/xmoji/icons/48x48/xmoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zirias/xmoji/HEAD/src/bin/xmoji/icons/48x48/xmoji.png -------------------------------------------------------------------------------- /src/bin/xmoji/icons/256x256/xmoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zirias/xmoji/HEAD/src/bin/xmoji/icons/256x256/xmoji.png -------------------------------------------------------------------------------- /tools/xtc/src/updater.h: -------------------------------------------------------------------------------- 1 | #ifndef XTC_UPDATER_H 2 | #define XTC_UPDATER_H 3 | 4 | int doupdate(int argc, char **argv); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /src/bin/xmoji/svghooks.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_SVGHOOKS_H 2 | #define XMOJI_SVGHOOKS_H 3 | 4 | const void *SvgHooks_get(void); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /tools/xtc/src/compiler.h: -------------------------------------------------------------------------------- 1 | #ifndef XTC_COMPILER_H 2 | #define XTC_COMPILER_H 3 | 4 | int docompile(int argc, char **argv); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /tools/xtc/src/sourcegen.h: -------------------------------------------------------------------------------- 1 | #ifndef XTC_SOURCEGEN_H 2 | #define XTC_SOURCEGEN_H 3 | 4 | int dosource(int argc, char **argv); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /tools/xtc/src/xtc.mk: -------------------------------------------------------------------------------- 1 | xtc_MODULES= compiler \ 2 | deffile \ 3 | sourcegen \ 4 | updater \ 5 | util \ 6 | xtc 7 | 8 | $(call binrules,xtc) 9 | -------------------------------------------------------------------------------- /tools/emojigen/src/sourcegen.h: -------------------------------------------------------------------------------- 1 | #ifndef EMOJIGEN_SOURCEGEN_H 2 | #define EMOJIGEN_SOURCEGEN_H 3 | 4 | int dosource(int argc, char **argv); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /tools/emojigen/src/translate.h: -------------------------------------------------------------------------------- 1 | #ifndef EMOJIGEN_TRANSLATE_H 2 | #define EMOJIGEN_TRANSLATE_H 3 | 4 | int dotranslate(int argc, char **argv); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /src/bin/xmoji/contrib/nanosvg.txt: -------------------------------------------------------------------------------- 1 | name: nanosvg 2 | www: https://github.com/memononen/nanosvg 3 | date: 20240526 4 | hash: 93ce879dc4c04a3ef1758428ec80083c38610b1f 5 | -------------------------------------------------------------------------------- /tools/emojigen/src/emojinames.h: -------------------------------------------------------------------------------- 1 | #ifndef EMOJIGEN_EMOJINAMES_H 2 | #define EMOJIGEN_EMOJINAMES_H 3 | 4 | int doemojinames(int argc, char **argv); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /tools/emojigen/src/groupnames.h: -------------------------------------------------------------------------------- 1 | #ifndef EMOJIGEN_GROUPNAMES_H 2 | #define EMOJIGEN_GROUPNAMES_H 3 | 4 | int dogroupnames(int argc, char **argv); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "zimk"] 2 | path = zimk 3 | url = https://github.com/Zirias/zimk.git 4 | [submodule "poser"] 5 | path = poser 6 | url = https://github.com/Zirias/poser.git 7 | -------------------------------------------------------------------------------- /src/bin/xmoji/nanosvg.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_NANOSVG_H 2 | #define XMOJI_NANOSVG_H 3 | 4 | #include "contrib/nanosvg/nanosvg.h" 5 | #include "contrib/nanosvg/nanosvgrast.h" 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /src/bin/xmoji/char32.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_CHAR32_H 2 | #define XMOJI_CHAR32_H 3 | 4 | #ifdef HAVE_CHAR32_T 5 | # include 6 | #else 7 | # include 8 | typedef uint_least32_t char32_t; 9 | #endif 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /tools/emojigen/src/util.h: -------------------------------------------------------------------------------- 1 | #ifndef EMOJIGEN_UTIL_H 2 | #define EMOJIGEN_UTIL_H 3 | 4 | #include 5 | 6 | void *xmalloc(size_t sz); 7 | void *xrealloc(void *p, size_t sz); 8 | void usage(const char *name); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /src/bin/xmoji/emojifont.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_EMOJIFONT_H 2 | #define XMOJI_EMOJIFONT_H 3 | 4 | typedef enum EmojiFont 5 | { 6 | EF_TINY, 7 | EF_SMALL, 8 | EF_MEDIUM, 9 | EF_LARGE, 10 | EF_HUGE 11 | } EmojiFont; 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /tools/emojigen/src/char32.h: -------------------------------------------------------------------------------- 1 | #ifndef EMOJIGEN_CHAR32_H 2 | #define EMOJIGEN_CHAR32_H 3 | 4 | #ifdef HAVE_CHAR32_T 5 | # include 6 | #else 7 | # include 8 | typedef uint_least32_t char32_t; 9 | #endif 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /tools/emojigen/src/emojigen.mk: -------------------------------------------------------------------------------- 1 | emojigen_MODULES= emojigen \ 2 | emojinames \ 3 | emojireader \ 4 | groupnames \ 5 | sourcegen \ 6 | translate \ 7 | util 8 | 9 | ifeq ($(HAVE_CHAR32_T),1) 10 | emojigen_DEFINES= -DHAVE_CHAR32_T 11 | endif 12 | 13 | $(call binrules,emojigen) 14 | -------------------------------------------------------------------------------- /tools/xtc/src/util.h: -------------------------------------------------------------------------------- 1 | #ifndef XTC_UTIL_H 2 | #define XTC_UTIL_H 3 | 4 | #include 5 | 6 | void *xmalloc(size_t sz); 7 | void *xrealloc(void *p, size_t sz); 8 | void usage(const char *name); 9 | char *derivename(const char *filename, const char *lang, 10 | const char *dir, const char *ext); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /src/bin/xmoji/macros.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_MACROS_H 2 | #define XMOJI_MACROS_H 3 | 4 | #define _STR(x) #x 5 | #define STR(x) _STR(x) 6 | 7 | #define X_ENUM(a) a, 8 | #define X_SZNM(a) { sizeof STR(a) - 1, STR(a) }, 9 | 10 | #define U8LPT(x) char(*)[sizeof x] 11 | #define U32LPT(x) char32_t(*)[sizeof x >> 2 ? sizeof x >> 2 : 1] 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /src/bin/xmoji/nanosvg.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "suppress.h" 6 | 7 | #define NANOSVG_ALL_COLOR_KEYWORDS 8 | #define NANOSVG_IMPLEMENTATION 9 | SUPPRESS(shadow) 10 | #include "contrib/nanosvg/nanosvg.h" 11 | ENDSUPPRESS 12 | 13 | #define NANOSVGRAST_IMPLEMENTATION 14 | #include "contrib/nanosvg/nanosvgrast.h" 15 | 16 | -------------------------------------------------------------------------------- /src/bin/xmoji/xmoji.desktop.in: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Xmoji 3 | GenericName=X11 Emoji Keyboard 4 | GenericName[de]=X11 Emoji-Tastatur 5 | Comment=Enter emojis faking keyboard input 6 | Comment[de]=Emojieingabe durch simulierte Tastaturereignisse 7 | Exec=%%bindir%%/xmoji 8 | Icon=xmoji 9 | StartupNotify=true 10 | Terminal=false 11 | Type=Application 12 | Categories=Utility; 13 | -------------------------------------------------------------------------------- /src/bin/xmoji/icons.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_ICONS_H 2 | #define XMOJI_ICONS_H 3 | 4 | #include 5 | 6 | extern const unsigned char *icon256; 7 | extern const size_t icon256sz; 8 | extern const unsigned char *icon48; 9 | extern const size_t icon48sz; 10 | extern const unsigned char *icon32; 11 | extern const size_t icon32sz; 12 | extern const unsigned char *icon16; 13 | extern const size_t icon16sz; 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /src/bin/xmoji/pen.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_PEN_H 2 | #define XMOJI_PEN_H 3 | 4 | #include "valuetypes.h" 5 | 6 | #include 7 | #include 8 | 9 | C_CLASS_DECL(Pen); 10 | 11 | Pen *Pen_create(void) ATTR_RETNONNULL; 12 | void Pen_configure(Pen *self, PictFormat format, Color color) CMETHOD; 13 | xcb_render_picture_t Pen_picture(Pen *self, 14 | xcb_render_picture_t ownerpic) CMETHOD; 15 | void Pen_destroy(Pen *self); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /src/bin/xmoji/icon.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_ICON_H 2 | #define XMOJI_ICON_H 3 | 4 | #include 5 | 6 | C_CLASS_DECL(Icon); 7 | C_CLASS_DECL(Pixmap); 8 | C_CLASS_DECL(Window); 9 | 10 | Icon *Icon_create(void); 11 | Icon *Icon_ref(Icon *icon) ATTR_NONNULL((1)); 12 | void Icon_add(Icon *self, Pixmap *pixmap) CMETHOD ATTR_NONNULL((2)); 13 | void Icon_apply(Icon *self, Window *window) CMETHOD ATTR_NONNULL((2)); 14 | void Icon_destroy(Icon *self); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/bin/xmoji/tablerow.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_TABLEROW_H 2 | #define XMOJI_TABLEROW_H 3 | 4 | #include "hbox.h" 5 | 6 | typedef struct MetaTableRow 7 | { 8 | MetaHBox base; 9 | } MetaTableRow; 10 | 11 | #define MetaTableRow_init(...) { \ 12 | .base = MetaHBox_init(__VA_ARGS__) \ 13 | } 14 | 15 | C_CLASS_DECL(TableRow); 16 | 17 | TableRow *TableRow_createBase(void *derived, void *parent); 18 | #define TableRow_create(...) TableRow_createBase(0, __VA_ARGS__) 19 | TableRow *TableRow_tryCast(void *obj); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /src/bin/xmoji/table.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_TABLE_H 2 | #define XMOJI_TABLE_H 3 | 4 | #include "tablerow.h" 5 | #include "vbox.h" 6 | 7 | typedef struct MetaTable 8 | { 9 | MetaVBox base; 10 | } MetaTable; 11 | 12 | #define MetaTable_init(...) { \ 13 | .base = MetaVBox_init(__VA_ARGS__) \ 14 | } 15 | 16 | C_CLASS_DECL(Table); 17 | 18 | Table *Table_createBase(void *derived, void *parent); 19 | #define Table_create(...) Table_createBase(0, __VA_ARGS__) 20 | void Table_addRow(void *self, TableRow *row) CMETHOD; 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /src/bin/xmoji/tooltip.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_TOOLTIP_H 2 | #define XMOJI_TOOLTIP_H 3 | 4 | #include "valuetypes.h" 5 | 6 | #include 7 | 8 | C_CLASS_DECL(Tooltip); 9 | C_CLASS_DECL(UniStr); 10 | C_CLASS_DECL(Widget); 11 | C_CLASS_DECL(Window); 12 | 13 | Tooltip *Tooltip_create(const UniStr *text, Widget *parent, unsigned delay); 14 | void Tooltip_activate(Tooltip *self, Window *window) 15 | CMETHOD ATTR_NONNULL((2)); 16 | void Tooltip_cancel(Tooltip *self) 17 | CMETHOD; 18 | void Tooltip_destroy(Tooltip *self); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /tools/xtc/src/xtc.c: -------------------------------------------------------------------------------- 1 | #include "compiler.h" 2 | #include "sourcegen.h" 3 | #include "updater.h" 4 | #include "util.h" 5 | 6 | #include 7 | 8 | int main(int argc, char **argv) 9 | { 10 | const char *name = argv[0]; 11 | if (!name) name = "xtc"; 12 | if (argc < 2) usage(name); 13 | 14 | if (!strcmp(argv[1], "source")) return dosource(argc, argv); 15 | if (!strcmp(argv[1], "update")) return doupdate(argc, argv); 16 | if (!strcmp(argv[1], "compile")) return docompile(argc, argv); 17 | usage(name); 18 | } 19 | 20 | -------------------------------------------------------------------------------- /src/bin/xmoji/singleinstance.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_SINGLEINSTANCE_H 2 | #define XMOJI_SINGLEINSTANCE_H 3 | 4 | #include 5 | 6 | C_CLASS_DECL(PSC_Event); 7 | C_CLASS_DECL(SingleInstance); 8 | 9 | SingleInstance *SingleInstance_create(void) ATTR_RETNONNULL; 10 | int SingleInstance_start(SingleInstance *self) CMETHOD; 11 | void SingleInstance_stop(SingleInstance *self) CMETHOD; 12 | PSC_Event *SingleInstance_secondary(SingleInstance *self) 13 | CMETHOD ATTR_RETNONNULL; 14 | void SingleInstance_destroy(SingleInstance *self); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/bin/xmoji/menu.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_MENU_H 2 | #define XMOJI_MENU_H 3 | 4 | #include "widget.h" 5 | 6 | typedef struct MetaMenu 7 | { 8 | MetaWidget base; 9 | } MetaMenu; 10 | 11 | #define MetaMenu_init(...) { \ 12 | .base = MetaWidget_init(__VA_ARGS__) \ 13 | } 14 | 15 | C_CLASS_DECL(Menu); 16 | 17 | Menu *Menu_createBase(void *derived, const char *name, void *parent); 18 | #define Menu_create(...) Menu_createBase(0, __VA_ARGS__) 19 | void Menu_addItem(void *self, void *command) CMETHOD; 20 | void Menu_popup(void *self, void *widget) CMETHOD; 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /src/bin/xmoji/scrollbox.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_SCROLLBOX_H 2 | #define XMOJI_SCROLLBOX_H 3 | 4 | #include "widget.h" 5 | 6 | typedef struct MetaScrollBox 7 | { 8 | MetaWidget base; 9 | } MetaScrollBox; 10 | 11 | #define MetaScrollBox_init(...) { \ 12 | .base = MetaWidget_init(__VA_ARGS__) \ 13 | } 14 | 15 | C_CLASS_DECL(ScrollBox); 16 | 17 | ScrollBox *ScrollBox_createBase(void *derived, const char *name, void *parent); 18 | #define ScrollBox_create(...) ScrollBox_createBase(0, __VA_ARGS__) 19 | void ScrollBox_setWidget(void *self, void *widget) CMETHOD; 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /src/bin/xmoji/tablerow.c: -------------------------------------------------------------------------------- 1 | #include "tablerow.h" 2 | 3 | #include 4 | #include 5 | 6 | static MetaTableRow mo = MetaTableRow_init( 7 | 0, 0, 0, 0, 8 | 0, 0, 0, 0, 0, 0, 0, 9 | 0, 0, 0, 0, 0, 0, 0, 10 | "TableRow", free); 11 | 12 | struct TableRow 13 | { 14 | Object base; 15 | }; 16 | 17 | TableRow *TableRow_createBase(void *derived, void *parent) 18 | { 19 | TableRow *self = PSC_malloc(sizeof *self); 20 | CREATEBASE(HBox, parent); 21 | return self; 22 | } 23 | 24 | TableRow *TableRow_tryCast(void *obj) 25 | { 26 | return Object_cast(obj); 27 | } 28 | 29 | -------------------------------------------------------------------------------- /src/bin/xmoji/x11app-int.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_X11APP_INT_H 2 | #define XMOJI_X11APP_INT_H 3 | 4 | #include "x11adapter.h" 5 | #include "x11app.h" 6 | 7 | void X11App_raiseError(X11App *self, Window *window, Widget *widget, 8 | RequestErrorEventArgs *err) 9 | CMETHOD ATTR_NONNULL((2)) ATTR_NONNULL((3)) ATTR_NONNULL((4)); 10 | 11 | void X11App_addWindow(X11App *self, Window *window) 12 | CMETHOD ATTR_NONNULL((2)); 13 | void X11App_removeWindow(X11App *self, Window *window) 14 | CMETHOD ATTR_NONNULL((2)); 15 | 16 | void X11App_setWmProperties(Window *window) ATTR_NONNULL((1)); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /src/bin/xmoji/pixmap.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_PIXMAP_H 2 | #define XMOJI_PIXMAP_H 3 | 4 | #include "valuetypes.h" 5 | 6 | #include 7 | #include 8 | 9 | C_CLASS_DECL(Pixmap); 10 | 11 | Pixmap *Pixmap_createFromPng(const unsigned char *data, size_t datasz); 12 | Pixmap *Pixmap_ref(Pixmap *pixmap) ATTR_NONNULL((1)); 13 | Size Pixmap_size(const Pixmap *self) CMETHOD; 14 | const unsigned char *Pixmap_bitmap(const Pixmap *self, size_t *size); 15 | void Pixmap_render(Pixmap *self, xcb_render_picture_t picture, Pos pos) 16 | CMETHOD; 17 | void Pixmap_destroy(Pixmap *self); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /src/bin/xmoji/shape.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_SHAPE_H 2 | #define XMOJI_SHAPE_H 3 | 4 | #include 5 | #include 6 | 7 | C_CLASS_DECL(Shape); 8 | 9 | typedef xcb_render_picture_t (*ShapeRenderer)(void *obj, 10 | xcb_render_picture_t ownerpic, const void *data); 11 | 12 | Shape *Shape_create(ShapeRenderer renderer, 13 | size_t datasz, const void *data) ATTR_RETNONNULL; 14 | void Shape_render(Shape *self, void *obj, 15 | xcb_render_picture_t ownerpic) CMETHOD; 16 | xcb_render_picture_t Shape_picture(const Shape *self) CMETHOD ATTR_PURE; 17 | void Shape_destroy(Shape *self); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /src/bin/xmoji/surface.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_SURFACE_H 2 | #define XMOJI_SURFACE_H 3 | 4 | #include "widget.h" 5 | 6 | typedef struct MetaSurface 7 | { 8 | MetaWidget base; 9 | } MetaSurface; 10 | 11 | #define MetaSurface_init(...) { \ 12 | .base = MetaWidget_init(__VA_ARGS__) \ 13 | } 14 | 15 | C_CLASS_DECL(Surface); 16 | 17 | Surface *Surface_createBase(void *derived, void *parent); 18 | #define Surface_create(...) Surface_createBase(0, __VA_ARGS__) 19 | void Surface_setWidget(void *self, void *widget) CMETHOD; 20 | void Surface_render(void *self, xcb_render_picture_t picture, 21 | Pos pos, Rect rect) CMETHOD; 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/bin/xmoji/xdgopen.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_XDGOPEN_H 2 | #define XMOJI_XDGOPEN_H 3 | 4 | #include 5 | 6 | typedef enum XdgOpenError 7 | { 8 | XOE_OK, /* no error, errorHandler isn't called */ 9 | XOE_SYSTEM, /* unspecified system error, e.g. fork() */ 10 | XOE_TOOLNOTFOUND, /* couldn't find the xdg-open tool */ 11 | XOE_EXEC /* error executing xdg-open */ 12 | } XdgOpenError; 13 | 14 | typedef void (*XdgOpenErrorHandler)(void *ctx, 15 | const char *url, XdgOpenError error); 16 | 17 | void xdgOpen(const char *url, void *ctx, XdgOpenErrorHandler errorHandler) 18 | ATTR_NONNULL((1)); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /src/bin/xmoji/hyperlink.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_HYPERLINK_H 2 | #define XMOJI_HYPERLINK_H 3 | 4 | #include "textlabel.h" 5 | 6 | typedef struct MetaHyperLink 7 | { 8 | MetaTextLabel base; 9 | } MetaHyperLink; 10 | 11 | #define MetaHyperLink_init(...) { \ 12 | .base = MetaTextLabel_init(__VA_ARGS__) \ 13 | } 14 | 15 | C_CLASS_DECL(HyperLink); 16 | 17 | HyperLink *HyperLink_createBase(void *derived, const char *name, void *parent); 18 | #define HyperLink_create(...) HyperLink_createBase(0, __VA_ARGS__) 19 | const char *HyperLink_link(const void *self) CMETHOD; 20 | void HyperLink_setLink(void *self, const char *link) CMETHOD; 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /src/bin/xmoji/filewatcher.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_FILEWATCHER_H 2 | #define XMOJI_FILEWATCHER_H 3 | 4 | #include 5 | 6 | C_CLASS_DECL(FileWatcher); 7 | C_CLASS_DECL(PSC_Event); 8 | 9 | typedef enum FileChange 10 | { 11 | FC_ERRORED, 12 | FC_MODIFIED, 13 | FC_CREATED, 14 | FC_DELETED 15 | } FileChange; 16 | 17 | FileWatcher *FileWatcher_create(const char *path) ATTR_NONNULL((1)); 18 | PSC_Event *FileWatcher_changed(FileWatcher *self) CMETHOD ATTR_RETNONNULL; 19 | int FileWatcher_watch(FileWatcher *self) CMETHOD; 20 | void FileWatcher_unwatch(FileWatcher *self) CMETHOD; 21 | void FileWatcher_destroy(FileWatcher *self); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/bin/xmoji/flyout.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_FLYOUT_H 2 | #define XMOJI_FLYOUT_H 3 | 4 | #include "widget.h" 5 | 6 | typedef struct MetaFlyout 7 | { 8 | MetaWidget base; 9 | } MetaFlyout; 10 | 11 | #define MetaFlyout_init(...) { \ 12 | .base = MetaWidget_init(__VA_ARGS__) \ 13 | } 14 | 15 | C_CLASS_DECL(Flyout); 16 | 17 | Flyout *Flyout_createBase(void *derived, const char *name, void *parent); 18 | #define Flyout_create(...) Flyout_createBase(0, __VA_ARGS__) 19 | void Flyout_setIncBorder(void *self, int enable) CMETHOD; 20 | void Flyout_setWidget(void *self, void *widget) CMETHOD; 21 | void Flyout_popup(void *self, void *widget) CMETHOD; 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .*.swp 2 | .*.cfg 3 | .*.pyc 4 | *.o 5 | *.d 6 | *.exe 7 | *.core 8 | defaults.mk 9 | *.files 10 | *.config 11 | *.includes 12 | *.creator 13 | *.user 14 | *.bin 15 | *.exo 16 | *.prg 17 | *.html 18 | *.path-cache 19 | *.xct 20 | /*.cfg 21 | /bin/ 22 | /lib/ 23 | /obj/ 24 | /test/ 25 | /dist/ 26 | /tools/*/bin/ 27 | /tools/*/lib/ 28 | /tools/*/obj/ 29 | /tools/*/test/ 30 | /tools/*/dist/ 31 | /tools/bin/ 32 | emojidata.h 33 | icon256.h 34 | icon48.h 35 | icon32.h 36 | icon16.h 37 | texts.c 38 | texts.h 39 | xmoji.desktop 40 | xmoji-emojis.def 41 | xmoji-emojis-*.def 42 | xmoji-ui.def 43 | .freetype-2.*.build 44 | freetype-2.* 45 | /ftbundle/root 46 | 47 | -------------------------------------------------------------------------------- /src/bin/xmoji/tabbox.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_TABBOX_H 2 | #define XMOJI_TABBOX_H 3 | 4 | #include "widget.h" 5 | 6 | typedef struct MetaTabBox 7 | { 8 | MetaWidget base; 9 | } MetaTabBox; 10 | 11 | #define MetaTabBox_init(...) { \ 12 | .base = MetaWidget_init(__VA_ARGS__) \ 13 | } 14 | 15 | C_CLASS_DECL(TabBox); 16 | 17 | TabBox *TabBox_createBase(void *derived, const char *name, void *parent); 18 | #define TabBox_create(...) TabBox_createBase(0, __VA_ARGS__) 19 | void TabBox_addTab(void *self, void *buttonWidget, void *contentWidget) 20 | CMETHOD ATTR_NONNULL((2)) ATTR_NONNULL((3)); 21 | void TabBox_setTab(void *self, int index) CMETHOD; 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/bin/xmoji/imagelabel.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_IMAGELABEL_H 2 | #define XMOJI_IMAGELABEL_H 3 | 4 | #include "widget.h" 5 | 6 | typedef struct MetaImageLabel 7 | { 8 | MetaWidget base; 9 | } MetaImageLabel; 10 | 11 | #define MetaImageLabel_init(...) { \ 12 | .base = MetaWidget_init(__VA_ARGS__) \ 13 | } 14 | 15 | C_CLASS_DECL(ImageLabel); 16 | C_CLASS_DECL(Pixmap); 17 | 18 | ImageLabel *ImageLabel_createBase(void *derived, 19 | const char *name, void *parent); 20 | #define ImageLabel_create(...) ImageLabel_createBase(0, __VA_ARGS__) 21 | Pixmap *ImageLabel_pixmap(const void *self) CMETHOD; 22 | void ImageLabel_setPixmap(void *self, Pixmap *pixmap) CMETHOD; 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /tools/emojigen/src/emojigen.c: -------------------------------------------------------------------------------- 1 | #include "emojinames.h" 2 | #include "groupnames.h" 3 | #include "sourcegen.h" 4 | #include "translate.h" 5 | #include "util.h" 6 | 7 | #include 8 | 9 | int main(int argc, char **argv) 10 | { 11 | const char *name = argv[0]; 12 | if (!name) name = "emojigen"; 13 | if (argc < 2) usage(name); 14 | 15 | if (!strcmp(argv[1], "source")) return dosource(argc, argv); 16 | if (!strcmp(argv[1], "groupnames")) return dogroupnames(argc, argv); 17 | if (!strcmp(argv[1], "emojinames")) return doemojinames(argc, argv); 18 | if (!strcmp(argv[1], "translate")) return dotranslate(argc, argv); 19 | usage(name); 20 | } 21 | 22 | -------------------------------------------------------------------------------- /src/bin/xmoji/keyinjector.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_KEYINJECTOR_H 2 | #define XMOJI_KEYINJECTOR_H 3 | 4 | #include 5 | 6 | C_CLASS_DECL(UniStr); 7 | 8 | typedef enum InjectorFlags 9 | { 10 | IF_NONE = 0, 11 | IF_ADDSPACE = 1 << 0, /* Inject a space after each emoji */ 12 | IF_ADDZWSPACE = 1 << 1, /* Inject a zero-width space after each emoji */ 13 | IF_EXTRAZWJ = 1 << 2 /* For a ZWJ sequence, add an extra ZWJ at the 14 | beginning */ 15 | } InjectorFlags; 16 | 17 | void KeyInjector_init(unsigned beforems, unsigned afterms, 18 | InjectorFlags flags); 19 | void KeyInjector_inject(const UniStr *str); 20 | void KeyInjector_done(void); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | env: 10 | CI_CFLAGS: -O2 -Wall -Wextra -Wshadow -Werror -pedantic 11 | CI_DEPS: | 12 | libfontconfig1-dev libfreetype-dev libharfbuzz-dev libxcb-cursor-dev 13 | libxcb-image0-dev libxcb-xkb-dev libxcb-xtest0-dev libxkbcommon-x11-dev 14 | 15 | jobs: 16 | build: 17 | runs-on: ubuntu-24.04 18 | steps: 19 | - uses: actions/checkout@v4 20 | with: 21 | submodules: recursive 22 | - name: build-depends 23 | run: sudo apt-get -y install ${CI_DEPS} 24 | - name: build 25 | run: make -j4 CFLAGS="${CI_CFLAGS}" 26 | 27 | -------------------------------------------------------------------------------- /src/bin/xmoji/flowgrid.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_FLOWGRID_H 2 | #define XMOJI_FLOWGRID_H 3 | 4 | #include "widget.h" 5 | 6 | typedef struct MetaFlowGrid 7 | { 8 | MetaWidget base; 9 | } MetaFlowGrid; 10 | 11 | #define MetaFlowGrid_init(...) { \ 12 | .base = MetaWidget_init(__VA_ARGS__) \ 13 | } 14 | 15 | C_CLASS_DECL(FlowGrid); 16 | 17 | FlowGrid *FlowGrid_createBase(void *derived, void *parent); 18 | #define FlowGrid_create(...) FlowGrid_createBase(0, __VA_ARGS__) 19 | void FlowGrid_addWidget(void *self, void *widget) CMETHOD; 20 | void *FlowGrid_widgetAt(void *self, size_t index) CMETHOD; 21 | Size FlowGrid_spacing(const void *self) CMETHOD; 22 | void FlowGrid_setSpacing(void *self, Size spacing) CMETHOD; 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /src/bin/xmoji/spinbox.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_SPINBOX_H 2 | #define XMOJI_SPINBOX_H 3 | 4 | #include "widget.h" 5 | 6 | typedef struct MetaSpinBox 7 | { 8 | MetaWidget base; 9 | } MetaSpinBox; 10 | 11 | #define MetaSpinBox_init(...) { \ 12 | .base = MetaWidget_init(__VA_ARGS__) \ 13 | } 14 | 15 | C_CLASS_DECL(PSC_Event); 16 | C_CLASS_DECL(SpinBox); 17 | 18 | SpinBox *SpinBox_createBase(void *derived, const char *name, 19 | int min, int max, int step, void *parent); 20 | #define SpinBox_create(...) SpinBox_createBase(0, __VA_ARGS__) 21 | int SpinBox_value(const void *self) CMETHOD; 22 | PSC_Event *SpinBox_valueChanged(void *self) CMETHOD ATTR_RETNONNULL; 23 | void SpinBox_setValue(void *self, int value) CMETHOD; 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /tools/emojigen/src/util.c: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | 3 | #include 4 | #include 5 | 6 | void *xmalloc(size_t sz) 7 | { 8 | void *p = malloc(sz); 9 | if (!p) abort(); 10 | return p; 11 | } 12 | 13 | void *xrealloc(void *p, size_t sz) 14 | { 15 | p = realloc(p, sz); 16 | if (!p) abort(); 17 | return p; 18 | } 19 | 20 | void usage(const char *name) 21 | { 22 | fprintf(stderr, "usage: %s source outname emoji-test.txt\n" 23 | " %s groupnames strings.def strings.def.in emoji-test.txt\n" 24 | " %s emojinames strings.def emoji-test.txt\n" 25 | " %s translate strings-lang.def emoji-test.txt lang.xml [lang.xml ...]\n", 26 | name, name, name, name); 27 | exit(EXIT_FAILURE); 28 | } 29 | 30 | -------------------------------------------------------------------------------- /tools/emojigen/src/emojireader.h: -------------------------------------------------------------------------------- 1 | #ifndef EMOJIGEN_EMOJIREADER_H 2 | #define EMOJIGEN_EMOJIREADER_H 3 | 4 | #include "char32.h" 5 | #include 6 | 7 | typedef struct Emoji Emoji; 8 | typedef struct EmojiGroup EmojiGroup; 9 | 10 | struct Emoji 11 | { 12 | char32_t *codepoints; 13 | char *name; 14 | size_t len; 15 | size_t namelen; 16 | size_t groupno; 17 | unsigned variants; 18 | }; 19 | 20 | struct EmojiGroup 21 | { 22 | char *name; 23 | size_t namelen; 24 | size_t start; 25 | size_t len; 26 | }; 27 | 28 | int readEmojis(const char *datafile); 29 | size_t Emoji_count(void); 30 | const Emoji *Emoji_at(size_t i); 31 | size_t EmojiGroup_count(void); 32 | const EmojiGroup *EmojiGroup_at(size_t i); 33 | void emojisDone(void); 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /src/bin/xmoji/translator.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_TRANSLATOR_H 2 | #define XMOJI_TRANSLATOR_H 3 | 4 | #include 5 | 6 | C_CLASS_DECL(Translator); 7 | 8 | #define TR Translator_getText 9 | #define FTR Translator_getTranslation 10 | #define NTR Translator_getOriginal 11 | 12 | Translator *Translator_create(const char *name, const char *lang, 13 | const void *(*gettext)(unsigned id)) 14 | ATTR_NONNULL((1)) ATTR_NONNULL((2)) ATTR_NONNULL((3)) ATTR_RETNONNULL; 15 | const void *Translator_getText(const Translator *self, unsigned id) 16 | CMETHOD; 17 | const void *Translator_getTranslation(const Translator *self, unsigned id) 18 | CMETHOD; 19 | const void *Translator_getOriginal(const Translator *self, unsigned id) 20 | CMETHOD; 21 | void Translator_destroy(Translator *self); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /tools/xtc/src/deffile.h: -------------------------------------------------------------------------------- 1 | #ifndef XTC_DEFFILE_H 2 | #define XTC_DEFFILE_H 3 | 4 | #include 5 | 6 | typedef enum DefType 7 | { 8 | DT_CHAR, 9 | DT_CHAR32 10 | } DefType; 11 | 12 | typedef struct DefEntry DefEntry; 13 | typedef struct DefFile DefFile; 14 | 15 | DefFile *DefFile_create(const char *filename); 16 | size_t DefFile_len(const DefFile *self); 17 | const DefEntry *DefFile_byId(const DefFile *self, unsigned id); 18 | const DefEntry *DefFile_byKey(const DefFile *self, const char *key); 19 | const char *DefEntry_key(const DefEntry *self); 20 | const char *DefEntry_from(const DefEntry *self); 21 | const char *DefEntry_to(const DefEntry *self); 22 | DefType DefEntry_type(const DefEntry *self); 23 | unsigned DefEntry_id(const DefEntry *self); 24 | void DefFile_destroy(DefFile *self); 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /src/bin/xmoji/hbox.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_HBOX_H 2 | #define XMOJI_HBOX_H 3 | 4 | #include "widget.h" 5 | 6 | typedef struct MetaHBox 7 | { 8 | MetaWidget base; 9 | } MetaHBox; 10 | 11 | #define MetaHBox_init(...) { \ 12 | .base = MetaWidget_init(__VA_ARGS__) \ 13 | } 14 | 15 | C_CLASS_DECL(HBox); 16 | 17 | HBox *HBox_createBase(void *derived, void *parent); 18 | #define HBox_create(...) HBox_createBase(0, __VA_ARGS__) 19 | void HBox_addWidget(void *self, void *widget) CMETHOD; 20 | uint16_t HBox_spacing(const void *self) CMETHOD; 21 | void HBox_setSpacing(void *self, uint16_t spacing) CMETHOD; 22 | 23 | /* "protected" */ 24 | unsigned HBox_cols(const void *self) CMETHOD; 25 | uint16_t HBox_minWidth(const void *self, unsigned col) CMETHOD; 26 | void HBox_setMinWidth(void *self, unsigned col, uint16_t minWidth) CMETHOD; 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /src/bin/xmoji/configfile.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_CONFIGFILE_H 2 | #define XMOJI_CONFIGFILE_H 3 | 4 | #include 5 | #include 6 | 7 | C_CLASS_DECL(ConfigFile); 8 | C_CLASS_DECL(PSC_Event); 9 | 10 | typedef struct ConfigFileChangedEventArgs 11 | { 12 | const char *key; 13 | } ConfigFileChangedEventArgs; 14 | 15 | ConfigFile *ConfigFile_create(const char *path, size_t nkeys, 16 | const char **keys); 17 | void ConfigFile_set(ConfigFile *self, const char *key, char *val) 18 | CMETHOD ATTR_NONNULL((2)) ATTR_NONNULL((3)); 19 | const char *ConfigFile_get(const ConfigFile *self, const char *key) 20 | CMETHOD ATTR_NONNULL((2)); 21 | PSC_Event *ConfigFile_changed(ConfigFile *self) 22 | CMETHOD; 23 | int ConfigFile_write(ConfigFile *self, int sync) 24 | CMETHOD; 25 | void ConfigFile_destroy(ConfigFile *self); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /src/bin/xmoji/dropdown.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_DROPDOWN_H 2 | #define XMOJI_DROPDOWN_H 3 | 4 | #include "button.h" 5 | 6 | typedef struct MetaDropdown 7 | { 8 | MetaButton base; 9 | } MetaDropdown; 10 | 11 | #define MetaDropdown_init(...) { \ 12 | .base = MetaButton_init(__VA_ARGS__) \ 13 | } 14 | 15 | C_CLASS_DECL(Dropdown); 16 | C_CLASS_DECL(PSC_Event); 17 | C_CLASS_DECL(UniStr); 18 | 19 | Dropdown *Dropdown_createBase(void *derived, 20 | const char *name, void *parent); 21 | #define Dropdown_create(...) Dropdown_createBase(0, __VA_ARGS__) 22 | void Dropdown_addOption(void *self, const UniStr *name) 23 | CMETHOD ATTR_NONNULL((2)); 24 | unsigned Dropdown_selectedIndex(const void *self) CMETHOD; 25 | PSC_Event *Dropdown_selected(void *self) CMETHOD ATTR_RETNONNULL; 26 | void Dropdown_select(void *self, unsigned index) CMETHOD; 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /src/bin/xmoji/emojihistory.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_EMOJIHISTORY_H 2 | #define XMOJI_EMOJIHISTORY_H 3 | 4 | #include 5 | #include 6 | 7 | C_CLASS_DECL(Emoji); 8 | C_CLASS_DECL(EmojiHistory); 9 | C_CLASS_DECL(PSC_Event); 10 | C_CLASS_DECL(UniStr); 11 | 12 | EmojiHistory *EmojiHistory_create(size_t size); 13 | 14 | PSC_Event *EmojiHistory_changed(EmojiHistory *self) 15 | CMETHOD ATTR_RETNONNULL; 16 | const Emoji *EmojiHistory_at(const EmojiHistory *self, size_t i) 17 | CMETHOD; 18 | void EmojiHistory_record(EmojiHistory *self, const UniStr *str) 19 | CMETHOD ATTR_NONNULL((2)); 20 | char *EmojiHistory_serialize(const EmojiHistory *self) 21 | CMETHOD; 22 | void EmojiHistory_deserialize(EmojiHistory *self, const char *str) 23 | CMETHOD ATTR_NONNULL((2)); 24 | 25 | void EmojiHistory_destroy(EmojiHistory *self); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /src/bin/xmoji/vbox.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_VBOX_H 2 | #define XMOJI_VBOX_H 3 | 4 | #include "widget.h" 5 | 6 | typedef struct MetaVBox 7 | { 8 | MetaWidget base; 9 | void (*layout)(void *vbox, int updateMinSize); 10 | } MetaVBox; 11 | 12 | #define MetaVBox_init(mlayout, ...) { \ 13 | .layout = mlayout, \ 14 | .base = MetaWidget_init(__VA_ARGS__) \ 15 | } 16 | 17 | C_CLASS_DECL(VBox); 18 | 19 | VBox *VBox_createBase(void *derived, void *parent); 20 | #define VBox_create(...) VBox_createBase(0, __VA_ARGS__) 21 | void VBox_addWidget(void *self, void *widget) CMETHOD; 22 | uint16_t VBox_spacing(const void *self) CMETHOD; 23 | void VBox_setSpacing(void *self, uint16_t spacing) CMETHOD; 24 | 25 | /* "protected" */ 26 | unsigned VBox_rows(const void *self) CMETHOD; 27 | void *VBox_widget(void *self, unsigned row) CMETHOD; 28 | int VBox_indexOf(void *self, void *widget) CMETHOD; 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/bin/xmoji/suppress.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_SUPPRESS_H 2 | #define XMOJI_SUPPRESS_H 3 | 4 | #if defined(__clang__) 5 | # define xmoji___compiler clang 6 | # define xmoji___unknown xmoji___suppress(-Wunknown-warning-option) 7 | #elif defined(__GNUC__) 8 | # define xmoji___compiler GCC 9 | # define xmoji___unknown xmoji___suppress(-Wpragmas) 10 | #endif 11 | #ifdef xmoji___compiler 12 | # define xmoji___pragma(x) _Pragma(#x) 13 | # define xmoji___diagprag1(x,y) xmoji___pragma(x diagnostic y) 14 | # define xmoji___diagprag(x) xmoji___diagprag1(xmoji___compiler, x) 15 | # define xmoji___suppress1(x) xmoji___diagprag(ignored x) 16 | # define xmoji___suppress(x) xmoji___suppress1(#x) 17 | # define SUPPRESS(x) xmoji___diagprag(push) \ 18 | xmoji___unknown xmoji___suppress(-W##x) 19 | # define ENDSUPPRESS xmoji___diagprag(pop) 20 | #else 21 | # define SUPPRESS(x) 22 | # define ENDSUPPRESS 23 | #endif 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /tools/emojigen/src/emojinames.c: -------------------------------------------------------------------------------- 1 | #include "emojinames.h" 2 | 3 | #include "emojireader.h" 4 | #include "util.h" 5 | 6 | #include 7 | #include 8 | 9 | int doemojinames(int argc, char **argv) 10 | { 11 | if (argc != 4) usage(argv[0]); 12 | int rc = EXIT_FAILURE; 13 | FILE *out = 0; 14 | if (readEmojis(argv[3]) < 0) 15 | { 16 | fprintf(stderr, "Cannot read emojis from `%s'\n", argv[3]); 17 | goto done; 18 | } 19 | out = fopen(argv[2], "w"); 20 | if (!out) 21 | { 22 | fprintf(stderr, "Cannot open `%s' for writing\n", argv[2]); 23 | goto done; 24 | } 25 | 26 | size_t emojisize = Emoji_count(); 27 | for (size_t i = 0; i < emojisize; ++i) 28 | { 29 | const Emoji *emoji = Emoji_at(i); 30 | fprintf(out, "$w$emoji%zu\n.\n%s\n.\n\n", i, emoji->name); 31 | } 32 | rc = EXIT_SUCCESS; 33 | 34 | done: 35 | if (out) fclose(out); 36 | emojisDone(); 37 | return rc; 38 | } 39 | 40 | -------------------------------------------------------------------------------- /src/bin/xmoji/contrib/nanosvg/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013-14 Mikko Mononen memon@inside.org 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 2. Altered source versions must be plainly marked as such, and must not be 16 | misrepresented as being the original software. 17 | 3. This notice may not be removed or altered from any source distribution. 18 | 19 | -------------------------------------------------------------------------------- /src/bin/xmoji/textlabel.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_TEXTLABEL_H 2 | #define XMOJI_TEXTLABEL_H 3 | 4 | #include "widget.h" 5 | 6 | typedef struct MetaTextLabel 7 | { 8 | MetaWidget base; 9 | } MetaTextLabel; 10 | 11 | #define MetaTextLabel_init(...) { \ 12 | .base = MetaWidget_init(__VA_ARGS__) \ 13 | } 14 | 15 | C_CLASS_DECL(TextLabel); 16 | C_CLASS_DECL(TextRenderer); 17 | C_CLASS_DECL(UniStr); 18 | 19 | typedef void (*RenderCallback)(void *ctx, TextRenderer *renderer); 20 | 21 | TextLabel *TextLabel_createBase(void *derived, const char *name, void *parent); 22 | #define TextLabel_create(...) TextLabel_createBase(0, __VA_ARGS__) 23 | const UniStr *TextLabel_text(const void *self) CMETHOD; 24 | void TextLabel_setText(void *self, const UniStr *text) CMETHOD; 25 | void TextLabel_setColor(void *self, ColorRole color) CMETHOD; 26 | void TextLabel_setUnderline(void *self, int px) CMETHOD; 27 | void TextLabel_setRenderCallback(void *self, void *ctx, 28 | RenderCallback callback) CMETHOD; 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/bin/xmoji/emojibutton.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_EMOJIBUTTON_H 2 | #define XMOJI_EMOJIBUTTON_H 3 | 4 | #include "button.h" 5 | 6 | typedef struct MetaEmojiButton 7 | { 8 | MetaButton base; 9 | } MetaEmojiButton; 10 | 11 | #define MetaEmojiButton_init(...) { \ 12 | .base = MetaButton_init(__VA_ARGS__) \ 13 | } 14 | 15 | C_CLASS_DECL(Emoji); 16 | C_CLASS_DECL(EmojiButton); 17 | C_CLASS_DECL(PSC_Event); 18 | C_CLASS_DECL(Translator); 19 | 20 | EmojiButton *EmojiButton_createBase(void *derived, 21 | const char *name, const Translator *tr, int variants, void *parent); 22 | #define EmojiButton_create(...) EmojiButton_createBase(0, __VA_ARGS__) 23 | PSC_Event *EmojiButton_injected(void *self) 24 | CMETHOD; 25 | PSC_Event *EmojiButton_pasted(void *self) 26 | CMETHOD; 27 | void EmojiButton_setEmoji(void *self, const Emoji *emoji) 28 | CMETHOD ATTR_NONNULL((2)); 29 | void EmojiButton_addVariant(void *self, const Emoji *variant) 30 | CMETHOD ATTR_NONNULL((2)); 31 | void EmojiButton_clearVariants(void *self) 32 | CMETHOD; 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /src/bin/xmoji/icons.c: -------------------------------------------------------------------------------- 1 | #include "icons.h" 2 | #include "suppress.h" 3 | 4 | SUPPRESS(overlength-strings) 5 | static const unsigned char icon256a[sizeof 6 | #include "icon256.h" 7 | - 1] = 8 | #include "icon256.h" 9 | ; 10 | const unsigned char *icon256 = icon256a; 11 | const size_t icon256sz = sizeof 12 | #include "icon256.h" 13 | - 1; 14 | 15 | static const unsigned char icon48a[sizeof 16 | #include "icon48.h" 17 | - 1] = 18 | #include "icon48.h" 19 | ; 20 | const unsigned char *icon48 = icon48a; 21 | const size_t icon48sz = sizeof 22 | #include "icon48.h" 23 | - 1; 24 | 25 | static const unsigned char icon32a[sizeof 26 | #include "icon32.h" 27 | - 1] = 28 | #include "icon32.h" 29 | ; 30 | const unsigned char *icon32 = icon32a; 31 | const size_t icon32sz = sizeof 32 | #include "icon32.h" 33 | - 1; 34 | 35 | static const unsigned char icon16a[sizeof 36 | #include "icon16.h" 37 | - 1] = 38 | #include "icon16.h" 39 | ; 40 | const unsigned char *icon16 = icon16a; 41 | const size_t icon16sz = sizeof 42 | #include "icon16.h" 43 | - 1; 44 | ENDSUPPRESS 45 | 46 | -------------------------------------------------------------------------------- /src/bin/xmoji/xselection.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_XSELECTION_H 2 | #define XMOJI_XSELECTION_H 3 | 4 | #include 5 | 6 | C_CLASS_DECL(Widget); 7 | C_CLASS_DECL(Window); 8 | C_CLASS_DECL(XSelection); 9 | 10 | typedef enum XSelectionName 11 | { 12 | XSN_PRIMARY, 13 | XSN_CLIPBOARD 14 | } XSelectionName; 15 | 16 | typedef enum XSelectionType 17 | { 18 | XST_NONE = 0, // data is NULL 19 | XST_TEXT = 1 << 0, // data is UniStr 20 | } XSelectionType; 21 | 22 | typedef struct XSelectionContent 23 | { 24 | void *data; 25 | XSelectionType type; 26 | } XSelectionContent; 27 | 28 | typedef void (*XSelectionCallback)(Widget *widget, XSelectionContent content); 29 | 30 | XSelection *XSelection_create(Window *w, XSelectionName name) 31 | ATTR_NONNULL((1)); 32 | void XSelection_request(XSelection *self, XSelectionType type, 33 | Widget *widget, XSelectionCallback received) 34 | CMETHOD ATTR_NONNULL((4)); 35 | void XSelection_publish(XSelection *self, Widget *owner, 36 | XSelectionContent content) 37 | CMETHOD; 38 | void XSelection_destroy(XSelection *self); 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /src/bin/xmoji/textbox.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_TEXTBOX_H 2 | #define XMOJI_TEXTBOX_H 3 | 4 | #include "widget.h" 5 | 6 | typedef struct MetaTextBox 7 | { 8 | MetaWidget base; 9 | } MetaTextBox; 10 | 11 | #define MetaTextBox_init(...) { \ 12 | .base = MetaWidget_init(__VA_ARGS__) \ 13 | } 14 | 15 | C_CLASS_DECL(PSC_Event); 16 | C_CLASS_DECL(TextBox); 17 | C_CLASS_DECL(UniStr); 18 | 19 | typedef int (*InputFilter)(void *obj, const UniStr *str); 20 | 21 | TextBox *TextBox_createBase(void *derived, const char *name, void *parent); 22 | #define TextBox_create(...) TextBox_createBase(0, __VA_ARGS__) 23 | const UniStr *TextBox_text(const void *self) CMETHOD; 24 | PSC_Event *TextBox_textChanged(void *self) CMETHOD ATTR_RETNONNULL; 25 | void TextBox_setText(void *self, const UniStr *text) CMETHOD; 26 | unsigned TextBox_maxLen(const void *self) CMETHOD; 27 | void TextBox_setMaxLen(void *self, unsigned len) CMETHOD; 28 | void TextBox_setPlaceholder(void *self, const UniStr *text) CMETHOD; 29 | void TextBox_setGrab(void *self, int grab) CMETHOD; 30 | void TextBox_setClearBtn(void *self, int enabled) CMETHOD; 31 | void TextBox_setInputFilter(void *self, void *obj, InputFilter filter) CMETHOD; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/bin/xmoji/button.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_BUTTON_H 2 | #define XMOJI_BUTTON_H 3 | 4 | #include "widget.h" 5 | 6 | typedef struct MetaButton 7 | { 8 | MetaWidget base; 9 | } MetaButton; 10 | 11 | #define MetaButton_init(...) { \ 12 | .base = MetaWidget_init(__VA_ARGS__) \ 13 | } 14 | 15 | C_CLASS_DECL(Button); 16 | C_CLASS_DECL(Command); 17 | C_CLASS_DECL(PSC_Event); 18 | C_CLASS_DECL(TextLabel); 19 | C_CLASS_DECL(UniStr); 20 | 21 | Button *Button_createBase(void *derived, const char *name, void *parent); 22 | #define Button_create(...) Button_createBase(0, __VA_ARGS__) 23 | PSC_Event *Button_clicked(void *self) CMETHOD; 24 | TextLabel *Button_label(void *self) CMETHOD; 25 | const UniStr *Button_text(const void *self) CMETHOD; 26 | void Button_setText(void *self, const UniStr *text) CMETHOD; 27 | void Button_setBorderWidth(void *self, uint8_t width) CMETHOD; 28 | void Button_setColors(void *self, ColorRole normal, ColorRole hover) CMETHOD; 29 | void Button_setLabelPadding(void *self, Box padding) CMETHOD; 30 | void Button_setLabelAlign(void *self, Align align) CMETHOD; 31 | void Button_setMinWidth(void *self, uint16_t width) CMETHOD; 32 | void Button_attachCommand(void *self, Command *command) CMETHOD; 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /src/bin/xmoji/unistrbuilder.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_UNISTRBUILDER_H 2 | #define XMOJI_UNISTRBUILDER_H 3 | 4 | #include "char32.h" 5 | 6 | #include 7 | #include 8 | 9 | C_CLASS_DECL(UniStr); 10 | C_CLASS_DECL(UniStrBuilder); 11 | 12 | UniStrBuilder *UniStrBuilder_create(void); 13 | UniStrBuilder *UniStrBuilder_clone(UniStrBuilder *builder); 14 | 15 | void UniStrBuilder_appendChar(UniStrBuilder *self, char32_t c) 16 | CMETHOD; 17 | void UniStrBuilder_appendStr(UniStrBuilder *self, const char32_t *s) 18 | CMETHOD ATTR_NONNULL((2)); 19 | 20 | void UniStrBuilder_insertChar(UniStrBuilder *self, 21 | size_t pos, char32_t c) 22 | CMETHOD; 23 | void UniStrBuilder_insertStr(UniStrBuilder *self, 24 | size_t pos, const char32_t *s, size_t maxlen) 25 | CMETHOD ATTR_NONNULL((3)); 26 | 27 | void UniStrBuilder_clear(UniStrBuilder *self) 28 | CMETHOD; 29 | void UniStrBuilder_remove(UniStrBuilder *self, 30 | size_t pos, size_t len) 31 | CMETHOD; 32 | 33 | UniStr *UniStrBuilder_string(const UniStrBuilder *self) 34 | CMETHOD ATTR_RETNONNULL; 35 | const UniStr *UniStrBuilder_stringView(const UniStrBuilder *self) 36 | CMETHOD ATTR_RETNONNULL; 37 | 38 | void UniStrBuilder_destroy(UniStrBuilder *self); 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /src/bin/xmoji/colorset.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_COLORSET_H 2 | #define XMOJI_COLORSET_H 3 | 4 | #include "valuetypes.h" 5 | 6 | #include 7 | 8 | typedef enum ColorRole 9 | { 10 | COLOR_NORMAL, 11 | COLOR_BG_NORMAL, 12 | COLOR_ABOVE, 13 | COLOR_BG_ABOVE, 14 | COLOR_BELOW, 15 | COLOR_BG_BELOW, 16 | COLOR_LOWEST, 17 | COLOR_BG_LOWEST, 18 | COLOR_ACTIVE, 19 | COLOR_BG_ACTIVE, 20 | COLOR_DISABLED, 21 | COLOR_BG_DISABLED, 22 | COLOR_SELECTED, 23 | COLOR_BG_SELECTED, 24 | COLOR_TOOLTIP, 25 | COLOR_BG_TOOLTIP, 26 | COLOR_LINK, 27 | COLOR_HOVER, 28 | COLOR_BORDER, 29 | COLOR_BORDER_TOOLTIP, 30 | COLOR_NUMROLES 31 | } ColorRole; 32 | 33 | C_CLASS_DECL(ColorSet); 34 | 35 | const ColorSet *ColorSet_default(void); 36 | ColorSet *ColorSet_create(void); 37 | ColorSet *ColorSet_createFor(const char *name); 38 | ColorSet *ColorSet_clone(const ColorSet *self) CMETHOD; 39 | int ColorSet_valid(const ColorSet *self) CMETHOD; 40 | Color ColorSet_color(const ColorSet *self, ColorRole role) CMETHOD; 41 | void ColorSet_setColor(ColorSet *self, ColorRole role, Color color) CMETHOD; 42 | void ColorSet_destroy(ColorSet *self); 43 | 44 | int Color_fromString(Color *color, const char *str); 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /src/bin/xmoji/command.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_COMMAND_H 2 | #define XMOJI_COMMAND_H 3 | 4 | #include "object.h" 5 | 6 | #include 7 | 8 | C_CLASS_DECL(Command); 9 | C_CLASS_DECL(PSC_Event); 10 | C_CLASS_DECL(UniStr); 11 | 12 | typedef struct CommandTriggeredEventArgs 13 | { 14 | void *sender; 15 | void *args; 16 | } CommandTriggeredEventArgs; 17 | 18 | typedef PSC_Event *(*WidgetEvent)(void *widget); 19 | 20 | typedef struct MetaCommand 21 | { 22 | MetaObject base; 23 | } MetaCommand; 24 | 25 | #define MetaCommand_init(...) { \ 26 | .base = MetaObject_init(__VA_ARGS__) \ 27 | } 28 | 29 | Command *Command_createBase(void *derived, 30 | const UniStr *name, const UniStr *description, void *parent); 31 | #define Command_create(...) Command_createBase(0, __VA_ARGS__) 32 | PSC_Event *Command_triggered(void *self) CMETHOD; 33 | const UniStr *Command_name(const void *self) CMETHOD; 34 | const UniStr *Command_description(const void *self) CMETHOD; 35 | void Command_trigger(void *self) CMETHOD; 36 | void Command_attach(void *self, void *widget, WidgetEvent event) 37 | CMETHOD ATTR_NONNULL((2)) ATTR_NONNULL((3)); 38 | void Command_detach(void *self, void *widget, WidgetEvent event) 39 | CMETHOD ATTR_NONNULL((2)) ATTR_NONNULL((3)); 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /src/bin/xmoji/xrdb.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_XRDB_H 2 | #define XMOJI_XRDB_H 3 | 4 | #include 5 | #include 6 | 7 | C_CLASS_DECL(XRdb); 8 | 9 | #define XRDB_KEYLEN 5 10 | typedef const char *XRdbKey[XRDB_KEYLEN]; 11 | #define XRdbKey(...) (XRdbKey){ __VA_ARGS__ } 12 | 13 | typedef enum XRdbQueryFlags 14 | { 15 | XRQF_NONE = 0, 16 | XRQF_OVERRIDES = 1 << 0, 17 | XRQF_ROOT = 1 << 1 18 | }XRdbQueryFlags; 19 | 20 | XRdb *XRdb_create(const char *str, size_t strlen, 21 | const char *className, const char *instanceName); 22 | void XRdb_register(XRdb *self, const char *className, const char *instanceName) 23 | CMETHOD ATTR_NONNULL((2)) ATTR_NONNULL((3)); 24 | void XRdb_setOverrides(XRdb *self, int argc, char **argv) 25 | CMETHOD ATTR_NONNULL((3)); 26 | const char *XRdb_value(const XRdb *self, XRdbKey key, XRdbQueryFlags flags) 27 | CMETHOD; 28 | int XRdb_bool(const XRdb *self, XRdbKey key, XRdbQueryFlags flags, int def) 29 | CMETHOD; 30 | long XRdb_int(const XRdb *self, XRdbKey key, XRdbQueryFlags flags, 31 | long def, long min, long max) 32 | CMETHOD; 33 | double XRdb_float(const XRdb *self, XRdbKey key, XRdbQueryFlags flags, 34 | double def, double min, double max) 35 | CMETHOD; 36 | void XRdb_destroy(XRdb *self); 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024, Felix Palmen 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /GNUmakefile: -------------------------------------------------------------------------------- 1 | BOOLCONFVARS_OFF= BUNDLED_FREETYPE TRACE 2 | BOOLCONFVARS_ON= BUNDLED_POSER WITH_NLS WITH_SVG 3 | USES= fdofiles gen pkgconfig sub 4 | 5 | SUBBUILD= BIN2CSTR EMOJIGEN FREETYPE XTC 6 | 7 | BIN2CSTR_TARGET= tools/bin/bin2cstr 8 | BIN2CSTR_SRCDIR= tools/bin2cstr 9 | BIN2CSTR_MAKEARGS= DESTDIR=../bin prefix= bindir= \ 10 | HOSTBUILD=1 PORTABLE=1 STATIC=0 11 | BIN2CSTR_MAKEGOAL= install 12 | BIN2CSTR_CLEANGOAL= distclean 13 | 14 | EMOJIGEN_TARGET= tools/bin/emojigen 15 | EMOJIGEN_SRCDIR= tools/emojigen 16 | EMOJIGEN_MAKEARGS= DESTDIR=../bin prefix= bindir= HOSTBUILD=1 \ 17 | PORTABLE=1 STATIC=0 HAVE_CHAR32_T=$(HAVE_CHAR32_T) 18 | EMOJIGEN_MAKEGOAL= install 19 | EMOJIGEN_CLEANGOAL= distclean 20 | 21 | FREETYPE_TARGET= ftbundle/root/lib/libfreetype.a 22 | FREETYPE_SRCDIR= ftbundle 23 | FREETYPE_MAKEARGS= CC=$(CC) 24 | 25 | XTC_TARGET= tools/bin/xtc 26 | XTC_SRCDIR= tools/xtc 27 | XTC_MAKEARGS= DESTDIR=../bin prefix= bindir= \ 28 | HOSTBUILD=1 PORTABLE=1 STATIC=0 29 | XTC_MAKEGOAL= install 30 | XTC_CLEANGOAL= distclean 31 | 32 | DISTCLEANDIRS= tools/bin 33 | 34 | NODIST= poser/zimk 35 | 36 | include zimk/zimk.mk 37 | 38 | ifeq ($(BUNDLED_POSER),1) 39 | WITH_TLS:= 0 40 | posercore_BUILDWITH:= # 41 | posercore_STRIPWITH:= # 42 | posercore_INSTALLWITH:= # 43 | posercore_PRECFLAGS:= -I./poser/include 44 | $(call zinc,poser/src/lib/core/core.mk) 45 | endif 46 | 47 | $(call zinc,src/bin/xmoji/xmoji.mk) 48 | -------------------------------------------------------------------------------- /src/bin/xmoji/x11app.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_X11APP_H 2 | #define XMOJI_X11APP_H 3 | 4 | #include "object.h" 5 | 6 | #include 7 | 8 | C_CLASS_DECL(X11Error); 9 | C_CLASS_DECL(PSC_Event); 10 | C_CLASS_DECL(Widget); 11 | C_CLASS_DECL(Window); 12 | C_CLASS_DECL(X11App); 13 | 14 | typedef struct MetaX11App 15 | { 16 | MetaObject base; 17 | int (*prestartup)(void *app); 18 | int (*startup)(void *app); 19 | void (*shutdown)(void *app); 20 | } MetaX11App; 21 | 22 | #define MetaX11App_init(mprestartup, mstartup, mshutdown, ...) { \ 23 | .base = MetaObject_init(__VA_ARGS__), \ 24 | .prestartup = mprestartup, \ 25 | .startup = mstartup, \ 26 | .shutdown = mshutdown, \ 27 | } 28 | 29 | X11App *app(void); 30 | 31 | X11App *X11App_createBase(void *derived, int argc, char **argv); 32 | #define X11App_create(...) X11App_createBase(0, __VA_ARGS__) 33 | int X11App_run(void); 34 | void X11App_quit(void); 35 | PSC_Event *X11App_error(void); 36 | 37 | Window *X11Error_window(X11Error *self) CMETHOD; 38 | Widget *X11Error_widget(X11Error *self) CMETHOD; 39 | uint8_t X11Error_code(X11Error *self) CMETHOD; 40 | uint8_t X11Error_opMajor(X11Error *self) CMETHOD; 41 | uint16_t X11Error_opMinor(X11Error *self) CMETHOD; 42 | void X11Error_ignore(X11Error *self) CMETHOD; 43 | 44 | void X11App_showWaitCursor(void); 45 | 46 | const char *X11App_name(void); 47 | const char *X11App_lcMessages(void); 48 | const char *X11App_hostname(void); 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /src/bin/xmoji/emoji.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_EMOJI_H 2 | #define XMOJI_EMOJI_H 3 | 4 | #include 5 | #include 6 | 7 | C_CLASS_DECL(Emoji); 8 | C_CLASS_DECL(EmojiGroup); 9 | C_CLASS_DECL(Translator); 10 | C_CLASS_DECL(UniStr); 11 | 12 | typedef enum EmojiSearchMode 13 | { 14 | ESM_NONE = 0, 15 | ESM_ORIG = 1 << 0, // search in original (english) names 16 | ESM_TRANS = 1 << 1, // search in translated names (current locale) 17 | ESM_FULL = 1 << 2 // search in text after colon 18 | } EmojiSearchMode; 19 | 20 | size_t EmojiGroup_numGroups(void) ATTR_CONST; 21 | const EmojiGroup *EmojiGroup_at(size_t index) ATTR_PURE; 22 | unsigned EmojiGroup_name(const EmojiGroup *self) CMETHOD ATTR_PURE; 23 | size_t EmojiGroup_len(const EmojiGroup *self) CMETHOD ATTR_PURE; 24 | const Emoji *EmojiGroup_emojiAt(const EmojiGroup *self, size_t index) 25 | CMETHOD ATTR_PURE; 26 | 27 | size_t Emoji_numEmojis(void) ATTR_CONST; 28 | const Emoji *Emoji_at(size_t index) ATTR_PURE; 29 | const EmojiGroup *Emoji_group(const Emoji *self) CMETHOD ATTR_PURE; 30 | const UniStr *Emoji_str(const Emoji *self) CMETHOD ATTR_PURE; 31 | unsigned Emoji_name(const Emoji *self) CMETHOD ATTR_PURE; 32 | unsigned Emoji_variants(const Emoji *self) CMETHOD ATTR_PURE; 33 | size_t Emoji_search(const Emoji **results, size_t resultsz, size_t maxresults, 34 | const UniStr *pattern, const Translator *tr, EmojiSearchMode mode) 35 | ATTR_NONNULL((1)) ATTR_NONNULL((4)) ATTR_NONNULL((5)); 36 | 37 | const void *XME_get(unsigned id); 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /tools/emojigen/src/groupnames.c: -------------------------------------------------------------------------------- 1 | #include "groupnames.h" 2 | 3 | #include "emojireader.h" 4 | #include "util.h" 5 | 6 | #include 7 | #include 8 | 9 | int dogroupnames(int argc, char **argv) 10 | { 11 | if (argc != 5) usage(argv[0]); 12 | int rc = EXIT_FAILURE; 13 | FILE *in = 0; 14 | FILE *out = 0; 15 | if (readEmojis(argv[4]) < 0) 16 | { 17 | fprintf(stderr, "Cannot read emojis from `%s'\n", argv[4]); 18 | goto done; 19 | } 20 | in = fopen(argv[3], "r"); 21 | if (!in) 22 | { 23 | fprintf(stderr, "Cannot open `%s' for reading\n", argv[3]); 24 | goto done; 25 | } 26 | out = fopen(argv[2], "w"); 27 | if (!out) 28 | { 29 | fprintf(stderr, "Cannot open `%s' for writing\n", argv[2]); 30 | goto done; 31 | } 32 | 33 | size_t groupsize = EmojiGroup_count(); 34 | for (size_t i = 0; i < groupsize; ++i) 35 | { 36 | const EmojiGroup *group = EmojiGroup_at(i); 37 | fprintf(out, "$w$emojiGroup%zu\n.\n%s\n.\n\n", i, group->name); 38 | } 39 | static char buf[8192]; 40 | size_t chunksz = 0; 41 | while ((chunksz = fread(buf, 1, sizeof buf, in))) 42 | { 43 | if (fwrite(buf, 1, chunksz, out) != chunksz) 44 | { 45 | fprintf(stderr, "Error writing to `%s'\n", argv[2]); 46 | goto done; 47 | } 48 | if (chunksz < sizeof buf) break; 49 | } 50 | if (ferror(in) || !feof(in)) 51 | { 52 | fprintf(stderr, "Error reading from `%s'\n", argv[3]); 53 | goto done; 54 | } 55 | rc = EXIT_SUCCESS; 56 | 57 | done: 58 | if (out) fclose(out); 59 | if (in) fclose(in); 60 | emojisDone(); 61 | return rc; 62 | } 63 | 64 | -------------------------------------------------------------------------------- /src/bin/xmoji/textrenderer.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_TEXTRENDERER_H 2 | #define XMOJI_TEXTRENDERER_H 3 | 4 | #include "valuetypes.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | C_CLASS_DECL(Font); 11 | C_CLASS_DECL(TextRenderer); 12 | C_CLASS_DECL(UniStr); 13 | C_CLASS_DECL(Widget); 14 | 15 | TextRenderer *TextRenderer_create(Widget *owner); 16 | Size TextRenderer_size(const TextRenderer *self) 17 | CMETHOD; 18 | void TextRenderer_setNoLigatures(TextRenderer *self, int noLigatures) 19 | CMETHOD; 20 | void TextRenderer_setUnderline(TextRenderer *self, int px) 21 | CMETHOD; 22 | void TextRenderer_setFont(TextRenderer *self, Font *font); 23 | Font *TextRenderer_font(TextRenderer *self) 24 | CMETHOD; 25 | int TextRenderer_setText(TextRenderer *self, const UniStr *text) 26 | CMETHOD; 27 | unsigned TextRenderer_nglyphs(const TextRenderer *self) 28 | CMETHOD; 29 | uint32_t TextRenderer_glyphIdAt(const TextRenderer *self, unsigned index) 30 | CMETHOD; 31 | unsigned TextRenderer_glyphLen(const TextRenderer *self, unsigned index) 32 | CMETHOD; 33 | unsigned TextRenderer_pixelOffset(const TextRenderer *self, unsigned index) 34 | CMETHOD; 35 | unsigned TextRenderer_charIndex(const TextRenderer *self, unsigned pixelpos) 36 | CMETHOD; 37 | int TextRenderer_renderWithSelection(TextRenderer *self, 38 | xcb_render_picture_t picture, Color color, Pos pos, 39 | Selection selection, Color selectionColor) 40 | CMETHOD; 41 | int TextRenderer_render(TextRenderer *self, 42 | xcb_render_picture_t picture, Color color, Pos pos) 43 | CMETHOD; 44 | void TextRenderer_destroy(TextRenderer *self); 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /src/bin/xmoji/tooltip.c: -------------------------------------------------------------------------------- 1 | #include "tooltip.h" 2 | 3 | #include "textlabel.h" 4 | #include "window.h" 5 | 6 | #include 7 | 8 | struct Tooltip 9 | { 10 | TextLabel *label; 11 | Widget *parent; 12 | Window *window; 13 | PSC_Timer *timer; 14 | }; 15 | 16 | static void timeout(void *receiver, void *sender, void *args) 17 | { 18 | (void)sender; 19 | (void)args; 20 | 21 | Tooltip *self = receiver; 22 | if (self->window) 23 | { 24 | Window_showTooltip(self->window, self->label, self->parent); 25 | } 26 | } 27 | 28 | Tooltip *Tooltip_create(const UniStr *text, Widget *parent, unsigned delay) 29 | { 30 | Tooltip *self = PSC_malloc(sizeof *self); 31 | self->label = TextLabel_create(0, 0); 32 | self->parent = parent; 33 | self->window = 0; 34 | self->timer = PSC_Timer_create(); 35 | PSC_Timer_setMs(self->timer, delay ? delay : 2000); 36 | PSC_Event_register(PSC_Timer_expired(self->timer), self, timeout, 0); 37 | 38 | TextLabel_setText(self->label, text); 39 | TextLabel_setColor(self->label, COLOR_TOOLTIP); 40 | Widget_setBackground(self->label, 1, COLOR_BG_TOOLTIP); 41 | Widget_show(self->label); 42 | 43 | return self; 44 | } 45 | 46 | void Tooltip_activate(Tooltip *self, Window *window) 47 | { 48 | self->window = window; 49 | PSC_Timer_start(self->timer, 0); 50 | } 51 | 52 | void Tooltip_cancel(Tooltip *self) 53 | { 54 | self->window = 0; 55 | PSC_Timer_stop(self->timer); 56 | } 57 | 58 | void Tooltip_destroy(Tooltip *self) 59 | { 60 | if (!self) return; 61 | PSC_Timer_destroy(self->timer); 62 | Object_destroy(self->label); 63 | free(self); 64 | } 65 | 66 | -------------------------------------------------------------------------------- /tools/bin2cstr/src/bin2cstr.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200112L 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static const char hex[16] = "0123456789abcdef"; 9 | 10 | int main(int argc, char **argv) 11 | { 12 | int rc = 1; 13 | if (argc != 3) return rc; 14 | 15 | int outfd = -1; 16 | int infd = -1; 17 | unsigned char *data = 0; 18 | char *datastr = 0; 19 | 20 | outfd = open(argv[1], O_WRONLY|O_CREAT|O_TRUNC, 0664); 21 | if (outfd < 0) goto done; 22 | infd = open(argv[2], O_RDONLY); 23 | if (infd < 0) goto done; 24 | struct stat st; 25 | if (fstat(infd, &st) < 0) goto done; 26 | 27 | size_t datasz = st.st_size; 28 | data = malloc(datasz); 29 | if (!data) goto done; 30 | size_t strsz = 4 * st.st_size + 3; 31 | datastr = malloc(strsz); 32 | if (!datastr) goto done; 33 | 34 | size_t rdpos = 0; 35 | ssize_t rdchunk = 0; 36 | while ((rdchunk = read(infd, data+rdpos, datasz-rdpos)) > 0) 37 | { 38 | if ((rdpos += rdchunk) == datasz) break; 39 | } 40 | if (rdpos < datasz) goto done; 41 | 42 | char *op = datastr; 43 | *op++ = '"'; 44 | for (size_t i = 0; i < datasz; ++i) 45 | { 46 | *op++ = '\\'; 47 | *op++ = 'x'; 48 | *op++ = hex[data[i]>>4]; 49 | *op++ = hex[data[i]&15]; 50 | } 51 | *op++ = '"'; 52 | *op = '\n'; 53 | 54 | size_t wrpos = 0; 55 | ssize_t wrchunk = 0; 56 | while ((wrchunk = write(outfd, datastr+wrpos, strsz-wrpos)) > 0) 57 | { 58 | if ((wrpos += wrchunk) == strsz) break; 59 | } 60 | if (wrpos == strsz) rc = 0; 61 | 62 | done: 63 | free(datastr); 64 | free(data); 65 | if (infd >= 0) close(infd); 66 | if (outfd >= 0) close(outfd); 67 | return rc; 68 | } 69 | -------------------------------------------------------------------------------- /src/bin/xmoji/config.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_CONFIG_H 2 | #define XMOJI_CONFIG_H 3 | 4 | #include "emoji.h" 5 | #include "emojifont.h" 6 | #include "keyinjector.h" 7 | 8 | #define HISTSIZE 64 9 | 10 | C_CLASS_DECL(Config); 11 | C_CLASS_DECL(EmojiHistory); 12 | C_CLASS_DECL(PSC_Event); 13 | 14 | typedef struct ConfigChangedEventArgs 15 | { 16 | int external; 17 | } ConfigChangedEventArgs; 18 | 19 | Config *Config_create(const char *path); 20 | 21 | EmojiHistory *Config_history(Config *self) CMETHOD; 22 | 23 | int Config_singleInstance(const Config *self) CMETHOD; 24 | void Config_setSingleInstance(Config *self, int singleInstance) CMETHOD; 25 | PSC_Event *Config_singleInstanceChanged(Config *self) CMETHOD ATTR_RETNONNULL; 26 | 27 | EmojiFont Config_scale(const Config *self) CMETHOD; 28 | void Config_setScale(Config *self, EmojiFont scale) CMETHOD; 29 | PSC_Event *Config_scaleChanged(Config *self) CMETHOD ATTR_RETNONNULL; 30 | 31 | InjectorFlags Config_injectorFlags(const Config *self) CMETHOD; 32 | void Config_setInjectorFlags(Config *self, InjectorFlags flags) CMETHOD; 33 | PSC_Event *Config_injectorFlagsChanged(Config *self) CMETHOD ATTR_RETNONNULL; 34 | 35 | unsigned Config_waitBefore(const Config *self) CMETHOD; 36 | void Config_setWaitBefore(Config *self, unsigned ms) CMETHOD; 37 | PSC_Event *Config_waitBeforeChanged(Config *self) CMETHOD ATTR_RETNONNULL; 38 | 39 | unsigned Config_waitAfter(const Config *self) CMETHOD; 40 | void Config_setWaitAfter(Config *self, unsigned ms) CMETHOD; 41 | PSC_Event *Config_waitAfterChanged(Config *self) CMETHOD ATTR_RETNONNULL; 42 | 43 | EmojiSearchMode Config_emojiSearchMode(const Config *self) CMETHOD; 44 | void Config_setEmojiSearchMode(Config *self, EmojiSearchMode mode) CMETHOD; 45 | PSC_Event *Config_emojiSearchModeChanged(Config *self) CMETHOD ATTR_RETNONNULL; 46 | 47 | void Config_destroy(Config *self); 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /src/bin/xmoji/shape.c: -------------------------------------------------------------------------------- 1 | #include "shape.h" 2 | 3 | #include "x11adapter.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | struct Shape 10 | { 11 | ShapeRenderer renderer; 12 | void *data; 13 | size_t datasz; 14 | xcb_render_picture_t picture; 15 | unsigned refcnt; 16 | }; 17 | 18 | static PSC_List *shapes; 19 | 20 | Shape *Shape_create(ShapeRenderer renderer, size_t datasz, const void *data) 21 | { 22 | if (!shapes) shapes = PSC_List_create(); 23 | for (size_t i = 0; i < PSC_List_size(shapes); ++i) 24 | { 25 | Shape *shape = PSC_List_at(shapes, i); 26 | if (shape->renderer == renderer && shape->datasz == datasz 27 | && !memcmp(shape->data, data, datasz)) 28 | { 29 | ++shape->refcnt; 30 | return shape; 31 | } 32 | } 33 | Shape *self = PSC_malloc(sizeof *self); 34 | self->renderer = renderer; 35 | self->data = PSC_malloc(datasz); 36 | memcpy(self->data, data, datasz); 37 | self->datasz = datasz; 38 | self->picture = 0; 39 | self->refcnt = 1; 40 | PSC_List_append(shapes, self, 0); 41 | return self; 42 | } 43 | 44 | void Shape_render(Shape *self, void *obj, xcb_render_picture_t ownerpic) 45 | { 46 | if (self->picture) return; 47 | self->picture = self->renderer(obj, ownerpic, self->data); 48 | } 49 | 50 | xcb_render_picture_t Shape_picture(const Shape *self) 51 | { 52 | return self->picture; 53 | } 54 | 55 | void Shape_destroy(Shape *self) 56 | { 57 | if (!self) return; 58 | if (--self->refcnt) return; 59 | PSC_List_remove(shapes, self); 60 | if (!PSC_List_size(shapes)) 61 | { 62 | PSC_List_destroy(shapes); 63 | shapes = 0; 64 | } 65 | if (self->picture) 66 | { 67 | xcb_render_free_picture(X11Adapter_connection(), self->picture); 68 | } 69 | free(self->data); 70 | free(self); 71 | } 72 | 73 | -------------------------------------------------------------------------------- /ftbundle/GNUmakefile: -------------------------------------------------------------------------------- 1 | FT_CONFIGFLAGS= --disable-shared \ 2 | --enable-static \ 3 | --prefix= \ 4 | --with-brotli=no \ 5 | --with-bzip2=no \ 6 | --with-harfbuzz=no \ 7 | --with-librsvg=no \ 8 | --with-png=yes \ 9 | --with-zlib=yes 10 | FT_DESTDIR= $(CURDIR)/root 11 | 12 | ifneq ($(filter all,$(or $(MAKECMDGOALS),all)),) 13 | DISTFILES= $(wildcard freetype-2.*.?z) 14 | ifeq ($(strip $(DISTFILES)),) 15 | define MISSING_DISTFILE 16 | No freetype2 distfile found! 17 | 18 | To build Xmoji with bundled freetype, please fetch a distfile 19 | (.tar.gz or .tar.xz) from https://freetype.org/download.html 20 | and place it in ftbundle/ in Xmoji's source tree. 21 | 22 | endef 23 | $(info $(MISSING_DISTFILE)) 24 | $(error Cannot build with bundled freetype) 25 | endif 26 | ifneq ($(words $(DISTFILES)),1) 27 | define MULTIPLE_DISTFILES 28 | More than one freetype2 distfile found! 29 | 30 | Please delete all freetype distfiles (.tar.gz and .tar.xz) except 31 | for the one you use from ftbundle/ in Xmoji's source tree. 32 | 33 | endef 34 | $(info $(MULTIPLE_DISTFILES)) 35 | $(error Cannot build with bundled freetype) 36 | endif 37 | FT_SRCDIR= $(basename $(basename $(DISTFILES))) 38 | FT_BUILDSTAMP= .$(FT_SRCDIR).build 39 | 40 | EXTRACT_.gz= gzcat 41 | EXTRACT_.xz= xzcat 42 | EXTRACT_CMD= $(EXTRACT_$(suffix $(DISTFILES))) $(DISTFILES) | tar xf - 43 | 44 | all: $(FT_BUILDSTAMP) 45 | endif 46 | 47 | wipedirs: 48 | @find . -type d -name freetype-2.\* -exec rm -fr \{\} \+ 49 | 50 | $(FT_SRCDIR)/configure: wipedirs 51 | $(EXTRACT_CMD) 52 | 53 | $(FT_BUILDSTAMP): $(FT_SRCDIR)/configure 54 | cd $(FT_SRCDIR) \ 55 | && ./configure $(FT_CONFIGFLAGS) && $(MAKE) \ 56 | && $(MAKE) DESTDIR=$(FT_DESTDIR) install 57 | rm -f .freetype-2.*.build 58 | touch $(FT_BUILDSTAMP) 59 | 60 | clean: wipedirs 61 | @rm -fr $(FT_DESTDIR) .freetype-2.*.build 62 | 63 | .PHONY: all wipedirs clean 64 | -------------------------------------------------------------------------------- /src/bin/xmoji/imagelabel.c: -------------------------------------------------------------------------------- 1 | #include "imagelabel.h" 2 | 3 | #include "pixmap.h" 4 | 5 | #include 6 | #include 7 | 8 | static void destroy(void *obj); 9 | static int draw(void *obj, xcb_render_picture_t picture); 10 | static Size minSize(const void *obj); 11 | 12 | static MetaImageLabel mo = MetaImageLabel_init( 13 | 0, draw, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, minSize, 0, 0, 0, 14 | "ImageLabel", destroy); 15 | 16 | struct ImageLabel 17 | { 18 | Object base; 19 | Pixmap *pixmap; 20 | Size minSize; 21 | }; 22 | 23 | static void destroy(void *obj) 24 | { 25 | ImageLabel *self = obj; 26 | Pixmap_destroy(self->pixmap); 27 | free(self); 28 | } 29 | 30 | static int draw(void *obj, xcb_render_picture_t picture) 31 | { 32 | ImageLabel *self = Object_instance(obj); 33 | if (!picture || !self->pixmap) return 0; 34 | Pos pos = Widget_contentOrigin(self, self->minSize); 35 | Pixmap_render(self->pixmap, picture, pos); 36 | return 0; 37 | } 38 | 39 | static Size minSize(const void *obj) 40 | { 41 | const ImageLabel *self = Object_instance(obj); 42 | return self->minSize; 43 | } 44 | 45 | ImageLabel *ImageLabel_createBase(void *derived, const char *name, void *parent) 46 | { 47 | ImageLabel *self = PSC_malloc(sizeof *self); 48 | CREATEBASE(Widget, name, parent); 49 | self->pixmap = 0; 50 | self->minSize = (Size){0, 0}; 51 | 52 | return self; 53 | } 54 | 55 | Pixmap *ImageLabel_pixmap(const void *self) 56 | { 57 | const ImageLabel *l = Object_instance(self); 58 | return l->pixmap; 59 | } 60 | 61 | void ImageLabel_setPixmap(void *self, Pixmap *pixmap) 62 | { 63 | ImageLabel *l = Object_instance(self); 64 | Pixmap_destroy(l->pixmap); 65 | l->pixmap = Pixmap_ref(pixmap); 66 | Size oldSize = l->minSize; 67 | l->minSize = Pixmap_size(pixmap); 68 | if (oldSize.width != l->minSize.width 69 | || oldSize.height != l->minSize.height) 70 | { 71 | Widget_requestSize(l); 72 | } 73 | } 74 | 75 | -------------------------------------------------------------------------------- /tools/xtc/src/util.c: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | void *xmalloc(size_t sz) 8 | { 9 | void *p = malloc(sz); 10 | if (!p) abort(); 11 | return p; 12 | } 13 | 14 | void *xrealloc(void *p, size_t sz) 15 | { 16 | p = realloc(p, sz); 17 | if (!p) abort(); 18 | return p; 19 | } 20 | 21 | void usage(const char *name) 22 | { 23 | fprintf(stderr, "usage: %s source outname namespace strings.def\n" 24 | " %s update lang strings.def\n" 25 | " %s compile outdir lang strings.def\n", 26 | name, name, name); 27 | exit(EXIT_FAILURE); 28 | } 29 | 30 | char *derivename(const char *filename, const char *lang, 31 | const char *dir, const char *ext) 32 | { 33 | char *slash = strrchr(filename, '/'); 34 | size_t dirlen = 0; 35 | if (dir) dirlen = strlen(dir); 36 | if (slash) 37 | { 38 | if (!dir) 39 | { 40 | dirlen = (size_t)(slash - filename); 41 | dir = filename; 42 | } 43 | filename = slash+1; 44 | } 45 | char *dot = strrchr(filename, '.'); 46 | size_t extlen = 0; 47 | size_t nmlen = 0; 48 | if (ext) extlen = strlen(ext); 49 | if (dot) 50 | { 51 | if (!ext) 52 | { 53 | extlen = strlen(dot); 54 | ext = dot; 55 | } 56 | nmlen = (size_t)(dot - filename); 57 | } else nmlen = strlen(filename); 58 | size_t langlen = strlen(lang); 59 | size_t reslen = dirlen + !!dirlen + nmlen + 1 + langlen + extlen; 60 | char *res = xmalloc(reslen+1); 61 | size_t respos = 0; 62 | if (dirlen) 63 | { 64 | memcpy(res, dir, dirlen); 65 | res[dirlen] = '/'; 66 | respos += dirlen + 1; 67 | } 68 | memcpy(res+respos, filename, nmlen); 69 | res[respos+nmlen] = '-'; 70 | respos += nmlen + 1; 71 | memcpy(res+respos, lang, langlen); 72 | respos += langlen; 73 | if (extlen) 74 | { 75 | memcpy(res+respos, ext, extlen); 76 | respos += extlen; 77 | } 78 | res[respos] = 0; 79 | return res; 80 | } 81 | -------------------------------------------------------------------------------- /tools/emojigen/src/sourcegen.c: -------------------------------------------------------------------------------- 1 | #include "sourcegen.h" 2 | 3 | #include "emojireader.h" 4 | #include "util.h" 5 | 6 | #include 7 | #include 8 | 9 | int dosource(int argc, char **argv) 10 | { 11 | if (argc != 4) usage(argv[0]); 12 | int rc = EXIT_FAILURE; 13 | FILE *out = 0; 14 | if (readEmojis(argv[3]) < 0) 15 | { 16 | fprintf(stderr, "Cannot read emojis from `%s'\n", argv[3]); 17 | goto done; 18 | } 19 | out = fopen(argv[2], "w"); 20 | if (!out) 21 | { 22 | fprintf(stderr, "Cannot open `%s' for writing\n", argv[2]); 23 | goto done; 24 | } 25 | 26 | size_t emojisize = Emoji_count(); 27 | size_t groupsize = EmojiGroup_count(); 28 | 29 | fprintf(out, "static const EmojiGroup groups[%zu];\n" 30 | "static const Emoji emojis[] = {", groupsize); 31 | 32 | for (size_t i = 0; i < emojisize; ++i) 33 | { 34 | if (i) fputc(',', out); 35 | const Emoji *emoji = Emoji_at(i); 36 | fprintf(out, "\n { groups + %zu, { .len = %zu, .str = U\"", 37 | emoji->groupno, emoji->len); 38 | for (size_t j = 0; j < emoji->len; ++j) 39 | { 40 | fprintf(out, "\\x%x", emoji->codepoints[j]); 41 | } 42 | fprintf(out, "\", .refcnt = -1 }, %zu, %u }", i, emoji->variants); 43 | } 44 | fputs("\n};\n" 45 | "static const EmojiGroup groups[] = {", out); 46 | for (size_t i = 0; i < groupsize; ++i) 47 | { 48 | if (i) fputc(',', out); 49 | const EmojiGroup *group = EmojiGroup_at(i); 50 | fprintf(out, "\n { emojis + %zu, %zu, %zu}", 51 | group->start, group->len, i); 52 | } 53 | fputs("\n};\n" 54 | "static const UniStr XME_texts[] = {", out); 55 | 56 | for (size_t i = 0; i < emojisize; ++i) 57 | { 58 | if (i) fputc(',', out); 59 | const Emoji *emoji = Emoji_at(i); 60 | fprintf(out, "\n { .len = %zu, .str = U\"%s\", .refcnt = -1 }", 61 | emoji->namelen, emoji->name); 62 | } 63 | fputs("\n};\n", out); 64 | 65 | rc = EXIT_SUCCESS; 66 | done: 67 | if (out) fclose(out); 68 | emojisDone(); 69 | return rc; 70 | } 71 | 72 | -------------------------------------------------------------------------------- /src/bin/xmoji/font.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_FONT_H 2 | #define XMOJI_FONT_H 3 | 4 | #include 5 | #include FT_FREETYPE_H 6 | #include 7 | #include 8 | #include 9 | 10 | C_CLASS_DECL(Font); 11 | 12 | typedef struct GlyphRenderInfo 13 | { 14 | uint8_t count; 15 | uint8_t pad[3]; 16 | int16_t dx; 17 | int16_t dy; 18 | uint32_t glyphid; 19 | } GlyphRenderInfo; 20 | 21 | typedef enum FontGlyphType 22 | { 23 | FGT_OUTLINE, 24 | FGT_BITMAP_GRAY, 25 | FGT_BITMAP_BGRA 26 | } FontGlyphType; 27 | 28 | typedef struct FontOptions 29 | { 30 | const char *classname; 31 | float maxUnscaledDeviation; 32 | uint8_t pixelFractionBits; 33 | } FontOptions; 34 | 35 | typedef enum FontStyle 36 | { 37 | FS_NONE = 0, 38 | FS_BOLD = 1 << 0, 39 | FS_ITALIC = 1 << 1 40 | } FontStyle; 41 | 42 | Font *Font_create(const char *pattern, const FontOptions *options); 43 | Font *Font_createVariant(Font *font, double pixelsize, FontStyle style, 44 | const FontOptions *options); 45 | Font *Font_ref(Font *font); 46 | FT_Face Font_face(const Font *self) CMETHOD ATTR_RETNONNULL; 47 | FontGlyphType Font_glyphtype(const Font *self) CMETHOD; 48 | double Font_pixelsize(const Font *self) CMETHOD; 49 | double Font_fixedpixelsize(const Font *self) CMETHOD; 50 | uint8_t Font_glyphidbits(const Font *self) CMETHOD; 51 | uint8_t Font_subpixelbits(const Font *self) CMETHOD; 52 | uint16_t Font_linespace(const Font *self) CMETHOD; 53 | uint32_t Font_maxWidth(const Font *self) CMETHOD; 54 | uint32_t Font_maxHeight(const Font *self) CMETHOD; 55 | uint32_t Font_baseline(const Font *self) CMETHOD; 56 | uint32_t Font_scale(const Font *self, uint32_t val) CMETHOD; 57 | int32_t Font_ftLoadFlags(const Font *self) CMETHOD; 58 | int Font_uploadGlyphs(Font *self, uint32_t ownerid, 59 | unsigned len, GlyphRenderInfo *glyphinfo) 60 | CMETHOD ATTR_NONNULL((4)); 61 | xcb_render_glyphset_t Font_glyphset(const Font *self) CMETHOD; 62 | xcb_render_glyphset_t Font_maskGlyphset(const Font *self) CMETHOD; 63 | void Font_destroy(Font *self); 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /src/bin/xmoji/hyperlink.c: -------------------------------------------------------------------------------- 1 | #include "hyperlink.h" 2 | 3 | #include "unistr.h" 4 | #include "xdgopen.h" 5 | 6 | #include 7 | #include 8 | 9 | static void destroy(void *obj); 10 | static void enter(void *obj); 11 | static void leave(void *obj); 12 | static int clicked(void *obj, const ClickEvent *event); 13 | 14 | static MetaHyperLink mo = MetaHyperLink_init( 15 | 0, 0, 0, 0, 0, 0, enter, leave, 0, 0, 0, 0, 0, 16 | 0, 0, 0, clicked, 0, 17 | "HyperLink", destroy); 18 | 19 | struct HyperLink 20 | { 21 | Object base; 22 | char *link; 23 | }; 24 | 25 | static void destroy(void *obj) 26 | { 27 | HyperLink *self = obj; 28 | free(self->link); 29 | free(self); 30 | } 31 | 32 | static void enter(void *obj) 33 | { 34 | TextLabel_setColor(obj, COLOR_HOVER); 35 | } 36 | 37 | static void leave(void *obj) 38 | { 39 | TextLabel_setColor(obj, COLOR_LINK); 40 | } 41 | 42 | static int clicked(void *obj, const ClickEvent *event) 43 | { 44 | if (event->button != MB_LEFT) return 0; 45 | HyperLink *self = Object_instance(obj); 46 | if (!self->link) return 0; 47 | xdgOpen(self->link, 0, 0); 48 | return 1; 49 | } 50 | 51 | HyperLink *HyperLink_createBase(void *derived, const char *name, void *parent) 52 | { 53 | HyperLink *self = PSC_malloc(sizeof *self); 54 | CREATEBASE(TextLabel, name, parent); 55 | self->link = 0; 56 | TextLabel_setColor(self, COLOR_LINK); 57 | TextLabel_setUnderline(self, 2); 58 | Widget_setCursor(self, XC_HAND); 59 | return self; 60 | } 61 | 62 | const char *HyperLink_link(const void *self) 63 | { 64 | const HyperLink *l = Object_instance(self); 65 | return l->link; 66 | } 67 | 68 | void HyperLink_setLink(void *self, const char *link) 69 | { 70 | HyperLink *l = Object_instance(self); 71 | free(l->link); 72 | if (link) 73 | { 74 | l->link = PSC_copystr(link); 75 | UniStr *tt = UniStr_create(link); 76 | Widget_setTooltip(l, tt, 0); 77 | UniStr_destroy(tt); 78 | } 79 | else 80 | { 81 | l->link = 0; 82 | Widget_setTooltip(l, 0, 0); 83 | } 84 | } 85 | 86 | -------------------------------------------------------------------------------- /tools/xtc/src/compiler.c: -------------------------------------------------------------------------------- 1 | #include "compiler.h" 2 | 3 | #include "deffile.h" 4 | #include "util.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #define MAGIC "XCT" 11 | #define VERSION 0 12 | 13 | static void write32le(FILE *out, unsigned val) 14 | { 15 | unsigned char data[4] = { 16 | val & 0xff, 17 | (val >> 8) & 0xff, 18 | (val >> 16) & 0xff, 19 | (val >> 24) & 0xff 20 | }; 21 | fwrite(data, sizeof data, 1, out); 22 | } 23 | 24 | int docompile(int argc, char **argv) 25 | { 26 | if (argc != 5) usage(argv[0]); 27 | int rc = EXIT_FAILURE; 28 | const char *lang = argv[3]; 29 | const char *defname = argv[4]; 30 | char *langname = derivename(defname, lang, 0, 0); 31 | char *xctname = derivename(defname, lang, argv[2], ".xct"); 32 | DefFile *df = 0; 33 | DefFile *ldf = 0; 34 | FILE *out = 0; 35 | 36 | df = DefFile_create(defname); 37 | if (!df) 38 | { 39 | fprintf(stderr, "Error reading `%s'\n", defname); 40 | goto done; 41 | } 42 | ldf = DefFile_create(langname); 43 | if (!ldf) 44 | { 45 | fprintf(stderr, "Error reading `%s'\n", langname); 46 | goto done; 47 | } 48 | out = fopen(xctname, "wb"); 49 | if (!out) 50 | { 51 | fprintf(stderr, "Error opening `%s' for writing\n", xctname); 52 | goto done; 53 | } 54 | 55 | fprintf(out, MAGIC "%c", VERSION); 56 | unsigned len = DefFile_len(df); 57 | write32le(out, len); 58 | for (unsigned i = 0; i < len; ++i) 59 | { 60 | const DefEntry *entry = DefFile_byId(df, i); 61 | const DefEntry *lentry = DefFile_byKey(ldf, DefEntry_key(entry)); 62 | const char *str = 0; 63 | if (lentry) str = DefEntry_to(lentry); 64 | if (str) 65 | { 66 | fputc(DefEntry_type(entry), out); 67 | unsigned slen = (unsigned)strlen(str); 68 | write32le(out, slen); 69 | fwrite(str, 1, slen, out); 70 | } 71 | else fputc(-1, out); 72 | } 73 | if (ferror(out)) 74 | { 75 | fprintf(stderr, "Error writing to `%s'\n", xctname); 76 | } 77 | else rc = EXIT_SUCCESS; 78 | 79 | done: 80 | if (out) fclose(out); 81 | DefFile_destroy(ldf); 82 | DefFile_destroy(df); 83 | return rc; 84 | } 85 | -------------------------------------------------------------------------------- /src/bin/xmoji/icon.c: -------------------------------------------------------------------------------- 1 | #include "icon.h" 2 | 3 | #include "pixmap.h" 4 | #include "window.h" 5 | #include "x11adapter.h" 6 | 7 | #include 8 | #include 9 | 10 | struct Icon 11 | { 12 | PSC_List *pixmaps; 13 | unsigned refcnt; 14 | }; 15 | 16 | static void destroyPixmap(void *obj) 17 | { 18 | Pixmap_destroy(obj); 19 | } 20 | 21 | Icon *Icon_create(void) 22 | { 23 | Icon *self = PSC_malloc(sizeof *self); 24 | self->pixmaps = PSC_List_create(); 25 | self->refcnt = 1; 26 | return self; 27 | } 28 | 29 | Icon *Icon_ref(Icon *icon) 30 | { 31 | ++icon->refcnt; 32 | return icon; 33 | } 34 | 35 | void Icon_add(Icon *self, Pixmap *pixmap) 36 | { 37 | PSC_List_append(self->pixmaps, Pixmap_ref(pixmap), destroyPixmap); 38 | } 39 | 40 | void Icon_apply(Icon *self, Window *window) 41 | { 42 | size_t n = PSC_List_size(self->pixmaps); 43 | xcb_window_t w = Window_id(window); 44 | xcb_connection_t *c = X11Adapter_connection(); 45 | if (!n) 46 | { 47 | CHECK(xcb_delete_property(c, w, A(_NET_WM_ICON)), 48 | "Cannot delete icon from 0x%x", (unsigned)w); 49 | return; 50 | } 51 | size_t propsz = 2 * n * sizeof(uint32_t); 52 | PSC_ListIterator *i = PSC_List_iterator(self->pixmaps); 53 | size_t bitmapsz; 54 | const unsigned char *bitmap; 55 | while (PSC_ListIterator_moveNext(i)) 56 | { 57 | Pixmap *p = PSC_ListIterator_current(i); 58 | bitmap = Pixmap_bitmap(p, &bitmapsz); 59 | propsz += bitmapsz; 60 | } 61 | uint32_t *propval = PSC_malloc(propsz); 62 | uint32_t *propp = propval; 63 | while (PSC_ListIterator_moveNext(i)) 64 | { 65 | Pixmap *p = PSC_ListIterator_current(i); 66 | Size dim = Pixmap_size(p); 67 | *propp++ = dim.width; 68 | *propp++ = dim.height; 69 | bitmap = Pixmap_bitmap(p, &bitmapsz); 70 | memcpy(propp, bitmap, bitmapsz); 71 | propp += bitmapsz >> 2; 72 | } 73 | PSC_ListIterator_destroy(i); 74 | CHECK(xcb_change_property(c, XCB_PROP_MODE_REPLACE, w, A(_NET_WM_ICON), 75 | XCB_ATOM_CARDINAL, 32, propsz >> 2, propval), 76 | "Cannot set icon on 0x%x", (unsigned)w); 77 | free(propval); 78 | } 79 | 80 | void Icon_destroy(Icon *self) 81 | { 82 | if (!self) return; 83 | if (--self->refcnt) return; 84 | PSC_List_destroy(self->pixmaps); 85 | free(self); 86 | } 87 | 88 | -------------------------------------------------------------------------------- /src/bin/xmoji/table.c: -------------------------------------------------------------------------------- 1 | #include "table.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | static void destroy(void *obj); 8 | static void layout(void *vbox, int updateMinSize); 9 | 10 | static MetaTable mo = MetaTable_init( 11 | layout, 12 | 0, 0, 0, 0, 13 | 0, 0, 0, 0, 0, 0, 0, 14 | 0, 0, 0, 0, 0, 0, 0, 15 | "Table", destroy); 16 | 17 | struct Table 18 | { 19 | Object base; 20 | unsigned cols; 21 | uint16_t *minWidth; 22 | }; 23 | 24 | static void destroy(void *obj) 25 | { 26 | Table *self = obj; 27 | free(self->minWidth); 28 | free(self); 29 | } 30 | 31 | static void layout(void *vbox, int updateMinSize) 32 | { 33 | if (!updateMinSize) return; 34 | Table *self = Object_instance(vbox); 35 | 36 | unsigned rows = VBox_rows(self); 37 | if (!rows) return; 38 | 39 | unsigned cols = 0; 40 | uint16_t *minWidth = 0; 41 | 42 | for (unsigned r = 0; r < rows; ++r) 43 | { 44 | void *widget = VBox_widget(self, r); 45 | TableRow *row = TableRow_tryCast(widget); 46 | if (!row) continue; 47 | unsigned rcols = HBox_cols(row); 48 | if (!rcols) continue; 49 | if (rcols > cols) 50 | { 51 | minWidth = PSC_realloc(minWidth, rcols * sizeof *minWidth); 52 | memset(minWidth + cols, 0, (rcols - cols) * sizeof *minWidth); 53 | cols = rcols; 54 | } 55 | for (unsigned c = 0; c < rcols; ++c) 56 | { 57 | uint16_t mw = HBox_minWidth(row, c); 58 | if (mw > minWidth[c]) minWidth[c] = mw; 59 | } 60 | } 61 | if (!cols) return; 62 | if (cols == self->cols && 63 | !memcmp(minWidth, self->minWidth, cols * sizeof *minWidth)) 64 | { 65 | free(minWidth); 66 | return; 67 | } 68 | self->cols = cols; 69 | free(self->minWidth); 70 | self->minWidth = minWidth; 71 | 72 | for (unsigned r = 0; r < rows; ++r) 73 | { 74 | void *widget = VBox_widget(self, r); 75 | TableRow *row = TableRow_tryCast(widget); 76 | if (!row) continue; 77 | for (unsigned c = 0; c < cols; ++c) 78 | { 79 | HBox_setMinWidth(row, c, minWidth[c]); 80 | } 81 | } 82 | } 83 | 84 | Table *Table_createBase(void *derived, void *parent) 85 | { 86 | Table *self = PSC_malloc(sizeof *self); 87 | CREATEBASE(VBox, parent); 88 | self->cols = 0; 89 | self->minWidth = 0; 90 | return self; 91 | } 92 | 93 | void Table_addRow(void *self, TableRow *row) 94 | { 95 | VBox_addWidget(self, row); 96 | } 97 | 98 | -------------------------------------------------------------------------------- /TRANSLATE.md: -------------------------------------------------------------------------------- 1 | # How to add a new translation 2 | 3 | This will guide you through the process of adding a translation for the 4 | hypothetical language "Xylophone" with language-code `xy`. 5 | 6 | ### Preparations 7 | 8 | Any changes should be done on top of the current `master` branch. 9 | 10 | The recommended workflow is to create a fork of `Xmoji` on github and work on 11 | a dedicated branch in your fork. For example, after forking to your account 12 | `__JohnDoe__`, you'd do the following: 13 | 14 | git clone --recurse-submodules https://github.com/__JohnDoe__/xmoji.git 15 | cd xmoji 16 | git checkout -b translate-xy 17 | 18 | In case you don't want to use github, you can also just clone the original 19 | repository and create your local branch (same as above with `Zirias` instead 20 | of `__JohnDoe__`). 21 | 22 | ### Obtain translated emoji names 23 | 24 | First navigate to the latest release (at the time of writing `release-45`) 25 | of the [Unicode CLDR](https://github.com/unicode-org/cldr). From the tree 26 | (there's a link on the release page), fetch the `xy.xml` files from 27 | `common/annotations` and `common/annotationsDerived` and place them in the 28 | respective directories in `src/bin/xmoji/contrib/cldr`. 29 | 30 | ### Add the new language 31 | 32 | In `src/bin/xmoji/xmoji.mk`, add `xy` to the `xmoji_LANGUAGES` variable. 33 | Please keep the language codes sorted alphabetically. 34 | 35 | Then run 36 | 37 | make update-translations 38 | 39 | This will generate a new file `src/bin/xmoji/translations/xmoji-ui-xy.def`. 40 | 41 | ### Translate the texts 42 | 43 | Edit the file `src/bin/xmoji/translations/xmoji-ui-xy.def` with your 44 | preferred text editor. The entries have the following format: 45 | 46 | $[cw]$ 47 | 48 | . 49 | 50 | . 51 | 52 | The `` part will be missing in newly generated entries or 53 | when the original text changed since running `make update-translations` the 54 | last time. You should only edit this part. Never change the ``, 55 | it will be used to determine whether a text changed. 56 | 57 | Edit `src/bin/xmoji/xmoji.desktop.in` and add `GenericName[xy]` and 58 | `Comment[xy]` entries. Please keep the language codes here sorted 59 | alphabetically as well. 60 | 61 | **IMPORTANT**: Both files you edit must use **UTF-8** text encoding. 62 | 63 | ### Finish 64 | 65 | Now, building and installing Xmoji should give you a version fully localized 66 | for "Xylophone". 67 | 68 | `git add` the new CLDR files, `src/bin/xmoji/translations/xmoji-ui-xy.def`, 69 | `src/bin/xmoji/xmoji.mk` and `src/bin/xmoji/xmoji.desktop.in`, create a 70 | commit, and please, send a pull request, thank you very much! 🤩 71 | 72 | If you didn't fork on github, you can also run `git format-patch -1` after 73 | committing and send me the resulting patch-file to 74 | [felix@palmen-it.de](mailto:felix@palmen-it.de). 75 | -------------------------------------------------------------------------------- /tools/xtc/src/updater.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200112L 2 | 3 | #include "updater.h" 4 | 5 | #include "deffile.h" 6 | #include "util.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | int doupdate(int argc, char **argv) 15 | { 16 | if (argc != 4) usage(argv[0]); 17 | int rc = EXIT_FAILURE; 18 | const char *lang = argv[2]; 19 | const char *defname = argv[3]; 20 | char *langname = derivename(defname, lang, 0, 0); 21 | DefFile *df = 0; 22 | DefFile *ldf = 0; 23 | FILE *out = 0; 24 | 25 | df = DefFile_create(defname); 26 | if (!df) 27 | { 28 | fprintf(stderr, "Error reading `%s'\n", defname); 29 | goto done; 30 | } 31 | 32 | struct stat st; 33 | errno = 0; 34 | if (stat(langname, &st) == 0) 35 | { 36 | ldf = DefFile_create(langname); 37 | if (!ldf) 38 | { 39 | fprintf(stderr, "Error reading `%s'\n" 40 | "Try manually fixing or deleting that file\n", langname); 41 | goto done; 42 | } 43 | } 44 | else if (errno != ENOENT) 45 | { 46 | fprintf(stderr, "Cannot stat `%s'\n" 47 | "Check permissions and filesystem consistency\n", langname); 48 | goto done; 49 | } 50 | 51 | unsigned deflen = DefFile_len(df); 52 | unsigned langlen = 0; 53 | if (ldf) langlen = DefFile_len(ldf); 54 | 55 | if (langlen == deflen) 56 | { 57 | int changed = 0; 58 | for (unsigned i = 0; i < deflen; ++i) 59 | { 60 | const DefEntry *entry = DefFile_byId(df, i); 61 | const DefEntry *lentry = DefFile_byKey(ldf, DefEntry_key(entry)); 62 | if (!lentry || strcmp(DefEntry_to(entry), DefEntry_from(lentry))) 63 | { 64 | changed = 1; 65 | break; 66 | } 67 | } 68 | if (!changed) 69 | { 70 | rc = EXIT_SUCCESS; 71 | goto done; 72 | } 73 | } 74 | 75 | out = fopen(langname, "w"); 76 | if (!out) 77 | { 78 | fprintf(stderr, "Error opening `%s' for writing\n", langname); 79 | goto done; 80 | } 81 | 82 | unsigned updated = 0; 83 | for (unsigned i = 0; i < deflen; ++i) 84 | { 85 | const DefEntry *entry = DefFile_byId(df, i); 86 | static const char typechar[] = { 'c', 'w' }; 87 | const char *key = DefEntry_key(entry); 88 | const char *from = DefEntry_to(entry); 89 | const char *to = 0; 90 | if (ldf) 91 | { 92 | const DefEntry *lentry = DefFile_byKey(ldf, key); 93 | if (lentry && !strcmp(from, DefEntry_from(lentry))) 94 | { 95 | to = DefEntry_to(lentry); 96 | } 97 | } 98 | if (to) 99 | { 100 | fprintf(out, "$%c$%s\n%s\n.\n%s\n.\n\n", 101 | typechar[DefEntry_type(entry)], key, from, to); 102 | } 103 | else 104 | { 105 | fprintf(out, "$%c$%s\n%s\n.\n.\n\n", 106 | typechar[DefEntry_type(entry)], key, from); 107 | ++updated; 108 | } 109 | } 110 | 111 | printf("Updated or added %u translations in `%s'\n" 112 | "Edit this file to complete the translation\n", updated, langname); 113 | rc = EXIT_SUCCESS; 114 | 115 | done: 116 | if (out) fclose(out); 117 | DefFile_destroy(ldf); 118 | DefFile_destroy(df); 119 | free(langname); 120 | return rc; 121 | } 122 | -------------------------------------------------------------------------------- /src/bin/xmoji/window.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_WINDOW_H 2 | #define XMOJI_WINDOW_H 3 | 4 | #include "valuetypes.h" 5 | #include "widget.h" 6 | 7 | #include 8 | #include 9 | 10 | typedef struct MetaWindow 11 | { 12 | MetaWidget base; 13 | } MetaWindow; 14 | 15 | #define MetaWindow_init(...) { \ 16 | .base = MetaWidget_init(__VA_ARGS__) \ 17 | } 18 | 19 | C_CLASS_DECL(UniStr); 20 | C_CLASS_DECL(Window); 21 | C_CLASS_DECL(XSelection); 22 | 23 | typedef enum WindowState 24 | { 25 | WS_NONE, 26 | WS_MINIMIZED, 27 | WS_NORMAL 28 | } WindowState; 29 | 30 | typedef enum WindowFlags 31 | { 32 | /* Immutable type of window */ 33 | WF_WINDOW_NORMAL = 0, 34 | WF_WINDOW_DIALOG = 1, 35 | WF_WINDOW_TOOLTIP = 2, 36 | WF_WINDOW_MENU = 3, 37 | 38 | WF_WINDOW_TYPE = 0xf, 39 | 40 | /* settable/clearable flags */ 41 | WF_REJECT_FOCUS = 0x10 << 0, 42 | WF_FIXED_SIZE = 0x10 << 1, 43 | WF_MODAL = 0x10 << 2, 44 | WF_SKIP_TASKBAR = 0x10 << 3, 45 | WF_SKIP_PAGER = 0x10 << 4, 46 | WF_STICKY = 0x10 << 5, 47 | WF_POS_PARENTWIDGET = 0x10 << 6, 48 | WF_POS_INCBORDER = 0x10 << 7, 49 | WF_ALWAYS_CLASS = 0x10 << 8 50 | } WindowFlags; 51 | 52 | Window *Window_createBase(void *derived, const char *name, 53 | WindowFlags flags, void *parent); 54 | #define Window_create(...) Window_createBase(0, __VA_ARGS__) 55 | 56 | Window *Window_fromWidget(void *widget) 57 | ATTR_NONNULL((1)); 58 | 59 | xcb_window_t Window_id(const void *self) 60 | CMETHOD ATTR_PURE; 61 | 62 | PSC_Event *Window_closed(void *self) 63 | CMETHOD ATTR_RETNONNULL; 64 | 65 | PSC_Event *Window_propertyChanged(void *self) 66 | CMETHOD ATTR_RETNONNULL; 67 | 68 | void Window_addFlags(void *self, WindowFlags flags) 69 | CMETHOD; 70 | void Window_removeFlags(void *self, WindowFlags flags) 71 | CMETHOD; 72 | 73 | const char *Window_title(const void *self) 74 | CMETHOD; 75 | void Window_setTitle(void *self, const char *title) 76 | CMETHOD; 77 | 78 | const char *Window_iconName(const void *self) 79 | CMETHOD; 80 | void Window_setIconName(void *self, const char *iconName) 81 | CMETHOD; 82 | 83 | void *Window_mainWidget(const void *self) 84 | CMETHOD; 85 | void Window_setMainWidget(void *self, void *widget) 86 | CMETHOD; 87 | 88 | void Window_setFocusWidget(void *self, void *widget) 89 | CMETHOD; 90 | 91 | xcb_atom_t Window_takeProperty(void *self) 92 | CMETHOD; 93 | void Window_returnProperty(void *self, xcb_atom_t property) 94 | CMETHOD; 95 | 96 | XSelection *Window_primary(void *self) 97 | CMETHOD; 98 | XSelection *Window_clipboard(void *self) 99 | CMETHOD; 100 | 101 | WindowState Window_state(const void *self) 102 | CMETHOD; 103 | void Window_close(void *self) 104 | CMETHOD; 105 | 106 | void Window_showTooltip(void *self, void *widget, void *parentWidget) 107 | CMETHOD ATTR_NONNULL((2)); 108 | 109 | void Window_invalidateHover(void *self) 110 | CMETHOD; 111 | 112 | void Window_showWaitCursor(void *self) 113 | CMETHOD; 114 | 115 | void Window_raise(void *self, int force) 116 | CMETHOD; 117 | 118 | void Window_expose(void *self) 119 | CMETHOD; 120 | 121 | #endif 122 | -------------------------------------------------------------------------------- /src/bin/xmoji/object.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_OBJECT_H 2 | #define XMOJI_OBJECT_H 3 | 4 | #include 5 | 6 | typedef struct MetaObject 7 | { 8 | uint32_t id; 9 | const char *name; 10 | void (*destroy)(void *obj); 11 | } MetaObject; 12 | 13 | #define MetaObject_init(mname, mdestroy) { \ 14 | .id = 0, \ 15 | .name = mname, \ 16 | .destroy = mdestroy \ 17 | } 18 | 19 | typedef struct Object 20 | { 21 | void *base; 22 | uint32_t type; 23 | } Object; 24 | 25 | uint32_t MetaObject_register(void *meta); 26 | const void *MetaObject_get(uint32_t id); 27 | 28 | Object *Object_createBase(void *derived); 29 | void *Object_ref(void *self); 30 | void Object_own(void *self, void *obj); 31 | void *Object_instanceOf(void *self, uint32_t type, int mustMatch); 32 | void *Object_mostDerived(void *self); 33 | const char *Object_className(void *self); 34 | void Object_destroy(void *self); 35 | 36 | #define priv_MO_basector0(derived, type) \ 37 | type ## _createBase (derived) 38 | #define priv_MO_basectorn(derived, type, ...) \ 39 | type ## _createBase (derived, __VA_ARGS__) 40 | #define priv_MO_pickctor(a,b,c,d,e,f,g,h,i,x,...) x 41 | #define priv_MO_basector(derived, ...) priv_MO_pickctor(__VA_ARGS__,\ 42 | priv_MO_basectorn,\ 43 | priv_MO_basectorn,\ 44 | priv_MO_basectorn,\ 45 | priv_MO_basectorn,\ 46 | priv_MO_basectorn,\ 47 | priv_MO_basectorn,\ 48 | priv_MO_basectorn,\ 49 | priv_MO_basectorn,\ 50 | priv_MO_basector0,)(derived, __VA_ARGS__) 51 | 52 | #define CREATEBASE(...) do { \ 53 | if (!derived) derived = self; \ 54 | self->base.type = MetaObject_register(&mo); \ 55 | self->base.base = 0; \ 56 | self->base.base = priv_MO_basector(derived, __VA_ARGS__); \ 57 | } while (0) 58 | 59 | #define CREATEFINALBASE(...) do { \ 60 | self->base.type = MetaObject_register(&mo); \ 61 | self->base.base = 0; \ 62 | self->base.base = priv_MO_basector(self, __VA_ARGS__); \ 63 | } while (0) 64 | 65 | #define priv_MO_id ((MetaObject *)&mo)->id 66 | #define Object_instance(o) Object_instanceOf((void *)(o), priv_MO_id, 1) 67 | #define Object_cast(o) Object_instanceOf((void *)(o), priv_MO_id, 0) 68 | 69 | #define priv_MO_docall(r, mo, m, ...) r = mo->m(__VA_ARGS__) 70 | #define priv_MO_docallv(r, mo, m, ...) mo->m(__VA_ARGS__) 71 | #define priv_MO_first(x, ...) (x) 72 | #define priv_MO_derived(...) \ 73 | Object_mostDerived((Object *)priv_MO_first(__VA_ARGS__,)) 74 | #define priv_MO_base(...) ((Object *)priv_MO_first(__VA_ARGS__,))->base 75 | #define priv_MO_vcall(b, c, r, t, m, ...) do { \ 76 | Object *mo_obj = b(__VA_ARGS__); \ 77 | while (mo_obj) { \ 78 | const Meta ## t *mo_meta = MetaObject_get(mo_obj->type); \ 79 | if (!mo_meta) break; \ 80 | if (mo_meta->m) { \ 81 | c(r, mo_meta, m, __VA_ARGS__); \ 82 | break; \ 83 | } \ 84 | if (mo_meta == (void *)&mo) break; \ 85 | mo_obj = mo_obj->base; \ 86 | } \ 87 | } while (0) 88 | 89 | #define Object_vcall(r, t, m, ...) \ 90 | priv_MO_vcall(priv_MO_derived, priv_MO_docall, r, t, m, __VA_ARGS__) 91 | #define Object_vcallv(t, m, ...) \ 92 | priv_MO_vcall(priv_MO_derived, priv_MO_docallv, 0, t, m, __VA_ARGS__) 93 | #define Object_bcall(r, t, m, ...) \ 94 | priv_MO_vcall(priv_MO_base, priv_MO_docall, r, t, m, __VA_ARGS__) 95 | #define Object_bcallv(t, m, ...) \ 96 | priv_MO_vcall(priv_MO_base, priv_MO_docallv, 0, t, m, __VA_ARGS__) 97 | 98 | #endif 99 | -------------------------------------------------------------------------------- /src/bin/xmoji/pen.c: -------------------------------------------------------------------------------- 1 | #include "pen.h" 2 | 3 | #include "x11adapter.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | typedef struct PenEntry 10 | { 11 | PictFormat format; 12 | Color color; 13 | xcb_render_picture_t picture; 14 | unsigned refcnt; 15 | } PenEntry; 16 | 17 | struct Pen 18 | { 19 | PenEntry *entry; 20 | PictFormat format; 21 | Color color; 22 | }; 23 | 24 | static PSC_List *entries; 25 | static unsigned refcnt; 26 | 27 | void deleteEntry(void *p) 28 | { 29 | if (!p) return; 30 | PenEntry *entry = p; 31 | if (entry->picture) 32 | { 33 | xcb_render_free_picture(X11Adapter_connection(), entry->picture); 34 | } 35 | free(entry); 36 | } 37 | 38 | Pen *Pen_create(void) 39 | { 40 | if (!refcnt++) entries = PSC_List_create(); 41 | Pen *self = PSC_malloc(sizeof *self); 42 | memset(self, 0, sizeof *self); 43 | return self; 44 | } 45 | 46 | void Pen_configure(Pen *self, PictFormat format, Color color) 47 | { 48 | if (self->format == format && self->color == color) return; 49 | self->format = format; 50 | self->color = color; 51 | if (self->entry) --self->entry->refcnt; 52 | self->entry = 0; 53 | } 54 | 55 | xcb_render_picture_t Pen_picture(Pen *self, 56 | xcb_render_picture_t ownerpic) 57 | { 58 | if (self->entry) return self->entry->picture; 59 | 60 | PenEntry *avail = 0; 61 | for (size_t i = 0; i < PSC_List_size(entries); ++i) 62 | { 63 | PenEntry *entry = PSC_List_at(entries, i); 64 | if (entry->format != self->format) continue; 65 | if (entry->color == self->color) 66 | { 67 | ++entry->refcnt; 68 | self->entry = entry; 69 | return self->entry->picture; 70 | } 71 | else if (!avail && !entry->refcnt) avail = entry; 72 | } 73 | if (!avail) 74 | { 75 | avail = PSC_malloc(sizeof *avail); 76 | avail->format = self->format; 77 | avail->picture = 0; 78 | avail->refcnt = 0; 79 | PSC_List_append(entries, avail, deleteEntry); 80 | } 81 | avail->color = self->color; 82 | ++avail->refcnt; 83 | self->entry = avail; 84 | 85 | xcb_connection_t *c = X11Adapter_connection(); 86 | if (!avail->picture) 87 | { 88 | xcb_pixmap_t tmp = xcb_generate_id(c); 89 | static const uint8_t depths[] = {8, 24, 32}; 90 | CHECK(xcb_create_pixmap(c, depths[self->format], tmp, 91 | X11Adapter_screen()->root, 1, 1), 92 | "Cannot create pen picture for 0x%x", (unsigned)ownerpic); 93 | avail->picture = xcb_generate_id(c); 94 | uint32_t repeat = XCB_RENDER_REPEAT_NORMAL; 95 | CHECK(xcb_render_create_picture(c, avail->picture, tmp, 96 | X11Adapter_format(self->format), 97 | XCB_RENDER_CP_REPEAT, &repeat), 98 | "Cannot create pen for 0x%x", (unsigned)ownerpic); 99 | xcb_free_pixmap(c, tmp); 100 | } 101 | static const xcb_rectangle_t rect = {0, 0, 1, 1}; 102 | CHECK(xcb_render_fill_rectangles(c, XCB_RENDER_PICT_OP_OVER, 103 | avail->picture, Color_xcb(self->color), 1, &rect), 104 | "Cannot colorize pen for 0x%x", (unsigned)ownerpic); 105 | 106 | return self->entry->picture; 107 | } 108 | 109 | void Pen_destroy(Pen *self) 110 | { 111 | if (!self) return; 112 | if (!--refcnt) 113 | { 114 | PSC_List_destroy(entries); 115 | entries = 0; 116 | } 117 | free(self); 118 | } 119 | 120 | -------------------------------------------------------------------------------- /src/bin/xmoji/emoji.c: -------------------------------------------------------------------------------- 1 | #include "emoji.h" 2 | 3 | #include "translator.h" 4 | #include "unistr.h" 5 | 6 | #include 7 | #include 8 | 9 | struct Emoji 10 | { 11 | const EmojiGroup *group; 12 | const UniStr str; 13 | const unsigned name; 14 | const unsigned variants; 15 | }; 16 | 17 | struct EmojiGroup 18 | { 19 | const Emoji *first; 20 | const size_t len; 21 | const unsigned name; 22 | }; 23 | 24 | #include "emojidata.h" 25 | 26 | size_t EmojiGroup_numGroups(void) 27 | { 28 | return sizeof groups / sizeof *groups; 29 | } 30 | 31 | const EmojiGroup *EmojiGroup_at(size_t index) 32 | { 33 | return groups + index; 34 | } 35 | 36 | unsigned EmojiGroup_name(const EmojiGroup *self) 37 | { 38 | return self->name; 39 | } 40 | 41 | size_t EmojiGroup_len(const EmojiGroup *self) 42 | { 43 | return self->len; 44 | } 45 | 46 | const Emoji *EmojiGroup_emojiAt(const EmojiGroup *self, size_t index) 47 | { 48 | return self->first + index; 49 | } 50 | 51 | size_t Emoji_numEmojis(void) 52 | { 53 | return sizeof emojis / sizeof *emojis; 54 | } 55 | 56 | const Emoji *Emoji_at(size_t index) 57 | { 58 | return emojis + index; 59 | } 60 | 61 | const EmojiGroup *Emoji_group(const Emoji *self) 62 | { 63 | return self->group; 64 | } 65 | 66 | const UniStr *Emoji_str(const Emoji *self) 67 | { 68 | return &self->str; 69 | } 70 | 71 | unsigned Emoji_name(const Emoji *self) 72 | { 73 | return self->name; 74 | } 75 | 76 | unsigned Emoji_variants(const Emoji *self) 77 | { 78 | return self->variants; 79 | } 80 | 81 | static int match(const UniStr *emojiName, const UniStr *pattern, 82 | EmojiSearchMode mode) 83 | { 84 | int matches = 0; 85 | if (emojiName) 86 | { 87 | if (mode & ESM_FULL) matches = UniStr_containslc(emojiName, pattern); 88 | else 89 | { 90 | UniStr *baseName = UniStr_cut(emojiName, U":"); 91 | matches = (baseName && UniStr_containslc(baseName, pattern)); 92 | UniStr_destroy(baseName); 93 | } 94 | } 95 | return matches; 96 | } 97 | 98 | size_t Emoji_search(const Emoji **results, size_t resultsz, size_t maxresults, 99 | const UniStr *pattern, const Translator *tr, EmojiSearchMode mode) 100 | { 101 | size_t resultlen = 0; 102 | size_t nresults = 0; 103 | int havebasevariant = 0; 104 | for (size_t i = 0; i < sizeof emojis / sizeof *emojis; ++i) 105 | { 106 | int matches = 0; 107 | if (havebasevariant && !emojis[i].variants) matches = 2; 108 | else if (emojis[i].variants) 109 | { 110 | if (!matches && (mode & ESM_ORIG)) 111 | { 112 | matches = match(NTR(tr, emojis[i].name), pattern, mode); 113 | } 114 | if (!matches && (mode & ESM_TRANS)) 115 | { 116 | matches = match(FTR(tr, emojis[i].name), pattern, mode); 117 | } 118 | } 119 | if (matches) 120 | { 121 | if (matches == 1) 122 | { 123 | if (++nresults > maxresults) break; 124 | if (resultlen + emojis[i].variants > resultsz) break; 125 | havebasevariant = 1; 126 | } 127 | results[resultlen++] = emojis + i; 128 | } 129 | else if (emojis[i].variants) havebasevariant = 0; 130 | } 131 | return resultlen; 132 | } 133 | 134 | const void *XME_get(unsigned id) 135 | { 136 | if (id >= sizeof XME_texts / sizeof *XME_texts) return 0; 137 | return XME_texts + id; 138 | } 139 | 140 | -------------------------------------------------------------------------------- /src/bin/xmoji/object.c: -------------------------------------------------------------------------------- 1 | #include "object.h" 2 | 3 | #include 4 | #include 5 | 6 | #define TYPESCHUNK 64 7 | 8 | static MetaObject mo = MetaObject_init("Object", free); 9 | static MetaObject **types; 10 | static uint32_t typesnum; 11 | static uint32_t typescapa; 12 | static size_t objects; 13 | 14 | typedef struct ObjectBase 15 | { 16 | Object base; 17 | void *mostDerived; 18 | PSC_List *owned; 19 | int refcnt; 20 | } ObjectBase; 21 | 22 | uint32_t MetaObject_register(void *meta) 23 | { 24 | MetaObject *m = meta; 25 | if (m->id && m->id < typesnum) goto done; 26 | if (!types) 27 | { 28 | typescapa = TYPESCHUNK; 29 | types = PSC_malloc(typescapa * sizeof *types); 30 | types[typesnum++] = &mo; 31 | } 32 | else if (typesnum == typescapa) 33 | { 34 | typescapa += TYPESCHUNK; 35 | types = PSC_realloc(types, typescapa * sizeof *types); 36 | } 37 | m->id = typesnum; 38 | types[typesnum++] = m; 39 | done: 40 | return m->id; 41 | } 42 | 43 | const void *MetaObject_get(uint32_t id) 44 | { 45 | if (id >= typesnum) return 0; 46 | return types[id]; 47 | } 48 | 49 | Object *Object_createBase(void *derived) 50 | { 51 | ++objects; 52 | ObjectBase *base = PSC_malloc(sizeof *base); 53 | base->base.base = 0; 54 | base->base.type = 0; 55 | base->mostDerived = derived; 56 | base->owned = 0; 57 | base->refcnt = 1; 58 | return (Object *)base; 59 | } 60 | 61 | void *Object_ref(void *self) 62 | { 63 | ObjectBase *base = Object_instanceOf(self, 0, 1); 64 | ++base->refcnt; 65 | return self; 66 | } 67 | 68 | void Object_own(void *self, void *obj) 69 | { 70 | ObjectBase *base = Object_instanceOf(self, 0, 1); 71 | if (!base->owned) base->owned = PSC_List_create(); 72 | PSC_List_append(base->owned, obj, Object_destroy); 73 | } 74 | 75 | void *Object_instanceOf(void *self, uint32_t type, int mustMatch) 76 | { 77 | int fromderived = !!type; 78 | Object *obj = fromderived ? Object_mostDerived(self) : self; 79 | while (obj) 80 | { 81 | if (obj->type == type) return obj; 82 | obj = obj->base; 83 | if (!obj && fromderived) 84 | { 85 | fromderived = 0; 86 | obj = self; 87 | } 88 | } 89 | if (!mustMatch) return 0; 90 | PSC_Service_panic("Bug: type error!"); 91 | } 92 | 93 | void *Object_mostDerived(void *self) 94 | { 95 | ObjectBase *base = Object_instanceOf(self, 0, 1); 96 | return base->mostDerived; 97 | } 98 | 99 | const char *Object_className(void *self) 100 | { 101 | Object *o = Object_mostDerived(self); 102 | if (!o) return 0; 103 | const MetaObject *m = MetaObject_get(o->type); 104 | if (!m) return 0; 105 | return m->name; 106 | } 107 | 108 | static void destroyRecursive(Object *obj) 109 | { 110 | if (!obj) return; 111 | Object *base = obj->base; 112 | MetaObject *m = types[obj->type]; 113 | m->destroy(obj); 114 | destroyRecursive(base); 115 | } 116 | 117 | void Object_destroy(void *self) 118 | { 119 | if (!self) return; 120 | ObjectBase *base = Object_instanceOf(self, 0, 1); 121 | if (--base->refcnt) return; 122 | PSC_List_destroy(base->owned); 123 | Object *obj = base->mostDerived; 124 | destroyRecursive(obj); 125 | if (!--objects) 126 | { 127 | free(types); 128 | types = 0; 129 | typesnum = 0; 130 | typescapa = 0; 131 | } 132 | } 133 | 134 | -------------------------------------------------------------------------------- /src/bin/xmoji/valuetypes.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_VALUETYPES_H 2 | #define XMOJI_VALUETYPES_H 3 | 4 | #include 5 | 6 | typedef uint32_t Color; 7 | 8 | #define priv_Color_comp(c,s) (((c) >> s) & 0xffU) 9 | #define Color_red(c) priv_Color_comp(c, 24) 10 | #define Color_green(c) priv_Color_comp(c, 16) 11 | #define Color_blue(c) priv_Color_comp(c, 8) 12 | #define Color_alpha(c) priv_Color_comp(c, 0) 13 | 14 | #define priv_Color_16(c) ((c) << 8 | (c)) 15 | #define Color_red16(c) priv_Color_16(Color_red(c)) 16 | #define Color_green16(c) priv_Color_16(Color_green(c)) 17 | #define Color_blue16(c) priv_Color_16(Color_blue(c)) 18 | #define Color_alpha16(c) priv_Color_16(Color_alpha(c)) 19 | 20 | #define Color_fromRgb(r,g,b) (((r)<<24)|((g)<<16)|((b)<<8)|0xffU) 21 | #define Color_fromRgba(r,g,b,a) (((r)<<24)|((g)<<16)|((b)<<8)|(a)) 22 | #define Color_xcb(c) (xcb_render_color_t){ \ 23 | .red = Color_red16(c), \ 24 | .green = Color_green16(c), \ 25 | .blue = Color_blue16(c), \ 26 | .alpha = Color_alpha16(c) \ 27 | } 28 | #define Color_setXcb(c,x) do { \ 29 | (x)->red = Color_red16(c); \ 30 | (x)->green = Color_green16(c); \ 31 | (x)->blue = Color_blue16(c); \ 32 | (x)->alpha = Color_alpha16(c); \ 33 | } while (0) 34 | 35 | typedef struct Pos 36 | { 37 | int16_t x; 38 | int16_t y; 39 | } Pos; 40 | 41 | typedef struct Size 42 | { 43 | uint16_t width; 44 | uint16_t height; 45 | } Size; 46 | 47 | typedef struct Rect 48 | { 49 | Pos pos; 50 | Size size; 51 | } Rect; 52 | 53 | #define Rect_overlaps(r1,r2) (( \ 54 | (r2.pos.x >= r1.pos.x && r2.pos.x < r1.pos.x + r1.size.width) || \ 55 | (r2.pos.x < r1.pos.x && r2.pos.x + r2.size.width >= r1.pos.x)) && ( \ 56 | (r2.pos.y >= r1.pos.y && r2.pos.y < r1.pos.y + r1.size.height) || \ 57 | (r2.pos.y < r1.pos.y && r2.pos.y + r2.size.height >= r1.pos.y))) 58 | 59 | #define Rect_contains(r1,r2) (r2.pos.x >= r1.pos.x && r2.pos.y >= r1.pos.y \ 60 | && r2.size.width + (r2.pos.x - r1.pos.x) <= r1.size.width \ 61 | && r2.size.height + (r2.pos.y - r1.pos.y) <= r1.size.height) 62 | 63 | #define Rect_containsPos(r,p) (p.x >= r.pos.x && p.x < r.pos.x + r.size.width \ 64 | && p.y >= r.pos.y && p.y < r.pos.y + r.size.height) 65 | 66 | typedef struct Box 67 | { 68 | int16_t left; 69 | int16_t top; 70 | int16_t right; 71 | int16_t bottom; 72 | } Box; 73 | 74 | #define Rect_pad(r,b) ((Rect){ \ 75 | .pos = { \ 76 | .x = (b.left + b.right > r.size.width) \ 77 | ? r.pos.x : r.pos.x + b.left, \ 78 | .y = (b.top + b.bottom > r.size.height) \ 79 | ? r.pos.y : r.pos.y + b.top \ 80 | }, \ 81 | .size = { \ 82 | .width = (b.left + b.right > r.size.width) \ 83 | ? r.size.width : r.size.width - b.left - b.right, \ 84 | .height = (b.top + b.bottom > r.size.height) \ 85 | ? r.size.height : r.size.height - b.top - b.bottom \ 86 | } \ 87 | }) 88 | 89 | #define Box_fromRect(r) ((Box){ \ 90 | .left = r.pos.x, \ 91 | .top = r.pos.y, \ 92 | .right = r.pos.x + r.size.width, \ 93 | .bottom = r.pos.y + r.size.height \ 94 | }) 95 | 96 | typedef struct Selection 97 | { 98 | unsigned start; 99 | unsigned len; 100 | } Selection; 101 | 102 | typedef enum Align 103 | { 104 | AH_RIGHT = 1 << 0, 105 | AH_CENTER = 1 << 1, 106 | AV_BOTTOM = 1 << 2, 107 | AV_MIDDLE = 1 << 3 108 | } Align; 109 | 110 | typedef enum Expand 111 | { 112 | EXPAND_NONE = 0, 113 | EXPAND_X = 1 << 0, 114 | EXPAND_Y = 1 << 1 115 | } Expand; 116 | 117 | typedef enum PictFormat 118 | { 119 | PICTFORMAT_ALPHA, 120 | PICTFORMAT_RGB, 121 | PICTFORMAT_ARGB 122 | } PictFormat; 123 | 124 | #endif 125 | -------------------------------------------------------------------------------- /tools/xtc/src/sourcegen.c: -------------------------------------------------------------------------------- 1 | #include "sourcegen.h" 2 | 3 | #include "deffile.h" 4 | #include "util.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | static void fputcstr(FILE *f, const char *str) 12 | { 13 | for (const char *c = str; *c; ++c) 14 | { 15 | switch (*c) 16 | { 17 | case '\r': 18 | break; 19 | case '\n': 20 | fputs("\\n", f); 21 | break; 22 | case '\t': 23 | fputs("\\t", f); 24 | break; 25 | case '"': 26 | fputs("\\\"", f); 27 | break; 28 | default: 29 | fputc(*c, f); 30 | } 31 | } 32 | } 33 | 34 | int dosource(int argc, char **argv) 35 | { 36 | if (argc != 5) usage(argv[0]); 37 | int rc = EXIT_FAILURE; 38 | size_t namelen = strlen(argv[2]); 39 | char *namebuf = xmalloc(namelen + 3); 40 | memcpy(namebuf, argv[2], namelen); 41 | FILE *hdr = 0; 42 | FILE *data = 0; 43 | DefFile *df = DefFile_create(argv[4]); 44 | if (!df) 45 | { 46 | fprintf(stderr, "Error reading `%s'\n", argv[4]); 47 | goto done; 48 | } 49 | memcpy(namebuf+namelen, ".c", 3); 50 | data = fopen(namebuf, "w"); 51 | if (!data) 52 | { 53 | fprintf(stderr, "Error opening `%s' for writing\n", namebuf); 54 | goto done; 55 | } 56 | memcpy(namebuf+namelen, ".h", 3); 57 | hdr = fopen(namebuf, "w"); 58 | if (!hdr) 59 | { 60 | fprintf(stderr, "Error opening `%s' for writing\n", namebuf); 61 | goto done; 62 | } 63 | 64 | char *psep = strrchr(namebuf, '/'); 65 | if (psep) 66 | { 67 | namelen -= psep - namebuf + 1; 68 | memmove(namebuf, psep+1, namelen+3); 69 | } 70 | fprintf(data, "#include \"%s\"\n\n#include \"unistr.h\"\n\n", namebuf); 71 | 72 | namebuf[namelen] = 0; 73 | for (char *c = namebuf; *c; ++c) 74 | { 75 | *c = isalnum((unsigned char)*c) ? toupper((unsigned char)*c) : '_'; 76 | } 77 | fprintf(hdr, "#ifndef XTCSOURCE_%s_H\n#define XTCSOURCE_%s_H\n\n" 78 | "typedef enum %sTexts {\n", namebuf, namebuf, argv[3]); 79 | 80 | for (unsigned i = 0; i < DefFile_len(df); ++i) 81 | { 82 | const DefEntry *entry = DefFile_byId(df, i); 83 | const char *key = DefEntry_key(entry); 84 | const char *to = DefEntry_to(entry); 85 | fprintf(hdr, " %s_txt_%s,\n", argv[3], key); 86 | switch (DefEntry_type(entry)) 87 | { 88 | case DT_CHAR: 89 | fprintf(data, "static const char %s[] = \"", key); 90 | fputcstr(data, to); 91 | fputs("\";\n", data); 92 | break; 93 | 94 | case DT_CHAR32: 95 | fprintf(data, "UniStrVal(%s, U\"", key); 96 | fputcstr(data, to); 97 | fputs("\");\n", data); 98 | break; 99 | } 100 | } 101 | 102 | fprintf(hdr, " %s_ntxt\n} %sTexts;\n\n" 103 | "const void *%s_get(unsigned id);\n\n" 104 | "#endif\n", argv[3], argv[3], argv[3]); 105 | fputs("\nstatic const void *strings[] = {", data); 106 | for (unsigned i = 0; i < DefFile_len(df); ++i) 107 | { 108 | const DefEntry *entry = DefFile_byId(df, i); 109 | static const char *valfmt[] = { 110 | ",\n %s", 111 | "\n %s", 112 | ",\n &%s_v", 113 | "\n &%s_v" 114 | }; 115 | int fidx = ((DefEntry_type(entry) == DT_CHAR32) << 1) + !i; 116 | fprintf(data, valfmt[fidx], DefEntry_key(entry)); 117 | } 118 | fprintf(data, "\n};\n\nconst void *%s_get(unsigned id)\n" 119 | "{\n" 120 | " if (id >= %s_ntxt) return 0;\n" 121 | " return strings[id];\n" 122 | "}\n", argv[3], argv[3]); 123 | 124 | rc = EXIT_SUCCESS; 125 | 126 | done: 127 | if (data) fclose(data); 128 | if (hdr) fclose(hdr); 129 | free(namebuf); 130 | DefFile_destroy(df); 131 | return rc; 132 | } 133 | -------------------------------------------------------------------------------- /src/bin/xmoji/singleinstance.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200112L 2 | 3 | #include "singleinstance.h" 4 | #include "x11app.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #define FNV1A_INIT64 0xcbf29ce484222325ULL 15 | #define FNV1A_PRIME64 0x100000001b3ULL 16 | #define B64HASHSZ 12 17 | 18 | struct SingleInstance 19 | { 20 | PSC_Event *secondary; 21 | char *sockname; 22 | PSC_Server *server; 23 | }; 24 | 25 | /* slightly modified base64 digits: 26 | * avoid '/', so the result can be used for a file name */ 27 | static const char mb64[64] = 28 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; 29 | 30 | /* hash a set of strings with 64bit FNV1a 31 | * base64 encode the result as big-endian number with two leading 0 bits */ 32 | static void b64hash(char hash[B64HASHSZ], const char *str[]) 33 | { 34 | uint64_t h = FNV1A_INIT64; 35 | 36 | while (*str) 37 | { 38 | const char *s = *str++; 39 | while (*s) 40 | { 41 | h ^= (uint64_t)*s++; 42 | h *= FNV1A_PRIME64; 43 | } 44 | } 45 | 46 | for (int shift = 60; shift >= 0; shift -= 6) 47 | { 48 | *hash++ = mb64[(h >> shift) & 0x3fU]; 49 | } 50 | *hash = 0; 51 | } 52 | 53 | SingleInstance *SingleInstance_create(void) 54 | { 55 | SingleInstance *self = PSC_malloc(sizeof *self); 56 | self->secondary = PSC_Event_create(self); 57 | self->server = 0; 58 | self->sockname = 0; 59 | return self; 60 | } 61 | 62 | static void onconnected(void *receiver, void *sender, void *args) 63 | { 64 | (void)sender; 65 | 66 | SingleInstance *self = receiver; 67 | PSC_Connection *conn = args; 68 | PSC_Connection_close(conn, 0); 69 | PSC_Event_raise(self->secondary, 0, 0); 70 | } 71 | 72 | int SingleInstance_start(SingleInstance *self) 73 | { 74 | if (self->server) return 1; 75 | if (!self->sockname) 76 | { 77 | const char *hostname = X11App_hostname(); 78 | if (!hostname) hostname = "localhost"; 79 | uid_t uid = geteuid(); 80 | struct passwd *pw = getpwuid(uid); 81 | const char *username = 0; 82 | if (pw) username = pw->pw_name; 83 | if (!username) username = getenv("USER"); 84 | if (!username) username = "nobody"; 85 | char hash[B64HASHSZ]; 86 | const char *hashargs[] = { hostname, ".", username, 0 }; 87 | b64hash(hash, hashargs); 88 | const char *tmpdir = getenv("TMPDIR"); 89 | if (!tmpdir) tmpdir = "/tmp"; 90 | size_t dirlen = strlen(tmpdir); 91 | const char *basename = X11App_name(); 92 | if (!basename) basename = "x11app"; 93 | size_t baselen = strlen(basename); 94 | size_t socknamesz = dirlen + 1 + baselen + 1 + B64HASHSZ; 95 | self->sockname = PSC_malloc(socknamesz); 96 | snprintf(self->sockname, socknamesz, "%s/%s_%s", 97 | tmpdir, basename, hash); 98 | } 99 | PSC_UnixServerOpts *opts = PSC_UnixServerOpts_create(self->sockname); 100 | PSC_Log_setSilent(1); 101 | self->server = PSC_Server_createUnix(opts); 102 | PSC_Log_setSilent(0); 103 | PSC_UnixServerOpts_destroy(opts); 104 | if (self->server) 105 | { 106 | PSC_Event_register(PSC_Server_clientConnected(self->server), 107 | self, onconnected, 0); 108 | return 1; 109 | } 110 | return 0; 111 | } 112 | 113 | void SingleInstance_stop(SingleInstance *self) 114 | { 115 | PSC_Server_destroy(self->server); 116 | self->server = 0; 117 | } 118 | 119 | PSC_Event *SingleInstance_secondary(SingleInstance *self) 120 | { 121 | return self->secondary; 122 | } 123 | 124 | void SingleInstance_destroy(SingleInstance *self) 125 | { 126 | if (!self) return; 127 | free(self->sockname); 128 | PSC_Server_destroy(self->server); 129 | PSC_Event_destroy(self->secondary); 130 | free(self); 131 | } 132 | 133 | -------------------------------------------------------------------------------- /src/bin/xmoji/emojihistory.c: -------------------------------------------------------------------------------- 1 | #include "emojihistory.h" 2 | 3 | #include "emoji.h" 4 | #include "unistr.h" 5 | #include "unistrbuilder.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | struct EmojiHistory 12 | { 13 | PSC_Event *changed; 14 | size_t size; 15 | const Emoji *history[]; 16 | }; 17 | 18 | EmojiHistory *EmojiHistory_create(size_t size) 19 | { 20 | EmojiHistory *self = PSC_malloc(sizeof *self + 21 | (size * sizeof *self->history)); 22 | self->changed = PSC_Event_create(self); 23 | self->size = size; 24 | memset(self->history, 0, size * sizeof *self->history); 25 | return self; 26 | } 27 | 28 | PSC_Event *EmojiHistory_changed(EmojiHistory *self) 29 | { 30 | return self->changed; 31 | } 32 | 33 | const Emoji *EmojiHistory_at(const EmojiHistory *self, size_t i) 34 | { 35 | if (i >= self->size) return 0; 36 | return self->history[i]; 37 | } 38 | 39 | static const Emoji *emojiById(const UniStr *str) 40 | { 41 | for (size_t i = 0; i < Emoji_numEmojis(); ++i) 42 | { 43 | const Emoji *tmp = Emoji_at(i); 44 | if (UniStr_equals(str, Emoji_str(tmp))) return tmp; 45 | } 46 | return 0; 47 | } 48 | 49 | void EmojiHistory_record(EmojiHistory *self, const UniStr *str) 50 | { 51 | const Emoji *emoji = emojiById(str); 52 | if (!emoji) return; 53 | 54 | size_t i; 55 | for (i = 0; i < self->size; ++i) 56 | { 57 | if (!self->history[i] || self->history[i] == emoji) break; 58 | } 59 | if (!i) 60 | { 61 | if (*self->history) return; 62 | } 63 | else 64 | { 65 | if (i == self->size) --i; 66 | memmove(self->history+1, self->history, i * sizeof *self->history); 67 | } 68 | 69 | *self->history = emoji; 70 | PSC_Event_raise(self->changed, 0, 0); 71 | } 72 | 73 | char *EmojiHistory_serialize(const EmojiHistory *self) 74 | { 75 | UniStrBuilder *builder = UniStrBuilder_create(); 76 | for (size_t i = 0; i < self->size; ++i) 77 | { 78 | if (!self->history[i]) break; 79 | if (i) UniStrBuilder_appendChar(builder, U' '); 80 | UniStrBuilder_appendStr(builder, 81 | UniStr_str(Emoji_str(self->history[i]))); 82 | } 83 | char *serialized = UniStr_toUtf8(UniStrBuilder_stringView(builder), 0); 84 | UniStrBuilder_destroy(builder); 85 | return serialized; 86 | } 87 | 88 | static const Emoji *findEmoji(char **str) 89 | { 90 | while (**str == ' ' || **str == '\t') ++(*str); 91 | if (!**str) return 0; 92 | const char *utf8 = *str; 93 | while (**str && **str != ' ' && **str != '\t') ++(*str); 94 | if (**str) 95 | { 96 | **str = 0; 97 | ++(*str); 98 | while (**str && (**str == ' ' || **str == '\t')) ++(*str); 99 | } 100 | if (!**str) *str = 0; 101 | 102 | UniStr *estr = UniStr_create(utf8); 103 | const Emoji *emoji = emojiById(estr); 104 | UniStr_destroy(estr); 105 | return emoji; 106 | } 107 | 108 | void EmojiHistory_deserialize(EmojiHistory *self, const char *str) 109 | { 110 | char *cstr = PSC_copystr(str); 111 | char *tmp = cstr; 112 | size_t pos = 0; 113 | int havechanges = 0; 114 | while (pos < self->size && tmp) 115 | { 116 | const Emoji *emoji = findEmoji(&tmp); 117 | for (size_t i = 0; emoji && i < pos; ++i) 118 | { 119 | if (self->history[i] == emoji) emoji = 0; 120 | } 121 | if (!emoji) continue; 122 | if (self->history[pos] != emoji) havechanges = 1; 123 | self->history[pos++] = emoji; 124 | } 125 | free(cstr); 126 | if (pos < self->size && self->history[pos]) 127 | { 128 | havechanges = 1; 129 | memset(self->history + pos, 0, self->size - pos); 130 | } 131 | if (havechanges) PSC_Event_raise(self->changed, 0, 0); 132 | } 133 | 134 | void EmojiHistory_destroy(EmojiHistory *self) 135 | { 136 | if (!self) return; 137 | PSC_Event_destroy(self->changed); 138 | free(self); 139 | } 140 | 141 | -------------------------------------------------------------------------------- /src/bin/xmoji/command.c: -------------------------------------------------------------------------------- 1 | #include "command.h" 2 | 3 | #include "unistr.h" 4 | #include "widget.h" 5 | 6 | #include 7 | #include 8 | 9 | static void destroy(void *obj); 10 | 11 | static MetaCommand mo = MetaCommand_init("Command", destroy); 12 | 13 | typedef struct EventEntry 14 | { 15 | Command *command; 16 | void *widget; 17 | PSC_Event *event; 18 | WidgetEvent accessor; 19 | } EventEntry; 20 | 21 | struct Command 22 | { 23 | Object base; 24 | UniStr *name; 25 | UniStr *description; 26 | PSC_Event *triggered; 27 | PSC_List *attachedEvents; 28 | }; 29 | 30 | static void trigger(void *receiver, void *sender, void *args) 31 | { 32 | Command *self = receiver; 33 | CommandTriggeredEventArgs ea = { 34 | .sender = sender, 35 | .args = args 36 | }; 37 | PSC_Event_raise(self->triggered, 0, &ea); 38 | } 39 | 40 | static void destroyEntry(void *obj) 41 | { 42 | EventEntry *entry = obj; 43 | PSC_Event_unregister(entry->event, entry->command, trigger, 0); 44 | Object_destroy(entry->widget); 45 | free(entry); 46 | } 47 | 48 | static void destroy(void *obj) 49 | { 50 | Command *self = Object_instance(obj); 51 | PSC_List_destroy(self->attachedEvents); 52 | PSC_Event_destroy(self->triggered); 53 | UniStr_destroy(self->description); 54 | UniStr_destroy(self->name); 55 | free(self); 56 | } 57 | 58 | Command *Command_createBase(void *derived, 59 | const UniStr *name, const UniStr *description, void *parent) 60 | { 61 | Command *self = PSC_malloc(sizeof *self); 62 | CREATEBASE(Object); 63 | self->name = name ? UniStr_ref(name) : 0; 64 | self->description = description ? UniStr_ref(description) : 0; 65 | self->triggered = PSC_Event_create(self); 66 | self->attachedEvents = PSC_List_create(); 67 | if (parent) Object_own(parent, self); 68 | return self; 69 | } 70 | 71 | PSC_Event *Command_triggered(void *self) 72 | { 73 | Command *cmd = Object_instance(self); 74 | return cmd->triggered; 75 | } 76 | 77 | const UniStr *Command_name(const void *self) 78 | { 79 | const Command *cmd = Object_instance(self); 80 | return cmd->name; 81 | } 82 | 83 | const UniStr *Command_description(const void *self) 84 | { 85 | const Command *cmd = Object_instance(self); 86 | return cmd->description; 87 | } 88 | 89 | void Command_trigger(void *self) 90 | { 91 | Command *cmd = Object_instance(self); 92 | trigger(cmd, cmd, 0); 93 | } 94 | 95 | void Command_attach(void *self, void *widget, WidgetEvent event) 96 | { 97 | Widget *w = Widget_cast(widget); 98 | if (!w) return; 99 | Command *cmd = Object_instance(self); 100 | for (size_t i = 0; i < PSC_List_size(cmd->attachedEvents); ++i) 101 | { 102 | EventEntry *entry = PSC_List_at(cmd->attachedEvents, i); 103 | if (entry->widget == w && entry->accessor == event) return; 104 | } 105 | EventEntry *entry = PSC_malloc(sizeof *entry); 106 | entry->command = cmd; 107 | entry->widget = Object_ref(w); 108 | entry->event = event(w); 109 | entry->accessor = event; 110 | PSC_Event_register(entry->event, cmd, trigger, 0); 111 | PSC_List_append(cmd->attachedEvents, entry, destroyEntry); 112 | } 113 | 114 | struct EntryMatch { 115 | void *widget; 116 | WidgetEvent event; 117 | }; 118 | 119 | static int entryMatches(void *obj, const void *arg) 120 | { 121 | EventEntry *entry = obj; 122 | const struct EntryMatch *match = arg; 123 | return (entry->widget == match->widget && entry->accessor == match->event); 124 | } 125 | 126 | void Command_detach(void *self, void *widget, WidgetEvent event) 127 | { 128 | Widget *w = Widget_cast(widget); 129 | if (!w) return; 130 | Command *cmd = Object_instance(self); 131 | struct EntryMatch match = { 132 | .widget = w, 133 | .event = event 134 | }; 135 | PSC_List_removeAll(cmd->attachedEvents, entryMatches, &match); 136 | } 137 | 138 | -------------------------------------------------------------------------------- /src/bin/xmoji/unistr.h: -------------------------------------------------------------------------------- 1 | #ifndef XMOJI_UNISTR_H 2 | #define XMOJI_UNISTR_H 3 | 4 | #include "char32.h" 5 | #include "macros.h" 6 | 7 | #include 8 | #include 9 | 10 | C_CLASS_DECL(PSC_List); 11 | 12 | typedef struct UniStr 13 | { 14 | size_t len; 15 | char32_t *str; 16 | int refcnt; 17 | } UniStr; 18 | 19 | /* static const initialization 20 | * 21 | * USAGE: UniStr(name, U"value"); 22 | * declares 'static const *UniStr name' and initializes it to "value". 23 | */ 24 | 25 | #define UniStrVal(n,x) static const UniStr n ## _v = { \ 26 | .len = (sizeof x >> 2) - 1, \ 27 | .str = (char32_t *)x, \ 28 | .refcnt = -1 } 29 | #define UniStr(n,x) UniStrVal(n,x); \ 30 | static const UniStr *n = & n ## _v 31 | 32 | /* constructor */ 33 | 34 | #define UniStr_create(x) _Generic(x, \ 35 | char *: UniStr_createFromUtf8, \ 36 | const char *: UniStr_createFromUtf8, \ 37 | char32_t *: UniStr_createOwned, \ 38 | const char32_t *: UniStr_createFromUtf32)(x) 39 | UniStr *UniStr_createFromUtf8(const char *utf8) 40 | ATTR_NONNULL((1)) ATTR_RETNONNULL; 41 | UniStr *UniStr_createFromUtf32(const char32_t *utf32) 42 | ATTR_NONNULL((1)) ATTR_RETNONNULL; 43 | UniStr *UniStr_createOwned(char32_t *utf32) 44 | ATTR_NONNULL((1)) ATTR_RETNONNULL; 45 | 46 | UniStr *UniStr_createFromLatin1(const char *latin1, size_t len) 47 | ATTR_NONNULL((1)) ATTR_RETNONNULL; 48 | 49 | /* reference */ 50 | 51 | UniStr *UniStr_ref(const UniStr *self) 52 | CMETHOD ATTR_RETNONNULL; 53 | 54 | /* getters */ 55 | 56 | size_t UniStr_len(const UniStr *self) 57 | CMETHOD; 58 | const char32_t *UniStr_str(const UniStr *self) 59 | CMETHOD ATTR_RETNONNULL; 60 | 61 | /* mutators, ceating new instances */ 62 | 63 | #define UniStr_append(s,x) _Generic(&x, \ 64 | U32LPT(x): UniStr_appendUtf32, \ 65 | const U32LPT(x): UniStr_appendUtf32, \ 66 | char **: UniStr_appendUtf8, \ 67 | const char **: UniStr_appendUtf8, \ 68 | char32_t **: UniStr_appendUtf32, \ 69 | const char32_t **: UniStr_appendUtf32)(s, x) 70 | UniStr *UniStr_appendUtf8(const UniStr *self, const char *utf8) 71 | CMETHOD ATTR_NONNULL((2)) ATTR_RETNONNULL; 72 | UniStr *UniStr_appendUtf32(const UniStr *self, const char32_t *utf32) 73 | CMETHOD ATTR_NONNULL((2)) ATTR_RETNONNULL; 74 | 75 | #define UniStr_split(s,x) _Generic(&x, \ 76 | U32LPT(x): UniStr_splitByUtf32, \ 77 | const U32LPT(x): UniStr_splitByUtf32, \ 78 | char **: UniStr_splitByUtf8, \ 79 | const char **: UniStr_splitByUtf8, \ 80 | char32_t **: UniStr_splitByUtf32, \ 81 | const char32_t **: UniStr_splitByUtf32)(s, x) 82 | PSC_List *UniStr_splitByUtf8(const UniStr *self, const char *delim) 83 | CMETHOD ATTR_NONNULL((2)) ATTR_RETNONNULL; 84 | PSC_List *UniStr_splitByUtf32(const UniStr *self, const char32_t *delim) 85 | CMETHOD ATTR_NONNULL((2)) ATTR_RETNONNULL; 86 | 87 | #define UniStr_cut(s,x) _Generic(&x, \ 88 | U32LPT(x): UniStr_cutByUtf32, \ 89 | const U32LPT(x): UniStr_cutByUtf32, \ 90 | char **: UniStr_cutByUtf8, \ 91 | const char **: UniStr_cutByUtf8, \ 92 | char32_t **: UniStr_cutByUtf32, \ 93 | const char32_t **: UniStr_cutByUtf32)(s, x) 94 | UniStr *UniStr_cutByUtf8(const UniStr *self, const char *delims) 95 | CMETHOD ATTR_NONNULL((2)) ATTR_RETNONNULL; 96 | UniStr *UniStr_cutByUtf32(const UniStr *self, const char32_t *delims) 97 | CMETHOD ATTR_NONNULL((2)) ATTR_RETNONNULL; 98 | 99 | /* destructor */ 100 | 101 | void UniStr_destroy(UniStr *self); 102 | 103 | /* related utility functions */ 104 | 105 | #define LATIN1(x) _Generic(x, \ 106 | const UniStr *: UniStr_toLatin1, \ 107 | UniStr *: UniStr_toLatin1, \ 108 | const char *: UniStr_utf8ToLatin1, \ 109 | char *: UniStr_utf8ToLatin1 )(x) 110 | char *UniStr_toLatin1(const UniStr *str) 111 | ATTR_NONNULL((1)); 112 | char *UniStr_utf8ToLatin1(const char *utf8); 113 | 114 | char *UniStr_toUtf8(const UniStr *str, size_t *len) 115 | ATTR_NONNULL((1)); 116 | 117 | size_t UniStr_utf32len(const char32_t *s) 118 | ATTR_NONNULL((1)); 119 | 120 | int UniStr_equals(const UniStr *str, const UniStr *other); 121 | int UniStr_containslc(const UniStr *big, const UniStr *little); 122 | 123 | #endif 124 | -------------------------------------------------------------------------------- /src/bin/xmoji/unistrbuilder.c: -------------------------------------------------------------------------------- 1 | #include "unistrbuilder.h" 2 | 3 | #include "unistr.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #define BUILDER_CHUNK 128 10 | 11 | struct UniStrBuilder 12 | { 13 | UniStr string; 14 | size_t capa; 15 | }; 16 | 17 | UniStrBuilder *UniStrBuilder_create(void) 18 | { 19 | UniStrBuilder *self = PSC_malloc(sizeof *self); 20 | memset(self, 0, sizeof *self); 21 | self->string.refcnt = -1; 22 | return self; 23 | } 24 | 25 | UniStrBuilder *UniStrBuilder_clone(UniStrBuilder *builder) 26 | { 27 | UniStrBuilder *self = PSC_malloc(sizeof *self); 28 | self->string.len = builder->string.len; 29 | if (builder->string.str) 30 | { 31 | self->string.str = PSC_malloc(builder->capa 32 | * sizeof *self->string.str); 33 | memcpy(self->string.str, builder->string.str, 34 | (self->string.len + 1) * sizeof *self->string.str); 35 | } 36 | else self->string.str = 0; 37 | self->string.len = builder->string.len; 38 | self->string.refcnt = -1; 39 | self->capa = builder->capa; 40 | return self; 41 | } 42 | 43 | static void adjust(UniStrBuilder *self, size_t newlen) 44 | { 45 | if (newlen < self->capa && 2 * newlen > self->capa) return; 46 | size_t newcapa = ((newlen + BUILDER_CHUNK) 47 | / BUILDER_CHUNK) * BUILDER_CHUNK; 48 | if (newcapa < self->capa && 2 * newcapa > self->capa) return; 49 | self->string.str = PSC_realloc(self->string.str, 50 | newcapa * sizeof *self->string.str); 51 | self->capa = newcapa; 52 | } 53 | 54 | void UniStrBuilder_appendChar(UniStrBuilder *self, char32_t c) 55 | { 56 | adjust(self, self->string.len + 1); 57 | self->string.str[self->string.len] = c; 58 | self->string.str[++self->string.len] = 0; 59 | } 60 | 61 | void UniStrBuilder_appendStr(UniStrBuilder *self, const char32_t *s) 62 | { 63 | size_t appendlen = UniStr_utf32len(s); 64 | adjust(self, self->string.len + appendlen); 65 | memcpy(self->string.str + self->string.len, s, 66 | (appendlen + 1) * sizeof *s); 67 | self->string.len += appendlen; 68 | } 69 | 70 | void UniStrBuilder_insertChar(UniStrBuilder *self, 71 | size_t pos, char32_t c) 72 | { 73 | if (pos >= self->string.len) 74 | { 75 | UniStrBuilder_appendChar(self, c); 76 | return; 77 | } 78 | adjust(self, self->string.len + 1); 79 | memmove(self->string.str + pos + 1, self->string.str + pos, 80 | (++self->string.len - pos) * sizeof *self->string.str); 81 | self->string.str[pos] = c; 82 | } 83 | 84 | void UniStrBuilder_insertStr(UniStrBuilder *self, 85 | size_t pos, const char32_t *s, size_t maxlen) 86 | { 87 | if (pos >= self->string.len) 88 | { 89 | UniStrBuilder_appendStr(self, s); 90 | return; 91 | } 92 | size_t insertlen = UniStr_utf32len(s); 93 | if (maxlen && maxlen < insertlen) insertlen = maxlen; 94 | adjust(self, self->string.len + insertlen); 95 | memmove(self->string.str + pos + insertlen, self->string.str + pos, 96 | (self->string.len + 1 - pos) * sizeof *self->string.str); 97 | memcpy(self->string.str + pos, s, insertlen * sizeof *s); 98 | self->string.len += insertlen; 99 | } 100 | 101 | void UniStrBuilder_clear(UniStrBuilder *self) 102 | { 103 | free(self->string.str); 104 | self->string.len = 0; 105 | self->string.str = 0; 106 | self->capa = 0; 107 | } 108 | 109 | void UniStrBuilder_remove(UniStrBuilder *self, 110 | size_t pos, size_t len) 111 | { 112 | if (!pos && len >= self->string.len) 113 | { 114 | UniStrBuilder_clear(self); 115 | return; 116 | } 117 | if (self->string.len - pos < len) len = self->string.len - pos; 118 | memmove(self->string.str + pos, self->string.str + pos + len, 119 | (self->string.len + 1 - pos - len) * sizeof *self->string.str); 120 | self->string.len -= len; 121 | adjust(self, self->string.len); 122 | } 123 | 124 | UniStr *UniStrBuilder_string(const UniStrBuilder *self) 125 | { 126 | UniStr *string = PSC_malloc(sizeof *string); 127 | string->len = self->string.len; 128 | string->str = PSC_malloc((string->len + 1) * sizeof *string->str); 129 | string->refcnt = 1; 130 | memcpy(string->str, self->string.str, 131 | (string->len + 1) * sizeof *string->str); 132 | return string; 133 | } 134 | 135 | const UniStr *UniStrBuilder_stringView(const UniStrBuilder *self) 136 | { 137 | return &self->string; 138 | } 139 | 140 | void UniStrBuilder_destroy(UniStrBuilder *self) 141 | { 142 | if (!self) return; 143 | free(self->string.str); 144 | free(self); 145 | } 146 | 147 | -------------------------------------------------------------------------------- /src/bin/xmoji/translator.c: -------------------------------------------------------------------------------- 1 | #include "translator.h" 2 | 3 | #include "unistr.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #ifdef WITH_NLS 11 | typedef struct TranslationEntry 12 | { 13 | void *str; 14 | int type; 15 | } TranslationEntry; 16 | #endif 17 | 18 | struct Translator 19 | { 20 | const void *(*gettext)(unsigned id); 21 | #ifdef WITH_NLS 22 | TranslationEntry *translations; 23 | unsigned translationslen; 24 | #endif 25 | }; 26 | 27 | #ifdef WITH_NLS 28 | static int read32le(unsigned *val, FILE *in) 29 | { 30 | unsigned char data[4]; 31 | if (fread(data, 4, 1, in) != 1) return -1; 32 | *val = data[0] + (data[1] << 8) + (data[2] << 16) + (data[3] << 24); 33 | return 0; 34 | } 35 | 36 | static char *xctname(const char *name, const char *lang) 37 | { 38 | size_t nmlen = strlen(name); 39 | size_t langlen = strcspn(lang, "_.@"); 40 | 41 | size_t xctlen = sizeof TRANSDIR + nmlen + langlen + 5; 42 | char *xctnm = PSC_malloc(xctlen+1); 43 | memcpy(xctnm, TRANSDIR, sizeof TRANSDIR - 1); 44 | xctnm[sizeof TRANSDIR - 1] = '/'; 45 | memcpy(xctnm + sizeof TRANSDIR, name, nmlen); 46 | xctnm[sizeof TRANSDIR + nmlen] = '-'; 47 | memcpy(xctnm + sizeof TRANSDIR + nmlen + 1, lang, langlen); 48 | memcpy(xctnm + sizeof TRANSDIR + nmlen + langlen + 1, 49 | ".xct", sizeof ".xct"); 50 | return xctnm; 51 | } 52 | 53 | static void loadTranslations(Translator *self, 54 | const char *name, const char *lang) 55 | { 56 | self->translations = 0; 57 | self->translationslen = 0; 58 | char *val = 0; 59 | UniStr *us = 0; 60 | char *xctnm = xctname(name, lang); 61 | FILE *xct = fopen(xctnm, "rb"); 62 | if (!xct) goto done; 63 | char magic[4]; 64 | if (fread(magic, 4, 1, xct) != 1) goto done; 65 | unsigned len; 66 | if (read32le(&len, xct) < 0) goto done; 67 | self->translations = PSC_malloc(len * sizeof *self->translations); 68 | memset(self->translations, 0, len * sizeof *self->translations); 69 | self->translationslen = len; 70 | for (unsigned i = 0; i < len; ++i) 71 | { 72 | signed char byte = fgetc(xct); 73 | if (byte < 0) continue; 74 | self->translations[i].type = byte; 75 | unsigned slen; 76 | if (read32le(&slen, xct) < 0) goto done; 77 | val = PSC_malloc(slen+1); 78 | if (fread(val, 1, slen, xct) < slen) goto done; 79 | val[slen] = 0; 80 | switch (byte) 81 | { 82 | case 0: 83 | self->translations[i].str = val; 84 | val = 0; 85 | break; 86 | case 1: 87 | us = UniStr_create(val); 88 | self->translations[i].str = us; 89 | free(val); 90 | val = 0; 91 | break; 92 | default: 93 | self->translations[i].type = -1; 94 | goto done; 95 | } 96 | } 97 | 98 | done: 99 | if (xct) fclose(xct); 100 | free(val); 101 | free(xctnm); 102 | } 103 | #endif 104 | 105 | Translator *Translator_create(const char *name, const char *lang, 106 | const void *(*gettext)(unsigned id)) 107 | { 108 | Translator *self = PSC_malloc(sizeof *self); 109 | self->gettext = gettext; 110 | #ifdef WITH_NLS 111 | loadTranslations(self, name, lang); 112 | #else 113 | (void)name; 114 | (void)lang; 115 | #endif 116 | return self; 117 | } 118 | 119 | const void *Translator_getText(const Translator *self, unsigned id) 120 | { 121 | const char *text = Translator_getTranslation(self, id); 122 | if (!text) text = Translator_getOriginal(self, id); 123 | return text; 124 | } 125 | 126 | const void *Translator_getTranslation(const Translator *self, unsigned id) 127 | { 128 | #ifdef WITH_NLS 129 | if (self->translations && id < self->translationslen 130 | && self->translations[id].str) 131 | { 132 | return self->translations[id].str; 133 | } 134 | else if (!self->translations) return self->gettext(id); 135 | #else 136 | (void)self; 137 | (void)id; 138 | #endif 139 | return 0; 140 | } 141 | 142 | const void *Translator_getOriginal(const Translator *self, unsigned id) 143 | { 144 | return self->gettext(id); 145 | } 146 | 147 | void Translator_destroy(Translator *self) 148 | { 149 | #ifdef WITH_NLS 150 | if (!self) return; 151 | if (self->translations) 152 | { 153 | for (unsigned i = 0; i < self->translationslen; ++i) 154 | { 155 | if (self->translations[i].type < 0) continue; 156 | if (self->translations[i].type == 1) 157 | { 158 | UniStr_destroy(self->translations[i].str); 159 | } 160 | else free(self->translations[i].str); 161 | } 162 | free(self->translations); 163 | } 164 | #endif 165 | free(self); 166 | } 167 | 168 | -------------------------------------------------------------------------------- /src/bin/xmoji/xdgopen.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200809L 2 | #define _XOPEN_SOURCE 700 3 | 4 | #include "xdgopen.h" 5 | 6 | #include "suppress.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #ifndef PATH_MAX 19 | #define PATH_MAX 4096 20 | #endif 21 | 22 | typedef struct XdgOpenJob 23 | { 24 | XdgOpenErrorHandler errorHandler; 25 | char *url; 26 | void *errorCtx; 27 | PSC_ThreadJob *job; 28 | XdgOpenError error; 29 | char tool[PATH_MAX]; 30 | } XdgOpenJob; 31 | 32 | static const char *findtool(XdgOpenJob *job) 33 | { 34 | const char *tool = 0; 35 | char *path = getenv("PATH"); 36 | if (!path) return 0; 37 | PSC_List *pathlist = PSC_List_fromString(path, ":"); 38 | PSC_ListIterator *i = PSC_List_iterator(pathlist); 39 | while (PSC_ListIterator_moveNext(i)) 40 | { 41 | const char *entry = PSC_ListIterator_current(i); 42 | snprintf(job->tool, sizeof job->tool, "%s/xdg-open", entry); 43 | struct stat st; 44 | if (stat(job->tool, &st) == 0 && 45 | (st.st_mode & (S_IFREG|S_IXUSR)) == (S_IFREG|S_IXUSR)) 46 | { 47 | tool = job->tool; 48 | break; 49 | } 50 | } 51 | PSC_ListIterator_destroy(i); 52 | PSC_List_destroy(pathlist); 53 | return tool; 54 | } 55 | 56 | static void doopen(void *arg) 57 | { 58 | XdgOpenJob *openJob = arg; 59 | const char *tool = findtool(openJob); 60 | if (!tool) 61 | { 62 | openJob->error = XOE_TOOLNOTFOUND; 63 | return; 64 | } 65 | 66 | pid_t pid = fork(); 67 | if (pid < 0) return; 68 | 69 | if (pid == 0) 70 | { 71 | int devnull = open("/dev/null", O_RDWR); 72 | if (devnull < 0) exit(XOE_SYSTEM); 73 | dup2(devnull, STDIN_FILENO); 74 | dup2(devnull, STDOUT_FILENO); 75 | dup2(devnull, STDERR_FILENO); 76 | close(devnull); 77 | if (setsid() < 0) exit(XOE_SYSTEM); 78 | int pfd[2]; 79 | if (pipe(pfd) < 0) exit(XOE_SYSTEM); 80 | if (fcntl(pfd[1], F_SETFD, FD_CLOEXEC) < 0) exit(XOE_SYSTEM); 81 | if ((pid = fork()) < 0) exit(XOE_SYSTEM); 82 | 83 | if (pid == 0) 84 | { 85 | close(pfd[0]); 86 | execl(tool, "xdg-open", openJob->url, NULL); 87 | char err = XOE_EXEC; 88 | SUPPRESS(unused-result) 89 | /* dummy write to newly created pipe */ 90 | write(pfd[1], &err, 1); 91 | ENDSUPPRESS 92 | exit(XOE_EXEC); 93 | } 94 | 95 | close(pfd[1]); 96 | fd_set set; 97 | FD_ZERO(&set); 98 | FD_SET(pfd[0], &set); 99 | struct timeval timeout; 100 | 101 | for (int i = 0; i < 6; ++i) 102 | { 103 | timeout.tv_sec = 0; 104 | timeout.tv_usec = 500000UL; 105 | int rc = select(pfd[0]+1, &set, 0, 0, &timeout); 106 | if (rc < 0) exit(XOE_SYSTEM); 107 | if (rc > 0) exit(XOE_EXEC); 108 | int status; 109 | pid_t cpid = waitpid(pid, &status, WNOHANG); 110 | if (cpid < 0) exit(XOE_SYSTEM); 111 | if (cpid == pid) 112 | { 113 | if (WIFEXITED(status) && WEXITSTATUS(status) == 0) 114 | { 115 | exit(XOE_OK); 116 | } 117 | exit(XOE_SYSTEM); 118 | } 119 | } 120 | exit(XOE_OK); 121 | } 122 | 123 | int status; 124 | if (waitpid(pid, &status, 0) == pid && WIFEXITED(status)) 125 | { 126 | openJob->error = WEXITSTATUS(status); 127 | } 128 | } 129 | 130 | static void opendone(void *receiver, void *sender, void *args) 131 | { 132 | (void)receiver; 133 | (void)sender; 134 | 135 | XdgOpenJob *openJob = args; 136 | if (openJob->job && openJob->error == XOE_OK 137 | && !PSC_ThreadJob_hasCompleted(openJob->job)) 138 | { 139 | openJob->error = XOE_SYSTEM; 140 | } 141 | if (openJob->errorHandler && openJob->error != XOE_OK) 142 | { 143 | openJob->errorHandler(openJob->errorCtx, openJob->url, openJob->error); 144 | } 145 | 146 | free(openJob->url); 147 | free(openJob); 148 | } 149 | 150 | void xdgOpen(const char *url, void *ctx, XdgOpenErrorHandler errorHandler) 151 | { 152 | XdgOpenJob *openJob = PSC_malloc(sizeof *openJob); 153 | openJob->errorHandler = errorHandler; 154 | openJob->url = PSC_copystr(url); 155 | openJob->errorCtx = ctx; 156 | openJob->error = XOE_SYSTEM; 157 | 158 | if (PSC_ThreadPool_active()) 159 | { 160 | openJob->job = PSC_ThreadJob_create(doopen, openJob, 0); 161 | PSC_Event_register(PSC_ThreadJob_finished(openJob->job), 162 | 0, opendone, 0); 163 | PSC_ThreadPool_enqueue(openJob->job); 164 | } 165 | else 166 | { 167 | openJob->job = 0; 168 | doopen(openJob); 169 | opendone(0, 0, openJob); 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /tools/emojigen/src/emojireader.c: -------------------------------------------------------------------------------- 1 | #include "emojireader.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "util.h" 9 | 10 | static Emoji *emojis; 11 | static Emoji *neutralskin; 12 | static EmojiGroup *groups; 13 | static size_t emojisize; 14 | static size_t emojicapa; 15 | static size_t groupsize; 16 | static char line[1024]; 17 | 18 | static char *copystr(const char *str, size_t *len) 19 | { 20 | size_t slen = strlen(str); 21 | char *s = xmalloc(slen+1); 22 | size_t clen = 0; 23 | for (char *w = s; *str; ++w, ++str) 24 | { 25 | if ((*str & 0xc0) != 0x80) ++clen; 26 | *w = *str; 27 | } 28 | s[slen] = 0; 29 | if (len) *len = clen; 30 | return s; 31 | } 32 | 33 | #define isws(c) (*(c) == ' ' || *(c) == '\t') 34 | #define skipws(c) do { while (isws(c)) ++c; } while (0) 35 | #define match(c, s) (!strncmp((c), (s), sizeof(s)-1) ? ((c)+=sizeof(s)-1) : 0) 36 | 37 | static char *findgroup(void) 38 | { 39 | char *c = line; 40 | if (*c++ != '#') return 0; 41 | skipws(c); 42 | if (!match(c, "group:")) return 0; 43 | skipws(c); 44 | if (!*c) return 0; 45 | return c; 46 | } 47 | 48 | static char32_t parsecodepoint(char **c) 49 | { 50 | char32_t codepoint = 0; 51 | skipws(*c); 52 | while ((**c >= '0' && **c <= '9') 53 | || (**c >= 'A' && **c <= 'F')) 54 | { 55 | codepoint <<= 4; 56 | if (**c <= '9') codepoint += (**c - '0'); 57 | else codepoint += (0xa + (**c - 'A')); 58 | ++(*c); 59 | } 60 | return isws(*c) ? codepoint : 0; 61 | } 62 | 63 | static void parseemoji(size_t groupno) 64 | { 65 | char32_t codepoints[16] = {0}; 66 | int ncodepoints = 0; 67 | char *c = line; 68 | char32_t cp = 0; 69 | while (ncodepoints < 15 && (cp = parsecodepoint(&c))) 70 | { 71 | codepoints[ncodepoints++] = cp; 72 | } 73 | if (!ncodepoints) return; 74 | skipws(c); 75 | if (*c++ != ';') return; 76 | skipws(c); 77 | if (!match(c, "fully-qualified")) return; 78 | skipws(c); 79 | if (*c++ != '#') return; 80 | while (*c && *c != 'E') ++c; 81 | ++c; 82 | while (isdigit((unsigned char)*c)) ++c; 83 | if (*c++ != '.') return; 84 | while (isdigit((unsigned char)*c)) ++c; 85 | if (!isws(c)) return; 86 | skipws(c); 87 | if (!*c) return; 88 | 89 | if (emojisize == emojicapa) 90 | { 91 | emojicapa += 512; 92 | emojis = xrealloc(emojis, emojicapa * sizeof *emojis); 93 | } 94 | Emoji *emoji = emojis + emojisize++; 95 | emoji->codepoints = xmalloc((ncodepoints+1) * sizeof *emoji->codepoints); 96 | memcpy(emoji->codepoints, codepoints, 97 | (ncodepoints+1) * sizeof *emoji->codepoints); 98 | emoji->name = copystr(c, &emoji->namelen); 99 | emoji->len = ncodepoints; 100 | emoji->groupno = groupno; 101 | emoji->variants = 1; 102 | if (strstr(emoji->name, "skin tone")) 103 | { 104 | if (neutralskin) 105 | { 106 | emoji->variants = 0; 107 | ++neutralskin->variants; 108 | } 109 | } 110 | else neutralskin = emoji; 111 | } 112 | 113 | int readEmojis(const char *datafile) 114 | { 115 | int rc = -1; 116 | EmojiGroup *group = 0; 117 | char *groupname = 0; 118 | 119 | FILE *in = fopen(datafile, "r"); 120 | if (!in) return rc; 121 | 122 | while (fgets(line, sizeof line, in)) 123 | { 124 | line[strcspn(line, "\n")] = 0; 125 | if ((groupname = findgroup())) 126 | { 127 | if (group) group->len = emojisize - group->start; 128 | group = 0; 129 | if (strcmp(groupname, "Component") != 0) 130 | { 131 | groups = xrealloc(groups, (groupsize+1) * sizeof *groups); 132 | group = groups + groupsize++; 133 | group->name = copystr(groupname, &group->namelen); 134 | group->start = emojisize; 135 | group->len = 0; 136 | } 137 | continue; 138 | } 139 | if (group) parseemoji(group-groups); 140 | } 141 | if (!groupsize || !emojisize) 142 | { 143 | emojisDone(); 144 | goto done; 145 | } 146 | group->len = emojisize - group->start; 147 | rc = 0; 148 | 149 | done: 150 | if (in) fclose(in); 151 | return rc; 152 | } 153 | 154 | size_t Emoji_count(void) 155 | { 156 | return emojisize; 157 | } 158 | 159 | const Emoji *Emoji_at(size_t i) 160 | { 161 | if (i >= emojisize) return 0; 162 | return emojis + i; 163 | } 164 | 165 | size_t EmojiGroup_count(void) 166 | { 167 | return groupsize; 168 | } 169 | 170 | const EmojiGroup *EmojiGroup_at(size_t i) 171 | { 172 | if (i >= groupsize) return 0; 173 | return groups + i; 174 | } 175 | 176 | void emojisDone(void) 177 | { 178 | if (!emojis) return; 179 | for (size_t i = 0; i < emojisize; ++i) 180 | { 181 | free(emojis[i].name); 182 | free(emojis[i].codepoints); 183 | } 184 | free(emojis); 185 | emojisize = 0; 186 | emojicapa = 0; 187 | emojis = 0; 188 | if (!groups) return; 189 | for (size_t i = 0; i < groupsize; ++i) 190 | { 191 | free(groups[i].name); 192 | } 193 | free(groups); 194 | groupsize = 0; 195 | groups = 0; 196 | } 197 | 198 | -------------------------------------------------------------------------------- /src/bin/xmoji/translations/xmoji-ui.def.in: -------------------------------------------------------------------------------- 1 | $w$about 2 | . 3 | About 4 | . 5 | 6 | $w$aboutdesc 7 | . 8 | About Xmoji 9 | . 10 | 11 | $w$settings 12 | . 13 | Settings 14 | . 15 | 16 | $w$settingsdesc 17 | . 18 | Configure runtime settings 19 | . 20 | 21 | $w$quit 22 | . 23 | Quit 24 | . 25 | 26 | $w$quitdesc 27 | . 28 | Exit the application 29 | . 30 | 31 | $w$searchText 32 | . 33 | Search 34 | . 35 | 36 | $w$clickToSearch 37 | . 38 | Click to type and search ... 39 | . 40 | 41 | $w$recentText 42 | . 43 | Recently used 44 | . 45 | 46 | $c$aboutDlgTitle 47 | . 48 | About Xmoji 49 | . 50 | 51 | $w$abouttxt 52 | . 53 | X11 Emoji Keyboard 54 | . 55 | 56 | $w$licenselbl 57 | . 58 | License: 59 | . 60 | 61 | $w$authorlbl 62 | . 63 | Author: 64 | . 65 | 66 | $w$ok 67 | . 68 | OK 69 | . 70 | 71 | $c$settingsDlgTitle 72 | . 73 | Xmoji settings 74 | . 75 | 76 | $w$instanceDesc 77 | . 78 | This allows to enforce running only one instance of Xmoji. 79 | In "Single" mode, trying to run a second instance as the same 80 | user on the same machine will bring the already running 81 | instance to the top of the current desktop. 82 | In "Multi" mode, many instance can run at the same time. 83 | . 84 | 85 | $w$instance 86 | . 87 | Instance mode: 88 | . 89 | 90 | $w$instanceSingle 91 | . 92 | Single 93 | . 94 | 95 | $w$instanceMulti 96 | . 97 | Multi 98 | . 99 | 100 | $w$scaleDesc 101 | . 102 | Scale factor for the size of the emoji font. 103 | Tiny means the default text character size. 104 | . 105 | 106 | $w$scale 107 | . 108 | Emoji scale: 109 | . 110 | 111 | $w$scaleTiny 112 | . 113 | Tiny 114 | . 115 | 116 | $w$scaleSmall 117 | . 118 | Small 119 | . 120 | 121 | $w$scaleMedium 122 | . 123 | Medium 124 | . 125 | 126 | $w$scaleLarge 127 | . 128 | Large 129 | . 130 | 131 | $w$scaleHuge 132 | . 133 | Huge 134 | . 135 | 136 | $w$injectFlagsDesc 137 | . 138 | These are workarounds/hacks to help some clients receiving the 139 | faked key press events to correctly display emojis. 140 | 141 | * Pre-ZWJ: If the emoji is a ZWJ sequence, prepend another 142 | ZWJ. This is nonstandard, but helps some clients. If none of 143 | the space options is active, a ZW space is also prepended. 144 | * Add space: Add a regular space after each emoji 145 | * Add ZW space: Add a zero-width space after each emoji 146 | . 147 | 148 | $w$injectFlags 149 | . 150 | Key injection flags: 151 | . 152 | 153 | $w$flagsNone 154 | . 155 | None 156 | . 157 | 158 | $w$flagsSpace 159 | . 160 | Add space 161 | . 162 | 163 | $w$flagsZws 164 | . 165 | Add ZW space 166 | . 167 | 168 | $w$flagsZwj 169 | . 170 | Pre-ZWJ 171 | . 172 | 173 | $w$flagsZwjSpace 174 | . 175 | Pre-ZWJ + add space 176 | . 177 | 178 | $w$flagsZwjZws 179 | . 180 | Pre-ZWJ + add ZW space 181 | . 182 | 183 | $w$waitBeforeDesc 184 | . 185 | Sending emojis as faked X11 key events requires temporarily 186 | changing the keyboard mapping. 187 | This is the time (in milliseconds) to wait after changing the 188 | mapping and before sending the key press events. 189 | This might help prevent possible race condition bugs in 190 | clients between applying the new mapping and processing 191 | the events. 192 | . 193 | 194 | $w$waitBefore 195 | . 196 | Wait before sending keys (ms): 197 | . 198 | 199 | $w$waitAfterDesc 200 | . 201 | Sending emojis as faked X11 key events requires temporarily 202 | changing the keyboard mapping. 203 | This is the time (in milliseconds) to wait after sending the 204 | key press events and before resetting the keyboard mapping. 205 | Some clients might apply a new keyboard map before processing 206 | all queued events, so if you see completely unrelated 207 | characters instead of the expected emojis, try increasing this 208 | value. 209 | Note that if you see multiple emojis instead of just one, this 210 | value is unrelated, but the client has issues correctly 211 | interpreting an emoji consisting of multiple codepoints. 212 | . 213 | 214 | $w$waitAfter 215 | . 216 | Wait after sending keys (ms): 217 | . 218 | 219 | $w$searchModeDesc 220 | . 221 | Configures how searching for emojis works. 222 | * Base: Only consider the base name (in front of an optional colon) 223 | of an emoji for searching. E.g. the "woman: red hair" emoji won't 224 | be matched when searching for "red". 225 | * Full: Search in the full name of the emoji. This still excludes 226 | skin colors, because the search always returns them grouped. 227 | * Original/English: Search in the english emoji names 228 | * Translated: Search in the translated emoji names 229 | * Both: Search in english and translated names 230 | When no translation is loaded or Xmoji is built without NLS support, 231 | the search always uses the english names. 232 | . 233 | 234 | $w$searchMode 235 | . 236 | Emoji search mode: 237 | . 238 | 239 | $w$searchModeOriginal 240 | . 241 | Base + Original/English 242 | . 243 | 244 | $w$searchModeTranslated 245 | . 246 | Base + Translated 247 | . 248 | 249 | $w$searchModeBoth 250 | . 251 | Base + Both 252 | . 253 | 254 | $w$searchModeFullOriginal 255 | . 256 | Full + Original/English 257 | . 258 | 259 | $w$searchModeFullTranslated 260 | . 261 | Full + Translated 262 | . 263 | 264 | $w$searchModeFullBoth 265 | . 266 | Full + Both 267 | . 268 | 269 | -------------------------------------------------------------------------------- /src/bin/xmoji/xmoji.mk: -------------------------------------------------------------------------------- 1 | GEN_BIN2CSTR_tool= $(BIN2CSTR_TARGET) 2 | GEN_BIN2CSTR_args= $1 $2 3 | GEN_EMOJIGEN_tool= $(EMOJIGEN_TARGET) 4 | GEN_EMOJIGEN_args= source $1 $2 5 | GEN_EMOJIGRP_tool= $(EMOJIGEN_TARGET) 6 | GEN_EMOJIGRP_args= groupnames $1 $2 $3 7 | GEN_EMOJINM_tool= $(EMOJIGEN_TARGET) 8 | GEN_EMOJINM_args= emojinames $1 $2 9 | GEN_EMOJITRANS_tool= $(EMOJIGEN_TARGET) 10 | GEN_EMOJITRANS_args= translate $1 $2 $3 $4 11 | GEN_TEXTS_tool= $(XTC_TARGET) 12 | GEN_TEXTS_args= source $(basename $1) XMU $2 13 | GEN_TRANS_tool= $(XTC_TARGET) 14 | GEN_TRANS_args= compile $(dir $1) \ 15 | $(lastword $(subst -, ,$(basename $1))) $2 16 | 17 | xmoji_PRECHECK= CHAR32_T INOTIFY KQUEUE 18 | 19 | CHAR32_T_TYPE= char32_t 20 | CHAR32_T_HEADERS= uchar.h 21 | INOTIFY_FUNC= inotify_init 22 | INOTIFY_HEADERS= sys/inotify.h 23 | INOTIFY_ARGS= void 24 | KQUEUE_FUNC= kqueue 25 | KQUEUE_HEADERS= sys/event.h 26 | KQUEUE_ARGS= void 27 | 28 | xmoji_VERSION= 0.8 29 | xmoji_DEFINES= -DLOCALEDIR=\"$(localedir)\" \ 30 | -DVERSION=\"$(xmoji_VERSION)\" 31 | xmoji_MODULES= button \ 32 | colorset \ 33 | command \ 34 | config \ 35 | configfile \ 36 | dropdown \ 37 | emoji \ 38 | emojibutton \ 39 | emojihistory \ 40 | filewatcher \ 41 | flowgrid \ 42 | flyout \ 43 | font \ 44 | hbox \ 45 | hyperlink \ 46 | icon \ 47 | icons \ 48 | imagelabel \ 49 | keyinjector \ 50 | menu \ 51 | object \ 52 | pen \ 53 | pixmap \ 54 | scrollbox \ 55 | shape \ 56 | singleinstance \ 57 | spinbox \ 58 | surface \ 59 | tabbox \ 60 | table \ 61 | tablerow \ 62 | textbox \ 63 | textlabel \ 64 | textrenderer \ 65 | texts \ 66 | tooltip \ 67 | translator \ 68 | unistr \ 69 | unistrbuilder \ 70 | vbox \ 71 | widget \ 72 | window \ 73 | x11adapter \ 74 | x11app \ 75 | xdgopen \ 76 | xmoji \ 77 | xrdb \ 78 | xselection 79 | xmoji_UITXT= translations/xmoji-ui.def 80 | xmoji_EMOJIDATA= contrib/emoji-test.txt 81 | xmoji_GEN= BIN2CSTR EMOJIGEN EMOJIGRP TEXTS 82 | xmoji_BIN2CSTR_FILES= icon256.h:icons/256x256/xmoji.png \ 83 | icon48.h:icons/48x48/xmoji.png \ 84 | icon32.h:icons/32x32/xmoji.png \ 85 | icon16.h:icons/16x16/xmoji.png 86 | xmoji_EMOJIGEN_FILES= emojidata.h:$(xmoji_EMOJIDATA) 87 | xmoji_EMOJIGRP_FILES= $(xmoji_UITXT):$(xmoji_UITXT).in:$(xmoji_EMOJIDATA) 88 | xmoji_transdir= $(xmoji_datadir)/translations 89 | xmoji_TEXTS_FILES= texts.c:$(xmoji_UITXT) 90 | xmoji_TRANSLATIONS= xmoji-ui xmoji-emojis 91 | xmoji_LANGUAGES= de 92 | xmoji_LDFLAGS= -Wl,--as-needed 93 | xmoji_LIBS= m 94 | xmoji_PKGDEPS= fontconfig \ 95 | harfbuzz \ 96 | libpng >= 1.6 \ 97 | xcb >= 1.14 \ 98 | xcb-cursor \ 99 | xcb-image \ 100 | xcb-render \ 101 | xcb-xkb \ 102 | xcb-xtest \ 103 | xkbcommon \ 104 | xkbcommon-x11 105 | xmoji_SUB_FILES= xmoji.desktop 106 | xmoji_SUB_LIST= bindir=$(bindir) 107 | xmoji_ICONSIZES= 16x16 32x32 48x48 256x256 108 | xmoji_DESKTOPFILE= xmoji 109 | xmoji_DOCS= README.md \ 110 | TRANSLATE.md 111 | xmoji_PREBUILD= # 112 | 113 | ifeq ($(TRACE),1) 114 | xmoji_DEFINES+= -DTRACE_X11_REQUESTS 115 | endif 116 | 117 | ifeq ($(BUNDLED_FREETYPE),1) 118 | xmoji_PRECFLAGS+= -I./ftbundle/root/include/freetype2 119 | xmoji_LDFLAGS+= $(FREETYPE_TARGET) 120 | xmoji_LIBS+= z 121 | xmoji_prebuild: $(FREETYPE_TARGET) 122 | else 123 | ifneq ($(FTLIBDIR),) 124 | xmoji_LDFLAGS+= -L$(FTLIBDIR) 125 | _xmoji_LINK+= $(FTLIBDIR)/libfreetype.so 126 | ifneq ($(FTINCDIR),) 127 | xmoji_CFLAGS+= -I$(FTINCDIR) 128 | endif 129 | else 130 | ifeq ($(WITH_SVG),1) 131 | xmoji_PKGDEPS+= freetype2 >= 24.2.18 132 | else 133 | xmoji_PKGDEPS+= freetype2 134 | endif 135 | endif 136 | endif 137 | 138 | ifeq ($(BUNDLED_POSER),1) 139 | xmoji_STATICDEPS+= posercore 140 | xmoji_PRECFLAGS+= -I./poser/include 141 | xmoji_LIBS+= posercore 142 | xmoji_LDFLAGS+= -pthread 143 | else 144 | xmoji_PKGDEPS+= posercore >= 1.2.2 145 | endif 146 | 147 | ifeq ($(WITH_NLS),1) 148 | xmoji_GEN+= EMOJINM EMOJITRANS TRANS 149 | xmoji_EMOJINM_FILES= translations/xmoji-emojis.def:$(xmoji_EMOJIDATA) 150 | xmoji_CLDR= contrib/cldr/annotations 151 | xmoji_cldrfiles= $(xmoji_CLDR)/$1.xml:$(xmoji_CLDR)Derived/$1.xml 152 | xmoji_emojitexts= translations/xmoji-emojis-$1.def:$(xmoji_EMOJIDATA) 153 | xmoji_emojitrans= $(call xmoji_emojitexts,$1):$(call xmoji_cldrfiles,$1) 154 | xmoji_EMOJITRANS_FILES= $(foreach l,$(xmoji_LANGUAGES),$(call \ 155 | xmoji_emojitrans,$l):translations/xmoji-emojis.def) 156 | xmoji_TRANS_FILES= $(foreach \ 157 | t,$(xmoji_TRANSLATIONS),$(foreach l,$(xmoji_LANGUAGES),\ 158 | translations/$t-$l.xct:translations/$t.def:translations/$t-$l.def)) 159 | xmoji_EXTRADIRS= trans 160 | xmoji_trans_FILES= $(filter %.xct,$(subst :, ,$(xmoji_TRANS_FILES))) 161 | xmoji_DEFINES+= -DWITH_NLS -DTRANSDIR=\"$(xmoji_transdir)\" 162 | xmoji_PREBUILD+= $(addprefix $(xmoji_SRCDIR)/,$(xmoji_trans_FILES)) 163 | endif 164 | 165 | ifeq ($(WITH_SVG),1) 166 | xmoji_MODULES+= nanosvg \ 167 | svghooks 168 | xmoji_DEFINES+= -DWITH_SVG 169 | endif 170 | 171 | $(call binrules,xmoji) 172 | 173 | xmoji_prebuild: $(xmoji_PREBUILD) 174 | update-translations: $(xmoji_SRCDIR)/$(xmoji_UITXT) $(XTC_TARGET) 175 | $(foreach l,$(xmoji_LANGUAGES),$(XTC_TARGET) update $l $<;) 176 | 177 | .PHONY: update-translations 178 | -------------------------------------------------------------------------------- /src/bin/xmoji/flyout.c: -------------------------------------------------------------------------------- 1 | #include "flyout.h" 2 | 3 | #include "window.h" 4 | 5 | #include 6 | #include 7 | 8 | C_CLASS_DECL(Font); 9 | 10 | static void destroy(void *obj); 11 | static void expose(void *obj, Rect region); 12 | static int draw(void *obj, xcb_render_picture_t picture); 13 | static int show(void *obj); 14 | static int hide(void *obj); 15 | static Size minSize(const void *obj); 16 | static void leave(void *obj); 17 | static void setFont(void *obj, Font *font); 18 | static Widget *childAt(void *obj, Pos pos); 19 | static int clicked(void *obj, const ClickEvent *event); 20 | 21 | static MetaFlyout mo = MetaFlyout_init( 22 | expose, draw, show, hide, 23 | 0, 0, 0, leave, 0, 0, 0, 0, setFont, childAt, 24 | minSize, 0, clicked, 0, 25 | "Flyout", destroy); 26 | 27 | static unsigned refcnt; 28 | static Window *window; 29 | 30 | struct Flyout 31 | { 32 | Object base; 33 | Widget *widget; 34 | int incborder; 35 | }; 36 | 37 | static void destroy(void *obj) 38 | { 39 | Flyout *self = obj; 40 | Object_destroy(self->widget); 41 | if (!--refcnt) Object_destroy(window); 42 | free(self); 43 | } 44 | 45 | static void expose(void *obj, Rect region) 46 | { 47 | Flyout *self = Object_instance(obj); 48 | if (self->widget) 49 | { 50 | Widget_invalidateRegion(self->widget, region); 51 | } 52 | } 53 | 54 | static int draw(void *obj, xcb_render_picture_t picture) 55 | { 56 | (void)picture; 57 | 58 | Flyout *self = Object_instance(obj); 59 | int rc = -1; 60 | if (self->widget) 61 | { 62 | rc = Widget_draw(self->widget); 63 | } 64 | return rc; 65 | } 66 | 67 | static int show(void *obj) 68 | { 69 | Flyout *self = Object_instance(obj); 70 | if (!window) return -1; 71 | int rc = 0; 72 | Object_bcall(rc, Widget, show, self); 73 | return rc < 0 ? rc : Widget_show(window); 74 | } 75 | 76 | static int hide(void *obj) 77 | { 78 | Flyout *self = Object_instance(obj); 79 | Window_close(window); 80 | int rc = 0; 81 | Object_bcall(rc, Widget, hide, self); 82 | return rc; 83 | } 84 | 85 | static Size minSize(const void *obj) 86 | { 87 | const Flyout *self = Object_instance(obj); 88 | if (self->widget) return Widget_minSize(self->widget); 89 | return (Size){0, 0}; 90 | } 91 | 92 | static void leave(void *obj) 93 | { 94 | Flyout *self = Object_instance(obj); 95 | if (self->widget) Widget_leave(self->widget); 96 | Window_close(window); 97 | } 98 | 99 | static void setFont(void *obj, Font *font) 100 | { 101 | Flyout *self = Object_instance(obj); 102 | if (self->widget) 103 | { 104 | Widget_offerFont(self->widget, font); 105 | } 106 | } 107 | 108 | static Widget *childAt(void *obj, Pos pos) 109 | { 110 | Flyout *self = Object_instance(obj); 111 | if (self->widget) return Widget_enterAt(self->widget, pos); 112 | return Widget_cast(self); 113 | } 114 | 115 | static int clicked(void *obj, const ClickEvent *event) 116 | { 117 | Flyout *self = Object_instance(obj); 118 | int handled = 0; 119 | if (self->widget) handled = Widget_clicked(self->widget, event); 120 | if (event->button == MB_LEFT) 121 | { 122 | Widget_hide(self); 123 | handled = 1; 124 | } 125 | return handled; 126 | } 127 | 128 | static void sizeChanged(void *receiver, void *sender, void *args) 129 | { 130 | (void)sender; 131 | 132 | Flyout *self = receiver; 133 | SizeChangedEventArgs *ea = args; 134 | 135 | if (self->widget) Widget_setSize(self->widget, ea->newSize); 136 | } 137 | 138 | Flyout *Flyout_createBase(void *derived, const char *name, void *parent) 139 | { 140 | Flyout *self = PSC_malloc(sizeof *self); 141 | CREATEBASE(Widget, name, parent); 142 | self->widget = 0; 143 | self->incborder = 0; 144 | 145 | Widget_setPadding(self, (Box){0, 0, 0, 0}); 146 | PSC_Event_register(Widget_sizeChanged(self), self, sizeChanged, 0); 147 | 148 | return self; 149 | } 150 | 151 | static void sizeRequested(void *receiver, void *sender, void *args) 152 | { 153 | (void)sender; 154 | (void)args; 155 | 156 | Widget_requestSize(receiver); 157 | } 158 | 159 | void Flyout_setIncBorder(void *self, int enable) 160 | { 161 | Flyout *f = Object_instance(self); 162 | f->incborder = enable; 163 | } 164 | 165 | void Flyout_setWidget(void *self, void *widget) 166 | { 167 | Flyout *f = Object_instance(self); 168 | if (f->widget) 169 | { 170 | Widget_setContainer(f->widget, 0); 171 | PSC_Event_unregister(Widget_sizeRequested(f->widget), self, 172 | sizeRequested, 0); 173 | Object_destroy(f->widget); 174 | } 175 | if (widget) 176 | { 177 | f->widget = Object_ref(Widget_cast(widget)); 178 | PSC_Event_register(Widget_sizeRequested(f->widget), self, 179 | sizeRequested, 0); 180 | Widget_setContainer(f->widget, f); 181 | } 182 | else f->widget = 0; 183 | } 184 | 185 | void Flyout_popup(void *self, void *widget) 186 | { 187 | Flyout *f = Object_instance(self); 188 | if (!f->widget) return; 189 | if (!window) 190 | { 191 | window = Window_create(0, 192 | WF_WINDOW_MENU|WF_POS_PARENTWIDGET|WF_ALWAYS_CLASS, 0); 193 | } 194 | Widget_setContainer(window, widget); 195 | Window_setMainWidget(window, f); 196 | if (f->incborder) Window_addFlags(window, WF_POS_INCBORDER); 197 | else Window_removeFlags(window, WF_POS_INCBORDER); 198 | Widget_show(f); 199 | } 200 | -------------------------------------------------------------------------------- /src/bin/xmoji/textlabel.c: -------------------------------------------------------------------------------- 1 | #include "textlabel.h" 2 | 3 | #include "font.h" 4 | #include "textrenderer.h" 5 | #include "unistr.h" 6 | 7 | #include 8 | #include 9 | 10 | static void destroy(void *obj); 11 | static int draw(void *obj, xcb_render_picture_t picture); 12 | static void setFont(void *obj, Font *font); 13 | static Size minSize(const void *obj); 14 | 15 | static MetaTextLabel mo = MetaTextLabel_init( 16 | 0, draw, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, setFont, 0, minSize, 0, 0, 0, 17 | "TextLabel", destroy); 18 | 19 | #define dummy ((void *)&mo) 20 | 21 | struct TextLabel 22 | { 23 | Object base; 24 | UniStr *text; 25 | PSC_List *renderers; 26 | RenderCallback callback; 27 | void *renderctx; 28 | Size minSize; 29 | ColorRole color; 30 | int underline; 31 | }; 32 | 33 | static void freerenderer(void *renderer) 34 | { 35 | TextRenderer_destroy(renderer); 36 | } 37 | 38 | static void update(TextLabel *self, Font *font) 39 | { 40 | PSC_List_destroy(self->renderers); 41 | self->renderers = 0; 42 | self->minSize = (Size){0, 0}; 43 | if (self->text) 44 | { 45 | self->renderers = PSC_List_create(); 46 | PSC_List *lines = UniStr_split(self->text, U"\n"); 47 | PSC_ListIterator *i = PSC_List_iterator(lines); 48 | while (PSC_ListIterator_moveNext(i)) 49 | { 50 | const UniStr *line = PSC_ListIterator_current(i); 51 | if (UniStr_len(line)) 52 | { 53 | TextRenderer *renderer = TextRenderer_create(self->base.base); 54 | TextRenderer_setFont(renderer, font); 55 | PSC_List_append(self->renderers, renderer, freerenderer); 56 | TextRenderer_setText(renderer, line); 57 | if (self->callback) self->callback(self->renderctx, renderer); 58 | Size linesize = TextRenderer_size(renderer); 59 | if (!self->minSize.height) 60 | { 61 | self->minSize.height = linesize.height; 62 | } 63 | if (linesize.width > self->minSize.width) 64 | { 65 | self->minSize.width = linesize.width; 66 | } 67 | } 68 | else PSC_List_append(self->renderers, dummy, 0); 69 | } 70 | PSC_ListIterator_destroy(i); 71 | PSC_List_destroy(lines); 72 | size_t nlines = PSC_List_size(self->renderers); 73 | if (nlines > 1) 74 | { 75 | self->minSize.height += (nlines - 1) * Font_linespace(font); 76 | } 77 | } 78 | Widget_requestSize(self); 79 | } 80 | 81 | static void destroy(void *obj) 82 | { 83 | TextLabel *self = obj; 84 | PSC_List_destroy(self->renderers); 85 | UniStr_destroy(self->text); 86 | free(self); 87 | } 88 | 89 | static int draw(void *obj, xcb_render_picture_t picture) 90 | { 91 | TextLabel *self = Object_instance(obj); 92 | if (!picture || !self->text) return 0; 93 | Font *font = Widget_font(self); 94 | if (!font) return 0; 95 | Color color = Widget_color(self, self->color); 96 | Pos pos = Widget_contentOrigin(self, self->minSize); 97 | Align align = Widget_align(self); 98 | uint16_t linespace = Font_linespace(font); 99 | int rc = 0; 100 | for (size_t i = 0; rc >= 0 && i < PSC_List_size(self->renderers); ++i) 101 | { 102 | TextRenderer *r = PSC_List_at(self->renderers, i); 103 | if (r != dummy) 104 | { 105 | Size lineSize = TextRenderer_size(r); 106 | Pos linePos = pos; 107 | int lineMargin = self->minSize.width - lineSize.width; 108 | if (lineMargin > 0) 109 | { 110 | if (align & AH_RIGHT) linePos.x += lineMargin; 111 | else if (align & AH_CENTER) linePos.x += lineMargin / 2; 112 | } 113 | TextRenderer_setUnderline(r, self->underline); 114 | rc = TextRenderer_render(r, picture, color, linePos); 115 | } 116 | pos.y += linespace; 117 | } 118 | return rc; 119 | } 120 | 121 | static void setFont(void *obj, Font *font) 122 | { 123 | update(Object_instance(obj), font); 124 | } 125 | 126 | static Size minSize(const void *obj) 127 | { 128 | const TextLabel *self = Object_instance(obj); 129 | return self->minSize; 130 | } 131 | 132 | TextLabel *TextLabel_createBase(void *derived, const char *name, void *parent) 133 | { 134 | TextLabel *self = PSC_malloc(sizeof *self); 135 | CREATEBASE(Widget, name, parent); 136 | self->text = 0; 137 | self->renderers = PSC_List_create(); 138 | self->callback = 0; 139 | self->renderctx = 0; 140 | self->minSize = (Size){0, 0}; 141 | self->color = COLOR_NORMAL; 142 | self->underline = 0; 143 | 144 | return self; 145 | } 146 | 147 | const UniStr *TextLabel_text(const void *self) 148 | { 149 | TextLabel *l = Object_instance(self); 150 | return l->text; 151 | } 152 | 153 | void TextLabel_setText(void *self, const UniStr *text) 154 | { 155 | TextLabel *l = Object_instance(self); 156 | UniStr_destroy(l->text); 157 | l->text = UniStr_ref(text); 158 | Font *font = Widget_font(l); 159 | if (font) update(l, font); 160 | } 161 | 162 | void TextLabel_setColor(void *self, ColorRole color) 163 | { 164 | TextLabel *l = Object_instance(self); 165 | if (l->color != color) 166 | { 167 | l->color = color; 168 | Widget_invalidate(l); 169 | } 170 | } 171 | 172 | void TextLabel_setUnderline(void *self, int underline) 173 | { 174 | TextLabel *l = Object_instance(self); 175 | if (l->underline != underline) 176 | { 177 | l->underline = underline; 178 | Widget_invalidate(l); 179 | } 180 | } 181 | 182 | void TextLabel_setRenderCallback(void *self, void *ctx, 183 | RenderCallback callback) 184 | { 185 | TextLabel *l = Object_instance(self); 186 | l->callback = callback; 187 | l->renderctx = ctx; 188 | } 189 | 190 | -------------------------------------------------------------------------------- /src/bin/xmoji/svghooks.c: -------------------------------------------------------------------------------- 1 | #include "svghooks.h" 2 | 3 | #include "nanosvg.h" 4 | 5 | #include 6 | #include 7 | #include FT_OTSVG_H 8 | #include 9 | 10 | typedef struct SvgGlyph 11 | { 12 | NSVGimage *svg; 13 | uint32_t scale; 14 | float xoff; 15 | float yoff; 16 | } SvgGlyph; 17 | 18 | typedef struct SvgRenderer 19 | { 20 | NSVGrasterizer *rast; 21 | SvgGlyph *glyph; 22 | } SvgRenderer; 23 | 24 | static void destroyGlyph(SvgGlyph *self) 25 | { 26 | if (!self) return; 27 | nsvgDelete(self->svg); 28 | free(self); 29 | } 30 | 31 | static void destroyRenderer(SvgRenderer *self) 32 | { 33 | if (!self) return; 34 | destroyGlyph(self->glyph); 35 | nsvgDeleteRasterizer(self->rast); 36 | free(self); 37 | } 38 | 39 | static FT_Error init_svg(FT_Pointer *data_pointer) 40 | { 41 | SvgRenderer *renderer = PSC_malloc(sizeof *renderer); 42 | renderer->rast = nsvgCreateRasterizer(); 43 | renderer->glyph = 0; 44 | *data_pointer = renderer; 45 | return FT_Err_Ok; 46 | } 47 | 48 | static void free_svg(FT_Pointer *data_pointer) 49 | { 50 | destroyRenderer(*data_pointer); 51 | *data_pointer = 0; 52 | } 53 | 54 | static FT_Error render_svg(FT_GlyphSlot slot, FT_Pointer *data_pointer) 55 | { 56 | SvgRenderer *renderer = *data_pointer; 57 | SvgGlyph *glyph = renderer->glyph; 58 | if (!glyph) return FT_Err_Invalid_SVG_Document; 59 | float scale = (float)glyph->scale / (float)(1<<22); 60 | nsvgRasterize(renderer->rast, glyph->svg, glyph->xoff * scale, 61 | glyph->yoff * scale, scale, slot->bitmap.buffer, 62 | slot->bitmap.width, slot->bitmap.rows, slot->bitmap.pitch); 63 | for (unsigned y = 0; y < slot->bitmap.rows; ++y) 64 | { 65 | uint8_t *row = slot->bitmap.buffer + y * slot->bitmap.pitch; 66 | for (unsigned x = 0; x < slot->bitmap.width; ++x) 67 | { 68 | uint8_t alpha = row[4*x+3]; 69 | if (alpha) 70 | { 71 | uint8_t red = alpha * row[4*x] / 0xffU; 72 | uint8_t blue = alpha * row[4*x+2] / 0xffU; 73 | row[4*x] = blue; 74 | row[4*x+1] = alpha * row[4*x+1] / 0xffU; 75 | row[4*x+2] = red; 76 | } 77 | else 78 | { 79 | row[4*x] = 0; 80 | row[4*x+1] = 0; 81 | row[4*x+2] = 0; 82 | } 83 | } 84 | } 85 | slot->format = FT_GLYPH_FORMAT_BITMAP; 86 | return FT_Err_Ok; 87 | } 88 | 89 | static FT_Error preset_slot(FT_GlyphSlot slot, 90 | FT_Bool cache, FT_Pointer *data_pointer) 91 | { 92 | FT_SVG_Document doc = (FT_SVG_Document)slot->other; 93 | if (doc->start_glyph_id != doc->end_glyph_id) 94 | { 95 | return FT_Err_Unimplemented_Feature; 96 | } 97 | 98 | char *svgstr = PSC_malloc(doc->svg_document_length + 1); 99 | memcpy(svgstr, doc->svg_document, doc->svg_document_length); 100 | svgstr[doc->svg_document_length] = 0; 101 | NSVGimage *svg = nsvgParse(svgstr, "px", 0.); 102 | free(svgstr); 103 | if (!svg) return FT_Err_Invalid_SVG_Document; 104 | 105 | SvgGlyph glyph; 106 | glyph.svg = svg; 107 | 108 | float xmin = HUGE_VALF; 109 | float ymin = HUGE_VALF; 110 | float xmax = -HUGE_VALF; 111 | float ymax = -HUGE_VALF; 112 | for (const struct NSVGshape *shape = svg->shapes; shape; 113 | shape = shape->next) 114 | { 115 | if (shape->bounds[0] < xmin) xmin = shape->bounds[0]; 116 | if (shape->bounds[1] < ymin) ymin = shape->bounds[1]; 117 | if (shape->bounds[2] > xmax) xmax = shape->bounds[2]; 118 | if (shape->bounds[3] > ymax) ymax = shape->bounds[3]; 119 | } 120 | uint16_t svgwidth = ceilf(xmax - xmin); 121 | uint16_t svgheight = ceilf(ymax - ymin); 122 | if (!svgwidth || !svgheight) 123 | { 124 | svgwidth = doc->units_per_EM; 125 | svgheight = doc->units_per_EM; 126 | } 127 | glyph.scale = doc->metrics.x_scale < doc->metrics.y_scale 128 | ? doc->metrics.x_scale : doc->metrics.y_scale; 129 | glyph.xoff = -xmin; 130 | glyph.yoff = -ymin; 131 | 132 | uint32_t width = FT_MulFix(svgwidth, glyph.scale); 133 | uint32_t height = FT_MulFix(svgheight, glyph.scale); 134 | 135 | slot->bitmap.rows = (height + 0x3fU) >> 6; 136 | slot->bitmap.width = (width + 0x3fU) >> 6; 137 | slot->bitmap.pitch = slot->bitmap.width << 2; 138 | slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA; 139 | slot->bitmap.num_grays = 256; 140 | slot->bitmap_left = slot->metrics.horiAdvance > width ? 141 | (slot->metrics.horiAdvance - width) >> 7 : 0; 142 | slot->bitmap_top = (doc->metrics.height + 143 | doc->metrics.descender + 0x7f) >> 6; 144 | if (slot->bitmap_top > (int)slot->bitmap.rows) 145 | { 146 | slot->bitmap_top = slot->bitmap.rows; 147 | } 148 | slot->metrics.width = width; 149 | slot->metrics.height = height; 150 | slot->metrics.horiBearingX = slot->bitmap_left * 64; 151 | slot->metrics.horiBearingY = -slot->bitmap_top * 64; 152 | slot->metrics.vertBearingX = -slot->metrics.horiAdvance * 32; 153 | slot->metrics.vertBearingY = 154 | (slot->metrics.vertAdvance - slot->metrics.height) *32; 155 | 156 | if (cache) 157 | { 158 | SvgRenderer *renderer = *data_pointer; 159 | destroyGlyph(renderer->glyph); 160 | renderer->glyph = PSC_malloc(sizeof *renderer->glyph); 161 | memcpy(renderer->glyph, &glyph, sizeof *renderer->glyph); 162 | } 163 | return FT_Err_Ok; 164 | } 165 | 166 | static const SVG_RendererHooks svghooks = { 167 | .init_svg = init_svg, 168 | .free_svg = free_svg, 169 | .render_svg = render_svg, 170 | .preset_slot = preset_slot 171 | }; 172 | 173 | const void *SvgHooks_get(void) 174 | { 175 | return &svghooks; 176 | } 177 | 178 | -------------------------------------------------------------------------------- /src/bin/xmoji/pixmap.c: -------------------------------------------------------------------------------- 1 | #include "pixmap.h" 2 | 3 | #include "x11adapter.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | struct Pixmap 12 | { 13 | unsigned char *data; 14 | size_t datasz; 15 | Size size; 16 | xcb_pixmap_t pixmap; 17 | xcb_render_picture_t picture; 18 | unsigned refcnt; 19 | }; 20 | 21 | Pixmap *Pixmap_createFromPng(const unsigned char *data, size_t datasz) 22 | { 23 | png_image png; 24 | png_bytep buffer = 0; 25 | memset(&png, 0, sizeof png); 26 | 27 | png.version = PNG_IMAGE_VERSION; 28 | if (!png_image_begin_read_from_memory(&png, data, datasz)) goto error; 29 | size_t bufsz = PNG_IMAGE_SIZE(png); 30 | buffer = PSC_malloc(bufsz); 31 | png.format = PNG_FORMAT_BGRA; 32 | if (!png_image_finish_read(&png, 0, buffer, 0, 0)) goto error; 33 | 34 | Pixmap *self = PSC_malloc(sizeof *self); 35 | self->data = buffer; 36 | self->datasz = bufsz; 37 | self->size = (Size){png.width, png.height}; 38 | self->pixmap = 0; 39 | self->picture = 0; 40 | self->refcnt = 1; 41 | 42 | png_image_free(&png); 43 | return self; 44 | 45 | error: 46 | free(buffer); 47 | png_image_free(&png); 48 | return 0; 49 | } 50 | 51 | Pixmap *Pixmap_ref(Pixmap *pixmap) 52 | { 53 | ++pixmap->refcnt; 54 | return pixmap; 55 | } 56 | 57 | Size Pixmap_size(const Pixmap *self) 58 | { 59 | return self->size; 60 | } 61 | 62 | const unsigned char *Pixmap_bitmap(const Pixmap *self, size_t *size) 63 | { 64 | if (size) *size = self->datasz; 65 | return self->data; 66 | } 67 | 68 | void Pixmap_render(Pixmap *self, xcb_render_picture_t picture, Pos pos) 69 | { 70 | xcb_connection_t *c = X11Adapter_connection(); 71 | xcb_screen_t *s = X11Adapter_screen(); 72 | if (!self->pixmap) 73 | { 74 | xcb_image_t *imgbgr = xcb_image_create_native(c, self->size.width, 75 | self->size.height, XCB_IMAGE_FORMAT_Z_PIXMAP, 24, 0, 0, 0); 76 | if (!imgbgr) return; 77 | xcb_image_t *imgalpha = xcb_image_create_native(c, self->size.width, 78 | self->size.height, XCB_IMAGE_FORMAT_Z_PIXMAP, 8, 0, 0, 0); 79 | if (!imgalpha) 80 | { 81 | xcb_image_destroy(imgbgr); 82 | return; 83 | } 84 | for (uint16_t y = 0; y < self->size.height; ++y) 85 | { 86 | const unsigned char *inrow = self->data + y * self->size.width * 4; 87 | uint8_t *outrow = imgbgr->data + y * imgbgr->stride; 88 | uint8_t *outalpha = imgalpha->data + y * imgalpha->stride; 89 | for (uint16_t x = 0; x < self->size.width; ++x) 90 | { 91 | outrow[4*x] = inrow[4*x]; 92 | outrow[4*x+1] = inrow[4*x+1]; 93 | outrow[4*x+2] = inrow[4*x+2]; 94 | outalpha[x] = inrow[4*x+3]; 95 | } 96 | } 97 | xcb_pixmap_t pmbgr = xcb_generate_id(c); 98 | CHECK(xcb_create_pixmap(c, 24, pmbgr, s->root, 99 | self->size.width, self->size.height), 100 | "Cannot create tmp bgr pixmap for 0x%x", (unsigned)picture); 101 | xcb_pixmap_t pmalpha = xcb_generate_id(c); 102 | CHECK(xcb_create_pixmap(c, 8, pmalpha, s->root, 103 | self->size.width, self->size.height), 104 | "Cannot create tmp alpha pixmap for 0x%x", (unsigned)picture); 105 | xcb_gcontext_t gcbgr = xcb_generate_id(c); 106 | CHECK(xcb_create_gc(c, gcbgr, pmbgr, 0, 0), 107 | "Cannot create graphics context for 0x%x", (unsigned)picture); 108 | xcb_gcontext_t gcalpha = xcb_generate_id(c); 109 | CHECK(xcb_create_gc(c, gcalpha, pmalpha, 0, 0), 110 | "Cannot create graphics context for 0x%x", (unsigned)picture); 111 | CHECK(xcb_image_put(c, pmbgr, gcbgr, imgbgr, 0, 0, 0), 112 | "Cannot upload rgb image data for 0x%x", (unsigned)picture); 113 | CHECK(xcb_image_put(c, pmalpha, gcalpha, imgalpha, 0, 0, 0), 114 | "Cannot upload alpha image data for 0x%x", (unsigned)picture); 115 | xcb_free_gc(c, gcalpha); 116 | xcb_free_gc(c, gcbgr); 117 | xcb_image_destroy(imgalpha); 118 | xcb_image_destroy(imgbgr); 119 | xcb_render_picture_t picbgr = xcb_generate_id(c); 120 | CHECK(xcb_render_create_picture(c, picbgr, pmbgr, 121 | X11Adapter_format(PICTFORMAT_RGB), 0, 0), 122 | "Cannot create tmp bgr picture for 0x%x", (unsigned)picture); 123 | xcb_render_picture_t picalpha = xcb_generate_id(c); 124 | CHECK(xcb_render_create_picture(c, picalpha, pmalpha, 125 | X11Adapter_format(PICTFORMAT_ALPHA), 0, 0), 126 | "Cannot create tmp alpha picture for 0x%x", (unsigned)picture); 127 | self->pixmap = xcb_generate_id(c); 128 | CHECK(xcb_create_pixmap(c, 32, self->pixmap, s->root, 129 | self->size.width, self->size.height), 130 | "Cannot create pixmap for 0x%x", (unsigned)picture); 131 | self->picture = xcb_generate_id(c); 132 | CHECK(xcb_render_create_picture(c, self->picture, self->pixmap, 133 | X11Adapter_format(PICTFORMAT_ARGB), 0, 0), 134 | "Cannot create picture for 0x%x", (unsigned)picture); 135 | CHECK(xcb_render_composite(c, XCB_RENDER_PICT_OP_SRC, 136 | picbgr, picalpha, self->picture, 0, 0, 0, 0, 0, 0, 137 | self->size.width, self->size.height), 138 | "Cannot compose picture for 0x%x", (unsigned)picture); 139 | xcb_render_free_picture(c, picalpha); 140 | xcb_render_free_picture(c, picbgr); 141 | xcb_free_pixmap(c, pmalpha); 142 | xcb_free_pixmap(c, pmbgr); 143 | } 144 | CHECK(xcb_render_composite(c, XCB_RENDER_PICT_OP_OVER, self->picture, 0, 145 | picture, 0, 0, 0, 0, pos.x, pos.y, 146 | self->size.width, self->size.height), 147 | "Cannot render pixmap to 0x%x", (unsigned)picture); 148 | } 149 | 150 | void Pixmap_destroy(Pixmap *self) 151 | { 152 | if (!self) return; 153 | if (--self->refcnt) return; 154 | if (self->pixmap) 155 | { 156 | xcb_connection_t *c = X11Adapter_connection(); 157 | xcb_render_free_picture(c, self->picture); 158 | xcb_free_pixmap(c, self->pixmap); 159 | } 160 | free(self->data); 161 | free(self); 162 | } 163 | 164 | -------------------------------------------------------------------------------- /src/bin/xmoji/surface.c: -------------------------------------------------------------------------------- 1 | #include "surface.h" 2 | 3 | #include 4 | #include 5 | 6 | static void destroy(void *obj); 7 | static void expose(void *obj, Rect region); 8 | static int draw(void *obj, xcb_render_picture_t picture); 9 | static Size minSize(const void *obj); 10 | static void leave(void *obj); 11 | static void unselect(void *obj); 12 | static void setFont(void *obj, Font *font); 13 | static Widget *childAt(void *obj, Pos pos); 14 | static int clicked(void *obj, const ClickEvent *event); 15 | 16 | static MetaSurface mo = MetaSurface_init( 17 | expose, draw, 0, 0, 18 | 0, 0, 0, leave, 0, 0, 0, unselect, setFont, childAt, 19 | minSize, 0, clicked, 0, 20 | "Surface", destroy); 21 | 22 | struct Surface 23 | { 24 | Object base; 25 | Widget *widget; 26 | xcb_pixmap_t p; 27 | xcb_render_picture_t pic; 28 | }; 29 | 30 | static void destroy(void *obj) 31 | { 32 | Surface *self = obj; 33 | Object_destroy(self->widget); 34 | if (self->p) 35 | { 36 | xcb_connection_t *c = X11Adapter_connection(); 37 | xcb_render_free_picture(c, self->pic); 38 | xcb_free_pixmap(c, self->p); 39 | } 40 | free(self); 41 | } 42 | 43 | static void expose(void *obj, Rect region) 44 | { 45 | Surface *self = Object_instance(obj); 46 | if (self->widget) Widget_invalidateRegion(self->widget, region); 47 | Widget *container = Widget_container(self); 48 | if (container) 49 | { 50 | Pos offset = Widget_offset(self); 51 | region.pos.x += offset.x; 52 | region.pos.y += offset.y; 53 | Widget_invalidateRegion(container, region); 54 | } 55 | } 56 | 57 | static int draw(void *obj, xcb_render_picture_t picture) 58 | { 59 | (void)picture; 60 | 61 | Surface *self = Object_instance(obj); 62 | int rc = 0; 63 | if (self->widget) rc = Widget_draw(self->widget); 64 | return rc; 65 | } 66 | 67 | static Size minSize(const void *obj) 68 | { 69 | const Surface *self = Object_instance(obj); 70 | return self->widget ? Widget_minSize(self->widget) : (Size){ 0, 0 }; 71 | } 72 | 73 | static void leave(void *obj) 74 | { 75 | Surface *self = Object_instance(obj); 76 | if (self->widget) Widget_leave(self->widget); 77 | } 78 | 79 | static void unselect(void *obj) 80 | { 81 | Surface *self = Object_instance(obj); 82 | if (self->widget) Widget_unselect(self->widget); 83 | } 84 | 85 | static void setFont(void *obj, Font *font) 86 | { 87 | Surface *self = Object_instance(obj); 88 | if (self->widget) Widget_offerFont(self->widget, font); 89 | } 90 | 91 | static Widget *childAt(void *obj, Pos pos) 92 | { 93 | Surface *self = Object_instance(obj); 94 | return self->widget 95 | ? Widget_enterAt(self->widget, pos) : Widget_cast(self); 96 | } 97 | 98 | static int clicked(void *obj, const ClickEvent *event) 99 | { 100 | const Surface *self = Object_instance(obj); 101 | return self->widget ? Widget_clicked(self->widget, event) : 0; 102 | } 103 | 104 | static void sizeChanged(void *receiver, void *sender, void *args) 105 | { 106 | (void)sender; 107 | 108 | Surface *self = receiver; 109 | SizeChangedEventArgs *ea = args; 110 | 111 | if (ea->newSize.width > 8192 || ea->newSize.height > 8192) return; 112 | xcb_connection_t *c = 0; 113 | if (ea->newSize.width > ea->oldSize.width 114 | || ea->newSize.height > ea->oldSize.height) 115 | { 116 | if (self->p) 117 | { 118 | c = X11Adapter_connection(); 119 | xcb_render_free_picture(c, self->pic); 120 | xcb_free_pixmap(c, self->p); 121 | } 122 | self->p = 0; 123 | self->pic = 0; 124 | } 125 | 126 | if (ea->newSize.width && ea->newSize.height && !self->p) 127 | { 128 | if (!c) c = X11Adapter_connection(); 129 | self->p = xcb_generate_id(c); 130 | CHECK(xcb_create_pixmap(c, 24, self->p, X11Adapter_screen()->root, 131 | ea->newSize.width, ea->newSize.height), 132 | "Cannot create backing store pixmap for 0x%x", 133 | (unsigned)Widget_picture(self)); 134 | Widget_setDrawable(self, self->p); 135 | self->pic = xcb_generate_id(c); 136 | CHECK(xcb_render_create_picture(c, self->pic, self->p, 137 | X11Adapter_format(PICTFORMAT_RGB), 0, 0), 138 | "Cannot create backing store picture for 0x%x", 139 | (unsigned)Widget_picture(self)); 140 | } 141 | 142 | if (self->widget) Widget_setSize(self->widget, ea->newSize); 143 | } 144 | 145 | static void sizeRequested(void *receiver, void *sender, void *args) 146 | { 147 | (void)sender; 148 | (void)args; 149 | 150 | Widget_requestSize(receiver); 151 | } 152 | 153 | Surface *Surface_createBase(void *derived, void *parent) 154 | { 155 | Surface *self = PSC_malloc(sizeof *self); 156 | CREATEBASE(Widget, 0, parent); 157 | self->widget = 0; 158 | self->p = 0; 159 | 160 | Widget_setPadding(self, (Box){ 0, 0, 0, 0 }); 161 | Widget_setBackground(self, 1, COLOR_BG_NORMAL); 162 | PSC_Event_register(Widget_sizeChanged(self), self, sizeChanged, 0); 163 | 164 | return self; 165 | } 166 | 167 | void Surface_setWidget(void *self, void *widget) 168 | { 169 | Surface *s = Object_instance(self); 170 | if (s->widget) 171 | { 172 | PSC_Event_unregister(Widget_sizeRequested(s->widget), s, 173 | sizeRequested, 0); 174 | Widget_setContainer(s->widget, 0); 175 | Object_destroy(s->widget); 176 | } 177 | if (widget) 178 | { 179 | s->widget = Object_ref(Widget_cast(widget)); 180 | Widget_setContainer(s->widget, s); 181 | Font *font = Widget_font(s); 182 | if (font) Widget_offerFont(s->widget, font); 183 | PSC_Event_register(Widget_sizeRequested(s->widget), s, 184 | sizeRequested, 0); 185 | sizeRequested(s, 0, 0); 186 | } 187 | else s->widget = 0; 188 | } 189 | 190 | void Surface_render(void *self, xcb_render_picture_t picture, 191 | Pos pos, Rect rect) 192 | { 193 | Surface *s = Object_instance(self); 194 | CHECK(xcb_render_composite(X11Adapter_connection(), XCB_RENDER_PICT_OP_SRC, 195 | s->pic, 0, picture, rect.pos.x, rect.pos.y, 0, 0, 196 | pos.x, pos.y, rect.size.width, rect.size.height), 197 | "Cannot render surface to 0x%x", (unsigned)picture); 198 | } 199 | -------------------------------------------------------------------------------- /tools/xtc/src/deffile.c: -------------------------------------------------------------------------------- 1 | #include "deffile.h" 2 | 3 | #include "util.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #define CHUNKSZ 128 10 | 11 | struct DefEntry 12 | { 13 | char *key; 14 | char *from; 15 | char *to; 16 | DefType type; 17 | unsigned id; 18 | unsigned next; 19 | }; 20 | 21 | struct DefFile 22 | { 23 | size_t len; 24 | DefEntry *entries; 25 | unsigned bucket[256]; 26 | }; 27 | 28 | static unsigned char hash(const char *key) 29 | { 30 | size_t h = 5381; 31 | while (*key) h += (h << 5) + ((unsigned char)*key++); 32 | return h; 33 | } 34 | 35 | DefFile *DefFile_create(const char *filename) 36 | { 37 | FILE *f = fopen(filename, "r"); 38 | if (!f) 39 | { 40 | fprintf(stderr, "Error opening `%s' for reading\n", filename); 41 | return 0; 42 | } 43 | 44 | enum { PS_START, PS_KEY, PS_FROM, PS_TO } parsestate = PS_START; 45 | static char buf[1024]; 46 | char *key = 0; 47 | char *from = 0; 48 | char *to = 0; 49 | size_t keylen = 0; 50 | size_t fromlen = 0; 51 | size_t tolen = 0; 52 | size_t linelen = 0; 53 | size_t newlen = 0; 54 | DefType type = DT_CHAR; 55 | int haveeol = 0; 56 | int hadeol = 0; 57 | int firstline = 0; 58 | 59 | DefFile *self = xmalloc(sizeof *self); 60 | memset(self, 0, sizeof *self); 61 | size_t capa = 0; 62 | 63 | for (unsigned lineno = 0; fgets(buf, sizeof buf, f); lineno += haveeol) 64 | { 65 | hadeol = haveeol; 66 | char *nl = strchr(buf, '\n'); 67 | haveeol = !!nl; 68 | *nl = 0; 69 | 70 | switch (parsestate) 71 | { 72 | case PS_START: 73 | if (buf[0] != '$') continue; 74 | if (buf[2] != '$') 75 | { 76 | fprintf(stderr, "%s:%u: malformed line\n", 77 | filename, lineno); 78 | goto error; 79 | } 80 | switch (buf[1]) 81 | { 82 | case 'c': 83 | type = DT_CHAR; break; 84 | case 'w': 85 | type = DT_CHAR32; break; 86 | default: 87 | fprintf(stderr, "%s:%u: malformed line\n", 88 | filename, lineno); 89 | goto error; 90 | } 91 | keylen = strlen(buf+3); 92 | if (keylen) 93 | { 94 | key = xmalloc(keylen+1); 95 | memcpy(key, buf+3, keylen+1); 96 | } 97 | else 98 | { 99 | fprintf(stderr, "%s:%u: missing key\n", filename, lineno); 100 | goto error; 101 | } 102 | parsestate = haveeol ? PS_FROM : PS_KEY; 103 | firstline = 1; 104 | break; 105 | 106 | case PS_KEY: 107 | linelen = strlen(buf); 108 | newlen = keylen + linelen; 109 | if (newlen != keylen) 110 | { 111 | key = xrealloc(key, newlen+1); 112 | memcpy(key+keylen, buf, linelen+1); 113 | keylen = newlen; 114 | } 115 | if (haveeol) parsestate = PS_FROM; 116 | break; 117 | 118 | case PS_FROM: 119 | if (hadeol && buf[0] == '.' && !buf[1]) 120 | { 121 | parsestate = PS_TO; 122 | firstline = 1; 123 | continue; 124 | } 125 | if (firstline) hadeol = 0; 126 | firstline = 0; 127 | linelen = strlen(buf); 128 | newlen = fromlen + linelen + hadeol; 129 | if (newlen == fromlen) continue; 130 | from = xrealloc(from, newlen+1); 131 | if (hadeol) from[fromlen++] = '\n'; 132 | memcpy(from+fromlen, buf, linelen+1); 133 | fromlen = newlen; 134 | break; 135 | 136 | case PS_TO: 137 | if (hadeol && buf[0] == '.' && !buf[1]) 138 | { 139 | if (self->len == capa) 140 | { 141 | capa += CHUNKSZ; 142 | self->entries = xrealloc(self->entries, 143 | capa * sizeof *self->entries); 144 | } 145 | DefEntry *entry = self->entries + self->len; 146 | unsigned char bucket = hash(key); 147 | entry->next = self->bucket[bucket]; 148 | entry->key = key; 149 | entry->from = from; 150 | entry->to = to; 151 | entry->type = type; 152 | entry->id = self->len++; 153 | self->bucket[bucket] = self->len; 154 | key = 0; 155 | from = 0; 156 | to = 0; 157 | fromlen = 0; 158 | tolen = 0; 159 | parsestate = PS_START; 160 | continue; 161 | } 162 | if (firstline) hadeol = 0; 163 | firstline = 0; 164 | linelen = strlen(buf); 165 | newlen = tolen + linelen + hadeol; 166 | if (newlen == tolen) continue; 167 | to = xrealloc(to, newlen+1); 168 | if (hadeol) to[tolen++] = '\n'; 169 | memcpy(to+tolen, buf, linelen+1); 170 | tolen = newlen; 171 | break; 172 | } 173 | } 174 | if (parsestate != PS_START) 175 | { 176 | fprintf(stderr, "%s: unexpected end of file", filename); 177 | goto error; 178 | } 179 | fclose(f); 180 | self->entries = xrealloc(self->entries, self->len * sizeof *self->entries); 181 | return self; 182 | 183 | error: 184 | fclose(f); 185 | free(to); 186 | free(from); 187 | DefFile_destroy(self); 188 | return 0; 189 | } 190 | 191 | size_t DefFile_len(const DefFile *self) 192 | { 193 | return self->len; 194 | } 195 | 196 | const DefEntry *DefFile_byId(const DefFile *self, unsigned id) 197 | { 198 | if (id > self->len) return 0; 199 | return self->entries + id; 200 | } 201 | 202 | const DefEntry *DefFile_byKey(const DefFile *self, const char *key) 203 | { 204 | unsigned id = self->bucket[hash(key)]; 205 | while (id) 206 | { 207 | const DefEntry *entry = DefFile_byId(self, id - 1); 208 | if (!entry) return 0; 209 | if (!strcmp(entry->key, key)) return entry; 210 | id = entry->next; 211 | } 212 | return 0; 213 | } 214 | 215 | const char *DefEntry_key(const DefEntry *self) 216 | { 217 | return self->key; 218 | } 219 | 220 | const char *DefEntry_from(const DefEntry *self) 221 | { 222 | return self->from; 223 | } 224 | 225 | const char *DefEntry_to(const DefEntry *self) 226 | { 227 | return self->to; 228 | } 229 | 230 | DefType DefEntry_type(const DefEntry *self) 231 | { 232 | return self->type; 233 | } 234 | 235 | unsigned DefEntry_id(const DefEntry *self) 236 | { 237 | return self->id; 238 | } 239 | 240 | void DefFile_destroy(DefFile *self) 241 | { 242 | if (!self) return; 243 | free(self->entries); 244 | free(self); 245 | } 246 | 247 | -------------------------------------------------------------------------------- /src/bin/xmoji/keyinjector.c: -------------------------------------------------------------------------------- 1 | #include "keyinjector.h" 2 | 3 | #include "unistr.h" 4 | #include "x11adapter.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define MAXQUEUELEN 64 12 | 13 | typedef struct InjectJob 14 | { 15 | UniStr *str; 16 | xcb_keysym_t *orig; 17 | unsigned len; 18 | unsigned symspercode; 19 | } InjectJob; 20 | 21 | static PSC_Timer *before; 22 | static PSC_Timer *after; 23 | static InjectorFlags injectflags; 24 | 25 | static InjectJob queue[MAXQUEUELEN]; 26 | static unsigned queuelen; 27 | static unsigned queuefront; 28 | static unsigned queueback; 29 | 30 | static void finish(void); 31 | static void resetkmap(void *receiver, void *sender, void *args); 32 | static void fakekeys(void *receiver, void *sender, void *args); 33 | static void doinject(void *obj, unsigned sequence, 34 | void *reply, xcb_generic_error_t *error); 35 | static void injectnext(void); 36 | 37 | static void finish(void) 38 | { 39 | if (++queuefront == MAXQUEUELEN) queuefront = 0; 40 | --queuelen; 41 | injectnext(); 42 | } 43 | 44 | static void resetkmap(void *receiver, void *sender, void *args) 45 | { 46 | (void)receiver; 47 | (void)sender; 48 | (void)args; 49 | 50 | xcb_connection_t *c = X11Adapter_connection(); 51 | const xcb_setup_t *setup = xcb_get_setup(c); 52 | CHECK(xcb_change_keyboard_mapping(c, queue[queuefront].len, 53 | setup->min_keycode, queue[queuefront].symspercode, 54 | queue[queuefront].orig), 55 | "KeyInjector: Cannot change keymap", 0); 56 | free(queue[queuefront].orig); 57 | finish(); 58 | } 59 | 60 | static void fakekeys(void *receiver, void *sender, void *args) 61 | { 62 | (void)receiver; 63 | (void)sender; 64 | (void)args; 65 | 66 | xcb_connection_t *c = X11Adapter_connection(); 67 | const xcb_setup_t *setup = xcb_get_setup(c); 68 | for (unsigned x = 0; x < queue[queuefront].len; ++x) 69 | { 70 | CHECK(xcb_test_fake_input(c, XCB_KEY_PRESS, setup->min_keycode + x, 71 | XCB_CURRENT_TIME, XCB_NONE, 0, 0, 0), 72 | "KeyInjector: Cannot inject fake key press event", 0); 73 | CHECK(xcb_test_fake_input(c, XCB_KEY_RELEASE, setup->min_keycode + x, 74 | XCB_CURRENT_TIME, XCB_NONE, 0, 0, 0), 75 | "KeyInjector: Cannot inject fake key release event", 0); 76 | } 77 | 78 | PSC_Timer_start(after, 0); 79 | } 80 | 81 | static void doinject(void *obj, unsigned sequence, 82 | void *reply, xcb_generic_error_t *error) 83 | { 84 | (void)obj; 85 | (void)sequence; 86 | 87 | if (!reply || error) 88 | { 89 | finish(); 90 | return; 91 | } 92 | 93 | xcb_get_keyboard_mapping_reply_t *kmap = reply; 94 | size_t len = UniStr_len(queue[queuefront].str); 95 | const char32_t *codepoints = UniStr_str(queue[queuefront].str); 96 | int zwj = 0; 97 | unsigned prelen = 0; 98 | unsigned postlen = 0; 99 | if (injectflags & IF_EXTRAZWJ) 100 | { 101 | for (unsigned x = 0; x < len; ++x) 102 | { 103 | if (codepoints[x] == 0x200d) 104 | { 105 | zwj = 1; 106 | break; 107 | } 108 | } 109 | if (zwj) 110 | { 111 | len += 2; 112 | ++prelen; 113 | if (!(injectflags & (IF_ADDSPACE|IF_ADDZWSPACE))) ++prelen; 114 | } 115 | } 116 | if (injectflags & (IF_ADDSPACE|IF_ADDZWSPACE)) 117 | { 118 | ++postlen; 119 | if (!zwj) ++len; 120 | } 121 | 122 | queue[queuefront].orig = PSC_malloc(len 123 | * kmap->keysyms_per_keycode * sizeof *queue[queuefront].orig); 124 | memcpy(queue[queuefront].orig, xcb_get_keyboard_mapping_keysyms(kmap), 125 | len * kmap->keysyms_per_keycode * sizeof *queue[queuefront].orig); 126 | xcb_keysym_t *syms = PSC_malloc(len 127 | * kmap->keysyms_per_keycode * sizeof *syms); 128 | unsigned z = 0; 129 | static const char32_t zw[] = { 0x200b, 0x200d }; 130 | for (unsigned x = 0; x < len; ++x) 131 | { 132 | char32_t codepoint; 133 | if (x < prelen) codepoint = *(zw + x + (2 - prelen)); 134 | else if (x < len - postlen) codepoint = codepoints[x-prelen]; 135 | else codepoint = (injectflags & IF_ADDSPACE) ? 0x20 : *zw; 136 | for (size_t y = 0; y < kmap->keysyms_per_keycode; ++y) 137 | { 138 | syms[z++] = codepoint > 0xff ? 0x1000000U + codepoint : codepoint; 139 | } 140 | } 141 | 142 | xcb_connection_t *c = X11Adapter_connection(); 143 | const xcb_setup_t *setup = xcb_get_setup(c); 144 | CHECK(xcb_change_keyboard_mapping(c, len, setup->min_keycode, 145 | kmap->keysyms_per_keycode, syms), 146 | "KeyInjector: Cannot change keymap", 0); 147 | free(syms); 148 | UniStr_destroy(queue[queuefront].str); 149 | queue[queuefront].len = len; 150 | queue[queuefront].symspercode = kmap->keysyms_per_keycode; 151 | 152 | if (before) PSC_Timer_start(before, 0); 153 | else fakekeys(0, 0, 0); 154 | } 155 | 156 | static void injectnext(void) 157 | { 158 | if (!queuelen) return; 159 | 160 | xcb_connection_t *c = X11Adapter_connection(); 161 | const xcb_setup_t *setup = xcb_get_setup(c); 162 | AWAIT(xcb_get_keyboard_mapping(c, setup->min_keycode, 163 | setup->max_keycode - setup->min_keycode + 1), 164 | 0, doinject); 165 | } 166 | 167 | void KeyInjector_init(unsigned beforems, unsigned afterms, InjectorFlags flags) 168 | { 169 | if (beforems) 170 | { 171 | if (!before) 172 | { 173 | before = PSC_Timer_create(); 174 | PSC_Event_register(PSC_Timer_expired(before), 0, fakekeys, 0); 175 | } 176 | PSC_Timer_setMs(before, beforems); 177 | } 178 | else if (before) 179 | { 180 | PSC_Timer_destroy(before); 181 | before = 0; 182 | } 183 | if (!after) 184 | { 185 | after = PSC_Timer_create(); 186 | PSC_Event_register(PSC_Timer_expired(after), 0, resetkmap, 0); 187 | } 188 | PSC_Timer_setMs(after, afterms); 189 | injectflags = flags; 190 | } 191 | 192 | void KeyInjector_inject(const UniStr *str) 193 | { 194 | if (queuelen == MAXQUEUELEN) return; 195 | queue[queueback].str = UniStr_ref(str); 196 | if (++queueback == MAXQUEUELEN) queueback = 0; 197 | if (!queuelen++) injectnext(); 198 | } 199 | 200 | void KeyInjector_done(void) 201 | { 202 | if (!after) return; 203 | PSC_Timer_destroy(after); 204 | after = 0; 205 | PSC_Timer_destroy(before); 206 | before = 0; 207 | } 208 | 209 | -------------------------------------------------------------------------------- /tools/emojigen/src/translate.c: -------------------------------------------------------------------------------- 1 | #include "translate.h" 2 | 3 | #include "emojireader.h" 4 | #include "util.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | typedef struct TranslationEntry 11 | { 12 | char32_t *emoji; 13 | char *text; 14 | } TranslationEntry; 15 | 16 | typedef struct TranslationBucket 17 | { 18 | size_t capa; 19 | size_t size; 20 | TranslationEntry **entries; 21 | } TranslationBucket; 22 | 23 | static int hashstr(const char32_t *s) 24 | { 25 | size_t h = 5381; 26 | while (*s) h += (h<<5) + *s++; 27 | return h & 0x3ffU; 28 | } 29 | 30 | static size_t fromutf8(char32_t *ucs4, size_t sz, const char *utf8) 31 | { 32 | const unsigned char *c = (const unsigned char *)utf8; 33 | size_t i = 0; 34 | 35 | while (*c && i < sz) 36 | { 37 | if (*c < 0x80) 38 | { 39 | ucs4[i++] = *c++; 40 | continue; 41 | } 42 | char32_t u = 0; 43 | int f = 0; 44 | if ((*c & 0xe0) == 0xc0) 45 | { 46 | u = (*c & 0x1f); 47 | f = 1; 48 | } 49 | else if ((*c & 0xf0) == 0xe0) 50 | { 51 | u = (*c & 0xf); 52 | f = 2; 53 | } 54 | else if ((*c & 0xf8) == 0xf0) 55 | { 56 | u = (*c & 0x7); 57 | f = 3; 58 | } 59 | else return 0; 60 | for (; f && *++c; --f) 61 | { 62 | if ((*c & 0xc0) != 0x80) return 0; 63 | u <<= 6; 64 | u |= (*c & 0x3f); 65 | } 66 | if (f) return 0; 67 | ucs4[i++] = u; 68 | ++c; 69 | } 70 | if (i == sz) return 0; 71 | ucs4[i++] = 0; 72 | return i; 73 | } 74 | 75 | #define isws(c) (*(c) == ' ' || *(c) == '\t') 76 | #define skipws(c) do { while (isws(c)) ++c; } while (0) 77 | #define match(c, s) (!strncmp((c), (s), sizeof(s)-1) ? ((c)+=sizeof(s)-1) : 0) 78 | 79 | static void readTranslations(TranslationBucket *buckets, FILE *in) 80 | { 81 | static char line[1024]; 82 | static char utf8[48]; 83 | static char32_t ucs4[16]; 84 | 85 | while (fgets(line, sizeof line, in)) 86 | { 87 | char *c = line; 88 | skipws(c); 89 | if (!match(c, " 47) continue; 97 | memcpy(utf8, c, utf8len); 98 | utf8[utf8len] = 0; 99 | c = e+1; 100 | skipws(c); 101 | if (!match(c, "type=\"tts\"")) continue; 102 | skipws(c); 103 | if (match(c, "draft=\"contributed\"")) skipws(c); 104 | if (*c != '>') continue; 105 | e = ++c; 106 | while (*e && *e != '<') ++e; 107 | if (*e != '<') continue; 108 | *e = 0; 109 | size_t cplen = fromutf8(ucs4, sizeof ucs4 / sizeof *ucs4, utf8); 110 | if (!cplen) continue; 111 | TranslationEntry *entry = xmalloc(sizeof *entry); 112 | entry->emoji = xmalloc(cplen * sizeof *entry->emoji); 113 | memcpy(entry->emoji, ucs4, cplen * sizeof *entry->emoji); 114 | size_t textlen = (size_t)(e - c + 1); 115 | entry->text = xmalloc(textlen); 116 | memcpy(entry->text, c, textlen); 117 | int hash = hashstr(entry->emoji); 118 | TranslationBucket *bucket = buckets + hash; 119 | if (bucket->capa == bucket->size) 120 | { 121 | bucket->capa += 16; 122 | bucket->entries = xrealloc(bucket->entries, 123 | bucket->capa * sizeof *bucket->entries); 124 | } 125 | bucket->entries[bucket->size++] = entry; 126 | } 127 | } 128 | 129 | static int cpequals(const char32_t *a, const char32_t *b) 130 | { 131 | while (*a == *b) 132 | { 133 | if (!*a) return 1; 134 | ++a; 135 | ++b; 136 | } 137 | return 0; 138 | } 139 | 140 | static void stripqualifiers(char32_t *dst, const char32_t *src, size_t sz) 141 | { 142 | while ((*dst = *src)) 143 | { 144 | if (!--sz) { *dst = 0; break; } 145 | ++src; 146 | if (*dst != 0xfe0e && *dst != 0xfe0f) ++dst; 147 | } 148 | } 149 | 150 | static const char *gettranslation( 151 | TranslationBucket *buckets, const Emoji *emoji) 152 | { 153 | char32_t codepoints[16]; 154 | stripqualifiers(codepoints, emoji->codepoints, 16); 155 | int hash = hashstr(codepoints); 156 | const char *result = 0; 157 | TranslationBucket *bucket = buckets + hash; 158 | for (size_t i = 0; i < bucket->size; ++i) 159 | { 160 | if (cpequals(codepoints, bucket->entries[i]->emoji)) 161 | { 162 | result = bucket->entries[i]->text; 163 | break; 164 | } 165 | } 166 | return result; 167 | } 168 | 169 | int dotranslate(int argc, char **argv) 170 | { 171 | if (argc < 5) usage(argv[0]); 172 | int rc = EXIT_FAILURE; 173 | FILE *out = 0; 174 | FILE *in[16] = { 0 }; 175 | TranslationBucket translations[1024] = { {0, 0, 0} }; 176 | 177 | if (argc - 4 > (int)(sizeof in / sizeof *in)) 178 | { 179 | fputs("Too many input files given\n", stderr); 180 | goto done; 181 | } 182 | if (readEmojis(argv[3]) < 0) 183 | { 184 | fprintf(stderr, "Cannot read emojis from `%s'\n", argv[3]); 185 | goto done; 186 | } 187 | for (int i = 0; i < argc - 4; ++i) 188 | { 189 | in[i] = fopen(argv[i+4], "r"); 190 | if (!in[i]) 191 | { 192 | fprintf(stderr, "Cannot open `%s' for reading\n", argv[i+4]); 193 | goto done; 194 | } 195 | } 196 | out = fopen(argv[2], "w"); 197 | if (!out) 198 | { 199 | fprintf(stderr, "Cannot open `%s' for writing\n", argv[2]); 200 | goto done; 201 | } 202 | 203 | for (int i = 0; i < argc - 4; ++i) readTranslations(translations, in[i]); 204 | 205 | size_t emojisize = Emoji_count(); 206 | for (size_t i = 0; i < emojisize; ++i) 207 | { 208 | const Emoji *emoji = Emoji_at(i); 209 | const char *text = gettranslation(translations, emoji); 210 | if (text) fprintf(out, "$w$emoji%zu\n%s\n.\n%s\n.\n\n", 211 | i, emoji->name, text); 212 | else fprintf(out, "$w$emoji%zu\n%s\n.\n.\n\n", i, emoji->name); 213 | } 214 | rc = EXIT_SUCCESS; 215 | 216 | done: 217 | for (size_t i = 0; i < sizeof translations / sizeof *translations; ++i) 218 | { 219 | if (!translations[i].size) continue; 220 | for (size_t j = 0; j < translations[i].size; ++j) 221 | { 222 | free(translations[i].entries[j]->text); 223 | free(translations[i].entries[j]->emoji); 224 | free(translations[i].entries[j]); 225 | } 226 | free(translations[i].entries); 227 | } 228 | if (out) fclose(out); 229 | for (int i = 0; i < argc -4; ++i) if (in[i]) fclose(in[i]); 230 | emojisDone(); 231 | return rc; 232 | } 233 | 234 | -------------------------------------------------------------------------------- /src/bin/xmoji/button.c: -------------------------------------------------------------------------------- 1 | #include "button.h" 2 | 3 | #include "command.h" 4 | #include "font.h" 5 | #include "textlabel.h" 6 | 7 | #include 8 | #include 9 | 10 | static void destroy(void *obj); 11 | static void expose(void *obj, Rect region); 12 | static int draw(void *obj, xcb_render_picture_t picture); 13 | static void enter(void *obj); 14 | static void leave(void *obj); 15 | static void setFont(void *obj, Font *font); 16 | static int clicked(void *obj, const ClickEvent *event); 17 | static Size minSize(const void *obj); 18 | 19 | static MetaButton mo = MetaButton_init( 20 | expose, draw, 0, 0, 21 | 0, 0, enter, leave, 0, 0, 0, 0, setFont, 22 | 0, minSize, 0, clicked, 0, 23 | "Button", destroy); 24 | 25 | struct Button 26 | { 27 | Object base; 28 | TextLabel *label; 29 | PSC_Event *clicked; 30 | ColorRole color; 31 | ColorRole background; 32 | ColorRole hoverBackground; 33 | uint16_t minwidth; 34 | uint8_t borderwidth; 35 | }; 36 | 37 | static void destroy(void *obj) 38 | { 39 | Button *self = obj; 40 | PSC_Event_destroy(self->clicked); 41 | free(self); 42 | } 43 | 44 | static void expose(void *obj, Rect region) 45 | { 46 | Button *self = Object_instance(obj); 47 | Widget_invalidateRegion(self->label, region); 48 | } 49 | 50 | static int draw(void *obj, xcb_render_picture_t picture) 51 | { 52 | Button *self = Object_instance(obj); 53 | if (picture) 54 | { 55 | Rect geom = Widget_geometry(self->label); 56 | xcb_rectangle_t rect = { geom.pos.x, geom.pos.y, 57 | geom.size.width, geom.size.height }; 58 | Color color = Widget_color(self, self->color); 59 | CHECK(xcb_render_fill_rectangles(X11Adapter_connection(), 60 | XCB_RENDER_PICT_OP_OVER, picture, Color_xcb(color), 61 | 1, &rect), 62 | "Cannot draw button background on 0x%x", (unsigned)picture); 63 | } 64 | return Widget_draw(self->label); 65 | } 66 | 67 | static void enter(void *obj) 68 | { 69 | Button *self = Object_instance(obj); 70 | if (self->color != self->hoverBackground) 71 | { 72 | self->color = self->hoverBackground; 73 | Widget_invalidate(self); 74 | } 75 | } 76 | 77 | static void leave(void *obj) 78 | { 79 | Button *self = Object_instance(obj); 80 | if (self->color != self->background) 81 | { 82 | self->color = self->background; 83 | Widget_invalidate(self); 84 | } 85 | } 86 | 87 | static void setFont(void *obj, Font *font) 88 | { 89 | const Button *self = Object_instance(obj); 90 | Widget_setFont(self->label, font); 91 | } 92 | 93 | static int clicked(void *obj, const ClickEvent *event) 94 | { 95 | if (event->button == MB_LEFT) 96 | { 97 | Button *self = Object_instance(obj); 98 | PSC_Event_raise(self->clicked, 0, 0); 99 | return 1; 100 | } 101 | return 0; 102 | } 103 | 104 | static Size minSize(const void *obj) 105 | { 106 | Button *self = Object_instance(obj); 107 | Size minSize = Widget_minSize(self->label); 108 | minSize.width += 2 * self->borderwidth; 109 | minSize.height += 2 * self->borderwidth; 110 | 111 | if (minSize.width < self->minwidth) minSize.width = self->minwidth; 112 | return minSize; 113 | } 114 | 115 | static void sizeRequested(void *receiver, void *sender, void *args) 116 | { 117 | (void)sender; 118 | (void)args; 119 | 120 | Widget_requestSize(receiver); 121 | } 122 | 123 | static void sizeChanged(void *receiver, void *sender, void *args) 124 | { 125 | (void)sender; 126 | 127 | Button *self = receiver; 128 | SizeChangedEventArgs *ea = args; 129 | Size size = ea->newSize; 130 | size.width -= 2 * self->borderwidth; 131 | size.height -= 2 * self->borderwidth; 132 | Widget_setSize(self->label, size); 133 | } 134 | 135 | static void originChanged(void *receiver, void *sender, void *args) 136 | { 137 | (void)sender; 138 | 139 | Button *self = receiver; 140 | OriginChangedEventArgs *ea = args; 141 | Pos origin = ea->newOrigin; 142 | origin.x += self->borderwidth; 143 | origin.y += self->borderwidth; 144 | Widget_setOrigin(self->label, origin); 145 | } 146 | 147 | Button *Button_createBase(void *derived, const char *name, void *parent) 148 | { 149 | Button *self = PSC_malloc(sizeof *self); 150 | CREATEBASE(Widget, name, parent); 151 | self->label = TextLabel_create(0, self); 152 | self->clicked = PSC_Event_create(self); 153 | self->color = COLOR_BG_ABOVE; 154 | self->background = COLOR_BG_ABOVE; 155 | self->hoverBackground = COLOR_BG_ACTIVE; 156 | self->minwidth = 120; 157 | self->borderwidth = 1; 158 | 159 | Widget_setBackground(self, 1, COLOR_BORDER); 160 | Widget_setPadding(self, (Box){1, 1, 1, 1}); 161 | Widget_setExpand(self, EXPAND_NONE); 162 | Widget_setCursor(self, XC_HAND); 163 | Widget_setAlign(self->label, AH_CENTER|AV_MIDDLE); 164 | Widget_setContainer(self->label, self); 165 | Widget_show(self->label); 166 | 167 | PSC_Event_register(Widget_sizeChanged(self), self, sizeChanged, 0); 168 | PSC_Event_register(Widget_originChanged(self), self, originChanged, 0); 169 | 170 | PSC_Event_register(Widget_sizeRequested(self->label), self, 171 | sizeRequested, 0); 172 | 173 | return self; 174 | } 175 | 176 | PSC_Event *Button_clicked(void *self) 177 | { 178 | Button *b = Object_instance(self); 179 | return b->clicked; 180 | } 181 | 182 | TextLabel *Button_label(void *self) 183 | { 184 | Button *b = Object_instance(self); 185 | return b->label; 186 | } 187 | 188 | const UniStr *Button_text(const void *self) 189 | { 190 | const Button *b = Object_instance(self); 191 | return TextLabel_text(b->label); 192 | } 193 | 194 | void Button_setText(void *self, const UniStr *text) 195 | { 196 | Button *b = Object_instance(self); 197 | TextLabel_setText(b->label, text); 198 | } 199 | 200 | void Button_setBorderWidth(void *self, uint8_t width) 201 | { 202 | Button *b = Object_instance(self); 203 | if (width == b->borderwidth) return; 204 | if (!b->borderwidth) Widget_setBackground(b, 1, COLOR_BORDER); 205 | else if (!width) Widget_setBackground(b, 0, 0); 206 | b->borderwidth = width; 207 | Pos origin = Widget_origin(b); 208 | origin.x += width; 209 | origin.y += width; 210 | Widget_setOrigin(b->label, origin); 211 | Widget_requestSize(self); 212 | } 213 | 214 | void Button_setColors(void *self, ColorRole normal, ColorRole hover) 215 | { 216 | Button *b = Object_instance(self); 217 | b->color = normal; 218 | b->background = normal; 219 | b->hoverBackground = hover; 220 | } 221 | 222 | void Button_setLabelPadding(void *self, Box padding) 223 | { 224 | Button *b = Object_instance(self); 225 | Widget_setPadding(b->label, padding); 226 | } 227 | 228 | void Button_setLabelAlign(void *self, Align align) 229 | { 230 | Button *b = Object_instance(self); 231 | Widget_setAlign(b->label, align); 232 | } 233 | 234 | void Button_setMinWidth(void *self, uint16_t width) 235 | { 236 | Button *b = Object_instance(self); 237 | b->minwidth = width; 238 | } 239 | 240 | void Button_attachCommand(void *self, Command *command) 241 | { 242 | Button *b = Object_instance(self); 243 | const UniStr *name = Command_name(command); 244 | const UniStr *description = Command_description(command); 245 | Command_attach(command, b, Button_clicked); 246 | if (name) TextLabel_setText(b->label, name); 247 | if (description) Widget_setTooltip(b, description, 0); 248 | } 249 | 250 | -------------------------------------------------------------------------------- /src/bin/xmoji/dropdown.c: -------------------------------------------------------------------------------- 1 | #include "dropdown.h" 2 | 3 | #include "flyout.h" 4 | #include "pen.h" 5 | #include "shape.h" 6 | #include "vbox.h" 7 | 8 | #include 9 | #include 10 | 11 | static void destroy(void *obj); 12 | static int draw(void *obj, xcb_render_picture_t picture); 13 | static void setFont(void *obj, Font *font); 14 | static Size minSize(const void *obj); 15 | static int clicked(void *obj, const ClickEvent *event); 16 | 17 | static MetaDropdown mo = MetaDropdown_init( 18 | 0, draw, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, setFont, 19 | 0, minSize, 0, clicked, 0, 20 | "Dropdown", destroy); 21 | 22 | struct Dropdown 23 | { 24 | Object base; 25 | PSC_Event *selected; 26 | Pen *pen; 27 | Shape *arrow; 28 | VBox *box; 29 | Flyout *flyout; 30 | Size minSize; 31 | unsigned index; 32 | }; 33 | 34 | static void destroy(void *obj) 35 | { 36 | Dropdown *self = obj; 37 | Shape_destroy(self->arrow); 38 | Pen_destroy(self->pen); 39 | PSC_Event_destroy(self->selected); 40 | free(self); 41 | } 42 | 43 | static xcb_render_picture_t renderArrow(void *obj, 44 | xcb_render_picture_t ownerpic, const void *data) 45 | { 46 | Dropdown *self = obj; 47 | const Size *sz = data; 48 | 49 | xcb_connection_t *c = X11Adapter_connection(); 50 | xcb_screen_t *s = X11Adapter_screen(); 51 | 52 | xcb_pixmap_t tmp = xcb_generate_id(c); 53 | CHECK(xcb_create_pixmap(c, 8, tmp, s->root, sz->height, sz->height), 54 | "Cannot create arrow pixmap for 0x%x", (unsigned)ownerpic); 55 | uint32_t pictopts[] = { 56 | XCB_RENDER_POLY_MODE_IMPRECISE, 57 | XCB_RENDER_POLY_EDGE_SMOOTH 58 | }; 59 | xcb_render_picture_t pic = xcb_generate_id(c); 60 | CHECK(xcb_render_create_picture(c, pic, tmp, 61 | X11Adapter_format(PICTFORMAT_ALPHA), 62 | XCB_RENDER_CP_POLY_MODE | XCB_RENDER_CP_POLY_EDGE, pictopts), 63 | "Cannot create arrow picture for 0x%x", (unsigned)ownerpic); 64 | xcb_free_pixmap(c, tmp); 65 | Color color = 0; 66 | xcb_rectangle_t rect = {0, 0, sz->height, sz->height}; 67 | CHECK(xcb_render_fill_rectangles(c, XCB_RENDER_PICT_OP_SRC, 68 | pic, Color_xcb(color), 1, &rect), 69 | "Cannot clear arrow picture for 0x%x", (unsigned)ownerpic); 70 | if (!self->pen) self->pen = Pen_create(); 71 | Pen_configure(self->pen, PICTFORMAT_ALPHA, 0xffffffff); 72 | xcb_render_triangle_t arr = { 73 | { (sz->height << 14), (sz->height << 14) + (sz->height << 13) }, 74 | { (sz->height << 15) + (sz->height << 14), 75 | (sz->height << 14) + (sz->height << 13) }, 76 | { (sz->height << 15), (sz->height << 15) + (sz->height << 13) } 77 | }; 78 | CHECK(xcb_render_triangles(c, XCB_RENDER_PICT_OP_OVER, 79 | Pen_picture(self->pen, ownerpic), pic, 0, 0, 0, 1, &arr), 80 | "Cannot render arrow for 0x%x", (unsigned)ownerpic); 81 | 82 | return pic; 83 | } 84 | static int draw(void *obj, xcb_render_picture_t picture) 85 | { 86 | Dropdown *self = Object_instance(obj); 87 | int rc = 0; 88 | Object_bcall(rc, Widget, draw, self, picture); 89 | if (rc < 0 || !picture) goto done; 90 | if (!self->arrow) 91 | { 92 | self->arrow = Shape_create(renderArrow, sizeof self->minSize, 93 | &self->minSize); 94 | Shape_render(self->arrow, self, picture); 95 | } 96 | if (!self->pen) self->pen = Pen_create(); 97 | Pen_configure(self->pen, PICTFORMAT_RGB, Widget_color(self, COLOR_NORMAL)); 98 | Rect geom = Widget_geometry(self); 99 | CHECK(xcb_render_composite(X11Adapter_connection(), 100 | XCB_RENDER_PICT_OP_OVER, Pen_picture(self->pen, picture), 101 | Shape_picture(self->arrow), picture, 0, 0, 0, 0, 102 | geom.pos.x + geom.size.width - geom.size.height, 103 | geom.pos.y, geom.size.height, geom.size.height), 104 | "Cannot composite arrow for 0x%x", (unsigned)picture); 105 | done: 106 | return rc; 107 | } 108 | 109 | static void setFont(void *obj, Font *font) 110 | { 111 | Dropdown *self = Object_instance(obj); 112 | Object_bcallv(Widget, setFont, self, font); 113 | if (self->box) Widget_setFont(self->box, font); 114 | } 115 | 116 | static Size minSize(const void *obj) 117 | { 118 | const Dropdown *self = Object_instance(obj); 119 | return self->minSize; 120 | } 121 | 122 | static int clicked(void *obj, const ClickEvent *event) 123 | { 124 | Dropdown *self = Object_instance(obj); 125 | if (self->flyout && event->button == MB_LEFT) 126 | { 127 | Flyout_popup(self->flyout, self); 128 | return 1; 129 | } 130 | return 0; 131 | } 132 | 133 | Dropdown *Dropdown_createBase(void *derived, const char *name, void *parent) 134 | { 135 | Dropdown *self = PSC_malloc(sizeof *self); 136 | CREATEBASE(Button, name, parent); 137 | self->selected = PSC_Event_create(self); 138 | self->pen = 0; 139 | self->arrow = 0; 140 | self->box = 0; 141 | self->flyout = 0; 142 | self->minSize = (Size){0, 0}; 143 | self->index = 0; 144 | 145 | Button_setLabelAlign(self, AV_MIDDLE); 146 | Widget_setPadding(self, (Box){0, 0, 0, 0}); 147 | Widget_setExpand(self, EXPAND_X); 148 | 149 | return self; 150 | } 151 | 152 | static void itemClicked(void *receiver, void *sender, void *args) 153 | { 154 | (void)args; 155 | 156 | Dropdown *self = receiver; 157 | int index = VBox_indexOf(self->box, sender); 158 | if (index >= 0 && (unsigned)index != self->index) 159 | { 160 | self->index = index; 161 | Button_setText(self, Button_text(sender)); 162 | Widget_hide(self->flyout); 163 | Widget_invalidate(self); 164 | PSC_Event_raise(self->selected, 0, &self->index); 165 | } 166 | } 167 | 168 | static void optionSizeReq(void *receiver, void *sender, void *args) 169 | { 170 | (void)args; 171 | 172 | Dropdown *self = receiver; 173 | Size minSz = Widget_minSize(sender); 174 | minSz.width += minSz.height; 175 | 176 | int changed = 0; 177 | if (minSz.width > self->minSize.width) 178 | { 179 | self->minSize.width = minSz.width; 180 | ++changed; 181 | } 182 | if (minSz.height > self->minSize.height) 183 | { 184 | self->minSize.height = minSz.height; 185 | ++changed; 186 | } 187 | if (changed) Widget_requestSize(self); 188 | } 189 | 190 | void Dropdown_addOption(void *self, const UniStr *name) 191 | { 192 | Dropdown *d = Object_instance(self); 193 | if (!d->flyout) 194 | { 195 | d->flyout = Flyout_create(0, d); 196 | Flyout_setIncBorder(d->flyout, 1); 197 | d->box = VBox_create(d); 198 | VBox_setSpacing(d->box, 0); 199 | Widget_setPadding(d->box, (Box){0, 0, 0, 0}); 200 | Widget_setFont(d->box, Widget_font(d)); 201 | Widget_show(d->box); 202 | Flyout_setWidget(d->flyout, d->box); 203 | Button_setText(d, name); 204 | } 205 | Button *b = Button_create(0, d->box); 206 | PSC_Event_register(Button_clicked(b), d, itemClicked, 0); 207 | Button_setText(b, name); 208 | Button_setBorderWidth(b, 0); 209 | Button_setLabelAlign(b, AV_MIDDLE); 210 | Button_setMinWidth(b, 0); 211 | Widget_setPadding(b, (Box){0, 0, 0, 0}); 212 | Widget_setExpand(b, EXPAND_X|EXPAND_Y); 213 | Widget_show(b); 214 | VBox_addWidget(d->box, b); 215 | PSC_Event_register(Widget_sizeRequested(b), d, optionSizeReq, 0); 216 | optionSizeReq(d, b, 0); 217 | } 218 | 219 | unsigned Dropdown_selectedIndex(const void *self) 220 | { 221 | const Dropdown *d = Object_instance(self); 222 | return d->index; 223 | } 224 | 225 | PSC_Event *Dropdown_selected(void *self) 226 | { 227 | Dropdown *d = Object_instance(self); 228 | return d->selected; 229 | } 230 | 231 | void Dropdown_select(void *self, unsigned index) 232 | { 233 | Dropdown *d = Object_instance(self); 234 | if (index == d->index) return; 235 | if (index >= VBox_rows(d->box)) return; 236 | d->index = index; 237 | Button_setText(self, Button_text(VBox_widget(d->box, index))); 238 | Widget_invalidate(self); 239 | } 240 | 241 | --------------------------------------------------------------------------------