"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "src/rxvtfont.C" between
rxvt-unicode-9.26.tar.bz2 and rxvt-unicode-9.29.tar.bz2

About: rxvt-unicode is a clone of the terminal emulator rxvt.

rxvtfont.C  (rxvt-unicode-9.26.tar.bz2):rxvtfont.C  (rxvt-unicode-9.29.tar.bz2)
skipping to change at line 139 skipping to change at line 139
{ CS_KSC5601_1987_0, "-baekmuk-gulim-*-*-*-*-*-*-*-*-c-*-ksc5601*" }, { CS_KSC5601_1987_0, "-baekmuk-gulim-*-*-*-*-*-*-*-*-c-*-ksc5601*" },
{ CS_KSC5601_1987_0, "-*-*-*-*-*-*-*-*-*-*-c-*-ksc5601*" }, { CS_KSC5601_1987_0, "-*-*-*-*-*-*-*-*-*-*-c-*-ksc5601*" },
# if XFT # if XFT
{ CS_KSC5601_1987_0, "xft:Baekmuk Gulim:antialias=false" }, { CS_KSC5601_1987_0, "xft:Baekmuk Gulim:antialias=false" },
{ CS_KSC5601_1987_0, "xft::lang=ko:antialias=false" }, { CS_KSC5601_1987_0, "xft::lang=ko:antialias=false" },
# endif # endif
#endif #endif
//{ CS_UNICODE, "-*-unifont-*-*-*-*-*-*-*-*-c-*-iso10646-1" }, // this gem of a font has actual dotted circles within the combining character glyphs. //{ CS_UNICODE, "-*-unifont-*-*-*-*-*-*-*-*-c-*-iso10646-1" }, // this gem of a font has actual dotted circles within the combining character glyphs.
#if XFT #if XFT
{ CS_UNICODE, "xft:Bitstream Vera Sans Mono:antialias=false:autohint=true { CS_UNICODE, "xft:DejaVu Sans Mono:antialias=false:autohint=true" },
" }, { CS_UNICODE, "xft:Courier New:antialias=false:autohint=true" },
{ CS_UNICODE, "xft:Courier New:antialias=false:autohint=true" { CS_UNICODE, "xft:Andale Mono:antialias=false:autohint=false" },
}, { CS_UNICODE, "xft:Arial Unicode MS:antialias=false:autohint=false" },
{ CS_UNICODE, "xft:Andale Mono:antialias=false:autohint=false"
},
{ CS_UNICODE, "xft:Arial Unicode MS:antialias=false:autohint=false"
},
// FreeMono is usually uglier than x fonts, so try after the others // FreeMono is usually uglier than x fonts, so try after the others
{ CS_UNICODE, "xft:FreeMono:autohint=true" }, { CS_UNICODE, "xft:FreeMono:autohint=true" },
#endif #endif
// generic font fallback, put this last, as many iso10646 fonts have extents // generic font fallback, put this last, as many iso10646 fonts have extents
// specified for all glyphs in the range they cover, but most are simply empty // specified for all glyphs in the range they cover, but most are simply empty
//{ CS_UNICODE, "-*-*-*-r-*-*-*-*-*-*-c-*-iso10646-1" }, //{ CS_UNICODE, "-*-*-*-r-*-*-*-*-*-*-c-*-iso10646-1" },
//{ CS_UNICODE, "-*-*-*-r-*-*-*-*-*-*-m-*-iso10646-1" }, //{ CS_UNICODE, "-*-*-*-r-*-*-*-*-*-*-m-*-iso10646-1" },
{ CS_UNKNOWN, 0 } { CS_UNKNOWN, 0 }
skipping to change at line 267 skipping to change at line 267
#else #else
XSetForeground (disp, gc, term->pix_colors[color]); XSetForeground (disp, gc, term->pix_colors[color]);
XFillRectangle (disp, d, gc, x, y, w, h); XFillRectangle (disp, d, gc, x, y, w, h);
#endif #endif
} }
} }
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
struct rxvt_font_default : rxvt_font { struct rxvt_font_default : rxvt_font
{
struct rxvt_fontset *fs; struct rxvt_fontset *fs;
rxvt_font_default (rxvt_fontset *fs) rxvt_font_default (rxvt_fontset *fs)
: rxvt_font () : rxvt_font ()
{ {
this->fs = fs; this->fs = fs;
} }
rxvt_fontprop properties () rxvt_fontprop properties ()
{ {
skipping to change at line 317 skipping to change at line 318
if (unicode <= 0x009f) if (unicode <= 0x009f)
return true; return true;
#ifdef BUILTIN_GLYPHS #ifdef BUILTIN_GLYPHS
if (unicode >= 0x2500 && unicode <= 0x259f && if (unicode >= 0x2500 && unicode <= 0x259f &&
!term->option (Opt_skipBuiltinGlyphs)) !term->option (Opt_skipBuiltinGlyphs))
return true; return true;
#endif #endif
if (IS_COMPOSE (unicode)) // we do not check for IS_COMPOSE here, as this would
return true; // rob other fonts from taking over.
switch (unicode) switch (unicode)
{ {
case ZERO_WIDTH_CHAR: case ZERO_WIDTH_CHAR:
case NOCHAR: case NOCHAR:
return true; return true;
} }
return false; return false;
} }
skipping to change at line 349 skipping to change at line 350
{ {
dTermDisplay; dTermDisplay;
dTermGC; dTermGC;
clear_rect (d, x, y, term->fwidth * len, term->fheight, bg); clear_rect (d, x, y, term->fwidth * len, term->fheight, bg);
XSetForeground (disp, gc, term->pix_colors[fg]); XSetForeground (disp, gc, term->pix_colors[fg]);
while (len) while (len)
{ {
#if ENABLE_COMBINING
compose_char *cc;
#endif
const text_t *tp = text; const text_t *tp = text;
text_t t = *tp; text_t t = *tp;
while (++text, --len && *text == NOCHAR) while (++text, --len && *text == NOCHAR)
; ;
#if ENABLE_COMBINING
compose_char *cc;
#endif
int width = text - tp; int width = text - tp;
int fwidth = term->fwidth * width; int fwidth = term->fwidth * width;
#ifdef BUILTIN_GLYPHS #ifdef BUILTIN_GLYPHS
if (0x2500 <= t && t <= 0x259f) if (0x2500 <= t && t <= 0x259f)
{ {
# include "table/linedraw.h" # include "table/linedraw.h"
uint16_t offs = linedraw_offs[t - 0x2500]; uint16_t offs = linedraw_offs[t - 0x2500];
uint32_t *a = linedraw_command + (offs >> 4); uint32_t *a = linedraw_command + (offs >> 4);
uint32_t *b = a + (offs & 15); uint32_t *b = a + (offs & 15);
skipping to change at line 449 skipping to change at line 450
} }
} }
#else #else
if (0) if (0)
; ;
#endif #endif
#if ENABLE_COMBINING #if ENABLE_COMBINING
else if (IS_COMPOSE (t) && (cc = rxvt_composite[t])) else if (IS_COMPOSE (t) && (cc = rxvt_composite[t]))
{ {
min_it (width, 2); // we only support wcwidth up to 2 min_it (width, 2); // we only support wcwidth up to 2
text_t chrs[2]; text_t chrs[2];
chrs [1] = NOCHAR; chrs [1] = NOCHAR;
*chrs = cc->c1; *chrs = cc->c1;
rxvt_font *f1 = (*fs)[fs->find_font_idx (cc->c1)]; rxvt_font *f1 = (*fs)[fs->find_font_idx (cc->c1)];
f1->draw (d, x, y, chrs, width, fg, bg); f1->draw (d, x, y, chrs, width, fg, bg);
if (cc->c2 != NOCHAR) if (cc->c2 != NOCHAR)
{ {
bool careful; bool careful;
skipping to change at line 479 skipping to change at line 479
} }
#endif #endif
else else
switch (t) switch (t)
{ {
case '\t': case '\t':
case ZERO_WIDTH_CHAR: case ZERO_WIDTH_CHAR:
case NOCHAR: case NOCHAR:
break; break;
/*
* If the base font does not support variation selectors, treat them
as ZWC.
* a point could be made to do this for all wcwidth == 0 characters,
but I
* decided against that until more data is available.
*/
case 0xfe00: case 0xfe01: case 0xfe02: case 0xfe03: case 0xfe04: cas
e 0xfe05: case 0xfe06: case 0xfe07:
case 0xfe08: case 0xfe09: case 0xfe0a: case 0xfe0b: case 0xfe0c: cas
e 0xfe0d: case 0xfe0e: case 0xfe0f:
break;
default: default:
XDrawRectangle (disp, d, gc, x + 2, y + 2, XDrawRectangle (disp, d, gc, x + 2, y + 2,
fwidth - 4, term->fheight - 4); fwidth - 4, term->fheight - 4);
} }
x += fwidth; x += fwidth;
} }
} }
struct rxvt_font_overflow : rxvt_font { struct rxvt_font_overflow : rxvt_font
{
struct rxvt_fontset *fs; struct rxvt_fontset *fs;
rxvt_font_overflow (rxvt_fontset *fs) rxvt_font_overflow (rxvt_fontset *fs)
: rxvt_font () : rxvt_font ()
{ {
this->fs = fs; this->fs = fs;
} }
rxvt_fontprop properties () rxvt_fontprop properties ()
{ {
skipping to change at line 512 skipping to change at line 522
p.ascent = rxvt_fontprop::unset; p.ascent = rxvt_fontprop::unset;
p.weight = rxvt_fontprop::medium; p.weight = rxvt_fontprop::medium;
p.slant = rxvt_fontprop::roman; p.slant = rxvt_fontprop::roman;
return p; return p;
} }
bool load (const rxvt_fontprop &prop, bool force_prop) bool load (const rxvt_fontprop &prop, bool force_prop)
{ {
width = 1; height = 1; width = 1; height = 1;
ascent = 1; descent = 0; ascent = 1;
descent = (*fs)[2]->descent;
set_name (strdup ("built-in rendition overflow font")); set_name (strdup ("built-in rendition overflow font"));
return true; return true;
} }
bool has_char (unicode_t unicode, const rxvt_fontprop *prop, bool &careful) co nst bool has_char (unicode_t unicode, const rxvt_fontprop *prop, bool &careful) co nst
{ {
return false; return false;
} }
skipping to change at line 544 skipping to change at line 555
(*fs)[fid]->draw (d, x, y, text, w, fg, bg); (*fs)[fid]->draw (d, x, y, text, w, fg, bg);
text += w; text += w;
len -= w; len -= w;
x += term->fwidth * w; x += term->fwidth * w;
} }
} }
}; };
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
struct rxvt_font_x11 : rxvt_font { struct rxvt_font_x11 : rxvt_font
{
rxvt_font_x11 () { f = 0; } rxvt_font_x11 () { f = 0; }
void clear (); void clear ();
rxvt_fontprop properties (); rxvt_fontprop properties ();
bool load (const rxvt_fontprop &prop, bool force_prop); bool load (const rxvt_fontprop &prop, bool force_prop);
bool has_char (unicode_t unicode, const rxvt_fontprop *prop, bool &careful) co nst; bool has_char (unicode_t unicode, const rxvt_fontprop *prop, bool &careful) co nst;
skipping to change at line 1129 skipping to change at line 1141
else else
XDrawString (disp, d, gc, x, y + base, xc, len); XDrawString (disp, d, gc, x, y + base, xc, len);
} }
} }
} }
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
#if XFT #if XFT
struct rxvt_font_xft : rxvt_font { struct rxvt_font_xft : rxvt_font
rxvt_font_xft () { f = 0; } {
#if XFT_CHAR_CACHE
// we cache the qascii range xoffsets in the name of speed,
// expecting terminals to deal mostly with these characters
// we also assume the xoff always fits into uint8_t,
// which is questionable, but let's see...
// also, it is uncomfortably big, due to the uints.
enum { char_cache_min = 0x20, char_cache_max = 0x7e };
uint8_t xoff_cache [char_cache_max - char_cache_min + 1];
FT_UInt glyph_cache [char_cache_max - char_cache_min + 1];
#endif
rxvt_font_xft ()
{
f = 0;
}
void clear (); void clear ();
rxvt_fontprop properties (); rxvt_fontprop properties ();
bool load (const rxvt_fontprop &prop, bool force_prop); bool load (const rxvt_fontprop &prop, bool force_prop);
void draw (rxvt_drawable &d, int x, int y, void draw (rxvt_drawable &d, int x, int y,
const text_t *text, int len, const text_t *text, int len,
int fg, int bg); int fg, int bg);
skipping to change at line 1242 skipping to change at line 1269
if (!f) if (!f)
{ {
FcPatternDestroy (p); FcPatternDestroy (p);
success = false; success = false;
break; break;
} }
FT_Face face = XftLockFace (f); FT_Face face = XftLockFace (f);
// fuck me plenty: XftLockFace can actually return 0. try not to crash.
// we also assume blindly that if the first lock succeeeds, then subsequen
t
// locks will also succeed.
if (!face)
{
XftFontClose (disp, f);
success = false;
break;
}
ascent = (face->size->metrics.ascender + 63) >> 6; ascent = (face->size->metrics.ascender + 63) >> 6;
descent = (-face->size->metrics.descender + 63) >> 6; descent = (-face->size->metrics.descender + 63) >> 6;
height = max (ascent + descent, (face->size->metrics.height + 63) >> 6); height = max (ascent + descent, (face->size->metrics.height + 63) >> 6);
width = 0; width = 0;
bool scalable = face->face_flags & FT_FACE_FLAG_SCALABLE; bool scalable = face->face_flags & FT_FACE_FLAG_SCALABLE;
XftUnlockFace (f); XftUnlockFace (f);
int glheight = height; int glheight = height;
skipping to change at line 1282 skipping to change at line 1319
int wcw = WCWIDTH (ch); int wcw = WCWIDTH (ch);
if (wcw > 0) g.width = (g.width + wcw - 1) / wcw; if (wcw > 0) g.width = (g.width + wcw - 1) / wcw;
if (width < g.width ) width = g.width; if (width < g.width ) width = g.width;
if (height < g.height ) height = g.height; if (height < g.height ) height = g.height;
if (glheight < g.height - g.y) glheight = g.height - g.y; if (glheight < g.height - g.y) glheight = g.height - g.y;
} }
if (!width) if (!width)
{ {
rxvt_warn ("unable to calculate font width for '%s', ignoring.\n", nam rxvt_warn ("unable to calculate font width for '%s', using max_advance
e); _width.\n", name);
width = f->max_advance_width;
XftFontClose (disp, f);
f = 0;
success = false;
break; break;
} }
if (prop.height == rxvt_fontprop::unset if (prop.height == rxvt_fontprop::unset
|| (height <= prop.height && glheight <= prop.height) || (height <= prop.height && glheight <= prop.height)
|| height <= 2 || height <= 2
|| !scalable) || !scalable)
break; break;
if (ftheight) if (ftheight)
skipping to change at line 1324 skipping to change at line 1357
FcPatternDestroy (match); FcPatternDestroy (match);
#if 0 // do it per-character #if 0 // do it per-character
if (prop.width != rxvt_fontprop::unset && width > prop.width) if (prop.width != rxvt_fontprop::unset && width > prop.width)
{ {
clear (); clear ();
success = false; success = false;
} }
#endif #endif
#if XFT_CHAR_CACHE
// populate char cache
for (FcChar16 ch = char_cache_min; ch <= char_cache_max; ++ch)
{
FT_UInt glyph = XftCharIndex (disp, f, ch);
glyph_cache [ch - char_cache_min] = glyph;
XGlyphInfo g;
XftGlyphExtents (disp, f, &glyph, 1, &g);
xoff_cache [ch - char_cache_min] = g.xOff;
}
#endif
return success; return success;
} }
bool bool
rxvt_font_xft::has_char (unicode_t unicode, const rxvt_fontprop *prop, bool &car eful) const rxvt_font_xft::has_char (unicode_t unicode, const rxvt_fontprop *prop, bool &car eful) const
{ {
careful = false; careful = false;
if (!XftCharExists (term->dpy, f, unicode)) // handle non-bmp chars when text_t is 16 bit
#if ENABLE_COMBINING && !UNICODE_3
if (ecb_expect_false (IS_COMPOSE (unicode)))
if (compose_char *cc = rxvt_composite [unicode])
if (cc->c2 == NOCHAR)
unicode = cc->c1;
else
return false;
else
return false;
#endif
FcChar32 chr = unicode;
if (!XftCharExists (term->dpy, f, chr))
return false; return false;
if (!prop || prop->width == rxvt_fontprop::unset) if (!prop || prop->width == rxvt_fontprop::unset)
return true; return true;
// check character against base font bounding box int wcw = max (WCWIDTH (chr), 1);
FcChar32 ch = unicode;
XGlyphInfo g; XGlyphInfo g;
XftTextExtents32 (term->dpy, f, &ch, 1, &g); XftTextExtents32 (term->dpy, f, &chr, 1, &g);
int cwidth = prop->width * wcw;
// use same adjustments as in ->draw, see there
g.x += g.xOff ? cwidth - g.xOff >> 1 : 0;
g.x += g.xOff ? 0 : cwidth;
int w = g.width - g.x; int w = g.width - g.x;
int wcw = max (WCWIDTH (unicode), 1);
careful = g.x > 0 || w > prop->width * wcw; careful = g.x > 0 || w > cwidth;
if (careful && !OVERLAP_OK (w, wcw, prop)) if (careful && !OVERLAP_OK (w, wcw, prop))
return false; return false;
// this weeds out _totally_ broken fonts, or glyphs // this weeds out _totally_ broken fonts, or glyphs
if (!OVERLAP_OK (g.xOff, wcw, prop)) if (!OVERLAP_OK (g.xOff, wcw, prop))
return false; return false;
return true; return true;
} }
void void
rxvt_font_xft::draw (rxvt_drawable &d, int x, int y, rxvt_font_xft::draw (rxvt_drawable &d, int x, int y,
const text_t *text, int len, const text_t *text, int len,
int fg, int bg) int fg, int bg)
{ {
XGlyphInfo extents;
XftGlyphSpec *enc = rxvt_temp_buf<XftGlyphSpec> (len); XftGlyphSpec *enc = rxvt_temp_buf<XftGlyphSpec> (len);
XftGlyphSpec *ep = enc; XftGlyphSpec *ep = enc;
dTermDisplay; dTermDisplay;
dTermGC; dTermGC;
int w = term->fwidth * len; int w = term->fwidth * len;
int h = term->fheight; int h = term->fheight;
bool buffered = bg >= Color_transparent bool buffered = bg >= Color_transparent
skipping to change at line 1386 skipping to change at line 1450
// cut trailing spaces // cut trailing spaces
while (len && text [len - 1] == ' ') while (len && text [len - 1] == ' ')
len--; len--;
int x_ = buffered ? 0 : x; int x_ = buffered ? 0 : x;
int y_ = buffered ? 0 : y; int y_ = buffered ? 0 : y;
while (len) while (len)
{ {
int cwidth = term->fwidth; int cwidth = term->fwidth;
FcChar32 fc = *text++; len--; FcChar32 chr = *text++; len--;
while (len && *text == NOCHAR) while (len && *text == NOCHAR)
text++, len--, cwidth += term->fwidth; text++, len--, cwidth += term->fwidth;
if (fc != ' ') // skip spaces if (chr != ' ') // skip spaces
{ {
FT_UInt glyph = XftCharIndex (disp, f, fc); // handle non-bmp chars when text_t is 16 bit
XftGlyphExtents (disp, f, &glyph, 1, &extents); #if ENABLE_COMBINING && !UNICODE_3
if (ecb_expect_false (IS_COMPOSE (chr)))
if (compose_char *cc = rxvt_composite [chr])
if (cc->c2 == NOCHAR)
chr = cc->c1;
#endif
#if 0
FT_UInt glyphs [decltype (exp)::max_size];
for (int i = 0; i < nchrs; ++i)
glyphs [i] = XftCharIndex (disp, f, chrs [i]);
for (int i = 0; i < nchrs; ++i)
{
XGlyphInfo ep;
XftGlyphExtents (disp, f, glyphs+i, 1, &ep);
printf ("gs %4x g %4x + %3d,%3d o %3d,%3d wh %3d,%3d\n", chrs[i],g
lyphs[i],ep.x,ep.y,ep.xOff,ep.yOff,ep.width,ep.height);
}
#endif
FT_UInt glyph;
int xOff;
#if XFT_CHAR_CACHE
if (ecb_expect_true (IN_RANGE_INC (chr, char_cache_min, char_cache_max
)))
{
glyph = glyph_cache [chr - char_cache_min];
xOff = xoff_cache [chr - char_cache_min];
}
else
#endif
{
glyph = XftCharIndex (disp, f, chr);
XGlyphInfo extents;
XftGlyphExtents (disp, f, &glyph, 1, &extents);
xOff = extents.xOff;
}
ep->glyph = glyph; ep->glyph = glyph;
ep->x = x_ + (cwidth - extents.xOff >> 1); ep->x = x_;
ep->y = y_ + ascent; ep->y = y_ + ascent;
if (extents.xOff == 0) // the xft font cell might differ from the terminal font cell,
ep->x = x_ + cwidth; // in which case we use the average between the two.
ep->x += xOff ? cwidth - xOff >> 1 : 0;
// xft/freetype represent combining characters as characters with zero
// width rendered over the previous character with some fonts, while
// in other fonts, they are considered normal characters, while yet
// in other fonts, they are shifted all over the place.
// we handle the first two cases by keying off on xOff being 0
// for zero-width chars. normally, we would add extents.xOff
// of the base character here, but we don't have that, so we use cwidt
h.
ep->x += xOff ? 0 : cwidth;
ep++; ++ep;
} }
x_ += cwidth; x_ += cwidth;
} }
if (buffered) if (buffered)
{ {
if (ep != enc) if (ep != enc)
{ {
rxvt_drawable &d2 = d.screen->scratch_drawable (w, h); rxvt_drawable &d2 = d.screen->scratch_drawable (w, h);
skipping to change at line 1682 skipping to change at line 1793
for (rxvt_font *const *f = fonts.begin (); f < fonts.end (); f++) for (rxvt_font *const *f = fonts.begin (); f < fonts.end (); f++)
if ((*f)->name && !strcmp ((*f)->name, name)) if ((*f)->name && !strcmp ((*f)->name, name))
return f - fonts.begin (); return f - fonts.begin ();
return -1; return -1;
} }
int int
rxvt_fontset::find_font_idx (unicode_t unicode) rxvt_fontset::find_font_idx (unicode_t unicode)
{ {
if (unicode >= 1<<20) // this limits fmap size. it has to accommodate COMPOSE_HI when UNICODE_3
if (unicode > 0x1fffff)
return 0; return 0;
unicode_t hi = unicode >> 8; unicode_t hi = unicode >> 8;
if (hi < fmap.size () if (hi < fmap.size ())
&& fmap[hi] if (pagemap *pm = fmap[hi])
&& (*fmap[hi])[unicode & 0xff] != 0xff) if ((*pm)[unicode & 0xff] != 0xff)
return (*fmap[hi])[unicode & 0xff]; return (*pm)[unicode & 0xff];
unsigned int i; unsigned int i;
for (i = 0; i < fonts.size (); i++) for (i = 0; i < fonts.size (); i++)
{ {
rxvt_font *f = fonts[i]; rxvt_font *f = fonts[i];
if (!f->loaded) if (!f->loaded)
{ {
if (FROM_UNICODE (f->cs, unicode) == NOCHAR) if (FROM_UNICODE (f->cs, unicode) == NOCHAR)
skipping to change at line 1724 skipping to change at line 1836
if (f->has_char (unicode, &prop, careful)) if (f->has_char (unicode, &prop, careful))
{ {
i = (i << 1) | careful; i = (i << 1) | careful;
goto found; goto found;
} }
next_font: next_font:
if (i == fonts.size () - 1) if (i == fonts.size () - 1)
{ {
// compose characters are handled by the default font, unless another
font takes over
// we do not go via the fallback list for speed reasons.
if (IS_COMPOSE (unicode))
return 0;
if (fallback->name) if (fallback->name)
{ {
// search through the fallback list // search through the fallback list
push_font (new_font (fallback->name, fallback->cs)); push_font (new_font (fallback->name, fallback->cs));
fallback++; fallback++;
} }
else else
{ {
// try to find a new font. // try to find a new font.
// only xft currently supported, as there is no // only xft currently supported, as there is no
 End of changes. 29 change blocks. 
47 lines changed or deleted 169 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)