Package: bogl-bterm

Hello,

I made several improvements to bterm. Please see these patches for details. For convenience, I also created a merge request at https://salsa.debian.org/debian/bogl/-/merge_requests/2


Best Regards,
Zhang Boyang
From 1e253d724658ffc22f1339212af3d2754249b251 Mon Sep 17 00:00:00 2001
From: Zhang Boyang <zhangboyang...@gmail.com>
Date: Mon, 23 May 2022 14:10:52 +0800
Subject: [PATCH 1/7] Better font reload handling

Previous font reload code will leak a mmap on each reload. This patch
adds the ability to munmap old font after reload. However, this also
introduces a bug, if font reload is triggered while drawing in progress,
after signal handler returns, the drawing code will continue to use old
font which has been freed, causing crash. This will be fixed in later
patches.
---
 bogl-bgf.c | 61 +++++++++++++++++++++++++++++++++++++++++++-----------
 bogl-bgf.h |  1 +
 bterm.c    | 10 ++++-----
 3 files changed, 54 insertions(+), 18 deletions(-)

diff --git a/bogl-bgf.c b/bogl-bgf.c
index 1032028..0383f4f 100644
--- a/bogl-bgf.c
+++ b/bogl-bgf.c
@@ -5,38 +5,55 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include <stddef.h>
 
 #include "bogl.h"
 #include "boglP.h"
 #include "bogl-font.h"
+#include "bogl-bgf.h"
+
+struct bogl_bgf {
+  void *f;    /* mmap area */
+  off_t size; /* size of mmap area */
+  struct bogl_font font; /* font descriptor */
+};
+#define bgf_of(font) \
+  ((struct bogl_bgf *)((char *)(font) - offsetof(struct bogl_bgf, font)))
 
 struct bogl_font *bogl_mmap_font(char *file)
 {
-  int fd;
+  struct bogl_bgf *bgf = NULL;
+  struct bogl_font *font = NULL;
+  int fd = -1;
   struct stat buf;
-  void *f;
-  struct bogl_font *font;
+  void *f = (void *)-1;
+
+  bgf = (struct bogl_bgf *)malloc(sizeof(struct bogl_bgf));
+  if (!bgf)
+    goto fail;
+  font = &bgf->font;
 
   fd = open(file, O_RDONLY);
   if (fd == -1)
-    return 0;
+    goto fail;
 
   if (bogl_cloexec(fd) < 0)
-    return 0;
+    goto fail;
 
   if (fstat(fd, &buf))
-    return 0;
+    goto fail;
+  bgf->size = buf.st_size;
+
+  if (buf.st_size < 4)
+    goto fail;
 
   f = mmap(0, buf.st_size, PROT_READ, MAP_SHARED, fd, 0);
   if (f == (void *)-1)
-    return 0;
+    goto fail;
+  bgf->f = f;
 
   if (memcmp("BGF1", f, 4))
-    return 0;
-
-  font = (struct bogl_font *)malloc(sizeof(struct bogl_font));
-  if (!font)
-    return 0;
+    goto fail;
 
   memcpy(font, f + 4, sizeof(*font));
   font->name = ((void *)font->name - (void *)0) + f;
@@ -44,5 +61,25 @@ struct bogl_font *bogl_mmap_font(char *file)
   font->index = ((void *)font->index - (void *)0) + f;
   font->content = ((void *)font->content - (void *)0) + f;
 
+done:
+  if (fd != -1)
+    close(fd);
   return font;
+
+fail:
+  if (bgf) {
+    free(bgf);
+    bgf = NULL;
+    font = NULL;
+  }
+  if (f != (void *)-1)
+    munmap(f, buf.st_size);
+  goto done;
+}
+
+void bogl_munmap_font(struct bogl_font *font)
+{
+  struct bogl_bgf *bgf = bgf_of(font);
+  munmap(bgf->f, bgf->size);
+  free(bgf);
 }
diff --git a/bogl-bgf.h b/bogl-bgf.h
index e9fb994..f14a260 100644
--- a/bogl-bgf.h
+++ b/bogl-bgf.h
@@ -1,2 +1,3 @@
 
 struct bogl_font *bogl_mmap_font(char *file);
+void bogl_munmap_font(struct bogl_font *font);
diff --git a/bterm.c b/bterm.c
index d7b574f..22cbef5 100644
--- a/bterm.c
+++ b/bterm.c
@@ -215,15 +215,13 @@ void reload_font(int sig)
   font = bogl_mmap_font (font_name);
   if (font == NULL)
     {
-      fprintf(stderr, "Bad font\n");
+      /* Fail silently. To simplify implementation, debian-installer may
+         trigger redundant font reload requests, and the font file may not
+         available at that time, so handle it gracefully. */
       return;
     }
   
-  /* This leaks a mmap.  Since the font reloading feature is only expected
-     to be used once per session (for instance, in debian-installer, after
-     the font is replaced with a larger version containing more characters),
-     we don't worry about the leak.  */
-  free(term->font);
+  bogl_munmap_font(term->font);
 
   term->font = font;
   term->xstep = bogl_font_glyph(term->font, ' ', 0);
-- 
2.30.2

From 0f1b0b57a2b36ae4e04a5a32c9a55634a1cbae39 Mon Sep 17 00:00:00 2001
From: Zhang Boyang <zhangboyang...@gmail.com>
Date: Mon, 23 May 2022 22:07:37 +0800
Subject: [PATCH 2/7] Use ioctl(FBIOPAN_DISPLAY) to update screen after drawing

Some framebuffer driver like i915 need explicit notify after drawing,
otherwise modifications in graphics buffer will not be reflected on
screen, so use FBIOPAN_DISPLAY to do this notify.
---
 bogl-term.c |  1 +
 bogl.c      | 10 +++++++++-
 bogl.h      |  1 +
 3 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/bogl-term.c b/bogl-term.c
index d5780d3..1f68523 100644
--- a/bogl-term.c
+++ b/bogl-term.c
@@ -863,4 +863,5 @@ bogl_term_redraw (struct bogl_term *term)
     {
         show_cursor(term, 1);
     }        
+    bogl_update();
 }
diff --git a/bogl.c b/bogl.c
index 6b9996b..a3014e8 100644
--- a/bogl.c
+++ b/bogl.c
@@ -123,12 +123,12 @@ static void kbd_done (void);
 static void vt_switch (int);
 
 static struct fb_fix_screeninfo fb_fix;
+static struct fb_var_screeninfo fb_var;
 /* Initialize BOGL. */
 int
 bogl_init (void)
 {
   unsigned long bogl_frame_offset, bogl_frame_len;
-  struct fb_var_screeninfo fb_var;
   struct vt_stat vts;
   int PAGE_MASK = ~(sysconf(_SC_PAGESIZE) - 1);
 
@@ -456,6 +456,14 @@ bogl_fb_set_palette (int c, int nc, const unsigned char palette[][3])
   ioctl (fb, FBIOPUTCMAP, &cmap);
 }
 
+/* Update screen, some framebuffer driver require this explicit notify after
+   modifying screen contents */
+void
+bogl_update (void)
+{
+  ioctl (fb, FBIOPAN_DISPLAY, &fb_var);
+}
+
 /* Returns the oldest error message since this function was last
    called.  Clears the error state.  Returns a null pointer if no
    errors have occurred.  The caller must free the returned
diff --git a/bogl.h b/bogl.h
index 3b2cb83..93f39ab 100644
--- a/bogl.h
+++ b/bogl.h
@@ -77,6 +77,7 @@ const char *bogl_error (void);
 
 void bogl_gray_scale (int make_gray);
 void bogl_fb_set_palette (int c, int nc, const unsigned char palette[][3]);
+void bogl_update(void);
 void bogl_rectangle (int x1, int y1, int x2, int y2, int c);
 int bogl_metrics (const char *s, int n, const struct bogl_font *font);
 
-- 
2.30.2

From a87f64b7d2e7b1d25123789a10c773d5ae2f144f Mon Sep 17 00:00:00 2001
From: Zhang Boyang <zhangboyang...@gmail.com>
Date: Tue, 24 May 2022 01:27:10 +0800
Subject: [PATCH 3/7] Better quit handling

Previous SIGCHLD handler is not async-signal-safe because it calls
exit(), and it also doesn't save-restore errno. Using _exit() will be
async-signal-safe safe but will result in unclean quit. In the end, this
patch will set a flag variable in signal handler, then defer real
cleanup and quitting to main loop.
---
 bterm.c | 21 +++++++++++++--------
 1 file changed, 13 insertions(+), 8 deletions(-)

diff --git a/bterm.c b/bterm.c
index 22cbef5..9fe9a42 100644
--- a/bterm.c
+++ b/bterm.c
@@ -64,7 +64,7 @@ static const unsigned char palette[16][3] =
 
 static int child_pid = 0;
 static struct termios ttysave;
-static int quit = 0;
+static volatile int quit = 0, quit_status = 0;
 
 /* Out of memory.  Give up. */
 static void out_of_memory (void)
@@ -144,24 +144,29 @@ void send_hangup(void)
 
 void sigchld(int sig)
 {
+  int errsv = errno;
   int status;
   if (waitpid(child_pid, &status, WNOHANG) > 0) {
     child_pid = 0;
     /* Reset ownership and permissions of ttyfd device? */
     tcsetattr(0, TCSAFLUSH, &ttysave);
     if (WIFEXITED (status))
-      exit(WEXITSTATUS (status));
-    if (WIFSIGNALED (status))
-      exit(128 + WTERMSIG (status));
-    if (WIFSTOPPED (status))
-      exit(128 + WSTOPSIG (status));
-    exit(status);
+      quit_status = WEXITSTATUS (status);
+    else if (WIFSIGNALED (status))
+      quit_status = 128 + WTERMSIG (status);
+    else if (WIFSTOPPED (status))
+      quit_status = 128 + WSTOPSIG (status);
+    else
+      quit_status = status;
+    quit = 1;
   }
   signal(SIGCHLD, sigchld);
+  errno = errsv;
 }
 
 void sigterm(int sig)
 {
+	quit_status = 128 + SIGTERM;
 	quit = 1;
 }
 
@@ -418,5 +423,5 @@ int main(int argc, char *argv[])
     }
   }
 
-  return 0;
+  exit(quit_status);
 }
-- 
2.30.2

From 2099586e412381f4c01a2455cf54efb93dffefb3 Mon Sep 17 00:00:00 2001
From: Zhang Boyang <zhangboyang...@gmail.com>
Date: Tue, 24 May 2022 12:58:01 +0800
Subject: [PATCH 4/7] Fix incorrect signal handling

There are problems in signal handlers. Signal handlers must not call any
non-async-signal-safe functions, and they must save-restore errno if
errno is modified inside signal handlers. This patch fixes these
problems by deferring real tasks to main loop. This also fixes crashes
caused by font reloading which was mentioned in previous patch.
---
 bogl.c  | 35 +++++++++++------------------------
 bogl.h  |  2 ++
 bterm.c | 26 ++++++++++++++++++++++----
 3 files changed, 35 insertions(+), 28 deletions(-)

diff --git a/bogl.c b/bogl.c
index a3014e8..9b628b1 100644
--- a/bogl.c
+++ b/bogl.c
@@ -65,6 +65,7 @@
 /* Global variables. */
 int bogl_xres, bogl_yres, bogl_bpp;	/* bogl.h */
 int bogl_refresh;
+volatile int vt_switch_pending = 0;
 volatile char *bogl_frame;		/* boglP.h */
 int bogl_drawing;
 int bogl_line_len;
@@ -120,7 +121,7 @@ static void draw_disable (void);
 static void kbd_init (void);
 static void kbd_done (void);
 
-static void vt_switch (int);
+static void sigusr2 (int);
 
 static struct fb_fix_screeninfo fb_fix;
 static struct fb_var_screeninfo fb_var;
@@ -181,7 +182,7 @@ bogl_init (void)
   mode.relsig = SIGUSR2;
   mode.acqsig = SIGUSR2;
 
-  signal (SIGUSR2, vt_switch);
+  signal (SIGUSR2, sigusr2);
 
   if (-1 == ioctl (tty, VT_SETMODE, &mode))
     return bogl_fail ("can't set VT mode: %s", strerror (errno));
@@ -591,32 +592,18 @@ draw_disable (void)
 /* Signal handler called whenever the kernel wants to switch to or
    from our tty. */
 static void
-vt_switch (int sig unused)
+sigusr2 (int sig unused)
 {
-  signal (SIGUSR2, vt_switch);
+  vt_switch_pending = 1;
+}
+void
+vt_switch (void)
+{
+  vt_switch_pending = 0;
 
-  /* If a BOGL drawing function is in progress then we cannot mode
-     switch right now because the drawing function would continue to
-     scribble on the screen after the switch.  So disable further
-     drawing and schedule an alarm to try again in .1 second. */
   if (bogl_drawing)
     {
-      draw_disable ();
-
-      signal (SIGALRM, vt_switch);
-      
-      {
-	struct itimerval duration;
-	
-	duration.it_interval.tv_sec = 0;
-	duration.it_interval.tv_usec = 0;
-	duration.it_value.tv_sec = 0;
-	duration.it_value.tv_usec = 100000;
-	if (-1 == setitimer (ITIMER_REAL, &duration, NULL))
-	  bogl_fail ("can't set timer: %s", strerror (errno));
-      }
-      
-      return;
+      abort();
     }
       
   if (visible)
diff --git a/bogl.h b/bogl.h
index 93f39ab..61b0275 100644
--- a/bogl.h
+++ b/bogl.h
@@ -69,6 +69,8 @@ extern int bogl_xres, bogl_yres, bogl_ncols;
 
 /* 1=Must refresh screen due to tty change. */
 extern int bogl_refresh;
+extern volatile int vt_switch_pending;
+extern void vt_switch (void);
 
 /* Generic routines. */
 int bogl_init (void);
diff --git a/bterm.c b/bterm.c
index 9fe9a42..f4e90a0 100644
--- a/bterm.c
+++ b/bterm.c
@@ -160,7 +160,6 @@ void sigchld(int sig)
       quit_status = status;
     quit = 1;
   }
-  signal(SIGCHLD, sigchld);
   errno = errsv;
 }
 
@@ -212,10 +211,16 @@ void set_window_size(int ttyfd, int x, int y)
 
 static char *font_name;
 static struct bogl_term *term;
+static volatile int reload_font_pending = 0;
 
-void reload_font(int sig)
+void sighup(int sig)
+{
+  reload_font_pending = 1;
+}
+void reload_font(void)
 {
   struct bogl_font *font;
+  reload_font_pending = 0;
 
   font = bogl_mmap_font (font_name);
   if (font == NULL)
@@ -231,6 +236,9 @@ void reload_font(int sig)
   term->font = font;
   term->xstep = bogl_font_glyph(term->font, ' ', 0);
   term->ystep = bogl_font_height(term->font);
+
+  /* Request redraw */
+  bogl_refresh = 2;
 }
 
 /*
@@ -242,6 +250,7 @@ int main(int argc, char *argv[])
 {
   struct termios ntio;
   int ret;
+  int errsv;
   char buf[8192];
   struct timeval tv;
   int ptyfd, ttyfd;
@@ -341,7 +350,7 @@ int main(int argc, char *argv[])
   }
   spawn_shell(ptyfd, ttyfd, command_args);
 
-  signal(SIGHUP, reload_font);
+  signal(SIGHUP, sighup);
   signal(SIGTERM, sigterm);
 
   ntio = ttysave;
@@ -362,6 +371,10 @@ int main(int argc, char *argv[])
 
     if (quit)
 	    break;
+    if (reload_font_pending)
+	    reload_font();
+    if (vt_switch_pending)
+	    vt_switch();
     
     if(pending)
     {
@@ -379,9 +392,14 @@ int main(int argc, char *argv[])
     if (ptyfd > max)
       max = ptyfd;
     ret = select(max+1, &fds, NULL, NULL, &tv);
+    errsv = errno;
 
     if (quit)
 	    break;
+    if (reload_font_pending)
+	    reload_font();
+    if (vt_switch_pending)
+	    vt_switch();
 
     if (bogl_refresh) {
       /* Handle VT switching.  */
@@ -396,7 +414,7 @@ int main(int argc, char *argv[])
       bogl_refresh = 0;
       bogl_term_redraw(term);
     }
-    if (ret == 0 || (ret < 0 && errno == EINTR))
+    if (ret == 0 || (ret < 0 && errsv == EINTR))
     {
       if(pending)
       {
-- 
2.30.2

From e92cdb7b1d5bad7471d90d296a9801726cf57af6 Mon Sep 17 00:00:00 2001
From: Zhang Boyang <zhangboyang...@gmail.com>
Date: Tue, 24 May 2022 23:49:31 +0800
Subject: [PATCH 5/7] Font scaler

Current fonts is too small to see on HiDPI displays. This patch
introduces a BGF font scaler, which can scale font glyphs on the fly
when loading. The scale factor can be automatically determined by screen
resolution or specified by a new command line argument '-s'. This patch
also add support for BGF fonts wider than 32 pixels.
---
 bogl-bgf.c   | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 bogl-bgf.h   |   2 +-
 bogl-pcfb.c  |   8 ++--
 bogl-tcfb.c  |   8 ++--
 bogl-vga16.c |  33 +++++++++------
 bterm.1      |   4 ++
 bterm.c      |  30 ++++++++++----
 7 files changed, 167 insertions(+), 29 deletions(-)

diff --git a/bogl-bgf.c b/bogl-bgf.c
index 0383f4f..1a8b9d9 100644
--- a/bogl-bgf.c
+++ b/bogl-bgf.c
@@ -20,7 +20,113 @@ struct bogl_bgf {
 #define bgf_of(font) \
   ((struct bogl_bgf *)((char *)(font) - offsetof(struct bogl_bgf, font)))
 
-struct bogl_font *bogl_mmap_font(char *file)
+/* Scale the font (both width and height)
+   Declare as inline function, will run faster if scale factor is constant */
+static inline void __attribute__((always_inline))
+bgf_scale_inline(struct bogl_bgf *bgf, int scale)
+{
+  void *new_f = (void *)-1;
+  off_t new_size;
+  struct bogl_font new_font;
+
+  /* old_size*pow(scale,2) should enough and have some waste here */
+  new_size = bgf->size * scale * scale;
+
+  /* Allocate new memory */
+  new_f = mmap(0, new_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  if (new_f == (void *)-1) {
+    /* If memory allocation failed, skip scaling silently */
+    goto fail;
+  }
+
+  /* Copy old font data to new memory */
+  memcpy(new_f, bgf->f, bgf->size);
+
+  /* Set font metadata */
+  struct bogl_font *font = &new_font;
+  memcpy(font, new_f + 4, sizeof(*font));
+  font->name = ((void *)font->name - (void *)0) + new_f;
+  font->offset = ((void *)font->offset - (void *)0) + new_f;
+  font->index = ((void *)font->index - (void *)0) + new_f;
+  font->content = ((void *)font->content - (void *)0) + new_f;
+  font->height = bgf->font.height * scale;
+
+  /* Scale each glyph */
+  int mask = font->index_mask;
+  for (int j = 0; j <= mask; j++) {
+    for (int i = font->offset[j]; font->index[i]; i += 2) {
+      int old_height = bgf->font.height;
+      int old_width = (font->index[i] & mask);
+      if (old_width * scale > mask) {
+        /* Scaled glyph width can't fit in mask, fail */
+        goto fail;
+      }
+      font->index[i] = (font->index[i] & ~mask) | (old_width * scale);
+      font->index[i + 1] *= scale * scale;
+      u_int32_t *in_bitmap = &bgf->font.content[bgf->font.index[i + 1]];
+      int in_pitch = (old_width + 31) / 32;
+      u_int32_t *out_bitmap = &font->content[font->index[i + 1]];
+      int out_pitch = (old_width * scale + 31) / 32;
+      for (int y = 0; y < old_height * scale; y++) {
+        const int outlined = 0; /* Useful for debugging */
+        if (y % scale == 0 || outlined) {
+          u_int32_t *in_ptr = in_bitmap + in_pitch * (y / scale);
+          u_int32_t *out_ptr = out_bitmap + out_pitch * y;
+          memset(out_ptr, 0, sizeof(u_int32_t) * out_pitch);
+          for (int x = 0; x < old_width * scale; x++) {
+            int in_off = (x / scale) / 32, in_bit = 31 - (x / scale) % 32;
+            int out_off = x / 32, out_bit = 31 - x % 32;
+            u_int32_t pixel = (in_ptr[in_off] >> in_bit) & 1;
+            if (outlined)
+              if (0 < x % scale && x % scale < scale - 1)
+                if (0 < y % scale && y % scale < scale - 1)
+                  pixel = 0;
+            out_ptr[out_off] |= pixel << out_bit;
+          }
+        } else {
+          u_int32_t *last_line = out_bitmap + out_pitch * (y - 1);
+          u_int32_t *curr_line = out_bitmap + out_pitch * y;
+          memcpy(curr_line, last_line, sizeof(u_int32_t) * out_pitch);
+        }
+      }
+    }
+  }
+
+  /* Replace old font with new font */
+  munmap(bgf->f, bgf->size);
+  bgf->f = new_f;
+  bgf->size = new_size;
+  bgf->font = new_font;
+  return;
+fail:
+  if (new_f != (void *)-1)
+    munmap(new_f, new_size);
+}
+static void __attribute__((noinline))
+bgf_scale_noinline(struct bogl_bgf *bgf, int scale)
+{
+  bgf_scale_inline(bgf, scale);
+}
+static void __attribute__((noinline)) bgf_scale_2x(struct bogl_bgf *bgf)
+{
+  bgf_scale_inline(bgf, 2);
+}
+static void __attribute__((noinline)) bgf_scale_4x(struct bogl_bgf *bgf)
+{
+  bgf_scale_inline(bgf, 4);
+}
+static void bgf_scale(struct bogl_bgf *bgf, int scale)
+{
+  switch (scale) {
+    /* Use fast implementation if possible */
+    case 2: bgf_scale_2x(bgf); return;
+    case 4: bgf_scale_4x(bgf); return;
+    /* Universal implementation as fallback */
+    default: bgf_scale_noinline(bgf, scale); return;
+  }
+}
+
+struct bogl_font *bogl_mmap_font(char *file, int scale)
 {
   struct bogl_bgf *bgf = NULL;
   struct bogl_font *font = NULL;
@@ -61,6 +167,9 @@ struct bogl_font *bogl_mmap_font(char *file)
   font->index = ((void *)font->index - (void *)0) + f;
   font->content = ((void *)font->content - (void *)0) + f;
 
+  if (scale >= 2)
+    bgf_scale(bgf, scale);
+
 done:
   if (fd != -1)
     close(fd);
diff --git a/bogl-bgf.h b/bogl-bgf.h
index f14a260..978da61 100644
--- a/bogl-bgf.h
+++ b/bogl-bgf.h
@@ -1,3 +1,3 @@
 
-struct bogl_font *bogl_mmap_font(char *file);
+struct bogl_font *bogl_mmap_font(char *file, int scale);
 void bogl_munmap_font(struct bogl_font *font);
diff --git a/bogl-pcfb.c b/bogl-pcfb.c
index c737d38..b16c804 100644
--- a/bogl-pcfb.c
+++ b/bogl-pcfb.c
@@ -142,6 +142,7 @@ bogl_pcfb_text (int xx, int yy, const char *s, int n, int fg, int bg, int ul,
 
       u_int32_t *character = NULL;
       int w = bogl_font_glyph (font, wc, &character);
+      int pitch = (w + 31) / 32;
 
       int x, y, h1 = ul ? h - 1 : h;
 
@@ -153,16 +154,15 @@ bogl_pcfb_text (int xx, int yy, const char *s, int n, int fg, int bg, int ul,
       
       for (y = 0; y < h1; y++)
 	{
-	  u_int32_t c = *character++;
+	  u_int32_t *c = character;
+	  character += pitch;
 	  
 	  for (x = 0; x < w; x++)
 	    {
-	      if (c & 0x80000000)
+	      if ((c[x / 32] & (1 << (31 - x % 32))))
 		put_var (dst, xx+x, cmap_lookup(fg), bpp);
 	      else if (bg != -1)
 		put_var (dst, xx+x, cmap_lookup(bg), bpp);
-
-	      c <<= 1;
 	    }
 
 	  dst += bogl_line_len;
diff --git a/bogl-tcfb.c b/bogl-tcfb.c
index 895d298..cd23b77 100644
--- a/bogl-tcfb.c
+++ b/bogl-tcfb.c
@@ -169,6 +169,7 @@ bogl_tcfb_text (int xx, int yy, const char *s, int n, int fg, int bg, int ul,
 
       u_int32_t *character = NULL;
       int w = bogl_font_glyph (font, wc, &character);
+      int pitch = (w + 31) / 32;
 
       int x, y, h1 = ul ? h - 1 : h;
       unsigned cfg = cmap_lookup (fg), cbg = cmap_lookup (bg);
@@ -181,16 +182,15 @@ bogl_tcfb_text (int xx, int yy, const char *s, int n, int fg, int bg, int ul,
       
       for (y = 0; y < h1; y++)
 	{
-	  u_int32_t c = *character++;
+	  u_int32_t *c = character;
+	  character += pitch;
 	  
 	  for (x = 0; x < w; x++)
 	    {
-	      if (c & 0x80000000)
+	      if ((c[x / 32] & (1 << (31 - x % 32))))
 		put_var (dst, xx+x, cfg, bpp);
 	      else if (bg != -1)
 		put_var (dst, xx+x, cbg, bpp);
-
-	      c <<= 1;
 	    }
 
 	  dst += bogl_line_len;
diff --git a/bogl-vga16.c b/bogl-vga16.c
index bb81803..6c2cbcf 100644
--- a/bogl-vga16.c
+++ b/bogl-vga16.c
@@ -394,25 +394,34 @@ bogl_vga16_text (int xx, int yy, const char *s, int n, int fg, int bg, int ul,
     {
       u_int32_t *character = NULL;
       int width = bogl_font_glyph (font, wc, &character);
+      int pitch = (width + ul_bits - 1) / ul_bits;
 
       if (character == NULL)
 	continue;
 
-      for (y = 0; y < h; y++)
-	bits[y] |= character[y] >> x;
-      x += width;
-
-      if (x >= (int) ul_bits)
-	{
-	  plot (bg);
+      int i, w;
+      for (i = 0; i < pitch; i++)
+        {
+	  w = width - i * ul_bits;
+	  if (w > ul_bits)
+	    w = ul_bits;
 
-	  x -= ul_bits;
 	  for (y = 0; y < h; y++)
-	    bits[y] = character[y] << (width - x);
+	    bits[y] |= character[y * pitch + i] >> x;
+	  x += w;
 
-	  xx += ul_bits;
-	  if (xx >= bogl_xres)
-	    goto done;
+	  if (x >= (int) ul_bits)
+	    {
+	      plot (bg);
+
+	      x -= ul_bits;
+	      for (y = 0; y < h; y++)
+		bits[y] = character[y * pitch + i] << (w - x - 1) << 1;
+
+	      xx += ul_bits;
+	      if (xx >= bogl_xres)
+		goto done;
+	    }
 	}
     }
   plot (bg);
diff --git a/bterm.1 b/bterm.1
index 8b96b99..e62d3ee 100644
--- a/bterm.1
+++ b/bterm.1
@@ -21,6 +21,7 @@ bterm - framebuffer terminal emulator
 .B bterm
 .B -f
 .RI font.bgf
+.RI "[-s " scale "]"
 .RI "[-l " locale "]"
 .RI "[" program "]"
 .SH DESCRIPTION
@@ -36,6 +37,9 @@ Specify the font.  BGF fonts are produced by
 .BR bdftobogl (1)
 from standard BDF font files.
 .TP
+.B -s scale
+Specify the font scale factor, 0 for auto.
+.TP
 .B -l locale
 Specify a locale for
 .B bterm
diff --git a/bterm.c b/bterm.c
index f4e90a0..f70e814 100644
--- a/bterm.c
+++ b/bterm.c
@@ -210,6 +210,7 @@ void set_window_size(int ttyfd, int x, int y)
 }
 
 static char *font_name;
+static int font_scale;
 static struct bogl_term *term;
 static volatile int reload_font_pending = 0;
 
@@ -222,7 +223,7 @@ void reload_font(void)
   struct bogl_font *font;
   reload_font_pending = 0;
 
-  font = bogl_mmap_font (font_name);
+  font = bogl_mmap_font (font_name, font_scale);
   if (font == NULL)
     {
       /* Fail silently. To simplify implementation, debian-installer may
@@ -267,6 +268,7 @@ int main(int argc, char *argv[])
           switch (argv[i][1])
           {
               case 'f':
+              case 's':
               case 'l':
                   o = argv[i][1];
                   break;
@@ -290,6 +292,11 @@ int main(int argc, char *argv[])
                     o = ' ';
                     break;
 
+                case 's':
+                    font_scale = atoi(argv[i]);
+                    o = ' ';
+                    break;
+
                 case 'l':
                     locale = argv[i];
                     o = ' ';
@@ -303,16 +310,11 @@ int main(int argc, char *argv[])
   setlocale(LC_CTYPE, locale);
 
   if (font_name == NULL) {
-    fprintf(stderr, "Usage: %s -f font.bgf [ -l locale ] [ program ]\n", argv[0]);
+    fprintf(stderr, "Usage: %s -f font.bgf [ -s scale ] [ -l locale ] [ program ]\n", argv[0]);
  
     return 1;
   }
 
-  if ((font = bogl_mmap_font(font_name)) == NULL) {
-    fprintf(stderr, "Bad font\n");
-    return 1;
-  }
-
   tcgetattr(0, &ttysave);
 
   if (!bogl_init()) {
@@ -320,6 +322,20 @@ int main(int argc, char *argv[])
     return 1;
   }
 
+  if (font_scale == 0) {
+    if (bogl_xres > 3840 && bogl_yres > 2160)
+      font_scale = 4;
+    else if (bogl_xres > 1920 && bogl_yres > 1080)
+      font_scale = 2;
+    else
+      font_scale = 1;
+  }
+
+  if ((font = bogl_mmap_font(font_name, font_scale)) == NULL) {
+    fprintf(stderr, "Bad font\n");
+    return 1;
+  }
+
   term = bogl_term_new(font);
   if (!term)
     exit(1);
-- 
2.30.2

From f49a022b952c36740376da2ad0f1c338868f9d58 Mon Sep 17 00:00:00 2001
From: Zhang Boyang <zhangboyang...@gmail.com>
Date: Wed, 25 May 2022 13:45:14 +0800
Subject: [PATCH 6/7] Fix character occasionally disappears from right edge of
 screen

Sometimes when a line is full, the right most character disappears. The
is because term->xpos may equals to term->xsize and this condition
confuses some code. Reported by AddressSanitizer.
---
 bogl-term.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/bogl-term.c b/bogl-term.c
index 1f68523..2d35d1d 100644
--- a/bogl-term.c
+++ b/bogl-term.c
@@ -283,7 +283,7 @@ static void
 clear_left (struct bogl_term *term)
 {
     int j, i = SCR(term->xpos, term->ypos);
-    if (!term->screen[i])
+    if (term->xpos < term->xsize && !term->screen[i])
     {
         for (j = i - 1; !term->screen[j]; j--)
         {
@@ -577,7 +577,7 @@ bogl_term_out (struct bogl_term *term, char *s, int n)
 
             if (wc == 'K')
             {                   /* el=\E[K */
-                if (term->state == 1 && !term->arg[0])
+                if (term->state == 1 && !term->arg[0] && term->xpos < term->xsize)
                 {
                     clear_left (term);
                     for (i = SCR (term->xpos, term->ypos); i < SCR (term->xsize, term->ypos); i++)
-- 
2.30.2

From 04d61235f329c731a8cf7591b4c6d84430045b1e Mon Sep 17 00:00:00 2001
From: Zhang Boyang <zhangboyang...@gmail.com>
Date: Wed, 25 May 2022 14:42:50 +0800
Subject: [PATCH 7/7] Fix out-of-bound read in tcfb's cmap_lookup()

When bogl_tcfb_text() is called with bg = -1, it will call
cmap_lookup(-1), resulting in out-of-bound read, although the return
value is not used. Reported by AddressSanitizer.
---
 bogl-tcfb.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/bogl-tcfb.c b/bogl-tcfb.c
index cd23b77..5e14892 100644
--- a/bogl-tcfb.c
+++ b/bogl-tcfb.c
@@ -48,7 +48,7 @@ static struct fb_var_screeninfo var;
 static inline unsigned int
 cmap_lookup (int entry)
 {
-  if (entry >= cmap_size)
+  if (entry < 0 || entry >= cmap_size)
     return 0;
   
   return cmap[entry];
-- 
2.30.2

Reply via email to