popup: replace FreeType renderer with PangoCairo
This commit is contained in:
@@ -1,13 +1,17 @@
|
|||||||
FROM archlinux:base@sha256:b0deabeb3d283da2c7f7dbf0eea051b7b2cd0554e0b737cc457fd21683bdcdd1
|
FROM archlinux:base@sha256:b0deabeb3d283da2c7f7dbf0eea051b7b2cd0554e0b737cc457fd21683bdcdd1
|
||||||
|
|
||||||
RUN pacman -Syu --noconfirm --needed \
|
RUN pacman -Syu --noconfirm --needed \
|
||||||
|
cairo \
|
||||||
|
fontconfig \
|
||||||
diffutils \
|
diffutils \
|
||||||
freetype2 \
|
|
||||||
gcc \
|
gcc \
|
||||||
gtk3 \
|
gtk3 \
|
||||||
|
harfbuzz \
|
||||||
libxcb \
|
libxcb \
|
||||||
libxkbcommon \
|
libxkbcommon \
|
||||||
make \
|
make \
|
||||||
|
noto-fonts-emoji \
|
||||||
|
pango \
|
||||||
pkgconf \
|
pkgconf \
|
||||||
plan9port \
|
plan9port \
|
||||||
python \
|
python \
|
||||||
|
|||||||
8
Makefile
8
Makefile
@@ -2,8 +2,8 @@ CC = 9c
|
|||||||
LD = 9l
|
LD = 9l
|
||||||
PKG_CFLAGS = $(shell pkg-config --cflags dbus-1 xkbcommon)
|
PKG_CFLAGS = $(shell pkg-config --cflags dbus-1 xkbcommon)
|
||||||
PKG_LIBS = $(shell pkg-config --libs dbus-1 xkbcommon)
|
PKG_LIBS = $(shell pkg-config --libs dbus-1 xkbcommon)
|
||||||
FT_CFLAGS = $(shell pkg-config --cflags freetype2)
|
TEXT_CFLAGS = $(shell pkg-config --cflags 'pangocairo >= 1.56' 'cairo >= 1.18')
|
||||||
FT_LIBS = $(shell pkg-config --libs freetype2)
|
TEXT_LIBS = $(shell pkg-config --libs 'pangocairo >= 1.56' 'cairo >= 1.18')
|
||||||
CFLAGS = -Wall -Wextra -O2 -g $(PKG_CFLAGS)
|
CFLAGS = -Wall -Wextra -O2 -g $(PKG_CFLAGS)
|
||||||
PROG = strans
|
PROG = strans
|
||||||
DOCKER_IMAGE = strans-build
|
DOCKER_IMAGE = strans-build
|
||||||
@@ -17,10 +17,10 @@ OBJS = $(SRCS:.c=.o)
|
|||||||
all: $(PROG) xim gtk
|
all: $(PROG) xim gtk
|
||||||
|
|
||||||
$(PROG): $(OBJS)
|
$(PROG): $(OBJS)
|
||||||
$(LD) -o $@ $(OBJS) -lthread -lbio -lxcb $(PKG_LIBS) $(FT_LIBS)
|
$(LD) -o $@ $(OBJS) -lthread -lbio -lxcb $(PKG_LIBS) $(TEXT_LIBS)
|
||||||
|
|
||||||
$(OBJS): dat.h fn.h ipc.h
|
$(OBJS): dat.h fn.h ipc.h
|
||||||
font.o: CFLAGS += $(FT_CFLAGS)
|
font.o: CFLAGS += $(TEXT_CFLAGS)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(OBJS) $(PROG)
|
rm -f $(OBJS) $(PROG)
|
||||||
|
|||||||
15
README.md
15
README.md
@@ -51,15 +51,24 @@ dependencies listed in [`Dockerfile`](Dockerfile) are installed.
|
|||||||
|
|
||||||
## Run
|
## Run
|
||||||
|
|
||||||
The popup needs one to four scalable font files. No fonts are bundled.
|
The popup needs one to four font files. No fonts are bundled. Each file is
|
||||||
`run.sh` uses installed DejaVu, Jigmo, or Noto CJK fonts when available, or
|
validated and registered with Pango as an application font. Pango shapes the
|
||||||
accepts explicit files in fallback order:
|
complete string and chooses glyph fallback from those fonts and the normal
|
||||||
|
system fonts; command-line file order is not an exact per-glyph fallback
|
||||||
|
contract.
|
||||||
|
|
||||||
|
`run.sh` uses installed Noto Color Emoji, DejaVu, Jigmo, or Noto CJK fonts when
|
||||||
|
available, or accepts explicit files. Supply a color emoji font such as Noto
|
||||||
|
Color Emoji to render emoji in color:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
./run.sh
|
./run.sh
|
||||||
./run.sh /path/to/primary.ttf /path/to/fallback.ttc
|
./run.sh /path/to/primary.ttf /path/to/fallback.ttc
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The popup renderer requires Pango/PangoCairo 1.56 or newer and Cairo 1.18 or
|
||||||
|
newer.
|
||||||
|
|
||||||
The script starts the daemon and, when `DISPLAY` is set, the XIM frontend. It
|
The script starts the daemon and, when `DISPLAY` is set, the XIM frontend. It
|
||||||
restarts any existing `strans` processes owned by the current user.
|
restarts any existing `strans` processes owned by the current user.
|
||||||
|
|
||||||
|
|||||||
@@ -47,14 +47,20 @@ new SKK revision.
|
|||||||
## Fonts
|
## Fonts
|
||||||
|
|
||||||
No font binaries are bundled. The daemon receives one to four font file paths
|
No font binaries are bundled. The daemon receives one to four font file paths
|
||||||
on its command line and tries them in that order; it does not discover fonts by
|
on its command line, validates them, and registers them as Pango application
|
||||||
family name or directory contents. `font/PROVENANCE` retains the exact
|
fonts. Pango shapes each complete string and selects glyph fallback from the
|
||||||
checksums, source revisions, and license records for the three Noto faces that
|
registered fonts and the normal system font set. Exact command-line file order
|
||||||
were formerly stored in the repository.
|
is deliberately not promised: preserving it would require a separate
|
||||||
|
Fontconfig policy alongside Pango's fallback. `font/PROVENANCE` retains the
|
||||||
|
exact checksums, source revisions, and license records for the three Noto faces
|
||||||
|
that were formerly stored in the repository.
|
||||||
|
|
||||||
The Docker renderer tests obtain their fonts from signed Arch packages rather
|
The Docker renderer tests obtain their fonts from signed Arch packages rather
|
||||||
than the source checkout. On August 13, 2026 the tested package contract was:
|
than the source checkout. On August 14, 2026 the tested package contract was:
|
||||||
|
|
||||||
|
- `noto-fonts-emoji` `1:2.051-1`, providing
|
||||||
|
`/usr/share/fonts/noto/NotoColorEmoji.ttf` (SHA-256
|
||||||
|
`72a635cb3d2f3524c51620cdde406b217204e8a6a06c6a096ff8ed4b5fd6e27b`);
|
||||||
- `ttf-dejavu` `2.37+18+g9b5d1b2f-8`, providing
|
- `ttf-dejavu` `2.37+18+g9b5d1b2f-8`, providing
|
||||||
`/usr/share/fonts/TTF/DejaVuSans.ttf` (SHA-256
|
`/usr/share/fonts/TTF/DejaVuSans.ttf` (SHA-256
|
||||||
`6038a160b491e121c1f12c7bccb4a9c8730296e3adc1086a059404ed84b7451c`);
|
`6038a160b491e121c1f12c7bccb4a9c8730296e3adc1086a059404ed84b7451c`);
|
||||||
@@ -64,13 +70,14 @@ than the source checkout. On August 13, 2026 the tested package contract was:
|
|||||||
and `/usr/share/fonts/TTF/Jigmo2.ttf` (SHA-256
|
and `/usr/share/fonts/TTF/Jigmo2.ttf` (SHA-256
|
||||||
`5da3582efe77e22073b86b3b86b556d7111148a76b957cfb53318a91da2efff0`).
|
`5da3582efe77e22073b86b3b86b556d7111148a76b957cfb53318a91da2efff0`).
|
||||||
|
|
||||||
Pacman validates the package checksums and repository signatures. DejaVu Sans
|
Pacman validates the package checksums and repository signatures. Noto Color
|
||||||
supplies ASCII and the monochrome U+1F600 outline in the tests; Jigmo supplies
|
Emoji supplies the color and shaped-sequence coverage; DejaVu Sans supplies
|
||||||
Hangul and BMP CJK, and Jigmo2 supplies U+2000B. Their installed license files
|
ASCII, Jigmo supplies Hangul and BMP CJK, and Jigmo2 supplies U+2000B. Their
|
||||||
are `/usr/share/licenses/ttf-dejavu/LICENSE` and
|
installed license files are `/usr/share/licenses/noto-fonts-emoji/LICENSE`,
|
||||||
`/usr/share/licenses/ttf-jigmo/LICENSE.txt`; the packages identify their terms
|
`/usr/share/licenses/ttf-dejavu/LICENSE`, and
|
||||||
as the DejaVu custom license and CC0 1.0, respectively. These packages are
|
`/usr/share/licenses/ttf-jigmo/LICENSE.txt`. The packages identify their terms
|
||||||
test fixtures, not names or paths hard-coded into the daemon.
|
as OFL-1.1-no-RFN, the DejaVu custom license, and CC0 1.0, respectively. These
|
||||||
|
packages are test fixtures, not names or paths hard-coded into the daemon.
|
||||||
|
|
||||||
## Git submodules
|
## Git submodules
|
||||||
|
|
||||||
|
|||||||
6
fn.h
6
fn.h
@@ -28,7 +28,6 @@ void dictthread(void*);
|
|||||||
void dictlookup(Dictreq*, Dictres*);
|
void dictlookup(Dictreq*, Dictres*);
|
||||||
|
|
||||||
void drawthread(void*);
|
void drawthread(void*);
|
||||||
int popupcells(Rune*, int);
|
|
||||||
void popupposition(Caret*, int, int, int, int, int, int, int*, int*);
|
void popupposition(Caret*, int, int, int, int, int, int, int*, int*);
|
||||||
void imthread(void*);
|
void imthread(void*);
|
||||||
Emit transmap(Im*, Rune);
|
Emit transmap(Im*, Rune);
|
||||||
@@ -44,5 +43,6 @@ void ibusthread(void*);
|
|||||||
void* emalloc(ulong);
|
void* emalloc(ulong);
|
||||||
void* erealloc(void*, ulong);
|
void* erealloc(void*, ulong);
|
||||||
|
|
||||||
int fontinit(char**, int);
|
int textinit(char**, int);
|
||||||
void putfont(u32int*, int, int, int, int, Rune);
|
int textwidth(Str*);
|
||||||
|
void textdraw(u32int*, int, int, int, int, Str*);
|
||||||
|
|||||||
254
font.c
254
font.c
@@ -1,168 +1,166 @@
|
|||||||
#include "dat.h"
|
#include "dat.h"
|
||||||
#include <ft2build.h>
|
#include <pango/pangocairo.h>
|
||||||
#include FT_FREETYPE_H
|
|
||||||
#include "fn.h"
|
#include "fn.h"
|
||||||
|
|
||||||
typedef struct Font Font;
|
static PangoFontMap *fontmap;
|
||||||
struct Font
|
static PangoContext *context;
|
||||||
|
static PangoLayout *layout;
|
||||||
|
|
||||||
|
static void
|
||||||
|
textclear(void)
|
||||||
{
|
{
|
||||||
FT_Face face;
|
if(layout != nil)
|
||||||
int base;
|
g_object_unref(layout);
|
||||||
};
|
if(context != nil)
|
||||||
|
g_object_unref(context);
|
||||||
static FT_Library lib;
|
if(fontmap != nil)
|
||||||
static Font fonts[Maxfonts];
|
g_object_unref(fontmap);
|
||||||
static int nfonts;
|
layout = nil;
|
||||||
static u32int blendtab[2][256];
|
context = nil;
|
||||||
|
fontmap = nil;
|
||||||
static u32int
|
|
||||||
blend(u32int bg, u32int fg, int a)
|
|
||||||
{
|
|
||||||
int r, g, b, inv;
|
|
||||||
|
|
||||||
inv = 255 - a;
|
|
||||||
r = ((bg >> 16 & 0xff) * inv + (fg >> 16 & 0xff) * a) / 255;
|
|
||||||
g = ((bg >> 8 & 0xff) * inv + (fg >> 8 & 0xff) * a) / 255;
|
|
||||||
b = ((bg & 0xff) * inv + (fg & 0xff) * a) / 255;
|
|
||||||
return (r << 16) | (g << 8) | b;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
loadfont(char *path)
|
|
||||||
{
|
|
||||||
FT_Face face;
|
|
||||||
Font *f;
|
|
||||||
vlong height, pixels;
|
|
||||||
|
|
||||||
if(nfonts >= nelem(fonts))
|
|
||||||
return 0;
|
|
||||||
if(FT_New_Face(lib, path, 0, &face) != 0){
|
|
||||||
fprint(2, "strans: popup: can't load font: %s\n", path);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
height = (vlong)face->ascender - face->descender;
|
|
||||||
if(height <= 0 || face->units_per_EM == 0){
|
|
||||||
fprint(2, "strans: popup: invalid font metrics: %s\n", path);
|
|
||||||
FT_Done_Face(face);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
pixels = (vlong)Fontsz * face->units_per_EM / height;
|
|
||||||
if(pixels < 1)
|
|
||||||
pixels = 1;
|
|
||||||
if(FT_Set_Pixel_Sizes(face, 0, pixels) != 0){
|
|
||||||
fprint(2, "strans: popup: can't size font: %s\n", path);
|
|
||||||
FT_Done_Face(face);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
f = &fonts[nfonts];
|
|
||||||
f->face = face;
|
|
||||||
f->base = (vlong)Fontsz * face->ascender / height;
|
|
||||||
nfonts++;
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
clearfonts(void)
|
setfont(void)
|
||||||
{
|
{
|
||||||
int i;
|
PangoFontDescription *font;
|
||||||
|
PangoRectangle r;
|
||||||
|
int size;
|
||||||
|
|
||||||
for(i = 0; i < nfonts; i++){
|
font = pango_font_description_new();
|
||||||
FT_Done_Face(fonts[i].face);
|
pango_font_description_set_family(font, "sans");
|
||||||
fonts[i].face = nil;
|
pango_font_description_set_absolute_size(font, Fontsz * PANGO_SCALE);
|
||||||
|
pango_layout_set_font_description(layout, font);
|
||||||
|
pango_layout_set_text(layout, "Mg", -1);
|
||||||
|
pango_layout_get_pixel_extents(layout, nil, &r);
|
||||||
|
if(r.height > 0){
|
||||||
|
/* Fontsz is the popup row height, not a point size. */
|
||||||
|
size = Fontsz * PANGO_SCALE * Fontsz / r.height;
|
||||||
|
pango_font_description_set_absolute_size(font, max(size, PANGO_SCALE));
|
||||||
|
pango_layout_set_font_description(layout, font);
|
||||||
}
|
}
|
||||||
nfonts = 0;
|
pango_font_description_free(font);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
fontinit(char **path, int npath)
|
textinit(char **path, int npath)
|
||||||
{
|
{
|
||||||
int i, a;
|
GError *err;
|
||||||
|
int i;
|
||||||
|
|
||||||
if(path == nil || npath < 1 || npath > nelem(fonts)){
|
textclear();
|
||||||
fprint(2, "strans: popup: need 1-%d font files\n", nelem(fonts));
|
if(path == nil || npath < 1 || npath > Maxfonts){
|
||||||
clearfonts();
|
fprint(2, "strans: popup: need 1-%d font files\n", Maxfonts);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if(lib == nil && FT_Init_FreeType(&lib) != 0){
|
fontmap = pango_cairo_font_map_new();
|
||||||
fprint(2, "strans: popup: can't initialize FreeType\n");
|
if(fontmap == nil){
|
||||||
|
fprint(2, "strans: popup: can't initialize PangoCairo\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
clearfonts();
|
|
||||||
for(i = 0; i < npath; i++){
|
for(i = 0; i < npath; i++){
|
||||||
if(path[i] == nil || !loadfont(path[i])){
|
if(path[i] == nil){
|
||||||
clearfonts();
|
fprint(2, "strans: popup: invalid font file\n");
|
||||||
|
textclear();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
err = nil;
|
||||||
|
if(!pango_font_map_add_font_file(fontmap, path[i], &err)){
|
||||||
|
fprint(2, "strans: popup: can't register font: %s: %s\n",
|
||||||
|
path[i], err == nil ? "unknown error" : err->message);
|
||||||
|
if(err != nil)
|
||||||
|
g_error_free(err);
|
||||||
|
textclear();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for(a = 0; a < 256; a++){
|
pango_cairo_font_map_set_resolution(PANGO_CAIRO_FONT_MAP(fontmap), 96);
|
||||||
blendtab[0][a] = blend(Colbg, Colfg, a);
|
context = pango_font_map_create_context(fontmap);
|
||||||
blendtab[1][a] = blend(Colsel, Colfg, a);
|
if(context != nil)
|
||||||
|
layout = pango_layout_new(context);
|
||||||
|
if(context == nil || layout == nil){
|
||||||
|
fprint(2, "strans: popup: can't create text layout\n");
|
||||||
|
textclear();
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
pango_layout_set_single_paragraph_mode(layout, TRUE);
|
||||||
|
setfont();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
drawglyph(Font *f, u32int *buf, int w, int h, int px, int py)
|
settext(Str *s)
|
||||||
{
|
{
|
||||||
FT_Bitmap *b;
|
char utf[Maxutf];
|
||||||
FT_GlyphSlot g;
|
int n;
|
||||||
uchar *row;
|
|
||||||
vlong pitch, x0, x1, y0, y1;
|
|
||||||
int a, sel, x, xa, xb, y, ya, yb;
|
|
||||||
u32int *p;
|
|
||||||
|
|
||||||
g = f->face->glyph;
|
if(layout == nil || s == nil || s->n <= 0)
|
||||||
b = &g->bitmap;
|
|
||||||
if(b->pixel_mode != FT_PIXEL_MODE_GRAY || b->num_grays != 256)
|
|
||||||
return 0;
|
return 0;
|
||||||
if(b->width == 0 || b->rows == 0)
|
n = stoutf(s, utf, sizeof utf);
|
||||||
return 1;
|
pango_layout_set_text(layout, utf, n);
|
||||||
pitch = b->pitch;
|
return n > 0;
|
||||||
if(pitch < 0)
|
}
|
||||||
pitch = -pitch;
|
|
||||||
if(b->buffer == nil || pitch < b->width)
|
static void
|
||||||
|
textextents(PangoRectangle *r)
|
||||||
|
{
|
||||||
|
PangoRectangle ink, logical;
|
||||||
|
int x0, y0, x1, y1;
|
||||||
|
|
||||||
|
pango_layout_get_pixel_extents(layout, &ink, &logical);
|
||||||
|
x0 = min(ink.x, logical.x);
|
||||||
|
y0 = min(ink.y, logical.y);
|
||||||
|
x1 = max(ink.x + ink.width, logical.x + logical.width);
|
||||||
|
y1 = max(ink.y + ink.height, logical.y + logical.height);
|
||||||
|
r->x = x0;
|
||||||
|
r->y = y0;
|
||||||
|
r->width = max(x1 - x0, 0);
|
||||||
|
r->height = max(y1 - y0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
textwidth(Str *s)
|
||||||
|
{
|
||||||
|
PangoRectangle r;
|
||||||
|
|
||||||
|
if(!settext(s))
|
||||||
return 0;
|
return 0;
|
||||||
x0 = (vlong)px + g->bitmap_left;
|
textextents(&r);
|
||||||
y0 = (vlong)py + f->base - g->bitmap_top;
|
return r.width;
|
||||||
x1 = x0 + b->width;
|
|
||||||
y1 = y0 + b->rows;
|
|
||||||
if(x1 <= 0 || y1 <= 0 || x0 >= w || y0 >= h)
|
|
||||||
return 1;
|
|
||||||
xa = x0 < 0 ? 0 : x0;
|
|
||||||
xb = x1 > w ? w : x1;
|
|
||||||
ya = y0 < 0 ? 0 : y0;
|
|
||||||
yb = y1 > h ? h : y1;
|
|
||||||
for(y = ya; y < yb; y++){
|
|
||||||
if(b->pitch < 0)
|
|
||||||
row = b->buffer + ((vlong)b->rows - 1 - (y - y0)) * pitch;
|
|
||||||
else
|
|
||||||
row = b->buffer + ((vlong)y - y0) * pitch;
|
|
||||||
for(x = xa; x < xb; x++){
|
|
||||||
a = row[(vlong)x - x0];
|
|
||||||
if(a != 0){
|
|
||||||
p = &buf[y * w + x];
|
|
||||||
sel = *p == Colsel;
|
|
||||||
*p = blendtab[sel][a];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
putfont(u32int *buf, int w, int h, int px, int py, Rune r)
|
textdraw(u32int *buf, int w, int h, int x, int y, Str *s)
|
||||||
{
|
{
|
||||||
FT_UInt g;
|
PangoRectangle r;
|
||||||
int f;
|
cairo_surface_t *surface;
|
||||||
|
cairo_t *cr;
|
||||||
|
int stride;
|
||||||
|
|
||||||
if(buf == nil || w <= 0 || h <= 0)
|
if(buf == nil || w <= 0 || h <= 0 || !settext(s))
|
||||||
|
return;
|
||||||
|
stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, w);
|
||||||
|
if(stride < 0 || (vlong)stride != (vlong)w * sizeof buf[0])
|
||||||
|
return;
|
||||||
|
surface = cairo_image_surface_create_for_data((uchar*)buf,
|
||||||
|
CAIRO_FORMAT_RGB24, w, h, stride);
|
||||||
|
if(cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS){
|
||||||
|
cairo_surface_destroy(surface);
|
||||||
return;
|
return;
|
||||||
for(f = 0; f < nfonts; f++){
|
|
||||||
g = FT_Get_Char_Index(fonts[f].face, r);
|
|
||||||
if(g == 0 || FT_Load_Glyph(fonts[f].face, g, FT_LOAD_DEFAULT) != 0 ||
|
|
||||||
FT_Render_Glyph(fonts[f].face->glyph, FT_RENDER_MODE_NORMAL) != 0)
|
|
||||||
continue;
|
|
||||||
if(drawglyph(&fonts[f], buf, w, h, px, py))
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
cairo_surface_mark_dirty(surface);
|
||||||
|
cr = cairo_create(surface);
|
||||||
|
if(cairo_status(cr) == CAIRO_STATUS_SUCCESS){
|
||||||
|
pango_cairo_update_layout(cr, layout);
|
||||||
|
textextents(&r);
|
||||||
|
/* A fallback face must not paint into another popup row. */
|
||||||
|
cairo_rectangle(cr, 0, y, w, Fontsz);
|
||||||
|
cairo_clip(cr);
|
||||||
|
cairo_move_to(cr, x - r.x, y + (Fontsz - r.height) / 2 - r.y);
|
||||||
|
cairo_set_source_rgb(cr, 0, 0, 0);
|
||||||
|
pango_cairo_show_layout(cr, layout);
|
||||||
|
}
|
||||||
|
cairo_destroy(cr);
|
||||||
|
cairo_surface_flush(surface);
|
||||||
|
cairo_surface_destroy(surface);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,6 @@
|
|||||||
#include "dat.h"
|
#include "dat.h"
|
||||||
#include "fn.h"
|
#include "fn.h"
|
||||||
|
|
||||||
int
|
|
||||||
popupcells(Rune *r, int n)
|
|
||||||
{
|
|
||||||
int i, w;
|
|
||||||
|
|
||||||
w = 0;
|
|
||||||
for(i = 0; i < n; i++)
|
|
||||||
if(r[i] != 0xfe0e && r[i] != 0xfe0f)
|
|
||||||
w++;
|
|
||||||
return w;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
popupposition(Caret *caret, int pointerx, int pointery, int screenw,
|
popupposition(Caret *caret, int pointerx, int pointery, int screenw,
|
||||||
int screenh, int w, int h, int *x, int *y)
|
int screenh, int w, int h, int *x, int *y)
|
||||||
|
|||||||
2
run.sh
2
run.sh
@@ -5,11 +5,13 @@ cd "$(dirname "$0")" || exit 1
|
|||||||
if test "$#" -eq 0; then
|
if test "$#" -eq 0; then
|
||||||
set --
|
set --
|
||||||
for font in \
|
for font in \
|
||||||
|
/usr/share/fonts/noto/NotoColorEmoji.ttf \
|
||||||
/usr/share/fonts/TTF/DejaVuSans.ttf \
|
/usr/share/fonts/TTF/DejaVuSans.ttf \
|
||||||
/usr/share/fonts/TTF/Jigmo.ttf \
|
/usr/share/fonts/TTF/Jigmo.ttf \
|
||||||
/usr/share/fonts/TTF/Jigmo2.ttf \
|
/usr/share/fonts/TTF/Jigmo2.ttf \
|
||||||
/usr/share/fonts/noto/NotoSansCJK-Regular.ttc
|
/usr/share/fonts/noto/NotoSansCJK-Regular.ttc
|
||||||
do
|
do
|
||||||
|
test "$#" -ge 4 && break
|
||||||
test -f "$font" && set -- "$@" "$font"
|
test -f "$font" && set -- "$@" "$font"
|
||||||
done
|
done
|
||||||
if test "$#" -eq 0; then
|
if test "$#" -eq 0; then
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ CC = 9c
|
|||||||
LD = 9l
|
LD = 9l
|
||||||
HOSTCC = cc
|
HOSTCC = cc
|
||||||
CFLAGS = -std=c99 -Wall -Wextra -O2 -g -I.. -I../cutest
|
CFLAGS = -std=c99 -Wall -Wextra -O2 -g -I.. -I../cutest
|
||||||
FT_CFLAGS = $(shell pkg-config --cflags freetype2)
|
TEXT_CFLAGS = $(shell pkg-config --cflags 'pangocairo >= 1.56' 'cairo >= 1.18')
|
||||||
FT_LIBS = $(shell pkg-config --libs freetype2)
|
TEXT_LIBS = $(shell pkg-config --libs 'pangocairo >= 1.56' 'cairo >= 1.18')
|
||||||
DBUS_CFLAGS = $(shell pkg-config --cflags dbus-1)
|
DBUS_CFLAGS = $(shell pkg-config --cflags dbus-1)
|
||||||
DBUS_LIBS = $(shell pkg-config --libs dbus-1)
|
DBUS_LIBS = $(shell pkg-config --libs dbus-1)
|
||||||
IBUS_CFLAGS = $(shell pkg-config --cflags dbus-1 xkbcommon)
|
IBUS_CFLAGS = $(shell pkg-config --cflags dbus-1 xkbcommon)
|
||||||
IBUS_LIBS = $(shell pkg-config --libs dbus-1 xkbcommon)
|
IBUS_LIBS = $(shell pkg-config --libs dbus-1 xkbcommon)
|
||||||
LIBS = -lthread -lbio $(FT_LIBS) $(IBUS_LIBS)
|
LIBS = -lthread -lbio $(TEXT_LIBS) $(IBUS_LIBS)
|
||||||
|
|
||||||
PROG = unit_test
|
PROG = unit_test
|
||||||
LIVE = ibus_live_test ipc_live_test daemon_collision_test daemon_failure_test \
|
LIVE = ibus_live_test ipc_live_test daemon_collision_test daemon_failure_test \
|
||||||
@@ -57,7 +57,7 @@ engine_test.o: ../strans.c
|
|||||||
ibus_test.o: CFLAGS += $(IBUS_CFLAGS)
|
ibus_test.o: CFLAGS += $(IBUS_CFLAGS)
|
||||||
ibus_test.o: ../ibus.c
|
ibus_test.o: ../ibus.c
|
||||||
server_test.o: ../srv.c
|
server_test.o: ../srv.c
|
||||||
unit_font.o: CFLAGS += $(FT_CFLAGS)
|
unit_font.o: CFLAGS += $(TEXT_CFLAGS)
|
||||||
|
|
||||||
unit_%.o: ../%.c ../dat.h ../fn.h ../ipc.h
|
unit_%.o: ../%.c ../dat.h ../fn.h ../ipc.h
|
||||||
$(CC) $(CFLAGS) -c -o $@ $<
|
$(CC) $(CFLAGS) -c -o $@ $<
|
||||||
|
|||||||
@@ -3,14 +3,15 @@
|
|||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
Guard = 8,
|
Guard = 8,
|
||||||
Testw = 3 * Fontsz,
|
Testw = 6 * Fontsz,
|
||||||
Testh = 3 * Fontsz,
|
Testh = 3 * Fontsz,
|
||||||
Testn = Testw * Testh,
|
Testn = Testw * Testh,
|
||||||
Missingn = Fontsz * Fontsz,
|
Rgbmask = 0xffffff,
|
||||||
};
|
};
|
||||||
|
|
||||||
static u32int guardcolor = 0x5a5a5a5a;
|
static u32int guardcolor = 0x5a5a5a5a;
|
||||||
static char *testfonts[] = {
|
static char *testfonts[] = {
|
||||||
|
"/usr/share/fonts/noto/NotoColorEmoji.ttf",
|
||||||
"/usr/share/fonts/TTF/DejaVuSans.ttf",
|
"/usr/share/fonts/TTF/DejaVuSans.ttf",
|
||||||
"/usr/share/fonts/TTF/Jigmo.ttf",
|
"/usr/share/fonts/TTF/Jigmo.ttf",
|
||||||
"/usr/share/fonts/TTF/Jigmo2.ttf",
|
"/usr/share/fonts/TTF/Jigmo2.ttf",
|
||||||
@@ -29,38 +30,84 @@ fillpixels(u32int *p, int n, u32int color)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
hasink(u32int *p, int n)
|
hasink(u32int *p, int n, u32int bg)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
bg &= Rgbmask;
|
||||||
for(i = 0; i < n; i++)
|
for(i = 0; i < n; i++)
|
||||||
if(p[i] != Colbg)
|
if((p[i] & Rgbmask) != bg)
|
||||||
return 1;
|
return 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
renders(Rune r)
|
hascolors(u32int *p, int n, u32int bg)
|
||||||
{
|
{
|
||||||
u32int buf[Fontsz * Fontsz];
|
u32int c, first;
|
||||||
|
int b, found, g, i, r;
|
||||||
|
|
||||||
fillpixels(buf, nelem(buf), Colbg);
|
bg &= Rgbmask;
|
||||||
putfont(buf, Fontsz, Fontsz, 0, 0, r);
|
first = 0;
|
||||||
return hasink(buf, nelem(buf));
|
found = 0;
|
||||||
|
for(i = 0; i < n; i++){
|
||||||
|
c = p[i] & Rgbmask;
|
||||||
|
if(c == bg)
|
||||||
|
continue;
|
||||||
|
r = c >> 16 & 0xff;
|
||||||
|
g = c >> 8 & 0xff;
|
||||||
|
b = c & 0xff;
|
||||||
|
if(r == g && g == b)
|
||||||
|
continue;
|
||||||
|
if(!found){
|
||||||
|
first = c;
|
||||||
|
found = 1;
|
||||||
|
}else if(c != first)
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
checkguards(struct ct *t, u32int *mem)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = 0; i < Guard; i++){
|
||||||
|
CT_EQ_UINT(t, guardcolor, mem[i]);
|
||||||
|
CT_EQ_UINT(t, guardcolor, mem[Guard + Testn + i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
checkoutside(struct ct *t, u32int *buf, u32int bg, int row)
|
||||||
|
{
|
||||||
|
int x, y, y0, y1;
|
||||||
|
|
||||||
|
bg &= Rgbmask;
|
||||||
|
y0 = max(row, 0);
|
||||||
|
y1 = min(row + Fontsz, Testh);
|
||||||
|
for(y = 0; y < Testh; y++){
|
||||||
|
if(y >= y0 && y < y1)
|
||||||
|
continue;
|
||||||
|
for(x = 0; x < Testw; x++)
|
||||||
|
CT_EQ_UINT(t, bg, buf[y * Testw + x] & Rgbmask);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
inkbounds(u32int *p, int *minx, int *miny, int *maxx, int *maxy)
|
inkbounds(u32int *p, u32int bg, int *minx, int *miny, int *maxx, int *maxy)
|
||||||
{
|
{
|
||||||
int x, y;
|
int x, y;
|
||||||
|
|
||||||
|
bg &= Rgbmask;
|
||||||
*minx = Testw;
|
*minx = Testw;
|
||||||
*miny = Testh;
|
*miny = Testh;
|
||||||
*maxx = -1;
|
*maxx = -1;
|
||||||
*maxy = -1;
|
*maxy = -1;
|
||||||
for(y = 0; y < Testh; y++){
|
for(y = 0; y < Testh; y++){
|
||||||
for(x = 0; x < Testw; x++){
|
for(x = 0; x < Testw; x++){
|
||||||
if(p[y * Testw + x] == Colbg)
|
if((p[y * Testw + x] & Rgbmask) == bg)
|
||||||
continue;
|
continue;
|
||||||
*minx = min(*minx, x);
|
*minx = min(*minx, x);
|
||||||
*miny = min(*miny, y);
|
*miny = min(*miny, y);
|
||||||
@@ -72,86 +119,124 @@ inkbounds(u32int *p, int *minx, int *miny, int *maxx, int *maxy)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
checkclip(struct ct *t, u32int *ref, int dx, int dy)
|
checktext(struct ct *t, u32int *buf, char *utf)
|
||||||
{
|
{
|
||||||
u32int *buf, *mem, *want;
|
Str s;
|
||||||
int i, sx, sy, x, y;
|
int maxx, maxy, minx, miny, w;
|
||||||
|
|
||||||
|
s = mkstr(utf);
|
||||||
|
w = textwidth(&s);
|
||||||
|
CT_CHECK(t, w > 0);
|
||||||
|
CT_CHECK(t, w < Testw - Fontsz);
|
||||||
|
fillpixels(buf, Testn, Colbg);
|
||||||
|
textdraw(buf, Testw, Testh, Fontsz, Fontsz, &s);
|
||||||
|
if(!CT_CHECK(t, inkbounds(buf, Colbg, &minx, &miny, &maxx, &maxy)))
|
||||||
|
return;
|
||||||
|
CT_CHECK(t, minx >= 0 && miny >= 0);
|
||||||
|
CT_CHECK(t, maxx < Testw && maxy < Testh);
|
||||||
|
CT_CHECK(t, maxx - minx + 1 <= w);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
checkcolor(struct ct *t, u32int *buf, char *utf)
|
||||||
|
{
|
||||||
|
Str s;
|
||||||
|
int w;
|
||||||
|
|
||||||
|
s = mkstr(utf);
|
||||||
|
w = textwidth(&s);
|
||||||
|
CT_CHECK(t, w > 0);
|
||||||
|
if(s.n > 1)
|
||||||
|
CT_CHECK(t, w < s.n * Fontsz);
|
||||||
|
fillpixels(buf, Testn, Colbg);
|
||||||
|
textdraw(buf, Testw, Testh, Fontsz, Fontsz, &s);
|
||||||
|
CT_CHECK(t, hasink(buf, Testn, Colbg));
|
||||||
|
CT_CHECK(t, hascolors(buf, Testn, Colbg));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
checkshape(struct ct *t, u32int *buf, char *utf)
|
||||||
|
{
|
||||||
|
Str s;
|
||||||
|
int w;
|
||||||
|
|
||||||
|
s = mkstr(utf);
|
||||||
|
w = textwidth(&s);
|
||||||
|
CT_CHECK(t, w > 0);
|
||||||
|
CT_CHECK(t, w < s.n * Fontsz);
|
||||||
|
fillpixels(buf, Testn, Colbg);
|
||||||
|
textdraw(buf, Testw, Testh, Fontsz, Fontsz, &s);
|
||||||
|
CT_CHECK(t, hasink(buf, Testn, Colbg));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
checkclip(struct ct *t, u32int *mem, int x, int y, Str *s)
|
||||||
|
{
|
||||||
|
u32int *buf;
|
||||||
|
|
||||||
mem = emalloc((Testn + 2 * Guard) * sizeof mem[0]);
|
|
||||||
want = emalloc(Testn * sizeof want[0]);
|
|
||||||
fillpixels(mem, Testn + 2 * Guard, guardcolor);
|
|
||||||
buf = mem + Guard;
|
buf = mem + Guard;
|
||||||
fillpixels(buf, Testn, Colbg);
|
fillpixels(buf, Testn, Colbg);
|
||||||
fillpixels(want, Testn, Colbg);
|
textdraw(buf, Testw, Testh, x, y, s);
|
||||||
for(y = 0; y < Testh; y++){
|
CT_CHECK(t, hasink(buf, Testn, Colbg));
|
||||||
for(x = 0; x < Testw; x++){
|
checkoutside(t, buf, Colbg, y);
|
||||||
sx = x - dx;
|
checkguards(t, mem);
|
||||||
sy = y - dy;
|
|
||||||
if(sx >= 0 && sx < Testw && sy >= 0 && sy < Testh)
|
|
||||||
want[y * Testw + x] = ref[sy * Testw + sx];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
putfont(buf, Testw, Testh, Fontsz + dx, Fontsz + dy, 'A');
|
|
||||||
CT_CHECK(t, hasink(buf, Testn));
|
|
||||||
CT_EQ_MEM(t, want, buf, Testn * sizeof buf[0]);
|
|
||||||
for(i = 0; i < Guard; i++){
|
|
||||||
CT_EQ_UINT(t, guardcolor, mem[i]);
|
|
||||||
CT_EQ_UINT(t, guardcolor, mem[Guard + Testn + i]);
|
|
||||||
}
|
|
||||||
free(want);
|
|
||||||
free(mem);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
font_render(struct ct *t)
|
font_render(struct ct *t)
|
||||||
{
|
{
|
||||||
u32int *buf, *missing, *ref;
|
static char *plain[] = { "A", "가", "あ", "漢", "☆", "𠀋" };
|
||||||
int i, minx, miny, maxx, maxy;
|
u32int *buf, *mem;
|
||||||
|
Str a, heart, missing;
|
||||||
|
int i, w;
|
||||||
|
|
||||||
missing = emalloc((Missingn + 2 * Guard) * sizeof missing[0]);
|
mem = emalloc((Testn + 2 * Guard) * sizeof mem[0]);
|
||||||
fillpixels(missing, Missingn + 2 * Guard, guardcolor);
|
fillpixels(mem, Testn + 2 * Guard, guardcolor);
|
||||||
buf = missing + Guard;
|
buf = mem + Guard;
|
||||||
fillpixels(buf, Missingn, Colbg);
|
fillpixels(buf, Testn, Colbg);
|
||||||
CT_CHECK(t, !fontinit(missingfonts, nelem(missingfonts)));
|
a = mkstr("A");
|
||||||
putfont(buf, Fontsz, Fontsz, 0, 0, 'A');
|
CT_CHECK(t, !textinit(missingfonts, nelem(missingfonts)));
|
||||||
for(i = 0; i < Missingn; i++)
|
CT_EQ_INT(t, 0, textwidth(&a));
|
||||||
|
textdraw(buf, Testw, Testh, 0, 0, &a);
|
||||||
|
for(i = 0; i < Testn; i++)
|
||||||
CT_EQ_UINT(t, Colbg, buf[i]);
|
CT_EQ_UINT(t, Colbg, buf[i]);
|
||||||
for(i = 0; i < Guard; i++){
|
checkguards(t, mem);
|
||||||
CT_EQ_UINT(t, guardcolor, missing[i]);
|
|
||||||
CT_EQ_UINT(t, guardcolor, missing[Guard + Missingn + i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!CT_CHECK(t, fontinit(testfonts, nelem(testfonts)))){
|
if(!CT_CHECK(t, textinit(testfonts, nelem(testfonts)))){
|
||||||
free(missing);
|
free(mem);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
CT_CHECK(t, renders('A'));
|
for(i = 0; i < nelem(plain); i++)
|
||||||
CT_CHECK(t, renders(0xac00));
|
checktext(t, buf, plain[i]);
|
||||||
CT_CHECK(t, renders(0x4e00));
|
|
||||||
CT_CHECK(t, renders(0x1f600));
|
|
||||||
CT_CHECK(t, renders(0x2000b));
|
|
||||||
fillpixels(missing, Missingn + 2 * Guard, guardcolor);
|
|
||||||
buf = missing + Guard;
|
|
||||||
fillpixels(buf, Missingn, Colbg);
|
|
||||||
putfont(buf, Fontsz, Fontsz, 0, 0, 0x10ffff);
|
|
||||||
for(i = 0; i < Missingn; i++)
|
|
||||||
CT_EQ_UINT(t, Colbg, buf[i]);
|
|
||||||
for(i = 0; i < Guard; i++){
|
|
||||||
CT_EQ_UINT(t, guardcolor, missing[i]);
|
|
||||||
CT_EQ_UINT(t, guardcolor, missing[Guard + Missingn + i]);
|
|
||||||
}
|
|
||||||
free(missing);
|
|
||||||
|
|
||||||
ref = emalloc(Testn * sizeof ref[0]);
|
checkcolor(t, buf, "😀");
|
||||||
fillpixels(ref, Testn, Colbg);
|
checkcolor(t, buf, "❤️");
|
||||||
putfont(ref, Testw, Testh, Fontsz, Fontsz, 'A');
|
checkcolor(t, buf, "☕️");
|
||||||
if(!CT_CHECK(t, inkbounds(ref, &minx, &miny, &maxx, &maxy))){
|
checkshape(t, buf, "👩💻");
|
||||||
free(ref);
|
checkshape(t, buf, "👍🏽");
|
||||||
return;
|
checkshape(t, buf, "🇰🇷");
|
||||||
|
checkshape(t, buf, "☕︎");
|
||||||
|
|
||||||
|
heart = mkstr("❤️");
|
||||||
|
fillpixels(buf, Testn, Colsel);
|
||||||
|
textdraw(buf, Testw, Testh, Fontsz, Fontsz, &heart);
|
||||||
|
CT_CHECK(t, hascolors(buf, Testn, Colsel));
|
||||||
|
CT_EQ_UINT(t, Colsel,
|
||||||
|
buf[(Fontsz + Fontsz/2) * Testw + Testw - 1] & Rgbmask);
|
||||||
|
checkoutside(t, buf, Colsel, Fontsz);
|
||||||
|
checkguards(t, mem);
|
||||||
|
|
||||||
|
w = textwidth(&a);
|
||||||
|
checkclip(t, mem, -w / 2, Fontsz, &a);
|
||||||
|
checkclip(t, mem, Testw - w / 2, Fontsz, &a);
|
||||||
|
checkclip(t, mem, Fontsz, -Fontsz / 2, &a);
|
||||||
|
checkclip(t, mem, Fontsz, Testh - Fontsz / 2, &a);
|
||||||
|
|
||||||
|
missing = mkstr("\xF4\x8F\xBF\xBF");
|
||||||
|
for(i = 0; i < 32; i++){
|
||||||
|
textdraw(buf, Testw, Testh, Fontsz, Fontsz, &heart);
|
||||||
|
textdraw(buf, Testw, Testh, 0, 0, &missing);
|
||||||
}
|
}
|
||||||
checkclip(t, ref, -minx - 1, 0);
|
checkguards(t, mem);
|
||||||
checkclip(t, ref, Testw - maxx, 0);
|
free(mem);
|
||||||
checkclip(t, ref, 0, -miny - 1);
|
|
||||||
checkclip(t, ref, 0, Testh - maxy);
|
|
||||||
free(ref);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,9 @@
|
|||||||
void
|
void
|
||||||
popup_layout(struct ct *t)
|
popup_layout(struct ct *t)
|
||||||
{
|
{
|
||||||
Rune heart[] = { 0x2764, 0xfe0f };
|
|
||||||
Caret caret;
|
Caret caret;
|
||||||
int x, y;
|
int x, y;
|
||||||
|
|
||||||
CT_EQ_INT(t, 1, popupcells(heart, nelem(heart)));
|
|
||||||
memset(&caret, 0, sizeof caret);
|
memset(&caret, 0, sizeof caret);
|
||||||
popupposition(&caret, 40, 50, 800, 600, 100, 60, &x, &y);
|
popupposition(&caret, 40, 50, 800, 600, 100, 60, &x, &y);
|
||||||
CT_EQ_INT(t, 50, x);
|
CT_EQ_INT(t, 50, x);
|
||||||
|
|||||||
32
win.c
32
win.c
@@ -141,7 +141,7 @@ wininit(void)
|
|||||||
mask = XCB_CW_BACK_PIXMAP;
|
mask = XCB_CW_BACK_PIXMAP;
|
||||||
xcb_change_window_attributes(conn, win, mask, &pix);
|
xcb_change_window_attributes(conn, win, mask, &pix);
|
||||||
img = emalloc(Imgw * Imgh * sizeof(img[0]));
|
img = emalloc(Imgw * Imgh * sizeof(img[0]));
|
||||||
if(!fontinit(fontfiles, nfontfiles)){
|
if(!textinit(fontfiles, nfontfiles)){
|
||||||
fprint(2, "strans: popup disabled: no usable fonts\n");
|
fprint(2, "strans: popup disabled: no usable fonts\n");
|
||||||
wincleanup();
|
wincleanup();
|
||||||
return 0;
|
return 0;
|
||||||
@@ -149,18 +149,6 @@ wininit(void)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
drawstr(u32int *buf, int x, int y, Rune *r, int n, int maxw, int maxh)
|
|
||||||
{
|
|
||||||
while(n-- > 0){
|
|
||||||
if(*r != 0xfe0e && *r != 0xfe0f){
|
|
||||||
putfont(buf, maxw, maxh, x, y, *r);
|
|
||||||
x += Fontsz;
|
|
||||||
}
|
|
||||||
r++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
fill(u32int *buf, int n, u32int color)
|
fill(u32int *buf, int n, u32int color)
|
||||||
{
|
{
|
||||||
@@ -174,20 +162,22 @@ static void
|
|||||||
drawkouho(Drawcmd *dc, int n, int w, int h)
|
drawkouho(Drawcmd *dc, int n, int w, int h)
|
||||||
{
|
{
|
||||||
int npre, sely, y, i;
|
int npre, sely, y, i;
|
||||||
Str *s;
|
Str num, *s;
|
||||||
|
|
||||||
fill(img, w * h, Colbg);
|
fill(img, w * h, Colbg);
|
||||||
npre = dc->pre.n != 0;
|
npre = dc->pre.n != 0;
|
||||||
if(npre)
|
if(npre)
|
||||||
drawstr(img, 0, 0, dc->pre.r, dc->pre.n, w, h);
|
textdraw(img, w, h, 0, 0, &dc->pre);
|
||||||
if(dc->sel >= 0 && dc->sel < n){
|
if(dc->sel >= 0 && dc->sel < n){
|
||||||
sely = (npre + dc->sel) * Fontsz;
|
sely = (npre + dc->sel) * Fontsz;
|
||||||
fill(img + sely * w, Fontsz * w, Colsel);
|
fill(img + sely * w, Fontsz * w, Colsel);
|
||||||
}
|
}
|
||||||
for(i = 0, y = npre * Fontsz; i < n; i++, y += Fontsz){
|
for(i = 0, y = npre * Fontsz; i < n; i++, y += Fontsz){
|
||||||
s = &dc->kouho[i];
|
s = &dc->kouho[i];
|
||||||
putfont(img, w, h, 0, y, '1' + i + Asciitofull);
|
sclear(&num);
|
||||||
drawstr(img, 2*Fontsz, y, s->r, s->n, w, h);
|
sputr(&num, '1' + i + Asciitofull);
|
||||||
|
textdraw(img, w, h, 0, y, &num);
|
||||||
|
textdraw(img, w, h, 2*Fontsz, y, s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,6 +200,7 @@ winhide(void)
|
|||||||
static int
|
static int
|
||||||
winshow(Drawcmd *dc)
|
winshow(Drawcmd *dc)
|
||||||
{
|
{
|
||||||
|
vlong width;
|
||||||
int npre, px, py, w, h, i, n, maxw;
|
int npre, px, py, w, h, i, n, maxw;
|
||||||
u32int vals[4];
|
u32int vals[4];
|
||||||
xcb_query_pointer_reply_t *ptr;
|
xcb_query_pointer_reply_t *ptr;
|
||||||
@@ -220,11 +211,12 @@ winshow(Drawcmd *dc)
|
|||||||
if(n == 0 && npre == 0){
|
if(n == 0 && npre == 0){
|
||||||
return winhide();
|
return winhide();
|
||||||
}
|
}
|
||||||
maxw = popupcells(dc->pre.r, dc->pre.n);
|
maxw = textwidth(&dc->pre);
|
||||||
for(i = 0; i < n; i++)
|
for(i = 0; i < n; i++)
|
||||||
maxw = max(maxw, popupcells(dc->kouho[i].r, dc->kouho[i].n));
|
maxw = max(maxw, textwidth(&dc->kouho[i]));
|
||||||
vals[3] = h = (n + npre) * Fontsz;
|
vals[3] = h = (n + npre) * Fontsz;
|
||||||
vals[2] = w = (maxw + 3) * Fontsz;
|
width = (vlong)maxw + 3*Fontsz;
|
||||||
|
vals[2] = w = min(max(width, 1), Imgw);
|
||||||
px = py = 0;
|
px = py = 0;
|
||||||
if(!dc->caret.valid){
|
if(!dc->caret.valid){
|
||||||
cookie = xcb_query_pointer(conn, scr->root);
|
cookie = xcb_query_pointer(conn, scr->root);
|
||||||
|
|||||||
Reference in New Issue
Block a user